react-native-quick-crypto 1.0.11 → 1.0.13

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 (197) hide show
  1. package/android/CMakeLists.txt +7 -0
  2. package/cpp/cipher/CCMCipher.cpp +4 -1
  3. package/cpp/cipher/ChaCha20Cipher.cpp +3 -1
  4. package/cpp/cipher/ChaCha20Poly1305Cipher.cpp +5 -5
  5. package/cpp/cipher/ChaCha20Poly1305Cipher.hpp +1 -2
  6. package/cpp/cipher/HybridCipher.cpp +10 -1
  7. package/cpp/cipher/HybridCipher.hpp +2 -0
  8. package/cpp/cipher/HybridRsaCipher.cpp +0 -13
  9. package/cpp/cipher/XChaCha20Poly1305Cipher.cpp +7 -5
  10. package/cpp/cipher/XChaCha20Poly1305Cipher.hpp +1 -2
  11. package/cpp/cipher/XSalsa20Cipher.cpp +4 -0
  12. package/cpp/cipher/XSalsa20Poly1305Cipher.cpp +7 -5
  13. package/cpp/cipher/XSalsa20Poly1305Cipher.hpp +1 -2
  14. package/cpp/ecdh/HybridECDH.cpp +20 -133
  15. package/cpp/keys/HybridKeyObjectHandle.cpp +144 -141
  16. package/cpp/keys/HybridKeyObjectHandle.hpp +6 -3
  17. package/cpp/keys/KeyObjectData.hpp +2 -0
  18. package/cpp/kmac/HybridKmac.cpp +83 -0
  19. package/cpp/kmac/HybridKmac.hpp +31 -0
  20. package/cpp/mldsa/HybridMlDsaKeyPair.cpp +11 -20
  21. package/cpp/mldsa/HybridMlDsaKeyPair.hpp +4 -2
  22. package/cpp/mlkem/HybridMlKemKeyPair.cpp +319 -0
  23. package/cpp/mlkem/HybridMlKemKeyPair.hpp +48 -0
  24. package/cpp/sign/SignUtils.hpp +9 -26
  25. package/cpp/utils/QuickCryptoUtils.cpp +44 -0
  26. package/cpp/utils/QuickCryptoUtils.hpp +39 -0
  27. package/cpp/x509/HybridX509Certificate.cpp +174 -0
  28. package/cpp/x509/HybridX509Certificate.hpp +51 -0
  29. package/lib/commonjs/cipher.js +15 -2
  30. package/lib/commonjs/cipher.js.map +1 -1
  31. package/lib/commonjs/dhKeyPair.js +3 -3
  32. package/lib/commonjs/dhKeyPair.js.map +1 -1
  33. package/lib/commonjs/dsa.js +3 -3
  34. package/lib/commonjs/dsa.js.map +1 -1
  35. package/lib/commonjs/ec.js +18 -18
  36. package/lib/commonjs/ec.js.map +1 -1
  37. package/lib/commonjs/ed.js +9 -9
  38. package/lib/commonjs/ed.js.map +1 -1
  39. package/lib/commonjs/hash.js +17 -12
  40. package/lib/commonjs/hash.js.map +1 -1
  41. package/lib/commonjs/hkdf.js.map +1 -1
  42. package/lib/commonjs/index.js +22 -0
  43. package/lib/commonjs/index.js.map +1 -1
  44. package/lib/commonjs/keys/classes.js +2 -2
  45. package/lib/commonjs/keys/classes.js.map +1 -1
  46. package/lib/commonjs/keys/index.js +24 -0
  47. package/lib/commonjs/keys/index.js.map +1 -1
  48. package/lib/commonjs/keys/publicCipher.js +2 -2
  49. package/lib/commonjs/keys/publicCipher.js.map +1 -1
  50. package/lib/commonjs/keys/signVerify.js +0 -2
  51. package/lib/commonjs/keys/signVerify.js.map +1 -1
  52. package/lib/commonjs/mlkem.js +219 -0
  53. package/lib/commonjs/mlkem.js.map +1 -0
  54. package/lib/commonjs/pbkdf2.js +18 -1
  55. package/lib/commonjs/pbkdf2.js.map +1 -1
  56. package/lib/commonjs/rsa.js +7 -7
  57. package/lib/commonjs/rsa.js.map +1 -1
  58. package/lib/commonjs/specs/kmac.nitro.js +6 -0
  59. package/lib/commonjs/specs/kmac.nitro.js.map +1 -0
  60. package/lib/commonjs/specs/mlKemKeyPair.nitro.js +6 -0
  61. package/lib/commonjs/specs/mlKemKeyPair.nitro.js.map +1 -0
  62. package/lib/commonjs/specs/x509certificate.nitro.js +6 -0
  63. package/lib/commonjs/specs/x509certificate.nitro.js.map +1 -0
  64. package/lib/commonjs/subtle.js +292 -112
  65. package/lib/commonjs/subtle.js.map +1 -1
  66. package/lib/commonjs/utils/conversion.js +3 -3
  67. package/lib/commonjs/utils/conversion.js.map +1 -1
  68. package/lib/commonjs/utils/hashnames.js +31 -0
  69. package/lib/commonjs/utils/hashnames.js.map +1 -1
  70. package/lib/commonjs/utils/types.js.map +1 -1
  71. package/lib/commonjs/x509certificate.js +189 -0
  72. package/lib/commonjs/x509certificate.js.map +1 -0
  73. package/lib/module/cipher.js +16 -3
  74. package/lib/module/cipher.js.map +1 -1
  75. package/lib/module/dhKeyPair.js +1 -1
  76. package/lib/module/dhKeyPair.js.map +1 -1
  77. package/lib/module/dsa.js +1 -1
  78. package/lib/module/dsa.js.map +1 -1
  79. package/lib/module/ec.js +6 -6
  80. package/lib/module/ec.js.map +1 -1
  81. package/lib/module/ed.js +1 -1
  82. package/lib/module/ed.js.map +1 -1
  83. package/lib/module/hash.js +17 -12
  84. package/lib/module/hash.js.map +1 -1
  85. package/lib/module/hkdf.js.map +1 -1
  86. package/lib/module/index.js +6 -0
  87. package/lib/module/index.js.map +1 -1
  88. package/lib/module/keys/classes.js +2 -2
  89. package/lib/module/keys/classes.js.map +1 -1
  90. package/lib/module/keys/index.js +25 -1
  91. package/lib/module/keys/index.js.map +1 -1
  92. package/lib/module/keys/publicCipher.js +2 -2
  93. package/lib/module/keys/publicCipher.js.map +1 -1
  94. package/lib/module/keys/signVerify.js +0 -2
  95. package/lib/module/keys/signVerify.js.map +1 -1
  96. package/lib/module/mlkem.js +211 -0
  97. package/lib/module/mlkem.js.map +1 -0
  98. package/lib/module/pbkdf2.js +18 -1
  99. package/lib/module/pbkdf2.js.map +1 -1
  100. package/lib/module/rsa.js +1 -1
  101. package/lib/module/rsa.js.map +1 -1
  102. package/lib/module/specs/kmac.nitro.js +4 -0
  103. package/lib/module/specs/kmac.nitro.js.map +1 -0
  104. package/lib/module/specs/mlKemKeyPair.nitro.js +4 -0
  105. package/lib/module/specs/mlKemKeyPair.nitro.js.map +1 -0
  106. package/lib/module/specs/x509certificate.nitro.js +4 -0
  107. package/lib/module/specs/x509certificate.nitro.js.map +1 -0
  108. package/lib/module/subtle.js +292 -112
  109. package/lib/module/subtle.js.map +1 -1
  110. package/lib/module/utils/conversion.js +3 -4
  111. package/lib/module/utils/conversion.js.map +1 -1
  112. package/lib/module/utils/hashnames.js +31 -0
  113. package/lib/module/utils/hashnames.js.map +1 -1
  114. package/lib/module/utils/types.js.map +1 -1
  115. package/lib/module/x509certificate.js +184 -0
  116. package/lib/module/x509certificate.js.map +1 -0
  117. package/lib/tsconfig.tsbuildinfo +1 -1
  118. package/lib/typescript/cipher.d.ts +3 -0
  119. package/lib/typescript/cipher.d.ts.map +1 -1
  120. package/lib/typescript/dhKeyPair.d.ts +1 -1
  121. package/lib/typescript/dhKeyPair.d.ts.map +1 -1
  122. package/lib/typescript/dsa.d.ts +1 -1
  123. package/lib/typescript/dsa.d.ts.map +1 -1
  124. package/lib/typescript/ec.d.ts +1 -1
  125. package/lib/typescript/ec.d.ts.map +1 -1
  126. package/lib/typescript/ed.d.ts +1 -1
  127. package/lib/typescript/ed.d.ts.map +1 -1
  128. package/lib/typescript/hash.d.ts.map +1 -1
  129. package/lib/typescript/hkdf.d.ts +2 -6
  130. package/lib/typescript/hkdf.d.ts.map +1 -1
  131. package/lib/typescript/index.d.ts +15 -4
  132. package/lib/typescript/index.d.ts.map +1 -1
  133. package/lib/typescript/keys/classes.d.ts +5 -5
  134. package/lib/typescript/keys/classes.d.ts.map +1 -1
  135. package/lib/typescript/keys/index.d.ts +2 -2
  136. package/lib/typescript/keys/index.d.ts.map +1 -1
  137. package/lib/typescript/keys/signVerify.d.ts.map +1 -1
  138. package/lib/typescript/mlkem.d.ts +30 -0
  139. package/lib/typescript/mlkem.d.ts.map +1 -0
  140. package/lib/typescript/pbkdf2.d.ts +2 -2
  141. package/lib/typescript/pbkdf2.d.ts.map +1 -1
  142. package/lib/typescript/rsa.d.ts +1 -1
  143. package/lib/typescript/rsa.d.ts.map +1 -1
  144. package/lib/typescript/specs/keyObjectHandle.nitro.d.ts +1 -0
  145. package/lib/typescript/specs/keyObjectHandle.nitro.d.ts.map +1 -1
  146. package/lib/typescript/specs/kmac.nitro.d.ts +10 -0
  147. package/lib/typescript/specs/kmac.nitro.d.ts.map +1 -0
  148. package/lib/typescript/specs/mlKemKeyPair.nitro.d.ts +18 -0
  149. package/lib/typescript/specs/mlKemKeyPair.nitro.d.ts.map +1 -0
  150. package/lib/typescript/specs/x509certificate.nitro.d.ts +34 -0
  151. package/lib/typescript/specs/x509certificate.nitro.d.ts.map +1 -0
  152. package/lib/typescript/subtle.d.ts +10 -0
  153. package/lib/typescript/subtle.d.ts.map +1 -1
  154. package/lib/typescript/utils/conversion.d.ts.map +1 -1
  155. package/lib/typescript/utils/hashnames.d.ts +1 -1
  156. package/lib/typescript/utils/hashnames.d.ts.map +1 -1
  157. package/lib/typescript/utils/types.d.ts +13 -7
  158. package/lib/typescript/utils/types.d.ts.map +1 -1
  159. package/lib/typescript/x509certificate.d.ts +64 -0
  160. package/lib/typescript/x509certificate.d.ts.map +1 -0
  161. package/nitrogen/generated/android/QuickCrypto+autolinking.cmake +3 -0
  162. package/nitrogen/generated/android/QuickCryptoOnLoad.cpp +30 -0
  163. package/nitrogen/generated/ios/QuickCryptoAutolinking.mm +30 -0
  164. package/nitrogen/generated/shared/c++/AsymmetricKeyType.hpp +12 -0
  165. package/nitrogen/generated/shared/c++/HybridKeyObjectHandleSpec.cpp +1 -0
  166. package/nitrogen/generated/shared/c++/HybridKeyObjectHandleSpec.hpp +1 -0
  167. package/nitrogen/generated/shared/c++/HybridKmacSpec.cpp +23 -0
  168. package/nitrogen/generated/shared/c++/HybridKmacSpec.hpp +66 -0
  169. package/nitrogen/generated/shared/c++/HybridMlKemKeyPairSpec.cpp +31 -0
  170. package/nitrogen/generated/shared/c++/HybridMlKemKeyPairSpec.hpp +74 -0
  171. package/nitrogen/generated/shared/c++/HybridX509CertificateHandleSpec.cpp +46 -0
  172. package/nitrogen/generated/shared/c++/HybridX509CertificateHandleSpec.hpp +96 -0
  173. package/package.json +4 -1
  174. package/src/cipher.ts +17 -3
  175. package/src/dhKeyPair.ts +1 -1
  176. package/src/dsa.ts +1 -1
  177. package/src/ec.ts +9 -9
  178. package/src/ed.ts +2 -2
  179. package/src/hash.ts +34 -11
  180. package/src/hkdf.ts +2 -7
  181. package/src/index.ts +7 -0
  182. package/src/keys/classes.ts +10 -9
  183. package/src/keys/index.ts +37 -2
  184. package/src/keys/publicCipher.ts +2 -2
  185. package/src/keys/signVerify.ts +0 -5
  186. package/src/mlkem.ts +350 -0
  187. package/src/pbkdf2.ts +34 -5
  188. package/src/rsa.ts +1 -1
  189. package/src/specs/keyObjectHandle.nitro.ts +5 -0
  190. package/src/specs/kmac.nitro.ts +12 -0
  191. package/src/specs/mlKemKeyPair.nitro.ts +32 -0
  192. package/src/specs/x509certificate.nitro.ts +38 -0
  193. package/src/subtle.ts +551 -125
  194. package/src/utils/conversion.ts +10 -4
  195. package/src/utils/hashnames.ts +33 -2
  196. package/src/utils/types.ts +42 -5
  197. package/src/x509certificate.ts +277 -0
