@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.cjs CHANGED
@@ -207,17 +207,20 @@ function kyberUnwrapKey(sharedSecret, nonceB64, wrappedB64) {
207
207
  return nacl4__default.default.secretbox.open(wrapped, nonce, kek) || null;
208
208
  }
209
209
  var ENVELOPE_VERSION = envelopeRegistry.OMNI_VERSIONS.HYBRID_V1;
210
+ var ENVELOPE_VERSION_V2 = envelopeRegistry.OMNI_VERSIONS.HYBRID_V2;
210
211
  var ENVELOPE_VERSION_LEGACY = envelopeRegistry.DEPRECATED_VERSIONS.PQC_DEMO_HYBRID_V1;
211
212
  var VAULT_VERSION = "omnituum.vault.v1";
212
213
  var VAULT_ENCRYPTED_VERSION = "omnituum.vault.enc.v1";
213
214
  var VAULT_ENCRYPTED_VERSION_V2 = "omnituum.vault.enc.v2";
214
215
  var ENVELOPE_SUITE = "x25519+kyber768";
216
+ var ENVELOPE_SUITE_V2 = "x25519+mlkem1024";
215
217
  var ENVELOPE_AEAD = "xsalsa20poly1305";
216
218
  var VAULT_KDF = "PBKDF2-SHA256";
217
219
  var VAULT_KDF_V2 = "Argon2id";
218
220
  var VAULT_ALGORITHM = "AES-256-GCM";
219
221
  var SUPPORTED_ENVELOPE_VERSIONS = [
220
222
  ENVELOPE_VERSION,
223
+ ENVELOPE_VERSION_V2,
221
224
  ENVELOPE_VERSION_LEGACY
222
225
  ];
