pq_crypto 0.6.5 → 0.6.6

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 (149) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +134 -0
  3. data/README.md +5 -3
  4. data/ext/pqcrypto/extconf.rb +4 -1
  5. data/ext/pqcrypto/pq_externalmu.c +35 -0
  6. data/ext/pqcrypto/pqcrypto_native_api.h +85 -75
  7. data/ext/pqcrypto/pqcrypto_ruby_secure.c +16 -9
  8. data/ext/pqcrypto/pqcrypto_secure.c +43 -33
  9. data/ext/pqcrypto/pqcrypto_secure.h +52 -47
  10. data/ext/pqcrypto/pqcrypto_version.h +1 -1
  11. data/ext/pqcrypto/vendor/.vendored +7 -7
  12. data/ext/pqcrypto/vendor/mldsa-native/BUILDING.md +5 -2
  13. data/ext/pqcrypto/vendor/mldsa-native/LICENSE +21 -2
  14. data/ext/pqcrypto/vendor/mldsa-native/README.md +20 -7
  15. data/ext/pqcrypto/vendor/mldsa-native/RELEASE.md +160 -0
  16. data/ext/pqcrypto/vendor/mldsa-native/SECURITY.md +1 -1
  17. data/ext/pqcrypto/vendor/mldsa-native/mldsa/README.md +2 -2
  18. data/ext/pqcrypto/vendor/mldsa-native/mldsa/mldsa_native.c +85 -59
  19. data/ext/pqcrypto/vendor/mldsa-native/mldsa/mldsa_native.h +292 -348
  20. data/ext/pqcrypto/vendor/mldsa-native/mldsa/mldsa_native_asm.S +122 -76
  21. data/ext/pqcrypto/vendor/mldsa-native/mldsa/mldsa_native_config.h +184 -86
  22. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/cbmc.h +49 -4
  23. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/common.h +49 -81
  24. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/context.h +152 -0
  25. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/ct.h +25 -12
  26. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/debug.c +2 -0
  27. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/debug.h +2 -0
  28. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/fips202x4.c +2 -2
  29. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/keccakf1600.c +9 -11
  30. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/auto.h +19 -11
  31. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccak_f1600_x1_scalar_aarch64_asm.S +6 -4
  32. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccak_f1600_x1_v84a_aarch64_asm.S +7 -4
  33. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccak_f1600_x2_v84a_aarch64_asm.S +7 -4
  34. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccak_f1600_x4_v8a_scalar_hybrid_aarch64_asm.S +12 -9
  35. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccak_f1600_x4_v8a_v84a_scalar_hybrid_aarch64_asm.S +12 -9
  36. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x1_scalar.h +1 -1
  37. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x1_v84a.h +3 -2
  38. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x2_v84a.h +3 -2
  39. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x4_v8a_scalar.h +6 -1
  40. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x4_v8a_v84a_scalar.h +3 -2
  41. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/api.h +11 -11
  42. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/mve.h +9 -22
  43. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/src/keccak_f1600_x4_mve.S +8 -5
  44. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/src/keccak_f1600_x4_mve.c +1 -0
  45. 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
  46. 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
  47. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/auto.h +5 -4
  48. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/x86_64/keccak_f1600_x4_avx2.h +2 -2
  49. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/x86_64/src/fips202_native_x86_64.h +1 -0
  50. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/fips202/native/x86_64/src/keccak_f1600_x4_avx2_asm.S +36 -2
  51. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/meta.h +62 -4
  52. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/arith_native_aarch64.h +87 -54
  53. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{intt_aarch64_asm.S → mldsa_intt_aarch64_asm.S} +39 -6
  54. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{ntt_aarch64_asm.S → mldsa_ntt_aarch64_asm.S} +39 -6
  55. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{pointwise_montgomery_aarch64_asm.S → mldsa_pointwise_montgomery_aarch64_asm.S} +25 -3
  56. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{poly_caddq_aarch64_asm.S → mldsa_poly_caddq_aarch64_asm.S} +19 -3
  57. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{poly_chknorm_aarch64_asm.S → mldsa_poly_chknorm_aarch64_asm.S} +24 -3
  58. 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
  59. 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
  60. 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
  61. 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
  62. 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
  63. 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
  64. 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
  65. 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
  66. 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
  67. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/aarch64/src/{rej_uniform_aarch64_asm.S → mldsa_rej_uniform_aarch64_asm.S} +48 -15
  68. 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
  69. 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
  70. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/api.h +11 -3
  71. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/meta.h +3 -2
  72. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/meta.h +28 -28
  73. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/arith_native_x86_64.h +171 -49
  74. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/{intt_avx2_asm.S → mldsa_intt_avx2_asm.S} +23 -1
  75. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/{ntt_avx2_asm.S → mldsa_ntt_avx2_asm.S} +23 -1
  76. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/{nttunpack_avx2_asm.S → mldsa_nttunpack_avx2_asm.S} +17 -1
  77. 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
  78. 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
  79. 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
  80. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/{pointwise_avx2_asm.S → mldsa_pointwise_avx2_asm.S} +31 -3
  81. 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
  82. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_poly_chknorm_avx2_asm.S +176 -0
  83. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_poly_decompose_32_avx2_asm.S +490 -0
  84. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_poly_decompose_88_avx2_asm.S +489 -0
  85. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_poly_use_hint_32_avx2_asm.S +123 -0
  86. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_poly_use_hint_88_avx2_asm.S +125 -0
  87. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_polyz_unpack_17_avx2_asm.S +355 -0
  88. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_polyz_unpack_19_avx2_asm.S +355 -0
  89. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_rej_uniform_avx2_asm.S +132 -0
  90. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_rej_uniform_eta2_avx2_asm.S +205 -0
  91. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_rej_uniform_eta4_avx2_asm.S +176 -0
  92. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/packing.c +27 -36
  93. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/packing.h +42 -8
  94. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/params.h +93 -17
  95. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/poly.c +74 -15
  96. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/poly.h +97 -11
  97. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/poly_kl.c +7 -38
  98. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/poly_kl.h +49 -7
  99. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/polyvec.c +16 -17
  100. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/polyvec.h +26 -9
  101. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/polyvec_lazy.c +3 -0
  102. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/polyvec_lazy.h +18 -19
  103. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/reduce.h +15 -3
  104. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/rounding.h +28 -6
  105. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/sign.c +311 -246
  106. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/sign.h +245 -240
  107. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/sys.h +64 -5
  108. data/ext/pqcrypto/vendor/mlkem-native/BUILDING.md +5 -2
  109. data/ext/pqcrypto/vendor/mlkem-native/LICENSE +21 -3
  110. data/ext/pqcrypto/vendor/mlkem-native/README.md +2 -2
  111. data/ext/pqcrypto/vendor/mlkem-native/RELEASE.md +113 -0
  112. data/ext/pqcrypto/vendor/mlkem-native/mlkem/README.md +2 -2
  113. data/ext/pqcrypto/vendor/mlkem-native/mlkem/mlkem_native.c +17 -27
  114. data/ext/pqcrypto/vendor/mlkem-native/mlkem/mlkem_native.h +68 -151
  115. data/ext/pqcrypto/vendor/mlkem-native/mlkem/mlkem_native_asm.S +17 -27
  116. data/ext/pqcrypto/vendor/mlkem-native/mlkem/mlkem_native_config.h +46 -44
  117. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/cbmc.h +25 -0
  118. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/common.h +37 -6
  119. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/context.h +9 -0
  120. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/fips202.h +2 -2
  121. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/keccakf1600.c +8 -8
  122. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/aarch64/x1_scalar.h +1 -1
  123. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/aarch64/x1_v84a.h +3 -3
  124. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/aarch64/x2_v84a.h +3 -3
  125. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/aarch64/x4_v8a_scalar.h +2 -2
  126. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/aarch64/x4_v8a_v84a_scalar.h +3 -3
  127. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/api.h +11 -11
  128. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/armv81m/mve.h +3 -3
  129. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/fips202/native/x86_64/keccak_f1600_x4_avx2.h +2 -2
  130. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/indcpa.h +14 -11
  131. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/kem.c +28 -11
  132. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/kem.h +39 -14
  133. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/aarch64/meta.h +10 -10
  134. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/native/x86_64/meta.h +20 -20
  135. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/sys.h +5 -5
  136. data/ext/pqcrypto/vendor/mlkem-native/mlkem/src/verify.h +11 -10
  137. data/lib/pq_crypto/version.rb +1 -1
  138. data/script/vendor_libs.rb +6 -6
  139. metadata +40 -38
  140. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/poly_chknorm_avx2.c +0 -52
  141. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/poly_decompose_32_avx2.c +0 -157
  142. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/poly_decompose_88_avx2.c +0 -157
  143. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/poly_use_hint_32_avx2.c +0 -103
  144. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/poly_use_hint_88_avx2.c +0 -105
  145. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/polyz_unpack_17_avx2.c +0 -94
  146. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/polyz_unpack_19_avx2.c +0 -96
  147. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/rej_uniform_avx2.c +0 -126
  148. data/ext/pqcrypto/vendor/mldsa-native/mldsa/src/native/x86_64/src/rej_uniform_eta2_avx2.c +0 -157
  149. 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: 273b83c96db2314540118b933a9f70a22c954e59a6fb38377f7c6cd1f337d698