@@ -17,9 +17,11 @@ import { ecImportKey, ecdsaSignVerify, ec_generateKeyPair, ecDeriveBits } from '
17
17
  import { rsa_generateKeyPair } from './rsa';
18
18
  import { getRandomValues } from './random';
19
19
  import { createHmac } from './hmac';
20
+ import { timingSafeEqual } from './utils/timingSafeEqual';
20
21
  import { createSign, createVerify } from './keys/signVerify';
21
22
  import { ed_generateKeyPairWebCrypto, x_generateKeyPairWebCrypto, xDeriveBits, Ed } from './ed';
22
23
  import { mldsa_generateKeyPairWebCrypto } from './mldsa';
24
+ import { mlkem_generateKeyPairWebCrypto, MlKem } from './mlkem';
23
25
  import { hkdfDeriveBits } from './hkdf';
24
26
  // Temporary enums that need to be defined
25
27
  var KWebCryptoKeyFormat = /*#__PURE__*/function (KWebCryptoKeyFormat) {
@@ -483,6 +485,100 @@ async function hmacGenerateKey(algorithm, extractable, keyUsages) {
483
485
  };
484
486
  return new CryptoKey(keyObject, keyAlgorithm, keyUsages, extractable);
485
487
  }
488
+ async function kmacGenerateKey(algorithm, extractable, keyUsages) {
489
+ const {
490
+ name
491
+ } = algorithm;
492
+ if (hasAnyNotIn(keyUsages, ['sign', 'verify'])) {
493
+ throw lazyDOMException(`Unsupported key usage for ${name} key`, 'SyntaxError');
494
+ }
495
+ const defaultLength = name === 'KMAC128' ? 128 : 256;
496
+ const length = algorithm.length ?? defaultLength;
497
+ if (length === 0) {
498
+ throw lazyDOMException('Zero-length key is not supported', 'OperationError');
499
+ }
500
+ const keyBytes = new Uint8Array(Math.ceil(length / 8));
501
+ getRandomValues(keyBytes);
502
+ const keyObject = createSecretKey(keyBytes);
503
+ const keyAlgorithm = {
504
+ name: name,
505
+ length
506
+ };
507
+ return new CryptoKey(keyObject, keyAlgorithm, keyUsages, extractable);
508
+ }
509
+ function kmacSignVerify(key, data, algorithm, signature) {
510
+ const {
511
+ name
512
+ } = algorithm;
513
+ const defaultLength = name === 'KMAC128' ? 256 : 512;
514
+ const outputLengthBits = algorithm.length ?? defaultLength;
515
+ if (outputLengthBits % 8 !== 0) {
516
+ throw lazyDOMException('KMAC output length must be a multiple of 8', 'OperationError');
517
+ }
518
+ const outputLengthBytes = outputLengthBits / 8;
519
+ const keyData = key.keyObject.export();
520
+ const kmac = NitroModules.createHybridObject('Kmac');
521
+ let customizationBuffer;
522
+ if (algorithm.customization !== undefined) {
523
+ customizationBuffer = bufferLikeToArrayBuffer(algorithm.customization);
524
+ }
525
+ kmac.createKmac(name, bufferLikeToArrayBuffer(keyData), outputLengthBytes, customizationBuffer);
526
+ kmac.update(bufferLikeToArrayBuffer(data));
527
+ const computed = kmac.digest();
528
+ if (signature === undefined) {
529
+ return computed;
530
+ }
531
+ const sigBuffer = bufferLikeToArrayBuffer(signature);
532
+ if (computed.byteLength !== sigBuffer.byteLength) {
533
+ return false;
534
+ }
535
+ return timingSafeEqual(new Uint8Array(computed), new Uint8Array(sigBuffer));
536
+ }
537
+ async function kmacImportKey(algorithm, format, data, extractable, keyUsages) {
538
+ const {
539
+ name
540
+ } = algorithm;
541
+ if (hasAnyNotIn(keyUsages, ['sign', 'verify'])) {
542
+ throw lazyDOMException(`Unsupported key usage for ${name} key`, 'SyntaxError');
543
+ }
544
+ let keyObject;
545
+ if (format === 'jwk') {
546
+ const jwk = data;
547
+ if (!jwk || typeof jwk !== 'object') {
548
+ throw lazyDOMException('Invalid keyData', 'DataError');
549
+ }
550
+ if (jwk.kty !== 'oct') {
551
+ throw lazyDOMException('Invalid JWK format for KMAC key', 'DataError');
552
+ }
553
+ const expectedAlg = name === 'KMAC128' ? 'K128' : 'K256';
554
+ if (jwk.alg !== undefined && jwk.alg !== expectedAlg) {
555
+ throw lazyDOMException('JWK "alg" does not match the requested algorithm', 'DataError');
556
+ }
557
+ const handle = NitroModules.createHybridObject('KeyObjectHandle');
558
+ const keyType = handle.initJwk(jwk, undefined);
559
+ if (keyType === undefined || keyType !== 0) {
560
+ throw lazyDOMException('Failed to import KMAC JWK', 'DataError');
561
+ }
562
+ keyObject = new SecretKeyObject(handle);
563
+ } else if (format === 'raw' || format === 'raw-secret') {
564
+ keyObject = createSecretKey(data);
565
+ } else {
566
+ throw lazyDOMException(`Unable to import ${name} key with format ${format}`, 'NotSupportedError');
567
+ }
568
+ const exported = keyObject.export();
569
+ const keyLength = exported.byteLength * 8;
570
+ if (keyLength === 0) {
571
+ throw lazyDOMException('Zero-length key is not supported', 'DataError');
572
+ }
573
+ if (algorithm.length !== undefined && algorithm.length !== keyLength) {
574
+ throw lazyDOMException('Invalid key length', 'DataError');
575
+ }
576
+ const keyAlgorithm = {
577
+ name: name,
578
+ length: keyLength
579
+ };
580
+ return new CryptoKey(keyObject, keyAlgorithm, keyUsages, extractable);
581
+ }
486
582
  function rsaImportKey(format, data, algorithm, extractable, keyUsages) {
487
583
  const {
488
584
  name
@@ -720,28 +816,48 @@ function edImportKey(format, data, algorithm, extractable, keyUsages) {
720
816
  name
721
817
  }, keyUsages, extractable);
722
818
  }
