@omnituum/pqc-shared 0.4.1 → 0.6.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.
Files changed (39) hide show
  1. package/dist/crypto/index.cjs +81 -54
  2. package/dist/crypto/index.d.cts +33 -11
  3. package/dist/crypto/index.d.ts +33 -11
  4. package/dist/crypto/index.js +81 -54
  5. package/dist/{decrypt-eSHlbh1j.d.cts → decrypt-O1iqFIDL.d.cts} +80 -6
  6. package/dist/{decrypt-eSHlbh1j.d.ts → decrypt-O1iqFIDL.d.ts} +80 -6
  7. package/dist/fs/index.cjs +286 -147
  8. package/dist/fs/index.d.cts +28 -7
  9. package/dist/fs/index.d.ts +28 -7
  10. package/dist/fs/index.js +277 -147
  11. package/dist/index.cjs +358 -229
  12. package/dist/index.d.cts +4 -4
  13. package/dist/index.d.ts +4 -4
  14. package/dist/index.js +357 -230
  15. package/dist/{integrity-BenoFsmP.d.cts → integrity-BkBDrHA-.d.cts} +12 -4
  16. package/dist/{integrity-D9J98Bty.d.ts → integrity-DsFJv5c-.d.ts} +12 -4
  17. package/dist/{types-CRka8PC7.d.cts → types-DmnVueAY.d.cts} +14 -4
  18. package/dist/{types-CRka8PC7.d.ts → types-DmnVueAY.d.ts} +14 -4
  19. package/dist/utils/index.cjs +7 -10
  20. package/dist/utils/index.d.cts +2 -2
  21. package/dist/utils/index.d.ts +2 -2
  22. package/dist/utils/index.js +7 -10
  23. package/dist/vault/index.cjs +8 -11
  24. package/dist/vault/index.d.cts +2 -2
  25. package/dist/vault/index.d.ts +2 -2
  26. package/dist/vault/index.js +8 -11
  27. package/package.json +7 -7
  28. package/src/crypto/dilithium.ts +4 -4
  29. package/src/crypto/hybrid.ts +182 -80
  30. package/src/crypto/index.ts +2 -0
  31. package/src/fs/decrypt.ts +145 -68
  32. package/src/fs/encrypt.ts +161 -112
  33. package/src/fs/format.ts +110 -15
  34. package/src/fs/index.ts +4 -0
  35. package/src/fs/types.ts +77 -6
  36. package/src/index.ts +4 -0
  37. package/src/utils/integrity.ts +16 -15
  38. package/src/vault/manager.ts +1 -1
  39. package/src/version.ts +29 -7
package/dist/index.js CHANGED
@@ -201,17 +201,20 @@ function kyberUnwrapKey(sharedSecret, nonceB64, wrappedB64) {
201
201
  return nacl4.secretbox.open(wrapped, nonce, kek) || null;
202
202
  }
203
203
  var ENVELOPE_VERSION = OMNI_VERSIONS.HYBRID_V1;
204
+ var ENVELOPE_VERSION_V2 = OMNI_VERSIONS.HYBRID_V2;
204
205
  var ENVELOPE_VERSION_LEGACY = DEPRECATED_VERSIONS.PQC_DEMO_HYBRID_V1;
205
206
  var VAULT_VERSION = "omnituum.vault.v1";
206
207
  var VAULT_ENCRYPTED_VERSION = "omnituum.vault.enc.v1";
207
208
  var VAULT_ENCRYPTED_VERSION_V2 = "omnituum.vault.enc.v2";
208
209
  var ENVELOPE_SUITE = "x25519+kyber768";
210
+ var ENVELOPE_SUITE_V2 = "x25519+mlkem1024";
209
211
  var ENVELOPE_AEAD = "xsalsa20poly1305";
210
212
  var VAULT_KDF = "PBKDF2-SHA256";
211
213
  var VAULT_KDF_V2 = "Argon2id";
212
214
  var VAULT_ALGORITHM = "AES-256-GCM";
213
215
  var SUPPORTED_ENVELOPE_VERSIONS = [
214
216
  ENVELOPE_VERSION,
217
+ ENVELOPE_VERSION_V2,
215
218
  ENVELOPE_VERSION_LEGACY
216
219
  ];