4
+ data.tar.gz: 0a6f4b88a3aefde58049f41f582548aa10f2aa939e98a8b60791ff23c8a5be0e
5
5
  SHA512:
6
- metadata.gz: 12bfac65233efde5fa34a30df51494bc6da59985287ae2b3954b377dc9afa949f16b509e98db25d7af2a61a7f3b6a4e572b21f33ff85e797a153ee0e0dde10a0
7
- data.tar.gz: 25ccfe8a57150355da41a115c401d7ccee2b10a0a6a42957f8335cd3c97024d14261188ee676bc6a122557f52a4ea5894d67062dace53f983b51b4244d9d1a6a
6
+ metadata.gz: f4905490a34b75ae7548a896cbb379b135c7615dfba9dca564add76892fb6074a6e0a12220ca3158417ea3264c2dcb2dc432b0a49fa91f8429eaebeef966d895
7
+ data.tar.gz: 854941b5a331956cba2aa91ef1777148f9a93713eec6ce746fe01475b126b69f409ad0dbcf631a1db5c7c09d127d55053d59585850bcf06a8047f8b2ae14adec
data/CHANGELOG.md CHANGED
@@ -1,5 +1,139 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.6.6] - 2026-08-10
4
+
5
+ ### Changed
6
+
7
+ - update native mlkem `v1.3.0` -> `v2.0.0` and mldsa `v1.0.0-beta2` -> `v2.0.0`
8
+
9
+ `mldsa-native` v2.0.0 is that project's first stable release; it is numbered
10
+ `2` only to align its API and configuration with `mlkem-native` v2. Upstream
11
+ now guarantees that non-experimental APIs and configuration options stay
12
+ stable until a `v3`.
13
+
14
+ No cryptographic output changed. All six `kat-sha256` values in the upstream
15
+ `META.yml` files are identical to the previously vendored versions, and the
16
+ vendored ML-KEM-768 keygen, ML-DSA-65 keygen and ML-DSA-65 siggen KAT vectors
17
+ reproduce byte-for-byte. This release is purely an API migration.
18
+
19
+ ### Breaking upstream changes handled
20
+
21
+ - **`siglen` removed from the ML-DSA signing and verification APIs.** Upstream
22
+ signatures are always exactly `MLDSA_BYTES(level)`, so `signature`,
23
+ `signature_internal`, `verify`, `signature_extmu` and `verify_extmu` no longer
24
+ carry a length parameter. Fifteen declarations in
25
+ `ext/pqcrypto/pqcrypto_native_api.h` were updated accordingly.
26
+
27
+ The `pq_crypto` C API is deliberately *not* changed to match: `pq_sign`,
28
+ `pq_verify`, `pq_sign_mu` and `pq_verify_mu` keep their length parameters.
29
+ The adaptation happens at the vendor boundary instead.
30
+
31
+ - **`pq_crypto` now owns the signing output length.** Because the backend no
32
+ longer reports it, every signing wrapper sets `*signature_len` itself from a
33
+ compile-time constant and clears it to `0` when the signing operation fails,
34
+ reproducing the pre-v2 behaviour. This affects `pq_sign`, `pq_mldsa44_sign`,
35
+ `pq_mldsa87_sign`, the level-agnostic deterministic helper
36
+ `pq_testing_mldsa_sign_from_seed_with`, and `pq_sign_mu`.
37
+
38
+ Argument-validation failures (`PQ_ERROR_BUFFER`) still leave `*signature_len`
39
+ untouched, exactly as before, since the pointer itself may be the invalid one.
40
+
41
+ - **`pq_crypto` now validates signature length before verifying (security
42
+ relevant).** Prior to v2, `mldsa-native` rejected a wrongly sized signature
43
+ itself via `if (siglen != MLDSA_BYTES) return -1;`, and `pq_crypto` relied on
44
+ that. In v2 the verification entry points take a fixed-size array and read
45
+ `MLDSA_BYTES(level)` bytes unconditionally, so a short caller-supplied buffer
46
+ would be an out-of-bounds read. `pq_verify`, `pq_mldsa44_verify` and
47
+ `pq_mldsa87_verify` now enforce the exact length and return `PQ_ERROR_VERIFY`
48
+ otherwise; the Ruby bindings additionally reject the wrong length before
49
+ copying the buffer.
50
+
51
+ This was not exploitable in 0.6.5 or earlier: the previously vendored
52
+ `mldsa-native` performed the check. It is a latent contract dependency that a
53
+ naive port to v2 would have turned into a real memory-safety bug. Verified
54
+ with AddressSanitizer: removing this check alone reproduces a
55
+ `heap-buffer-overflow` read inside `pqcr_mldsa65_verify_internal`.
56
+
57
+ Observable Ruby behaviour is unchanged: a wrongly sized signature is a failed
58
+ verification (`false`), never an `ArgumentError`.
59
+
60
+ - **ExternalMu callbacks adapted rather than re-typed.** Six new adapters,
61
+ `pq_mldsa{44,65,87}_{signature,verify}_extmu_compat`, sit between the
62
+ pre-v2 callback contract and the fixed-size v2 API. `pq_sign_mu` and
63
+ `pq_verify_mu` therefore keep their existing signatures and no consumer of the
64
+ C interface has to change.
65
+
66
+ - **`MLK_CONFIG_NO_SUPERCOP` / `MLD_CONFIG_NO_SUPERCOP` removed upstream**
67
+ along with the SUPERCOP `crypto_*` aliases themselves. The define is no longer
68
+ passed by `extconf.rb`. Upstream has no guard against the stale macro, so it
69
+ would have been a silently dead `-D` rather than a build failure.
70
+
71
+ - **Legacy `MLK_CONFIG_API_*` / `MLD_CONFIG_API_*` configuration removed
72
+ upstream.** The extension build already used the modern `*_CONFIG_*` macros;
73
+ only `test/native_api_conformance` still used the legacy path and has been
74
+ converted.
75
+
76
+ Upstream breaking changes that do not affect this gem, checked explicitly: the
77
+ SUPERCOP `crypto_*` and signed-message APIs (never used), the split of
78
+ `*_ERR_FAIL` into specific error codes (all call sites compare against `0`), the
79
+ `*_SYS_CAP_*` and `*_USE_NATIVE_FIPS202_*` renames (only relevant to custom
80
+ backends), and the raised `MLD_CONFIG_MAX_SIGNING_ATTEMPTS` floor of 821, which
81
+ this build does not set. `MLD_CONFIG_KEYGEN_PCT` / `MLK_CONFIG_KEYGEN_PCT`
82
+ remain opt-in upstream and are not enabled here, so key generation gains no new
83
+ failure mode or cost.
84
+
85
+ ### Testing
86
+
87
+ - `test/native_api_conformance` reworked. It previously proved size agreement
88
+ via `_Static_assert` and, through the double declaration of upstream and
89
+ gem headers, prototype agreement. It could not detect a symbol that upstream
90
+ removed entirely, because it only ever compiled to an object file and both a
91
+ plain compile and a relocatable link tolerate unresolved symbols. The harness
92
+ now also builds the vendored objects with the same flags `extconf.rb` uses
93
+ (including which level owns the shared code) and diffs `nm -u` on the probe
94
+ object against the symbols the vendored build actually defines.
95
+
96
+ - Conformance now covers `pqcr_mlkem_shake256` and `pqcr_mlkem_sha3_256`, the
97
+ extension's only dependency on a non-public part of `mlkem-native` (they back
98
+ the X-Wing key expansion and combiner). They were absent from `probes[]`
99
+ entirely, and because they are not declared in `mlkem_native.h` the
100
+ double-declaration check could not reach them either. The shared-level
101
+ conformance unit now includes the internal `src/fips202/fips202.h` so that
102
+ prototype drift in these two is caught as well.
103
+
104
+ - Added `test/c_api`, direct tests for the `pq_crypto` C API, runnable under
105
+ AddressSanitizer via `PQCRYPTO_SANITIZE=address test/c_api/run.sh`. The Ruby
106
+ bindings pre-set `call.signature_len` before invoking the signing entry points
107
+ and read the same field back afterwards, so a wrapper that stopped writing
108
+ `*signature_len` would leave the entire Ruby suite green. That contract, and
109
+ the new signature-length validation, now have coverage that does not go
110
+ through Ruby.
111
+
112
+ - `rake test` now runs the native API conformance and C API checks alongside the
113
+ Ruby suite.
114
+
115
+ - The C API tests were themselves audited by mutation testing: each contract
116
+ this release changes was deliberately broken in turn and the suite had to
117
+ fail. That found three checks which passed against broken code and have been
118
+ rewritten:
119
+
120
+ - The ML-DSA-44 output-length check was phrased as "signing failed OR the
121
+ length is right" and reused the ML-DSA-65 secret key, so a
122
+ `pq_mldsa44_sign` that always returned an error satisfied it. ML-DSA-44 and
123
+ -87 now have their own key material and unconditional assertions.
124
+ - The ExternalMu failure check used a stub callback that cleared `*siglen`
125
+ itself, so it passed even when the real adapter had stopped doing so. It now
126
+ drives the genuine backend error path by exhausting the thread-local test
127
+ seed in `pq_randombytes.c`, which makes `randombytes()` fail. The same
128
+ technique gives `pq_sign` real coverage of its own error path.
129
+ - The verify adapters' length guards were unreachable through `pq_verify_mu`,
130
+ which validates the length first, so they were never exercised. They are now
131
+ called directly.
132
+
133
+ Two further gaps were closed: `pq_sign`'s recovery after a transient RNG
134
+ failure is now asserted, and a poisoned `*signature_len` that was set but
135
+ never checked is now checked.
136
+
3
137
  ## [0.6.5] - 2026-08-04
