Skip to content

Commit 944415f

Browse files
🚧 progress: Import existing code.
1 parent 3532939 commit 944415f

File tree

6 files changed

+9364
-8
lines changed

6 files changed

+9364
-8
lines changed

package.json

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,17 +68,22 @@
6868
"test:module": "IMPORT_MAP_PATH=test/import-maps/dist/index.module.json npm run test-cmd",
6969
"test:src": "IMPORT_MAP_PATH=test/import-maps/src/index.json npm run test-cmd"
7070
},
71-
"dependencies": {},
71+
"dependencies": {
72+
"@arithmetic-type/uint32": "^2.0.0"
73+
},
7274
"devDependencies": {
75+
"@array-like/alloc": "0.0.1",
7376
"@babel/core": "7.19.0",
7477
"@babel/plugin-transform-destructuring": "7.18.13",
7578
"@babel/plugin-transform-for-of": "7.18.8",
7679
"@babel/preset-env": "7.19.0",
80+
"@codec-bytes/ascii": "3.0.0",
7781
"@commitlint/cli": "17.1.2",
7882
"@js-library/commitlint-config": "0.0.4",
7983
"@node-loader/babel": "2.0.1",
8084
"@node-loader/core": "2.0.0",
8185
"@node-loader/import-maps": "1.1.0",
86+
"@set-theory/cartesian-product": "2.0.2",
8287
"ava": "4.3.3",
8388
"babel-plugin-transform-remove-console": "6.9.4",
8489
"babel-plugin-unassert": "3.2.0",
@@ -225,6 +230,15 @@
225230
"doc/**"
226231
],
227232
"env": "browser"
233+
},
234+
{
235+
"files": [
236+
"src/**"
237+
],
238+
"rules": {
239+
"no-bitwise": "off",
240+
"unicorn/prefer-math-trunc": "off"
241+
}
228242
}
229243
]
230244
}

src/index.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
const answer = 42;
2-
export default answer;
1+
export * from './sha1.js';