819
+ function pqcImportKeyObject(format, data, name) {
820
+ if (format === 'spki') {
821
+ return KeyObject.createKeyObject('public', bufferLikeToArrayBuffer(data), KFormatType.DER, KeyEncoding.SPKI);
822
+ } else if (format === 'pkcs8') {
823
+ return KeyObject.createKeyObject('private', bufferLikeToArrayBuffer(data), KFormatType.DER, KeyEncoding.PKCS8);
824
+ } else if (format === 'raw') {
825
+ const handle = NitroModules.createHybridObject('KeyObjectHandle');
826
+ if (!handle.initPqcRaw(name, bufferLikeToArrayBuffer(data), true)) {
827
+ throw lazyDOMException(`Failed to import ${name} raw public key`, 'DataError');
828
+ }
829
+ return new PublicKeyObject(handle);
830
+ } else if (format === 'raw-seed') {
831
+ const handle = NitroModules.createHybridObject('KeyObjectHandle');
832
+ if (!handle.initPqcRaw(name, bufferLikeToArrayBuffer(data), false)) {
833
+ throw lazyDOMException(`Failed to import ${name} raw seed`, 'DataError');
834
+ }
835
+ return new PrivateKeyObject(handle);
836
+ }
837
+ throw lazyDOMException(`Unsupported format for ${name} import: ${format}`, 'NotSupportedError');
838
+ }
723
839
  function mldsaImportKey(format, data, algorithm, extractable, keyUsages) {
724
840
  const {
725
841
  name
726
842
  } = algorithm;
727
-
728
- // Validate usages
729
- if (hasAnyNotIn(keyUsages, ['sign', 'verify'])) {
843
+ const isPublicFormat = format === 'spki' || format === 'raw';
844
+ if (hasAnyNotIn(keyUsages, isPublicFormat ? ['verify'] : ['sign'])) {
730
845
  throw lazyDOMException(`Unsupported key usage for ${name} key`, 'SyntaxError');
731
846
  }
732
- let keyObject;
733
- if (format === 'spki') {
734
- // Import public key
735
- const keyData = bufferLikeToArrayBuffer(data);
736
- keyObject = KeyObject.createKeyObject('public', keyData, KFormatType.DER, KeyEncoding.SPKI);
737
- } else if (format === 'pkcs8') {
738
- // Import private key
739
- const keyData = bufferLikeToArrayBuffer(data);
740
- keyObject = KeyObject.createKeyObject('private', keyData, KFormatType.DER, KeyEncoding.PKCS8);
741
- } else {
742
- throw lazyDOMException(`Unsupported format for ${name} import: ${format}`, 'NotSupportedError');
847
+ return new CryptoKey(pqcImportKeyObject(format, data, name), {
848
+ name
849
+ }, keyUsages, extractable);
850
+ }
851
+ function mlkemImportKey(format, data, algorithm, extractable, keyUsages) {
852
+ const {
853
+ name
854
+ } = algorithm;
855
+ const isPublicFormat = format === 'spki' || format === 'raw';
856
+ const allowedUsages = isPublicFormat ? ['encapsulateBits', 'encapsulateKey'] : ['decapsulateBits', 'decapsulateKey'];
857
+ if (hasAnyNotIn(keyUsages, allowedUsages)) {
858
+ throw lazyDOMException(`Unsupported key usage for ${name} key`, 'SyntaxError');
743
859
  }
744
- return new CryptoKey(keyObject, {
860
+ return new CryptoKey(pqcImportKeyObject(format, data, name), {
745
861
  name
746
862
  }, keyUsages, extractable);
747
863
  }
@@ -785,6 +901,15 @@ const exportKeySpki = async key => {
785
901
  return bufferLikeToArrayBuffer(key.keyObject.handle.exportKey(KFormatType.DER, KeyEncoding.SPKI));
786
902
  }
787
903
  break;
904
+ case 'ML-KEM-512':
905
+ // Fall through
906
+ case 'ML-KEM-768':
907
+ // Fall through
908
+ case 'ML-KEM-1024':
909
+ if (key.type === 'public') {
910
+ return bufferLikeToArrayBuffer(key.keyObject.handle.exportKey(KFormatType.DER, KeyEncoding.SPKI));
911
+ }
912
+ break;
788
913
  }
789
914
  throw new Error(`Unable to export a spki ${key.algorithm.name} ${key.type} key`);
790
915
  };
@@ -828,6 +953,15 @@ const exportKeyPkcs8 = async key => {
828
953
  return bufferLikeToArrayBuffer(key.keyObject.handle.exportKey(KFormatType.DER, KeyEncoding.PKCS8));
829
954
  }
830
955
  break;
956
+ case 'ML-KEM-512':
957
+ // Fall through
958
+ case 'ML-KEM-768':
959
+ // Fall through
960
+ case 'ML-KEM-1024':
961
+ if (key.type === 'private') {
962
+ return bufferLikeToArrayBuffer(key.keyObject.handle.exportKey(KFormatType.DER, KeyEncoding.PKCS8));
963
+ }
964
+ break;
831
965
  }
832
966
  throw new Error(`Unable to export a pkcs8 ${key.algorithm.name} ${key.type} key`);
833
967
  };