4
138
 
5
139
  ### 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:
@@ -524,7 +524,10 @@ def native_flags(kind, level, shared:)
524
524
  flags << "-D#{prefix}_CONFIG_MULTILEVEL_BUILD"
525
525
  flags << "-D#{prefix}_CONFIG_PARAMETER_SET=#{level}"
526
526
  flags << "-D#{prefix}_CONFIG_NAMESPACE_PREFIX=#{ns}"
527
- flags << "-D#{prefix}_CONFIG_NO_SUPERCOP"
527
+ # NOTE: MLK/MLD_CONFIG_NO_SUPERCOP was removed in mlkem-native v2.0.0 and
528
+ # mldsa-native v2.0.0 along with the SUPERCOP crypto_* aliases themselves.
529
+ # Upstream has no guard against the stale define, so passing it would be a
530
+ # silently dead -D rather than a build error. Do not reintroduce it.
528
531
  flags << (shared ? "-D#{prefix}_CONFIG_MULTILEVEL_WITH_SHARED" : "-D#{prefix}_CONFIG_MULTILEVEL_NO_SHARED")
529
532
  flags << "-D#{prefix}_CONFIG_USE_NATIVE_BACKEND_ARITH" if NATIVE_ARITH
530
533
  flags << "-D#{prefix}_CONFIG_USE_NATIVE_BACKEND_FIPS202" if NATIVE_FIPS202
