pq_crypto 0.6.5 → 0.6.7

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 (157) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +233 -0
  3. data/README.md +16 -3
  4. data/SECURITY.md +46 -0
  5. data/ext/pqcrypto/extconf.rb +8 -1
  6. data/ext/pqcrypto/pq_externalmu.c +35 -0
  7. data/ext/pqcrypto/pqcrypto_native_api.h +91 -75
  8. data/ext/pqcrypto/pqcrypto_ruby_secure.c +97 -9
  9. data/ext/pqcrypto/pqcrypto_secure.c +95 -33
  10. data/ext/pqcrypto/pqcrypto_secure.h +66 -48
  11. data/ext/pqcrypto/pqcrypto_version.h +1 -1
  12. data/ext/pqcrypto/vendor/.vendored +7 -7
  13. data/ext/pqcrypto/vendor/mldsa-native/BUILDING.md +5 -2
  14. data/ext/pqcrypto/vendor/mldsa-native/LICENSE +21 -2
  15. data/ext/pqcrypto/vendor/mldsa-native/README.md +20 -7
  16. data/ext/pqcrypto/vendor/mldsa-native/RELEASE.md +160 -0
  17. data/ext/pqcrypto/vendor/mldsa-native/SECURITY.md +1 -1
  18. data/ext/pqcrypto/vendor/mldsa-native/mldsa/README.md +2 -2
  19. data/ext/pqcrypto/vendor/mldsa-native/mldsa/mldsa_native.c +85 -59
  20. data/ext/pqcrypto/vendor/mldsa-native/mldsa/mldsa_native.h +292 -348
  21. data/ext/pqcrypto/vendor/mldsa-native/mldsa/mldsa_native_asm.S +122 -76
  22. data/ext/pqcrypto/vendor/mldsa-native/mldsa/mldsa_native_config.h +184 -86
  23. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/cbmc.h +49 -4
  24. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/common.h +49 -81
  25. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/context.h +152 -0
  26. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/ct.h +25 -12
  27. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/debug.c +2 -0
  28. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/debug.h +2 -0
  29. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/fips202x4.c +2 -2
  30. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/keccakf1600.c +9 -11
  31. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/auto.h +19 -11
  32. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccak_f1600_x1_scalar_aarch64_asm.S +6 -4
  33. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccak_f1600_x1_v84a_aarch64_asm.S +7 -4
  34. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccak_f1600_x2_v84a_aarch64_asm.S +7 -4
  35. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccak_f1600_x4_v8a_scalar_hybrid_aarch64_asm.S +12 -9
  36. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccak_f1600_x4_v8a_v84a_scalar_hybrid_aarch64_asm.S +12 -9
  37. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x1_scalar.h +1 -1
  38. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x1_v84a.h +3 -2
  39. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x2_v84a.h +3 -2
  40. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x4_v8a_scalar.h +6 -1
  41. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x4_v8a_v84a_scalar.h +3 -2
  42. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/api.h +11 -11
  43. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/mve.h +9 -22
  44. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/src/keccak_f1600_x4_mve.S +8 -5
  45. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/src/keccak_f1600_x4_mve.c +1 -0
  46. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/src/{state_extract_bytes_x4_mve.S → keccak_f1600_x4_state_extract_bytes_mve.S} +14 -14
  47. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/src/{state_xor_bytes_x4_mve.S → keccak_f1600_x4_state_xor_bytes_mve.S} +12 -12
  48. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/auto.h +5 -4
  49. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/x86_64/keccak_f1600_x4_avx2.h +2 -2
  50. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/x86_64/src/fips202_native_x86_64.h +1 -0
  51. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/x86_64/src/keccak_f1600_x4_avx2_asm.S +36 -2
  52. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/meta.h +62 -4
  53. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/arith_native_aarch64.h +87 -54
  54. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{intt_aarch64_asm.S → mldsa_intt_aarch64_asm.S} +39 -6
  55. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{ntt_aarch64_asm.S → mldsa_ntt_aarch64_asm.S} +39 -6
  56. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{pointwise_montgomery_aarch64_asm.S → mldsa_pointwise_montgomery_aarch64_asm.S} +25 -3
  57. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{poly_caddq_aarch64_asm.S → mldsa_poly_caddq_aarch64_asm.S} +19 -3
  58. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{poly_chknorm_aarch64_asm.S → mldsa_poly_chknorm_aarch64_asm.S} +24 -3
  59. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{poly_decompose_32_aarch64_asm.S → mldsa_poly_decompose_32_aarch64_asm.S} +25 -3
  60. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{poly_decompose_88_aarch64_asm.S → mldsa_poly_decompose_88_aarch64_asm.S} +25 -3
  61. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{poly_use_hint_32_aarch64_asm.S → mldsa_poly_use_hint_32_aarch64_asm.S} +25 -3
  62. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{poly_use_hint_88_aarch64_asm.S → mldsa_poly_use_hint_88_aarch64_asm.S} +25 -3
  63. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{mld_polyvecl_pointwise_acc_montgomery_l4_aarch64_asm.S → mldsa_polyvecl_pointwise_acc_montgomery_l4_aarch64_asm.S} +31 -3
  64. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{mld_polyvecl_pointwise_acc_montgomery_l5_aarch64_asm.S → mldsa_polyvecl_pointwise_acc_montgomery_l5_aarch64_asm.S} +31 -3
  65. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{mld_polyvecl_pointwise_acc_montgomery_l7_aarch64_asm.S → mldsa_polyvecl_pointwise_acc_montgomery_l7_aarch64_asm.S} +31 -3
  66. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{polyz_unpack_17_aarch64_asm.S → mldsa_polyz_unpack_17_aarch64_asm.S} +31 -3
  67. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{polyz_unpack_19_aarch64_asm.S → mldsa_polyz_unpack_19_aarch64_asm.S} +31 -3
  68. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{rej_uniform_aarch64_asm.S → mldsa_rej_uniform_aarch64_asm.S} +48 -15
  69. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{rej_uniform_eta2_aarch64_asm.S → mldsa_rej_uniform_eta2_aarch64_asm.S} +42 -9
  70. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{rej_uniform_eta4_aarch64_asm.S → mldsa_rej_uniform_eta4_aarch64_asm.S} +42 -9
  71. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/api.h +11 -3
  72. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/meta.h +3 -2
  73. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/meta.h +28 -28
  74. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/arith_native_x86_64.h +171 -49
  75. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/{intt_avx2_asm.S → mldsa_intt_avx2_asm.S} +23 -1
  76. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/{ntt_avx2_asm.S → mldsa_ntt_avx2_asm.S} +23 -1
  77. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/{nttunpack_avx2_asm.S → mldsa_nttunpack_avx2_asm.S} +17 -1
  78. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/{pointwise_acc_l4_avx2_asm.S → mldsa_pointwise_acc_l4_avx2_asm.S} +37 -3
  79. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/{pointwise_acc_l5_avx2_asm.S → mldsa_pointwise_acc_l5_avx2_asm.S} +37 -3
  80. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/{pointwise_acc_l7_avx2_asm.S → mldsa_pointwise_acc_l7_avx2_asm.S} +37 -3
  81. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/{pointwise_avx2_asm.S → mldsa_pointwise_avx2_asm.S} +31 -3
  82. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/{poly_caddq_avx2_asm.S → mldsa_poly_caddq_avx2_asm.S} +18 -9
  83. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_poly_chknorm_avx2_asm.S +176 -0
  84. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_poly_decompose_32_avx2_asm.S +490 -0
  85. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_poly_decompose_88_avx2_asm.S +489 -0
  86. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_poly_use_hint_32_avx2_asm.S +123 -0
  87. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_poly_use_hint_88_avx2_asm.S +125 -0
  88. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_polyz_unpack_17_avx2_asm.S +355 -0
  89. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_polyz_unpack_19_avx2_asm.S +355 -0
  90. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_rej_uniform_avx2_asm.S +132 -0
  91. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_rej_uniform_eta2_avx2_asm.S +205 -0
  92. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_rej_uniform_eta4_avx2_asm.S +176 -0
  93. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/packing.c +27 -36
  94. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/packing.h +42 -8
  95. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/params.h +93 -17
  96. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/poly.c +74 -15
  97. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/poly.h +97 -11
  98. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/poly_kl.c +7 -38
  99. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/poly_kl.h +49 -7
  100. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/polyvec.c +16 -17
  101. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/polyvec.h +26 -9
  102. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/polyvec_lazy.c +3 -0
  103. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/polyvec_lazy.h +18 -19
  104. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/reduce.h +15 -3
  105. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/rounding.h +28 -6
  106. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/sign.c +311 -246
  107. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/sign.h +245 -240
  108. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/sys.h +64 -5
  109. data/ext/pqcrypto/vendor/mlkem-native/BUILDING.md +5 -2
  110. data/ext/pqcrypto/vendor/mlkem-native/LICENSE +21 -3
  111. data/ext/pqcrypto/vendor/mlkem-native/README.md +2 -2
  112. data/ext/pqcrypto/vendor/mlkem-native/RELEASE.md +113 -0
  113. data/ext/pqcrypto/vendor/mlkem-native/mlkem/README.md +2 -2
  114. data/ext/pqcrypto/vendor/mlkem-native/mlkem/mlkem_native.c +17 -27
  115. data/ext/pqcrypto/vendor/mlkem-native/mlkem/mlkem_native.h +68 -151
  116. data/ext/pqcrypto/vendor/mlkem-native/mlkem/mlkem_native_asm.S +17 -27
  117. data/ext/pqcrypto/vendor/mlkem-native/mlkem/mlkem_native_config.h +46 -44
  118. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/cbmc.h +25 -0
  119. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/common.h +37 -6
  120. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/context.h +9 -0
  121. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/fips202.h +2 -2
  122. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/keccakf1600.c +8 -8
  123. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/aarch64/x1_scalar.h +1 -1
  124. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/aarch64/x1_v84a.h +3 -3
  125. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/aarch64/x2_v84a.h +3 -3
  126. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/aarch64/x4_v8a_scalar.h +2 -2
  127. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/aarch64/x4_v8a_v84a_scalar.h +3 -3
  128. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/api.h +11 -11
  129. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/armv81m/mve.h +3 -3
  130. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/x86_64/keccak_f1600_x4_avx2.h +2 -2
  131. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/indcpa.h +14 -11
  132. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/kem.c +28 -11
  133. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/kem.h +39 -14
  134. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/aarch64/meta.h +10 -10
  135. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/meta.h +20 -20
  136. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/sys.h +5 -5
  137. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/verify.h +11 -10
  138. data/lib/pq_crypto/internal.rb +10 -0
  139. data/lib/pq_crypto/kem.rb +47 -5
  140. data/lib/pq_crypto/key.rb +14 -8
  141. data/lib/pq_crypto/pkcs8.rb +13 -5
  142. data/lib/pq_crypto/signature.rb +34 -9
  143. data/lib/pq_crypto/spki.rb +4 -2
  144. data/lib/pq_crypto/version.rb +1 -1
  145. data/lib/pq_crypto.rb +9 -0
  146. data/script/vendor_libs.rb +6 -6
  147. metadata +40 -38
  148. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/poly_chknorm_avx2.c +0 -52
  149. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/poly_decompose_32_avx2.c +0 -157
  150. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/poly_decompose_88_avx2.c +0 -157
  151. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/poly_use_hint_32_avx2.c +0 -103
  152. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/poly_use_hint_88_avx2.c +0 -105
  153. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/polyz_unpack_17_avx2.c +0 -94
  154. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/polyz_unpack_19_avx2.c +0 -96
  155. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/rej_uniform_avx2.c +0 -126
  156. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/rej_uniform_eta2_avx2.c +0 -157
  157. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/rej_uniform_eta4_avx2.c +0 -141
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 68e8c2cd661300cb364be85b4939787590eb88217a2f3cb3463e262b66c4c662
4
- data.tar.gz: fef5359500d367c68b50b2a72b54cfc8980b3c6dfd6b0e132998212e34cbd47a
3
+ metadata.gz: cf8d3b17b9579ef551ef337aab16a9eb356846a36ff492577e2d728ec0e285e2
4
+ data.tar.gz: 26c3c3646d066617e37cb2cb1219e7ee5ca640488c84b480b6259461999b48e5
5
5
  SHA512:
