leviathan-crypto 1.4.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +129 -94
- package/README.md +166 -223
- package/SECURITY.md +85 -45
- package/dist/chacha20/cipher-suite.d.ts +4 -0
- package/dist/chacha20/cipher-suite.js +78 -0
- package/dist/chacha20/embedded.d.ts +1 -0
- package/dist/chacha20/embedded.js +27 -0
- package/dist/chacha20/index.d.ts +20 -27
- package/dist/chacha20/index.js +40 -59
- package/dist/chacha20/ops.d.ts +1 -1
- package/dist/chacha20/ops.js +19 -18
- package/dist/chacha20/pool-worker.js +77 -0
- package/dist/ct-wasm.d.ts +1 -0
- package/dist/ct-wasm.js +3 -0
- package/dist/ct.wasm +0 -0
- package/dist/docs/aead.md +320 -0
- package/dist/docs/architecture.md +419 -285
- package/dist/docs/argon2id.md +42 -30
- package/dist/docs/chacha20.md +192 -266
- package/dist/docs/exports.md +241 -0
- package/dist/docs/fortuna.md +60 -69
- package/dist/docs/init.md +172 -178
- package/dist/docs/loader.md +87 -142
- package/dist/docs/serpent.md +134 -583
- package/dist/docs/sha2.md +91 -103
- package/dist/docs/sha3.md +70 -36
- package/dist/docs/types.md +93 -16
- package/dist/docs/utils.md +109 -32
- package/dist/embedded/kyber.d.ts +1 -0
- package/dist/embedded/kyber.js +3 -0
- package/dist/errors.d.ts +10 -0
- package/dist/errors.js +38 -0
- package/dist/fortuna.d.ts +0 -6
- package/dist/fortuna.js +5 -5
- package/dist/index.d.ts +25 -9
- package/dist/index.js +36 -7
- package/dist/init.d.ts +3 -7
- package/dist/init.js +18 -35
- package/dist/keccak/embedded.d.ts +1 -0
- package/dist/keccak/embedded.js +27 -0
- package/dist/keccak/index.d.ts +4 -0
- package/dist/keccak/index.js +31 -0
- package/dist/kyber/embedded.d.ts +1 -0
- package/dist/kyber/embedded.js +27 -0
- package/dist/kyber/indcpa.d.ts +49 -0
- package/dist/kyber/indcpa.js +352 -0
- package/dist/kyber/index.d.ts +38 -0
- package/dist/kyber/index.js +150 -0
- package/dist/kyber/kem.d.ts +21 -0
- package/dist/kyber/kem.js +160 -0
- package/dist/kyber/params.d.ts +14 -0
- package/dist/kyber/params.js +37 -0
- package/dist/kyber/suite.d.ts +13 -0
- package/dist/kyber/suite.js +93 -0
- package/dist/kyber/types.d.ts +98 -0
- package/dist/kyber/types.js +25 -0
- package/dist/kyber/validate.d.ts +19 -0
- package/dist/kyber/validate.js +68 -0
- package/dist/kyber.wasm +0 -0
- package/dist/loader.d.ts +15 -6
- package/dist/loader.js +65 -21
- package/dist/serpent/cipher-suite.d.ts +4 -0
- package/dist/serpent/cipher-suite.js +121 -0
- package/dist/serpent/embedded.d.ts +1 -0
- package/dist/serpent/embedded.js +27 -0
- package/dist/serpent/index.d.ts +6 -37
- package/dist/serpent/index.js +9 -118
- package/dist/serpent/pool-worker.d.ts +1 -0
- package/dist/serpent/pool-worker.js +202 -0
- package/dist/serpent/serpent-cbc.d.ts +30 -0
- package/dist/serpent/serpent-cbc.js +136 -0
- package/dist/sha2/embedded.d.ts +1 -0
- package/dist/sha2/embedded.js +27 -0
- package/dist/sha2/hkdf.js +6 -2
- package/dist/sha2/index.d.ts +3 -2
- package/dist/sha2/index.js +3 -4
- package/dist/sha3/embedded.d.ts +1 -0
- package/dist/sha3/embedded.js +27 -0
- package/dist/sha3/index.d.ts +3 -2
- package/dist/sha3/index.js +3 -4
- package/dist/stream/constants.d.ts +6 -0
- package/dist/stream/constants.js +30 -0
- package/dist/stream/header.d.ts +9 -0
- package/dist/stream/header.js +77 -0
- package/dist/stream/index.d.ts +7 -0
- package/dist/stream/index.js +27 -0
- package/dist/stream/open-stream.d.ts +21 -0
- package/dist/stream/open-stream.js +146 -0
- package/dist/stream/seal-stream-pool.d.ts +38 -0
- package/dist/stream/seal-stream-pool.js +391 -0
- package/dist/stream/seal-stream.d.ts +20 -0
- package/dist/stream/seal-stream.js +142 -0
- package/dist/stream/seal.d.ts +9 -0
- package/dist/stream/seal.js +75 -0
- package/dist/stream/types.d.ts +24 -0
- package/dist/stream/types.js +26 -0
- package/dist/utils.d.ts +7 -2
- package/dist/utils.js +49 -3
- package/dist/wasm-source.d.ts +12 -0
- package/dist/wasm-source.js +26 -0
- package/package.json +13 -5
- package/dist/chacha20/pool.d.ts +0 -52
- package/dist/chacha20/pool.js +0 -178
- package/dist/chacha20/pool.worker.js +0 -37
- package/dist/chacha20/stream-sealer.d.ts +0 -49
- package/dist/chacha20/stream-sealer.js +0 -327
- package/dist/docs/chacha20_pool.md +0 -309
- package/dist/docs/wasm.md +0 -194
- package/dist/serpent/seal.d.ts +0 -8
- package/dist/serpent/seal.js +0 -72
- package/dist/serpent/stream-pool.d.ts +0 -48
- package/dist/serpent/stream-pool.js +0 -275
- package/dist/serpent/stream-sealer.d.ts +0 -55
- package/dist/serpent/stream-sealer.js +0 -342
- package/dist/serpent/stream.d.ts +0 -28
- package/dist/serpent/stream.js +0 -205
- package/dist/serpent/stream.worker.d.ts +0 -32
- package/dist/serpent/stream.worker.js +0 -117
- /package/dist/chacha20/{pool.worker.d.ts → pool-worker.d.ts} +0 -0
package/dist/serpent/stream.d.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { SerpentCtr } from './index.js';
|
|
2
|
-
import { HMAC_SHA256, HKDF_SHA256 } from '../sha2/index.js';
|
|
3
|
-
export declare function u32be(n: number): Uint8Array;
|
|
4
|
-
export declare function u64be(n: number): Uint8Array;
|
|
5
|
-
export declare function chunkInfo(streamNonce: Uint8Array, chunkSize: number, chunkCount: number, index: number, isLast: boolean): Uint8Array;
|
|
6
|
-
export declare function deriveChunkKeys(hkdf: HKDF_SHA256, masterKey: Uint8Array, streamNonce: Uint8Array, chunkSize: number, chunkCount: number, index: number, isLast: boolean): {
|
|
7
|
-
encKey: Uint8Array;
|
|
8
|
-
macKey: Uint8Array;
|
|
9
|
-
};
|
|
10
|
-
/**
|
|
11
|
-
* Encrypt one chunk. Returns ciphertext || hmac_tag (32 bytes).
|
|
12
|
-
* Does not generate keys -- caller provides encKey and macKey.
|
|
13
|
-
*/
|
|
14
|
-
export declare function sealChunk(ctr: SerpentCtr, hmac: HMAC_SHA256, encKey: Uint8Array, macKey: Uint8Array, chunk: Uint8Array): Uint8Array;
|
|
15
|
-
/**
|
|
16
|
-
* Decrypt one chunk. Throws 'SerpentStream: authentication failed' on bad tag.
|
|
17
|
-
* Returns plaintext.
|
|
18
|
-
*/
|
|
19
|
-
export declare function openChunk(ctr: SerpentCtr, hmac: HMAC_SHA256, encKey: Uint8Array, macKey: Uint8Array, wire: Uint8Array): Uint8Array;
|
|
20
|
-
export declare class SerpentStream {
|
|
21
|
-
private readonly _ctr;
|
|
22
|
-
private readonly _hmac;
|
|
23
|
-
private readonly _hkdf;
|
|
24
|
-
constructor();
|
|
25
|
-
seal(key: Uint8Array, plaintext: Uint8Array, chunkSize?: number, _nonce?: Uint8Array): Uint8Array;
|
|
26
|
-
open(key: Uint8Array, ciphertext: Uint8Array): Uint8Array;
|
|
27
|
-
dispose(): void;
|
|
28
|
-
}
|
package/dist/serpent/stream.js
DELETED
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
// ▄▄▄▄▄▄▄▄▄▄
|
|
2
|
-
// ▄████████████████████▄▄ ▒ ▄▀▀ ▒ ▒ █ ▄▀▄ ▀█▀ █ ▒ ▄▀▄ █▀▄
|
|
3
|
-
// ▄██████████████████████ ▀████▄ ▓ ▓▀ ▓ ▓ ▓ ▓▄▓ ▓ ▓▀▓ ▓▄▓ ▓ ▓
|
|
4
|
-
// ▄█████████▀▀▀ ▀███████▄▄███████▌ ▀▄ ▀▄▄ ▀▄▀ ▒ ▒ ▒ ▒ ▒ █ ▒ ▒ ▒ █
|
|
5
|
-
// ▐████████▀ ▄▄▄▄ ▀████████▀██▀█▌
|
|
6
|
-
// ████████ ███▀▀ ████▀ █▀ █▀ Leviathan Crypto Library
|
|
7
|
-
// ███████▌ ▀██▀ ███
|
|
8
|
-
// ███████ ▀███ ▀██ ▀█▄ Repository & Mirror:
|
|
9
|
-
// ▀██████ ▄▄██ ▀▀ ██▄ github.com/xero/leviathan-crypto
|
|
10
|
-
// ▀█████▄ ▄██▄ ▄▀▄▀ unpkg.com/leviathan-crypto
|
|
11
|
-
// ▀████▄ ▄██▄
|
|
12
|
-
// ▐████ ▐███ Author: xero (https://x-e.ro)
|
|
13
|
-
// ▄▄██████████ ▐███ ▄▄ License: MIT
|
|
14
|
-
// ▄██▀▀▀▀▀▀▀▀▀▀ ▄████ ▄██▀
|
|
15
|
-
// ▄▀ ▄▄█████████▄▄ ▀▀▀▀▀ ▄███ This file is provided completely
|
|
16
|
-
// ▄██████▀▀▀▀▀▀██████▄ ▀▄▄▄▄████▀ free, "as is", and without
|
|
17
|
-
// ████▀ ▄▄▄▄▄▄▄ ▀████▄ ▀█████▀ ▄▄▄▄ warranty of any kind. The author
|
|
18
|
-
// █████▄▄█████▀▀▀▀▀▀▄ ▀███▄ ▄████ assumes absolutely no liability
|
|
19
|
-
// ▀██████▀ ▀████▄▄▄████▀ for its {ab,mis,}use.
|
|
20
|
-
// ▀█████▀▀
|
|
21
|
-
//
|
|
22
|
-
// src/ts/serpent/stream.ts
|
|
23
|
-
//
|
|
24
|
-
// SerpentStream — chunked authenticated encryption for large payloads.
|
|
25
|
-
// Tier 2 pure-TS composition: SerpentCtr + HMAC_SHA256 + HKDF_SHA256.
|
|
26
|
-
import { SerpentCtr, _serpentReady } from './index.js';
|
|
27
|
-
import { HMAC_SHA256, HKDF_SHA256, _sha2Ready } from '../sha2/index.js';
|
|
28
|
-
import { constantTimeEqual } from '../utils.js';
|
|
29
|
-
// ── Constants ─────────────────────────────────────────────────────────────────
|
|
30
|
-
const DOMAIN = 'serpent-stream-v1'; // UTF-8, 17 bytes
|
|
31
|
-
const ZERO_IV = new Uint8Array(16); // fixed zero IV for CTR
|
|
32
|
-
const CHUNK_MIN = 1024; // 1 KB
|
|
33
|
-
const CHUNK_MAX = 65536; // 64 KB
|
|
34
|
-
const CHUNK_DEF = 65536; // default
|
|
35
|
-
// ── Internal helpers ──────────────────────────────────────────────────────────
|
|
36
|
-
export function u32be(n) {
|
|
37
|
-
const b = new Uint8Array(4);
|
|
38
|
-
b[0] = (n >>> 24) & 0xff;
|
|
39
|
-
b[1] = (n >>> 16) & 0xff;
|
|
40
|
-
b[2] = (n >>> 8) & 0xff;
|
|
41
|
-
b[3] = n & 0xff;
|
|
42
|
-
return b;
|
|
43
|
-
}
|
|
44
|
-
export function u64be(n) {
|
|
45
|
-
const b = new Uint8Array(8);
|
|
46
|
-
// high 32 bits (safe for n < 2^53)
|
|
47
|
-
const hi = Math.floor(n / 0x100000000);
|
|
48
|
-
const lo = n >>> 0;
|
|
49
|
-
b[0] = (hi >>> 24) & 0xff;
|
|
50
|
-
b[1] = (hi >>> 16) & 0xff;
|
|
51
|
-
b[2] = (hi >>> 8) & 0xff;
|
|
52
|
-
b[3] = hi & 0xff;
|
|
53
|
-
b[4] = (lo >>> 24) & 0xff;
|
|
54
|
-
b[5] = (lo >>> 16) & 0xff;
|
|
55
|
-
b[6] = (lo >>> 8) & 0xff;
|
|
56
|
-
b[7] = lo & 0xff;
|
|
57
|
-
return b;
|
|
58
|
-
}
|
|
59
|
-
const DOMAIN_BYTES = new TextEncoder().encode(DOMAIN);
|
|
60
|
-
export function chunkInfo(streamNonce, chunkSize, chunkCount, index, isLast) {
|
|
61
|
-
// 17 + 16 + 4 + 8 + 8 + 1 = 54 bytes
|
|
62
|
-
const info = new Uint8Array(54);
|
|
63
|
-
let off = 0;
|
|
64
|
-
info.set(DOMAIN_BYTES, off);
|
|
65
|
-
off += 17;
|
|
66
|
-
info.set(streamNonce, off);
|
|
67
|
-
off += 16;
|
|
68
|
-
info.set(u32be(chunkSize), off);
|
|
69
|
-
off += 4;
|
|
70
|
-
info.set(u64be(chunkCount), off);
|
|
71
|
-
off += 8;
|
|
72
|
-
info.set(u64be(index), off);
|
|
73
|
-
off += 8;
|
|
74
|
-
info[off] = isLast ? 0x01 : 0x00;
|
|
75
|
-
return info;
|
|
76
|
-
}
|
|
77
|
-
export function deriveChunkKeys(hkdf, masterKey, streamNonce, chunkSize, chunkCount, index, isLast) {
|
|
78
|
-
const info = chunkInfo(streamNonce, chunkSize, chunkCount, index, isLast);
|
|
79
|
-
const derived = hkdf.derive(masterKey, streamNonce, info, 64);
|
|
80
|
-
return {
|
|
81
|
-
encKey: derived.subarray(0, 32),
|
|
82
|
-
macKey: derived.subarray(32, 64),
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
// ── Exported chunk-level ops (used by Part 2 pool worker) ─────────────────────
|
|
86
|
-
/**
|
|
87
|
-
* Encrypt one chunk. Returns ciphertext || hmac_tag (32 bytes).
|
|
88
|
-
* Does not generate keys -- caller provides encKey and macKey.
|
|
89
|
-
*/
|
|
90
|
-
export function sealChunk(ctr, hmac, encKey, macKey, chunk) {
|
|
91
|
-
ctr.beginEncrypt(encKey, ZERO_IV);
|
|
92
|
-
const ciphertext = ctr.encryptChunk(chunk);
|
|
93
|
-
const tag = hmac.hash(macKey, ciphertext);
|
|
94
|
-
const out = new Uint8Array(ciphertext.length + 32);
|
|
95
|
-
out.set(ciphertext, 0);
|
|
96
|
-
out.set(tag, ciphertext.length);
|
|
97
|
-
return out;
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Decrypt one chunk. Throws 'SerpentStream: authentication failed' on bad tag.
|
|
101
|
-
* Returns plaintext.
|
|
102
|
-
*/
|
|
103
|
-
export function openChunk(ctr, hmac, encKey, macKey, wire) {
|
|
104
|
-
if (wire.length < 32)
|
|
105
|
-
throw new RangeError('SerpentStream: chunk wire data too short');
|
|
106
|
-
const ciphertext = wire.subarray(0, wire.length - 32);
|
|
107
|
-
const tag = wire.subarray(wire.length - 32);
|
|
108
|
-
const expectedTag = hmac.hash(macKey, ciphertext);
|
|
109
|
-
if (!constantTimeEqual(tag, expectedTag))
|
|
110
|
-
throw new Error('SerpentStream: authentication failed');
|
|
111
|
-
ctr.beginEncrypt(encKey, ZERO_IV);
|
|
112
|
-
return ctr.encryptChunk(ciphertext);
|
|
113
|
-
}
|
|
114
|
-
// ── SerpentStream class ───────────────────────────────────────────────────────
|
|
115
|
-
export class SerpentStream {
|
|
116
|
-
_ctr;
|
|
117
|
-
_hmac;
|
|
118
|
-
_hkdf;
|
|
119
|
-
constructor() {
|
|
120
|
-
if (!_serpentReady() || !_sha2Ready())
|
|
121
|
-
throw new Error('leviathan-crypto: call init([\'serpent\', \'sha2\']) before using SerpentStream');
|
|
122
|
-
this._ctr = new SerpentCtr({ dangerUnauthenticated: true });
|
|
123
|
-
this._hmac = new HMAC_SHA256();
|
|
124
|
-
this._hkdf = new HKDF_SHA256();
|
|
125
|
-
}
|
|
126
|
-
// _nonce: test seam only — inject a fixed nonce for deterministic KAT vectors
|
|
127
|
-
seal(key, plaintext, chunkSize, _nonce) {
|
|
128
|
-
if (key.length !== 32)
|
|
129
|
-
throw new RangeError(`SerpentStream key must be 32 bytes (got ${key.length})`);
|
|
130
|
-
const cs = chunkSize ?? CHUNK_DEF;
|
|
131
|
-
if (cs < CHUNK_MIN || cs > CHUNK_MAX)
|
|
132
|
-
throw new RangeError(`SerpentStream chunkSize must be ${CHUNK_MIN}..${CHUNK_MAX} (got ${cs})`);
|
|
133
|
-
const streamNonce = (_nonce && _nonce.length === 16) ? _nonce : new Uint8Array(16);
|
|
134
|
-
if (!_nonce || _nonce.length !== 16)
|
|
135
|
-
crypto.getRandomValues(streamNonce);
|
|
136
|
-
const chunkCount = plaintext.length === 0 ? 1 : Math.ceil(plaintext.length / cs);
|
|
137
|
-
// Compute total output size
|
|
138
|
-
let totalWire = 28; // header
|
|
139
|
-
for (let i = 0; i < chunkCount; i++) {
|
|
140
|
-
const start = i * cs;
|
|
141
|
-
const end = Math.min(start + cs, plaintext.length);
|
|
142
|
-
totalWire += (end - start) + 32;
|
|
143
|
-
}
|
|
144
|
-
const out = new Uint8Array(totalWire);
|
|
145
|
-
// Write header
|
|
146
|
-
out.set(streamNonce, 0);
|
|
147
|
-
out.set(u32be(cs), 16);
|
|
148
|
-
out.set(u64be(chunkCount), 20);
|
|
149
|
-
let pos = 28;
|
|
150
|
-
for (let i = 0; i < chunkCount; i++) {
|
|
151
|
-
const start = i * cs;
|
|
152
|
-
const end = Math.min(start + cs, plaintext.length);
|
|
153
|
-
const slice = plaintext.subarray(start, end);
|
|
154
|
-
const isLast = i === chunkCount - 1;
|
|
155
|
-
const { encKey, macKey } = deriveChunkKeys(this._hkdf, key, streamNonce, cs, chunkCount, i, isLast);
|
|
156
|
-
const wire = sealChunk(this._ctr, this._hmac, encKey, macKey, slice);
|
|
157
|
-
out.set(wire, pos);
|
|
158
|
-
pos += wire.length;
|
|
159
|
-
}
|
|
160
|
-
return out;
|
|
161
|
-
}
|
|
162
|
-
open(key, ciphertext) {
|
|
163
|
-
if (key.length !== 32)
|
|
164
|
-
throw new RangeError(`SerpentStream key must be 32 bytes (got ${key.length})`);
|
|
165
|
-
if (ciphertext.length < 28 + 32)
|
|
166
|
-
throw new RangeError('SerpentStream: ciphertext too short');
|
|
167
|
-
// Parse header
|
|
168
|
-
const streamNonce = ciphertext.subarray(0, 16);
|
|
169
|
-
const csView = ciphertext.subarray(16, 20);
|
|
170
|
-
const cs = (csView[0] << 24) | (csView[1] << 16) | (csView[2] << 8) | csView[3];
|
|
171
|
-
const ccView = ciphertext.subarray(20, 28);
|
|
172
|
-
let chunkCount = 0;
|
|
173
|
-
for (let i = 0; i < 8; i++)
|
|
174
|
-
chunkCount = chunkCount * 256 + ccView[i];
|
|
175
|
-
// Compute total plaintext size
|
|
176
|
-
let totalPt = 0;
|
|
177
|
-
let pos = 28;
|
|
178
|
-
for (let i = 0; i < chunkCount; i++) {
|
|
179
|
-
const isLast = i === chunkCount - 1;
|
|
180
|
-
const wireLen = isLast ? ciphertext.length - pos : cs + 32;
|
|
181
|
-
totalPt += wireLen - 32;
|
|
182
|
-
if (!isLast)
|
|
183
|
-
pos += wireLen;
|
|
184
|
-
}
|
|
185
|
-
const plaintext = new Uint8Array(totalPt);
|
|
186
|
-
pos = 28;
|
|
187
|
-
let ptPos = 0;
|
|
188
|
-
for (let i = 0; i < chunkCount; i++) {
|
|
189
|
-
const isLast = i === chunkCount - 1;
|
|
190
|
-
const wireLen = isLast ? ciphertext.length - pos : cs + 32;
|
|
191
|
-
const wireSlice = ciphertext.subarray(pos, pos + wireLen);
|
|
192
|
-
const { encKey, macKey } = deriveChunkKeys(this._hkdf, key, streamNonce, cs, chunkCount, i, isLast);
|
|
193
|
-
const pt = openChunk(this._ctr, this._hmac, encKey, macKey, wireSlice);
|
|
194
|
-
plaintext.set(pt, ptPos);
|
|
195
|
-
ptPos += pt.length;
|
|
196
|
-
pos += wireLen;
|
|
197
|
-
}
|
|
198
|
-
return plaintext;
|
|
199
|
-
}
|
|
200
|
-
dispose() {
|
|
201
|
-
this._ctr.dispose();
|
|
202
|
-
this._hmac.dispose();
|
|
203
|
-
this._hkdf.dispose();
|
|
204
|
-
}
|
|
205
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
interface SerpentExports {
|
|
2
|
-
memory: WebAssembly.Memory;
|
|
3
|
-
getKeyOffset: () => number;
|
|
4
|
-
getNonceOffset: () => number;
|
|
5
|
-
getChunkPtOffset: () => number;
|
|
6
|
-
getChunkCtOffset: () => number;
|
|
7
|
-
getChunkSize: () => number;
|
|
8
|
-
loadKey: (n: number) => number;
|
|
9
|
-
resetCounter: () => void;
|
|
10
|
-
encryptChunk: (n: number) => number;
|
|
11
|
-
wipeBuffers: () => void;
|
|
12
|
-
}
|
|
13
|
-
interface Sha2Exports {
|
|
14
|
-
memory: WebAssembly.Memory;
|
|
15
|
-
getSha256InputOffset: () => number;
|
|
16
|
-
getSha256OutOffset: () => number;
|
|
17
|
-
sha256Init: () => void;
|
|
18
|
-
sha256Update: (len: number) => void;
|
|
19
|
-
sha256Final: () => void;
|
|
20
|
-
hmac256Init: (keyLen: number) => void;
|
|
21
|
-
hmac256Update: (len: number) => void;
|
|
22
|
-
hmac256Final: () => void;
|
|
23
|
-
wipeBuffers: () => void;
|
|
24
|
-
}
|
|
25
|
-
declare let sx: SerpentExports | undefined;
|
|
26
|
-
declare let hx: Sha2Exports | undefined;
|
|
27
|
-
declare const ZERO_IV: Uint8Array<ArrayBuffer>;
|
|
28
|
-
declare function hmacSha256(hx: Sha2Exports, key: Uint8Array, msg: Uint8Array): Uint8Array;
|
|
29
|
-
declare function ctrEncrypt(sx: SerpentExports, key: Uint8Array, chunk: Uint8Array): Uint8Array;
|
|
30
|
-
declare function constantTimeEqual(a: Uint8Array, b: Uint8Array): boolean;
|
|
31
|
-
declare function workerSealChunk(encKey: Uint8Array, macKey: Uint8Array, chunk: Uint8Array): Uint8Array;
|
|
32
|
-
declare function workerOpenChunk(encKey: Uint8Array, macKey: Uint8Array, wire: Uint8Array): Uint8Array;
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// / <reference lib="webworker" />
|
|
3
|
-
// src/ts/serpent/stream.worker.ts
|
|
4
|
-
//
|
|
5
|
-
// Worker entry point for SerpentStreamPool. Runs in a Web Worker —
|
|
6
|
-
// no access to the main thread's module cache. Owns its own
|
|
7
|
-
// serpent.wasm and sha2.wasm instances with isolated linear memory.
|
|
8
|
-
// Implements sealChunk/openChunk inline using raw WASM exports.
|
|
9
|
-
let sx;
|
|
10
|
-
let hx;
|
|
11
|
-
const ZERO_IV = new Uint8Array(16);
|
|
12
|
-
// ── Inline chunk ops ──────────────────────────────────────────────────────────
|
|
13
|
-
function hmacSha256(hx, key, msg) {
|
|
14
|
-
// RFC 2104 §3: keys longer than block size (64 bytes) are pre-hashed.
|
|
15
|
-
// mac_key is always 32 bytes in normal usage (half of HKDF 64-byte output),
|
|
16
|
-
// but this guard must match the main-thread HMAC_SHA256.hash() behaviour
|
|
17
|
-
// exactly — any divergence would cause authentication failures if key sizes
|
|
18
|
-
// ever change.
|
|
19
|
-
let k = key;
|
|
20
|
-
if (k.length > 64) {
|
|
21
|
-
hx.sha256Init();
|
|
22
|
-
let pos = 0;
|
|
23
|
-
while (pos < k.length) {
|
|
24
|
-
const n = Math.min(k.length - pos, 64);
|
|
25
|
-
new Uint8Array(hx.memory.buffer).set(k.subarray(pos, pos + n), hx.getSha256InputOffset());
|
|
26
|
-
hx.sha256Update(n);
|
|
27
|
-
pos += n;
|
|
28
|
-
}
|
|
29
|
-
hx.sha256Final();
|
|
30
|
-
const out = new Uint8Array(hx.memory.buffer);
|
|
31
|
-
k = out.slice(hx.getSha256OutOffset(), hx.getSha256OutOffset() + 32);
|
|
32
|
-
}
|
|
33
|
-
const mem = new Uint8Array(hx.memory.buffer);
|
|
34
|
-
const inputOff = hx.getSha256InputOffset();
|
|
35
|
-
mem.set(k, inputOff);
|
|
36
|
-
hx.hmac256Init(k.length);
|
|
37
|
-
let pos = 0;
|
|
38
|
-
while (pos < msg.length) {
|
|
39
|
-
const n = Math.min(msg.length - pos, 64);
|
|
40
|
-
new Uint8Array(hx.memory.buffer).set(msg.subarray(pos, pos + n), inputOff);
|
|
41
|
-
hx.hmac256Update(n);
|
|
42
|
-
pos += n;
|
|
43
|
-
}
|
|
44
|
-
hx.hmac256Final();
|
|
45
|
-
const out = new Uint8Array(hx.memory.buffer);
|
|
46
|
-
return out.slice(hx.getSha256OutOffset(), hx.getSha256OutOffset() + 32);
|
|
47
|
-
}
|
|
48
|
-
function ctrEncrypt(sx, key, chunk) {
|
|
49
|
-
const mem = new Uint8Array(sx.memory.buffer);
|
|
50
|
-
mem.set(key, sx.getKeyOffset());
|
|
51
|
-
mem.set(ZERO_IV, sx.getNonceOffset());
|
|
52
|
-
sx.loadKey(key.length);
|
|
53
|
-
sx.resetCounter();
|
|
54
|
-
new Uint8Array(sx.memory.buffer).set(chunk, sx.getChunkPtOffset());
|
|
55
|
-
sx.encryptChunk(chunk.length);
|
|
56
|
-
const out = new Uint8Array(sx.memory.buffer);
|
|
57
|
-
return out.slice(sx.getChunkCtOffset(), sx.getChunkCtOffset() + chunk.length);
|
|
58
|
-
}
|
|
59
|
-
function constantTimeEqual(a, b) {
|
|
60
|
-
if (a.length !== b.length)
|
|
61
|
-
return false;
|
|
62
|
-
let diff = 0;
|
|
63
|
-
for (let i = 0; i < a.length; i++)
|
|
64
|
-
diff |= a[i] ^ b[i];
|
|
65
|
-
return diff === 0;
|
|
66
|
-
}
|
|
67
|
-
function workerSealChunk(encKey, macKey, chunk) {
|
|
68
|
-
const ciphertext = ctrEncrypt(sx, encKey, chunk);
|
|
69
|
-
const tag = hmacSha256(hx, macKey, ciphertext);
|
|
70
|
-
const out = new Uint8Array(ciphertext.length + 32);
|
|
71
|
-
out.set(ciphertext, 0);
|
|
72
|
-
out.set(tag, ciphertext.length);
|
|
73
|
-
return out;
|
|
74
|
-
}
|
|
75
|
-
function workerOpenChunk(encKey, macKey, wire) {
|
|
76
|
-
if (wire.length < 32)
|
|
77
|
-
throw new RangeError('SerpentStream: chunk wire data too short');
|
|
78
|
-
const ciphertext = wire.subarray(0, wire.length - 32);
|
|
79
|
-
const tag = wire.subarray(wire.length - 32);
|
|
80
|
-
const expectedTag = hmacSha256(hx, macKey, ciphertext);
|
|
81
|
-
if (!constantTimeEqual(tag, expectedTag))
|
|
82
|
-
throw new Error('SerpentStream: authentication failed');
|
|
83
|
-
return ctrEncrypt(sx, encKey, ciphertext);
|
|
84
|
-
}
|
|
85
|
-
// ── Message handler ───────────────────────────────────────────────────────────
|
|
86
|
-
self.onmessage = async (e) => {
|
|
87
|
-
const msg = e.data;
|
|
88
|
-
if (msg.type === 'init') {
|
|
89
|
-
try {
|
|
90
|
-
const serpentMem = new WebAssembly.Memory({ initial: 3, maximum: 3 });
|
|
91
|
-
const sha2Mem = new WebAssembly.Memory({ initial: 3, maximum: 3 });
|
|
92
|
-
const serpentInst = await WebAssembly.instantiate(msg.serpentModule, { env: { memory: serpentMem } });
|
|
93
|
-
const sha2Inst = await WebAssembly.instantiate(msg.sha2Module, { env: { memory: sha2Mem } });
|
|
94
|
-
sx = serpentInst.exports;
|
|
95
|
-
hx = sha2Inst.exports;
|
|
96
|
-
self.postMessage({ type: 'ready' });
|
|
97
|
-
}
|
|
98
|
-
catch (err) {
|
|
99
|
-
self.postMessage({ type: 'error', id: -1, message: err.message });
|
|
100
|
-
}
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
if (!sx || !hx) {
|
|
104
|
-
self.postMessage({ type: 'error', id: msg.id, message: 'worker not initialized' });
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
try {
|
|
108
|
-
const { id, op, encKey, macKey, data } = msg;
|
|
109
|
-
const result = op === 'seal'
|
|
110
|
-
? workerSealChunk(encKey, macKey, data)
|
|
111
|
-
: workerOpenChunk(encKey, macKey, data);
|
|
112
|
-
self.postMessage({ type: 'result', id, data: result }, [result.buffer]);
|
|
113
|
-
}
|
|
114
|
-
catch (err) {
|
|
115
|
-
self.postMessage({ type: 'error', id: msg.id, message: err.message });
|
|
116
|
-
}
|
|
117
|
-
};
|
|
File without changes
|