馃 distributed transcription service
thistle.dunkirk.sh
1/**
2 * Pure JavaScript crypto implementations for non-secure contexts.
3 * Only used when crypto.subtle is unavailable (HTTP non-localhost).
4 */
5
6/**
7 * Pure JS SHA-256 implementation.
8 */
9export async function sha256(data: Uint8Array): Promise<Uint8Array> {
10 const K = new Uint32Array([
11 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
12 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
13 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
14 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
15 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
16 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
17 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
18 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
19 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
20 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
21 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
22 ]);
23
24 const rotr = (x: number, n: number) => (x >>> n) | (x << (32 - n));
25 const ch = (x: number, y: number, z: number) => (x & y) ^ (~x & z);
26 const maj = (x: number, y: number, z: number) =>
27 (x & y) ^ (x & z) ^ (y & z);
28 const s0 = (x: number) => rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22);
29 const s1 = (x: number) => rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25);
30 const g0 = (x: number) => rotr(x, 7) ^ rotr(x, 18) ^ (x >>> 3);
31 const g1 = (x: number) => rotr(x, 17) ^ rotr(x, 19) ^ (x >>> 10);
32
33 // Pad message
34 const msgLen = data.length;
35 const bitLen = msgLen * 8;
36 const padLen = msgLen + 1 + ((119 - msgLen) % 64);
37 const padded = new Uint8Array(padLen + 8);
38 padded.set(data);
39 padded[msgLen] = 0x80;
40 new DataView(padded.buffer).setUint32(padLen + 4, bitLen, false);
41
42 // Initialize hash
43 let h0 = 0x6a09e667;
44 let h1 = 0xbb67ae85;
45 let h2 = 0x3c6ef372;
46 let h3 = 0xa54ff53a;
47 let h4 = 0x510e527f;
48 let h5 = 0x9b05688c;
49 let h6 = 0x1f83d9ab;
50 let h7 = 0x5be0cd19;
51
52 // Process blocks
53 const w = new Uint32Array(64);
54 for (let i = 0; i < padded.length; i += 64) {
55 const view = new DataView(padded.buffer, i, 64);
56 for (let j = 0; j < 16; j++) w[j] = view.getUint32(j * 4, false);
57 for (let j = 16; j < 64; j++)
58 w[j] = (g1(w[j - 2]) + w[j - 7] + g0(w[j - 15]) + w[j - 16]) | 0;
59
60 let a = h0,
61 b = h1,
62 c = h2,
63 d = h3,
64 e = h4,
65 f = h5,
66 g = h6,
67 h = h7;
68
69 for (let j = 0; j < 64; j++) {
70 const t1 = (h + s1(e) + ch(e, f, g) + K[j] + w[j]) | 0;
71 const t2 = (s0(a) + maj(a, b, c)) | 0;
72 h = g;
73 g = f;
74 f = e;
75 e = (d + t1) | 0;
76 d = c;
77 c = b;
78 b = a;
79 a = (t1 + t2) | 0;
80 }
81
82 h0 = (h0 + a) | 0;
83 h1 = (h1 + b) | 0;
84 h2 = (h2 + c) | 0;
85 h3 = (h3 + d) | 0;
86 h4 = (h4 + e) | 0;
87 h5 = (h5 + f) | 0;
88 h6 = (h6 + g) | 0;
89 h7 = (h7 + h) | 0;
90 }
91
92 const result = new Uint8Array(32);
93 const view = new DataView(result.buffer);
94 view.setUint32(0, h0, false);
95 view.setUint32(4, h1, false);
96 view.setUint32(8, h2, false);
97 view.setUint32(12, h3, false);
98 view.setUint32(16, h4, false);
99 view.setUint32(20, h5, false);
100 view.setUint32(24, h6, false);
101 view.setUint32(28, h7, false);
102
103 return result;
104}
105
106/**
107 * HMAC-SHA256 using pure JS SHA-256.
108 */
109async function hmac(key: Uint8Array, data: Uint8Array): Promise<Uint8Array> {
110 const blockSize = 64;
111 const opad = new Uint8Array(blockSize).fill(0x5c);
112 const ipad = new Uint8Array(blockSize).fill(0x36);
113
114 if (key.length > blockSize) {
115 key = await sha256(key);
116 }
117
118 const keyPadded = new Uint8Array(blockSize);
119 keyPadded.set(key);
120
121 for (let i = 0; i < blockSize; i++) {
122 opad[i] ^= keyPadded[i];
123 ipad[i] ^= keyPadded[i];
124 }
125
126 const inner = new Uint8Array(blockSize + data.length);
127 inner.set(ipad);
128 inner.set(data, blockSize);
129
130 const innerHash = await sha256(inner);
131
132 const outer = new Uint8Array(blockSize + 32);
133 outer.set(opad);
134 outer.set(innerHash, blockSize);
135
136 return sha256(outer);
137}
138
139/**
140 * Pure JS PBKDF2-HMAC-SHA256 implementation.
141 */
142export async function pbkdf2Fallback(
143 password: Uint8Array,
144 salt: Uint8Array,
145 iterations: number,
146): Promise<Uint8Array> {
147 const dkLen = 32; // 256 bits
148 const hLen = 32; // SHA-256 output length
149 const l = Math.ceil(dkLen / hLen);
150 const r = dkLen - (l - 1) * hLen;
151
152 const dk = new Uint8Array(dkLen);
153
154 for (let i = 1; i <= l; i++) {
155 const saltInt = new Uint8Array(salt.length + 4);
156 saltInt.set(salt);
157 new DataView(saltInt.buffer).setUint32(salt.length, i, false);
158
159 let u = await hmac(password, saltInt);
160 const t = new Uint8Array(u);
161
162 for (let j = 1; j < iterations; j++) {
163 u = await hmac(password, u);
164 for (let k = 0; k < hLen; k++) {
165 t[k] ^= u[k];
166 }
167 }
168
169 const offset = (i - 1) * hLen;
170 const len = i === l ? r : hLen;
171 dk.set(t.subarray(0, len), offset);
172 }
173
174 return dk;
175}