src/sha1.js

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
import {get32, big32, rotl32, add32} from '@arithmetic-type/uint32';
2+
3+
function cycle(h, w) {
4+
// Initialize hash value for this chunk:
5+
let a = h[0];
6+
let b = h[1];
7+
let c = h[2];
8+
let d = h[3];
9+
let e = h[4]; // eslint-disable-line unicorn/prevent-abbreviations
10+
11+
// Main loop:[35]
12+
// for j from 0 to 79
13+
for (let j = 0; j < 80; ++j) {
14+
let f;
15+
let k;
16+
17+
// If 0 ≤ j ≤ 19 then
18+
if (j >= 0 && j <= 19) {
19+
// F = (b and c) or ((not b) and d)
20+
f = (b & c) | (~b & d);
21+
k = 1_518_500_249; // 0x5A827999
22+
}
23+
// Else if 20 ≤ j ≤ 39
24+
else if (j >= 20 && j <= 39) {
25+
// F = b xor c xor d
26+
f = b ^ c ^ d;
27+
k = 1_859_775_393; // 0x6ED9EBA1
28+
}
29+
// Else if 40 ≤ j ≤ 59
30+
else if (j >= 40 && j <= 59) {
31+
// F = (b and c) or (b and d) or (c and d)
32+
f = (b & c) | (b & d) | (c & d);
33+
k = -1_894_007_588; // 0x8F1BBCDC
34+
}
35+
// Else if 60 ≤ j ≤ 79
36+
else {
37+
// F = b xor c xor d
38+
f = b ^ c ^ d;
39+
k = -899_497_514; // 0xCA62C1D6
40+
}
41+
42+
// T = (a leftrotate 5) + f + e + k + w[j]
43+
const t = add32(add32(rotl32(a, 5), f), add32(add32(e, k), w[j]));
44+
e = d;
45+
d = c;
46+
// C = b leftrotate 30
47+
c = rotl32(b, 30);
48+
b = a;
49+
a = t;
50+
}
51+
52+
// Add this chunk's hash to result so far:
53+
h[0] = add32(h[0], a);
54+
h[1] = add32(h[1], b);
55+
h[2] = add32(h[2], c);
56+
h[3] = add32(h[3], d);
57+
h[4] = add32(h[4], e);
58+
}
59+
60+
function call(h, data, o) {
61+
const w = Array.from({length: 80});
62+
63+
// Break chunk into sixteen 32-bit big-endian words w[i], 0 ≤ i ≤ 15
64+
for (let j = 0; j < 16; ++j) {
65+
w[j] = big32(data, o + j * 4);
66+
}
67+
68+
// Extend the sixteen 32-bit words into eighty 32-bit words:
69+
// for j from 16 to 79
70+
for (let j = 16; j < 80; ++j) {
71+
// W[j] = (w[j-3] xor w[j-8] xor w[j-14] xor w[j-16]) leftrotate 1
72+
const k = w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16];
73+
w[j] = rotl32(k, 1);
74+
}
75+
76+
cycle(h, w);
77+
}
78+
79+
/**
80+
* SHA1
81+
*/
82+
export function sha1(bytes, n, digest) {
83+
// PREPARE
84+
85+
const q = (n / 8) | 0;
86+
const z = q * 8;
87+
const u = n - z;
88+
89+
// Append the bit '1' to the message
90+
const last = u > 0 ? bytes[q] & (~0 << (7 - u)) : 0x80;
91+
92+
// Note 1: All variables are unsigned 32 bits and wrap modulo 2^32 when calculating
93+
// Note 2: All constants in this pseudo code are in big endian.
94+
// Within each word, the most significant byte is stored in the leftmost byte position
95+
96+
// Initialize state:
97+
const h = [
98+
get32(0x67_45_23_01),
99+
get32(0xef_cd_ab_89),
100+
get32(0x98_ba_dc_fe),
101+
get32(0x10_32_54_76),
102+
get32(0xc3_d2_e1_f0),
103+
];
104+
105+
// Process the message in successive 512-bit chunks:
106+
// break message into 512-bit chunks
107+
108+
const m = (n / 512) | 0;
109+
const y = ((n - 512 * m) / 8) | 0;
110+
111+
// Offset in data
112+
let o = 0;
113+
114+
// For each chunk
115+
for (let j = 0; j < m; ++j, o += 64) {
116+
call(h, bytes, o);
117+
}
118+
119+
// Last bytes + padding + length
120+
let tail = [];
121+
122+
// Last bytes
123+
for (let j = 0; j < y; ++j) {
124+
tail.push(bytes[o + j]);
125+
}
126+
127+
// Special care taken for the very last byte which could
128+
// have been modified if n is not a multiple of 8
129+
tail.push(last);
130+
131+
// Append 0 ≤ k < 512 bits '0', so that the resulting
132+
// message length (in bits) is congruent to 448 (mod 512)
133+
let zeroes = ((448 - ((n + 1) % 512)) / 8) | 0;
134+
135+
if (zeroes < 0) {
136+
// We need an additional block as there is
137+
// not enough space left to append
138+
// the length of the data in bits
139+
140+
for (let j = 0; j < -zeroes; ++j) {
141+
tail.push(0);
142+
}
143+
144+
call(h, tail, 0);
145+
146+
zeroes = 448 / 8;
147+
tail = [];
148+
}
149+
150+
// Pad with zeroes
151+
for (let j = 0; j < zeroes; ++j) {
152+
tail.push(0);
153+
}
154+
155+
// Append length of message (before preparation), in bits,
156+
// as 64-bit big-endian integer
157+
158+
// JavaScript works with 32 bit integers.
159+
// tail.push((n >>> 56) & 0xFF);
160+
// tail.push((n >>> 48) & 0xFF);
161+
// tail.push((n >>> 40) & 0xFF);
162+
// tail.push((n >>> 32) & 0xFF);
163+
tail.push(
164+
0,
165+
0,
166+
0,
167+
0,
168+
(n >>> 24) & 0xff,
169+
(n >>> 16) & 0xff,
170+
(n >>> 8) & 0xff,
171+
(n >>> 0) & 0xff,
172+
);
173+
174+
call(h, tail, 0);
175+
176+
digest[0] = (h[0] >>> 24) & 0xff;
177+
digest[1] = (h[0] >>> 16) & 0xff;
178+
digest[2] = (h[0] >>> 8) & 0xff;
179+
digest[3] = (h[0] >>> 0) & 0xff;
180+
digest[4] = (h[1] >>> 24) & 0xff;
181+
digest[5] = (h[1] >>> 16) & 0xff;
182+
digest[6] = (h[1] >>> 8) & 0xff;
183+
digest[7] = (h[1] >>> 0) & 0xff;
184+
digest[8] = (h[2] >>> 24) & 0xff;
185+
digest[9] = (h[2] >>> 16) & 0xff;
186+
digest[10] = (h[2] >>> 8) & 0xff;
187+
digest[11] = (h[2] >>> 0) & 0xff;
188+
digest[12] = (h[3] >>> 24) & 0xff;
189+
digest[13] = (h[3] >>> 16) & 0xff;
190+
digest[14] = (h[3] >>> 8) & 0xff;
191+
digest[15] = (h[3] >>> 0) & 0xff;
192+
digest[16] = (h[4] >>> 24) & 0xff;
193+
digest[17] = (h[4] >>> 16) & 0xff;
194+
digest[18] = (h[4] >>> 8) & 0xff;
195+
digest[19] = (h[4] >>> 0) & 0xff;
196+
197+
return digest;
198+
}

