Cleanup - unused files / unused exports / duplicate exports
[ProtonMail-WebClient.git] / packages / shared / lib / pow / pbkdfWorker.ts
blob052324039fa01d38aacbe5cd9c38a693a2e8def4
1 import { uint8ArrayToBase64String } from '../helpers/encoding';
3 const Pbkdf2PRFKeySize = 32;
4 const Pbkdf2ChallengeSize = 3 * Pbkdf2PRFKeySize + 32 + 4;
5 const Pbkdf2OutputSize = 32;
6 const sha256Size = 32;
7 function areBuffersEqual(buf1: ArrayBuffer, buf2: ArrayBuffer): boolean {
8     if (buf1.byteLength !== buf2.byteLength) {
9         return false;
10     }
11     const dv1 = new Uint8Array(buf1);
12     const dv2 = new Uint8Array(buf2);
13     for (let i = 0; i !== buf1.byteLength; i++) {
14         if (dv1[i] !== dv2[i]) {
15             return false;
16         }
17     }
18     return true;
20 async function solveChallengePbkdf2Preimage(b64challenge: string, deadlineUnixMilli: number) {
21     const buffer = new ArrayBuffer(8);
22     const challenge = Uint8Array.from(atob(b64challenge), (c) => c.charCodeAt(0));
23     if (challenge.length !== Pbkdf2ChallengeSize) {
24         throw new Error('Invalid challenge length');
25     }
26     const prfKeys = challenge.subarray(0, 3 * Pbkdf2PRFKeySize);
27     const goal = challenge.subarray(3 * Pbkdf2PRFKeySize, 3 * Pbkdf2PRFKeySize + sha256Size);
28     const pbkdf2Params = challenge.subarray(3 * Pbkdf2PRFKeySize + sha256Size, Pbkdf2ChallengeSize);
29     const iterations = new DataView(pbkdf2Params.buffer, pbkdf2Params.byteOffset, pbkdf2Params.byteLength).getUint32(
30         0,
31         true
32     );
33     const startTime = Date.now();
34     let stage: ArrayBuffer = new ArrayBuffer(0);
35     let i: number = 0;
36     const prePRFKey = await crypto.subtle.importKey(
37         'raw',
38         prfKeys.subarray(0, Pbkdf2PRFKeySize),
39         { name: 'HMAC', hash: 'SHA-256' },
40         false,
41         ['sign']
42     );
43     const postPRFKey = await crypto.subtle.importKey(
44         'raw',
45         prfKeys.subarray(2 * Pbkdf2PRFKeySize),
46         { name: 'HMAC', hash: 'SHA-256' },
47         false,
48         ['sign']
49     );
50     const salt = prfKeys.subarray(Pbkdf2PRFKeySize, 2 * Pbkdf2PRFKeySize);
51     while (true) {
52         if (Date.now() - startTime > deadlineUnixMilli) {
53             throw new Error('Operation timed out');
54         }
55         new DataView(buffer).setUint32(0, i, true);
56         const prePRFHash = await crypto.subtle.sign('HMAC', prePRFKey, buffer);
57         const derivedKey = await crypto.subtle.importKey('raw', prePRFHash, 'PBKDF2', false, ['deriveBits']);
58         stage = await crypto.subtle.deriveBits(
59             {
60                 name: 'PBKDF2',
61                 salt: salt,
62                 iterations: iterations,
63                 hash: 'SHA-256',
64             },
65             derivedKey,
66             Pbkdf2OutputSize * 8
67         );
68         const postPRFHash = await crypto.subtle.sign('HMAC', postPRFKey, stage);
69         if (areBuffersEqual(postPRFHash, goal)) {
70             break;
71         }
72         i++;
73     }
74     let solution = new Uint8Array(buffer.byteLength + stage.byteLength);
75     solution.set(new Uint8Array(buffer), 0);
76     solution.set(new Uint8Array(stage), buffer.byteLength);
78     const duration = Date.now() - startTime;
79     return { solution, duration };
82 self.onmessage = async function (event) {
83     const { b64Source } = event.data;
85     const result = await solveChallengePbkdf2Preimage(b64Source, 15000);
86     const b64 = uint8ArrayToBase64String(result.solution);
87     self.postMessage(b64 + `, ${result.duration}`);