@@ -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);
@@ -84,58 +97,55 @@ void pqcr_mlkem_sha3_256(uint8_t *output, const uint8_t *input, size_t inlen);
84
97
  /* mldsa-native symbols: namespace prefix pqcr_mldsa + level suffix. */
85
98
  int pqcr_mldsa44_keypair(uint8_t *pk, uint8_t *sk);
86
99
  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],
100
+ int pqcr_mldsa44_signature(uint8_t *sig, const uint8_t *m, size_t mlen, const uint8_t *ctx,
101
+ size_t ctxlen, const uint8_t *sk);
102
+ int pqcr_mldsa44_signature_internal(uint8_t *sig, const uint8_t *m, size_t mlen, const uint8_t *pre,
103
+ size_t prelen, const uint8_t rnd[MLDSA_RNDBYTES],
104
+ const uint8_t *sk, int externalmu);
105
+ int pqcr_mldsa44_verify(const uint8_t *sig, const uint8_t *m, size_t mlen, const uint8_t *ctx,
106
+ size_t ctxlen, const uint8_t *pk);
107
+ size_t
108
+ pqcr_mldsa44_prepare_domain_separation_prefix(uint8_t prefix[MLDSA_DOMAIN_SEPARATION_MAX_BYTES],
109
+ const uint8_t *ph, size_t phlen, const uint8_t *ctx,
110
+ size_t ctxlen, int hashalg);
111
+ int pqcr_mldsa44_signature_extmu(uint8_t *sig, const uint8_t mu[MLDSA_CRHBYTES], const uint8_t *sk);
112
+ int pqcr_mldsa44_verify_extmu(const uint8_t *sig, const uint8_t mu[MLDSA_CRHBYTES],
101
113
  const uint8_t *pk);