223
226
  var SUPPORTED_VAULT_VERSIONS = [
@@ -262,6 +265,8 @@ function isVaultVersionSupported(version) {
262
265
  function isVaultEncryptedVersionSupported(version) {
263
266
  return SUPPORTED_VAULT_ENCRYPTED_VERSIONS.includes(version);
264
267
  }
268
+ var ENVELOPE_V1_FIELDS = ["x25519Epk", "x25519Wrap", "kyberKemCt", "kyberWrap", "contentNonce", "ciphertext", "meta"];
269
+ var ENVELOPE_V2_FIELDS = ["x25519Epk", "kyberKemCt", "ckWrap", "contentNonce", "ciphertext", "meta"];
265
270
  function validateEnvelope(envelope) {
266
271
  const errors = [];
267
272
  if (!envelope || typeof envelope !== "object") {
@@ -273,14 +278,16 @@ function validateEnvelope(envelope) {
273
278
  } else if (!isEnvelopeVersionSupported(env.v)) {
274
279
  errors.push(`Unsupported envelope version: ${env.v}`);
275
280
  }
276
- if (env.suite !== ENVELOPE_SUITE) {
277
- errors.push(`Invalid suite: expected "${ENVELOPE_SUITE}", got "${env.suite}"`);
281
+ const isV2 = env.v === ENVELOPE_VERSION_V2;
282
+ const expectedSuite = isV2 ? ENVELOPE_SUITE_V2 : ENVELOPE_SUITE;
283
+ const requiredFields = isV2 ? ENVELOPE_V2_FIELDS : ENVELOPE_V1_FIELDS;
284
+ if (env.suite !== expectedSuite) {
285
+ errors.push(`Invalid suite: expected "${expectedSuite}", got "${env.suite}"`);
278
286
  }
279
287
  if (env.aead !== ENVELOPE_AEAD) {
280
288
  errors.push(`Invalid aead: expected "${ENVELOPE_AEAD}", got "${env.aead}"`);
281
289
  }
282
- const required = ["x25519Epk", "x25519Wrap", "kyberKemCt", "kyberWrap", "contentNonce", "ciphertext", "meta"];
283
- for (const field of required) {
290
+ for (const field of requiredFields) {
284
291
  if (!(field in env)) {
285
292
  errors.push(`Missing required field: ${field}`);
286
293
  }
@@ -375,7 +382,6 @@ async function generateHybridIdentity(name) {
375
382
  const x25519 = generateX25519Keypair();
376
383
  const kyber = await generateKyberKeypair();
377
384
  if (!kyber) {
378
- console.error("Kyber key generation failed - library not available");
379
385
  return null;
380
386
  }
381
387
  return {
@@ -401,49 +407,74 @@ function getSecretKeys(identity) {
401
407
  kyberSecB64: identity.kyberSecB64
402
408
  };
403
409
  }
410
+ function combinedKekV2(kyberShared, x25519Shared, x25519EpkHex, kyberKemCtB64) {
411
+ const ikm = new Uint8Array(kyberShared.length + x25519Shared.length);
412
+ ikm.set(kyberShared, 0);
413
+ ikm.set(x25519Shared, kyberShared.length);
414
+ try {
415
+ return hkdfFlex(ikm, "omnituum/hybrid-v2", `wrap-ck|${x25519EpkHex}|${kyberKemCtB64}`);
416
+ } finally {
417
+ ikm.fill(0);
418
+ }
419
+ }
404
420
  async function hybridEncrypt(plaintext, recipientPublicKeys, sender) {
405
421
  const pt = typeof plaintext === "string" ? textEncoder.encode(plaintext) : plaintext;
406
422
  const CK = rand32();
407
- const contentNonce = rand24();
408
- const ciphertext = nacl4__default.default.secretbox(pt, contentNonce, CK);
409
- const x25519EphKp = nacl4__default.default.box.keyPair();
410
- const recipientX25519Pk = fromHex(recipientPublicKeys.x25519PubHex);
411
- const x25519Shared = nacl4__default.default.scalarMult(x25519EphKp.secretKey, recipientX25519Pk);
412
- const x25519Kek = hkdfFlex(x25519Shared, "omnituum/x25519", "wrap-ck");
413
- const x25519WrapNonce = rand24();
414
- const x25519Wrapped = nacl4__default.default.secretbox(CK, x25519WrapNonce, x25519Kek);
415
- const kyberResult = await kyberEncapsulate(recipientPublicKeys.kyberPubB64);
416
- const kyberKek = hkdfFlex(kyberResult.sharedSecret, "omnituum/kyber", "wrap-ck");
417
- const kyberWrapNonce = rand24();
418
- const kyberWrapped = nacl4__default.default.secretbox(CK, kyberWrapNonce, kyberKek);
419
- return {
420
- v: ENVELOPE_VERSION,
421
- suite: ENVELOPE_SUITE,
422
- aead: ENVELOPE_AEAD,
423
- x25519Epk: toHex(x25519EphKp.publicKey),
424
- x25519Wrap: {
425
- nonce: b64(x25519WrapNonce),
426
- wrapped: b64(x25519Wrapped)
427
- },
428
- kyberKemCt: b64(kyberResult.ciphertext),
429
- kyberWrap: {
430
- nonce: b64(kyberWrapNonce),
431
- wrapped: b64(kyberWrapped)
432
- },
433
- contentNonce: b64(contentNonce),
434
- ciphertext: b64(ciphertext),
435
- meta: {
436
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
437
- senderName: sender?.name,
438
- senderId: sender?.id
439
- }
440
- };
423
+ try {
424
+ const contentNonce = rand24();
425
+ const ciphertext = nacl4__default.default.secretbox(pt, contentNonce, CK);
426
+ const x25519EphKp = nacl4__default.default.box.keyPair();
427
+ const recipientX25519Pk = fromHex(recipientPublicKeys.x25519PubHex);
428
+ const x25519Shared = nacl4__default.default.scalarMult(x25519EphKp.secretKey, recipientX25519Pk);
429
+ const x25519EpkHex = toHex(x25519EphKp.publicKey);
430
+ const kyberResult = await kyberEncapsulate(recipientPublicKeys.kyberPubB64);
431
+ const kyberKemCtB64 = b64(kyberResult.ciphertext);
432
+ const kek = combinedKekV2(kyberResult.sharedSecret, x25519Shared, x25519EpkHex, kyberKemCtB64);
433
+ const ckWrapNonce = rand24();
434
+ const ckWrapped = nacl4__default.default.secretbox(CK, ckWrapNonce, kek);
435
+ kek.fill(0);
436
+ x25519Shared.fill(0);
437
+ kyberResult.sharedSecret.fill(0);
438
+ x25519EphKp.secretKey.fill(0);
439
+ return {
440
+ v: ENVELOPE_VERSION_V2,
441
+ suite: ENVELOPE_SUITE_V2,
442
+ aead: ENVELOPE_AEAD,
443
+ x25519Epk: x25519EpkHex,
444
+ kyberKemCt: kyberKemCtB64,
445
+ ckWrap: {
446
+ nonce: b64(ckWrapNonce),
447
+ wrapped: b64(ckWrapped)
448
+ },
449
+ contentNonce: b64(contentNonce),
450
+ ciphertext: b64(ciphertext),
451
+ meta: {
452
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
453
+ senderName: sender?.name,
454
+ senderId: sender?.id
455
+ }
456
+ };
457
+ } finally {
458
+ CK.fill(0);
459
+ }
460
+ }
461
+ async function unwrapCkV2(envelope, secretKeys) {
462
+ const kyberShared = await kyberDecapsulate(envelope.kyberKemCt, secretKeys.kyberSecB64);
463
+ const ephPk = fromHex(envelope.x25519Epk);
464
+ const sk = fromHex(secretKeys.x25519SecHex);
465
+ const x25519Shared = nacl4__default.default.scalarMult(sk, ephPk);
466
+ const kek = combinedKekV2(kyberShared, x25519Shared, envelope.x25519Epk, envelope.kyberKemCt);
467
+ kyberShared.fill(0);
468
+ x25519Shared.fill(0);
469
+ const CK = nacl4__default.default.secretbox.open(ub64(envelope.ckWrap.wrapped), ub64(envelope.ckWrap.nonce), kek);
470
+ kek.fill(0);
471
+ if (!CK) {
472
+ throw new Error("Could not unwrap content key \u2014 combined-KEK authentication failed");
473
+ }
474
+ return CK;
441
475
  }
442
- async function hybridDecrypt(envelope, secretKeys) {
443
- const version = envelope.v;
444
- assertEnvelopeVersion(version);
476
+ async function unwrapCkV1(envelope, secretKeys, saltPrefix) {
445
477
  let CK = null;
446
- const saltPrefix = version === "pqc-demo.hybrid.v1" ? "pqc-demo" : "omnituum";
447
478
  try {
448
479
  const kyberShared = await kyberDecapsulate(envelope.kyberKemCt, secretKeys.kyberSecB64);
449
480
  const kyberKek = hkdfFlex(kyberShared, `${saltPrefix}/kyber`, "wrap-ck");
@@ -452,11 +483,7 @@ async function hybridDecrypt(envelope, secretKeys) {
452
483
  ub64(envelope.kyberWrap.nonce),
453
484
  kyberKek
454
485
  );
455
- if (CK) {
456
- console.log("[Hybrid] Decrypted using Kyber (post-quantum)");
457
- }
458
- } catch (e) {
459
- console.warn("[Hybrid] Kyber decapsulation failed:", e);
486
+ } catch {
460
487
  }
461
488
  if (!CK) {
462
489
  try {
@@ -469,21 +496,28 @@ async function hybridDecrypt(envelope, secretKeys) {
469
496
  ub64(envelope.x25519Wrap.nonce),
470
497
  x25519Kek
471
498
  );
472
- if (CK) {
473
- console.log("[Hybrid] Decrypted using X25519 (classical)");
474
- }
475
- } catch (e) {
476
- console.warn("[Hybrid] X25519 decryption failed:", e);
499
+ } catch {
477
500
  }
478
501
  }
479
502
  if (!CK) {
480
503
  throw new Error("Could not unwrap content key with either algorithm");
481
504
  }
505
+ return CK;
506
+ }
507
+ async function hybridDecrypt(envelope, secretKeys) {
508
+ const version = envelope.v;
509
+ assertEnvelopeVersion(version);
510
+ const CK = version === ENVELOPE_VERSION_V2 ? await unwrapCkV2(envelope, secretKeys) : await unwrapCkV1(
511
+ envelope,
512
+ secretKeys,
513
+ version === "pqc-demo.hybrid.v1" ? "pqc-demo" : "omnituum"
514
+ );
482
515
  const pt = nacl4__default.default.secretbox.open(
483
516
  ub64(envelope.ciphertext),
484
517
  ub64(envelope.contentNonce),
485
518
  CK
486
519
  );
520
+ CK.fill(0);
487
521
  if (!pt) {
488
522
  throw new Error("Content authentication failed");
489
523
  }
@@ -502,8 +536,7 @@ async function loadDilithium() {
502
536
  const { ml_dsa65 } = await import('@noble/post-quantum/ml-dsa.js');
503
537
  dilithiumModule = ml_dsa65;
504
538
  return dilithiumModule;
505
- } catch (e) {
506
- console.warn("[Dilithium] Failed to load @noble/post-quantum:", e);
539
+ } catch {
507
540
  return null;
508
541
  }
509
542
  }
@@ -521,8 +554,7 @@ async function generateDilithiumKeypair() {
521
554
  publicB64: toB64(kp.publicKey),
522
555
  secretB64: toB64(kp.secretKey)
523
556
  };
524
- } catch (e) {
525
- console.warn("[Dilithium] Key generation failed:", e);
557
+ } catch {
526
558
  return null;
527
559
  }
528
560
  }
@@ -861,17 +893,11 @@ function computeIntegrityHash(identities) {
861
893
  createdAt: i.createdAt,
862
894
  rotationCount: i.rotationCount
863
895
  }));
864
- const serialized = JSON.stringify(canonical, Object.keys(canonical[0] || {}).sort());
896
+ const serialized = JSON.stringify(canonical);
865
897
  return computeStringHash(serialized);
866
898
  }
867
899
  function computeStringHash(str) {
868
- let hash = 0;
869
- for (let i = 0; i < str.length; i++) {
870
- const char = str.charCodeAt(i);
871
- hash = (hash << 5) - hash + char;
872
- hash = hash & hash;
873
- }
874
- return Math.abs(hash).toString(16).padStart(16, "0");
900
+ return toHex(sha256(textEncoder.encode(str)));
875
901
  }
876
902
  async function computeHashAsync(data) {
877
903
  const encoder = new TextEncoder();
@@ -1122,7 +1148,6 @@ async function createIdentity(name) {
1122
1148
  const x25519 = generateX25519Keypair();
1123
1149
  const kyber = await generateKyberKeypair();
1124
1150
  if (!kyber) {
1125
- console.error("Kyber key generation failed");
1126
1151
  return null;
1127
1152
  }
1128
1153
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -1345,12 +1370,22 @@ async function migrateEncryptedVault(options) {
1345
1370
 
1346
1371
  // src/fs/types.ts
1347
1372
  var OQE_MAGIC = new Uint8Array([79, 81, 69, 70]);
1348
- var OQE_FORMAT_VERSION = 1;
1373
+ var OQE_FORMAT_VERSION_V1 = 1;
1374
+ var OQE_FORMAT_VERSION_V2 = 2;
1375
+ var SUPPORTED_OQE_VERSIONS = [OQE_FORMAT_VERSION_V1, OQE_FORMAT_VERSION_V2];
1349
1376
  var ALGORITHM_SUITES = {
1350
- /** Hybrid: X25519 ECDH + Kyber768 KEM + AES-256-GCM */
1377
+ /**
1378
+ * LEGACY (read-only): Hybrid X25519 + Kyber with independent per-primitive
1379
+ * wraps (either secret unwraps). Only appears in v1 files. Never written.
1380
+ */
1351
1381
  HYBRID_X25519_KYBER768_AES256GCM: 1,
1352
1382
  /** Password: Argon2id + AES-256-GCM */
1353
- PASSWORD_ARGON2ID_AES256GCM: 2
1383
+ PASSWORD_ARGON2ID_AES256GCM: 2,
1384
+ /**
1385
+ * Hybrid X25519 + ML-KEM-1024 with a single AND-combined KEK wrap
1386
+ * (HKDF(ss_mlkem || ss_x25519), transcript-bound). Written by v2.
1387
+ */
1388
+ HYBRID_X25519_MLKEM1024_AES256GCM: 3
1354
1389
  };
1355
1390
  var DEFAULT_ARGON2ID_PARAMS = {
1356
1391
  memoryCost: 65536,
@@ -1360,7 +1395,8 @@ var DEFAULT_ARGON2ID_PARAMS = {
1360
1395
  hashLength: 32,
1361
1396
  saltLength: 32
1362
1397
  };
1363
- var OQE_HEADER_SIZE = 30;
1398
+ var OQE_HEADER_SIZE_V1 = 30;
1399
+ var OQE_HEADER_SIZE_V2 = 42;
1364
1400
  var OQEError = class extends Error {
1365
1401
  constructor(code, message) {
1366
1402
  super(message);
@@ -1430,6 +1466,7 @@ function toArrayBuffer(arr) {
1430
1466
  }
1431
1467
  var AES_KEY_SIZE = 32;
1432
1468
  var AES_GCM_IV_SIZE = 12;
1469
+ var AES_GCM_TAG_SIZE = 16;
1433
1470
  async function importAesKey(keyBytes) {
1434
1471
  if (keyBytes.length !== AES_KEY_SIZE) {
1435
1472
  throw new Error(`AES key must be ${AES_KEY_SIZE} bytes, got ${keyBytes.length}`);
@@ -1453,7 +1490,7 @@ async function aesEncrypt(plaintext, key, iv, additionalData) {
1453
1490
  {
1454
1491
  name: "AES-GCM",
1455
1492
  iv: toArrayBuffer(ivBytes),
1456
- additionalData: void 0,
1493
+ additionalData: additionalData ? toArrayBuffer(additionalData) : void 0,
1457
1494
  tagLength: 128
1458
1495
  // 16 bytes
1459
1496
  },
@@ -1489,6 +1526,9 @@ async function aesDecrypt(ciphertext, key, iv, additionalData) {
1489
1526
  }
1490
1527
 
1491
1528
  // src/fs/format.ts
1529
+ function oqeHeaderSize(version) {
1530
+ return version === OQE_FORMAT_VERSION_V2 ? OQE_HEADER_SIZE_V2 : OQE_HEADER_SIZE_V1;
1531
+ }
1492
1532
  function writeUint32BE(value) {
1493
1533
  const buffer = new ArrayBuffer(4);
1494
1534
  new DataView(buffer).setUint32(0, value, false);
@@ -1506,7 +1546,8 @@ function readUint16BE(data, offset) {
1506
1546
  return new DataView(data.buffer, data.byteOffset + offset).getUint16(0, false);
1507
1547
  }
1508
1548
  function writeOQEHeader(header) {
1509
- const buffer = new Uint8Array(OQE_HEADER_SIZE);
1549
+ const isV2 = header.version === OQE_FORMAT_VERSION_V2;
1550
+ const buffer = new Uint8Array(oqeHeaderSize(header.version));
1510
1551
  let offset = 0;
1511
1552
  buffer.set(OQE_MAGIC, offset);
1512
1553
  offset += 4;
@@ -1519,11 +1560,18 @@ function writeOQEHeader(header) {
1519
1560
  buffer.set(writeUint32BE(header.keyMaterialLength), offset);
1520
1561
  offset += 4;
1521
1562
  buffer.set(header.iv, offset);
1563
+ offset += 12;
1564
+ if (isV2) {
1565
+ if (!header.contentIv || header.contentIv.length !== 12) {
1566
+ throw new OQEError("INVALID_HEADER", "v2 header requires a 12-byte contentIv");
1567
+ }
1568
+ buffer.set(header.contentIv, offset);
1569
+ }
1522
1570
  return buffer;
1523
1571
  }
1524
1572
  function parseOQEHeader(data) {
1525
- if (data.length < OQE_HEADER_SIZE) {
1526
- throw new OQEError("INVALID_HEADER", `File too small: need ${OQE_HEADER_SIZE} bytes, got ${data.length}`);
1573
+ if (data.length < OQE_HEADER_SIZE_V1) {
1574
+ throw new OQEError("INVALID_HEADER", `File too small: need at least ${OQE_HEADER_SIZE_V1} bytes, got ${data.length}`);
1527
1575
  }
1528
1576
  let offset = 0;
1529
1577
  const magic = data.slice(0, 4);
@@ -1532,14 +1580,19 @@ function parseOQEHeader(data) {
1532
1580
  }
1533
1581
  offset += 4;
1534
1582
  const version = data[offset++];
1535
- if (version !== OQE_FORMAT_VERSION) {
1583
+ if (!SUPPORTED_OQE_VERSIONS.includes(version)) {
1536
1584
  throw new OQEError("UNSUPPORTED_VERSION", `Unsupported OQE version: ${version}`);
1537
1585
  }
1538
1586
  const algorithmSuiteRaw = data[offset++];
1539
- if (algorithmSuiteRaw !== ALGORITHM_SUITES.HYBRID_X25519_KYBER768_AES256GCM && algorithmSuiteRaw !== ALGORITHM_SUITES.PASSWORD_ARGON2ID_AES256GCM) {
1587
+ if (algorithmSuiteRaw !== ALGORITHM_SUITES.HYBRID_X25519_KYBER768_AES256GCM && algorithmSuiteRaw !== ALGORITHM_SUITES.HYBRID_X25519_MLKEM1024_AES256GCM && algorithmSuiteRaw !== ALGORITHM_SUITES.PASSWORD_ARGON2ID_AES256GCM) {
1540
1588
  throw new OQEError("UNSUPPORTED_ALGORITHM", `Unsupported algorithm suite: 0x${algorithmSuiteRaw.toString(16)}`);
1541
1589
  }
1542
1590
  const algorithmSuite = algorithmSuiteRaw;
1591
+ const isV2 = version === OQE_FORMAT_VERSION_V2;
1592
+ const headerSize = oqeHeaderSize(version);
1593
+ if (data.length < headerSize) {
1594
+ throw new OQEError("INVALID_HEADER", `Truncated v${version} header: need ${headerSize} bytes, got ${data.length}`);
1595
+ }
1543
1596
  const flags = readUint32BE(data, offset);
1544
1597
  offset += 4;
1545
1598
  const metadataLength = readUint32BE(data, offset);
@@ -1547,35 +1600,18 @@ function parseOQEHeader(data) {
1547
1600
  const keyMaterialLength = readUint32BE(data, offset);
1548
1601
  offset += 4;
1549
1602
  const iv = data.slice(offset, offset + 12);
1603
+ offset += 12;
1604
+ const contentIv = isV2 ? data.slice(offset, offset + 12) : void 0;
1550
1605
  return {
1551
1606
  version,
1552
1607
  algorithmSuite,
1553
1608
  flags,
1554
1609
  metadataLength,
1555
1610
  keyMaterialLength,
1556
- iv
1611
+ iv,
1612
+ contentIv
1557
1613
  };
1558
1614
  }
1559
- function serializeHybridKeyMaterial(km) {
1560
- const parts = [];
1561
- parts.push(km.x25519EphemeralPk);
1562
- parts.push(km.x25519Nonce);
1563
- parts.push(writeUint16BE(km.x25519WrappedKey.length));
1564
- parts.push(km.x25519WrappedKey);
1565
- parts.push(writeUint16BE(km.kyberCiphertext.length));
1566
- parts.push(km.kyberCiphertext);
1567
- parts.push(km.kyberNonce);
1568
- parts.push(writeUint16BE(km.kyberWrappedKey.length));
1569
- parts.push(km.kyberWrappedKey);
1570
- const totalLength = parts.reduce((sum, p) => sum + p.length, 0);
1571
- const result = new Uint8Array(totalLength);
1572
- let offset = 0;
1573
- for (const part of parts) {
1574
- result.set(part, offset);
1575
- offset += part.length;
1576
- }
1577
- return result;
1578
- }
1579
1615
  function parseHybridKeyMaterial(data) {
1580
1616
  let offset = 0;
1581
1617
  const x25519EphemeralPk = data.slice(offset, offset + 32);
@@ -1604,6 +1640,38 @@ function parseHybridKeyMaterial(data) {
1604
1640
  kyberWrappedKey
1605
1641
  };
1606
1642
  }
1643
+ function serializeHybridKeyMaterialV2(km) {
1644
+ const parts = [];
1645
+ parts.push(km.x25519EphemeralPk);
1646
+ parts.push(writeUint16BE(km.kyberCiphertext.length));
1647
+ parts.push(km.kyberCiphertext);
1648
+ parts.push(km.ckWrapNonce);
1649
+ parts.push(writeUint16BE(km.ckWrapped.length));
1650
+ parts.push(km.ckWrapped);
1651
+ const totalLength = parts.reduce((sum, p) => sum + p.length, 0);
1652
+ const result = new Uint8Array(totalLength);
1653
+ let offset = 0;
1654
+ for (const part of parts) {
1655
+ result.set(part, offset);
1656
+ offset += part.length;
1657
+ }
1658
+ return result;
1659
+ }
1660
+ function parseHybridKeyMaterialV2(data) {
1661
+ let offset = 0;
1662
+ const x25519EphemeralPk = data.slice(offset, offset + 32);
1663
+ offset += 32;
1664
+ const kyberCtLen = readUint16BE(data, offset);
1665
+ offset += 2;
1666
+ const kyberCiphertext = data.slice(offset, offset + kyberCtLen);
1667
+ offset += kyberCtLen;
1668
+ const ckWrapNonce = data.slice(offset, offset + 24);
1669
+ offset += 24;
1670
+ const ckWrappedLen = readUint16BE(data, offset);
1671
+ offset += 2;
1672
+ const ckWrapped = data.slice(offset, offset + ckWrappedLen);
1673
+ return { x25519EphemeralPk, kyberCiphertext, ckWrapNonce, ckWrapped };
1674
+ }
1607
1675
  function serializePasswordKeyMaterial(km) {
1608
1676
  const result = new Uint8Array(32 + 4 + 4 + 4);
1609
1677
  result.set(km.salt, 0);
@@ -1645,7 +1713,7 @@ function assembleOQEFile(components) {
1645
1713
  }
1646
1714
  function parseOQEFile(data) {
1647
1715
  const header = parseOQEHeader(data);
1648
- let offset = OQE_HEADER_SIZE;
1716
+ let offset = oqeHeaderSize(header.version);
1649
1717
  const keyMaterial = data.slice(offset, offset + header.keyMaterialLength);
1650
1718
  offset += header.keyMaterialLength;
1651
1719
  const encryptedMetadata = data.slice(offset, offset + header.metadataLength);
@@ -1665,61 +1733,81 @@ function addOQEExtension(filename) {
1665
1733
  }
1666
1734
  return `${filename}${OQE_EXTENSION}`;
1667
1735
  }
1668
- function hkdfFlex2(ikm, salt, info) {
1669
- return hkdfSha256(ikm, { salt: u8(salt), info: u8(info), length: 32 });
1670
- }
1736
+ var AES_GCM_TAG_LEN = AES_GCM_TAG_SIZE;
1671
1737
  function computeIdentityHash(publicKeyHex) {
1672
1738
  const hash = sha256(fromHex(publicKeyHex));
1673
1739
  return toHex(hash).slice(0, 16);
1674
1740
  }
1741
+ function combinedFileKekV2(kyberShared, x25519Shared, x25519EpkHex, kyberKemCtB64) {
1742
+ const ikm = new Uint8Array(kyberShared.length + x25519Shared.length);
1743
+ ikm.set(kyberShared, 0);
1744
+ ikm.set(x25519Shared, kyberShared.length);
1745
+ try {
1746
+ return hkdfSha256(ikm, {
1747
+ salt: u8("omnituum/fs/hybrid-v2"),
1748
+ info: u8(`wrap-content-key|${x25519EpkHex}|${kyberKemCtB64}`),
1749
+ length: 32
1750
+ });
1751
+ } finally {
1752
+ ikm.fill(0);
1753
+ }
1754
+ }
1675
1755
  async function encryptHybrid(plaintext, metadata, options) {
1676
1756
  if (!await isKyberAvailable()) {
1677
1757
  throw new OQEError("KYBER_UNAVAILABLE", "Kyber library not available in this environment");
1678
1758
  }
1679
1759
  const contentKey = rand32();
1680
- const iv = rand12();
1681
- const x25519EphKp = nacl4__default.default.box.keyPair();
1682
- const recipientX25519Pk = fromHex(options.recipientPublicKeys.x25519PubHex);
1683
- const x25519Shared = nacl4__default.default.scalarMult(x25519EphKp.secretKey, recipientX25519Pk);
1684
- const x25519Kek = hkdfFlex2(x25519Shared, "omnituum/fs/x25519", "wrap-content-key");
1685
- const x25519Nonce = rand24();
1686
- const x25519WrappedKey = nacl4__default.default.secretbox(contentKey, x25519Nonce, x25519Kek);
1687
- const kyberResult = await kyberEncapsulate(options.recipientPublicKeys.kyberPubB64);
1688
- const kyberKek = hkdfFlex2(kyberResult.sharedSecret, "omnituum/fs/kyber", "wrap-content-key");
1689
- const kyberNonce = rand24();
1690
- const kyberWrappedKey = nacl4__default.default.secretbox(contentKey, kyberNonce, kyberKek);
1691
- const keyMaterial = {
1692
- x25519EphemeralPk: x25519EphKp.publicKey,
1693
- x25519Nonce,
1694
- x25519WrappedKey,
1695
- kyberCiphertext: kyberResult.ciphertext,
1696
- kyberNonce,
1697
- kyberWrappedKey
1698
- };
1699
- const keyMaterialBytes = serializeHybridKeyMaterial(keyMaterial);
1700
- const metadataBytes = serializeMetadata(metadata);
1701
- const { ciphertext: encryptedMetadata } = await aesEncrypt(metadataBytes, contentKey, iv);
1702
- const { ciphertext: encryptedContent } = await aesEncrypt(plaintext, contentKey, iv);
1703
- const header = {
1704
- version: OQE_FORMAT_VERSION,
1705
- algorithmSuite: ALGORITHM_SUITES.HYBRID_X25519_KYBER768_AES256GCM,
1706
- flags: 0,
1707
- metadataLength: encryptedMetadata.length,
1708
- keyMaterialLength: keyMaterialBytes.length,
1709
- iv
1710
- };
1711
- const fileData = assembleOQEFile({
1712
- header,
1713
- keyMaterial: keyMaterialBytes,
1714
- encryptedMetadata,
1715
- encryptedContent
1716
- });
1717
- return {
1718
- data: fileData,
1719
- filename: addOQEExtension(metadata.filename),
1720
- metadata,
1721
- mode: "hybrid"
1722
- };
1760
+ try {
1761
+ const metadataIv = rand12();
1762
+ const contentIv = rand12();
1763
+ const x25519EphKp = nacl4__default.default.box.keyPair();
1764
+ const recipientX25519Pk = fromHex(options.recipientPublicKeys.x25519PubHex);
1765
+ const x25519Shared = nacl4__default.default.scalarMult(x25519EphKp.secretKey, recipientX25519Pk);
1766
+ const x25519EpkHex = toHex(x25519EphKp.publicKey);
1767
+ const kyberResult = await kyberEncapsulate(options.recipientPublicKeys.kyberPubB64);
1768
+ const kyberKemCtB64 = b64(kyberResult.ciphertext);
1769
+ const kek = combinedFileKekV2(kyberResult.sharedSecret, x25519Shared, x25519EpkHex, kyberKemCtB64);
1770
+ const ckWrapNonce = rand24();
1771
+ const ckWrapped = nacl4__default.default.secretbox(contentKey, ckWrapNonce, kek);
1772
+ kek.fill(0);
1773
+ x25519Shared.fill(0);
1774
+ kyberResult.sharedSecret.fill(0);
1775
+ x25519EphKp.secretKey.fill(0);
1776
+ const keyMaterial = {
1777
+ x25519EphemeralPk: x25519EphKp.publicKey,
1778
+ kyberCiphertext: kyberResult.ciphertext,
1779
+ ckWrapNonce,
1780
+ ckWrapped
1781
+ };
1782
+ const keyMaterialBytes = serializeHybridKeyMaterialV2(keyMaterial);
1783
+ const metadataBytes = serializeMetadata(metadata);
1784
+ const header = {
1785
+ version: OQE_FORMAT_VERSION_V2,
1786
+ algorithmSuite: ALGORITHM_SUITES.HYBRID_X25519_MLKEM1024_AES256GCM,
1787
+ flags: 0,
1788
+ metadataLength: metadataBytes.length + AES_GCM_TAG_LEN,
1789
+ keyMaterialLength: keyMaterialBytes.length,
1790
+ iv: metadataIv,
1791
+ contentIv
1792
+ };
1793
+ const aad = writeOQEHeader(header);
1794
+ const { ciphertext: encryptedMetadata } = await aesEncrypt(metadataBytes, contentKey, metadataIv, aad);
1795
+ const { ciphertext: encryptedContent } = await aesEncrypt(plaintext, contentKey, contentIv, aad);
1796
+ const fileData = assembleOQEFile({
1797
+ header,
1798
+ keyMaterial: keyMaterialBytes,
1799
+ encryptedMetadata,
1800
+ encryptedContent
1801
+ });
1802
+ return {
1803
+ data: fileData,
1804
+ filename: addOQEExtension(metadata.filename),
1805
+ metadata,
1806
+ mode: "hybrid"
1807
+ };
1808
+ } finally {
1809
+ contentKey.fill(0);
1810
+ }
1723
1811
  }
1724
1812
  async function encryptPassword(plaintext, metadata, options) {
1725
1813
  if (!await isArgon2Available()) {
@@ -1731,37 +1819,44 @@ async function encryptPassword(plaintext, metadata, options) {
1731
1819
  };
1732
1820
  const salt = generateArgon2Salt(params.saltLength);
1733
1821
  const contentKey = await deriveKeyFromPassword(options.password, salt, params);
1734
- const iv = rand12();
1735
- const keyMaterial = {
1736
- salt,
1737
- memoryCost: params.memoryCost,
1738
- timeCost: params.timeCost,
1739
- parallelism: params.parallelism
1740
- };
1741
- const keyMaterialBytes = serializePasswordKeyMaterial(keyMaterial);
1742
- const metadataBytes = serializeMetadata(metadata);
1743
- const { ciphertext: encryptedMetadata } = await aesEncrypt(metadataBytes, contentKey, iv);
1744
- const { ciphertext: encryptedContent } = await aesEncrypt(plaintext, contentKey, iv);
1745
- const header = {
1746
- version: OQE_FORMAT_VERSION,
1747
- algorithmSuite: ALGORITHM_SUITES.PASSWORD_ARGON2ID_AES256GCM,
1748
- flags: 0,
1749
- metadataLength: encryptedMetadata.length,
1750
- keyMaterialLength: keyMaterialBytes.length,
1751
- iv
1752
- };
1753
- const fileData = assembleOQEFile({
1754
- header,
1755
- keyMaterial: keyMaterialBytes,
1756
- encryptedMetadata,
1757
- encryptedContent
1758
- });
1759
- return {
1760
- data: fileData,
1761
- filename: addOQEExtension(metadata.filename),
1762
- metadata,
1763
- mode: "password"
1764
- };
1822
+ try {
1823
+ const metadataIv = rand12();
1824
+ const contentIv = rand12();
1825
+ const keyMaterial = {
1826
+ salt,
1827
+ memoryCost: params.memoryCost,
1828
+ timeCost: params.timeCost,
1829
+ parallelism: params.parallelism
1830
+ };
1831
+ const keyMaterialBytes = serializePasswordKeyMaterial(keyMaterial);
1832
+ const metadataBytes = serializeMetadata(metadata);
1833
+ const header = {
1834
+ version: OQE_FORMAT_VERSION_V2,
1835
+ algorithmSuite: ALGORITHM_SUITES.PASSWORD_ARGON2ID_AES256GCM,
1836
+ flags: 0,
1837
+ metadataLength: metadataBytes.length + AES_GCM_TAG_LEN,
1838
+ keyMaterialLength: keyMaterialBytes.length,
1839
+ iv: metadataIv,
1840
+ contentIv
1841
+ };
1842
+ const aad = writeOQEHeader(header);
1843
+ const { ciphertext: encryptedMetadata } = await aesEncrypt(metadataBytes, contentKey, metadataIv, aad);
1844
+ const { ciphertext: encryptedContent } = await aesEncrypt(plaintext, contentKey, contentIv, aad);
1845
+ const fileData = assembleOQEFile({
1846
+ header,
1847
+ keyMaterial: keyMaterialBytes,
1848
+ encryptedMetadata,
1849
+ encryptedContent
1850
+ });
1851
+ return {
1852
+ data: fileData,
1853
+ filename: addOQEExtension(metadata.filename),
1854
+ metadata,
1855
+ mode: "password"
1856
+ };
1857
+ } finally {
1858
+ contentKey.fill(0);
1859
+ }
1765
1860
  }
1766
1861
  async function encryptFile(input, options) {
1767
1862
  const plaintext = await toUint8Array(input.data);
@@ -1789,17 +1884,56 @@ async function encryptFileWithPassword(input, password) {
1789
1884
  password
1790
1885
  });
1791
1886
  }
1792
- function hkdfFlex3(ikm, salt, info) {
1887
+ function hkdfFlex2(ikm, salt, info) {
1793
1888
  return hkdfSha256(ikm, { salt: u8(salt), info: u8(info), length: 32 });
1794
1889
  }
1890
+ function headerAad(header) {
1891
+ return writeOQEHeader(header);
1892
+ }
1795
1893
  async function decryptHybrid(encryptedData, options) {
1796
- const { header, keyMaterial, encryptedMetadata, encryptedContent } = parseOQEFile(encryptedData);
1797
- if (header.algorithmSuite !== ALGORITHM_SUITES.HYBRID_X25519_KYBER768_AES256GCM) {
1798
- throw new OQEError(
1799
- "UNSUPPORTED_ALGORITHM",
1800
- "This file was not encrypted with hybrid mode. Use password decryption."
1801
- );
1894
+ const parsed = parseOQEFile(encryptedData);
1895
+ const { header } = parsed;
1896
+ if (header.algorithmSuite === ALGORITHM_SUITES.HYBRID_X25519_MLKEM1024_AES256GCM) {
1897
+ return decryptHybridV2(parsed, options);
1898
+ }
1899
+ if (header.algorithmSuite === ALGORITHM_SUITES.HYBRID_X25519_KYBER768_AES256GCM) {
1900
+ return decryptHybridV1Legacy(parsed, options);
1802
1901
  }
1902
+ throw new OQEError(
1903
+ "UNSUPPORTED_ALGORITHM",
1904
+ "This file was not encrypted with hybrid mode. Use password decryption."
1905
+ );
1906
+ }
1907
+ async function decryptHybridV2(parsed, options) {
1908
+ const { header, keyMaterial, encryptedMetadata, encryptedContent } = parsed;
1909
+ if (header.version !== OQE_FORMAT_VERSION_V2 || !header.contentIv) {
1910
+ throw new OQEError("INVALID_HEADER", "v2 hybrid file missing content IV");
1911
+ }
1912
+ const km = parseHybridKeyMaterialV2(keyMaterial);
1913
+ const x25519EpkHex = toHex(km.x25519EphemeralPk);
1914
+ const kyberKemCtB64 = b64(km.kyberCiphertext);
1915
+ const kyberShared = await kyberDecapsulate(kyberKemCtB64, options.recipientSecretKeys.kyberSecB64);
1916
+ const sk = fromHex(options.recipientSecretKeys.x25519SecHex);
1917
+ const x25519Shared = nacl4__default.default.scalarMult(sk, km.x25519EphemeralPk);
1918
+ const kek = combinedFileKekV2(kyberShared, x25519Shared, x25519EpkHex, kyberKemCtB64);
1919
+ kyberShared.fill(0);
1920
+ x25519Shared.fill(0);
1921
+ const contentKey = nacl4__default.default.secretbox.open(km.ckWrapped, km.ckWrapNonce, kek);
1922
+ kek.fill(0);
1923
+ if (!contentKey) {
1924
+ throw new OQEError("KEY_UNWRAP_FAILED", "Could not unwrap content key \u2014 combined-KEK authentication failed");
1925
+ }
1926
+ const aad = headerAad(header);
1927
+ try {
1928
+ const metadata = await decryptSection(encryptedMetadata, contentKey, header.iv, aad, "metadata");
1929
+ const plaintext = await aesDecrypt(encryptedContent, contentKey, header.contentIv, aad);
1930
+ return buildResult(plaintext, metadata, "hybrid");
1931
+ } finally {
1932
+ contentKey.fill(0);
1933
+ }
1934
+ }
1935
+ async function decryptHybridV1Legacy(parsed, options) {
1936
+ const { header, keyMaterial, encryptedMetadata, encryptedContent } = parsed;
1803
1937
  const km = parseHybridKeyMaterial(keyMaterial);
1804
1938
  let contentKey = null;
1805
1939
  if (await isKyberAvailable()) {
@@ -1808,54 +1942,47 @@ async function decryptHybrid(encryptedData, options) {
1808
1942
  toB64(km.kyberCiphertext),
1809
1943
  options.recipientSecretKeys.kyberSecB64
1810
1944
  );
1811
- const kyberKek = hkdfFlex3(kyberShared, "omnituum/fs/kyber", "wrap-content-key");
1812
- const unwrapped = nacl4__default.default.secretbox.open(km.kyberWrappedKey, km.kyberNonce, kyberKek);
1813
- if (unwrapped) {
1814
- contentKey = unwrapped;
1815
- console.log("[OQE] Decrypted content key via Kyber (post-quantum secure)");
1816
- }
1817
- } catch (e) {
1818
- console.warn("[OQE] Kyber decapsulation failed, trying X25519:", e);
1945
+ const kyberKek = hkdfFlex2(kyberShared, "omnituum/fs/kyber", "wrap-content-key");
1946
+ contentKey = nacl4__default.default.secretbox.open(km.kyberWrappedKey, km.kyberNonce, kyberKek);
1947
+ } catch {
1819
1948
  }
1820
1949
  }
1821
1950
  if (!contentKey) {
1822
1951
  try {
1823
- const ephPk = km.x25519EphemeralPk;
1824
1952
  const sk = fromHex(options.recipientSecretKeys.x25519SecHex);
1825
- const x25519Shared = nacl4__default.default.scalarMult(sk, ephPk);
1826
- const x25519Kek = hkdfFlex3(x25519Shared, "omnituum/fs/x25519", "wrap-content-key");
1827
- const unwrapped = nacl4__default.default.secretbox.open(km.x25519WrappedKey, km.x25519Nonce, x25519Kek);
1828
- if (unwrapped) {
1829
- contentKey = unwrapped;
1830
- console.log("[OQE] Decrypted content key via X25519 (classical)");
1831
- }
1832
- } catch (e) {
1833
- console.warn("[OQE] X25519 decryption failed:", e);
1953
+ const x25519Shared = nacl4__default.default.scalarMult(sk, km.x25519EphemeralPk);
1954
+ const x25519Kek = hkdfFlex2(x25519Shared, "omnituum/fs/x25519", "wrap-content-key");
1955
+ contentKey = nacl4__default.default.secretbox.open(km.x25519WrappedKey, km.x25519Nonce, x25519Kek);
1956
+ } catch {
1834
1957
  }
1835
1958
  }
1836
1959
  if (!contentKey) {
1837
1960
  throw new OQEError("KEY_UNWRAP_FAILED", "Could not unwrap content key with provided keys");
1838
1961
  }
1839
- let metadata;
1840
1962
  try {
1841
- const metadataBytes = await aesDecrypt(encryptedMetadata, contentKey, header.iv);
1842
- metadata = parseMetadata(metadataBytes);
1843
- } catch (e) {
1844
- throw new OQEError("DECRYPTION_FAILED", "Failed to decrypt file metadata");
1963
+ const metadata = await decryptSection(encryptedMetadata, contentKey, header.iv, void 0, "metadata");
1964
+ const plaintext = await aesDecrypt(encryptedContent, contentKey, header.iv);
1965
+ return buildResult(plaintext, metadata, "hybrid");
1966
+ } finally {
1967
+ contentKey.fill(0);
1845
1968
  }
1846
- let plaintext;
1969
+ }
1970
+ async function decryptSection(ciphertext, key, iv, aad, what) {
1847
1971
  try {
1848
- plaintext = await aesDecrypt(encryptedContent, contentKey, header.iv);
1849
- } catch (e) {
1850
- throw new OQEError("DECRYPTION_FAILED", "Failed to decrypt file content");
1972
+ const bytes = await aesDecrypt(ciphertext, key, iv, aad);
1973
+ return parseMetadata(bytes);
1974
+ } catch {
1975
+ throw new OQEError("DECRYPTION_FAILED", `Failed to decrypt file ${what}`);
1851
1976
  }
1977
+ }
1978
+ function buildResult(plaintext, metadata, mode) {
1852
1979
  return {
1853
1980
  data: plaintext,
1854
1981
  filename: metadata.filename,
1855
1982
  mimeType: metadata.mimeType,
1856
1983
  originalSize: metadata.originalSize,
1857
1984
  metadata,
1858
- mode: "hybrid"
1985
+ mode
1859
1986
  };
1860
1987
  }
1861
1988
  async function decryptPassword(encryptedData, options) {
@@ -1877,27 +2004,27 @@ async function decryptPassword(encryptedData, options) {
1877
2004
  hashLength: 32,
1878
2005
  saltLength: km.salt.length
1879
2006
  });
1880
- let metadata;
2007
+ const isV2 = header.version === OQE_FORMAT_VERSION_V2;
2008
+ const aad = isV2 ? headerAad(header) : void 0;
2009
+ const contentIv = isV2 && header.contentIv ? header.contentIv : header.iv;
1881
2010
  try {
1882
- const metadataBytes = await aesDecrypt(encryptedMetadata, contentKey, header.iv);
1883
- metadata = parseMetadata(metadataBytes);
1884
- } catch (e) {
1885
- throw new OQEError("PASSWORD_WRONG", "Incorrect password or corrupted file");
1886
- }
1887
- let plaintext;
1888
- try {
1889
- plaintext = await aesDecrypt(encryptedContent, contentKey, header.iv);
1890
- } catch (e) {
1891
- throw new OQEError("DECRYPTION_FAILED", "Failed to decrypt file content");
2011
+ let metadata;
2012
+ try {
2013
+ const metadataBytes = await aesDecrypt(encryptedMetadata, contentKey, header.iv, aad);
2014
+ metadata = parseMetadata(metadataBytes);
2015
+ } catch {
2016
+ throw new OQEError("PASSWORD_WRONG", "Incorrect password or corrupted file");
2017
+ }
2018
+ let plaintext;
2019
+ try {
2020
+ plaintext = await aesDecrypt(encryptedContent, contentKey, contentIv, aad);
2021
+ } catch {
2022
+ throw new OQEError("DECRYPTION_FAILED", "Failed to decrypt file content");
2023
+ }
2024
+ return buildResult(plaintext, metadata, "password");
2025
+ } finally {
2026
+ contentKey.fill(0);
1892
2027
  }
1893
- return {
1894
- data: plaintext,
1895
- filename: metadata.filename,
1896
- mimeType: metadata.mimeType,
1897
- originalSize: metadata.originalSize,
1898
- metadata,
1899
- mode: "password"
1900
- };
1901
2028
  }
1902
2029
  async function decryptFile(encryptedData, options) {
1903
2030
  const data = await toUint8Array(encryptedData);
@@ -2004,7 +2131,9 @@ exports.DILITHIUM_SECRET_KEY_SIZE = DILITHIUM_SECRET_KEY_SIZE;
2004
2131
  exports.DILITHIUM_SIGNATURE_SIZE = DILITHIUM_SIGNATURE_SIZE;
2005
2132
  exports.ENVELOPE_AEAD = ENVELOPE_AEAD;
2006
2133
  exports.ENVELOPE_SUITE = ENVELOPE_SUITE;
2134
+ exports.ENVELOPE_SUITE_V2 = ENVELOPE_SUITE_V2;
2007
2135
  exports.ENVELOPE_VERSION = ENVELOPE_VERSION;
2136
+ exports.ENVELOPE_VERSION_V2 = ENVELOPE_VERSION_V2;
2008
2137
  exports.KDF_CONFIG_ARGON2ID = KDF_CONFIG_ARGON2ID;
2009
2138
  exports.KDF_CONFIG_PBKDF2 = KDF_CONFIG_PBKDF2;
2010
2139
  exports.KYBER_CIPHERTEXT_SIZE = KYBER_CIPHERTEXT_SIZE;