6
- metadata.gz: 12bfac65233efde5fa34a30df51494bc6da59985287ae2b3954b377dc9afa949f16b509e98db25d7af2a61a7f3b6a4e572b21f33ff85e797a153ee0e0dde10a0
7
- data.tar.gz: 25ccfe8a57150355da41a115c401d7ccee2b10a0a6a42957f8335cd3c97024d14261188ee676bc6a122557f52a4ea5894d67062dace53f983b51b4244d9d1a6a
6
+ metadata.gz: bd58150566a566af1c3083ef2c72538acafb6b15c5ebe954d0d7af35f96cdf41a5f1fe17c8d3c956e29b3c51bd89b29ec24245749efbca82389b1d2dc3c91ede
7
+ data.tar.gz: 5d974df3a99f9284da5c5cdfff25ba1808dc083063c12f97db6b803a34aa15687a0cda9565c8617edd95015c8d23139181d56d69d2483a3ac17b2239bec3cba3
data/CHANGELOG.md CHANGED
@@ -1,5 +1,238 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.6.7] - 2026-08-10
4
+
5
+ ### Fixed
6
+
7
+ - **DER parsers could raise exceptions outside the `PQCrypto::Error` hierarchy.**
8
+ `OpenSSL::ASN1.decode` raises more than `OpenSSL::ASN1::ASN1Error`, and
9
+ re-encoding a successfully decoded object can fail as well. Three shapes
10
+ escaped the previous handling in `PQCrypto::SPKI`:
11
+
12
+ - `"\x0A\x01\xFF"` (ENUMERATED) raised `OpenSSL::OpenSSLError`
13
+ - `"\x17\x00"` / `"\x18\x00"` (empty UTCTIME / GENERALIZEDTIME) raised `TypeError`
14
+ - `"\x10\x00"` (universal SEQUENCE tag without the constructed bit) decoded
15
+ cleanly but raised `TypeError` from the `to_der` round trip used for the
16
+ trailing-data check
17
+
18
+ `TypeError` does not descend from `OpenSSL::OpenSSLError`, so widening the
19
+ rescue to the OpenSSL base class alone was not sufficient. Both rescue sites
20
+ now catch `OpenSSL::OpenSSLError, TypeError`, and `SPKI.decode_der` is guarded
21
+ end to end rather than only around the decode call.
22
+
23
+ Impact: code written as `rescue PQCrypto::Error` around key loading could be
24
+ bypassed by malformed input. No memory-safety consequence. Covered by a
25
+ systematic tag sweep over 1,280 DER shapes × three entry points
26
+ (`Key.from_der`, `SPKI.decode_der`, `PKCS8.decode_der`).
27
+
28
+ ### Added
29
+
30
+ - **FIPS 203 key validation for ML-KEM (boundary diagnostics).** mlkem-native
31
+ v2 exposes `check_pk` and `check_sk`, which this gem did not use. Both are
32
+ now wired in and run when a `PQCrypto::KEM::PublicKey` or `SecretKey` is
33
+ constructed:
34
+
35
+ - public keys get the Section 7.2 modulus check (coefficients in `[0,q-1]`)
36
+ - secret keys get the Section 7.3 hash check (`H(pk)` embedded in the sk)
37
+
38
+ Scope limits (intentional, matching FIPS 203):
39
+
40
+ - these checks are **diagnostics at import**, not a substitute for the
41
+ checks already performed inside encapsulation / decapsulation
42
+ - Section 7.3 does **not** detect a corrupted IND-CPA secret (`dk`) when
43
+ the embedded `H(pk)` is still intact — `#valid?` can still be true
44
+ - X-Wing public keys validate only the ML-KEM-768 half; the X25519 half is
45
+ unrestricted. X-Wing secret keys are 32-byte seeds and are not structure-
46
+ checked
47
+
48
+ `#valid?` is exposed on both key classes. Failures raise
49
+ `PQCrypto::InvalidKeyError`. Public-key internal buffers are frozen after
50
+ construction to block post-validation mutation; secret-key buffers stay
51
+ mutable so `#wipe!` can still zero them in place.
52
+
53
+ New C API: `pq_mlkem{,512,1024}_check_public_key` / `_check_secret_key`,
54
+ plus `PQ_ERROR_INVALID_PUBLIC_KEY` (-11) and `PQ_ERROR_INVALID_SECRET_KEY`
55
+ (-12). Upstream OOM (`-2`) is mapped to `PQ_ERROR_NOMEM` rather than
56
+ “invalid key”.
57
+
58
+ - **ML-DSA secret-key structure check on import.** Expanded ML-DSA secret
59
+ keys are validated via upstream `pk_from_sk` (coefficient norms, recomputed
60
+ `t0`, and `tr = H(pk)`). This matches the check mldsa-native documents for
61
+ importers and is enforced on `Signature::SecretKey` construction with
62
+ `#valid?` exposed. Public keys remain length-checked only (FIPS 204 has no
63
+ ML-KEM-style modulus check for the public key).
64
+
65
+ The check runs on import only. Keys produced by this process's own key
66
+ generation are valid by construction, and `pk_from_sk` re-derives the public
67
+ key, so validating them would roughly double the cost of every ML-DSA
68
+ keypair. Upstream also documents `pk_from_sk` as leaking secret-key validity
69
+ through its return value and timing.
70
+
71
+ New C API: `pq_mldsa{,44,87}_check_secret_key`.
72
+
73
+ - **`PQCRYPTO_KEYGEN_PCT=1` build option.** Enables the FIPS 140-3 pairwise
74
+ consistency test in both backends (`MLK_CONFIG_KEYGEN_PCT` /
75
+ `MLD_CONFIG_KEYGEN_PCT`): after generating a keypair it is exercised to catch
76
+ a faulty RNG or memory corruption at generation time. Upstream ships this
77
+ opt-in because it makes key generation noticeably more expensive, and it stays
78
+ opt-in here for the same reason. The selected setting is reported in the
79
+ build configuration banner.
80
+
81
+ - **`require_encrypted:` for PKCS#8 loading.** Supplying a passphrase states an
82
+ expectation that the key at rest is encrypted, but an unencrypted
83
+ PrivateKeyInfo was previously accepted and the passphrase left unused. An
84
+ encrypted key file replaced with a plaintext one therefore loaded without
85
+ complaint.
86
+
87
+ `require_encrypted: true` enforces that the input is an
88
+ EncryptedPrivateKeyInfo (DER content, not PEM label — relabeling a plaintext
89
+ key as `ENCRYPTED PRIVATE KEY` does not satisfy the policy). Available on
90
+ `PKCS8.decode_der` / `decode_pem`, `Key.from_der` / `from_pem`, and
91
+ `secret_key_from_pkcs8_der` / `_pem` on both `KEM` and `Signature`.
92
+
93
+ Limits (documented so the flag is not over-read):
94
+
95
+ - default is `false`; other loaders (`from_bytes`, `pqc_container_*`) are
96
+ unchanged
97
+ - the flag checks **format**, not passphrase strength (empty passphrase and
98
+ low PBKDF iterations still count as encrypted)
99
+ - the value must be a real boolean (`true`/`false`); strings like `"false"`
100
+ raise `ArgumentError` instead of being treated as truthy
101
+
102
+ ## [0.6.6] - 2026-08-10
103
+
104
+ ### Changed
105
+
106
+ - update native mlkem `v1.3.0` -> `v2.0.0` and mldsa `v1.0.0-beta2` -> `v2.0.0`
107
+
108
+ `mldsa-native` v2.0.0 is that project's first stable release; it is numbered
109
+ `2` only to align its API and configuration with `mlkem-native` v2. Upstream
110
+ now guarantees that non-experimental APIs and configuration options stay
111
+ stable until a `v3`.
112
+
113
+ No cryptographic output changed. All six `kat-sha256` values in the upstream
114
+ `META.yml` files are identical to the previously vendored versions, and the
115
+ vendored ML-KEM-768 keygen, ML-DSA-65 keygen and ML-DSA-65 siggen KAT vectors
116
+ reproduce byte-for-byte. This release is purely an API migration.
117
+
118
+ ### Breaking upstream changes handled
119
+
120
+ - **`siglen` removed from the ML-DSA signing and verification APIs.** Upstream
121
+ signatures are always exactly `MLDSA_BYTES(level)`, so `signature`,
122
+ `signature_internal`, `verify`, `signature_extmu` and `verify_extmu` no longer
123
+ carry a length parameter. Fifteen declarations in
124
+ `ext/pqcrypto/pqcrypto_native_api.h` were updated accordingly.
125
+
126
+ The `pq_crypto` C API is deliberately *not* changed to match: `pq_sign`,
127
+ `pq_verify`, `pq_sign_mu` and `pq_verify_mu` keep their length parameters.
128
+ The adaptation happens at the vendor boundary instead.
129
+
130
+ - **`pq_crypto` now owns the signing output length.** Because the backend no
131
+ longer reports it, every signing wrapper sets `*signature_len` itself from a
132
+ compile-time constant and clears it to `0` when the signing operation fails,
133
+ reproducing the pre-v2 behaviour. This affects `pq_sign`, `pq_mldsa44_sign`,
134
+ `pq_mldsa87_sign`, the level-agnostic deterministic helper
135
+ `pq_testing_mldsa_sign_from_seed_with`, and `pq_sign_mu`.
136
+
137
+ Argument-validation failures (`PQ_ERROR_BUFFER`) still leave `*signature_len`
138
+ untouched, exactly as before, since the pointer itself may be the invalid one.
139
+
140
+ - **`pq_crypto` now validates signature length before verifying (security
141
+ relevant).** Prior to v2, `mldsa-native` rejected a wrongly sized signature
142
+ itself via `if (siglen != MLDSA_BYTES) return -1;`, and `pq_crypto` relied on
143
+ that. In v2 the verification entry points take a fixed-size array and read
144
+ `MLDSA_BYTES(level)` bytes unconditionally, so a short caller-supplied buffer
145
+ would be an out-of-bounds read. `pq_verify`, `pq_mldsa44_verify` and
146
+ `pq_mldsa87_verify` now enforce the exact length and return `PQ_ERROR_VERIFY`
147
+ otherwise; the Ruby bindings additionally reject the wrong length before
148
+ copying the buffer.
149
+
150
+ This was not exploitable in 0.6.5 or earlier: the previously vendored
151
+ `mldsa-native` performed the check. It is a latent contract dependency that a
152
+ naive port to v2 would have turned into a real memory-safety bug. Verified
153
+ with AddressSanitizer: removing this check alone reproduces a
154
+ `heap-buffer-overflow` read inside `pqcr_mldsa65_verify_internal`.
155
+
156
+ Observable Ruby behaviour is unchanged: a wrongly sized signature is a failed
157
+ verification (`false`), never an `ArgumentError`.
158
+
159
+ - **ExternalMu callbacks adapted rather than re-typed.** Six new adapters,
160
+ `pq_mldsa{44,65,87}_{signature,verify}_extmu_compat`, sit between the
161
+ pre-v2 callback contract and the fixed-size v2 API. `pq_sign_mu` and
162
+ `pq_verify_mu` therefore keep their existing signatures and no consumer of the
163
+ C interface has to change.
164
+
165
+ - **`MLK_CONFIG_NO_SUPERCOP` / `MLD_CONFIG_NO_SUPERCOP` removed upstream**
166
+ along with the SUPERCOP `crypto_*` aliases themselves. The define is no longer
167
+ passed by `extconf.rb`. Upstream has no guard against the stale macro, so it
168
+ would have been a silently dead `-D` rather than a build failure.
169
+
170
+ - **Legacy `MLK_CONFIG_API_*` / `MLD_CONFIG_API_*` configuration removed
171
+ upstream.** The extension build already used the modern `*_CONFIG_*` macros;
172
+ only `test/native_api_conformance` still used the legacy path and has been
173
+ converted.
174
+
175
+ Upstream breaking changes that do not affect this gem, checked explicitly: the
176
+ SUPERCOP `crypto_*` and signed-message APIs (never used), the split of
177
+ `*_ERR_FAIL` into specific error codes (all call sites compare against `0`), the
178
+ `*_SYS_CAP_*` and `*_USE_NATIVE_FIPS202_*` renames (only relevant to custom
179
+ backends), and the raised `MLD_CONFIG_MAX_SIGNING_ATTEMPTS` floor of 821, which
180
+ this build does not set. `MLD_CONFIG_KEYGEN_PCT` / `MLK_CONFIG_KEYGEN_PCT`
181
+ remain opt-in upstream and are not enabled here, so key generation gains no new
182
+ failure mode or cost.
183
+
184
+ ### Testing
185
+
186
+ - `test/native_api_conformance` reworked. It previously proved size agreement
187
+ via `_Static_assert` and, through the double declaration of upstream and
188
+ gem headers, prototype agreement. It could not detect a symbol that upstream
189
+ removed entirely, because it only ever compiled to an object file and both a
190
+ plain compile and a relocatable link tolerate unresolved symbols. The harness
191
+ now also builds the vendored objects with the same flags `extconf.rb` uses
192
+ (including which level owns the shared code) and diffs `nm -u` on the probe
193
+ object against the symbols the vendored build actually defines.
194
+
195
+ - Conformance now covers `pqcr_mlkem_shake256` and `pqcr_mlkem_sha3_256`, the
196
+ extension's only dependency on a non-public part of `mlkem-native` (they back
197
+ the X-Wing key expansion and combiner). They were absent from `probes[]`
198
+ entirely, and because they are not declared in `mlkem_native.h` the
199
+ double-declaration check could not reach them either. The shared-level
200
+ conformance unit now includes the internal `src/fips202/fips202.h` so that
201
+ prototype drift in these two is caught as well.
202
+
203
+ - Added `test/c_api`, direct tests for the `pq_crypto` C API, runnable under
204
+ AddressSanitizer via `PQCRYPTO_SANITIZE=address test/c_api/run.sh`. The Ruby
205
+ bindings pre-set `call.signature_len` before invoking the signing entry points
206
+ and read the same field back afterwards, so a wrapper that stopped writing
207
+ `*signature_len` would leave the entire Ruby suite green. That contract, and
208
+ the new signature-length validation, now have coverage that does not go
209
+ through Ruby.
210
+
211
+ - `rake test` now runs the native API conformance and C API checks alongside the
212
+ Ruby suite.
213
+
214
+ - The C API tests were themselves audited by mutation testing: each contract
215
+ this release changes was deliberately broken in turn and the suite had to
216
+ fail. That found three checks which passed against broken code and have been
217
+ rewritten:
218
+
219
+ - The ML-DSA-44 output-length check was phrased as "signing failed OR the
220
+ length is right" and reused the ML-DSA-65 secret key, so a
221
+ `pq_mldsa44_sign` that always returned an error satisfied it. ML-DSA-44 and
222
+ -87 now have their own key material and unconditional assertions.
223
+ - The ExternalMu failure check used a stub callback that cleared `*siglen`
224
+ itself, so it passed even when the real adapter had stopped doing so. It now
225
+ drives the genuine backend error path by exhausting the thread-local test
226
+ seed in `pq_randombytes.c`, which makes `randombytes()` fail. The same
227
+ technique gives `pq_sign` real coverage of its own error path.
228
+ - The verify adapters' length guards were unreachable through `pq_verify_mu`,
229
+ which validates the length first, so they were never exercised. They are now
230
+ called directly.
231
+
232
+ Two further gaps were closed: `pq_sign`'s recovery after a transient RNG
233
+ failure is now asserted, and a poisoned `*signature_len` that was set but
234
+ never checked is now checked.
235
+
3
236
  ## [0.6.5] - 2026-08-04
