Skip to content

Commit e6e3278

Browse files
committed
Merge pull request #22 from lsegal/unicode-support
Add support for hashing Unicode data by using Buffers
2 parents fea07e6 + ce33500 commit e6e3278

File tree

6 files changed

+94
-388
lines changed

6 files changed

+94
-388
lines changed

helpers.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
var Buffer = require('buffer').Buffer;
2+
var intSize = 4;
3+
var zeroBuffer = new Buffer(intSize); zeroBuffer.fill(0);
4+
var chrsz = 8;
5+
6+
function toArray(buf, bigEndian) {
7+
if ((buf.length % intSize) !== 0) {
8+
var len = buf.length + (intSize - (buf.length % intSize));
9+
buf = Buffer.concat([buf, zeroBuffer], len);
10+
}
11+
12+
var arr = [];
13+
var fn = bigEndian ? buf.readInt32BE : buf.readInt32LE;
14+
for (var i = 0; i < buf.length; i += intSize) {
15+
arr.push(fn.call(buf, i));
16+
}
17+
return arr;
18+
}
19+
20+
function toBuffer(arr, size, bigEndian) {
21+
var buf = new Buffer(size);
22+
var fn = bigEndian ? buf.writeInt32BE : buf.writeInt32LE;
23+
for (var i = 0; i < arr.length; i++) {
24+
fn.call(buf, arr[i], i * 4, true);
25+
}
26+
return buf;
27+
}
28+
29+
function hash(buf, fn, hashSize, bigEndian) {
30+
if (!Buffer.isBuffer(buf)) buf = new Buffer(buf);
31+
var arr = fn(toArray(buf, bigEndian), buf.length * chrsz);
32+
return toBuffer(arr, hashSize, bigEndian);
33+
}
34+
35+
module.exports = { hash: hash };

index.js

Lines changed: 43 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -5,105 +5,72 @@ var rng = require('./rng')
55
var md5 = require('./md5')
66

77
var algorithms = {
8-
sha1: {
9-
hex: sha.hex_sha1,
10-
base64: sha.b64_sha1,
11-
binary: sha.str_sha1
12-
},
13-
sha256: {
14-
hex: sha256.hex_sha256,
15-
base64: sha256.b64_sha256,
16-
binary: sha256.str_sha256
17-
},
18-
md5: {
19-
hex: md5.hex_md5,
20-
base64: md5.b64_md5,
21-
binary: md5.bin_md5
22-
}
8+
sha1: sha,
9+
sha256: sha256,
10+
md5: md5
2311
}
2412

25-
var algorithmsHmac = {
26-
sha1: {
27-
hex: sha.hex_hmac_sha1,
28-
base64: sha.b64_hmac_sha1,
29-
binary: sha.str_hmac_sha1
30-
},
31-
sha256: {
32-
hex: sha256.hex_hmac_sha256,
33-
base64: sha256.b64_hmac_sha256,
34-
binary: sha256.str_hmac_sha256
35-
},
36-
md5: {
37-
hex: md5.hex_hmac_md5,
38-
base64: md5.b64_hmac_md5,
39-
binary: md5.bin_hmac_md5
13+
var blocksize = 64
14+
var zeroBuffer = new Buffer(blocksize); zeroBuffer.fill(0)
15+
function hmac(fn, key, data) {
16+
if(!Buffer.isBuffer(key)) key = new Buffer(key)
17+
if(!Buffer.isBuffer(data)) data = new Buffer(data)
18+
19+
if(key.length > blocksize) {
20+
key = fn(key)
21+
} else if(key.length < blocksize) {
22+
key = Buffer.concat([key, zeroBuffer], blocksize)
4023
}
41-
}
4224

25+
var ipad = new Buffer(blocksize), opad = new Buffer(blocksize)
26+
for(var i = 0; i < blocksize; i++) {
27+
ipad[i] = key[i] ^ 0x36
28+
opad[i] = key[i] ^ 0x5C
29+
}
4330

44-
function error () {
45-
var m = [].slice.call(arguments).join(' ')
46-
throw new Error([
47-
m,
48-
'we accept pull requests',
49-
'http://github.com/dominictarr/crypto-browserify'
50-
].join('\n'))
31+
var hash = fn(Buffer.concat([ipad, data]))
32+
return fn(Buffer.concat([opad, hash]))
5133
}
5234

53-
exports.createHash = function (alg) {
35+
function hash(alg, key) {
5436
alg = alg || 'sha1'
55-
if(!algorithms[alg])
56-
error('algorithm:', alg, 'is not yet supported')
57-
var s = ''
58-
var _alg = algorithms[alg]
37+
var fn = algorithms[alg]
38+
var bufs = []
39+
var length = 0
40+
if(!fn) error('algorithm:', alg, 'is not yet supported')
5941
return {
6042
update: function (data) {
61-
s += data
43+
bufs.push(data)
44+
length += data.length
6245
return this
6346
},
6447
digest: function (enc) {
65-
enc = enc || 'binary'
66-
var fn
67-
if(!(fn = _alg[enc]))
68-
error('encoding:', enc , 'is not yet supported for algorithm', alg)
69-
var r = fn(s)
70-
s = null //not meant to use the hash after you've called digest.
71-
return r
48+
var buf = Buffer.concat(bufs)
49+
var r = key ? hmac(fn, key, buf) : fn(buf)
50+
bufs = null
51+
return enc ? r.toString(enc) : r
7252
}
7353
}
7454
}
7555

76-
exports.createHmac = function (alg, key) {
77-
if (!algorithmsHmac[alg])
78-
error('algorithm:', alg, 'is not yet supported')
79-
if (typeof key != 'string')
80-
key = key.toString('binary')
81-
var s = ''
82-
var _alg = algorithmsHmac[alg]
83-
return {
84-
update: function (data) {
85-
s += data
86-
return this
87-
},
88-
digest: function (enc) {
89-
enc = enc || 'binary'
90-
var fn
91-
if (!(fn = _alg[enc]))
92-
error('encoding:', enc, 'is not yet support for algorithm', alg)
93-
var r = fn(key, s)
94-
s = null
95-
return r
96-
}
97-
}
56+
function error () {
57+
var m = [].slice.call(arguments).join(' ')
58+
throw new Error([
59+
m,
60+
'we accept pull requests',
61+
'http://github.com/dominictarr/crypto-browserify'
62+
].join('\n'))
9863
}
9964

65+
exports.createHash = function (alg) { return hash(alg) }
66+
exports.createHmac = function (alg, key) { return hash(alg, key) }
10067
exports.randomBytes = function(size, callback) {
10168
if (callback && callback.call) {
10269
try {
103-
callback.call(this, undefined, new Buffer(rng(size)));
104-
} catch (err) { callback(err); }
70+
callback.call(this, undefined, new Buffer(rng(size)))
71+
} catch (err) { callback(err) }
10572
} else {
106-
return new Buffer(rng(size));
73+
return new Buffer(rng(size))
10774
}
10875
}
10976

md5.js

Lines changed: 4 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,7 @@
77
* See http://pajhome.org.uk/crypt/md5 for more info.
88
*/
99

10-
/*
11-
* Configurable variables. You may need to tweak these to be compatible with
12-
* the server-side, but the defaults work in most cases.
13-
*/
14-
var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
15-
var b64pad = "="; /* base-64 pad character. "=" for strict RFC compliance */
16-
var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */
17-
18-
/*
19-
* These are the functions you'll usually want to call
20-
* They take string arguments and return either hex or base-64 encoded strings
21-
*/
22-
function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}
23-
function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}
24-
function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}
25-
function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
26-
function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
27-
function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }
10+
var helpers = require('./helpers');
2811