test/src/api.js

Lines changed: 0 additions & 5 deletions
This file was deleted.

test/src/sha1.js

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import test from 'ava';
2+
import {alloc} from '@array-like/alloc';
3+
import {product} from '@set-theory/cartesian-product';
4+
import * as ascii from '@codec-bytes/ascii';
5+
6+
import * as hash from '#module';
7+
8+
function macro(t, [[sha1name, sha1], [string, expected]]) {
9+
const digest = sha1(ascii.encode(string), string.length * 8, alloc(20));
10+
11+
t.deepEqual(digest, expected, `${sha1name} ${string}`);
12+
}
13+
14+
macro.title = (title, [[f], [x, y]]) =>
15+
title ?? `${f}(${JSON.stringify(x)}) == ${JSON.stringify(y)}`;
16+
17+
const inputs = product(
18+
[
19+
[['sha1', hash.sha1]],
20+
21+
[
22+
[
23+
'The quick brown fox jumps over the lazy dog',
24+
[
25+
0x2f, 0xd4, 0xe1, 0xc6, 0x7a, 0x2d, 0x28, 0xfc, 0xed, 0x84, 0x9e,
26+
0xe1, 0xbb, 0x76, 0xe7, 0x39, 0x1b, 0x93, 0xeb, 0x12,
27+
],
28+
],
29+
[
30+
'The quick brown fox jumps over the lazy cog',
31+
[
32+
0xde, 0x9f, 0x2c, 0x7f, 0xd2, 0x5e, 0x1b, 0x3a, 0xfa, 0xd3, 0xe8,
33+
0x5a, 0x0b, 0xd1, 0x7d, 0x9b, 0x10, 0x0d, 0xb4, 0xb3,
34+
],
35+
],
36+
[
37+
'',
38+
[
39+
0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, 0xbf,
40+
0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09,
41+
],
42+
],
43+
[
44+
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
45+
[
46+
0xcd, 0x36, 0xb3, 0x70, 0x75, 0x8a, 0x25, 0x9b, 0x34, 0x84, 0x50,
47+
0x84, 0xa6, 0xcc, 0x38, 0x47, 0x3c, 0xb9, 0x5e, 0x27,
48+
],
49+
],
50+
[
51+
'apple',
52+
[
53+
0xd0, 0xbe, 0x2d, 0xc4, 0x21, 0xbe, 0x4f, 0xcd, 0x01, 0x72, 0xe5,
54+
0xaf, 0xce, 0xea, 0x39, 0x70, 0xe2, 0xf3, 0xd9, 0x40,
55+
],
56+
],
57+
[
58+
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do',
59+
[
60+
0x69, 0x45, 0xdf, 0xee, 0x74, 0x11, 0xa3, 0x7b, 0x37, 0xcd, 0x6b,
61+
0x0f, 0x47, 0xb0, 0xb2, 0x82, 0x64, 0x0d, 0xf1, 0x96,
62+
],
63+
],
64+
[
65+
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do ',
66+
[
67+
0x15, 0xf5, 0xc9, 0x56, 0xef, 0x13, 0xd8, 0x6f, 0xaa, 0xf4, 0xd7,
68+
0x44, 0xae, 0xd4, 0xb0, 0x6b, 0x11, 0x5f, 0x5b, 0x06,
69+
],
70+
],
71+
[
72+
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do e',
73+
[
74+
0xe8, 0x79, 0x1d, 0xfb, 0x0c, 0xa2, 0x63, 0x71, 0x21, 0x4f, 0x62,
75+
0x92, 0x01, 0x16, 0x43, 0x07, 0xd3, 0x13, 0x29, 0xe0,
76+
],
77+
],
78+
],
79+
],
80+
1,
81+
);
82+
83+
for (const x of inputs) test(macro, x);

0 commit comments

Comments
 (0)