4
237
 
5
238
  ### Changed
data/README.md CHANGED
@@ -88,9 +88,11 @@ bundle exec rake compile
88
88
  ```
89
89
 
90
90
  Upstream pins are recorded in `ext/pqcrypto/vendor/.vendored` and enforced by
91
- `bundle exec rake vendor:verify`. `mlkem-native` is pinned at `v1.3.0`, which
92
- upstream describes as the last release before a breaking `v2`; `release/v1.3` is
93
- the upstream support branch. `mldsa-native` is pinned at `v1.0.0-beta2`.
91
+ `bundle exec rake vendor:verify`. Both `mlkem-native` and `mldsa-native` are
92
+ pinned at `v2.0.0`. For `mldsa-native` this is the first stable release; it
93
+ carries the major version `2` only so that its API and configuration line up
94
+ with `mlkem-native` v2. Upstream guarantees that APIs and configuration options
95
+ not marked experimental stay stable until a `v3`.
94
96
 
95
97
  The default build uses the portable native source path. Upstream native assembly
96
98
  can be tested explicitly with:
@@ -99,6 +101,13 @@ can be tested explicitly with:
99
101
  PQCRYPTO_NATIVE_ASM=1 bundle exec rake compile
100
102
  ```
101
103
 
104
+ The FIPS 140-3 pairwise consistency test is available but off by default,
105
+ because it makes key generation noticeably more expensive:
106
+
107
+ ```bash
108
+ PQCRYPTO_KEYGEN_PCT=1 bundle exec rake compile
109
+ ```
110
+
102
111
  ## Security status