102
114
  int pqcr_mldsa44_pk_from_sk(uint8_t *pk, const uint8_t *sk);
103
115
 
104
116
  int pqcr_mldsa65_keypair(uint8_t *pk, uint8_t *sk);
105
117
  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],
118
+ int pqcr_mldsa65_signature(uint8_t *sig, const uint8_t *m, size_t mlen, const uint8_t *ctx,
119
+ size_t ctxlen, const uint8_t *sk);
120
+ int pqcr_mldsa65_signature_internal(uint8_t *sig, const uint8_t *m, size_t mlen, const uint8_t *pre,
121
+ size_t prelen, const uint8_t rnd[MLDSA_RNDBYTES],
122
+ const uint8_t *sk, int externalmu);
123
+ int pqcr_mldsa65_verify(const uint8_t *sig, const uint8_t *m, size_t mlen, const uint8_t *ctx,
124
+ size_t ctxlen, const uint8_t *pk);
125
+ size_t
126
+ pqcr_mldsa65_prepare_domain_separation_prefix(uint8_t prefix[MLDSA_DOMAIN_SEPARATION_MAX_BYTES],
127
+ const uint8_t *ph, size_t phlen, const uint8_t *ctx,
128
+ size_t ctxlen, int hashalg);
129
+ int pqcr_mldsa65_signature_extmu(uint8_t *sig, const uint8_t mu[MLDSA_CRHBYTES], const uint8_t *sk);
130
+ int pqcr_mldsa65_verify_extmu(const uint8_t *sig, const uint8_t mu[MLDSA_CRHBYTES],
120
131
  const uint8_t *pk);
121
132
  int pqcr_mldsa65_pk_from_sk(uint8_t *pk, const uint8_t *sk);
122
133
 
123
134
  int pqcr_mldsa87_keypair(uint8_t *pk, uint8_t *sk);
124
135
  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],
136
+ int pqcr_mldsa87_signature(uint8_t *sig, const uint8_t *m, size_t mlen, const uint8_t *ctx,
137
+ size_t ctxlen, const uint8_t *sk);
138
+ int pqcr_mldsa87_signature_internal(uint8_t *sig, const uint8_t *m, size_t mlen, const uint8_t *pre,
139
+ size_t prelen, const uint8_t rnd[MLDSA_RNDBYTES],
140
+ const uint8_t *sk, int externalmu);
141
+ int pqcr_mldsa87_verify(const uint8_t *sig, const uint8_t *m, size_t mlen, const uint8_t *ctx,
142
+ size_t ctxlen, const uint8_t *pk);
143
+ size_t
144
+ pqcr_mldsa87_prepare_domain_separation_prefix(uint8_t prefix[MLDSA_DOMAIN_SEPARATION_MAX_BYTES],
145
+ const uint8_t *ph, size_t phlen, const uint8_t *ctx,
146
+ size_t ctxlen, int hashalg);
147
+ int pqcr_mldsa87_signature_extmu(uint8_t *sig, const uint8_t mu[MLDSA_CRHBYTES], const uint8_t *sk);
148
+ int pqcr_mldsa87_verify_extmu(const uint8_t *sig, const uint8_t mu[MLDSA_CRHBYTES],
139
149
  const uint8_t *pk);