2912
/*
3013
* Perform a simple self-test to see if the VM is working
@@ -156,25 +139,6 @@ function md5_ii(a, b, c, d, x, s, t)
156139
return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
157140
}
158141

159-
/*
160-
* Calculate the HMAC-MD5, of a key and some data
161-
*/
162-
function core_hmac_md5(key, data)
163-
{
164-
var bkey = str2binl(key);
165-
if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);
166-
167-
var ipad = Array(16), opad = Array(16);
168-
for(var i = 0; i < 16; i++)
169-
{
170-
ipad[i] = bkey[i] ^ 0x36363636;
171-
opad[i] = bkey[i] ^ 0x5C5C5C5C;
172-
}
173-
174-
var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
175-
return core_md5(opad.concat(hash), 512 + 128);
176-
}
177-
178142
/*
179143
* Add integers, wrapping at 2^32. This uses 16-bit operations internally
180144
* to work around bugs in some JS interpreters.
@@ -194,70 +158,6 @@ function bit_rol(num, cnt)
194158
return (num << cnt) | (num >>> (32 - cnt));
195159
}
196160

197-
/*
198-
* Convert a string to an array of little-endian words
199-
* If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
200-
*/
201-
function str2binl(str)
202-
{
203-
var bin = Array();
204-
var mask = (1 << chrsz) - 1;
205-
for(var i = 0; i < str.length * chrsz; i += chrsz)
206-
bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
207-
return bin;
208-
}
209-
210-
/*
211-
* Convert an array of little-endian words to a string
212-
*/
213-
function binl2str(bin)
214-
{
215-
var str = "";
216-
var mask = (1 << chrsz) - 1;
217-
for(var i = 0; i < bin.length * 32; i += chrsz)
218-
str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
219-
return str;
220-
}
221-
222-
/*
223-
* Convert an array of little-endian words to a hex string.
224-
*/
225-
function binl2hex(binarray)
226-
{
227-
var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
228-
var str = "";
229-
for(var i = 0; i < binarray.length * 4; i++)
230-
{
231-
str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
232-
hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF);
233-
}
234-
return str;
235-
}
236-
237-
/*
238-
* Convert an array of little-endian words to a base-64 string
239-
*/
240-
function binl2b64(binarray)
241-
{
242-
var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
243-
var str = "";
244-
for(var i = 0; i < binarray.length * 4; i += 3)
245-
{
246-
var triplet = (((binarray[i >> 2] >> 8 * ( i %4)) & 0xFF) << 16)
247-
| (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
248-
| ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
249-
for(var j = 0; j < 4; j++)
250-
{
251-
if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
252-
else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
253-
}
254-
}
255-
return str;
256-
}
257-
258-
exports.hex_md5 = hex_md5;
259-
exports.b64_md5 = b64_md5;
260-
exports.bin_md5 = str_md5;
261-
exports.hex_hmac_md5 = hex_hmac_md5;
262-
exports.b64_hmac_md5 = b64_hmac_md5;
263-
exports.bin_hmac_md5 = str_hmac_md5;
161+
module.exports = function md5(buf) {
162+
return helpers.hash(buf, core_md5, 16);
163+
};

0 commit comments

Comments
 (0)