103
112
 
104
113
  `pq_crypto` is experimental and not audited. Treat it as a low-level primitive
@@ -123,8 +132,12 @@ PQCrypto::Signature.secret_key_from_seed(:ml_dsa_65, seed_32_bytes)
123
132
 
124
133
  PQCrypto::HybridKEM.generate(:ml_kem_768_x25519_xwing)
125
134
  PQCrypto::Key.from_pem(pem, passphrase: passphrase)
135
+ PQCrypto::Key.from_pem(pem, passphrase: passphrase, require_encrypted: true)
136
+ keypair.public_key.valid? # ML-KEM structure check; ML-DSA public keys are length-only
137
+ keypair.secret_key.valid?
126
138
  ```
127
139
 
140
+
128
141
  ## More examples
129
142
 
130
143
  Detailed usage examples live in [`GET_STARTED.md`](GET_STARTED.md):
data/SECURITY.md CHANGED
@@ -127,6 +127,52 @@ into `mldsa-native` `signature_internal`; for an empty context this prefix is
127
127
  Outside of test-only deterministic calls, production randomness delegates
128
128
  directly to OpenSSL `RAND_bytes`.
129
129
 
130
+ ## Key import validation (0.6.7+)
131
+
132
+ ML-KEM public/secret keys and ML-DSA secret keys are structure-checked when
133
+ the corresponding Ruby key objects are constructed:
134
+
135
+ | Object | Check | Source |
136
+ | --- | --- | --- |
137
+ | `KEM::PublicKey` | FIPS 203 §7.2 modulus check | `check_pk` |
138
+ | `KEM::SecretKey` | FIPS 203 §7.3 `H(pk)` check | `check_sk` |
139
+ | `Signature::SecretKey` | norms / `t0` / `tr` consistency | `pk_from_sk` |
140
+ | `Signature::PublicKey` | length only | — |
141
+ | X-Wing public key | ML-KEM-768 half only | `check_pk` |
142
+ | X-Wing secret key | none (32-byte seed) | — |
143
+
144
+ These are **import diagnostics**. Encapsulation, decapsulation, signing, and
145
+ verification still apply their own backend checks. A true `#valid?` result
146
+ does not prove full cryptographic integrity of every field (for example an
147
+ ML-KEM secret key with a corrupted IND-CPA `dk` but intact `H(pk)` still
148
+ passes §7.3).
149
+
150
+ The checks run on import. Keys produced by this process's own key generation
151
+ are valid by construction and are not re-checked, because the ML-DSA check
152
+ re-derives the public key and would roughly double the cost of every keypair.
153
+
154
+ The ML-DSA check (`pk_from_sk`) is documented upstream as leaking whether the
155
+ secret key is valid, through both its return value and its timing.
156
+
157
+ `require_encrypted:` applies only to the PKCS#8 branch. `PQCrypto::Key.from_der`
158
+ and `.from_pem` also accept SPKI public keys, and for those inputs the flag has
159
+ no effect and is silently ignored.
160
+
161
+ Public-key internal buffers are frozen after construction. Secret-key buffers
162
+ remain mutable so `#wipe!` can zero them.
163
+
164
+ ### `require_encrypted:`
165
+
166
+ `require_encrypted: true` on PKCS#8 loaders rejects plaintext PrivateKeyInfo
167
+ and requires EncryptedPrivateKeyInfo (classified from DER, not PEM labels).
168
+ It does **not**:
169
+
170
+ - apply to `from_bytes` / `pqc_container_*` loaders
171
+ - enforce passphrase quality or PBKDF iteration count
172
+ - default on (default remains `false`)
173
+
174
+ The flag must be a boolean; non-boolean values raise `ArgumentError`.
175
+
130
176
  ## Memory wiping
