@omnituum/pqc-shared 0.3.0 → 0.4.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/dist/crypto/index.cjs +29 -71
- package/dist/crypto/index.d.cts +38 -45
- package/dist/crypto/index.d.ts +38 -45
- package/dist/crypto/index.js +28 -72
- package/dist/crypto/safe/index.cjs +556 -0
- package/dist/crypto/safe/index.d.cts +341 -0
- package/dist/crypto/safe/index.d.ts +341 -0
- package/dist/crypto/safe/index.js +524 -0
- package/dist/fs/index.cjs +6 -34
- package/dist/fs/index.js +6 -34
- package/dist/index.cjs +29 -71
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +28 -72
- package/dist/{integrity-CCYjrap3.d.ts → integrity-BPvNsC50.d.ts} +1 -1
- package/dist/{integrity-Dx9jukMH.d.cts → integrity-ByMp24VA.d.cts} +1 -1
- package/dist/{types-61c7Q9ri.d.ts → types-Dx_AFqow.d.cts} +54 -2
- package/dist/{types-Ch0y-n7K.d.cts → types-Dx_AFqow.d.ts} +54 -2
- package/dist/utils/index.d.cts +2 -3
- package/dist/utils/index.d.ts +2 -3
- package/dist/vault/index.cjs +13 -41
- package/dist/vault/index.d.cts +2 -3
- package/dist/vault/index.d.ts +2 -3
- package/dist/vault/index.js +13 -41
- package/package.json +27 -13
- package/src/crypto/dilithium.ts +8 -4
- package/src/crypto/hybrid.ts +13 -29
- package/src/crypto/index.ts +3 -0
- package/src/crypto/kyber.ts +63 -121
- package/src/crypto/safe/adapters.ts +306 -0
- package/src/crypto/safe/dilithium.ts +74 -0
- package/src/crypto/safe/index.ts +97 -0
- package/src/crypto/safe/kyber.ts +47 -0
- package/src/crypto/safe/manifests.ts +192 -0
- package/src/crypto/safe/secretbox.ts +24 -0
- package/src/crypto/safe/types.ts +88 -0
- package/src/crypto/safe/x25519.ts +31 -0
- package/src/index.ts +7 -4
- package/src/version.ts +9 -4
- package/dist/version-BygzPVGs.d.cts +0 -55
- package/dist/version-BygzPVGs.d.ts +0 -55
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
import '@noble/hashes/sha2.js';
|
|
2
|
+
import '@noble/hashes/hmac.js';
|
|
3
|
+
import { ml_kem1024 } from '@noble/post-quantum/ml-kem.js';
|
|
4
|
+
import nacl3 from 'tweetnacl';
|
|
5
|
+
|
|
6
|
+
var __defProp = Object.defineProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/crypto/safe/dilithium.ts
|
|
13
|
+
var dilithium_exports = {};
|
|
14
|
+
__export(dilithium_exports, {
|
|
15
|
+
keygen: () => keygen,
|
|
16
|
+
sign: () => sign,
|
|
17
|
+
verify: () => verify
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// src/crypto/safe/manifests.ts
|
|
21
|
+
var ML_DSA_65 = {
|
|
22
|
+
algorithm: "ML-DSA-65",
|
|
23
|
+
publicKeyBytes: 1952,
|
|
24
|
+
secretKeyBytes: 4032,
|
|
25
|
+
signatureBytes: 3309
|
|
26
|
+
};
|
|
27
|
+
var ML_KEM_768 = {
|
|
28
|
+
algorithm: "ML-KEM-768",
|
|
29
|
+
encapsulationKeyBytes: 1184,
|
|
30
|
+
decapsulationKeyBytes: 2400,
|
|
31
|
+
ciphertextBytes: 1088,
|
|
32
|
+
sharedSecretBytes: 32
|
|
33
|
+
};
|
|
34
|
+
var X25519 = {
|
|
35
|
+
algorithm: "X25519",
|
|
36
|
+
publicKeyBytes: 32,
|
|
37
|
+
secretKeyBytes: 32,
|
|
38
|
+
sharedSecretBytes: 32
|
|
39
|
+
};
|
|
40
|
+
var XSALSA20_POLY1305 = {
|
|
41
|
+
algorithm: "XSalsa20-Poly1305",
|
|
42
|
+
keyBytes: 32,
|
|
43
|
+
nonceBytes: 24,
|
|
44
|
+
tagBytes: 16
|
|
45
|
+
};
|
|
46
|
+
function assertSignaturePrimitive(noble, manifest) {
|
|
47
|
+
const msg = new Uint8Array([222, 173, 190, 239]);
|
|
48
|
+
const { publicKey, secretKey } = noble.keygen();
|
|
49
|
+
const sig = noble.sign(msg, secretKey);
|
|
50
|
+
if (publicKey.length !== manifest.publicKeyBytes) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
`${manifest.algorithm}: publicKey ${publicKey.length} bytes, expected ${manifest.publicKeyBytes}`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
if (secretKey.length !== manifest.secretKeyBytes) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`${manifest.algorithm}: secretKey ${secretKey.length} bytes, expected ${manifest.secretKeyBytes}`
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
if (sig.length !== manifest.signatureBytes) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
`${manifest.algorithm}: signature ${sig.length} bytes, expected ${manifest.signatureBytes}`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
if (!noble.verify(sig, msg, publicKey)) {
|
|
66
|
+
throw new Error(`${manifest.algorithm}: sign/verify roundtrip failed`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function assertKemPrimitive(noble, manifest) {
|
|
70
|
+
const { publicKey, secretKey } = noble.keygen();
|
|
71
|
+
const { ciphertext, sharedSecret: ss1 } = noble.encapsulate(publicKey);
|
|
72
|
+
const ss2 = noble.decapsulate(ciphertext, secretKey);
|
|
73
|
+
if (publicKey.length !== manifest.encapsulationKeyBytes) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
`${manifest.algorithm}: encapsulationKey ${publicKey.length} bytes, expected ${manifest.encapsulationKeyBytes}`
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
if (secretKey.length !== manifest.decapsulationKeyBytes) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
`${manifest.algorithm}: decapsulationKey ${secretKey.length} bytes, expected ${manifest.decapsulationKeyBytes}`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
if (ciphertext.length !== manifest.ciphertextBytes) {
|
|
84
|
+
throw new Error(
|
|
85
|
+
`${manifest.algorithm}: ciphertext ${ciphertext.length} bytes, expected ${manifest.ciphertextBytes}`
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
if (ss1.length !== manifest.sharedSecretBytes) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
`${manifest.algorithm}: sharedSecret ${ss1.length} bytes, expected ${manifest.sharedSecretBytes}`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
if (ss2.length !== manifest.sharedSecretBytes) {
|
|
94
|
+
throw new Error(
|
|
95
|
+
`${manifest.algorithm}: decapsulated sharedSecret ${ss2.length} bytes, expected ${manifest.sharedSecretBytes}`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
for (let i = 0; i < ss1.length; i++) {
|
|
99
|
+
if (ss1[i] !== ss2[i]) {
|
|
100
|
+
throw new Error(`${manifest.algorithm}: encap/decap shared secrets differ at byte ${i}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function assertDhPrimitive(noble, manifest) {
|
|
105
|
+
const sk = noble.utils.randomPrivateKey();
|
|
106
|
+
const pk = noble.getPublicKey(sk);
|
|
107
|
+
if (pk.length !== manifest.publicKeyBytes) {
|
|
108
|
+
throw new Error(
|
|
109
|
+
`${manifest.algorithm}: publicKey ${pk.length} bytes, expected ${manifest.publicKeyBytes}`
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
if (sk.length !== manifest.secretKeyBytes) {
|
|
113
|
+
throw new Error(
|
|
114
|
+
`${manifest.algorithm}: secretKey ${sk.length} bytes, expected ${manifest.secretKeyBytes}`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
const sk2 = noble.utils.randomPrivateKey();
|
|
118
|
+
const pk2 = noble.getPublicKey(sk2);
|
|
119
|
+
const ss1 = noble.getSharedSecret(sk, pk2);
|
|
120
|
+
const ss2 = noble.getSharedSecret(sk2, pk);
|
|
121
|
+
if (ss1.length !== manifest.sharedSecretBytes) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
`${manifest.algorithm}: sharedSecret ${ss1.length} bytes, expected ${manifest.sharedSecretBytes}`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
for (let i = 0; i < ss1.length; i++) {
|
|
127
|
+
if (ss1[i] !== ss2[i]) {
|
|
128
|
+
throw new Error(`${manifest.algorithm}: DH commutativity check failed at byte ${i}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// src/crypto/safe/dilithium.ts
|
|
134
|
+
var _mod = null;
|
|
135
|
+
var _invariantChecked = false;
|
|
136
|
+
async function load() {
|
|
137
|
+
if (!_mod) {
|
|
138
|
+
const { ml_dsa65 } = await import('@noble/post-quantum/ml-dsa.js');
|
|
139
|
+
_mod = ml_dsa65;
|
|
140
|
+
}
|
|
141
|
+
return _mod;
|
|
142
|
+
}
|
|
143
|
+
async function checkInvariant() {
|
|
144
|
+
if (_invariantChecked) return;
|
|
145
|
+
const mod = await load();
|
|
146
|
+
assertSignaturePrimitive(mod, ML_DSA_65);
|
|
147
|
+
_invariantChecked = true;
|
|
148
|
+
}
|
|
149
|
+
async function keygen() {
|
|
150
|
+
const mod = await load();
|
|
151
|
+
await checkInvariant();
|
|
152
|
+
const kp = mod.keygen();
|
|
153
|
+
return {
|
|
154
|
+
publicKey: kp.publicKey,
|
|
155
|
+
secretKey: kp.secretKey
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
async function sign(params) {
|
|
159
|
+
const mod = await load();
|
|
160
|
+
await checkInvariant();
|
|
161
|
+
return mod.sign(params.message, params.secretKey);
|
|
162
|
+
}
|
|
163
|
+
async function verify(params) {
|
|
164
|
+
const mod = await load();
|
|
165
|
+
await checkInvariant();
|
|
166
|
+
try {
|
|
167
|
+
return mod.verify(params.signature, params.message, params.publicKey);
|
|
168
|
+
} catch {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// src/crypto/safe/kyber.ts
|
|
174
|
+
var kyber_exports = {};
|
|
175
|
+
__export(kyber_exports, {
|
|
176
|
+
decapsulate: () => decapsulate,
|
|
177
|
+
encapsulate: () => encapsulate,
|
|
178
|
+
keygen: () => keygen2
|
|
179
|
+
});
|
|
180
|
+
new TextEncoder();
|
|
181
|
+
new TextDecoder();
|
|
182
|
+
function toB64(bytes) {
|
|
183
|
+
let binary = "";
|
|
184
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
185
|
+
binary += String.fromCharCode(bytes[i]);
|
|
186
|
+
}
|
|
187
|
+
return btoa(binary);
|
|
188
|
+
}
|
|
189
|
+
function fromB64(str) {
|
|
190
|
+
const binary = atob(str);
|
|
191
|
+
const bytes = new Uint8Array(binary.length);
|
|
192
|
+
for (let i = 0; i < binary.length; i++) {
|
|
193
|
+
bytes[i] = binary.charCodeAt(i);
|
|
194
|
+
}
|
|
195
|
+
return bytes;
|
|
196
|
+
}
|
|
197
|
+
function toHex(bytes) {
|
|
198
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
199
|
+
}
|
|
200
|
+
function assertLen(label, arr, n) {
|
|
201
|
+
if (arr.length !== n) {
|
|
202
|
+
throw new Error(`${label} must be ${n} bytes, got ${arr.length}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
function randN(n) {
|
|
206
|
+
return globalThis.crypto.getRandomValues(new Uint8Array(n));
|
|
207
|
+
}
|
|
208
|
+
var b64 = toB64;
|
|
209
|
+
var ub64 = fromB64;
|
|
210
|
+
async function generateKyberKeypair() {
|
|
211
|
+
const seed = randN(64);
|
|
212
|
+
const kp = ml_kem1024.keygen(seed);
|
|
213
|
+
return {
|
|
214
|
+
publicB64: b64(kp.publicKey),
|
|
215
|
+
secretB64: b64(kp.secretKey)
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
async function kyberEncapsulate(pubKeyB64) {
|
|
219
|
+
const pk = ub64(pubKeyB64);
|
|
220
|
+
const enc = ml_kem1024.encapsulate(pk);
|
|
221
|
+
return {
|
|
222
|
+
ciphertext: enc.cipherText,
|
|
223
|
+
sharedSecret: enc.sharedSecret
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
async function kyberDecapsulate(kemCiphertextB64, secretKeyB64) {
|
|
227
|
+
const ct = ub64(kemCiphertextB64);
|
|
228
|
+
const sk = ub64(secretKeyB64);
|
|
229
|
+
return ml_kem1024.decapsulate(ct, sk);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// src/crypto/safe/kyber.ts
|
|
233
|
+
async function keygen2() {
|
|
234
|
+
const kp = await generateKyberKeypair();
|
|
235
|
+
if (!kp) return null;
|
|
236
|
+
return {
|
|
237
|
+
encapsulationKey: fromB64(kp.publicB64),
|
|
238
|
+
decapsulationKey: fromB64(kp.secretB64)
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
async function encapsulate(params) {
|
|
242
|
+
const result = await kyberEncapsulate(toB64(params.encapsulationKey));
|
|
243
|
+
return {
|
|
244
|
+
ciphertext: result.ciphertext,
|
|
245
|
+
sharedSecret: result.sharedSecret
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
async function decapsulate(params) {
|
|
249
|
+
const ss = await kyberDecapsulate(toB64(params.ciphertext), toB64(params.decapsulationKey));
|
|
250
|
+
return ss;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// src/crypto/safe/x25519.ts
|
|
254
|
+
var x25519_exports = {};
|
|
255
|
+
__export(x25519_exports, {
|
|
256
|
+
keygen: () => keygen3,
|
|
257
|
+
sharedSecret: () => sharedSecret
|
|
258
|
+
});
|
|
259
|
+
function generateX25519Keypair() {
|
|
260
|
+
const kp = nacl3.box.keyPair();
|
|
261
|
+
return {
|
|
262
|
+
publicHex: "0x" + toHex(kp.publicKey),
|
|
263
|
+
secretHex: "0x" + toHex(kp.secretKey),
|
|
264
|
+
publicBytes: kp.publicKey,
|
|
265
|
+
secretBytes: kp.secretKey
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
function x25519SharedSecret(ourSecretKey, theirPublicKey) {
|
|
269
|
+
assertLen("secret key", ourSecretKey, 32);
|
|
270
|
+
assertLen("public key", theirPublicKey, 32);
|
|
271
|
+
return nacl3.scalarMult(ourSecretKey, theirPublicKey);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// src/crypto/safe/x25519.ts
|
|
275
|
+
function keygen3() {
|
|
276
|
+
const kp = generateX25519Keypair();
|
|
277
|
+
return {
|
|
278
|
+
publicKey: kp.publicBytes,
|
|
279
|
+
secretKey: kp.secretBytes
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
function sharedSecret(params) {
|
|
283
|
+
return x25519SharedSecret(params.ourSecretKey, params.theirPublicKey);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// src/crypto/safe/secretbox.ts
|
|
287
|
+
var secretbox_exports = {};
|
|
288
|
+
__export(secretbox_exports, {
|
|
289
|
+
open: () => open,
|
|
290
|
+
seal: () => seal
|
|
291
|
+
});
|
|
292
|
+
function secretboxRaw(key, plaintext, nonce) {
|
|
293
|
+
assertLen("secretbox key", key, 32);
|
|
294
|
+
assertLen("nonce", nonce, 24);
|
|
295
|
+
return nacl3.secretbox(plaintext, nonce, key);
|
|
296
|
+
}
|
|
297
|
+
function secretboxOpenRaw(key, ciphertext, nonce) {
|
|
298
|
+
assertLen("secretbox key", key, 32);
|
|
299
|
+
assertLen("nonce", nonce, 24);
|
|
300
|
+
return nacl3.secretbox.open(ciphertext, nonce, key) || null;
|
|
301
|
+
}
|
|
302
|
+
nacl3.secretbox.keyLength;
|
|
303
|
+
nacl3.secretbox.nonceLength;
|
|
304
|
+
nacl3.secretbox.overheadLength;
|
|
305
|
+
nacl3.box.publicKeyLength;
|
|
306
|
+
nacl3.box.nonceLength;
|
|
307
|
+
|
|
308
|
+
// src/crypto/safe/secretbox.ts
|
|
309
|
+
function seal(params) {
|
|
310
|
+
return secretboxRaw(params.key, params.plaintext, params.nonce);
|
|
311
|
+
}
|
|
312
|
+
function open(params) {
|
|
313
|
+
return secretboxOpenRaw(params.key, params.ciphertext, params.nonce);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// src/crypto/safe/types.ts
|
|
317
|
+
function asMessage(data) {
|
|
318
|
+
return data;
|
|
319
|
+
}
|
|
320
|
+
function asDilithiumSecretKey(data) {
|
|
321
|
+
return data;
|
|
322
|
+
}
|
|
323
|
+
function asDilithiumPublicKey(data) {
|
|
324
|
+
return data;
|
|
325
|
+
}
|
|
326
|
+
function asKyberEncapsulationKey(data) {
|
|
327
|
+
return data;
|
|
328
|
+
}
|
|
329
|
+
function asKyberDecapsulationKey(data) {
|
|
330
|
+
return data;
|
|
331
|
+
}
|
|
332
|
+
function asX25519PublicKey(data) {
|
|
333
|
+
return data;
|
|
334
|
+
}
|
|
335
|
+
function asX25519SecretKey(data) {
|
|
336
|
+
return data;
|
|
337
|
+
}
|
|
338
|
+
function asSymmetricKey(data) {
|
|
339
|
+
return data;
|
|
340
|
+
}
|
|
341
|
+
function asNonce(data) {
|
|
342
|
+
return data;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// src/crypto/safe/adapters.ts
|
|
346
|
+
function makeSignatureAdapter(noble, manifest) {
|
|
347
|
+
let checked = false;
|
|
348
|
+
const alg = manifest.algorithm;
|
|
349
|
+
function ensureInvariant() {
|
|
350
|
+
if (checked) return;
|
|
351
|
+
const msg = new Uint8Array([222, 173, 190, 239]);
|
|
352
|
+
const { publicKey, secretKey } = noble.keygen();
|
|
353
|
+
const sig = noble.sign(msg, secretKey);
|
|
354
|
+
if (publicKey.length !== manifest.publicKeyBytes) {
|
|
355
|
+
throw new Error(`${alg}: publicKey ${publicKey.length}B, expected ${manifest.publicKeyBytes}B`);
|
|
356
|
+
}
|
|
357
|
+
if (secretKey.length !== manifest.secretKeyBytes) {
|
|
358
|
+
throw new Error(`${alg}: secretKey ${secretKey.length}B, expected ${manifest.secretKeyBytes}B`);
|
|
359
|
+
}
|
|
360
|
+
if (sig.length !== manifest.signatureBytes) {
|
|
361
|
+
throw new Error(`${alg}: signature ${sig.length}B, expected ${manifest.signatureBytes}B`);
|
|
362
|
+
}
|
|
363
|
+
if (!noble.verify(sig, msg, publicKey)) {
|
|
364
|
+
throw new Error(`${alg}: sign/verify roundtrip failed`);
|
|
365
|
+
}
|
|
366
|
+
checked = true;
|
|
367
|
+
}
|
|
368
|
+
return {
|
|
369
|
+
keygen() {
|
|
370
|
+
ensureInvariant();
|
|
371
|
+
const kp = noble.keygen();
|
|
372
|
+
return {
|
|
373
|
+
publicKey: kp.publicKey,
|
|
374
|
+
secretKey: kp.secretKey
|
|
375
|
+
};
|
|
376
|
+
},
|
|
377
|
+
sign(params) {
|
|
378
|
+
ensureInvariant();
|
|
379
|
+
return noble.sign(params.message, params.secretKey);
|
|
380
|
+
},
|
|
381
|
+
verify(params) {
|
|
382
|
+
ensureInvariant();
|
|
383
|
+
return noble.verify(params.signature, params.message, params.publicKey);
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
function asSigMessage(v) {
|
|
388
|
+
return v;
|
|
389
|
+
}
|
|
390
|
+
function makeKemAdapter(noble, manifest) {
|
|
391
|
+
let checked = false;
|
|
392
|
+
const alg = manifest.algorithm;
|
|
393
|
+
function ensureInvariant() {
|
|
394
|
+
if (checked) return;
|
|
395
|
+
const { publicKey, secretKey } = noble.keygen();
|
|
396
|
+
const { ciphertext, sharedSecret: ss1 } = noble.encapsulate(publicKey);
|
|
397
|
+
const ss2 = noble.decapsulate(ciphertext, secretKey);
|
|
398
|
+
if (publicKey.length !== manifest.encapsulationKeyBytes) {
|
|
399
|
+
throw new Error(`${alg}: encapsKey ${publicKey.length}B, expected ${manifest.encapsulationKeyBytes}B`);
|
|
400
|
+
}
|
|
401
|
+
if (secretKey.length !== manifest.decapsulationKeyBytes) {
|
|
402
|
+
throw new Error(`${alg}: decapsKey ${secretKey.length}B, expected ${manifest.decapsulationKeyBytes}B`);
|
|
403
|
+
}
|
|
404
|
+
if (ciphertext.length !== manifest.ciphertextBytes) {
|
|
405
|
+
throw new Error(`${alg}: ciphertext ${ciphertext.length}B, expected ${manifest.ciphertextBytes}B`);
|
|
406
|
+
}
|
|
407
|
+
if (ss1.length !== manifest.sharedSecretBytes) {
|
|
408
|
+
throw new Error(`${alg}: sharedSecret ${ss1.length}B, expected ${manifest.sharedSecretBytes}B`);
|
|
409
|
+
}
|
|
410
|
+
if (ss2.length !== ss1.length) {
|
|
411
|
+
throw new Error(`${alg}: encap/decap shared secret length mismatch`);
|
|
412
|
+
}
|
|
413
|
+
for (let i = 0; i < ss1.length; i++) {
|
|
414
|
+
if (ss1[i] !== ss2[i]) throw new Error(`${alg}: encap/decap shared secrets differ`);
|
|
415
|
+
}
|
|
416
|
+
checked = true;
|
|
417
|
+
}
|
|
418
|
+
return {
|
|
419
|
+
keygen() {
|
|
420
|
+
ensureInvariant();
|
|
421
|
+
const kp = noble.keygen();
|
|
422
|
+
return {
|
|
423
|
+
encapsulationKey: kp.publicKey,
|
|
424
|
+
decapsulationKey: kp.secretKey
|
|
425
|
+
};
|
|
426
|
+
},
|
|
427
|
+
encapsulate(params) {
|
|
428
|
+
ensureInvariant();
|
|
429
|
+
const result = noble.encapsulate(params.encapsulationKey);
|
|
430
|
+
return {
|
|
431
|
+
ciphertext: result.ciphertext,
|
|
432
|
+
sharedSecret: result.sharedSecret
|
|
433
|
+
};
|
|
434
|
+
},
|
|
435
|
+
decapsulate(params) {
|
|
436
|
+
ensureInvariant();
|
|
437
|
+
return noble.decapsulate(params.ciphertext, params.decapsulationKey);
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
function makeDhAdapter(noble, manifest) {
|
|
442
|
+
let checked = false;
|
|
443
|
+
const alg = manifest.algorithm;
|
|
444
|
+
function ensureInvariant() {
|
|
445
|
+
if (checked) return;
|
|
446
|
+
const sk = noble.utils.randomPrivateKey();
|
|
447
|
+
const pk = noble.getPublicKey(sk);
|
|
448
|
+
if (pk.length !== manifest.publicKeyBytes) {
|
|
449
|
+
throw new Error(`${alg}: publicKey ${pk.length}B, expected ${manifest.publicKeyBytes}B`);
|
|
450
|
+
}
|
|
451
|
+
if (sk.length !== manifest.secretKeyBytes) {
|
|
452
|
+
throw new Error(`${alg}: secretKey ${sk.length}B, expected ${manifest.secretKeyBytes}B`);
|
|
453
|
+
}
|
|
454
|
+
const sk2 = noble.utils.randomPrivateKey();
|
|
455
|
+
const pk2 = noble.getPublicKey(sk2);
|
|
456
|
+
const ss1 = noble.getSharedSecret(sk, pk2);
|
|
457
|
+
const ss2 = noble.getSharedSecret(sk2, pk);
|
|
458
|
+
if (ss1.length !== manifest.sharedSecretBytes) {
|
|
459
|
+
throw new Error(`${alg}: sharedSecret ${ss1.length}B, expected ${manifest.sharedSecretBytes}B`);
|
|
460
|
+
}
|
|
461
|
+
for (let i = 0; i < ss1.length; i++) {
|
|
462
|
+
if (ss1[i] !== ss2[i]) throw new Error(`${alg}: DH commutativity failed`);
|
|
463
|
+
}
|
|
464
|
+
checked = true;
|
|
465
|
+
}
|
|
466
|
+
return {
|
|
467
|
+
keygen() {
|
|
468
|
+
ensureInvariant();
|
|
469
|
+
const secretKey = noble.utils.randomPrivateKey();
|
|
470
|
+
const publicKey = noble.getPublicKey(secretKey);
|
|
471
|
+
return {
|
|
472
|
+
publicKey,
|
|
473
|
+
secretKey
|
|
474
|
+
};
|
|
475
|
+
},
|
|
476
|
+
sharedSecret(params) {
|
|
477
|
+
ensureInvariant();
|
|
478
|
+
return noble.getSharedSecret(params.ourSecretKey, params.theirPublicKey);
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
function makeSecretboxAdapter(impl, manifest) {
|
|
483
|
+
let checked = false;
|
|
484
|
+
const alg = manifest.algorithm;
|
|
485
|
+
function ensureInvariant() {
|
|
486
|
+
if (checked) return;
|
|
487
|
+
const key = new Uint8Array(manifest.keyBytes);
|
|
488
|
+
crypto.getRandomValues(key);
|
|
489
|
+
const nonce = new Uint8Array(manifest.nonceBytes);
|
|
490
|
+
crypto.getRandomValues(nonce);
|
|
491
|
+
const plaintext = new Uint8Array([202, 254, 186, 190]);
|
|
492
|
+
const ct = impl.seal(key, plaintext, nonce);
|
|
493
|
+
const overhead = ct.length - plaintext.length;
|
|
494
|
+
if (overhead !== manifest.tagBytes) {
|
|
495
|
+
throw new Error(`${alg}: overhead ${overhead}B, expected ${manifest.tagBytes}B tag`);
|
|
496
|
+
}
|
|
497
|
+
const pt = impl.open(key, ct, nonce);
|
|
498
|
+
if (!pt || pt.length !== plaintext.length) {
|
|
499
|
+
throw new Error(`${alg}: seal/open roundtrip failed`);
|
|
500
|
+
}
|
|
501
|
+
for (let i = 0; i < plaintext.length; i++) {
|
|
502
|
+
if (pt[i] !== plaintext[i]) throw new Error(`${alg}: seal/open roundtrip mismatch`);
|
|
503
|
+
}
|
|
504
|
+
checked = true;
|
|
505
|
+
}
|
|
506
|
+
return {
|
|
507
|
+
seal(params) {
|
|
508
|
+
ensureInvariant();
|
|
509
|
+
return impl.seal(params.key, params.plaintext, params.nonce);
|
|
510
|
+
},
|
|
511
|
+
open(params) {
|
|
512
|
+
ensureInvariant();
|
|
513
|
+
return impl.open(params.key, params.ciphertext, params.nonce);
|
|
514
|
+
}
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
function asBoxKey(v) {
|
|
518
|
+
return v;
|
|
519
|
+
}
|
|
520
|
+
function asBoxNonce(v) {
|
|
521
|
+
return v;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
export { ML_DSA_65, ML_KEM_768, X25519, XSALSA20_POLY1305, asBoxKey, asBoxNonce, asDilithiumPublicKey, asDilithiumSecretKey, asKyberDecapsulationKey, asKyberEncapsulationKey, asMessage, asNonce, asSigMessage, asSymmetricKey, asX25519PublicKey, asX25519SecretKey, assertDhPrimitive, assertKemPrimitive, assertSignaturePrimitive, dilithium_exports as dilithium, kyber_exports as kyber, makeDhAdapter, makeKemAdapter, makeSecretboxAdapter, makeSignatureAdapter, secretbox_exports as secretbox, x25519_exports as x25519 };
|
package/dist/fs/index.cjs
CHANGED
|
@@ -4,6 +4,7 @@ var hashWasm = require('hash-wasm');
|
|
|
4
4
|
var sha2_js = require('@noble/hashes/sha2.js');
|
|
5
5
|
var hmac_js = require('@noble/hashes/hmac.js');
|
|
6
6
|
var nacl2 = require('tweetnacl');
|
|
7
|
+
var mlKem_js = require('@noble/post-quantum/ml-kem.js');
|
|
7
8
|
|
|
8
9
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
10
|
|
|
@@ -548,50 +549,21 @@ function getAlgorithmName(suiteId) {
|
|
|
548
549
|
}
|
|
549
550
|
return `Unknown (0x${suiteId.toString(16)})`;
|
|
550
551
|
}
|
|
551
|
-
var kyberModule = null;
|
|
552
|
-
async function loadKyber() {
|
|
553
|
-
if (kyberModule) return kyberModule;
|
|
554
|
-
try {
|
|
555
|
-
const m = await import('kyber-crystals');
|
|
556
|
-
const k = m.default ?? m;
|
|
557
|
-
kyberModule = k.kyber ?? k;
|
|
558
|
-
return kyberModule;
|
|
559
|
-
} catch (e) {
|
|
560
|
-
console.warn("[Kyber] Failed to load kyber-crystals:", e);
|
|
561
|
-
return null;
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
552
|
async function isKyberAvailable() {
|
|
565
|
-
|
|
566
|
-
return mod !== null;
|
|
553
|
+
return true;
|
|
567
554
|
}
|
|
568
555
|
async function kyberEncapsulate(pubKeyB64) {
|
|
569
|
-
const kyber = await loadKyber();
|
|
570
|
-
if (!kyber?.encrypt) {
|
|
571
|
-
throw new Error("Kyber encrypt not available");
|
|
572
|
-
}
|
|
573
556
|
const pk = ub64(pubKeyB64);
|
|
574
|
-
const
|
|
575
|
-
const ctRaw = r?.ciphertext ?? r?.cyphertext ?? r?.ct ?? r?.bytes?.ciphertext ?? r?.bytes?.cyphertext ?? r?.bytes?.ct ?? (Array.isArray(r) ? r[0] : void 0);
|
|
576
|
-
const ssRaw = r?.key ?? r?.sharedSecret ?? r?.secret ?? r?.bytes?.key ?? r?.bytes?.sharedSecret ?? (Array.isArray(r) ? r[1] : void 0);
|
|
577
|
-
if (!ctRaw || !ssRaw) {
|
|
578
|
-
throw new Error("Kyber encapsulate failed: missing ciphertext or shared secret");
|
|
579
|
-
}
|
|
557
|
+
const enc = mlKem_js.ml_kem1024.encapsulate(pk);
|
|
580
558
|
return {
|
|
581
|
-
ciphertext:
|
|
582
|
-
sharedSecret:
|
|
559
|
+
ciphertext: enc.cipherText,
|
|
560
|
+
sharedSecret: enc.sharedSecret
|
|
583
561
|
};
|
|
584
562
|
}
|
|
585
563
|
async function kyberDecapsulate(kemCiphertextB64, secretKeyB64) {
|
|
586
|
-
const kyber = await loadKyber();
|
|
587
|
-
if (!kyber?.decrypt && !kyber?.decapsulate) {
|
|
588
|
-
throw new Error("Kyber decrypt/decapsulate not available");
|
|
589
|
-
}
|
|
590
564
|
const ct = ub64(kemCiphertextB64);
|
|
591
565
|
const sk = ub64(secretKeyB64);
|
|
592
|
-
|
|
593
|
-
const key = r && (r.key ?? r.sharedSecret) ? r.key ?? r.sharedSecret : r;
|
|
594
|
-
return new Uint8Array(key);
|
|
566
|
+
return mlKem_js.ml_kem1024.decapsulate(ct, sk);
|
|
595
567
|
}
|
|
596
568
|
|
|
597
569
|
// src/fs/encrypt.ts
|
package/dist/fs/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { argon2id } from 'hash-wasm';
|
|
|
2
2
|
import { sha256 as sha256$1 } from '@noble/hashes/sha2.js';
|
|
3
3
|
import { hmac } from '@noble/hashes/hmac.js';
|
|
4
4
|
import nacl2 from 'tweetnacl';
|
|
5
|
+
import { ml_kem1024 } from '@noble/post-quantum/ml-kem.js';
|
|
5
6
|
|
|
6
7
|
// src/fs/types.ts
|
|
7
8
|
var OQE_MAGIC = new Uint8Array([79, 81, 69, 70]);
|
|
@@ -542,50 +543,21 @@ function getAlgorithmName(suiteId) {
|
|
|
542
543
|
}
|
|
543
544
|
return `Unknown (0x${suiteId.toString(16)})`;
|
|
544
545
|
}
|
|
545
|
-
var kyberModule = null;
|
|
546
|
-
async function loadKyber() {
|
|
547
|
-
if (kyberModule) return kyberModule;
|
|
548
|
-
try {
|
|
549
|
-
const m = await import('kyber-crystals');
|
|
550
|
-
const k = m.default ?? m;
|
|
551
|
-
kyberModule = k.kyber ?? k;
|
|
552
|
-
return kyberModule;
|
|
553
|
-
} catch (e) {
|
|
554
|
-
console.warn("[Kyber] Failed to load kyber-crystals:", e);
|
|
555
|
-
return null;
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
546
|
async function isKyberAvailable() {
|
|
559
|
-
|
|
560
|
-
return mod !== null;
|
|
547
|
+
return true;
|
|
561
548
|
}
|
|
562
549
|
async function kyberEncapsulate(pubKeyB64) {
|
|
563
|
-
const kyber = await loadKyber();
|
|
564
|
-
if (!kyber?.encrypt) {
|
|
565
|
-
throw new Error("Kyber encrypt not available");
|
|
566
|
-
}
|
|
567
550
|
const pk = ub64(pubKeyB64);
|
|
568
|
-
const
|
|
569
|
-
const ctRaw = r?.ciphertext ?? r?.cyphertext ?? r?.ct ?? r?.bytes?.ciphertext ?? r?.bytes?.cyphertext ?? r?.bytes?.ct ?? (Array.isArray(r) ? r[0] : void 0);
|
|
570
|
-
const ssRaw = r?.key ?? r?.sharedSecret ?? r?.secret ?? r?.bytes?.key ?? r?.bytes?.sharedSecret ?? (Array.isArray(r) ? r[1] : void 0);
|
|
571
|
-
if (!ctRaw || !ssRaw) {
|
|
572
|
-
throw new Error("Kyber encapsulate failed: missing ciphertext or shared secret");
|
|
573
|
-
}
|
|
551
|
+
const enc = ml_kem1024.encapsulate(pk);
|
|
574
552
|
return {
|
|
575
|
-
ciphertext:
|
|
576
|
-
sharedSecret:
|
|
553
|
+
ciphertext: enc.cipherText,
|
|
554
|
+
sharedSecret: enc.sharedSecret
|
|
577
555
|
};
|
|
578
556
|
}
|
|
579
557
|
async function kyberDecapsulate(kemCiphertextB64, secretKeyB64) {
|
|
580
|
-
const kyber = await loadKyber();
|
|
581
|
-
if (!kyber?.decrypt && !kyber?.decapsulate) {
|
|
582
|
-
throw new Error("Kyber decrypt/decapsulate not available");
|
|
583
|
-
}
|
|
584
558
|
const ct = ub64(kemCiphertextB64);
|
|
585
559
|
const sk = ub64(secretKeyB64);
|
|
586
|
-
|
|
587
|
-
const key = r && (r.key ?? r.sharedSecret) ? r.key ?? r.sharedSecret : r;
|
|
588
|
-
return new Uint8Array(key);
|
|
560
|
+
return ml_kem1024.decapsulate(ct, sk);
|
|
589
561
|
}
|
|
590
562
|
|
|
591
563
|
// src/fs/encrypt.ts
|