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
@@ -17,6 +17,11 @@
17
17
  * National Institute of Standards and Technology
18
18
  * https://csrc.nist.gov/pubs/fips/204/final
19
19
  *
20
+ * - [FIPS204_UPDATES]
21
+ * FIPS 204 Potential Updates (Errata)
22
+ * National Institute of Standards and Technology
23
+ * https://csrc.nist.gov/files/pubs/fips/204/final/docs/fips-204-potential-updates.xlsx
24
+ *
20
25
  * - [Round3_Spec]
21
26
  * CRYSTALS-Dilithium Algorithm Specifications and Supporting Documentation
22
27
  * (Version 3.1)
@@ -60,7 +65,10 @@ static int mld_check_pct(uint8_t const pk[MLDSA_CRYPTO_PUBLICKEYBYTES],
60
65
  __contract__(
61
66
  requires(memory_no_alias(pk, MLDSA_CRYPTO_PUBLICKEYBYTES))
62
67
  requires(memory_no_alias(sk, MLDSA_CRYPTO_SECRETKEYBYTES))
63
- ensures(return_value == 0 || MLD_ANY_ERROR(return_value))
68
+ ensures(return_value == 0 || return_value == MLD_ERR_OUT_OF_MEMORY ||
69
+ return_value == MLD_ERR_RNG_FAIL ||
70
+ return_value == MLD_ERR_SIGNING_PAUSED ||
71
+ return_value == MLD_ERR_PCT_FAIL)
64
72
  );
65
73
 
66
74
  #if defined(MLD_CONFIG_KEYGEN_PCT)
@@ -83,14 +91,19 @@ __contract__(
83
91
  * MLD_CONFIG_CONTEXT_PARAMETER is defined; type set by
84
92
  * MLD_CONFIG_CONTEXT_PARAMETER_TYPE.
85
93
  *
86
- * @return 0 if the signature was successfully verified, non-zero otherwise.
94
+ * @retval 0 Success.
95
+ * @retval MLD_ERR_OUT_OF_MEMORY MLD_CONFIG_CUSTOM_ALLOC_FREE was used and an
96
+ * allocation via MLD_CUSTOM_ALLOC returned NULL.
97
+ * @retval MLD_ERR_RNG_FAIL Random number generation failed.
98
+ * @retval MLD_ERR_SIGNING_PAUSED The PCT's signing step was paused by a
99
+ * MLD_CONFIG_SIGN_HOOK_ATTEMPT hook.
100
+ * @retval MLD_ERR_PCT_FAIL The consistency check failed.
87
101
  */
88
102
  static int mld_check_pct(uint8_t const pk[MLDSA_CRYPTO_PUBLICKEYBYTES],
89
103
  uint8_t const sk[MLDSA_CRYPTO_SECRETKEYBYTES],
90
104
  MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
91
105
  {
92
106
  MLD_ALIGN uint8_t message[1] = {0};
93
- size_t siglen;
94
107
  int ret;
95
108
  MLD_ALLOC(signature, uint8_t, MLDSA_CRYPTO_BYTES, context);
96
109
  MLD_ALLOC(pk_test, uint8_t, MLDSA_CRYPTO_PUBLICKEYBYTES, context);
@@ -105,8 +118,8 @@ static int mld_check_pct(uint8_t const pk[MLDSA_CRYPTO_PUBLICKEYBYTES],
105
118
  mld_memcpy(pk_test, pk, MLDSA_CRYPTO_PUBLICKEYBYTES);
106
119
 
107
120
  /* Sign a test message using the original secret key */
108
- ret = mld_sign_signature(signature, &siglen, message, sizeof(message), NULL,
109
- 0, sk, context);
121
+ ret = mld_sign_signature(signature, message, sizeof(message), NULL, 0, sk,
122
+ context);
110
123
  if (ret != 0)
111
124
  {
112
125
  goto cleanup;
@@ -120,18 +133,33 @@ static int mld_check_pct(uint8_t const pk[MLDSA_CRYPTO_PUBLICKEYBYTES],
120
133
  }
121
134
  #endif /* MLD_CONFIG_KEYGEN_PCT_BREAKAGE_TEST */
122
135
 
123
- /* Verify the signature using the (potentially corrupted) public key */
124
- ret = mld_sign_verify(signature, siglen, message, sizeof(message), NULL, 0,
125
- pk_test, context);
136
+ /* Verify the signature using the (potentially corrupted) public key. */
137
+ ret = mld_sign_verify(signature, message, sizeof(message), NULL, 0, pk_test,
138
+ context);
139
+ if (ret != 0)
140
+ {
141
+ goto cleanup;
142
+ }
126
143
 
127
144
  cleanup:
128
145
  /* @[FIPS204, Section 3.6.3] Destruction of intermediate values. */
129
146
  MLD_FREE(pk_test, uint8_t, MLDSA_CRYPTO_PUBLICKEYBYTES, context);
130
147
  MLD_FREE(signature, uint8_t, MLDSA_CRYPTO_BYTES, context);
131
148
 
149
+ /* A failed signing operation or an invalid signature hint at a faulty
150
+ * implementation and map to a dedicated error code for PCT failure. */
151
+ if (ret == MLD_ERR_INVALID_SIGNATURE ||
152
+ ret == MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED)
153
+ {
154
+ ret = MLD_ERR_PCT_FAIL;
155
+ }
156
+
157
+ /* Other error codes, e.g. platform failures like out of memory or
158
+ * randomness failure, are passed on unmodified. */
159
+
132
160
  return ret;
133
161
  }
134
- #else /* MLD_CONFIG_KEYGEN_PCT */
162
+ #else /* MLD_CONFIG_KEYGEN_PCT */
135
163
  static int mld_check_pct(uint8_t const pk[MLDSA_CRYPTO_PUBLICKEYBYTES],
136
164
  uint8_t const sk[MLDSA_CRYPTO_SECRETKEYBYTES],
137
165
  MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
@@ -139,13 +167,21 @@ static int mld_check_pct(uint8_t const pk[MLDSA_CRYPTO_PUBLICKEYBYTES],
139
167
  /* Skip PCT */
140
168
  ((void)pk);
141
169
  ((void)sk);
142
- #if defined(MLD_CONFIG_CONTEXT_PARAMETER)
143
- ((void)context);
144
- #endif
170
+ MLD_CONTEXT_UNUSED(context);
145
171
  return 0;
146
172
  }
147
173
  #endif /* !MLD_CONFIG_KEYGEN_PCT */
148
174
 
175
+ /**
176
+ * Sample the short secret vectors s1 (length MLDSA_L) and s2 (length MLDSA_K)
177
+ * with coefficients in [-MLDSA_ETA, MLDSA_ETA] from the seed.
178
+ *
179
+ * @spec{Implements @[FIPS204, Algorithm 33, ExpandS].}
180
+ *
181
+ * @param[out] s1 Output vector s1.
182
+ * @param[out] s2 Output vector s2.
183
+ * @param[in] seed Byte array with seed of length MLDSA_CRHBYTES.
184
+ */
149
185
  static void mld_sample_s1_s2(mld_polyvecl *s1, mld_polyveck *s2,
150
186
  const uint8_t seed[MLDSA_CRHBYTES])
151
187
  __contract__(
@@ -204,6 +240,9 @@ __contract__(
204
240
  * t1[k] via power2round, and bit-pack t1[k] into pk_t1 and t0[k] into the
205
241
  * t0_packed buffer. Used by both keygen and pk_from_sk.
206
242
  *
243
+ * @spec{Partially implements @[FIPS204, Algorithm 22, pkEncode] (t1) and
244
+ * @[FIPS204, Algorithm 24, skEncode] (t0).}
245
+ *
207
246
  * @param[out] pk_t1 Output buffer for packed t1 (size
208
247
  * MLDSA_K * MLDSA_POLYT1_PACKEDBYTES; i.e. the t1
209
248
  * region of pk).
@@ -353,14 +392,11 @@ int mld_sign_keypair_internal(uint8_t pk[MLDSA_CRYPTO_PUBLICKEYBYTES],
353
392
  mld_polyvecl_ntt(s1);
354
393
 
355
394
  /* Pack rho into pk */
356
- mld_memcpy(pk, rho, MLDSA_SEEDBYTES);
395
+ mld_memcpy(pk + MLDSA_PK_RHO_OFFSET, rho, MLDSA_SEEDBYTES);
357
396
 
358
397
  /* Compute t = A*s1hat + s2 row by row, decompose into t1/t0, and pack
359
398
  * t1 into pk and t0 directly into the t0 region of sk. */
360
- ret = mld_compute_pack_t0_t1(pk + MLDSA_SEEDBYTES,
361
- sk + 2 * MLDSA_SEEDBYTES + MLDSA_TRBYTES +
362
- MLDSA_L * MLDSA_POLYETA_PACKEDBYTES +
363
- MLDSA_K * MLDSA_POLYETA_PACKEDBYTES,
399
+ ret = mld_compute_pack_t0_t1(pk + MLDSA_PK_T1_OFFSET, sk + MLDSA_SK_T0_OFFSET,
364
400
  s1, s2, rho, context);
365
401
  if (ret != 0)
366
402
  {
@@ -384,14 +420,21 @@ cleanup:
384
420
  MLD_FREE(inbuf, uint8_t, MLDSA_SEEDBYTES + 2, context);
385
421
  MLD_FREE(seedbuf, uint8_t, 2 * MLDSA_SEEDBYTES + MLDSA_CRHBYTES, context);
386
422
 
423
+ /* Pairwise Consistency Test (PCT) @[FIPS140_3_IG, p.87] */
424
+ /* Do this after freeing all temporaries. */
425
+ if (ret == 0)
426
+ {
427
+ ret = mld_check_pct(pk, sk, context);
428
+ }
429
+
387
430
  if (ret != 0)
388
431
  {
389
- return ret;
432
+ /* Clear caller outputs on failure. */
433
+ mld_zeroize(pk, MLDSA_CRYPTO_PUBLICKEYBYTES);
434
+ mld_zeroize(sk, MLDSA_CRYPTO_SECRETKEYBYTES);
390
435
  }
391
436
 
392
- /* Pairwise Consistency Test (PCT) @[FIPS140_3_IG, p.87] */
393
- /* Do this after freeing all temporaries. */
394
- return mld_check_pct(pk, sk, context);
437
+ return ret;
395
438
  }
396
439
 
397
440
  #if !defined(MLD_CONFIG_CORE_API_ONLY)
@@ -480,13 +523,8 @@ __contract__(
480
523
  #endif /* !MLD_CONFIG_NO_SIGN_API || !MLD_CONFIG_NO_VERIFY_API */
481
524
 
482
525
  #if !defined(MLD_CONFIG_NO_SIGN_API)
483
- /* Reference: The reference implementation does not explicitly check the
484
- * maximum nonce value, but instead loops indefinitely (even when the nonce
485
- * would overflow). Internally, sampling of y uses
486
- * (nonce*L), (nonce*L+1), ..., (nonce*L + L - 1).
487
- * Hence, there are no overflows if nonce < (UINT16_MAX - L)/L.
488
- * Explicitly checking for this explicitly allows us to prove type-safety. */
489
- #define MLD_NONCE_UB ((UINT16_MAX - MLDSA_L) / MLDSA_L)
526
+ /* MLD_MAX_KAPPA (see params.h) bounds the rejection-sampling counter kappa;
527
+ * MLD_MAX_SIGNING_ATTEMPTS below turns that into a bound on attempts. */
490
528
 
491
529
  /**
492
530
  * Compute z = y + s1*c, check that z has coefficients smaller than
@@ -528,7 +566,7 @@ __contract__(
528
566
  MLD_IF_REDUCE_RAM(
529
567
  requires(memory_no_alias(s1hat->packed, MLDSA_L * MLDSA_POLYETA_PACKEDBYTES))
530
568
  requires(memory_no_alias(y->rhoprime, MLDSA_CRHBYTES))
531
- requires(y->nonce <= MLD_NONCE_UB)
569
+ requires(y->kappa <= MLD_MAX_KAPPA)
532
570
  )
533
571
  assigns(memory_slice(sig, MLDSA_CRYPTO_BYTES))
534
572
  assigns(memory_slice(z, sizeof(mld_poly)))
@@ -580,41 +618,54 @@ __contract__(
580
618
  return 0;
581
619
  }
582
620
 
583
- /* User-facing bound on signing attempts. See MLD_CONFIG_MAX_SIGNING_ATTEMPTS
584
- * in mldsa_native_config.h. Default is chosen so that failure probability
585
- * is < 2^{-256}, that is, signatures will practically always succeed. */
586
- #ifndef MLD_CONFIG_MAX_SIGNING_ATTEMPTS
587
- #define MLD_CONFIG_MAX_SIGNING_ATTEMPTS MLD_NONCE_UB
588
- #endif
621
+ /* Effective bound on signing attempts: the configured bound
622
+ * MLD_CONFIG_MAX_SIGNING_ATTEMPTS (see mldsa_native_config.h) if set, otherwise
623
+ * the hard type-safety bound MLD_MAX_KAPPA / MLDSA_L (see MLD_MAX_KAPPA in
624
+ * params.h). */
625
+ #if defined(MLD_CONFIG_MAX_SIGNING_ATTEMPTS)
589
626
 
590
627
  #if !defined(MLD_ALLOW_NONCOMPLIANT_SIGNING_BOUND) && \
591
- MLD_CONFIG_MAX_SIGNING_ATTEMPTS < 814
592
- #error Bad configuration: MLD_CONFIG_MAX_SIGNING_ATTEMPTS must be >= 814 for FIPS 204 compliance @[FIPS204, Appendix C]
628
+ MLD_CONFIG_MAX_SIGNING_ATTEMPTS < 821
629
+ #error Bad configuration: MLD_CONFIG_MAX_SIGNING_ATTEMPTS must be >= 821 for FIPS 204 compliance @[FIPS204, Appendix C] @[FIPS204_UPDATES]
593
630
  #endif
594
631
 
595
632
  #if MLD_CONFIG_MAX_SIGNING_ATTEMPTS < 1
596
633
  #error Bad configuration: MLD_CONFIG_MAX_SIGNING_ATTEMPTS must be >= 1
597
634
  #endif
598
635
 
599
- #if MLD_CONFIG_MAX_SIGNING_ATTEMPTS > MLD_NONCE_UB
636
+ #if MLD_CONFIG_MAX_SIGNING_ATTEMPTS > MLD_MAX_KAPPA / MLDSA_L
600
637
  #error Bad configuration: MLD_CONFIG_MAX_SIGNING_ATTEMPTS exceeds the maximum allowed value.
601
638
  #endif
602
639
 
640
+ #define MLD_MAX_SIGNING_ATTEMPTS MLD_CONFIG_MAX_SIGNING_ATTEMPTS
641
+ #else /* MLD_CONFIG_MAX_SIGNING_ATTEMPTS */
642
+ #define MLD_MAX_SIGNING_ATTEMPTS (MLD_MAX_KAPPA / MLDSA_L)
643
+ #endif /* !MLD_CONFIG_MAX_SIGNING_ATTEMPTS */
644
+
603
645
  MLD_MUST_CHECK_RETURN_VALUE
604
646
  static MLD_INLINE uint16_t mld_get_max_signing_attempts(void)
605
647
  __contract__(
606
648
  ensures(return_value >= 1)
607
- ensures(return_value <= MLD_NONCE_UB)
649
+ ensures(return_value <= MLD_MAX_KAPPA / MLDSA_L)
608
650
  )
609
651
  {
610
652
  /* cassert(0) ensures CBMC uses the contract rather than inlining the body,
611
653
  * keeping proofs agnostic of the configured value. */
612
654
  cassert(0);
613
- return MLD_CONFIG_MAX_SIGNING_ATTEMPTS;
655
+ return MLD_MAX_SIGNING_ATTEMPTS;
614
656
  }
615
657
 
616
658
  /**
617
- * Attempt to generate a single signature.
659
+ * Attempt to generate a single signature: one iteration of the
660
+ * ML-DSA.Sign_internal rejection-sampling loop.
661
+ *
662
+ * @spec{Implements one iteration of the rejection-sampling loop body of
663
+ * @[FIPS204, Algorithm 7, ML-DSA.Sign_internal] (lines 11-30) plus, on success,
664
+ * the sigEncode step (line 33). The per-signature setup (Algorithm 7 lines 1-7:
665
+ * skDecode, NTT of s1/s2/t0, ExpandA, and computation of mu and rhoprime) and
666
+ * the loop itself (lines 8-10, 31-32) live in the caller
667
+ * mld_sign_signature_internal; kappa is this iteration's counter, used to
668
+ * sample y.}
618
669
  *
619
670
  * @reference{This code differs from the reference implementation in that it
620
671
  * factors out the core signature generation step into a distinct function
@@ -624,7 +675,7 @@ __contract__(
624
675
  * @param[in] mu Pointer to message or hash of exactly MLDSA_CRHBYTES
625
676
  * bytes.
626
677
  * @param[in] rhoprime Pointer to randomness seed.
627
- * @param nonce Current nonce value.
678
+ * @param kappa Counter for this iteration (= attempt*MLDSA_L).
628
679
  * @param[in] mat Expanded matrix.
629
680
  * @param[in] s1hat Secret vector s1 in NTT domain.
630
681
  * @param[in] s2hat Secret vector s2 in NTT domain.
@@ -639,9 +690,10 @@ __contract__(
639
690
  * an allocation via MLD_CUSTOM_ALLOC returned NULL.
640
691
  */
641
692
  MLD_MUST_CHECK_RETURN_VALUE
693
+ /* NOLINTNEXTLINE(readability-function-cognitive-complexity) */
642
694
  static int mld_attempt_signature_generation(
643
695
  uint8_t sig[MLDSA_CRYPTO_BYTES], const uint8_t *mu,
644
- const uint8_t rhoprime[MLDSA_CRHBYTES], uint16_t nonce, mld_polymat *mat,
696
+ const uint8_t rhoprime[MLDSA_CRHBYTES], uint16_t kappa, mld_polymat *mat,
645
697
  const mld_sk_s1hat *s1hat, const mld_sk_s2hat *s2hat,
646
698
  const mld_sk_t0hat *t0hat, MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
647
699
  __contract__(
@@ -652,7 +704,7 @@ __contract__(
652
704
  requires(memory_no_alias(s1hat, sizeof(mld_sk_s1hat)))
653
705
  requires(memory_no_alias(s2hat, sizeof(mld_sk_s2hat)))
654
706
  requires(memory_no_alias(t0hat, sizeof(mld_sk_t0hat)))
655
- requires(nonce <= MLD_NONCE_UB)
707
+ requires(kappa <= MLD_MAX_KAPPA)
656
708
  MLD_IF_NOT_REDUCE_RAM(
657
709
  requires(forall(k1, 0, MLDSA_K, forall(l1, 0, MLDSA_L,
658
710
  array_bound(mat->vec[k1].vec[l1].coeffs, 0, MLDSA_N, 0, MLDSA_Q))))
@@ -702,15 +754,24 @@ __contract__(
702
754
  w1 = &w1tmp->w1;
703
755
  tmp = &w1tmp->tmp;
704
756
 
705
- /* Sample/initialize intermediate vector y */
706
- mld_yvec_init(y, rhoprime, nonce);
757
+ /* @[FIPS204, Algorithm 7, line 11] y <- ExpandMask(rhoprime, kappa). */
758
+ mld_yvec_init(y, rhoprime, kappa);
707
759
 
708
- /* Matrix-vector multiplication, fused with y sampling in REDUCE_RAM mode */
760
+ /* @[FIPS204, Algorithm 7, line 12] w <- invNTT(A_hat o NTT(y)). This call
761
+ * performs the whole line: it NTTs y, accumulates the pointwise product with
762
+ * A_hat, and applies the inverse NTT. In REDUCE_RAM mode the y sampling is
763
+ * fused into the same pass. */
709
764
  mld_polyvec_matrix_pointwise_montgomery_yvec(w0, mat, y, tmp);
710
765
 
711
- /* Decompose w and call the random oracle */
766
+ /* @[FIPS204, Algorithm 7, line 13] w1 <- HighBits(w), here together with the
767
+ * low part: Decompose yields w = 2*GAMMA2*w1 + w0, keeping both w1 and w0
768
+ * (w0 is reused below in the line-21/26 alternative, see further down). */
712
769
  mld_polyveck_caddq(w0);
713
770
  mld_polyveck_decompose(w1, w0);
771
+
772
+ /* @[FIPS204, Algorithm 7, line 15] ctilde <- H(mu || w1Encode(w1), lambda/4).
773
+ * w1Encode(w1) is packed into the w1 region of sig (mld_polyveck_pack_w1),
774
+ * then absorbed by H together with mu. */
714
775
  mld_polyveck_pack_w1(sig, w1);
715
776
 
716
777
  mld_H(challenge_bytes, MLDSA_CTILDEBYTES, mu, MLDSA_CRHBYTES, sig,
@@ -720,18 +781,56 @@ __contract__(
720
781
  * This also applies to challenges for rejected signatures.
721
782
  * See Section 5.5 of @[Round3_Spec]. */
722
783
  MLD_CT_TESTING_DECLASSIFY(challenge_bytes, MLDSA_CTILDEBYTES);
784
+ /* @[FIPS204, Algorithm 7, line 16] c <- SampleInBall(ctilde) and
785
+ * @[FIPS204, Algorithm 7, line 17] c_hat <- NTT(c). */
723
786
  mld_poly_challenge(cp, challenge_bytes);
724
787
  mld_poly_ntt(cp);
725
788
 
726
- /* Compute z, reject if it reveals secret */
789
+ /* @[FIPS204, Algorithm 7, lines 18+20] cs1 <- invNTT(c_hat o s1_hat) and
790
+ * z <- y + cs1, followed by the line-23 norm check ||z||_inf >= GAMMA1 -
791
+ * BETA. mld_compute_pack_z fuses all three per polynomial and, on success,
792
+ * packs z into sig; it returns MLD_ERR_FAIL if the norm check rejects z. */
727
793
  ret = mld_compute_pack_z(sig, cp, s1hat, y, t, z);
728
794
  if (ret != 0)
729
795
  {
730
796
  goto cleanup;
731
797
  }
732
798
 
733
- /* Compute w0 - cs2 + ct0 per-component, checking norms incrementally.
734
- * This avoids allocating a full polyveck for h. */
799
+ /* The remaining steps realize @[FIPS204, Algorithm 7, lines 21-28] (the
800
+ * low-bits norm check and the hint h) via the faster alternative formulation
801
+ * of @[Round3_Spec, Section 5.1]. @[FIPS204] explicitly permits this: the
802
+ * note accompanying Algorithm 7 states that the validity checks on z and the
803
+ * computation of h may instead be implemented "as described in Section 5.1 of
804
+ * [6]", and that reference is @[Round3_Spec, Section 5.1].
805
+ *
806
+ * The loop below builds w0 - cs2 + ct0 in place in w0; w1 is unmodified, and
807
+ * is HighBits(w) from line 13. Those are the inputs to the streamlined
808
+ * computation of MakeHint explained below.
809
+ *
810
+ * Low-bits norm check:
811
+ * @[FIPS204, Algorithm 7, line 21] computes r0 = LowBits(w - cs2) and line
812
+ * 23 rejects when ||r0||_inf >= GAMMA2 - BETA. By @[Round3_Spec, Section
813
+ * 5.1] (Lemma 3), this line-23 check on r0 = LowBits(w - cs2) is implied by
814
+ * ||w0 - cs2||_inf < GAMMA2 - BETA, where w0 is the low part of w. In our
815
+ * context, w0 already holds the low part of w from the line-13 Decompose;
816
+ * after subtracting cs2 from it in place, the mld_poly_chknorm(w0, GAMMA2 -
817
+ * BETA) call below is exactly that check.
818
+ *
819
+ * Hint:
820
+ * @[FIPS204, Algorithm 7, line 26] sets h = MakeHint(-ct0, w - cs2 + ct0),
821
+ * and line 28 rejects when ||ct0||_inf >= GAMMA2 or h has more than OMEGA
822
+ * nonzero coefficients. @[Round3_Spec, Section 5.1] provides the following
823
+ * alternative description for MakeHint(-ct0, w - cs2 + ct0): a hint bit is
824
+ * zero exactly when the coefficient of w0 - cs2 + ct0 lies in
825
+ * (-GAMMA2, GAMMA2], or equals -GAMMA2 while the matching w1 coefficient is
826
+ * zero (the Decompose border case), and is set otherwise. This equivalence
827
+ * is precisely what mld_pack_sig_h -> mld_make_hint compute from w0
828
+ * (= w0 - cs2 + ct0) and w1. The line-28 ||ct0||_inf >= GAMMA2 check is the
829
+ * mld_poly_chknorm(z, GAMMA2) call on ct0 below; the weight bound is
830
+ * enforced by mld_pack_sig_h.
831
+ *
832
+ * Building w0 per-component and checking norms incrementally also avoids
833
+ * allocating a full polyveck for h. */
735
834
  for (k = 0; k < MLDSA_K; k++)
736
835
  __loop__(
737
836
  assigns(k,
@@ -743,7 +842,8 @@ __contract__(
743
842
  decreases(MLDSA_K - k)
744
843
  )
745
844
  {
746
- /* Compute cs2[k] and subtract from w0[k] */
845
+ /* @[FIPS204, Algorithm 7, line 19] cs2[k] <- invNTT(c_hat o s2_hat)[k],
846
+ * then subtract from w0[k] to form (w0 - cs2)[k]. */
747
847
  mld_sk_s2hat_get_poly(z, s2hat, k);
748
848
  mld_poly_pointwise_montgomery(z, cp);
749
849
  mld_poly_invntt_tomont(z);
@@ -751,8 +851,8 @@ __contract__(
751
851
  mld_poly_sub(&w0->vec[k], z);
752
852
  mld_poly_reduce(&w0->vec[k]);
753
853
 
754
- /* Check that subtracting cs2 does not change high bits of w and low bits
755
- * do not reveal secret information */
854
+ /* Low-bits norm check (see block comment above): the line-23 check on
855
+ * r0 = LowBits(w - cs2) holds via ||w0 - cs2||_inf < GAMMA2 - BETA. */
756
856
  w0_invalid = mld_poly_chknorm(&w0->vec[k], MLDSA_GAMMA2 - MLDSA_BETA);
757
857
  /* Constant time: w0_invalid may be leaked - see comment for z_invalid. */
758
858
  MLD_CT_TESTING_DECLASSIFY(&w0_invalid, sizeof(uint32_t));
@@ -762,12 +862,14 @@ __contract__(
762
862
  goto cleanup;
763
863
  }
764
864
 
765
- /* Compute ct0[k], check norm, and add to w0[k] */
865
+ /* @[FIPS204, Algorithm 7, line 25] ct0[k] <- invNTT(c_hat o t0_hat)[k]. */
766
866
  mld_sk_t0hat_get_poly(z, t0hat, k);
767
867
  mld_poly_pointwise_montgomery(z, cp);
768
868
  mld_poly_invntt_tomont(z);
769
869
  mld_poly_reduce(z);
770
870
 
871
+ /* @[FIPS204, Algorithm 7, line 28] reject when ||ct0||_inf >= GAMMA2 (the
872
+ * second part, the OMEGA weight bound, is enforced by mld_pack_sig_h). */
771
873
  h_invalid = mld_poly_chknorm(z, MLDSA_GAMMA2);
772
874
  /* Constant time: h_invalid may be leaked - see comment for z_invalid. */
773
875
  MLD_CT_TESTING_DECLASSIFY(&h_invalid, sizeof(uint32_t));
@@ -777,6 +879,8 @@ __contract__(
777
879
  goto cleanup;
778
880
  }
779
881
 
882
+ /* Add ct0[k] to (w0 - cs2)[k], leaving (w0 - cs2 + ct0)[k] in w0[k] -- the
883
+ * MakeHint input prepared for mld_pack_sig_h (see block comment above). */
780
884
  mld_poly_add(&w0->vec[k], z);
781
885
  }
782
886
 
@@ -793,9 +897,16 @@ __contract__(
793
897
  MLD_CT_TESTING_DECLASSIFY(w0, sizeof(*w0));
794
898
  MLD_CT_TESTING_DECLASSIFY(w1, sizeof(*w1));
795
899
 
796
- /* Pack challenge bytes and hints. */
900
+ /* @[FIPS204, Algorithm 7, line 33] sigEncode(ctilde, z mod+/- q, h) is split
901
+ * across three calls: z was already packed by mld_compute_pack_z, this call
902
+ * packs ctilde, and mld_pack_sig_h below packs the hint h. */
797
903
  mld_pack_sig_c(sig, challenge_bytes);
798
904
 
905
+ /* @[FIPS204, Algorithm 7, line 26] h <- MakeHint(-ct0, w - cs2 + ct0),
906
+ * computed from (w0 = w0 - cs2 + ct0, w1) as described in the block comment
907
+ * above, and packed as the h component of the line-33 sigEncode. Returns
908
+ * MLD_ERR_FAIL if h would exceed OMEGA nonzero coefficients (the remaining
909
+ * part of the line-28 check), in which case we reject. */
799
910
  ret = mld_pack_sig_h(sig, w0, w1);
800
911
  if (ret != 0)
801
912
  {
@@ -821,7 +932,7 @@ cleanup:
821
932
  }
822
933
  MLD_MUST_CHECK_RETURN_VALUE
823
934
  MLD_EXTERNAL_API
824
- int mld_sign_signature_internal(uint8_t sig[MLDSA_CRYPTO_BYTES], size_t *siglen,
935
+ int mld_sign_signature_internal(uint8_t sig[MLDSA_CRYPTO_BYTES],
825
936
  const uint8_t *m, size_t mlen,
826
937
  const uint8_t *pre, size_t prelen,
827
938
  const uint8_t rnd[MLDSA_RNDBYTES],
@@ -831,8 +942,8 @@ int mld_sign_signature_internal(uint8_t sig[MLDSA_CRYPTO_BYTES], size_t *siglen,
831
942
  {
832
943
  int ret;
833
944
  uint8_t *rho, *tr, *key, *mu, *rhoprime;
834
- uint16_t nonce = 0;
835
- const uint16_t nonce_limit = mld_get_max_signing_attempts();
945
+ uint16_t attempt;
946
+ const uint16_t max_signing_attempts = mld_get_max_signing_attempts();
836
947
  MLD_ALLOC(seedbuf, uint8_t,
837
948
  2 * MLDSA_SEEDBYTES + MLDSA_TRBYTES + 2 * MLDSA_CRHBYTES, context);
838
949
  MLD_ALLOC(mat, mld_polymat, 1, context);
@@ -847,46 +958,67 @@ int mld_sign_signature_internal(uint8_t sig[MLDSA_CRYPTO_BYTES], size_t *siglen,
847
958
  goto cleanup;
848
959
  }
849
960
 
961
+ /* If a resume hook is configured via MLD_CONFIG_SIGN_HOOK_RESUME, it provides
962
+ * the attempt to resume from after an earlier pause. Otherwise, we start at
963
+ * 0. Clamp to max_signing_attempts. */
964
+ attempt = mld_sign_resume(context);
965
+ if (attempt > max_signing_attempts)
966
+ {
967
+ attempt = max_signing_attempts;
968
+ }
969
+
850
970
  rho = seedbuf;
851
971
  tr = rho + MLDSA_SEEDBYTES;
852
972
  key = tr + MLDSA_TRBYTES;
853
973
  mu = key + MLDSA_SEEDBYTES;
854
974
  rhoprime = mu + MLDSA_CRHBYTES;
975
+ /* @[FIPS204, Algorithm 7, line 1] (rho, K, tr, s1, s2, t0) <- skDecode(sk)
976
+ * and @[FIPS204, Algorithm 7, lines 2-4] s1_hat/s2_hat/t0_hat <- NTT(...):
977
+ * mld_unpack_sk returns s1hat, s2hat, t0hat already in NTT domain. The spec's
978
+ * private random seed K is held in the local variable key. */
855
979
  mld_unpack_sk(rho, tr, key, t0hat, s1hat, s2hat, sk);
856
980
 
857
981
  if (!externalmu)
858
982
  {
859
- /* Compute mu = CRH(tr, pre, msg) */
983
+ /* @[FIPS204, Algorithm 7, line 6] mu <- H(BytesToBits(tr) || M', 64). */
860
984
  mld_H(mu, MLDSA_CRHBYTES, tr, MLDSA_TRBYTES, pre, prelen, m, mlen);
861
985
  }
862
986
  else
863
987
  {
864
- /* mu has been provided directly */
988
+ /* mu has been provided directly (external-mu variant; line 6 done by the
989
+ * caller in a separate cryptographic module). */
865
990
  mld_memcpy(mu, m, MLDSA_CRHBYTES);
866
991
  }
867
992
 
868
- /* Compute rhoprime = CRH(key, rnd, mu) */
993
+ /* @[FIPS204, Algorithm 7, line 7] rhoprime <- H(K || rnd || mu, 64). */
869
994
  mld_H(rhoprime, MLDSA_CRHBYTES, key, MLDSA_SEEDBYTES, rnd, MLDSA_RNDBYTES, mu,
870
995
  MLDSA_CRHBYTES);
871
996
 
872
997
  /* Constant time: rho is part of the public key and, hence, public. */
873
998
  MLD_CT_TESTING_DECLASSIFY(rho, MLDSA_SEEDBYTES);
874
- /* Expand matrix and transform vectors */
999
+ /* @[FIPS204, Algorithm 7, line 5] A_hat <- ExpandA(rho). */
875
1000
  mld_polyvec_matrix_expand(mat, rho);
876
1001
 
877
- /* Reference: This code is re-structured using a while(1), */
878
- /* with explicit "continue" statements (rather than "goto") */
879
- /* to implement rejection of invalid signatures. */
880
- while (1)
1002
+ /* @[FIPS204, Algorithm 7, lines 8-10 and 31-32] the rejection-sampling loop,
1003
+ * tracked by attempt (kappa = attempt*MLDSA_L). Each iteration's body (lines
1004
+ * 11-30) plus, on success, the line-33 sigEncode are performed by
1005
+ * mld_attempt_signature_generation. */
1006
+
1007
+ /* Reference: the reference loops unboundedly; we instead iterate over the
1008
+ * bounded range [0, max_signing_attempts) for predictable termination.
1009
+ * A success or fatal error exits via goto cleanup; running to completion
1010
+ * means every attempt was rejected; with a FIPS compliant choice of
1011
+ * MLD_CONFIG_MAX_SIGNING_ATTEMPTS, this should never happen. */
1012
+ for (; attempt < max_signing_attempts; attempt++)
881
1013
  __loop__(
882
1014
  MLD_IF_NOT_REDUCE_RAM(
883
- assigns(nonce, ret, object_whole(siglen), memory_slice(sig, MLDSA_CRYPTO_BYTES))
1015
+ assigns(attempt, ret, memory_slice(sig, MLDSA_CRYPTO_BYTES))
884
1016
  )
885
1017
  MLD_IF_REDUCE_RAM(
886
- assigns(nonce, ret, object_whole(siglen), memory_slice(sig, MLDSA_CRYPTO_BYTES),
1018
+ assigns(attempt, ret, memory_slice(sig, MLDSA_CRYPTO_BYTES),
887
1019
  memory_slice(mat, sizeof(mld_polymat)))
888
1020
  )
889
- invariant(nonce <= nonce_limit)
1021
+ invariant(attempt <= max_signing_attempts)
890
1022
 
891
1023
  /* t0, s1, s2, and mat are initialized above and are NOT changed by this */
892
1024
  /* loop. We can therefore re-assert their bounds here as part of the */
@@ -898,43 +1030,56 @@ int mld_sign_signature_internal(uint8_t sig[MLDSA_CRYPTO_BYTES], size_t *siglen,
898
1030
  invariant(forall(k3, 0, MLDSA_L, array_abs_bound(s1hat->vec.vec[k3].coeffs, 0, MLDSA_N, MLD_NTT_BOUND)))
899
1031
  invariant(forall(k4, 0, MLDSA_K, array_abs_bound(s2hat->vec.vec[k4].coeffs, 0, MLDSA_N, MLD_NTT_BOUND)))
900
1032
  )
901
- decreases(nonce_limit - nonce)
1033
+ decreases(max_signing_attempts - attempt)
902
1034
  )
903
1035
  {
904
- /* Reference: this code explicitly checks for exhaustion of signing */
905
- /* attempts to provide predictable termination and results in that */
906
- /* case. Checking here also means that incrementing nonce below can */
907
- /* be proven to be type-safe. */
908
- if (nonce == nonce_limit)
1036
+ /* Safety: attempt < max_signing_attempts <= MLD_MAX_KAPPA / MLDSA_L, so
1037
+ * kappa <= MLD_MAX_KAPPA and the cast is safe. */
1038
+ const uint16_t kappa = (uint16_t)(attempt * MLDSA_L);
1039
+
1040
+ /* Query configurable signing hook whether signing should be paused.
1041
+ * This is skipped by default and only used if the user sets the
1042
+ * configuration option MLD_CONFIG_SIGN_HOOK_ATTEMPT. */
1043
+ if (mld_sign_attempt(attempt, context) != 0)
909
1044
  {
910
- ret = MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED;
911
- break;
1045
+ ret = MLD_ERR_SIGNING_PAUSED;
1046
+ goto cleanup;
912
1047
  }
913
1048
 
914
- ret = mld_attempt_signature_generation(sig, mu, rhoprime, nonce, mat, s1hat,
1049
+ ret = mld_attempt_signature_generation(sig, mu, rhoprime, kappa, mat, s1hat,
915
1050
  s2hat, t0hat, context);
916
- nonce++;
1051
+
1052
+ /* Decide whether to keep trying based on the return value:
1053
+ * - ret == 0: a valid signature was produced; we are done.
1054
+ * - ret == MLD_ERR_FAIL: this candidate was rejected by one of the norm
1055
+ * or hint checks. We continue the loop and try again with the next
1056
+ * nonce.
1057
+ * - any other value (e.g. MLD_ERR_OUT_OF_MEMORY): an unrecoverable error
1058
+ * occurred, so we propagate it to the caller. */
917
1059
  if (ret == 0)
918
1060
  {
919
- *siglen = MLDSA_CRYPTO_BYTES;
920
- break;
1061
+ /* Signing succeeded: record the attempt that succeeded. No-op in the
1062
+ * default build. */
1063
+ mld_sign_finish(attempt, context);
1064
+ goto cleanup;
921
1065
  }
922
- else if (ret != MLD_ERR_FAIL)
1066
+ if (ret != MLD_ERR_FAIL)
923
1067
  {
924
- /* For failures such as out-of-memory, propagate and exit immediately. */
925
- break;
1068
+ goto cleanup;
926
1069
  }
927
-
928
- /* Otherwise, try again. */
929
1070
  }
930
1071
 
1072
+ /* Loop ran to completion: all attempts rejected, budget exhausted.
1073
+ * This should never happen with a FIPS compliant choice of
1074
+ * MLD_CONFIG_MAX_SIGNING_ATTEMPTS. */
1075
+ ret = MLD_ERR_SIGN_ATTEMPTS_EXHAUSTED;
1076
+
931
1077
  cleanup:
932
1078
 
933
1079
  if (ret != 0)
934
1080
  {
935
1081
  /* To be on the safe-side, we zeroize the signature buffer. */
936
- *siglen = 0;
937
- mld_memset(sig, 0, MLDSA_CRYPTO_BYTES);
1082
+ mld_zeroize(sig, MLDSA_CRYPTO_BYTES);
938
1083
  }
939
1084
 
940
1085
  /* @[FIPS204, Section 3.6.3] Destruction of intermediate values. */
@@ -951,9 +1096,8 @@ cleanup:
951
1096
  #if !defined(MLD_CONFIG_NO_RANDOMIZED_API)
952
1097
  MLD_MUST_CHECK_RETURN_VALUE
953
1098
  MLD_EXTERNAL_API
954
- int mld_sign_signature(uint8_t sig[MLDSA_CRYPTO_BYTES], size_t *siglen,
955
- const uint8_t *m, size_t mlen, const uint8_t *ctx,
956
- size_t ctxlen,
1099
+ int mld_sign_signature(uint8_t sig[MLDSA_CRYPTO_BYTES], const uint8_t *m,
1100
+ size_t mlen, const uint8_t *ctx, size_t ctxlen,
957
1101
  const uint8_t sk[MLDSA_CRYPTO_SECRETKEYBYTES],
958
1102
  MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
959
1103
  {
@@ -973,7 +1117,7 @@ int mld_sign_signature(uint8_t sig[MLDSA_CRYPTO_BYTES], size_t *siglen,
973
1117
  MLD_PREHASH_NONE);
974
1118
  if (pre_len == 0)
975
1119
  {
976
- ret = MLD_ERR_FAIL;
1120
+ ret = MLD_ERR_INVALID_ARG;
977
1121
  goto cleanup;
978
1122
  }
979
1123
 
@@ -986,20 +1130,19 @@ int mld_sign_signature(uint8_t sig[MLDSA_CRYPTO_BYTES], size_t *siglen,
986
1130
  }
987
1131
  MLD_CT_TESTING_SECRET(rnd, MLDSA_RNDBYTES);
988
1132
 
989
- ret = mld_sign_signature_internal(sig, siglen, m, mlen, pre, pre_len, rnd, sk,
990
- 0, context);
1133
+ ret = mld_sign_signature_internal(sig, m, mlen, pre, pre_len, rnd, sk, 0,
1134
+ context);
991
1135
 
992
1136
  cleanup:
993
1137
  if (ret != 0)
994
1138
  {
995
- /* To be on the safe-side, make sure *siglen and sig have a well-defined
996
- * value, even in the case of error.
1139
+ /* To be on the safe-side, make sure sig has a well-defined value, even in
1140
+ * the case of error.
997
1141
  *
998
- * If we come from mld_sign_signature_internal, both are redundant,
999
- * but the error case should not be the norm, and the added cost of the
1000
- * memset insignificant. */
1001
- *siglen = 0;
1002
- mld_memset(sig, 0, MLDSA_CRYPTO_BYTES);
1142
+ * If we come from mld_sign_signature_internal, this is redundant, but the
1143
+ * error case should not be the norm, and the added cost of the zeroization
1144
+ * insignificant. */
1145
+ mld_zeroize(sig, MLDSA_CRYPTO_BYTES);
1003
1146
  }
1004
1147
 
1005
1148
  /* @[FIPS204, Section 3.6.3] Destruction of intermediate values. */
@@ -1013,7 +1156,7 @@ cleanup:
1013
1156
  #if !defined(MLD_CONFIG_NO_RANDOMIZED_API)
1014
1157
  MLD_MUST_CHECK_RETURN_VALUE
1015
1158
  MLD_EXTERNAL_API
1016
- int mld_sign_signature_extmu(uint8_t sig[MLDSA_CRYPTO_BYTES], size_t *siglen,
1159
+ int mld_sign_signature_extmu(uint8_t sig[MLDSA_CRYPTO_BYTES],
1017
1160
  const uint8_t mu[MLDSA_CRHBYTES],
1018
1161
  const uint8_t sk[MLDSA_CRYPTO_SECRETKEYBYTES],
1019
1162
  MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
@@ -1023,7 +1166,6 @@ int mld_sign_signature_extmu(uint8_t sig[MLDSA_CRYPTO_BYTES], size_t *siglen,
1023
1166
 
1024
1167
  if (rnd == NULL)
1025
1168
  {
1026
- *siglen = 0;
1027
1169
  ret = MLD_ERR_OUT_OF_MEMORY;
1028
1170
  goto cleanup;
1029
1171
  }
@@ -1032,14 +1174,13 @@ int mld_sign_signature_extmu(uint8_t sig[MLDSA_CRYPTO_BYTES], size_t *siglen,
1032
1174
  * call mld_sign_signature_internal directly with all-zero rnd. */
1033
1175
  if (mld_randombytes(rnd, MLDSA_RNDBYTES) != 0)
1034
1176
  {
1035
- *siglen = 0;
1036
1177
  ret = MLD_ERR_RNG_FAIL;
1037
1178
  goto cleanup;
1038
1179
  }
1039
1180
  MLD_CT_TESTING_SECRET(rnd, MLDSA_RNDBYTES);
1040
1181
 
1041
- ret = mld_sign_signature_internal(sig, siglen, mu, MLDSA_CRHBYTES, NULL, 0,
1042
- rnd, sk, 1, context);
1182
+ ret = mld_sign_signature_internal(sig, mu, MLDSA_CRHBYTES, NULL, 0, rnd, sk,
1183
+ 1, context);
1043
1184
 
1044
1185
  cleanup:
1045
1186
  /* @[FIPS204, Section 3.6.3] Destruction of intermediate values. */
@@ -1048,43 +1189,13 @@ cleanup:
1048
1189
  return ret;
1049
1190
  }
1050
1191
  #endif /* !MLD_CONFIG_NO_RANDOMIZED_API */
1051
-
1052
- #if !defined(MLD_CONFIG_NO_RANDOMIZED_API)
1053
- MLD_MUST_CHECK_RETURN_VALUE
1054
- MLD_EXTERNAL_API
1055
- int mld_sign(uint8_t *sm, size_t *smlen, const uint8_t *m, size_t mlen,
1056
- const uint8_t *ctx, size_t ctxlen,
1057
- const uint8_t sk[MLDSA_CRYPTO_SECRETKEYBYTES],
1058
- MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
1059
- {
1060
- int ret;
1061
- size_t i;
1062
-
1063
- for (i = 0; i < mlen; ++i)
1064
- __loop__(
1065
- assigns(i, object_whole(sm))
1066
- invariant(i <= mlen)
1067
- decreases(mlen - i)
1068
- )
1069
- {
1070
- sm[MLDSA_CRYPTO_BYTES + mlen - 1 - i] = m[mlen - 1 - i];
1071
- }
1072
- ret = mld_sign_signature(sm, smlen, sm + MLDSA_CRYPTO_BYTES, mlen, ctx,
1073
- ctxlen, sk, context);
1074
- if (ret == 0)
1075
- {
1076
- *smlen += mlen;
1077
- }
1078
- return ret;
1079
- }
1080
- #endif /* !MLD_CONFIG_NO_RANDOMIZED_API */
1081
1192
  #endif /* !MLD_CONFIG_CORE_API_ONLY */
1082
1193
  #endif /* !MLD_CONFIG_NO_SIGN_API */
1083
1194
 
1084
1195
  #if !defined(MLD_CONFIG_NO_VERIFY_API)
1085
1196
  MLD_MUST_CHECK_RETURN_VALUE
1086
1197
  MLD_EXTERNAL_API
1087
- int mld_sign_verify_internal(const uint8_t *sig, size_t siglen,
1198
+ int mld_sign_verify_internal(const uint8_t sig[MLDSA_CRYPTO_BYTES],
1088
1199
  const uint8_t *m, size_t mlen, const uint8_t *pre,
1089
1200
  size_t prelen,
1090
1201
  const uint8_t pk[MLDSA_CRYPTO_PUBLICKEYBYTES],
@@ -1111,20 +1222,15 @@ int mld_sign_verify_internal(const uint8_t *sig, size_t siglen,
1111
1222
  goto cleanup;
1112
1223
  }
1113
1224
 
1114
- if (siglen != MLDSA_CRYPTO_BYTES)
1115
- {
1116
- ret = MLD_ERR_FAIL;
1117
- goto cleanup;
1118
- }
1119
-
1120
1225
  mld_memcpy(c, sig, MLDSA_CTILDEBYTES);
1121
- mld_polyvecl_unpack_z(z, sig + MLDSA_CTILDEBYTES);
1226
+ mld_polyvecl_unpack_z(z, sig + MLDSA_SIG_Z_OFFSET);
1122
1227
 
1123
1228
  /* mld_polyvecl_chknorm signals failure through a single non-zero error code
1124
- * that's not yet aligned with MLD_ERR_XXX. Map it to MLD_ERR_FAIL. */
1229
+ * that's not yet aligned with MLD_ERR_XXX. A norm-check failure here means
1230
+ * the signature is invalid, so map it to MLD_ERR_INVALID_SIGNATURE. */
1125
1231
  if (mld_polyvecl_chknorm(z, MLDSA_GAMMA1 - MLDSA_BETA))
1126
1232
  {
1127
- ret = MLD_ERR_FAIL;
1233
+ ret = MLD_ERR_INVALID_SIGNATURE;
1128
1234
  goto cleanup;
1129
1235
  }
1130
1236
 
@@ -1178,10 +1284,11 @@ int mld_sign_verify_internal(const uint8_t *sig, size_t siglen,
1178
1284
  mld_poly_invntt_tomont(w1);
1179
1285
  mld_poly_caddq(w1);
1180
1286
 
1181
- /* tmp = h_i (decoded and validated from signature) */
1182
- ret = mld_sig_unpack_hints(tmp, sig, i);
1183
- if (ret != 0)
1287
+ /* tmp = h_i (decoded and validated from signature). A non-zero return
1288
+ * means the hint encoding is malformed, i.e. the signature is invalid. */
1289
+ if (mld_sig_unpack_hints(tmp, sig, i) != 0)
1184
1290
  {
1291
+ ret = MLD_ERR_INVALID_SIGNATURE;
1185
1292
  goto cleanup;
1186
1293
  }
1187
1294
 
@@ -1199,7 +1306,7 @@ int mld_sign_verify_internal(const uint8_t *sig, size_t siglen,
1199
1306
  /* Declassify the result of the verification. */
1200
1307
  MLD_CT_TESTING_DECLASSIFY(&cmp, sizeof(cmp));
1201
1308
 
1202
- ret = cmp == 0 ? 0 : MLD_ERR_FAIL;
1309
+ ret = cmp == 0 ? 0 : MLD_ERR_INVALID_SIGNATURE;
1203
1310
 
1204
1311
  cleanup:
1205
1312
  /* @[FIPS204, Section 3.6.3] Destruction of intermediate values. */
@@ -1218,7 +1325,7 @@ cleanup:
1218
1325
  #if !defined(MLD_CONFIG_CORE_API_ONLY)
1219
1326
  MLD_MUST_CHECK_RETURN_VALUE
1220
1327
  MLD_EXTERNAL_API
1221
- int mld_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m,
1328
+ int mld_sign_verify(const uint8_t sig[MLDSA_CRYPTO_BYTES], const uint8_t *m,
1222
1329
  size_t mlen, const uint8_t *ctx, size_t ctxlen,
1223
1330
  const uint8_t pk[MLDSA_CRYPTO_PUBLICKEYBYTES],
1224
1331
  MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
@@ -1231,12 +1338,11 @@ int mld_sign_verify(const uint8_t *sig, size_t siglen, const uint8_t *m,
1231
1338
  MLD_PREHASH_NONE);
1232
1339
  if (pre_len == 0)
1233
1340
  {
1234
- ret = MLD_ERR_FAIL;
1341
+ ret = MLD_ERR_INVALID_ARG;
1235
1342
  goto cleanup;
1236
1343
  }
1237
1344
 
1238
- ret = mld_sign_verify_internal(sig, siglen, m, mlen, pre, pre_len, pk, 0,
1239
- context);
1345
+ ret = mld_sign_verify_internal(sig, m, mlen, pre, pre_len, pk, 0, context);
1240
1346
 
1241
1347
  cleanup:
1242
1348
  /* @[FIPS204, Section 3.6.3] Destruction of intermediate values. */
@@ -1247,58 +1353,13 @@ cleanup:
1247
1353
 
1248
1354
  MLD_MUST_CHECK_RETURN_VALUE
1249
1355
  MLD_EXTERNAL_API
1250
- int mld_sign_verify_extmu(const uint8_t *sig, size_t siglen,
1356
+ int mld_sign_verify_extmu(const uint8_t sig[MLDSA_CRYPTO_BYTES],
1251
1357
  const uint8_t mu[MLDSA_CRHBYTES],
1252
1358
  const uint8_t pk[MLDSA_CRYPTO_PUBLICKEYBYTES],
1253
1359
  MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
1254
1360
  {
1255
- return mld_sign_verify_internal(sig, siglen, mu, MLDSA_CRHBYTES, NULL, 0, pk,
1256
- 1, context);
1257
- }
1258
-
1259
- MLD_MUST_CHECK_RETURN_VALUE
1260
- MLD_EXTERNAL_API
1261
- int mld_sign_open(uint8_t *m, size_t *mlen, const uint8_t *sm, size_t smlen,
1262
- const uint8_t *ctx, size_t ctxlen,
1263
- const uint8_t pk[MLDSA_CRYPTO_PUBLICKEYBYTES],
1264
- MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
1265
- {
1266
- int ret;
1267
- size_t i;
1268
-
1269
- if (smlen < MLDSA_CRYPTO_BYTES)
1270
- {
1271
- ret = MLD_ERR_FAIL;
1272
- goto cleanup;
1273
- }
1274
-
1275
- *mlen = smlen - MLDSA_CRYPTO_BYTES;
1276
- ret = mld_sign_verify(sm, MLDSA_CRYPTO_BYTES, sm + MLDSA_CRYPTO_BYTES, *mlen,
1277
- ctx, ctxlen, pk, context);
1278
- if (ret == 0)
1279
- {
1280
- /* All good, copy msg, return 0 */
1281
- for (i = 0; i < *mlen; ++i)
1282
- __loop__(
1283
- assigns(i, memory_slice(m, *mlen))
1284
- invariant(i <= *mlen)
1285
- decreases(*mlen - i)
1286
- )
1287
- {
1288
- m[i] = sm[MLDSA_CRYPTO_BYTES + i];
1289
- }
1290
- }
1291
-
1292
- cleanup:
1293
-
1294
- if (ret != 0)
1295
- {
1296
- /* To be on the safe-side, we zeroize the message buffer. */
1297
- *mlen = 0;
1298
- mld_memset(m, 0, smlen);
1299
- }
1300
-
1301
- return ret;
1361
+ return mld_sign_verify_internal(sig, mu, MLDSA_CRHBYTES, NULL, 0, pk, 1,
1362
+ context);
1302
1363
  }
1303
1364
  #endif /* !MLD_CONFIG_CORE_API_ONLY */
1304
1365
  #endif /* !MLD_CONFIG_NO_VERIFY_API */
@@ -1308,9 +1369,8 @@ cleanup:
1308
1369
  MLD_MUST_CHECK_RETURN_VALUE
1309
1370
  MLD_EXTERNAL_API
1310
1371
  int mld_sign_signature_pre_hash_internal(
1311
- uint8_t sig[MLDSA_CRYPTO_BYTES], size_t *siglen, const uint8_t *ph,
1312
- size_t phlen, const uint8_t *ctx, size_t ctxlen,
1313
- const uint8_t rnd[MLDSA_RNDBYTES],
1372
+ uint8_t sig[MLDSA_CRYPTO_BYTES], const uint8_t *ph, size_t phlen,
1373
+ const uint8_t *ctx, size_t ctxlen, const uint8_t rnd[MLDSA_RNDBYTES],
1314
1374
  const uint8_t sk[MLDSA_CRYPTO_SECRETKEYBYTES], int hashalg,
1315
1375
  MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
1316
1376
  {
@@ -1318,27 +1378,32 @@ int mld_sign_signature_pre_hash_internal(
1318
1378
  size_t pre_len;
1319
1379
  int ret;
1320
1380
 
1381
+ if (hashalg == MLD_PREHASH_NONE)
1382
+ {
1383
+ ret = MLD_ERR_INVALID_ARG;
1384
+ goto cleanup;
1385
+ }
1386
+
1321
1387
  pre_len = mld_prepare_domain_separation_prefix(pre, ph, phlen, ctx, ctxlen,
1322
1388
  hashalg);
1323
1389
  if (pre_len == 0)
1324
1390
  {
1325
- ret = MLD_ERR_FAIL;
1391
+ ret = MLD_ERR_INVALID_ARG;
1326
1392
  goto cleanup;
1327
1393
  }
1328
1394
 
1329
- ret = mld_sign_signature_internal(sig, siglen, pre, pre_len, NULL, 0, rnd, sk,
1330
- 0, context);
1395
+ ret = mld_sign_signature_internal(sig, pre, pre_len, NULL, 0, rnd, sk, 0,
1396
+ context);
1331
1397
  cleanup:
1332
1398
  if (ret != 0)
1333
1399
  {
1334
- /* To be on the safe-side, make sure *siglen and sig have a well-defined
1335
- * value, even in the case of error.
1400
+ /* To be on the safe-side, make sure sig has a well-defined value, even in
1401
+ * the case of error.
1336
1402
  *
1337
- * If we come from mld_sign_signature_internal, both are redundant,
1338
- * but the error case should not be the norm, and the added cost of the
1339
- * memset insignificant. */
1340
- *siglen = 0;
1341
- mld_memset(sig, 0, MLDSA_CRYPTO_BYTES);
1403
+ * If we come from mld_sign_signature_internal, this is redundant, but the
1404
+ * error case should not be the norm, and the added cost of the zeroization
1405
+ * insignificant. */
1406
+ mld_zeroize(sig, MLDSA_CRYPTO_BYTES);
1342
1407
  }
1343
1408
 
1344
1409
  /* @[FIPS204, Section 3.6.3] Destruction of intermediate values. */
@@ -1351,7 +1416,7 @@ cleanup:
1351
1416
  MLD_MUST_CHECK_RETURN_VALUE
1352
1417
  MLD_EXTERNAL_API
1353
1418
  int mld_sign_verify_pre_hash_internal(
1354
- const uint8_t *sig, size_t siglen, const uint8_t *ph, size_t phlen,
1419
+ const uint8_t sig[MLDSA_CRYPTO_BYTES], const uint8_t *ph, size_t phlen,
1355
1420
  const uint8_t *ctx, size_t ctxlen,
1356
1421
  const uint8_t pk[MLDSA_CRYPTO_PUBLICKEYBYTES], int hashalg,
1357
1422
  MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
@@ -1360,16 +1425,21 @@ int mld_sign_verify_pre_hash_internal(
1360
1425
  size_t pre_len;
1361
1426
  int ret;
1362
1427
 
1428
+ if (hashalg == MLD_PREHASH_NONE)
1429
+ {
1430
+ ret = MLD_ERR_INVALID_ARG;
1431
+ goto cleanup;
1432
+ }
1433
+
1363
1434
  pre_len = mld_prepare_domain_separation_prefix(pre, ph, phlen, ctx, ctxlen,
1364
1435
  hashalg);
1365
1436
  if (pre_len == 0)
1366
1437
  {
1367
- ret = MLD_ERR_FAIL;
1438
+ ret = MLD_ERR_INVALID_ARG;
1368
1439
  goto cleanup;
1369
1440
  }
1370
1441
 
1371
- ret = mld_sign_verify_internal(sig, siglen, pre, pre_len, NULL, 0, pk, 0,
1372
- context);
1442
+ ret = mld_sign_verify_internal(sig, pre, pre_len, NULL, 0, pk, 0, context);
1373
1443
 
1374
1444
  cleanup:
1375
1445
  /* @[FIPS204, Section 3.6.3] Destruction of intermediate values. */
@@ -1382,18 +1452,17 @@ cleanup:
1382
1452
  MLD_MUST_CHECK_RETURN_VALUE
1383
1453
  MLD_EXTERNAL_API
1384
1454
  int mld_sign_signature_pre_hash_shake256(
1385
- uint8_t sig[MLDSA_CRYPTO_BYTES], size_t *siglen, const uint8_t *m,
1386
- size_t mlen, const uint8_t *ctx, size_t ctxlen,
1387
- const uint8_t rnd[MLDSA_RNDBYTES],
1455
+ uint8_t sig[MLDSA_CRYPTO_BYTES], const uint8_t *m, size_t mlen,
1456
+ const uint8_t *ctx, size_t ctxlen, const uint8_t rnd[MLDSA_RNDBYTES],
1388
1457
  const uint8_t sk[MLDSA_CRYPTO_SECRETKEYBYTES],
1389
1458
  MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
1390
1459
  {
1391
1460
  MLD_ALIGN uint8_t ph[64];
1392
1461
  int ret;
1393
1462
  mld_shake256(ph, sizeof(ph), m, mlen);
1394
- ret = mld_sign_signature_pre_hash_internal(sig, siglen, ph, sizeof(ph), ctx,
1395
- ctxlen, rnd, sk,
1396
- MLD_PREHASH_SHAKE_256, context);
1463
+ ret = mld_sign_signature_pre_hash_internal(sig, ph, sizeof(ph), ctx, ctxlen,
1464
+ rnd, sk, MLD_PREHASH_SHAKE_256,
1465
+ context);
1397
1466
  /* @[FIPS204, Section 3.6.3] Destruction of intermediate values. */
1398
1467
  mld_zeroize(ph, sizeof(ph));
1399
1468
  return ret;
@@ -1404,7 +1473,7 @@ int mld_sign_signature_pre_hash_shake256(
1404
1473
  MLD_MUST_CHECK_RETURN_VALUE
1405
1474
  MLD_EXTERNAL_API
1406
1475
  int mld_sign_verify_pre_hash_shake256(
1407
- const uint8_t *sig, size_t siglen, const uint8_t *m, size_t mlen,
1476
+ const uint8_t sig[MLDSA_CRYPTO_BYTES], const uint8_t *m, size_t mlen,
1408
1477
  const uint8_t *ctx, size_t ctxlen,
1409
1478
  const uint8_t pk[MLDSA_CRYPTO_PUBLICKEYBYTES],
1410
1479
  MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
@@ -1412,9 +1481,8 @@ int mld_sign_verify_pre_hash_shake256(
1412
1481
  MLD_ALIGN uint8_t ph[64];
1413
1482
  int ret;
1414
1483
  mld_shake256(ph, sizeof(ph), m, mlen);
1415
- ret = mld_sign_verify_pre_hash_internal(sig, siglen, ph, sizeof(ph), ctx,
1416
- ctxlen, pk, MLD_PREHASH_SHAKE_256,
1417
- context);
1484
+ ret = mld_sign_verify_pre_hash_internal(sig, ph, sizeof(ph), ctx, ctxlen, pk,
1485
+ MLD_PREHASH_SHAKE_256, context);
1418
1486
  /* @[FIPS204, Section 3.6.3] Destruction of intermediate values. */
1419
1487
  mld_zeroize(ph, sizeof(ph));
1420
1488
  return ret;
@@ -1505,10 +1573,12 @@ static int mld_validate_hash_length(int hashalg, size_t len)
1505
1573
  return (len == 256 / 8) ? 0 : -1;
1506
1574
  case MLD_PREHASH_SHAKE_256:
1507
1575
  return (len == 512 / 8) ? 0 : -1;
1576
+ default:
1577
+ return -1;
1508
1578
  }
1509
- return -1;
1510
1579
  }
1511
1580
 
1581
+ MLD_EXTERNAL_API
1512
1582
  size_t mld_prepare_domain_separation_prefix(
1513
1583
  uint8_t prefix[MLD_DOMAIN_SEPARATION_MAX_BYTES], const uint8_t *ph,
1514
1584
  size_t phlen, const uint8_t *ctx, size_t ctxlen, int hashalg)
@@ -1572,12 +1642,11 @@ int mld_sign_pk_from_sk(uint8_t pk[MLDSA_CRYPTO_PUBLICKEYBYTES],
1572
1642
  /* Inline unpack_sk: mld_unpack_sk uses lazy types for s1/s2/t0 which
1573
1643
  * we cannot use here. t0 stays in packed form -- we compare it against
1574
1644
  * the recomputed value below. */
1575
- mld_memcpy(rho, sk, MLDSA_SEEDBYTES);
1576
- mld_memcpy(key, sk + MLDSA_SEEDBYTES, MLDSA_SEEDBYTES);
1577
- mld_memcpy(tr, sk + 2 * MLDSA_SEEDBYTES, MLDSA_TRBYTES);
1578
- mld_polyvecl_unpack_eta(s1, sk + 2 * MLDSA_SEEDBYTES + MLDSA_TRBYTES);
1579
- mld_polyveck_unpack_eta(s2, sk + 2 * MLDSA_SEEDBYTES + MLDSA_TRBYTES +
1580
- MLDSA_L * MLDSA_POLYETA_PACKEDBYTES);
1645
+ mld_memcpy(rho, sk + MLDSA_SK_RHO_OFFSET, MLDSA_SEEDBYTES);
1646
+ mld_memcpy(key, sk + MLDSA_SK_KEY_OFFSET, MLDSA_SEEDBYTES);
1647
+ mld_memcpy(tr, sk + MLDSA_SK_TR_OFFSET, MLDSA_TRBYTES);
1648
+ mld_polyvecl_unpack_eta(s1, sk + MLDSA_SK_S1_OFFSET);
1649
+ mld_polyveck_unpack_eta(s2, sk + MLDSA_SK_S2_OFFSET);
1581
1650
 
1582
1651
  /* Validate s1 and s2 coefficients are within [-MLDSA_ETA, MLDSA_ETA] */
1583
1652
  chk1 = mld_polyvecl_chknorm(s1, MLDSA_ETA + 1) & 0xFF;
@@ -1587,11 +1656,11 @@ int mld_sign_pk_from_sk(uint8_t pk[MLDSA_CRYPTO_PUBLICKEYBYTES],
1587
1656
  mld_polyvecl_ntt(s1);
1588
1657
 
1589
1658
  /* Pack rho into pk */
1590
- mld_memcpy(pk, rho, MLDSA_SEEDBYTES);
1659
+ mld_memcpy(pk + MLDSA_PK_RHO_OFFSET, rho, MLDSA_SEEDBYTES);
1591
1660
 
1592
1661
  /* Recompute t row by row, decompose, and pack t1 into pk and t0 into
1593
1662
  * t0_packed. */
1594
- ret = mld_compute_pack_t0_t1(pk + MLDSA_SEEDBYTES, t0_packed, s1, s2, rho,
1663
+ ret = mld_compute_pack_t0_t1(pk + MLDSA_PK_T1_OFFSET, t0_packed, s1, s2, rho,
1595
1664
  context);
1596
1665
  if (ret != 0)
1597
1666
  {
@@ -1599,10 +1668,7 @@ int mld_sign_pk_from_sk(uint8_t pk[MLDSA_CRYPTO_PUBLICKEYBYTES],
1599
1668
  }
1600
1669
 
1601
1670
  /* Compare recomputed packed t0 against the t0 region of sk. */
1602
- cmp0 = mld_ct_memcmp(t0_packed,
1603
- sk + 2 * MLDSA_SEEDBYTES + MLDSA_TRBYTES +
1604
- MLDSA_L * MLDSA_POLYETA_PACKEDBYTES +
1605
- MLDSA_K * MLDSA_POLYETA_PACKEDBYTES,
1671
+ cmp0 = mld_ct_memcmp(t0_packed, sk + MLDSA_SK_T0_OFFSET,
1606
1672
  MLDSA_K * MLDSA_POLYT0_PACKEDBYTES);
1607
1673
 
1608
1674
  /* Compute tr_computed = H(pk) and compare to the stored tr */
@@ -1613,7 +1679,7 @@ int mld_sign_pk_from_sk(uint8_t pk[MLDSA_CRYPTO_PUBLICKEYBYTES],
1613
1679
 
1614
1680
  /* Declassify the final result of the validity check. */
1615
1681
  MLD_CT_TESTING_DECLASSIFY(&check, sizeof(check));
1616
- ret = (check != 0) ? MLD_ERR_FAIL : 0;
1682
+ ret = (check != 0) ? MLD_ERR_INVALID_KEY : 0;
1617
1683
 
1618
1684
  cleanup:
1619
1685
 
@@ -1650,6 +1716,5 @@ cleanup:
1650
1716
  #undef mld_attempt_signature_generation
1651
1717
  #undef mld_compute_pack_t0_t1
1652
1718
  #undef mld_get_max_signing_attempts
1653
- #undef MLD_NONCE_UB
1654
- #undef MLD_CONFIG_MAX_SIGNING_ATTEMPTS
1719
+ #undef MLD_MAX_SIGNING_ATTEMPTS
1655
1720
  #undef MLD_PRE_HASH_OID_LEN