131
177
 
132
178
  `PQCrypto.secure_wipe` clears mutable Ruby strings in place. Ruby key objects
@@ -84,6 +84,8 @@ def env_bool(name, default)
84
84
  end
85
85
  end
86
86
 
87
+ KEYGEN_PCT = env_bool("PQCRYPTO_KEYGEN_PCT", false)
88
+
87
89
  NATIVE_ASM = env_bool("PQCRYPTO_NATIVE_ASM", native_asm_supported_by_default?)
88
90
  NATIVE_ARITH = env_bool("PQCRYPTO_NATIVE_ARITH", NATIVE_ASM)
89
91
  NATIVE_FIPS202 = env_bool("PQCRYPTO_NATIVE_FIPS202", NATIVE_ASM)
@@ -524,8 +526,12 @@ def native_flags(kind, level, shared:)
524
526
  flags << "-D#{prefix}_CONFIG_MULTILEVEL_BUILD"
525
527
  flags << "-D#{prefix}_CONFIG_PARAMETER_SET=#{level}"
526
528
  flags << "-D#{prefix}_CONFIG_NAMESPACE_PREFIX=#{ns}"
527
- flags << "-D#{prefix}_CONFIG_NO_SUPERCOP"
529
+ # NOTE: MLK/MLD_CONFIG_NO_SUPERCOP was removed in mlkem-native v2.0.0 and
530
+ # mldsa-native v2.0.0 along with the SUPERCOP crypto_* aliases themselves.
531
+ # Upstream has no guard against the stale define, so passing it would be a
532
+ # silently dead -D rather than a build error. Do not reintroduce it.
528
533
  flags << (shared ? "-D#{prefix}_CONFIG_MULTILEVEL_WITH_SHARED" : "-D#{prefix}_CONFIG_MULTILEVEL_NO_SHARED")
534
+ flags << "-D#{prefix}_CONFIG_KEYGEN_PCT" if KEYGEN_PCT
529
535
  flags << "-D#{prefix}_CONFIG_USE_NATIVE_BACKEND_ARITH" if NATIVE_ARITH
530
536
  flags << "-D#{prefix}_CONFIG_USE_NATIVE_BACKEND_FIPS202" if NATIVE_FIPS202