@@ -848,7 +982,22 @@ const exportKeyRaw = key => {
848
982
  // Fall through
849
983
  case 'X448':
850
984
  if (key.type === 'public') {
851
- // Export raw public key
985
+ const exported = key.keyObject.handle.exportKey();
986
+ return bufferLikeToArrayBuffer(exported);
987
+ }
988
+ break;
989
+ case 'ML-KEM-512':
990
+ // Fall through
991
+ case 'ML-KEM-768':
992
+ // Fall through
993
+ case 'ML-KEM-1024':
994
+ // Fall through
995
+ case 'ML-DSA-44':
996
+ // Fall through
997
+ case 'ML-DSA-65':
998
+ // Fall through
999
+ case 'ML-DSA-87':
1000
+ if (key.type === 'public') {
852
1001
  const exported = key.keyObject.handle.exportKey();
853
1002
  return bufferLikeToArrayBuffer(exported);
854
1003
  }
@@ -866,6 +1015,10 @@ const exportKeyRaw = key => {
866
1015
  case 'ChaCha20-Poly1305':
867
1016
  // Fall through
868
1017
  case 'HMAC':
1018
+ // Fall through
1019
+ case 'KMAC128':
1020
+ // Fall through
1021
+ case 'KMAC256':
869
1022
  {
870
1023
  const exported = key.keyObject.export();
871
1024
  // Convert Buffer to ArrayBuffer
@@ -892,6 +1045,12 @@ const exportKeyJWK = key => {
892
1045
  case 'HMAC':
893
1046
  jwk.alg = normalizeHashName(key.algorithm.hash, HashContext.JwkHmac);
894
1047
  return jwk;
1048
+ case 'KMAC128':
1049
+ jwk.alg = 'K128';
1050
+ return jwk;
1051
+ case 'KMAC256':
1052
+ jwk.alg = 'K256';
1053
+ return jwk;
895
1054
  case 'ECDSA':
896
1055
  // Fall through
897
1056
  case 'ECDH':
@@ -1030,20 +1189,12 @@ function hmacSignVerify(key, data, signature) {
1030
1189
  // Sign operation - return the HMAC as ArrayBuffer
1031
1190
  return computed.buffer.slice(computed.byteOffset, computed.byteOffset + computed.byteLength);
1032
1191
  }
1033
-
1034
- // Verify operation - compare computed HMAC with provided signature
1035
- const sigBytes = new Uint8Array(bufferLikeToArrayBuffer(signature));
1036
- const computedBytes = new Uint8Array(computed.buffer, computed.byteOffset, computed.byteLength);
1037
- if (computedBytes.length !== sigBytes.length) {
1192
+ const sigBuffer = bufferLikeToArrayBuffer(signature);
1193
+ const computedBuffer = computed.buffer.slice(computed.byteOffset, computed.byteOffset + computed.byteLength);
1194
+ if (computedBuffer.byteLength !== sigBuffer.byteLength) {
1038
1195
  return false;
1039
1196
  }
1040
-
1041
- // Constant-time comparison to prevent timing attacks
1042
- let result = 0;
1043
- for (let i = 0; i < computedBytes.length; i++) {
1044
- result |= computedBytes[i] ^ sigBytes[i];
1045
- }
1046
- return result === 0;
1197
+ return timingSafeEqual(new Uint8Array(computedBuffer), new Uint8Array(sigBuffer));
1047
1198
  }
1048
1199
  function rsaSignVerify(key, data, padding, signature, saltLength) {
1049
1200
  // Get hash algorithm from key
@@ -1146,6 +1297,9 @@ const signVerify = (algorithm, key, data, signature) => {
1146
1297
  case 'ML-DSA-65':
1147
1298
  case 'ML-DSA-87':
1148
1299
  return mldsaSignVerify(key, data, signature);
1300
+ case 'KMAC128':
1301
+ case 'KMAC256':
1302
+ return kmacSignVerify(key, data, algorithm, signature);
1149
1303
  }
1150
1304
  throw lazyDOMException(`Unrecognized algorithm name '${algorithm.name}' for '${usage}'`, 'NotSupportedError');
1151
1305
  };
@@ -1174,17 +1328,21 @@ const cipherOrWrap = async (mode, algorithm, key, data, op) => {
1174
1328
  const SUPPORTED_ALGORITHMS = {
1175
1329
  encrypt: new Set(['RSA-OAEP', 'AES-CTR', 'AES-CBC', 'AES-GCM', 'AES-OCB', 'ChaCha20-Poly1305']),
1176
1330
  decrypt: new Set(['RSA-OAEP', 'AES-CTR', 'AES-CBC', 'AES-GCM', 'AES-OCB', 'ChaCha20-Poly1305']),
1177
- sign: new Set(['RSASSA-PKCS1-v1_5', 'RSA-PSS', 'ECDSA', 'HMAC', 'Ed25519', 'Ed448', 'ML-DSA-44', 'ML-DSA-65', 'ML-DSA-87']),
1178
- verify: new Set(['RSASSA-PKCS1-v1_5', 'RSA-PSS', 'ECDSA', 'HMAC', 'Ed25519', 'Ed448', 'ML-DSA-44', 'ML-DSA-65', 'ML-DSA-87']),
1179
- digest: new Set(['SHA-1', 'SHA-256', 'SHA-384', 'SHA-512']),
1180
- generateKey: new Set(['RSASSA-PKCS1-v1_5', 'RSA-PSS', 'RSA-OAEP', 'ECDSA', 'ECDH', 'Ed25519', 'Ed448', 'X25519', 'X448', 'AES-CTR', 'AES-CBC', 'AES-GCM', 'AES-KW', 'AES-OCB', 'ChaCha20-Poly1305', 'HMAC', 'ML-DSA-44', 'ML-DSA-65', 'ML-DSA-87']),
1181
- importKey: new Set(['RSASSA-PKCS1-v1_5', 'RSA-PSS', 'RSA-OAEP', 'ECDSA', 'ECDH', 'Ed25519', 'Ed448', 'X25519', 'X448', 'AES-CTR', 'AES-CBC', 'AES-GCM', 'AES-KW', 'AES-OCB', 'ChaCha20-Poly1305', 'HMAC', 'HKDF', 'PBKDF2', 'Argon2d', 'Argon2i', 'Argon2id', 'ML-DSA-44', 'ML-DSA-65', 'ML-DSA-87']),
1182
- exportKey: new Set(['RSASSA-PKCS1-v1_5', 'RSA-PSS', 'RSA-OAEP', 'ECDSA', 'ECDH', 'Ed25519', 'Ed448', 'X25519', 'X448', 'AES-CTR', 'AES-CBC', 'AES-GCM', 'AES-KW', 'AES-OCB', 'ChaCha20-Poly1305', 'HMAC', 'ML-DSA-44', 'ML-DSA-65', 'ML-DSA-87']),
1331
+ sign: new Set(['RSASSA-PKCS1-v1_5', 'RSA-PSS', 'ECDSA', 'HMAC', 'KMAC128', 'KMAC256', 'Ed25519', 'Ed448', 'ML-DSA-44', 'ML-DSA-65', 'ML-DSA-87']),
1332
+ verify: new Set(['RSASSA-PKCS1-v1_5', 'RSA-PSS', 'ECDSA', 'HMAC', 'KMAC128', 'KMAC256', 'Ed25519', 'Ed448', 'ML-DSA-44', 'ML-DSA-65', 'ML-DSA-87']),
1333
+ digest: new Set(['SHA-1', 'SHA-256', 'SHA-384', 'SHA-512', 'SHA3-256', 'SHA3-384', 'SHA3-512', 'cSHAKE128', 'cSHAKE256']),
1334
+ generateKey: new Set(['RSASSA-PKCS1-v1_5', 'RSA-PSS', 'RSA-OAEP', 'ECDSA', 'ECDH', 'Ed25519', 'Ed448', 'X25519', 'X448', 'AES-CTR', 'AES-CBC', 'AES-GCM', 'AES-KW', 'AES-OCB', 'ChaCha20-Poly1305', 'HMAC', 'KMAC128', 'KMAC256', 'ML-DSA-44', 'ML-DSA-65', 'ML-DSA-87', 'ML-KEM-512', 'ML-KEM-768', 'ML-KEM-1024']),
1335
+ importKey: new Set(['RSASSA-PKCS1-v1_5', 'RSA-PSS', 'RSA-OAEP', 'ECDSA', 'ECDH', 'Ed25519', 'Ed448', 'X25519', 'X448', 'AES-CTR', 'AES-CBC', 'AES-GCM', 'AES-KW', 'AES-OCB', 'ChaCha20-Poly1305', 'HMAC', 'KMAC128', 'KMAC256', 'HKDF', 'PBKDF2', 'Argon2d', 'Argon2i', 'Argon2id', 'ML-DSA-44', 'ML-DSA-65', 'ML-DSA-87', 'ML-KEM-512', 'ML-KEM-768', 'ML-KEM-1024']),
1336
+ exportKey: new Set(['RSASSA-PKCS1-v1_5', 'RSA-PSS', 'RSA-OAEP', 'ECDSA', 'ECDH', 'Ed25519', 'Ed448', 'X25519', 'X448', 'AES-CTR', 'AES-CBC', 'AES-GCM', 'AES-KW', 'AES-OCB', 'ChaCha20-Poly1305', 'HMAC', 'KMAC128', 'KMAC256', 'ML-DSA-44', 'ML-DSA-65', 'ML-DSA-87', 'ML-KEM-512', 'ML-KEM-768', 'ML-KEM-1024']),
1183
1337
  deriveBits: new Set(['PBKDF2', 'HKDF', 'ECDH', 'X25519', 'X448', 'Argon2d', 'Argon2i', 'Argon2id']),
1184
1338
  wrapKey: new Set(['AES-CTR', 'AES-CBC', 'AES-GCM', 'AES-KW', 'AES-OCB', 'ChaCha20-Poly1305', 'RSA-OAEP']),
1185
- unwrapKey: new Set(['AES-CTR', 'AES-CBC', 'AES-GCM', 'AES-KW', 'AES-OCB', 'ChaCha20-Poly1305', 'RSA-OAEP'])
1339
+ unwrapKey: new Set(['AES-CTR', 'AES-CBC', 'AES-GCM', 'AES-KW', 'AES-OCB', 'ChaCha20-Poly1305', 'RSA-OAEP']),
1340
+ encapsulateBits: new Set(['ML-KEM-512', 'ML-KEM-768', 'ML-KEM-1024']),
1341
+ decapsulateBits: new Set(['ML-KEM-512', 'ML-KEM-768', 'ML-KEM-1024']),
1342
+ encapsulateKey: new Set(['ML-KEM-512', 'ML-KEM-768', 'ML-KEM-1024']),
1343
+ decapsulateKey: new Set(['ML-KEM-512', 'ML-KEM-768', 'ML-KEM-1024'])
1186
1344
  };
1187
- const ASYMMETRIC_ALGORITHMS = new Set(['RSASSA-PKCS1-v1_5', 'RSA-PSS', 'RSA-OAEP', 'ECDSA', 'ECDH', 'Ed25519', 'Ed448', 'X25519', 'X448', 'ML-DSA-44', 'ML-DSA-65', 'ML-DSA-87']);
1345
+ const ASYMMETRIC_ALGORITHMS = new Set(['RSASSA-PKCS1-v1_5', 'RSA-PSS', 'RSA-OAEP', 'ECDSA', 'ECDH', 'Ed25519', 'Ed448', 'X25519', 'X448', 'ML-DSA-44', 'ML-DSA-65', 'ML-DSA-87', 'ML-KEM-512', 'ML-KEM-768', 'ML-KEM-1024']);
1188
1346
  export class Subtle {
1189
1347
  static supports(operation, algorithm, _lengthOrAdditionalAlgorithm) {
1190
1348
  let normalizedAlgorithm;
@@ -1291,6 +1449,18 @@ export class Subtle {
1291
1449
  }
1292
1450
  async exportKey(format, key) {
1293
1451
  if (!key.extractable) throw new Error('key is not extractable');
1452
+ if (format === 'raw-seed') {
1453
+ const pqcAlgos = ['ML-KEM-512', 'ML-KEM-768', 'ML-KEM-1024', 'ML-DSA-44', 'ML-DSA-65', 'ML-DSA-87'];
1454
+ if (!pqcAlgos.includes(key.algorithm.name)) {
1455
+ throw lazyDOMException('raw-seed export only supported for PQC keys', 'NotSupportedError');
1456
+ }
1457
+ if (key.type !== 'private') {
1458
+ throw lazyDOMException('raw-seed export requires a private key', 'InvalidAccessError');
1459
+ }
1460
+ return bufferLikeToArrayBuffer(key.keyObject.handle.exportKey());
1461
+ }
1462
+
1463
+ // Note: 'raw-seed' is handled above; do NOT normalize it here
1294
1464
  if (format === 'raw-secret' || format === 'raw-public') format = 'raw';
1295
1465
  switch (format) {
1296
1466
  case 'spki':
@@ -1416,6 +1586,11 @@ export class Subtle {
1416
1586
  case 'HMAC':
1417
1587
  result = await hmacGenerateKey(algorithm, extractable, keyUsages);
1418
1588
  break;
1589
+ case 'KMAC128':
1590
+ // Fall through
1591
+ case 'KMAC256':
1592
+ result = await kmacGenerateKey(algorithm, extractable, keyUsages);
1593
+ break;
1419
1594
  case 'Ed25519':
1420
1595
  // Fall through
1421
1596
  case 'Ed448':
@@ -1436,6 +1611,14 @@ export class Subtle {
1436
1611
  result = await x_generateKeyPairWebCrypto(algorithm.name.toLowerCase(), extractable, keyUsages);
1437
1612
  checkCryptoKeyPairUsages(result);
1438
1613
  break;
1614
+ case 'ML-KEM-512':
1615
+ // Fall through
1616
+ case 'ML-KEM-768':
1617
+ // Fall through
1618
+ case 'ML-KEM-1024':
1619
+ result = await mlkem_generateKeyPairWebCrypto(algorithm.name, extractable, keyUsages);
1620
+ checkCryptoKeyPairUsages(result);
1621
+ break;
1439
1622
  default:
1440
1623
  throw new Error(`'subtle.generateKey()' is not implemented for ${algorithm.name}.
1441
1624
  Unrecognized algorithm name`);
@@ -1453,6 +1636,7 @@ export class Subtle {
1453
1636
  return publicKeyObject.toCryptoKey(key.algorithm, true, keyUsages);
1454
1637
  }
1455
1638
  async importKey(format, data, algorithm, extractable, keyUsages) {
1639
+ // Note: 'raw-seed' is NOT normalized — PQC import functions handle it directly
1456
1640
  if (format === 'raw-secret' || format === 'raw-public') format = 'raw';
1457
1641
  const normalizedAlgorithm = normalizeAlgorithm(algorithm, 'importKey');
1458
1642
  let result;
@@ -1472,6 +1656,11 @@ export class Subtle {
1472
1656
  case 'HMAC':
1473
1657
  result = await hmacImportKey(normalizedAlgorithm, format, data, extractable, keyUsages);
1474
1658
  break;
1659
+ case 'KMAC128':
1660
+ // Fall through
1661
+ case 'KMAC256':
1662
+ result = await kmacImportKey(normalizedAlgorithm, format, data, extractable, keyUsages);
1663
+ break;
1475
1664
  case 'AES-CTR':
1476
1665
  // Fall through
1477
1666
  case 'AES-CBC':
@@ -1510,6 +1699,13 @@ export class Subtle {
1510
1699
  case 'ML-DSA-87':
1511
1700
  result = mldsaImportKey(format, data, normalizedAlgorithm, extractable, keyUsages);
1512
1701
  break;
1702
+ case 'ML-KEM-512':
1703
+ // Fall through
1704
+ case 'ML-KEM-768':
1705
+ // Fall through
1706
+ case 'ML-KEM-1024':
1707
+ result = mlkemImportKey(format, data, normalizedAlgorithm, extractable, keyUsages);
1708
+ break;
1513
1709
  default:
1514
1710
  throw new Error(`"subtle.importKey()" is not implemented for ${normalizedAlgorithm.name}`);
1515
1711
  }
@@ -1519,85 +1715,65 @@ export class Subtle {
1519
1715
  return result;
1520
1716
  }
1521
1717
  async sign(algorithm, key, data) {
1522
- const normalizedAlgorithm = normalizeAlgorithm(algorithm, 'sign');
1523
- if (normalizedAlgorithm.name === 'HMAC') {
1524
- // Validate key usage
1525
- if (!key.usages.includes('sign')) {
1526
- throw lazyDOMException('Key does not have sign usage', 'InvalidAccessError');
1527
- }
1528
-
1529
- // Get hash algorithm from key or algorithm params
1530
- // Hash can be either a string or an object with name property
1531
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1532
- const alg = normalizedAlgorithm;
1533
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1534
- const keyAlg = key.algorithm;
1535
- let hashAlgorithm = 'SHA-256';
1536
- if (typeof alg.hash === 'string') {
1537
- hashAlgorithm = alg.hash;
1538
- } else if (alg.hash?.name) {
1539
- hashAlgorithm = alg.hash.name;
1540
- } else if (typeof keyAlg.hash === 'string') {
1541
- hashAlgorithm = keyAlg.hash;
1542
- } else if (keyAlg.hash?.name) {
1543
- hashAlgorithm = keyAlg.hash.name;
1544
- }
1545
-
1546
- // Create HMAC and sign
1547
- const keyData = key.keyObject.export();
1548
- const hmac = createHmac(hashAlgorithm, keyData);
1549
- hmac.update(bufferLikeToArrayBuffer(data));
1550
- return bufferLikeToArrayBuffer(hmac.digest());
1551
- }
1552
- return signVerify(normalizedAlgorithm, key, data);
1718
+ return signVerify(normalizeAlgorithm(algorithm, 'sign'), key, data);
1553
1719
  }
1554
1720
  async verify(algorithm, key, signature, data) {
1555
- const normalizedAlgorithm = normalizeAlgorithm(algorithm, 'verify');
1556
- if (normalizedAlgorithm.name === 'HMAC') {
1557
- // Validate key usage
1558
- if (!key.usages.includes('verify')) {
1559
- throw lazyDOMException('Key does not have verify usage', 'InvalidAccessError');
1560
- }
1561
-
1562
- // Get hash algorithm
1563
- // Hash can be either a string or an object with name property
1564
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1565
- const alg = normalizedAlgorithm;
1566
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1567
- const keyAlg = key.algorithm;
1568
- let hashAlgorithm = 'SHA-256';
1569
- if (typeof alg.hash === 'string') {
1570
- hashAlgorithm = alg.hash;
1571
- } else if (alg.hash?.name) {
1572
- hashAlgorithm = alg.hash.name;
1573
- } else if (typeof keyAlg.hash === 'string') {
1574
- hashAlgorithm = keyAlg.hash;
1575
- } else if (keyAlg.hash?.name) {
1576
- hashAlgorithm = keyAlg.hash.name;
1577
- }
1578
-
1579
- // Create HMAC and compute expected signature
1580
- const keyData = key.keyObject.export();
1581
- const hmac = createHmac(hashAlgorithm, keyData);
1582
- const dataBuffer = bufferLikeToArrayBuffer(data);
1583
- hmac.update(dataBuffer);
1584
- const expectedDigest = hmac.digest();
1585
- const expected = new Uint8Array(bufferLikeToArrayBuffer(expectedDigest));
1586
-
1587
- // Constant-time comparison
1588
- const signatureArray = new Uint8Array(bufferLikeToArrayBuffer(signature));
1589
- if (expected.length !== signatureArray.length) {
1590
- return false;
1591
- }
1592
-
1593
- // Manual constant-time comparison
1594
- let result = 0;
1595
- for (let i = 0; i < expected.length; i++) {
1596
- result |= expected[i] ^ signatureArray[i];
1597
- }
1598
- return result === 0;
1721
+ return signVerify(normalizeAlgorithm(algorithm, 'verify'), key, data, signature);
1722
+ }
1723
+ _encapsulateCore(algorithm, key) {
1724
+ const normalizedAlgorithm = normalizeAlgorithm(algorithm, 'encapsulateBits');
1725
+ if (key.algorithm.name !== normalizedAlgorithm.name) {
1726
+ throw lazyDOMException('Key algorithm mismatch', 'InvalidAccessError');
1727
+ }
1728
+ const variant = normalizedAlgorithm.name;
1729
+ const mlkem = new MlKem(variant);
1730
+ const keyData = key.keyObject.handle.exportKey(KFormatType.DER, KeyEncoding.SPKI);
1731
+ mlkem.setPublicKey(bufferLikeToArrayBuffer(keyData), KFormatType.DER, KeyEncoding.SPKI);
1732
+ return mlkem.encapsulateSync();
1733
+ }
1734
+ _decapsulateCore(algorithm, key, ciphertext) {
1735
+ const normalizedAlgorithm = normalizeAlgorithm(algorithm, 'decapsulateBits');
1736
+ if (key.algorithm.name !== normalizedAlgorithm.name) {
1737
+ throw lazyDOMException('Key algorithm mismatch', 'InvalidAccessError');
1738
+ }
1739
+ const variant = normalizedAlgorithm.name;
1740
+ const mlkem = new MlKem(variant);
1741
+ const keyData = key.keyObject.handle.exportKey(KFormatType.DER, KeyEncoding.PKCS8);
1742
+ mlkem.setPrivateKey(bufferLikeToArrayBuffer(keyData), KFormatType.DER, KeyEncoding.PKCS8);
1743
+ return mlkem.decapsulateSync(bufferLikeToArrayBuffer(ciphertext));
1744
+ }
1745
+ async encapsulateBits(algorithm, key) {
1746
+ if (!key.usages.includes('encapsulateBits')) {
1747
+ throw lazyDOMException('Key does not have encapsulateBits usage', 'InvalidAccessError');
1599
1748
  }
1600
- return signVerify(normalizedAlgorithm, key, data, signature);
1749
+ return this._encapsulateCore(algorithm, key);
1750
+ }
1751
+ async encapsulateKey(algorithm, key, sharedKeyAlgorithm, extractable, usages) {
1752
+ if (!key.usages.includes('encapsulateKey')) {
1753
+ throw lazyDOMException('Key does not have encapsulateKey usage', 'InvalidAccessError');
1754
+ }
1755
+ const {
1756
+ sharedKey,
1757
+ ciphertext
1758
+ } = this._encapsulateCore(algorithm, key);
1759
+ const importedKey = await this.importKey('raw', sharedKey, sharedKeyAlgorithm, extractable, usages);
1760
+ return {
1761
+ key: importedKey,
1762
+ ciphertext
1763
+ };
1764
+ }
1765
+ async decapsulateBits(algorithm, key, ciphertext) {
1766
+ if (!key.usages.includes('decapsulateBits')) {
1767
+ throw lazyDOMException('Key does not have decapsulateBits usage', 'InvalidAccessError');
1768
+ }
1769
+ return this._decapsulateCore(algorithm, key, ciphertext);
1770
+ }
1771
+ async decapsulateKey(algorithm, key, ciphertext, sharedKeyAlgorithm, extractable, usages) {
1772
+ if (!key.usages.includes('decapsulateKey')) {
1773
+ throw lazyDOMException('Key does not have decapsulateKey usage', 'InvalidAccessError');
1774
+ }
1775
+ const sharedKey = this._decapsulateCore(algorithm, key, ciphertext);
1776
+ return this.importKey('raw', sharedKey, sharedKeyAlgorithm, extractable, usages);
1601
1777
  }
1602
1778
  }
1603
1779
  export const subtle = new Subtle();
@@ -1616,6 +1792,10 @@ function getKeyLength(algorithm) {
1616
1792
  const hmacAlg = algorithm;
1617
1793
  return hmacAlg.length || 256;
1618
1794
  }
1795
+ case 'KMAC128':
1796
+ return algorithm.length || 128;
1797
+ case 'KMAC256':
1798
+ return algorithm.length || 256;
1619
1799
  default:
1620
1800
  throw lazyDOMException(`Cannot determine key length for ${name}`, 'NotSupportedError');
1621
1801
  }