馃 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) => (x & y) ^ (x & z) ^ (y & z);
27 const s0 = (x: number) => rotr(x, 2) ^ rotr(x, 13) ^ rotr(x, 22);
28 const s1 = (x: number) => rotr(x, 6) ^ rotr(x, 11) ^ rotr(x, 25);
29 const g0 = (x: number) => rotr(x, 7) ^ rotr(x, 18) ^ (x >>> 3);
30 const g1 = (x: number) => rotr(x, 17) ^ rotr(x, 19) ^ (x >>> 10);
31
32 // Pad message
33 const msgLen = data.length;
34 const bitLen = msgLen * 8;
35 const padLen = msgLen + 1 + ((119 - msgLen) % 64);
36 const padded = new Uint8Array(padLen + 8);
37 padded.set(data);
38 padded[msgLen] = 0x80;
39 new DataView(padded.buffer).setUint32(padLen + 4, bitLen, false);
40
41 // Initialize hash
42 let h0 = 0x6a09e667;
43 let h1 = 0xbb67ae85;
44 let h2 = 0x3c6ef372;
45 let h3 = 0xa54ff53a;
46 let h4 = 0x510e527f;
47 let h5 = 0x9b05688c;
48 let h6 = 0x1f83d9ab;
49 let h7 = 0x5be0cd19;
50
51 // Process blocks
52 const w = new Uint32Array(64);
53 for (let i = 0; i < padded.length; i += 64) {
54 const view = new DataView(padded.buffer, i, 64);
55 for (let j = 0; j < 16; j++) w[j] = view.getUint32(j * 4, false);
56 for (let j = 16; j < 64; j++)
57 w[j] = (g1(w[j - 2]) + w[j - 7] + g0(w[j - 15]) + w[j - 16]) | 0;
58
59 let a = h0,
60 b = h1,
61 c = h2,
62 d = h3,
63 e = h4,
64 f = h5,
65 g = h6,
66 h = h7;
67
68 for (let j = 0; j < 64; j++) {
69 const t1 = (h + s1(e) + ch(e, f, g) + K[j] + w[j]) | 0;
70 const t2 = (s0(a) + maj(a, b, c)) | 0;
71 h = g;
72 g = f;
73 f = e;
74 e = (d + t1) | 0;
75 d = c;
76 c = b;
77 b = a;
78 a = (t1 + t2) | 0;
79 }
80
81 h0 = (h0 + a) | 0;
82 h1 = (h1 + b) | 0;
83 h2 = (h2 + c) | 0;
84 h3 = (h3 + d) | 0;
85 h4 = (h4 + e) | 0;
86 h5 = (h5 + f) | 0;
87 h6 = (h6 + g) | 0;
88 h7 = (h7 + h) | 0;
89 }
90
91 const result = new Uint8Array(32);
92 const view = new DataView(result.buffer);
93 view.setUint32(0, h0, false);
94 view.setUint32(4, h1, false);
95 view.setUint32(8, h2, false);
96 view.setUint32(12, h3, false);
97 view.setUint32(16, h4, false);
98 view.setUint32(20, h5, false);
99 view.setUint32(24, h6, false);
100 view.setUint32(28, h7, false);
101
102 return result;
103}
104
105/**
106 * HMAC-SHA256 using pure JS SHA-256.
107 */
108async function hmac(key: Uint8Array, data: Uint8Array): Promise<Uint8Array> {
109 const blockSize = 64;
110 const opad = new Uint8Array(blockSize).fill(0x5c);
111 const ipad = new Uint8Array(blockSize).fill(0x36);
112
113 if (key.length > blockSize) {
114 key = await sha256(key);
115 }
116
117 const keyPadded = new Uint8Array(blockSize);
118 keyPadded.set(key);
119
120 for (let i = 0; i < blockSize; i++) {
121 opad[i] ^= keyPadded[i];
122 ipad[i] ^= keyPadded[i];
123 }
124
125 const inner = new Uint8Array(blockSize + data.length);
126 inner.set(ipad);
127 inner.set(data, blockSize);
128
129 const innerHash = await sha256(inner);
130
131 const outer = new Uint8Array(blockSize + 32);
132 outer.set(opad);
133 outer.set(innerHash, blockSize);
134
135 return sha256(outer);
136}
137
138/**
139 * Pure JS PBKDF2-HMAC-SHA256 implementation.
140 */
141export async function pbkdf2Fallback(
142 password: Uint8Array,
143 salt: Uint8Array,
144 iterations: number,
145): Promise<Uint8Array> {
146 const dkLen = 32; // 256 bits
147 const hLen = 32; // SHA-256 output length
148 const l = Math.ceil(dkLen / hLen);
149 const r = dkLen - (l - 1) * hLen;
150
151 const dk = new Uint8Array(dkLen);
152
153 for (let i = 1; i <= l; i++) {
154 const saltInt = new Uint8Array(salt.length + 4);
155 saltInt.set(salt);
156 new DataView(saltInt.buffer).setUint32(salt.length, i, false);
157
158 let u = await hmac(password, saltInt);
159 const t = new Uint8Array(u);
160
161 for (let j = 1; j < iterations; j++) {
162 u = await hmac(password, u);
163 for (let k = 0; k < hLen; k++) {
164 t[k] ^= u[k];
165 }
166 }
167
168 const offset = (i - 1) * hLen;
169 const len = i === l ? r : hLen;
170 dk.set(t.subarray(0, len), offset);
171 }
172
173 return dk;
174}