531
537
  flags.join(" ")
@@ -612,6 +618,7 @@ puts "Vendor ASM arch flags: #{VENDOR_ASM_ARCH_FLAGS.empty? ? '(none)' : VENDOR_
612
618
  if x86_64_host? && (NATIVE_ARITH || NATIVE_FIPS202)
613
619
  puts "x86_64 native backend: AVX2 build flags enabled"
614
620
  end
621
+ puts "Keygen PCT (FIPS 140-3): #{KEYGEN_PCT ? 'enabled' : 'disabled'}"
615
622
  puts "PQClean fallback: removed"
616
623
  puts "Output: pqcrypto/pqcrypto_secure"
617
624
  puts "===================================="
@@ -66,6 +66,41 @@ int pq_mldsa_compute_tr_from_public_key(uint8_t *tr_out, const uint8_t *public_k
66
66
  return pq_shake256(tr_out, PQ_MLDSA_TRBYTES, public_key, public_key_len);
67
67
  }
68
68
 
69
+ #define PQ_DEFINE_SIGNATURE_EXTMU_COMPAT(level, native, sig_bytes) \
70
+ int pq_mldsa##level##_signature_extmu_compat(uint8_t *sig, size_t *siglen, const uint8_t *mu, \
71
+ const uint8_t *sk) { \
72
+ if (sig == NULL || siglen == NULL || mu == NULL || sk == NULL) { \
73
+ return -1; \
74
+ } \
75
+ if (native##_signature_extmu(sig, mu, sk) != 0) { \
76
+ *siglen = 0; \
77
+ return -1; \
78
+ } \
79
+ *siglen = (size_t)(sig_bytes); \
80
+ return 0; \
81
+ }
82
+
83
+ PQ_DEFINE_SIGNATURE_EXTMU_COMPAT(44, pqcr_mldsa44, MLDSA44_BYTES)
84
+ PQ_DEFINE_SIGNATURE_EXTMU_COMPAT(65, pqcr_mldsa65, MLDSA65_BYTES)
85
+ PQ_DEFINE_SIGNATURE_EXTMU_COMPAT(87, pqcr_mldsa87, MLDSA87_BYTES)
86
+
87
+ #undef PQ_DEFINE_SIGNATURE_EXTMU_COMPAT
88
+
89
+ #define PQ_DEFINE_VERIFY_EXTMU_COMPAT(level, native, sig_bytes) \
90
+ int pq_mldsa##level##_verify_extmu_compat(const uint8_t *sig, size_t siglen, \
91
+ const uint8_t *mu, const uint8_t *pk) { \
92
+ if (sig == NULL || mu == NULL || pk == NULL || siglen != (size_t)(sig_bytes)) { \
93
+ return -1; \
94
+ } \
95
+ return native##_verify_extmu(sig, mu, pk); \
96
+ }
97
+
98
+ PQ_DEFINE_VERIFY_EXTMU_COMPAT(44, pqcr_mldsa44, MLDSA44_BYTES)
99
+ PQ_DEFINE_VERIFY_EXTMU_COMPAT(65, pqcr_mldsa65, MLDSA65_BYTES)
100
+ PQ_DEFINE_VERIFY_EXTMU_COMPAT(87, pqcr_mldsa87, MLDSA87_BYTES)
101
+
102
+ #undef PQ_DEFINE_VERIFY_EXTMU_COMPAT
103
+
69
104
  int pq_sign_mu(uint8_t *signature, size_t *signature_len, const uint8_t *mu,
70
105
  const uint8_t *secret_key,
71
106
  int (*signature_extmu)(uint8_t *, size_t *, const uint8_t *, const uint8_t *)) {
@@ -15,49 +15,62 @@
15
15
  * MLD_CONFIG_NAMESPACE_PREFIX=pqcr_mldsa
16
16
  * and MLK/MLD_CONFIG_MULTILEVEL_BUILD enabled. Do not add PQClean aliases here:
17
17
  * we want one backend only so build/runtime failures point at the new stack.
18
+ *
19
+ * mldsa-native v2.0.0 removed the `siglen` in/out parameter from the signing
20
+ * and verification APIs: signatures are always exactly MLDSA_BYTES(level) and
21
+ * the buffer size is part of the API contract rather than a runtime value.
22
+ *
23
+ * Two consequences for every caller in this extension:
24
+ * 1. Signing no longer reports the produced length. Each pq_crypto wrapper
25
+ * sets its own *signature_len from the compile-time constant, and clears
26
+ * it to 0 on failure, preserving the pre-v2 pq_crypto contract.
27
+ * 2. Verification no longer rejects a wrongly sized signature. The caller
28
+ * MUST check the length itself before handing the buffer to
29
+ * pqcr_mldsa*_verify / pqcr_mldsa*_verify_extmu, which read
30
+ * MLDSA_BYTES(level) bytes unconditionally.
18
31
  */
19
32
 
20
- #define MLKEM512_SECRETKEYBYTES 1632
21
- #define MLKEM512_PUBLICKEYBYTES 800
22
- #define MLKEM512_CIPHERTEXTBYTES 768
23
- #define MLKEM512_SHAREDSECRETBYTES 32
33
+ #define MLKEM512_SECRETKEYBYTES 1632
34
+ #define MLKEM512_PUBLICKEYBYTES 800
35
+ #define MLKEM512_CIPHERTEXTBYTES 768
36
+ #define MLKEM512_SHAREDSECRETBYTES 32
24
37
 
25
- #define MLKEM768_SECRETKEYBYTES 2400
26
- #define MLKEM768_PUBLICKEYBYTES 1184
27
- #define MLKEM768_CIPHERTEXTBYTES 1088
28
- #define MLKEM768_SHAREDSECRETBYTES 32
38
+ #define MLKEM768_SECRETKEYBYTES 2400
39
+ #define MLKEM768_PUBLICKEYBYTES 1184
40
+ #define MLKEM768_CIPHERTEXTBYTES 1088
41
+ #define MLKEM768_SHAREDSECRETBYTES 32
29
42
 
30
43
  #define MLKEM1024_SECRETKEYBYTES 3168
31
44
  #define MLKEM1024_PUBLICKEYBYTES 1568
32
45
  #define MLKEM1024_CIPHERTEXTBYTES 1568
33
46
  #define MLKEM1024_SHAREDSECRETBYTES 32
34
47
 
35
- #define MLKEM_PUBLICKEYBYTES MLKEM768_PUBLICKEYBYTES
36
- #define MLKEM_SECRETKEYBYTES MLKEM768_SECRETKEYBYTES
37
- #define MLKEM_CIPHERTEXTBYTES MLKEM768_CIPHERTEXTBYTES
38
- #define MLKEM_SHAREDSECRETBYTES MLKEM768_SHAREDSECRETBYTES
39
-
40
- #define MLDSA44_SECRETKEYBYTES 2560
41
- #define MLDSA44_PUBLICKEYBYTES 1312
42
- #define MLDSA44_BYTES 2420
43
-
44
- #define MLDSA65_SECRETKEYBYTES 4032
45
- #define MLDSA65_PUBLICKEYBYTES 1952
46
- #define MLDSA65_BYTES 3309
47
-
48
- #define MLDSA87_SECRETKEYBYTES 4896
49
- #define MLDSA87_PUBLICKEYBYTES 2592
50
- #define MLDSA87_BYTES 4627
51
-
52
- #define MLDSA_PUBLICKEYBYTES MLDSA65_PUBLICKEYBYTES
53
- #define MLDSA_SECRETKEYBYTES MLDSA65_SECRETKEYBYTES
54
- #define MLDSA_BYTES MLDSA65_BYTES
55
- #define MLDSA_SEEDBYTES 32
56
- #define MLDSA_RNDBYTES 32
57
- #define MLDSA_TRBYTES 64
58
- #define MLDSA_CRHBYTES 64
48
+ #define MLKEM_PUBLICKEYBYTES MLKEM768_PUBLICKEYBYTES
49
+ #define MLKEM_SECRETKEYBYTES MLKEM768_SECRETKEYBYTES
50
+ #define MLKEM_CIPHERTEXTBYTES MLKEM768_CIPHERTEXTBYTES
51
+ #define MLKEM_SHAREDSECRETBYTES MLKEM768_SHAREDSECRETBYTES
52
+
53
+ #define MLDSA44_SECRETKEYBYTES 2560
54
+ #define MLDSA44_PUBLICKEYBYTES 1312
55
+ #define MLDSA44_BYTES 2420
56
+
57
+ #define MLDSA65_SECRETKEYBYTES 4032
58
+ #define MLDSA65_PUBLICKEYBYTES 1952
59
+ #define MLDSA65_BYTES 3309
60
+
61
+ #define MLDSA87_SECRETKEYBYTES 4896
62
+ #define MLDSA87_PUBLICKEYBYTES 2592
63
+ #define MLDSA87_BYTES 4627
64
+
65
+ #define MLDSA_PUBLICKEYBYTES MLDSA65_PUBLICKEYBYTES
66
+ #define MLDSA_SECRETKEYBYTES MLDSA65_SECRETKEYBYTES
67
+ #define MLDSA_BYTES MLDSA65_BYTES
68
+ #define MLDSA_SEEDBYTES 32
69
+ #define MLDSA_RNDBYTES 32
70
+ #define MLDSA_TRBYTES 64
71
+ #define MLDSA_CRHBYTES 64
59
72
  #define MLDSA_DOMAIN_SEPARATION_MAX_BYTES (2 + 255 + 11 + 64)
60
- #define MLDSA_PREHASH_NONE 0
73
+ #define MLDSA_PREHASH_NONE 0
61
74
 
62
75
  /* mlkem-native symbols: namespace prefix pqcr_mlkem + level suffix. */
63
76
  int pqcr_mlkem512_keypair(uint8_t *pk, uint8_t *sk);
@@ -65,18 +78,24 @@ int pqcr_mlkem512_keypair_derand(uint8_t *pk, uint8_t *sk, const uint8_t *coins)
65
78
  int pqcr_mlkem512_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk);