140
150
  int pqcr_mldsa87_pk_from_sk(uint8_t *pk, const uint8_t *sk);
141
151
 
@@ -173,11 +173,11 @@ typedef struct {
173
173
 
174
174
  static const pq_mldsa_profile_t MLDSA_PROFILES[] = {
175
175
  {"ml_dsa_44", MLDSA44_PUBLICKEYBYTES, MLDSA44_SECRETKEYBYTES, MLDSA44_BYTES,
176
- pqcr_mldsa44_pk_from_sk, pqcr_mldsa44_signature_extmu, pqcr_mldsa44_verify_extmu},
176
+ pqcr_mldsa44_pk_from_sk, pq_mldsa44_signature_extmu_compat, pq_mldsa44_verify_extmu_compat},
177
177
  {"ml_dsa_65", MLDSA65_PUBLICKEYBYTES, MLDSA65_SECRETKEYBYTES, MLDSA65_BYTES,
178
- pqcr_mldsa65_pk_from_sk, pqcr_mldsa65_signature_extmu, pqcr_mldsa65_verify_extmu},
178
+ pqcr_mldsa65_pk_from_sk, pq_mldsa65_signature_extmu_compat, pq_mldsa65_verify_extmu_compat},
179
179
  {"ml_dsa_87", MLDSA87_PUBLICKEYBYTES, MLDSA87_SECRETKEYBYTES, MLDSA87_BYTES,
180
- pqcr_mldsa87_pk_from_sk, pqcr_mldsa87_signature_extmu, pqcr_mldsa87_verify_extmu},
180
+ pqcr_mldsa87_pk_from_sk, pq_mldsa87_signature_extmu_compat, pq_mldsa87_verify_extmu_compat},
181
181
  };
182
182
 
