1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116 | /**
* Incremental SHA-1. Workers' crypto.subtle is one-shot and async; pack
* streaming needs a running digest and the object database wants sync
* hashing, so we carry our own (git still speaks SHA-1 for object ids).
*/
export class Sha1 {
private h0 = 0x67452301 | 0;
private h1 = 0xefcdab89 | 0;
private h2 = 0x98badcfe | 0;
private h3 = 0x10325476 | 0;
private h4 = 0xc3d2e1f0 | 0;
private block = new Uint8Array(64);
private blockLen = 0;
private bytes = 0;
private w = new Int32Array(80);
/** Reinitialize so one instance can hash many inputs without reallocating. */
reset(): this {
this.h0 = 0x67452301 | 0;
this.h1 = 0xefcdab89 | 0;
this.h2 = 0x98badcfe | 0;
this.h3 = 0x10325476 | 0;
this.h4 = 0xc3d2e1f0 | 0;
this.blockLen = 0;
this.bytes = 0;
return this;
}
update(data: Uint8Array): this {
this.bytes += data.length;
let off = 0;
if (this.blockLen > 0) {
const need = 64 - this.blockLen;
const take = Math.min(need, data.length);
this.block.set(data.subarray(0, take), this.blockLen);
this.blockLen += take;
off = take;
if (this.blockLen === 64) {
this.compress(this.block, 0);
this.blockLen = 0;
}
}
while (off + 64 <= data.length) {
this.compress(data, off);
off += 64;
}
if (off < data.length) {
this.block.set(data.subarray(off), 0);
this.blockLen = data.length - off;
}
return this;
}
digest(): Uint8Array {
const bitLenHi = Math.floor((this.bytes * 8) / 0x100000000);
const bitLenLo = (this.bytes * 8) >>> 0;
const pad = new Uint8Array(((this.blockLen < 56 ? 56 : 120) - this.blockLen) + 8);
pad[0] = 0x80;
const dv = new DataView(pad.buffer);
dv.setUint32(pad.length - 8, bitLenHi);
dv.setUint32(pad.length - 4, bitLenLo);
this.update(pad);
const out = new Uint8Array(20);
const ov = new DataView(out.buffer);
ov.setInt32(0, this.h0);
ov.setInt32(4, this.h1);
ov.setInt32(8, this.h2);
ov.setInt32(12, this.h3);
ov.setInt32(16, this.h4);
return out;
}
private compress(buf: Uint8Array, off: number): void {
const w = this.w;
for (let i = 0; i < 16; i++) {
const j = off + i * 4;
w[i] = (buf[j] << 24) | (buf[j + 1] << 16) | (buf[j + 2] << 8) | buf[j + 3];
}
for (let i = 16; i < 80; i++) {
const n = w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16];
w[i] = (n << 1) | (n >>> 31);
}
let a = this.h0, b = this.h1, c = this.h2, d = this.h3, e = this.h4;
for (let i = 0; i < 80; i++) {
let f: number, k: number;
if (i < 20) {
f = (b & c) | (~b & d);
k = 0x5a827999;
} else if (i < 40) {
f = b ^ c ^ d;
k = 0x6ed9eba1;
} else if (i < 60) {
f = (b & c) | (b & d) | (c & d);
k = 0x8f1bbcdc | 0;
} else {
f = b ^ c ^ d;
k = 0xca62c1d6 | 0;
}
const t = (((a << 5) | (a >>> 27)) + f + e + k + w[i]) | 0;
e = d;
d = c;
c = (b << 30) | (b >>> 2);
b = a;
a = t;
}
this.h0 = (this.h0 + a) | 0;
this.h1 = (this.h1 + b) | 0;
this.h2 = (this.h2 + c) | 0;
this.h3 = (this.h3 + d) | 0;
this.h4 = (this.h4 + e) | 0;
}
}
export function sha1(data: Uint8Array): Uint8Array {
return new Sha1().update(data).digest();
}
|