66
79
  int pqcr_mlkem512_enc_derand(uint8_t *ct, uint8_t *ss, const uint8_t *pk, const uint8_t *coins);
67
80
  int pqcr_mlkem512_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk);
81
+ int pqcr_mlkem512_check_pk(const uint8_t *pk);
82
+ int pqcr_mlkem512_check_sk(const uint8_t *sk);
68
83
 
69
84
  int pqcr_mlkem768_keypair(uint8_t *pk, uint8_t *sk);
70
85
  int pqcr_mlkem768_keypair_derand(uint8_t *pk, uint8_t *sk, const uint8_t *coins);
71
86
  int pqcr_mlkem768_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk);
72
87
  int pqcr_mlkem768_enc_derand(uint8_t *ct, uint8_t *ss, const uint8_t *pk, const uint8_t *coins);
73
88
  int pqcr_mlkem768_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk);
89
+ int pqcr_mlkem768_check_pk(const uint8_t *pk);
90
+ int pqcr_mlkem768_check_sk(const uint8_t *sk);
74
91
 
75
92
  int pqcr_mlkem1024_keypair(uint8_t *pk, uint8_t *sk);
76
93
  int pqcr_mlkem1024_keypair_derand(uint8_t *pk, uint8_t *sk, const uint8_t *coins);
77
94
  int pqcr_mlkem1024_enc(uint8_t *ct, uint8_t *ss, const uint8_t *pk);
78
95
  int pqcr_mlkem1024_enc_derand(uint8_t *ct, uint8_t *ss, const uint8_t *pk, const uint8_t *coins);
79
96
  int pqcr_mlkem1024_dec(uint8_t *ss, const uint8_t *ct, const uint8_t *sk);
97
+ int pqcr_mlkem1024_check_pk(const uint8_t *pk);
98
+ int pqcr_mlkem1024_check_sk(const uint8_t *sk);
80
99
 
81
100
  void pqcr_mlkem_shake256(uint8_t *output, size_t outlen, const uint8_t *input, size_t inlen);
82
101
  void pqcr_mlkem_sha3_256(uint8_t *output, const uint8_t *input, size_t inlen);
@@ -84,58 +103,55 @@ void pqcr_mlkem_sha3_256(uint8_t *output, const uint8_t *input, size_t inlen);
84
103
  /* mldsa-native symbols: namespace prefix pqcr_mldsa + level suffix. */
85
104
  int pqcr_mldsa44_keypair(uint8_t *pk, uint8_t *sk);
86
105
  int pqcr_mldsa44_keypair_internal(uint8_t *pk, uint8_t *sk, const uint8_t seed[MLDSA_SEEDBYTES]);