183
183
  static const pq_mldsa_profile_t *pq_mldsa_profile_from_value(VALUE algorithm) {
@@ -1306,7 +1306,8 @@ PQ_DEFINE_RUBY_MLDSA_SIGN(ml_dsa_87_sign, mldsa_87_sign, MLDSA87_SECRETKEYBYTES,
1306
1306
  #undef PQ_DEFINE_RUBY_MLDSA_SIGN
1307
1307
 
1308
1308
  static VALUE pq_run_verify(void *(*nogvl)(void *), VALUE message, VALUE signature, VALUE public_key,
1309
- VALUE context, size_t public_key_len_expected) {
1309
+ VALUE context, size_t public_key_len_expected,
1310
+ size_t signature_len_expected) {
1310
1311
  StringValue(signature);
1311
1312
  pq_validate_bytes_argument(public_key, public_key_len_expected, "public key");
1312
1313
  StringValue(context);
@@ -1314,6 +1315,10 @@ static VALUE pq_run_verify(void *(*nogvl)(void *), VALUE message, VALUE signatur
1314
1315
  rb_raise(rb_eArgError, "ML-DSA context length must be <= 255 bytes");
1315
1316
  }
1316
1317
 
1318
+ if ((size_t)RSTRING_LEN(signature) != signature_len_expected) {
1319
+ return Qfalse;
1320
+ }
1321
+
1317
1322
  verify_call_t call = {0};
1318
1323
  size_t public_key_len = 0;
1319
1324
  size_t signature_len = 0;
@@ -1341,7 +1346,7 @@ static VALUE pq_run_verify(void *(*nogvl)(void *), VALUE message, VALUE signatur
1341
1346
  pq_raise_general_error(call.result);
1342
1347
  }
1343
1348
 
1344
- #define PQ_DEFINE_RUBY_MLDSA_VERIFY(rb_name, nogvl_stem, pk_bytes) \
1349
+ #define PQ_DEFINE_RUBY_MLDSA_VERIFY(rb_name, nogvl_stem, pk_bytes, sig_bytes) \
1345
1350
  static VALUE pqcrypto_##rb_name(int argc, VALUE *argv, VALUE self) { \
1346
1351
  (void)self; \
1347
1352
  VALUE message, signature, public_key, context; \
@@ -1350,12 +1355,14 @@ static VALUE pq_run_verify(void *(*nogvl)(void *), VALUE message, VALUE signatur
1350
1355
  context = rb_str_new("", 0); \
1351
1356
  } \
1352
1357
  return pq_run_verify(pq_##nogvl_stem##_nogvl, message, signature, public_key, context, \
1353
- pk_bytes); \
1358
+ pk_bytes, sig_bytes); \
1354
1359
  }
1355
1360
 
1356
- PQ_DEFINE_RUBY_MLDSA_VERIFY(verify, verify, PQ_MLDSA_PUBLICKEYBYTES)
1357
- PQ_DEFINE_RUBY_MLDSA_VERIFY(ml_dsa_44_verify, mldsa_44_verify, MLDSA44_PUBLICKEYBYTES)
1358
- PQ_DEFINE_RUBY_MLDSA_VERIFY(ml_dsa_87_verify, mldsa_87_verify, MLDSA87_PUBLICKEYBYTES)
1361
+ PQ_DEFINE_RUBY_MLDSA_VERIFY(verify, verify, PQ_MLDSA_PUBLICKEYBYTES, PQ_MLDSA_BYTES)
1362
+ PQ_DEFINE_RUBY_MLDSA_VERIFY(ml_dsa_44_verify, mldsa_44_verify, MLDSA44_PUBLICKEYBYTES,
1363
+ MLDSA44_BYTES)
1364
+ PQ_DEFINE_RUBY_MLDSA_VERIFY(ml_dsa_87_verify, mldsa_87_verify, MLDSA87_PUBLICKEYBYTES,
1365
+ MLDSA87_BYTES)
1359
1366
 
1360
1367
  #undef PQ_DEFINE_RUBY_MLDSA_VERIFY
1361
1368
 
@@ -322,27 +322,29 @@ PQ_DEFINE_MLDSA_SIGN_KEYPAIR(mldsa87_sign, pqcr_mldsa87)
322
322
 
323
323
  #undef PQ_DEFINE_MLDSA_SIGN_KEYPAIR
324
324
 
325
- #define PQ_DEFINE_MLDSA_SIGN(name, native) \
326
- int pq_##name(uint8_t *signature, size_t *signature_len, const uint8_t *message, \
327
- size_t message_len, const uint8_t *ctx, size_t ctx_len, \
328
- const uint8_t *secret_key) { \
329
- if (!signature || !signature_len || !secret_key || (message_len > 0 && !message) || \
330
- (ctx_len > 0 && !ctx) || ctx_len > 255) { \
331
- return PQ_ERROR_BUFFER; \
332
- } \
333
- return native##_signature(signature, signature_len, message, message_len, ctx, ctx_len, \
334
- secret_key) == 0 \
335
- ? PQ_SUCCESS \
336
- : PQ_ERROR_SIGN; \
337
- }
338
-
339
- PQ_DEFINE_MLDSA_SIGN(sign, pqcr_mldsa65)
340
- PQ_DEFINE_MLDSA_SIGN(mldsa44_sign, pqcr_mldsa44)
341
- PQ_DEFINE_MLDSA_SIGN(mldsa87_sign, pqcr_mldsa87)
325
+ #define PQ_DEFINE_MLDSA_SIGN(name, native, sig_bytes) \
326
+ int pq_##name(uint8_t *signature, size_t *signature_len, const uint8_t *message, \
327
+ size_t message_len, const uint8_t *ctx, size_t ctx_len, \
328
+ const uint8_t *secret_key) { \
329
+ if (!signature || !signature_len || !secret_key || (message_len > 0 && !message) || \
330
+ (ctx_len > 0 && !ctx) || ctx_len > 255) { \
331
+ return PQ_ERROR_BUFFER; \
332
+ } \
333
+ if (native##_signature(signature, message, message_len, ctx, ctx_len, secret_key) != 0) { \
334
+ *signature_len = 0; \
335
+ return PQ_ERROR_SIGN; \
336
+ } \
337
+ *signature_len = (size_t)(sig_bytes); \
338
+ return PQ_SUCCESS; \
339
+ }
340
+
341
+ PQ_DEFINE_MLDSA_SIGN(sign, pqcr_mldsa65, MLDSA65_BYTES)
342
+ PQ_DEFINE_MLDSA_SIGN(mldsa44_sign, pqcr_mldsa44, MLDSA44_BYTES)
343
+ PQ_DEFINE_MLDSA_SIGN(mldsa87_sign, pqcr_mldsa87, MLDSA87_BYTES)
342
344
 
343
345
  #undef PQ_DEFINE_MLDSA_SIGN
344
346
 
345
- #define PQ_DEFINE_MLDSA_VERIFY(name, native) \
347
+ #define PQ_DEFINE_MLDSA_VERIFY(name, native, sig_bytes) \
346
348
  int pq_##name(const uint8_t *signature, size_t signature_len, const uint8_t *message, \
347
349
  size_t message_len, const uint8_t *ctx, size_t ctx_len, \
348
350
  const uint8_t *public_key) { \
@@ -350,29 +352,32 @@ PQ_DEFINE_MLDSA_SIGN(mldsa87_sign, pqcr_mldsa87)
350
352
  ctx_len > 255) { \
351
353
  return PQ_ERROR_BUFFER; \
352
354
  } \
353
- return native##_verify(signature, signature_len, message, message_len, ctx, ctx_len, \
354
- public_key) == 0 \
355
+ if (signature_len != (size_t)(sig_bytes)) { \
356
+ return PQ_ERROR_VERIFY; \
357
+ } \
358
+ return native##_verify(signature, message, message_len, ctx, ctx_len, public_key) == 0 \
355
359
  ? PQ_SUCCESS \
356
360
  : PQ_ERROR_VERIFY; \
357
361
  }
358
362
 
