Skip to content

Commit 35cd221

Browse files
committed
Shim crypto.
1 parent f7f828f commit 35cd221

2 files changed

Lines changed: 126 additions & 0 deletions

File tree

browser/crypto.js

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*! @license MIT ©2015-2019 Ruben Verborgh, Ghent University - imec */
2+
/* Browser replacement for a subset of crypto. */
3+
4+
exports.getHashes = () => ['sha1'];
5+
6+
exports.createHash = () => {
7+
let contents;
8+
return {
9+
update: c => contents ? (contents += c) : (contents = c),
10+
digest: () => sha1(contents),
11+
};
12+
};
13+
14+
/*! @license MIT ©2002-2014 Chris Veness */
15+
/* SHA-1 implementation */
16+
17+
// constants [§4.2.1]
18+
const K = [0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6];
19+
const pow2to35 = Math.pow(2, 35);
20+
21+
/**
22+
* Generates SHA-1 hash of string.
23+
*
24+
* @param {string} msg - (Unicode) string to be hashed.
25+
* @returns {string} Hash of msg as hex character string.
26+
*/
27+
function sha1(msg = '') {
28+
// PREPROCESSING
29+
msg += '\u0080'; // add trailing '1' bit (+ 0's padding) to string [§5.1.1]
30+
31+
// convert string msg into 512-bit/16-integer blocks arrays of ints [§5.2.1]
32+
const length = msg.length;
33+
const l = length / 4 + 2; // length (in 32-bit integers) of msg + ‘1’ + appended length
34+
const N = ~~((l + 15) / 16); // number of 16-integer-blocks required to hold 'l' ints
35+
const M = new Array(N);
36+
37+
for (let i = 0, index = 0; i < N; i++) {
38+
M[i] = new Array(16);
39+
for (let j = 0; j < 16; j++, index++) { // encode 4 chars per integer, big-endian encoding
40+
M[i][j] = (index < length ? msg.charCodeAt(index) << 24 : 0) |
41+
(++index < length ? msg.charCodeAt(index) << 16 : 0) |
42+
(++index < length ? msg.charCodeAt(index) << 8 : 0) |
43+
(++index < length ? msg.charCodeAt(index) : 0);
44+
}
45+
}
46+
// add length (in bits) into final pair of 32-bit integers (big-endian) [§5.1.1]
47+
// note: most significant word would be (len-1)*8 >>> 32, but since JS converts
48+
// bitwise-op args to 32 bits, we need to simulate this by arithmetic operators
49+
M[N - 1][14] = ~~((length - 1) / pow2to35);
50+
M[N - 1][15] = ((length - 1) * 8) & 0xffffffff;
51+
52+
// set initial hash value [§5.3.1]
53+
let H0 = 0x67452301, H1 = 0xefcdab89, H2 = 0x98badcfe, H3 = 0x10325476, H4 = 0xc3d2e1f0;
54+
55+
// HASH COMPUTATION [§6.1.2]
56+
const W = new Array(80);
57+
for (let i = 0; i < N; i++) {
58+
// 1 - prepare message schedule 'W'
59+
for (let t = 0; t < 16; t++)
60+
W[t] = M[i][t];
61+
for (let t = 16; t < 80; t++)
62+
W[t] = rotl(W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1);
63+
64+
// 2 - initialise five working variables a, b, c, d, e with previous hash value
65+
let a = H0, b = H1, c = H2, d = H3, e = H4;
66+
67+
// 3 - main loop
68+
for (let t = 0; t < 80; t++) {
69+
const s = ~~(t / 20); // seq for blocks of 'f' functions and 'K' constants
70+
const T = (rotl(a, 5) + f(s, b, c, d) + e + K[s] + W[t]) & 0xffffffff;
71+
e = d;
72+
d = c;
73+
c = rotl(b, 30);
74+
b = a;
75+
a = T;
76+
}
77+
78+
// 4 - compute the new intermediate hash value (note 'addition modulo 2^32')
79+
H0 = (H0 + a) & 0xffffffff;
80+
H1 = (H1 + b) & 0xffffffff;
81+
H2 = (H2 + c) & 0xffffffff;
82+
H3 = (H3 + d) & 0xffffffff;
83+
H4 = (H4 + e) & 0xffffffff;
84+
}
85+
86+
return toHexStr(H0) + toHexStr(H1) + toHexStr(H2) + toHexStr(H3) + toHexStr(H4);
87+
}
88+
89+
/**
90+
* Function 'f' [§4.1.1].
91+
*/
92+
function f(s, x, y, z) {
93+
switch (s) {
94+
case 0:
95+
return (x & y) ^ (~x & z); // Ch()
96+
case 1:
97+
return x ^ y ^ z; // Parity()
98+
case 2:
99+
return (x & y) ^ (x & z) ^ (y & z); // Maj()
100+
case 3:
101+
return x ^ y ^ z; // Parity()
102+
}
103+
}
104+
105+
/**
106+
* Rotates left (circular left shift) value x by n positions [§3.2.5].
107+
*/
108+
function rotl(x, n) {
109+
return (x << n) | (x >>> (32 - n));
110+
}
111+
112+
/**
113+
* Hexadecimal representation of a number.
114+
*/
115+
function toHexStr(n) {
116+
// note can't use toString(16) as it is implementation-dependent,
117+
// and in IE returns signed numbers when used on full words
118+
let s = '';
119+
for (let i = 7; i >= 0; i--) {
120+
const v = (n >>> (i * 4)) & 0xf;
121+
s += v.toString(16);
122+
}
123+
return s;
124+
}

webpack/webpack.common.config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ module.exports = ({ outputDir }) => ({
2727
new CleanWebpackPlugin([outputDir]),
2828
// Use latest readable-stream version (as opposed to stream-browserify)
2929
new NormalModuleReplacementPlugin(/^stream$/, require.resolve('readable-stream/readable-browser')),
30+
// Shim crypto for smaller bundle size
31+
new NormalModuleReplacementPlugin(/^crypto$/, require.resolve('../browser/crypto')),
3032
// Shim process to use faster process.nextTick implementation
3133
new NormalModuleReplacementPlugin(/process\/browser\.js$/, require.resolve('../browser/process')),
3234
// Shim setImmediate to a faster implementation

0 commit comments

Comments
 (0)