87
- int pqcr_mldsa44_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen,
88
- const uint8_t *ctx, size_t ctxlen, const uint8_t *sk);
89
- int pqcr_mldsa44_signature_internal(uint8_t *sig, size_t *siglen, const uint8_t *m,
90
- size_t mlen, const uint8_t *pre, size_t prelen,
91
- const uint8_t rnd[MLDSA_RNDBYTES], const uint8_t *sk,
92
- int externalmu);
93
- int pqcr_mldsa44_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen,
94
- const uint8_t *ctx, size_t ctxlen, const uint8_t *pk);
95
- size_t pqcr_mldsa44_prepare_domain_separation_prefix(
96
- uint8_t prefix[MLDSA_DOMAIN_SEPARATION_MAX_BYTES], const uint8_t *ph, size_t phlen,
97
- const uint8_t *ctx, size_t ctxlen, int hashalg);
98
- int pqcr_mldsa44_signature_extmu(uint8_t *sig, size_t *siglen, const uint8_t mu[MLDSA_CRHBYTES],
99
- const uint8_t *sk);
100
- int pqcr_mldsa44_verify_extmu(const uint8_t *sig, size_t siglen, const uint8_t mu[MLDSA_CRHBYTES],
106
+ int pqcr_mldsa44_signature(uint8_t *sig, const uint8_t *m, size_t mlen, const uint8_t *ctx,
107
+ size_t ctxlen, const uint8_t *sk);
108
+ int pqcr_mldsa44_signature_internal(uint8_t *sig, const uint8_t *m, size_t mlen, const uint8_t *pre,
109
+ size_t prelen, const uint8_t rnd[MLDSA_RNDBYTES],
110
+ const uint8_t *sk, int externalmu);
111
+ int pqcr_mldsa44_verify(const uint8_t *sig, const uint8_t *m, size_t mlen, const uint8_t *ctx,
112
+ size_t ctxlen, const uint8_t *pk);
113
+ size_t
114
+ pqcr_mldsa44_prepare_domain_separation_prefix(uint8_t prefix[MLDSA_DOMAIN_SEPARATION_MAX_BYTES],
115
+ const uint8_t *ph, size_t phlen, const uint8_t *ctx,
116
+ size_t ctxlen, int hashalg);
117
+ int pqcr_mldsa44_signature_extmu(uint8_t *sig, const uint8_t mu[MLDSA_CRHBYTES], const uint8_t *sk);
118
+ int pqcr_mldsa44_verify_extmu(const uint8_t *sig, const uint8_t mu[MLDSA_CRHBYTES],
101
119
  const uint8_t *pk);
102
120
  int pqcr_mldsa44_pk_from_sk(uint8_t *pk, const uint8_t *sk);
103
121
 
104
122
  int pqcr_mldsa65_keypair(uint8_t *pk, uint8_t *sk);
105
123
  int pqcr_mldsa65_keypair_internal(uint8_t *pk, uint8_t *sk, const uint8_t seed[MLDSA_SEEDBYTES]);
106
- int pqcr_mldsa65_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen,
107
- const uint8_t *ctx, size_t ctxlen, const uint8_t *sk);
108
- int pqcr_mldsa65_signature_internal(uint8_t *sig, size_t *siglen, const uint8_t *m,
109
- size_t mlen, const uint8_t *pre, size_t prelen,
110
- const uint8_t rnd[MLDSA_RNDBYTES], const uint8_t *sk,
111
- int externalmu);
112
- int pqcr_mldsa65_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen,
113
- const uint8_t *ctx, size_t ctxlen, const uint8_t *pk);
114
- size_t pqcr_mldsa65_prepare_domain_separation_prefix(
115
- uint8_t prefix[MLDSA_DOMAIN_SEPARATION_MAX_BYTES], const uint8_t *ph, size_t phlen,
116
- const uint8_t *ctx, size_t ctxlen, int hashalg);
117
- int pqcr_mldsa65_signature_extmu(uint8_t *sig, size_t *siglen, const uint8_t mu[MLDSA_CRHBYTES],
118
- const uint8_t *sk);
119
- int pqcr_mldsa65_verify_extmu(const uint8_t *sig, size_t siglen, const uint8_t mu[MLDSA_CRHBYTES],
124
+ int pqcr_mldsa65_signature(uint8_t *sig, const uint8_t *m, size_t mlen, const uint8_t *ctx,
125
+ size_t ctxlen, const uint8_t *sk);
126
+ int pqcr_mldsa65_signature_internal(uint8_t *sig, const uint8_t *m, size_t mlen, const uint8_t *pre,
127
+ size_t prelen, const uint8_t rnd[MLDSA_RNDBYTES],
128
+ const uint8_t *sk, int externalmu);
129
+ int pqcr_mldsa65_verify(const uint8_t *sig, const uint8_t *m, size_t mlen, const uint8_t *ctx,
130
+ size_t ctxlen, const uint8_t *pk);
131
+ size_t
132
+ pqcr_mldsa65_prepare_domain_separation_prefix(uint8_t prefix[MLDSA_DOMAIN_SEPARATION_MAX_BYTES],
133
+ const uint8_t *ph, size_t phlen, const uint8_t *ctx,
134
+ size_t ctxlen, int hashalg);
135
+ int pqcr_mldsa65_signature_extmu(uint8_t *sig, const uint8_t mu[MLDSA_CRHBYTES], const uint8_t *sk);
136
+ int pqcr_mldsa65_verify_extmu(const uint8_t *sig, const uint8_t mu[MLDSA_CRHBYTES],
120
137
  const uint8_t *pk);
121
138
  int pqcr_mldsa65_pk_from_sk(uint8_t *pk, const uint8_t *sk);
122
139
 
123
140
  int pqcr_mldsa87_keypair(uint8_t *pk, uint8_t *sk);
124
141
  int pqcr_mldsa87_keypair_internal(uint8_t *pk, uint8_t *sk, const uint8_t seed[MLDSA_SEEDBYTES]);
125
- int pqcr_mldsa87_signature(uint8_t *sig, size_t *siglen, const uint8_t *m, size_t mlen,
126
- const uint8_t *ctx, size_t ctxlen, const uint8_t *sk);
127
- int pqcr_mldsa87_signature_internal(uint8_t *sig, size_t *siglen, const uint8_t *m,
128
- size_t mlen, const uint8_t *pre, size_t prelen,
129
- const uint8_t rnd[MLDSA_RNDBYTES], const uint8_t *sk,
130
- int externalmu);
131
- int pqcr_mldsa87_verify(const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen,
132
- const uint8_t *ctx, size_t ctxlen, const uint8_t *pk);
133
- size_t pqcr_mldsa87_prepare_domain_separation_prefix(
134
- uint8_t prefix[MLDSA_DOMAIN_SEPARATION_MAX_BYTES], const uint8_t *ph, size_t phlen,
135
- const uint8_t *ctx, size_t ctxlen, int hashalg);
136
- int pqcr_mldsa87_signature_extmu(uint8_t *sig, size_t *siglen, const uint8_t mu[MLDSA_CRHBYTES],
137
- const uint8_t *sk);
138
- int pqcr_mldsa87_verify_extmu(const uint8_t *sig, size_t siglen, const uint8_t mu[MLDSA_CRHBYTES],
142
+ int pqcr_mldsa87_signature(uint8_t *sig, const uint8_t *m, size_t mlen, const uint8_t *ctx,
143
+ size_t ctxlen, const uint8_t *sk);
144
+ int pqcr_mldsa87_signature_internal(uint8_t *sig, const uint8_t *m, size_t mlen, const uint8_t *pre,
145
+ size_t prelen, const uint8_t rnd[MLDSA_RNDBYTES],
146
+ const uint8_t *sk, int externalmu);
147
+ int pqcr_mldsa87_verify(const uint8_t *sig, const uint8_t *m, size_t mlen, const uint8_t *ctx,
148
+ size_t ctxlen, const uint8_t *pk);
149
+ size_t
150
+ pqcr_mldsa87_prepare_domain_separation_prefix(uint8_t prefix[MLDSA_DOMAIN_SEPARATION_MAX_BYTES],
151
+ const uint8_t *ph, size_t phlen, const uint8_t *ctx,
152
+ size_t ctxlen, int hashalg);
153
+ int pqcr_mldsa87_signature_extmu(uint8_t *sig, const uint8_t mu[MLDSA_CRHBYTES], const uint8_t *sk);
154
+ int pqcr_mldsa87_verify_extmu(const uint8_t *sig, const uint8_t mu[MLDSA_CRHBYTES],
139
155
  const uint8_t *pk);
140
156
  int pqcr_mldsa87_pk_from_sk(uint8_t *pk, const uint8_t *sk);
141
157