359
- PQ_DEFINE_MLDSA_VERIFY(verify, pqcr_mldsa65)
360
- PQ_DEFINE_MLDSA_VERIFY(mldsa44_verify, pqcr_mldsa44)
361
- PQ_DEFINE_MLDSA_VERIFY(mldsa87_verify, pqcr_mldsa87)
363
+ PQ_DEFINE_MLDSA_VERIFY(verify, pqcr_mldsa65, MLDSA65_BYTES)
364
+ PQ_DEFINE_MLDSA_VERIFY(mldsa44_verify, pqcr_mldsa44, MLDSA44_BYTES)
365
+ PQ_DEFINE_MLDSA_VERIFY(mldsa87_verify, pqcr_mldsa87, MLDSA87_BYTES)
362
366
 
363
367
  #undef PQ_DEFINE_MLDSA_VERIFY
364
368
 
365
369
  static int pq_testing_mldsa_sign_from_seed_with(
366
370
  uint8_t *signature, size_t *signature_len, const uint8_t *message, size_t message_len,
367
- const uint8_t *secret_key, const uint8_t *seed, size_t seed_len,
368
- int (*signature_internal)(uint8_t *, size_t *, const uint8_t *, size_t, const uint8_t *, size_t,
371
+ const uint8_t *secret_key, const uint8_t *seed, size_t seed_len, size_t expected_signature_len,
372
+ int (*signature_internal)(uint8_t *, const uint8_t *, size_t, const uint8_t *, size_t,
369
373
  const uint8_t *, const uint8_t *, int),
370
374
  size_t (*prepare_prefix)(uint8_t *, const uint8_t *, size_t, const uint8_t *, size_t, int)) {
371
375
  uint8_t pre[MLDSA_DOMAIN_SEPARATION_MAX_BYTES];
372
376
  size_t pre_len;
373
377
 
374
378
  if (!signature || !signature_len || !secret_key || !seed || seed_len != MLDSA_RNDBYTES ||
375
- !signature_internal || !prepare_prefix || (message_len > 0 && !message)) {
379
+ !signature_internal || !prepare_prefix || expected_signature_len == 0 ||
380
+ (message_len > 0 && !message)) {
376
381
  return PQ_ERROR_BUFFER;
377
382
  }
378
383
 
@@ -385,13 +390,18 @@ static int pq_testing_mldsa_sign_from_seed_with(
385
390
  */
386
391
  pre_len = prepare_prefix(pre, NULL, 0, NULL, 0, MLDSA_PREHASH_NONE);
387
392
  if (pre_len == 0) {
393
+ *signature_len = 0;
388
394
  return PQ_ERROR_SIGN;
389
395
  }
390
396
 
391
- return signature_internal(signature, signature_len, message, message_len, pre, pre_len, seed,
392
- secret_key, 0) == 0
393
- ? PQ_SUCCESS
394
- : PQ_ERROR_SIGN;
397
+ if (signature_internal(signature, message, message_len, pre, pre_len, seed, secret_key, 0) !=
398
+ 0) {
399
+ *signature_len = 0;
400
+ return PQ_ERROR_SIGN;
401
+ }
402
+
403
+ *signature_len = expected_signature_len;
404
+ return PQ_SUCCESS;
395
405
  }
396
406
 
397
407
  int pq_mldsa44_keypair_from_seed(uint8_t *public_key, uint8_t *secret_key, const uint8_t *seed32) {
@@ -447,7 +457,7 @@ int pq_testing_mldsa_sign_from_seed(uint8_t *signature, size_t *signature_len,
447
457
  const uint8_t *secret_key, const uint8_t *seed,
448
458
  size_t seed_len) {
449
459
  return pq_testing_mldsa_sign_from_seed_with(
450
- signature, signature_len, message, message_len, secret_key, seed, seed_len,
460
+ signature, signature_len, message, message_len, secret_key, seed, seed_len, MLDSA65_BYTES,
451
461
  pqcr_mldsa65_signature_internal, pqcr_mldsa65_prepare_domain_separation_prefix);
452
462
  }
453
463
 
@@ -456,7 +466,7 @@ int pq_testing_mldsa44_sign_from_seed(uint8_t *signature, size_t *signature_len,
456
466
  const uint8_t *secret_key, const uint8_t *seed,
457
467
  size_t seed_len) {
458
468
  return pq_testing_mldsa_sign_from_seed_with(
459
- signature, signature_len, message, message_len, secret_key, seed, seed_len,
469
+ signature, signature_len, message, message_len, secret_key, seed, seed_len, MLDSA44_BYTES,
460
470
  pqcr_mldsa44_signature_internal, pqcr_mldsa44_prepare_domain_separation_prefix);
461
471
  }
462
472
 
@@ -465,7 +475,7 @@ int pq_testing_mldsa87_sign_from_seed(uint8_t *signature, size_t *signature_len,
465
475
  const uint8_t *secret_key, const uint8_t *seed,
466
476
  size_t seed_len) {
467
477
  return pq_testing_mldsa_sign_from_seed_with(
468
- signature, signature_len, message, message_len, secret_key, seed, seed_len,
478
+ signature, signature_len, message, message_len, secret_key, seed, seed_len, MLDSA87_BYTES,
469
479
  pqcr_mldsa87_signature_internal, pqcr_mldsa87_prepare_domain_separation_prefix);
470
480
  }
471
481