217
220
  var SUPPORTED_VAULT_VERSIONS = [
@@ -256,6 +259,8 @@ function isVaultVersionSupported(version) {
256
259
  function isVaultEncryptedVersionSupported(version) {
257
260
  return SUPPORTED_VAULT_ENCRYPTED_VERSIONS.includes(version);
258
261
  }
262
+ var ENVELOPE_V1_FIELDS = ["x25519Epk", "x25519Wrap", "kyberKemCt", "kyberWrap", "contentNonce", "ciphertext", "meta"];
263
+ var ENVELOPE_V2_FIELDS = ["x25519Epk", "kyberKemCt", "ckWrap", "contentNonce", "ciphertext", "meta"];
259
264
  function validateEnvelope(envelope) {
260
265
  const errors = [];
261
266
  if (!envelope || typeof envelope !== "object") {
@@ -267,14 +272,16 @@ function validateEnvelope(envelope) {
267
272
  } else if (!isEnvelopeVersionSupported(env.v)) {
268
273
  errors.push(`Unsupported envelope version: ${env.v}`);
269
274
  }
270
- if (env.suite !== ENVELOPE_SUITE) {
271
- errors.push(`Invalid suite: expected "${ENVELOPE_SUITE}", got "${env.suite}"`);
275
+ const isV2 = env.v === ENVELOPE_VERSION_V2;
276
+ const expectedSuite = isV2 ? ENVELOPE_SUITE_V2 : ENVELOPE_SUITE;
277
+ const requiredFields = isV2 ? ENVELOPE_V2_FIELDS : ENVELOPE_V1_FIELDS;
278
+ if (env.suite !== expectedSuite) {
279
+ errors.push(`Invalid suite: expected "${expectedSuite}", got "${env.suite}"`);
272
280
  }
273
281
  if (env.aead !== ENVELOPE_AEAD) {
274
282
  errors.push(`Invalid aead: expected "${ENVELOPE_AEAD}", got "${env.aead}"`);
275
283
  }
276
- const required = ["x25519Epk", "x25519Wrap", "kyberKemCt", "kyberWrap", "contentNonce", "ciphertext", "meta"];
277
- for (const field of required) {
284
+ for (const field of requiredFields) {
278
285
  if (!(field in env)) {
279
286
  errors.push(`Missing required field: ${field}`);
280
287
  }
@@ -369,7 +376,6 @@ async function generateHybridIdentity(name) {
369
376
  const x25519 = generateX25519Keypair();
370
377
  const kyber = await generateKyberKeypair();
371
378
  if (!kyber) {
372
- console.error("Kyber key generation failed - library not available");
373
379
  return null;
374
380
  }
375
381
  return {
@@ -395,49 +401,74 @@ function getSecretKeys(identity) {
395
401
  kyberSecB64: identity.kyberSecB64
396
402
  };
397
403
  }
404
+ function combinedKekV2(kyberShared, x25519Shared, x25519EpkHex, kyberKemCtB64) {
405
+ const ikm = new Uint8Array(kyberShared.length + x25519Shared.length);
406
+ ikm.set(kyberShared, 0);
407
+ ikm.set(x25519Shared, kyberShared.length);
408
+ try {
409
+ return hkdfFlex(ikm, "omnituum/hybrid-v2", `wrap-ck|${x25519EpkHex}|${kyberKemCtB64}`);
410
+ } finally {
411
+ ikm.fill(0);
412
+ }
413
+ }
398
414
  async function hybridEncrypt(plaintext, recipientPublicKeys, sender) {
399
415
  const pt = typeof plaintext === "string" ? textEncoder.encode(plaintext) : plaintext;
400
416
  const CK = rand32();
401
- const contentNonce = rand24();
402
- const ciphertext = nacl4.secretbox(pt, contentNonce, CK);
403
- const x25519EphKp = nacl4.box.keyPair();
404
- const recipientX25519Pk = fromHex(recipientPublicKeys.x25519PubHex);
405
- const x25519Shared = nacl4.scalarMult(x25519EphKp.secretKey, recipientX25519Pk);
406
- const x25519Kek = hkdfFlex(x25519Shared, "omnituum/x25519", "wrap-ck");
407
- const x25519WrapNonce = rand24();
408
- const x25519Wrapped = nacl4.secretbox(CK, x25519WrapNonce, x25519Kek);
409
- const kyberResult = await kyberEncapsulate(recipientPublicKeys.kyberPubB64);
410
- const kyberKek = hkdfFlex(kyberResult.sharedSecret, "omnituum/kyber", "wrap-ck");
411
- const kyberWrapNonce = rand24();
412
- const kyberWrapped = nacl4.secretbox(CK, kyberWrapNonce, kyberKek);
413
- return {
414
- v: ENVELOPE_VERSION,
415
- suite: ENVELOPE_SUITE,
416
- aead: ENVELOPE_AEAD,
417
- x25519Epk: toHex(x25519EphKp.publicKey),
418
- x25519Wrap: {
419
- nonce: b64(x25519WrapNonce),
420
- wrapped: b64(x25519Wrapped)
421
- },
422
- kyberKemCt: b64(kyberResult.ciphertext),
423
- kyberWrap: {
424
- nonce: b64(kyberWrapNonce),
425
- wrapped: b64(kyberWrapped)
426
- },
427
- contentNonce: b64(contentNonce),
428
- ciphertext: b64(ciphertext),
429
- meta: {
430
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
431
- senderName: sender?.name,
432
- senderId: sender?.id
433
- }
434
- };
417
+ try {
418
+ const contentNonce = rand24();
419
+ const ciphertext = nacl4.secretbox(pt, contentNonce, CK);
420
+ const x25519EphKp = nacl4.box.keyPair();
421
+ const recipientX25519Pk = fromHex(recipientPublicKeys.x25519PubHex);
422
+ const x25519Shared = nacl4.scalarMult(x25519EphKp.secretKey, recipientX25519Pk);
423
+ const x25519EpkHex = toHex(x25519EphKp.publicKey);
424
+ const kyberResult = await kyberEncapsulate(recipientPublicKeys.kyberPubB64);
425
+ const kyberKemCtB64 = b64(kyberResult.ciphertext);
426
+ const kek = combinedKekV2(kyberResult.sharedSecret, x25519Shared, x25519EpkHex, kyberKemCtB64);
427
+ const ckWrapNonce = rand24();
428
+ const ckWrapped = nacl4.secretbox(CK, ckWrapNonce, kek);
429
+ kek.fill(0);
430
+ x25519Shared.fill(0);
431
+ kyberResult.sharedSecret.fill(0);
432
+ x25519EphKp.secretKey.fill(0);
433
+ return {
434
+ v: ENVELOPE_VERSION_V2,
435
+ suite: ENVELOPE_SUITE_V2,
436
+ aead: ENVELOPE_AEAD,
437
+ x25519Epk: x25519EpkHex,
438
+ kyberKemCt: kyberKemCtB64,
439
+ ckWrap: {
440
+ nonce: b64(ckWrapNonce),
441
+ wrapped: b64(ckWrapped)
442
+ },
443
+ contentNonce: b64(contentNonce),
444
+ ciphertext: b64(ciphertext),
445
+ meta: {
446
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
447
+ senderName: sender?.name,
448
+ senderId: sender?.id
449
+ }
450
+ };
451
+ } finally {
452
+ CK.fill(0);
453
+ }
454
+ }
455
+ async function unwrapCkV2(envelope, secretKeys) {
456
+ const kyberShared = await kyberDecapsulate(envelope.kyberKemCt, secretKeys.kyberSecB64);
457
+ const ephPk = fromHex(envelope.x25519Epk);
458
+ const sk = fromHex(secretKeys.x25519SecHex);
459
+ const x25519Shared = nacl4.scalarMult(sk, ephPk);
460
+ const kek = combinedKekV2(kyberShared, x25519Shared, envelope.x25519Epk, envelope.kyberKemCt);
461
+ kyberShared.fill(0);
462
+ x25519Shared.fill(0);
463
+ const CK = nacl4.secretbox.open(ub64(envelope.ckWrap.wrapped), ub64(envelope.ckWrap.nonce), kek);
464
+ kek.fill(0);
465
+ if (!CK) {
466
+ throw new Error("Could not unwrap content key \u2014 combined-KEK authentication failed");
467
+ }
468
+ return CK;
435
469
  }
436
- async function hybridDecrypt(envelope, secretKeys) {
437
- const version = envelope.v;
438
- assertEnvelopeVersion(version);
470
+ async function unwrapCkV1(envelope, secretKeys, saltPrefix) {
439
471
  let CK = null;
440
- const saltPrefix = version === "pqc-demo.hybrid.v1" ? "pqc-demo" : "omnituum";
441
472
  try {
442
473
  const kyberShared = await kyberDecapsulate(envelope.kyberKemCt, secretKeys.kyberSecB64);
443
474
  const kyberKek = hkdfFlex(kyberShared, `${saltPrefix}/kyber`, "wrap-ck");
@@ -446,11 +477,7 @@ async function hybridDecrypt(envelope, secretKeys) {
446
477
  ub64(envelope.kyberWrap.nonce),
447
478
  kyberKek
448
479
  );
449
- if (CK) {
450
- console.log("[Hybrid] Decrypted using Kyber (post-quantum)");
451
- }
452
- } catch (e) {
453
- console.warn("[Hybrid] Kyber decapsulation failed:", e);
480
+ } catch {
454
481
  }
455
482
  if (!CK) {
456
483
  try {
@@ -463,21 +490,28 @@ async function hybridDecrypt(envelope, secretKeys) {
463
490
  ub64(envelope.x25519Wrap.nonce),
464
491
  x25519Kek
465
492
  );
466
- if (CK) {
467
- console.log("[Hybrid] Decrypted using X25519 (classical)");
468
- }
469
- } catch (e) {
470
- console.warn("[Hybrid] X25519 decryption failed:", e);
493
+ } catch {
471
494
  }
472
495
  }
473
496
  if (!CK) {
474
497
  throw new Error("Could not unwrap content key with either algorithm");
475
498
  }
499
+ return CK;
500
+ }
501
+ async function hybridDecrypt(envelope, secretKeys) {
502
+ const version = envelope.v;
503
+ assertEnvelopeVersion(version);
504
+ const CK = version === ENVELOPE_VERSION_V2 ? await unwrapCkV2(envelope, secretKeys) : await unwrapCkV1(
505
+ envelope,
506
+ secretKeys,
507
+ version === "pqc-demo.hybrid.v1" ? "pqc-demo" : "omnituum"
508
+ );
476
509
  const pt = nacl4.secretbox.open(
477
510
  ub64(envelope.ciphertext),
478
511
  ub64(envelope.contentNonce),
479
512
  CK
480
513
  );
514
+ CK.fill(0);
481
515
  if (!pt) {
482
516
  throw new Error("Content authentication failed");
483
517
  }
@@ -496,8 +530,7 @@ async function loadDilithium() {
496
530
  const { ml_dsa65 } = await import('@noble/post-quantum/ml-dsa.js');
497
531
  dilithiumModule = ml_dsa65;
498
532
  return dilithiumModule;
499
- } catch (e) {
500
- console.warn("[Dilithium] Failed to load @noble/post-quantum:", e);
533
+ } catch {
501
534
  return null;
502
535
  }
503
536
  }
@@ -515,8 +548,7 @@ async function generateDilithiumKeypair() {
515
548
  publicB64: toB64(kp.publicKey),
516
549
  secretB64: toB64(kp.secretKey)
517
550
  };
518
- } catch (e) {
519
- console.warn("[Dilithium] Key generation failed:", e);
551
+ } catch {
520
552
  return null;
521
553
  }
522
554
  }
@@ -855,17 +887,11 @@ function computeIntegrityHash(identities) {
855
887
  createdAt: i.createdAt,
856
888
  rotationCount: i.rotationCount
857
889
  }));
858
- const serialized = JSON.stringify(canonical, Object.keys(canonical[0] || {}).sort());
890
+ const serialized = JSON.stringify(canonical);
859
891
  return computeStringHash(serialized);
860
892
  }
861
893
  function computeStringHash(str) {
862
- let hash = 0;
863
- for (let i = 0; i < str.length; i++) {
864
- const char = str.charCodeAt(i);
865
- hash = (hash << 5) - hash + char;
866
- hash = hash & hash;
867
- }
868
- return Math.abs(hash).toString(16).padStart(16, "0");
894
+ return toHex(sha256(textEncoder.encode(str)));
869
895
  }
870
896
  async function computeHashAsync(data) {
871
897
  const encoder = new TextEncoder();
@@ -1116,7 +1142,6 @@ async function createIdentity(name) {
1116
1142
  const x25519 = generateX25519Keypair();
1117
1143
  const kyber = await generateKyberKeypair();
1118
1144
  if (!kyber) {
1119
- console.error("Kyber key generation failed");
1120
1145
  return null;
1121
1146
  }
1122
1147
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -1339,12 +1364,22 @@ async function migrateEncryptedVault(options) {
1339
1364
 
1340
1365
  // src/fs/types.ts
1341
1366
  var OQE_MAGIC = new Uint8Array([79, 81, 69, 70]);
1342
- var OQE_FORMAT_VERSION = 1;
1367
+ var OQE_FORMAT_VERSION_V1 = 1;
1368
+ var OQE_FORMAT_VERSION_V2 = 2;
1369
+ var SUPPORTED_OQE_VERSIONS = [OQE_FORMAT_VERSION_V1, OQE_FORMAT_VERSION_V2];
1343
1370
  var ALGORITHM_SUITES = {
1344
- /** Hybrid: X25519 ECDH + Kyber768 KEM + AES-256-GCM */
1371
+ /**
1372
+ * LEGACY (read-only): Hybrid X25519 + Kyber with independent per-primitive
1373
+ * wraps (either secret unwraps). Only appears in v1 files. Never written.
1374
+ */
1345
1375
  HYBRID_X25519_KYBER768_AES256GCM: 1,
1346
1376
  /** Password: Argon2id + AES-256-GCM */
1347
- PASSWORD_ARGON2ID_AES256GCM: 2
1377
+ PASSWORD_ARGON2ID_AES256GCM: 2,
1378
+ /**
1379
+ * Hybrid X25519 + ML-KEM-1024 with a single AND-combined KEK wrap
1380
+ * (HKDF(ss_mlkem || ss_x25519), transcript-bound). Written by v2.
1381
+ */
1382
+ HYBRID_X25519_MLKEM1024_AES256GCM: 3
1348
1383
  };
1349
1384
  var DEFAULT_ARGON2ID_PARAMS = {
1350
1385
  memoryCost: 65536,
@@ -1354,7 +1389,8 @@ var DEFAULT_ARGON2ID_PARAMS = {
1354
1389
  hashLength: 32,
1355
1390
  saltLength: 32
1356
1391
  };
1357
- var OQE_HEADER_SIZE = 30;
1392
+ var OQE_HEADER_SIZE_V1 = 30;
1393
+ var OQE_HEADER_SIZE_V2 = 42;
1358
1394
  var OQEError = class extends Error {
1359
1395
  constructor(code, message) {
1360
1396
  super(message);
@@ -1424,6 +1460,7 @@ function toArrayBuffer(arr) {
1424
1460
  }
1425
1461
  var AES_KEY_SIZE = 32;
1426
1462
  var AES_GCM_IV_SIZE = 12;
1463
+ var AES_GCM_TAG_SIZE = 16;
1427
1464
  async function importAesKey(keyBytes) {
1428
1465
  if (keyBytes.length !== AES_KEY_SIZE) {
1429
1466
  throw new Error(`AES key must be ${AES_KEY_SIZE} bytes, got ${keyBytes.length}`);
@@ -1447,7 +1484,7 @@ async function aesEncrypt(plaintext, key, iv, additionalData) {
1447
1484
  {
1448
1485
  name: "AES-GCM",
1449
1486
  iv: toArrayBuffer(ivBytes),
1450
- additionalData: void 0,
1487
+ additionalData: additionalData ? toArrayBuffer(additionalData) : void 0,
1451
1488
  tagLength: 128
1452
1489
  // 16 bytes
1453
1490
  },
@@ -1483,6 +1520,9 @@ async function aesDecrypt(ciphertext, key, iv, additionalData) {
1483
1520
  }
1484
1521
 
1485
1522
  // src/fs/format.ts
1523
+ function oqeHeaderSize(version) {
1524
+ return version === OQE_FORMAT_VERSION_V2 ? OQE_HEADER_SIZE_V2 : OQE_HEADER_SIZE_V1;
1525
+ }
1486
1526
  function writeUint32BE(value) {
1487
1527
  const buffer = new ArrayBuffer(4);
1488
1528
  new DataView(buffer).setUint32(0, value, false);
@@ -1500,7 +1540,8 @@ function readUint16BE(data, offset) {
1500
1540
  return new DataView(data.buffer, data.byteOffset + offset).getUint16(0, false);
1501
1541
  }
1502
1542
  function writeOQEHeader(header) {
1503
- const buffer = new Uint8Array(OQE_HEADER_SIZE);
1543
+ const isV2 = header.version === OQE_FORMAT_VERSION_V2;
1544
+ const buffer = new Uint8Array(oqeHeaderSize(header.version));
1504
1545
  let offset = 0;
1505
1546
  buffer.set(OQE_MAGIC, offset);
1506
1547
  offset += 4;
@@ -1513,11 +1554,18 @@ function writeOQEHeader(header) {
1513
1554
  buffer.set(writeUint32BE(header.keyMaterialLength), offset);
1514
1555
  offset += 4;
1515
1556
  buffer.set(header.iv, offset);
1557
+ offset += 12;
1558
+ if (isV2) {
1559
+ if (!header.contentIv || header.contentIv.length !== 12) {
1560
+ throw new OQEError("INVALID_HEADER", "v2 header requires a 12-byte contentIv");
1561
+ }
1562
+ buffer.set(header.contentIv, offset);
1563
+ }
1516
1564
  return buffer;
1517
1565
  }
1518
1566
  function parseOQEHeader(data) {
1519
- if (data.length < OQE_HEADER_SIZE) {
1520
- throw new OQEError("INVALID_HEADER", `File too small: need ${OQE_HEADER_SIZE} bytes, got ${data.length}`);
1567
+ if (data.length < OQE_HEADER_SIZE_V1) {
1568
+ throw new OQEError("INVALID_HEADER", `File too small: need at least ${OQE_HEADER_SIZE_V1} bytes, got ${data.length}`);
1521
1569
  }
1522
1570
  let offset = 0;
1523
1571
  const magic = data.slice(0, 4);
@@ -1526,14 +1574,19 @@ function parseOQEHeader(data) {
1526
1574
  }
1527
1575
  offset += 4;
1528
1576
  const version = data[offset++];
1529
- if (version !== OQE_FORMAT_VERSION) {
1577
+ if (!SUPPORTED_OQE_VERSIONS.includes(version)) {
1530
1578
  throw new OQEError("UNSUPPORTED_VERSION", `Unsupported OQE version: ${version}`);
1531
1579
  }
1532
1580
  const algorithmSuiteRaw = data[offset++];
1533
- if (algorithmSuiteRaw !== ALGORITHM_SUITES.HYBRID_X25519_KYBER768_AES256GCM && algorithmSuiteRaw !== ALGORITHM_SUITES.PASSWORD_ARGON2ID_AES256GCM) {
1581
+ if (algorithmSuiteRaw !== ALGORITHM_SUITES.HYBRID_X25519_KYBER768_AES256GCM && algorithmSuiteRaw !== ALGORITHM_SUITES.HYBRID_X25519_MLKEM1024_AES256GCM && algorithmSuiteRaw !== ALGORITHM_SUITES.PASSWORD_ARGON2ID_AES256GCM) {
1534
1582
  throw new OQEError("UNSUPPORTED_ALGORITHM", `Unsupported algorithm suite: 0x${algorithmSuiteRaw.toString(16)}`);
1535
1583
  }
1536
1584
  const algorithmSuite = algorithmSuiteRaw;
1585
+ const isV2 = version === OQE_FORMAT_VERSION_V2;
1586
+ const headerSize = oqeHeaderSize(version);
1587
+ if (data.length < headerSize) {
1588
+ throw new OQEError("INVALID_HEADER", `Truncated v${version} header: need ${headerSize} bytes, got ${data.length}`);
1589
+ }
1537
1590
  const flags = readUint32BE(data, offset);
1538
1591
  offset += 4;
1539
1592
  const metadataLength = readUint32BE(data, offset);
@@ -1541,35 +1594,18 @@ function parseOQEHeader(data) {
1541
1594
  const keyMaterialLength = readUint32BE(data, offset);
1542
1595
  offset += 4;
1543
1596
  const iv = data.slice(offset, offset + 12);
1597
+ offset += 12;
1598
+ const contentIv = isV2 ? data.slice(offset, offset + 12) : void 0;
1544
1599
  return {
1545
1600
  version,
1546
1601
  algorithmSuite,
1547
1602
  flags,
1548
1603
  metadataLength,
1549
1604
  keyMaterialLength,
1550
- iv
1605
+ iv,
1606
+ contentIv
1551
1607
  };
1552
1608
  }
1553
- function serializeHybridKeyMaterial(km) {
1554
- const parts = [];
1555
- parts.push(km.x25519EphemeralPk);
1556
- parts.push(km.x25519Nonce);
1557
- parts.push(writeUint16BE(km.x25519WrappedKey.length));
1558
- parts.push(km.x25519WrappedKey);
1559
- parts.push(writeUint16BE(km.kyberCiphertext.length));
1560
- parts.push(km.kyberCiphertext);
1561
- parts.push(km.kyberNonce);
1562
- parts.push(writeUint16BE(km.kyberWrappedKey.length));
1563
- parts.push(km.kyberWrappedKey);
1564
- const totalLength = parts.reduce((sum, p) => sum + p.length, 0);
1565
- const result = new Uint8Array(totalLength);
1566
- let offset = 0;
1567
- for (const part of parts) {
1568
- result.set(part, offset);
1569
- offset += part.length;
1570
- }
1571
- return result;
1572
- }
1573
1609
  function parseHybridKeyMaterial(data) {
1574
1610
  let offset = 0;
1575
1611
  const x25519EphemeralPk = data.slice(offset, offset + 32);
@@ -1598,6 +1634,38 @@ function parseHybridKeyMaterial(data) {
1598
1634
  kyberWrappedKey
1599
1635
  };
1600
1636
  }
1637
+ function serializeHybridKeyMaterialV2(km) {
1638
+ const parts = [];
1639
+ parts.push(km.x25519EphemeralPk);
1640
+ parts.push(writeUint16BE(km.kyberCiphertext.length));
1641
+ parts.push(km.kyberCiphertext);
1642
+ parts.push(km.ckWrapNonce);
1643
+ parts.push(writeUint16BE(km.ckWrapped.length));
1644
+ parts.push(km.ckWrapped);
1645
+ const totalLength = parts.reduce((sum, p) => sum + p.length, 0);
1646
+ const result = new Uint8Array(totalLength);
1647
+ let offset = 0;
1648
+ for (const part of parts) {
1649
+ result.set(part, offset);
1650
+ offset += part.length;
1651
+ }
1652
+ return result;
1653
+ }
1654
+ function parseHybridKeyMaterialV2(data) {
1655
+ let offset = 0;
1656
+ const x25519EphemeralPk = data.slice(offset, offset + 32);
1657
+ offset += 32;
1658
+ const kyberCtLen = readUint16BE(data, offset);
1659
+ offset += 2;
1660
+ const kyberCiphertext = data.slice(offset, offset + kyberCtLen);
1661
+ offset += kyberCtLen;
1662
+ const ckWrapNonce = data.slice(offset, offset + 24);
1663
+ offset += 24;
1664
+ const ckWrappedLen = readUint16BE(data, offset);
1665
+ offset += 2;
1666
+ const ckWrapped = data.slice(offset, offset + ckWrappedLen);
1667
+ return { x25519EphemeralPk, kyberCiphertext, ckWrapNonce, ckWrapped };
1668
+ }
1601
1669
  function serializePasswordKeyMaterial(km) {
1602
1670
  const result = new Uint8Array(32 + 4 + 4 + 4);
1603
1671
  result.set(km.salt, 0);
@@ -1639,7 +1707,7 @@ function assembleOQEFile(components) {
1639
1707
  }
1640
1708
  function parseOQEFile(data) {
1641
1709
  const header = parseOQEHeader(data);
1642
- let offset = OQE_HEADER_SIZE;
1710
+ let offset = oqeHeaderSize(header.version);
1643
1711
  const keyMaterial = data.slice(offset, offset + header.keyMaterialLength);
1644
1712
  offset += header.keyMaterialLength;
1645
1713
  const encryptedMetadata = data.slice(offset, offset + header.metadataLength);
@@ -1659,61 +1727,81 @@ function addOQEExtension(filename) {
1659
1727
  }
1660
1728
  return `${filename}${OQE_EXTENSION}`;
1661
1729
  }
1662
- function hkdfFlex2(ikm, salt, info) {
1663
- return hkdfSha256(ikm, { salt: u8(salt), info: u8(info), length: 32 });
1664
- }
1730
+ var AES_GCM_TAG_LEN = AES_GCM_TAG_SIZE;
1665
1731
  function computeIdentityHash(publicKeyHex) {
1666
1732
  const hash = sha256(fromHex(publicKeyHex));
1667
1733
  return toHex(hash).slice(0, 16);
1668
1734
  }
1735
+ function combinedFileKekV2(kyberShared, x25519Shared, x25519EpkHex, kyberKemCtB64) {
1736
+ const ikm = new Uint8Array(kyberShared.length + x25519Shared.length);
1737
+ ikm.set(kyberShared, 0);
1738
+ ikm.set(x25519Shared, kyberShared.length);
1739
+ try {
1740
+ return hkdfSha256(ikm, {
1741
+ salt: u8("omnituum/fs/hybrid-v2"),
1742
+ info: u8(`wrap-content-key|${x25519EpkHex}|${kyberKemCtB64}`),
1743
+ length: 32
1744
+ });
1745
+ } finally {
1746
+ ikm.fill(0);
1747
+ }
1748
+ }
1669
1749
  async function encryptHybrid(plaintext, metadata, options) {
1670
1750
  if (!await isKyberAvailable()) {
1671
1751
  throw new OQEError("KYBER_UNAVAILABLE", "Kyber library not available in this environment");
1672
1752
  }
1673
1753
  const contentKey = rand32();
1674
- const iv = rand12();
1675
- const x25519EphKp = nacl4.box.keyPair();
1676
- const recipientX25519Pk = fromHex(options.recipientPublicKeys.x25519PubHex);
1677
- const x25519Shared = nacl4.scalarMult(x25519EphKp.secretKey, recipientX25519Pk);
1678
- const x25519Kek = hkdfFlex2(x25519Shared, "omnituum/fs/x25519", "wrap-content-key");
1679
- const x25519Nonce = rand24();
1680
- const x25519WrappedKey = nacl4.secretbox(contentKey, x25519Nonce, x25519Kek);
1681
- const kyberResult = await kyberEncapsulate(options.recipientPublicKeys.kyberPubB64);
1682
- const kyberKek = hkdfFlex2(kyberResult.sharedSecret, "omnituum/fs/kyber", "wrap-content-key");
1683
- const kyberNonce = rand24();
1684
- const kyberWrappedKey = nacl4.secretbox(contentKey, kyberNonce, kyberKek);
1685
- const keyMaterial = {
1686
- x25519EphemeralPk: x25519EphKp.publicKey,
1687
- x25519Nonce,
1688
- x25519WrappedKey,
1689
- kyberCiphertext: kyberResult.ciphertext,
1690
- kyberNonce,
1691
- kyberWrappedKey
1692
- };
1693
- const keyMaterialBytes = serializeHybridKeyMaterial(keyMaterial);
1694
- const metadataBytes = serializeMetadata(metadata);
1695
- const { ciphertext: encryptedMetadata } = await aesEncrypt(metadataBytes, contentKey, iv);
1696
- const { ciphertext: encryptedContent } = await aesEncrypt(plaintext, contentKey, iv);
1697
- const header = {
1698
- version: OQE_FORMAT_VERSION,
1699
- algorithmSuite: ALGORITHM_SUITES.HYBRID_X25519_KYBER768_AES256GCM,
1700
- flags: 0,
1701
- metadataLength: encryptedMetadata.length,
1702
- keyMaterialLength: keyMaterialBytes.length,
1703
- iv
1704
- };
1705
- const fileData = assembleOQEFile({
1706
- header,
1707
- keyMaterial: keyMaterialBytes,
1708
- encryptedMetadata,
1709
- encryptedContent
1710
- });
1711
- return {
1712
- data: fileData,
1713
- filename: addOQEExtension(metadata.filename),
1714
- metadata,
1715
- mode: "hybrid"
1716
- };
1754
+ try {
1755
+ const metadataIv = rand12();
1756
+ const contentIv = rand12();
1757
+ const x25519EphKp = nacl4.box.keyPair();
1758
+ const recipientX25519Pk = fromHex(options.recipientPublicKeys.x25519PubHex);
1759
+ const x25519Shared = nacl4.scalarMult(x25519EphKp.secretKey, recipientX25519Pk);
1760
+ const x25519EpkHex = toHex(x25519EphKp.publicKey);
1761
+ const kyberResult = await kyberEncapsulate(options.recipientPublicKeys.kyberPubB64);
1762
+ const kyberKemCtB64 = b64(kyberResult.ciphertext);
1763
+ const kek = combinedFileKekV2(kyberResult.sharedSecret, x25519Shared, x25519EpkHex, kyberKemCtB64);
1764
+ const ckWrapNonce = rand24();
1765
+ const ckWrapped = nacl4.secretbox(contentKey, ckWrapNonce, kek);
1766
+ kek.fill(0);
1767
+ x25519Shared.fill(0);
1768
+ kyberResult.sharedSecret.fill(0);
1769
+ x25519EphKp.secretKey.fill(0);
1770
+ const keyMaterial = {
1771
+ x25519EphemeralPk: x25519EphKp.publicKey,
1772
+ kyberCiphertext: kyberResult.ciphertext,
1773
+ ckWrapNonce,
1774
+ ckWrapped
1775
+ };
1776
+ const keyMaterialBytes = serializeHybridKeyMaterialV2(keyMaterial);
1777
+ const metadataBytes = serializeMetadata(metadata);
1778
+ const header = {
1779
+ version: OQE_FORMAT_VERSION_V2,
1780
+ algorithmSuite: ALGORITHM_SUITES.HYBRID_X25519_MLKEM1024_AES256GCM,
1781
+ flags: 0,
1782
+ metadataLength: metadataBytes.length + AES_GCM_TAG_LEN,
1783
+ keyMaterialLength: keyMaterialBytes.length,
1784
+ iv: metadataIv,
1785
+ contentIv
1786
+ };
1787
+ const aad = writeOQEHeader(header);
1788
+ const { ciphertext: encryptedMetadata } = await aesEncrypt(metadataBytes, contentKey, metadataIv, aad);
1789
+ const { ciphertext: encryptedContent } = await aesEncrypt(plaintext, contentKey, contentIv, aad);
1790
+ const fileData = assembleOQEFile({
1791
+ header,
1792
+ keyMaterial: keyMaterialBytes,
1793
+ encryptedMetadata,
1794
+ encryptedContent
1795
+ });
1796
+ return {
1797
+ data: fileData,
1798
+ filename: addOQEExtension(metadata.filename),
1799
+ metadata,
1800
+ mode: "hybrid"
1801
+ };
1802
+ } finally {
1803
+ contentKey.fill(0);
1804
+ }
1717
1805
  }
1718
1806
  async function encryptPassword(plaintext, metadata, options) {
1719
1807
  if (!await isArgon2Available()) {
@@ -1725,37 +1813,44 @@ async function encryptPassword(plaintext, metadata, options) {
1725
1813
  };
1726
1814
  const salt = generateArgon2Salt(params.saltLength);
1727
1815
  const contentKey = await deriveKeyFromPassword(options.password, salt, params);
1728
- const iv = rand12();
1729
- const keyMaterial = {
1730
- salt,
1731
- memoryCost: params.memoryCost,
1732
- timeCost: params.timeCost,
1733
- parallelism: params.parallelism
1734
- };
1735
- const keyMaterialBytes = serializePasswordKeyMaterial(keyMaterial);
1736
- const metadataBytes = serializeMetadata(metadata);
1737
- const { ciphertext: encryptedMetadata } = await aesEncrypt(metadataBytes, contentKey, iv);
1738
- const { ciphertext: encryptedContent } = await aesEncrypt(plaintext, contentKey, iv);
1739
- const header = {
1740
- version: OQE_FORMAT_VERSION,
1741
- algorithmSuite: ALGORITHM_SUITES.PASSWORD_ARGON2ID_AES256GCM,
1742
- flags: 0,
1743
- metadataLength: encryptedMetadata.length,
1744
- keyMaterialLength: keyMaterialBytes.length,
1745
- iv
1746
- };
1747
- const fileData = assembleOQEFile({
1748
- header,
1749
- keyMaterial: keyMaterialBytes,
1750
- encryptedMetadata,
1751
- encryptedContent
1752
- });
1753
- return {
1754
- data: fileData,
1755
- filename: addOQEExtension(metadata.filename),
1756
- metadata,
1757
- mode: "password"
1758
- };
1816
+ try {
1817
+ const metadataIv = rand12();
1818
+ const contentIv = rand12();
1819
+ const keyMaterial = {
1820
+ salt,
1821
+ memoryCost: params.memoryCost,
1822
+ timeCost: params.timeCost,
1823
+ parallelism: params.parallelism
1824
+ };
1825
+ const keyMaterialBytes = serializePasswordKeyMaterial(keyMaterial);
1826
+ const metadataBytes = serializeMetadata(metadata);
1827
+ const header = {
1828
+ version: OQE_FORMAT_VERSION_V2,
1829
+ algorithmSuite: ALGORITHM_SUITES.PASSWORD_ARGON2ID_AES256GCM,
1830
+ flags: 0,
1831
+ metadataLength: metadataBytes.length + AES_GCM_TAG_LEN,
1832
+ keyMaterialLength: keyMaterialBytes.length,
1833
+ iv: metadataIv,
1834
+ contentIv
1835
+ };
1836
+ const aad = writeOQEHeader(header);
1837
+ const { ciphertext: encryptedMetadata } = await aesEncrypt(metadataBytes, contentKey, metadataIv, aad);
1838
+ const { ciphertext: encryptedContent } = await aesEncrypt(plaintext, contentKey, contentIv, aad);
1839
+ const fileData = assembleOQEFile({
1840
+ header,
1841
+ keyMaterial: keyMaterialBytes,
1842
+ encryptedMetadata,
1843
+ encryptedContent
1844
+ });
1845
+ return {
1846
+ data: fileData,
1847
+ filename: addOQEExtension(metadata.filename),
1848
+ metadata,
1849
+ mode: "password"
1850
+ };
1851
+ } finally {
1852
+ contentKey.fill(0);
1853
+ }
1759
1854
  }
1760
1855
  async function encryptFile(input, options) {
1761
1856
  const plaintext = await toUint8Array(input.data);
@@ -1783,17 +1878,56 @@ async function encryptFileWithPassword(input, password) {
1783
1878
  password
1784
1879
  });
1785
1880
  }
1786
- function hkdfFlex3(ikm, salt, info) {
1881
+ function hkdfFlex2(ikm, salt, info) {
1787
1882
  return hkdfSha256(ikm, { salt: u8(salt), info: u8(info), length: 32 });
1788
1883
  }
1884
+ function headerAad(header) {
1885
+ return writeOQEHeader(header);
1886
+ }
1789
1887
  async function decryptHybrid(encryptedData, options) {
1790
- const { header, keyMaterial, encryptedMetadata, encryptedContent } = parseOQEFile(encryptedData);
1791
- if (header.algorithmSuite !== ALGORITHM_SUITES.HYBRID_X25519_KYBER768_AES256GCM) {
1792
- throw new OQEError(
1793
- "UNSUPPORTED_ALGORITHM",
1794
- "This file was not encrypted with hybrid mode. Use password decryption."
1795
- );
1888
+ const parsed = parseOQEFile(encryptedData);
1889
+ const { header } = parsed;
1890
+ if (header.algorithmSuite === ALGORITHM_SUITES.HYBRID_X25519_MLKEM1024_AES256GCM) {
1891
+ return decryptHybridV2(parsed, options);
1892
+ }
1893
+ if (header.algorithmSuite === ALGORITHM_SUITES.HYBRID_X25519_KYBER768_AES256GCM) {
1894
+ return decryptHybridV1Legacy(parsed, options);
1796
1895
  }
1896
+ throw new OQEError(
1897
+ "UNSUPPORTED_ALGORITHM",
1898
+ "This file was not encrypted with hybrid mode. Use password decryption."
1899
+ );
1900
+ }
1901
+ async function decryptHybridV2(parsed, options) {
1902
+ const { header, keyMaterial, encryptedMetadata, encryptedContent } = parsed;
1903
+ if (header.version !== OQE_FORMAT_VERSION_V2 || !header.contentIv) {
1904
+ throw new OQEError("INVALID_HEADER", "v2 hybrid file missing content IV");
1905
+ }
1906
+ const km = parseHybridKeyMaterialV2(keyMaterial);
1907
+ const x25519EpkHex = toHex(km.x25519EphemeralPk);
1908
+ const kyberKemCtB64 = b64(km.kyberCiphertext);
1909
+ const kyberShared = await kyberDecapsulate(kyberKemCtB64, options.recipientSecretKeys.kyberSecB64);
1910
+ const sk = fromHex(options.recipientSecretKeys.x25519SecHex);
1911
+ const x25519Shared = nacl4.scalarMult(sk, km.x25519EphemeralPk);
1912
+ const kek = combinedFileKekV2(kyberShared, x25519Shared, x25519EpkHex, kyberKemCtB64);
1913
+ kyberShared.fill(0);
1914
+ x25519Shared.fill(0);
1915
+ const contentKey = nacl4.secretbox.open(km.ckWrapped, km.ckWrapNonce, kek);
1916
+ kek.fill(0);
1917
+ if (!contentKey) {
1918
+ throw new OQEError("KEY_UNWRAP_FAILED", "Could not unwrap content key \u2014 combined-KEK authentication failed");
1919
+ }
1920
+ const aad = headerAad(header);
1921
+ try {
1922
+ const metadata = await decryptSection(encryptedMetadata, contentKey, header.iv, aad, "metadata");
1923
+ const plaintext = await aesDecrypt(encryptedContent, contentKey, header.contentIv, aad);
1924
+ return buildResult(plaintext, metadata, "hybrid");
1925
+ } finally {
1926
+ contentKey.fill(0);
1927
+ }
1928
+ }
1929
+ async function decryptHybridV1Legacy(parsed, options) {
1930
+ const { header, keyMaterial, encryptedMetadata, encryptedContent } = parsed;
1797
1931
  const km = parseHybridKeyMaterial(keyMaterial);
1798
1932
  let contentKey = null;
1799
1933
  if (await isKyberAvailable()) {
@@ -1802,54 +1936,47 @@ async function decryptHybrid(encryptedData, options) {
1802
1936
  toB64(km.kyberCiphertext),
1803
1937
  options.recipientSecretKeys.kyberSecB64
1804
1938
  );
1805
- const kyberKek = hkdfFlex3(kyberShared, "omnituum/fs/kyber", "wrap-content-key");
1806
- const unwrapped = nacl4.secretbox.open(km.kyberWrappedKey, km.kyberNonce, kyberKek);
1807
- if (unwrapped) {
1808
- contentKey = unwrapped;
1809
- console.log("[OQE] Decrypted content key via Kyber (post-quantum secure)");
1810
- }
1811
- } catch (e) {
1812
- console.warn("[OQE] Kyber decapsulation failed, trying X25519:", e);
1939
+ const kyberKek = hkdfFlex2(kyberShared, "omnituum/fs/kyber", "wrap-content-key");
1940
+ contentKey = nacl4.secretbox.open(km.kyberWrappedKey, km.kyberNonce, kyberKek);
1941
+ } catch {
1813
1942
  }
1814
1943
  }
1815
1944
  if (!contentKey) {
1816
1945
  try {
1817
- const ephPk = km.x25519EphemeralPk;
1818
1946
  const sk = fromHex(options.recipientSecretKeys.x25519SecHex);
1819
- const x25519Shared = nacl4.scalarMult(sk, ephPk);
1820
- const x25519Kek = hkdfFlex3(x25519Shared, "omnituum/fs/x25519", "wrap-content-key");
1821
- const unwrapped = nacl4.secretbox.open(km.x25519WrappedKey, km.x25519Nonce, x25519Kek);
1822
- if (unwrapped) {
1823
- contentKey = unwrapped;
1824
- console.log("[OQE] Decrypted content key via X25519 (classical)");
1825
- }
1826
- } catch (e) {
1827
- console.warn("[OQE] X25519 decryption failed:", e);
1947
+ const x25519Shared = nacl4.scalarMult(sk, km.x25519EphemeralPk);
1948
+ const x25519Kek = hkdfFlex2(x25519Shared, "omnituum/fs/x25519", "wrap-content-key");
1949
+ contentKey = nacl4.secretbox.open(km.x25519WrappedKey, km.x25519Nonce, x25519Kek);
1950
+ } catch {
1828
1951
  }
1829
1952
  }
1830
1953
  if (!contentKey) {
1831
1954
  throw new OQEError("KEY_UNWRAP_FAILED", "Could not unwrap content key with provided keys");
1832
1955
  }
1833
- let metadata;
1834
1956
  try {
1835
- const metadataBytes = await aesDecrypt(encryptedMetadata, contentKey, header.iv);
1836
- metadata = parseMetadata(metadataBytes);
1837
- } catch (e) {
1838
- throw new OQEError("DECRYPTION_FAILED", "Failed to decrypt file metadata");
1957
+ const metadata = await decryptSection(encryptedMetadata, contentKey, header.iv, void 0, "metadata");
1958
+ const plaintext = await aesDecrypt(encryptedContent, contentKey, header.iv);
1959
+ return buildResult(plaintext, metadata, "hybrid");
1960
+ } finally {
1961
+ contentKey.fill(0);
1839
1962
  }
1840
- let plaintext;
1963
+ }
1964
+ async function decryptSection(ciphertext, key, iv, aad, what) {
1841
1965
  try {
1842
- plaintext = await aesDecrypt(encryptedContent, contentKey, header.iv);
1843
- } catch (e) {
1844
- throw new OQEError("DECRYPTION_FAILED", "Failed to decrypt file content");
1966
+ const bytes = await aesDecrypt(ciphertext, key, iv, aad);
1967
+ return parseMetadata(bytes);
1968
+ } catch {
1969
+ throw new OQEError("DECRYPTION_FAILED", `Failed to decrypt file ${what}`);
1845
1970
  }
1971
+ }
1972
+ function buildResult(plaintext, metadata, mode) {
1846
1973
  return {
1847
1974
  data: plaintext,
1848
1975
  filename: metadata.filename,
1849
1976
  mimeType: metadata.mimeType,
1850
1977
  originalSize: metadata.originalSize,
1851
1978
  metadata,
1852
- mode: "hybrid"
1979
+ mode
1853
1980
  };
1854
1981
  }
1855
1982
  async function decryptPassword(encryptedData, options) {
@@ -1871,27 +1998,27 @@ async function decryptPassword(encryptedData, options) {
1871
1998
  hashLength: 32,
1872
1999
  saltLength: km.salt.length
1873
2000
  });
1874
- let metadata;
2001
+ const isV2 = header.version === OQE_FORMAT_VERSION_V2;
2002
+ const aad = isV2 ? headerAad(header) : void 0;
2003
+ const contentIv = isV2 && header.contentIv ? header.contentIv : header.iv;
1875
2004
  try {
1876
- const metadataBytes = await aesDecrypt(encryptedMetadata, contentKey, header.iv);
1877
- metadata = parseMetadata(metadataBytes);
1878
- } catch (e) {
1879
- throw new OQEError("PASSWORD_WRONG", "Incorrect password or corrupted file");
1880
- }
1881
- let plaintext;
1882
- try {
1883
- plaintext = await aesDecrypt(encryptedContent, contentKey, header.iv);
1884
- } catch (e) {
1885
- throw new OQEError("DECRYPTION_FAILED", "Failed to decrypt file content");
2005
+ let metadata;
2006
+ try {
2007
+ const metadataBytes = await aesDecrypt(encryptedMetadata, contentKey, header.iv, aad);
2008
+ metadata = parseMetadata(metadataBytes);
2009
+ } catch {
2010
+ throw new OQEError("PASSWORD_WRONG", "Incorrect password or corrupted file");
2011
+ }
2012
+ let plaintext;
2013
+ try {
2014
+ plaintext = await aesDecrypt(encryptedContent, contentKey, contentIv, aad);
2015
+ } catch {
2016
+ throw new OQEError("DECRYPTION_FAILED", "Failed to decrypt file content");
2017
+ }
2018
+ return buildResult(plaintext, metadata, "password");
2019
+ } finally {
2020
+ contentKey.fill(0);
1886
2021
  }
1887
- return {
1888
- data: plaintext,
1889
- filename: metadata.filename,
1890
- mimeType: metadata.mimeType,
1891
- originalSize: metadata.originalSize,
1892
- metadata,
1893
- mode: "password"
1894
- };
1895
2022
  }
1896
2023
  async function decryptFile(encryptedData, options) {
1897
2024
  const data = await toUint8Array(encryptedData);
@@ -1988,4 +2115,4 @@ function createTunnelSession(keys) {
1988
2115
  };
1989
2116
  }
1990
2117
 
1991
- export { BLAKE3_OUTPUT_LENGTH, BOX_KEY_SIZE, BOX_NONCE_SIZE, CHACHA20_KEY_SIZE, DILITHIUM_ALGORITHM, DILITHIUM_PUBLIC_KEY_SIZE, DILITHIUM_SECRET_KEY_SIZE, DILITHIUM_SIGNATURE_SIZE, ENVELOPE_AEAD, ENVELOPE_SUITE, ENVELOPE_VERSION, KDF_CONFIG_ARGON2ID, KDF_CONFIG_PBKDF2, KYBER_CIPHERTEXT_SIZE, KYBER_PUBLIC_KEY_SIZE, KYBER_SECRET_KEY_SIZE, KYBER_SEED_SIZE, KYBER_SHARED_SECRET_SIZE, KYBER_SUITE, POLY1305_TAG_SIZE, SECRETBOX_KEY_SIZE, SECRETBOX_NONCE_SIZE, SECRETBOX_OVERHEAD, SecureBuffer, TUNNEL_KEY_SIZE, TUNNEL_NONCE_SIZE, TUNNEL_VERSION, VAULT_ALGORITHM, VAULT_ENCRYPTED_VERSION, VAULT_ENCRYPTED_VERSION_V2, VAULT_KDF, VAULT_KDF_V2, VAULT_VERSION, XCHACHA20_NONCE_SIZE, addIdentity, assertLen, b64, benchmarkKDF, blake3, blake3DeriveKey, blake3Hex, blake3Mac, boxDecrypt, boxEncrypt, boxUnwrapWithX25519, boxWrapWithX25519, chaCha20Poly1305Decrypt, chaCha20Poly1305Encrypt, computeIntegrityHash, computeKeyFingerprint, constantTimeEqual, createChaCha20Poly1305, createEmptyVault, createIdentity, createSession, createTunnelSession, createXChaCha20Poly1305, decryptFile, decryptFileWithPassword, decryptVault, deriveKeyFromShared, dilithiumSign, dilithiumSignRaw, dilithiumVerify, dilithiumVerifyRaw, encryptFile, encryptFileWithPassword, encryptVault, fromB64, fromHex, generateDilithiumKeypair, generateDilithiumKeypairFromSeed, generateHybridIdentity, generateKyberKeypair, generateKyberKeypairFromSeed, generateSalt, generateX25519Keypair, generateX25519KeypairFromSeed, getPublicKeys, getRecommendedConfig, getSecretKeys, getVaultKdfInfo, hkdfDerive, hkdfExpand, hkdfExtract, hkdfSha256, hkdfSplitForNoise, hkdfTripleSplitForNoise, hybridDecrypt, hybridDecryptToString, hybridEncrypt, isDilithiumAvailable, isKyberAvailable, isSessionTimedOut, isV2Vault, kdfDeriveKey, kyberDecapsulate, kyberEncapsulate, kyberUnwrapKey, kyberWrapKey, lockSecureSession, migrateEncryptedVault, needsMigration, rand12, rand24, rand32, randN, secretboxDecrypt, secretboxDecryptString, secretboxEncrypt, secretboxEncryptString, secretboxOpenRaw, secretboxRaw, sha256, sha256String, textDecoder, textEncoder, toB64, toHex, u8, ub64, unlockSecureSession, validateEncryptedVault, validateEnvelope, validateVault, withSecureData, x25519SharedSecret, xChaCha20Poly1305Decrypt, xChaCha20Poly1305Encrypt, zeroAll, zeroMemory };
2118
+ export { BLAKE3_OUTPUT_LENGTH, BOX_KEY_SIZE, BOX_NONCE_SIZE, CHACHA20_KEY_SIZE, DILITHIUM_ALGORITHM, DILITHIUM_PUBLIC_KEY_SIZE, DILITHIUM_SECRET_KEY_SIZE, DILITHIUM_SIGNATURE_SIZE, ENVELOPE_AEAD, ENVELOPE_SUITE, ENVELOPE_SUITE_V2, ENVELOPE_VERSION, ENVELOPE_VERSION_V2, KDF_CONFIG_ARGON2ID, KDF_CONFIG_PBKDF2, KYBER_CIPHERTEXT_SIZE, KYBER_PUBLIC_KEY_SIZE, KYBER_SECRET_KEY_SIZE, KYBER_SEED_SIZE, KYBER_SHARED_SECRET_SIZE, KYBER_SUITE, POLY1305_TAG_SIZE, SECRETBOX_KEY_SIZE, SECRETBOX_NONCE_SIZE, SECRETBOX_OVERHEAD, SecureBuffer, TUNNEL_KEY_SIZE, TUNNEL_NONCE_SIZE, TUNNEL_VERSION, VAULT_ALGORITHM, VAULT_ENCRYPTED_VERSION, VAULT_ENCRYPTED_VERSION_V2, VAULT_KDF, VAULT_KDF_V2, VAULT_VERSION, XCHACHA20_NONCE_SIZE, addIdentity, assertLen, b64, benchmarkKDF, blake3, blake3DeriveKey, blake3Hex, blake3Mac, boxDecrypt, boxEncrypt, boxUnwrapWithX25519, boxWrapWithX25519, chaCha20Poly1305Decrypt, chaCha20Poly1305Encrypt, computeIntegrityHash, computeKeyFingerprint, constantTimeEqual, createChaCha20Poly1305, createEmptyVault, createIdentity, createSession, createTunnelSession, createXChaCha20Poly1305, decryptFile, decryptFileWithPassword, decryptVault, deriveKeyFromShared, dilithiumSign, dilithiumSignRaw, dilithiumVerify, dilithiumVerifyRaw, encryptFile, encryptFileWithPassword, encryptVault, fromB64, fromHex, generateDilithiumKeypair, generateDilithiumKeypairFromSeed, generateHybridIdentity, generateKyberKeypair, generateKyberKeypairFromSeed, generateSalt, generateX25519Keypair, generateX25519KeypairFromSeed, getPublicKeys, getRecommendedConfig, getSecretKeys, getVaultKdfInfo, hkdfDerive, hkdfExpand, hkdfExtract, hkdfSha256, hkdfSplitForNoise, hkdfTripleSplitForNoise, hybridDecrypt, hybridDecryptToString, hybridEncrypt, isDilithiumAvailable, isKyberAvailable, isSessionTimedOut, isV2Vault, kdfDeriveKey, kyberDecapsulate, kyberEncapsulate, kyberUnwrapKey, kyberWrapKey, lockSecureSession, migrateEncryptedVault, needsMigration, rand12, rand24, rand32, randN, secretboxDecrypt, secretboxDecryptString, secretboxEncrypt, secretboxEncryptString, secretboxOpenRaw, secretboxRaw, sha256, sha256String, textDecoder, textEncoder, toB64, toHex, u8, ub64, unlockSecureSession, validateEncryptedVault, validateEnvelope, validateVault, withSecureData, x25519SharedSecret, xChaCha20Poly1305Decrypt, xChaCha20Poly1305Encrypt, zeroAll, zeroMemory };