mldsa_gh 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +88 -0
  4. data/ext/mldsa_gh_native/extconf.rb +14 -0
  5. data/ext/mldsa_gh_native/mldsa_gh_native.c +222 -0
  6. data/ext/mldsa_gh_native/mldsa_gh_native_all.c +24 -0
  7. data/ext/mldsa_gh_native/mldsa_gh_native_all.h +26 -0
  8. data/ext/mldsa_gh_native/vendor/mldsa-native/BUILDING.md +108 -0
  9. data/ext/mldsa_gh_native/vendor/mldsa-native/LICENSE +305 -0
  10. data/ext/mldsa_gh_native/vendor/mldsa-native/README.md +247 -0
  11. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/README.md +23 -0
  12. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/mldsa_native.c +803 -0
  13. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/mldsa_native.h +956 -0
  14. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/mldsa_native_asm.S +830 -0
  15. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/mldsa_native_config.h +855 -0
  16. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/cbmc.h +233 -0
  17. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/common.h +301 -0
  18. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/context.h +152 -0
  19. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/ct.c +21 -0
  20. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/ct.h +373 -0
  21. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/debug.c +75 -0
  22. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/debug.h +125 -0
  23. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/fips202.c +270 -0
  24. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/fips202.h +224 -0
  25. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/fips202x4.c +187 -0
  26. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/fips202x4.h +125 -0
  27. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/keccakf1600.c +510 -0
  28. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/keccakf1600.h +110 -0
  29. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/auto.h +85 -0
  30. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/fips202_native_aarch64.h +69 -0
  31. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccak_f1600_x1_scalar_aarch64_asm.S +378 -0
  32. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccak_f1600_x1_v84a_aarch64_asm.S +207 -0
  33. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccak_f1600_x2_v84a_aarch64_asm.S +262 -0
  34. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccak_f1600_x4_v8a_scalar_hybrid_aarch64_asm.S +1080 -0
  35. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccak_f1600_x4_v8a_v84a_scalar_hybrid_aarch64_asm.S +990 -0
  36. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccakf1600_round_constants.c +47 -0
  37. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x1_scalar.h +27 -0
  38. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x1_v84a.h +36 -0
  39. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x2_v84a.h +40 -0
  40. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x4_v8a_scalar.h +32 -0
  41. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x4_v8a_v84a_scalar.h +37 -0
  42. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/api.h +129 -0
  43. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/README.md +10 -0
  44. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/mve.h +67 -0
  45. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/src/fips202_native_armv81m.h +37 -0
  46. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/src/keccak_f1600_x4_mve.S +717 -0
  47. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/src/keccak_f1600_x4_mve.c +42 -0
  48. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/src/keccak_f1600_x4_state_extract_bytes_mve.S +334 -0
  49. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/src/keccak_f1600_x4_state_xor_bytes_mve.S +355 -0
  50. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/src/keccakf1600_round_constants.c +53 -0
  51. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/auto.h +35 -0
  52. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/x86_64/keccak_f1600_x4_avx2.h +34 -0
  53. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/x86_64/src/fips202_native_x86_64.h +45 -0
  54. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/x86_64/src/keccak_f1600_x4_avx2_asm.S +488 -0
  55. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/x86_64/src/keccakf1600_constants.c +52 -0
  56. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/meta.h +314 -0
  57. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/aarch64_zetas.c +248 -0
  58. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/arith_native_aarch64.h +367 -0
  59. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_intt_aarch64_asm.S +786 -0
  60. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_ntt_aarch64_asm.S +686 -0
  61. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_pointwise_montgomery_aarch64_asm.S +106 -0
  62. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_poly_caddq_aarch64_asm.S +69 -0
  63. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_poly_chknorm_aarch64_asm.S +76 -0
  64. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_poly_decompose_32_aarch64_asm.S +108 -0
  65. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_poly_decompose_88_aarch64_asm.S +108 -0
  66. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_poly_use_hint_32_aarch64_asm.S +125 -0
  67. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_poly_use_hint_88_aarch64_asm.S +133 -0
  68. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_polyvecl_pointwise_acc_montgomery_l4_aarch64_asm.S +157 -0
  69. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_polyvecl_pointwise_acc_montgomery_l5_aarch64_asm.S +173 -0
  70. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_polyvecl_pointwise_acc_montgomery_l7_aarch64_asm.S +205 -0
  71. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_polyz_unpack_17_aarch64_asm.S +103 -0
  72. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_polyz_unpack_19_aarch64_asm.S +100 -0
  73. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_rej_uniform_aarch64_asm.S +222 -0
  74. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_rej_uniform_eta2_aarch64_asm.S +170 -0
  75. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_rej_uniform_eta4_aarch64_asm.S +163 -0
  76. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/polyz_unpack_table.c +52 -0
  77. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/rej_uniform_eta_table.c +547 -0
  78. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/rej_uniform_table.c +63 -0
  79. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/api.h +617 -0
  80. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/meta.h +24 -0
  81. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/meta.h +323 -0
  82. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/arith_native_x86_64.h +330 -0
  83. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/consts.c +157 -0
  84. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/consts.h +27 -0
  85. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_intt_avx2_asm.S +2333 -0
  86. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_ntt_avx2_asm.S +2405 -0
  87. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_nttunpack_avx2_asm.S +254 -0
  88. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_pointwise_acc_l4_avx2_asm.S +173 -0
  89. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_pointwise_acc_l5_avx2_asm.S +189 -0
  90. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_pointwise_acc_l7_avx2_asm.S +221 -0
  91. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_pointwise_avx2_asm.S +158 -0
  92. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_poly_caddq_avx2_asm.S +199 -0
  93. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_poly_chknorm_avx2_asm.S +176 -0
  94. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_poly_decompose_32_avx2_asm.S +490 -0
  95. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_poly_decompose_88_avx2_asm.S +489 -0
  96. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_poly_use_hint_32_avx2_asm.S +123 -0
  97. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_poly_use_hint_88_avx2_asm.S +125 -0
  98. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_polyz_unpack_17_avx2_asm.S +355 -0
  99. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_polyz_unpack_19_avx2_asm.S +355 -0
  100. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_rej_uniform_avx2_asm.S +132 -0
  101. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_rej_uniform_eta2_avx2_asm.S +205 -0
  102. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_rej_uniform_eta4_avx2_asm.S +176 -0
  103. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/rej_uniform_table.c +161 -0
  104. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/packing.c +213 -0
  105. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/packing.h +277 -0
  106. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/params.h +153 -0
  107. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/poly.c +1066 -0
  108. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/poly.h +464 -0
  109. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/poly_kl.c +910 -0
  110. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/poly_kl.h +367 -0
  111. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/polyvec.c +509 -0
  112. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/polyvec.h +435 -0
  113. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/polyvec_lazy.c +311 -0
  114. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/polyvec_lazy.h +652 -0
  115. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/randombytes.h +26 -0
  116. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/reduce.h +144 -0
  117. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/rounding.h +265 -0
  118. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/sign.c +1720 -0
  119. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/sign.h +850 -0
  120. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/symmetric.h +68 -0
  121. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/sys.h +327 -0
  122. data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/zetas.inc +55 -0
  123. data/lib/mldsa/parameter_set.rb +82 -0
  124. data/lib/mldsa/signing_key.rb +147 -0
  125. data/lib/mldsa/verify_key.rb +72 -0
  126. data/lib/mldsa/version.rb +7 -0
  127. data/lib/mldsa_gh.rb +93 -0
  128. metadata +225 -0
@@ -0,0 +1,1720 @@
1
+ /*
2
+ * Copyright (c) The mldsa-native project authors
3
+ * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT
4
+ */
5
+
6
+ /* References
7
+ * ==========
8
+ *
9
+ * - [FIPS140_3_IG]
10
+ * Implementation Guidance for FIPS 140-3 and the Cryptographic Module
11
+ * Validation Program
12
+ * National Institute of Standards and Technology
13
+ * https://csrc.nist.gov/projects/cryptographic-module-validation-program/fips-140-3-ig-announcements
14
+ *
15
+ * - [FIPS204]
16
+ * FIPS 204 Module-Lattice-Based Digital Signature Standard
17
+ * National Institute of Standards and Technology
18
+ * https://csrc.nist.gov/pubs/fips/204/final
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
+ *
25
+ * - [Round3_Spec]
26
+ * CRYSTALS-Dilithium Algorithm Specifications and Supporting Documentation
27
+ * (Version 3.1)
28
+ * Bai, Ducas, Kiltz, Lepoint, Lyubashevsky, Schwabe, Seiler, Stehlé
29
+ * https://pq-crystals.org/dilithium/data/dilithium-specification-round3-20210208.pdf
30
+ */
31
+
32
+ #include "sign.h"
33
+
34
+ #include "cbmc.h"
35
+ #include "ct.h"
36
+ #include "debug.h"
37
+ #include "packing.h"
38
+ #include "poly.h"
39
+ #include "poly_kl.h"
40
+ #include "polyvec.h"
41
+ #include "randombytes.h"
42
+ #include "symmetric.h"
43
+
44
+ /* Parameter set namespacing
45
+ * This is to facilitate building multiple instances
46
+ * of mldsa-native (e.g. with varying parameter sets)
47
+ * within a single compilation unit. */
48
+ #define mld_check_pct MLD_ADD_PARAM_SET(mld_check_pct) MLD_CONTEXT_PARAMETERS_2
49
+ #define mld_sample_s1_s2 MLD_ADD_PARAM_SET(mld_sample_s1_s2)
50
+ #define mld_validate_hash_length MLD_ADD_PARAM_SET(mld_validate_hash_length)
51
+ #define mld_get_hash_oid MLD_ADD_PARAM_SET(mld_get_hash_oid)
52
+ #define mld_H MLD_ADD_PARAM_SET(mld_H)
53
+ #define mld_compute_pack_z MLD_ADD_PARAM_SET(mld_compute_pack_z)
54
+ #define mld_attempt_signature_generation \
55
+ MLD_ADD_PARAM_SET(mld_attempt_signature_generation) MLD_CONTEXT_PARAMETERS_8
56
+ #define mld_compute_pack_t0_t1 \
57
+ MLD_ADD_PARAM_SET(mld_compute_pack_t0_t1) MLD_CONTEXT_PARAMETERS_5
58
+ #define mld_get_max_signing_attempts \
59
+ MLD_ADD_PARAM_SET(mld_get_max_signing_attempts)
60
+
61
+ #if !defined(MLD_CONFIG_NO_KEYPAIR_API)
62
+ static int mld_check_pct(uint8_t const pk[MLDSA_CRYPTO_PUBLICKEYBYTES],
63
+ uint8_t const sk[MLDSA_CRYPTO_SECRETKEYBYTES],
64
+ MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
65
+ __contract__(
66
+ requires(memory_no_alias(pk, MLDSA_CRYPTO_PUBLICKEYBYTES))
67
+ requires(memory_no_alias(sk, MLDSA_CRYPTO_SECRETKEYBYTES))
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)
72
+ );
73
+
74
+ #if defined(MLD_CONFIG_KEYGEN_PCT)
75
+ /**
76
+ * Pair-wise Consistency Test (PCT) for DSA keypairs.
77
+ *
78
+ * @[FIPS140_3_IG] TE10.35.02
79
+ * (https://csrc.nist.gov/csrc/media/Projects/cryptographic-module-validation-program/documents/fips%20140-3/FIPS%20140-3%20IG.pdf).
80
+ *
81
+ * Validates that a generated public/private key pair can correctly sign and
82
+ * verify data. Performs signature generation using the private key (sk),
83
+ * followed by signature verification using the public key (pk).
84
+ *
85
+ * @note @[FIPS204] requires that public/private key pairs are to be used
86
+ * only for the calculation and/or verification of digital signatures.
87
+ *
88
+ * @param[in] pk Public key.
89
+ * @param[in] sk Secret key.
90
+ * @param context Application context. Only present when
91
+ * MLD_CONFIG_CONTEXT_PARAMETER is defined; type set by
92
+ * MLD_CONFIG_CONTEXT_PARAMETER_TYPE.
93
+ *
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.
101
+ */
102
+ static int mld_check_pct(uint8_t const pk[MLDSA_CRYPTO_PUBLICKEYBYTES],
103
+ uint8_t const sk[MLDSA_CRYPTO_SECRETKEYBYTES],
104
+ MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
105
+ {
106
+ MLD_ALIGN uint8_t message[1] = {0};
107
+ int ret;
108
+ MLD_ALLOC(signature, uint8_t, MLDSA_CRYPTO_BYTES, context);
109
+ MLD_ALLOC(pk_test, uint8_t, MLDSA_CRYPTO_PUBLICKEYBYTES, context);
110
+
111
+ if (signature == NULL || pk_test == NULL)
112
+ {
113
+ ret = MLD_ERR_OUT_OF_MEMORY;
114
+ goto cleanup;
115
+ }
116
+
117
+ /* Copy public key for testing */
118
+ mld_memcpy(pk_test, pk, MLDSA_CRYPTO_PUBLICKEYBYTES);
119
+
120
+ /* Sign a test message using the original secret key */
121
+ ret = mld_sign_signature(signature, message, sizeof(message), NULL, 0, sk,
122
+ context);
123
+ if (ret != 0)
124
+ {
125
+ goto cleanup;
126
+ }
127
+
128
+ #if defined(MLD_CONFIG_KEYGEN_PCT_BREAKAGE_TEST)
129
+ /* Deliberately break public key for testing purposes */
130
+ if (mld_break_pct())
131
+ {
132
+ pk_test[0] = ~pk_test[0];
133
+ }
134
+ #endif /* MLD_CONFIG_KEYGEN_PCT_BREAKAGE_TEST */
135
+
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
+ }
143
+
144
+ cleanup:
145
+ /* @[FIPS204, Section 3.6.3] Destruction of intermediate values. */
146
+ MLD_FREE(pk_test, uint8_t, MLDSA_CRYPTO_PUBLICKEYBYTES, context);
147
+ MLD_FREE(signature, uint8_t, MLDSA_CRYPTO_BYTES, context);
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
+
160
+ return ret;
161
+ }
162
+ #else /* MLD_CONFIG_KEYGEN_PCT */
163
+ static int mld_check_pct(uint8_t const pk[MLDSA_CRYPTO_PUBLICKEYBYTES],
164
+ uint8_t const sk[MLDSA_CRYPTO_SECRETKEYBYTES],
165
+ MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
166
+ {
167
+ /* Skip PCT */
168
+ ((void)pk);
169
+ ((void)sk);
170
+ MLD_CONTEXT_UNUSED(context);
171
+ return 0;
172
+ }
173
+ #endif /* !MLD_CONFIG_KEYGEN_PCT */
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
+ */
185
+ static void mld_sample_s1_s2(mld_polyvecl *s1, mld_polyveck *s2,
186
+ const uint8_t seed[MLDSA_CRHBYTES])
187
+ __contract__(
188
+ requires(memory_no_alias(s1, sizeof(mld_polyvecl)))
189
+ requires(memory_no_alias(s2, sizeof(mld_polyveck)))
190
+ requires(memory_no_alias(seed, MLDSA_CRHBYTES))
191
+ assigns(object_whole(s1), object_whole(s2))
192
+ ensures(forall(l0, 0, MLDSA_L, array_abs_bound(s1->vec[l0].coeffs, 0, MLDSA_N, MLDSA_ETA + 1)))
193
+ ensures(forall(k0, 0, MLDSA_K, array_abs_bound(s2->vec[k0].coeffs, 0, MLDSA_N, MLDSA_ETA + 1)))
194
+ )
195
+ {
196
+ /* Sample short vectors s1 and s2 */
197
+ #if defined(MLD_CONFIG_SERIAL_FIPS202_ONLY)
198
+ int i;
199
+ uint16_t nonce = 0;
200
+ /* Safety: The nonces are at most 14 (MLDSA_L + MLDSA_K - 1), and, hence, the
201
+ * casts are safe. */
202
+ for (i = 0; i < MLDSA_L; i++)
203
+ {
204
+ mld_poly_uniform_eta(&s1->vec[i], seed, (uint8_t)(nonce + i));
205
+ }
206
+ for (i = 0; i < MLDSA_K; i++)
207
+ {
208
+ mld_poly_uniform_eta(&s2->vec[i], seed, (uint8_t)(nonce + MLDSA_L + i));
209
+ }
210
+ #else /* MLD_CONFIG_SERIAL_FIPS202_ONLY */
211
+ #if MLD_CONFIG_PARAMETER_SET == 44
212
+ mld_poly_uniform_eta_4x(&s1->vec[0], &s1->vec[1], &s1->vec[2], &s1->vec[3],
213
+ seed, 0, 1, 2, 3);
214
+ mld_poly_uniform_eta_4x(&s2->vec[0], &s2->vec[1], &s2->vec[2], &s2->vec[3],
215
+ seed, 4, 5, 6, 7);
216
+ #elif MLD_CONFIG_PARAMETER_SET == 65
217
+ mld_poly_uniform_eta_4x(&s1->vec[0], &s1->vec[1], &s1->vec[2], &s1->vec[3],
218
+ seed, 0, 1, 2, 3);
219
+ mld_poly_uniform_eta_4x(&s1->vec[4], &s2->vec[0], &s2->vec[1],
220
+ &s2->vec[2] /* irrelevant */, seed, 4, 5, 6,
221
+ 0xFF /* irrelevant */);
222
+ mld_poly_uniform_eta_4x(&s2->vec[2], &s2->vec[3], &s2->vec[4], &s2->vec[5],
223
+ seed, 7, 8, 9, 10);
224
+ #elif MLD_CONFIG_PARAMETER_SET == 87
225
+ mld_poly_uniform_eta_4x(&s1->vec[0], &s1->vec[1], &s1->vec[2], &s1->vec[3],
226
+ seed, 0, 1, 2, 3);
227
+ mld_poly_uniform_eta_4x(&s1->vec[4], &s1->vec[5], &s1->vec[6],
228
+ &s2->vec[0] /* irrelevant */, seed, 4, 5, 6,
229
+ 0xFF /* irrelevant */);
230
+ mld_poly_uniform_eta_4x(&s2->vec[0], &s2->vec[1], &s2->vec[2], &s2->vec[3],
231
+ seed, 7, 8, 9, 10);
232
+ mld_poly_uniform_eta_4x(&s2->vec[4], &s2->vec[5], &s2->vec[6], &s2->vec[7],
233
+ seed, 11, 12, 13, 14);
234
+ #endif /* MLD_CONFIG_PARAMETER_SET == 87 */
235
+ #endif /* !MLD_CONFIG_SERIAL_FIPS202_ONLY */
236
+ }
237
+
238
+ /**
239
+ * Compute t = A*s1hat + s2 row by row, decompose each row into t0[k] and
240
+ * t1[k] via power2round, and bit-pack t1[k] into pk_t1 and t0[k] into the
241
+ * t0_packed buffer. Used by both keygen and pk_from_sk.
242
+ *
243
+ * @spec{Partially implements @[FIPS204, Algorithm 22, pkEncode] (t1) and
244
+ * @[FIPS204, Algorithm 24, skEncode] (t0).}
245
+ *
246
+ * @param[out] pk_t1 Output buffer for packed t1 (size
247
+ * MLDSA_K * MLDSA_POLYT1_PACKEDBYTES; i.e. the t1
248
+ * region of pk).
249
+ * @param[out] t0_packed Output buffer for packed t0 (size
250
+ * MLDSA_K * MLDSA_POLYT0_PACKEDBYTES).
251
+ * @param[in] s1hat s1 in NTT domain.
252
+ * @param[in] s2 s2.
253
+ * @param[in] rho Byte array containing seed rho.
254
+ * @param context Application context. Only present when
255
+ * MLD_CONFIG_CONTEXT_PARAMETER is defined; type set by
256
+ * MLD_CONFIG_CONTEXT_PARAMETER_TYPE.
257
+ *
258
+ * @return - 0: Success.
259
+ * - MLD_ERR_OUT_OF_MEMORY: If MLD_CONFIG_CUSTOM_ALLOC_FREE is used and
260
+ * an allocation via MLD_CUSTOM_ALLOC returned NULL.
261
+ */
262
+ MLD_MUST_CHECK_RETURN_VALUE
263
+ static int mld_compute_pack_t0_t1(
264
+ uint8_t pk_t1[MLDSA_K * MLDSA_POLYT1_PACKEDBYTES],
265
+ uint8_t t0_packed[MLDSA_K * MLDSA_POLYT0_PACKEDBYTES],
266
+ const mld_polyvecl *s1hat, const mld_polyveck *s2,
267
+ const uint8_t rho[MLDSA_SEEDBYTES],
268
+ MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
269
+ __contract__(
270
+ requires(memory_no_alias(pk_t1, MLDSA_K * MLDSA_POLYT1_PACKEDBYTES))
271
+ requires(memory_no_alias(t0_packed, MLDSA_K * MLDSA_POLYT0_PACKEDBYTES))
272
+ requires(memory_no_alias(s1hat, sizeof(mld_polyvecl)))
273
+ requires(memory_no_alias(s2, sizeof(mld_polyveck)))
274
+ requires(memory_no_alias(rho, MLDSA_SEEDBYTES))
275
+ requires(forall(l1, 0, MLDSA_L,
276
+ array_abs_bound(s1hat->vec[l1].coeffs, 0, MLDSA_N, MLD_NTT_BOUND)))
277
+ requires(forall(k2, 0, MLDSA_K,
278
+ array_bound(s2->vec[k2].coeffs, 0, MLDSA_N,
279
+ MLD_POLYETA_UNPACK_LOWER_BOUND, MLDSA_ETA + 1)))
280
+ assigns(memory_slice(pk_t1, MLDSA_K * MLDSA_POLYT1_PACKEDBYTES))
281
+ assigns(memory_slice(t0_packed, MLDSA_K * MLDSA_POLYT0_PACKEDBYTES))
282
+ ensures(return_value == 0 || return_value == MLD_ERR_OUT_OF_MEMORY))
283
+ {
284
+ unsigned int k;
285
+ int ret;
286
+ MLD_ALLOC(mat, mld_polymat, 1, context);
287
+ MLD_ALLOC(t0k, mld_poly, 1, context);
288
+ MLD_ALLOC(t1k, mld_poly, 1, context);
289
+
290
+ if (mat == NULL || t0k == NULL || t1k == NULL)
291
+ {
292
+ ret = MLD_ERR_OUT_OF_MEMORY;
293
+ goto cleanup;
294
+ }
295
+
296
+ /* Expand matrix */
297
+ mld_polyvec_matrix_expand(mat, rho);
298
+
299
+ for (k = 0; k < MLDSA_K; k++)
300
+ __loop__(
301
+ assigns(k, memory_slice(pk_t1, MLDSA_K * MLDSA_POLYT1_PACKEDBYTES),
302
+ memory_slice(t0_packed, MLDSA_K * MLDSA_POLYT0_PACKEDBYTES),
303
+ memory_slice(t0k, sizeof(mld_poly)),
304
+ memory_slice(t1k, sizeof(mld_poly))
305
+ MLD_IF_REDUCE_RAM(, memory_slice(mat, sizeof(mld_polymat))))
306
+ invariant(k <= MLDSA_K)
307
+ decreases(MLDSA_K - k)
308
+ )
309
+ {
310
+ /* t0k = (A * s1hat)_k in NTT domain */
311
+ mld_polyvec_matrix_pointwise_montgomery_row(t0k, mat, s1hat, k);
312
+
313
+ /* t0k = invNTT(t0k) */
314
+ mld_poly_invntt_tomont(t0k);
315
+
316
+ /* t0k += s2[k] */
317
+ mld_poly_add(t0k, &s2->vec[k]);
318
+
319
+ /* Reference: The following reduction is not present in the reference
320
+ * implementation. Omitting this reduction requires the output
321
+ * of the invntt to be small enough such that the addition of
322
+ * s2 does not result in absolute values >= MLDSA_Q. While our
323
+ * C, x86_64, and AArch64 invntt implementations produce small
324
+ * enough values for this to work out, it complicates the
325
+ * bounds reasoning. We instead add an additional reduction,
326
+ * and can consequently, relax the bounds requirements for the
327
+ * invntt.
328
+ */
329
+ mld_poly_reduce(t0k);
330
+
331
+ /* Decompose into t1[k] and t0[k] (in place into t0k). */
332
+ mld_poly_caddq(t0k);
333
+ mld_poly_power2round(t1k, t0k, t0k);
334
+
335
+ /* Pack t1[k] into pk and t0[k] into the t0 output buffer. */
336
+ mld_polyt1_pack(pk_t1 + k * MLDSA_POLYT1_PACKEDBYTES, t1k);
337
+ mld_polyt0_pack(t0_packed + k * MLDSA_POLYT0_PACKEDBYTES, t0k);
338
+ }
339
+
340
+ ret = 0;
341
+ cleanup:
342
+ /* @[FIPS204, Section 3.6.3] Destruction of intermediate values. */
343
+ MLD_FREE(t1k, mld_poly, 1, context);
344
+ MLD_FREE(t0k, mld_poly, 1, context);
345
+ MLD_FREE(mat, mld_polymat, 1, context);
346
+ return ret;
347
+ }
348
+
349
+ MLD_MUST_CHECK_RETURN_VALUE
350
+ MLD_EXTERNAL_API
351
+ int mld_sign_keypair_internal(uint8_t pk[MLDSA_CRYPTO_PUBLICKEYBYTES],
352
+ uint8_t sk[MLDSA_CRYPTO_SECRETKEYBYTES],
353
+ const uint8_t seed[MLDSA_SEEDBYTES],
354
+ MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
355
+ {
356
+ int ret;
357
+ const uint8_t *rho, *rhoprime, *key;
358
+
359
+ MLD_ALLOC(seedbuf, uint8_t, 2 * MLDSA_SEEDBYTES + MLDSA_CRHBYTES, context);
360
+ MLD_ALLOC(inbuf, uint8_t, MLDSA_SEEDBYTES + 2, context);
361
+ MLD_ALLOC(tr, uint8_t, MLDSA_TRBYTES, context);
362
+ MLD_ALLOC(s1, mld_polyvecl, 1, context);
363
+ MLD_ALLOC(s2, mld_polyveck, 1, context);
364
+
365
+ if (seedbuf == NULL || inbuf == NULL || tr == NULL || s1 == NULL ||
366
+ s2 == NULL)
367
+ {
368
+ ret = MLD_ERR_OUT_OF_MEMORY;
369
+ goto cleanup;
370
+ }
371
+
372
+ /* Get randomness for rho, rhoprime and key */
373
+ mld_memcpy(inbuf, seed, MLDSA_SEEDBYTES);
374
+ inbuf[MLDSA_SEEDBYTES + 0] = MLDSA_K;
375
+ inbuf[MLDSA_SEEDBYTES + 1] = MLDSA_L;
376
+ mld_shake256(seedbuf, 2 * MLDSA_SEEDBYTES + MLDSA_CRHBYTES, inbuf,
377
+ MLDSA_SEEDBYTES + 2);
378
+ rho = seedbuf;
379
+ rhoprime = rho + MLDSA_SEEDBYTES;
380
+ key = rhoprime + MLDSA_CRHBYTES;
381
+
382
+ /* Constant time: rho is part of the public key and, hence, public. */
383
+ MLD_CT_TESTING_DECLASSIFY(rho, MLDSA_SEEDBYTES);
384
+
385
+ /* Sample s1 and s2 */
386
+ mld_sample_s1_s2(s1, s2, rhoprime);
387
+
388
+ /* Pack s1 into sk before NTT */
389
+ mld_pack_sk_s1(sk, s1);
390
+
391
+ /* NTT s1 in place to use as s1hat */
392
+ mld_polyvecl_ntt(s1);
393
+
394
+ /* Pack rho into pk */
395
+ mld_memcpy(pk + MLDSA_PK_RHO_OFFSET, rho, MLDSA_SEEDBYTES);
396
+
397
+ /* Compute t = A*s1hat + s2 row by row, decompose into t1/t0, and pack
398
+ * t1 into pk and t0 directly into the t0 region of sk. */
399
+ ret = mld_compute_pack_t0_t1(pk + MLDSA_PK_T1_OFFSET, sk + MLDSA_SK_T0_OFFSET,
400
+ s1, s2, rho, context);
401
+ if (ret != 0)
402
+ {
403
+ goto cleanup;
404
+ }
405
+
406
+ /* Compute tr = H(pk) */
407
+ mld_shake256(tr, MLDSA_TRBYTES, pk, MLDSA_CRYPTO_PUBLICKEYBYTES);
408
+
409
+ /* Pack remaining secret key components (s1 and t0 already packed) */
410
+ mld_pack_sk_rho_key_tr_s2(sk, rho, tr, key, s2);
411
+
412
+ /* Constant time: pk is the public key, inherently public data */
413
+ MLD_CT_TESTING_DECLASSIFY(pk, MLDSA_CRYPTO_PUBLICKEYBYTES);
414
+
415
+ cleanup:
416
+ /* @[FIPS204, Section 3.6.3] Destruction of intermediate values. */
417
+ MLD_FREE(s2, mld_polyveck, 1, context);
418
+ MLD_FREE(s1, mld_polyvecl, 1, context);
419
+ MLD_FREE(tr, uint8_t, MLDSA_TRBYTES, context);
420
+ MLD_FREE(inbuf, uint8_t, MLDSA_SEEDBYTES + 2, context);
421
+ MLD_FREE(seedbuf, uint8_t, 2 * MLDSA_SEEDBYTES + MLDSA_CRHBYTES, context);
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
+
430
+ if (ret != 0)
431
+ {
432
+ /* Clear caller outputs on failure. */
433
+ mld_zeroize(pk, MLDSA_CRYPTO_PUBLICKEYBYTES);
434
+ mld_zeroize(sk, MLDSA_CRYPTO_SECRETKEYBYTES);
435
+ }
436
+
437
+ return ret;
438
+ }
439
+
440
+ #if !defined(MLD_CONFIG_CORE_API_ONLY)
441
+ #if !defined(MLD_CONFIG_NO_RANDOMIZED_API)
442
+ MLD_MUST_CHECK_RETURN_VALUE
443
+ MLD_EXTERNAL_API
444
+ int mld_sign_keypair(uint8_t pk[MLDSA_CRYPTO_PUBLICKEYBYTES],
445
+ uint8_t sk[MLDSA_CRYPTO_SECRETKEYBYTES],
446
+ MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
447
+ {
448
+ int ret;
449
+ MLD_ALLOC(seed, uint8_t, MLDSA_SEEDBYTES, context);
450
+
451
+ if (seed == NULL)
452
+ {
453
+ ret = MLD_ERR_OUT_OF_MEMORY;
454
+ goto cleanup;
455
+ }
456
+
457
+ if (mld_randombytes(seed, MLDSA_SEEDBYTES) != 0)
458
+ {
459
+ ret = MLD_ERR_RNG_FAIL;
460
+ goto cleanup;
461
+ }
462
+ MLD_CT_TESTING_SECRET(seed, MLDSA_SEEDBYTES);
463
+ ret = mld_sign_keypair_internal(pk, sk, seed, context);
464
+
465
+ cleanup:
466
+ /* @[FIPS204, Section 3.6.3] Destruction of intermediate values. */
467
+ MLD_FREE(seed, uint8_t, MLDSA_SEEDBYTES, context);
468
+ return ret;
469
+ }
470
+ #endif /* !MLD_CONFIG_NO_RANDOMIZED_API */
471
+ #endif /* !MLD_CONFIG_CORE_API_ONLY */
472
+ #endif /* !MLD_CONFIG_NO_KEYPAIR_API */
473
+
474
+ #if !defined(MLD_CONFIG_NO_SIGN_API) || !defined(MLD_CONFIG_NO_VERIFY_API)
475
+ /**
476
+ * Abstracts application of SHAKE256 to one, two or three blocks of data,
477
+ * yielding a user-requested size of output.
478
+ *
479
+ * @param[out] out Pointer to output.
480
+ * @param outlen Requested output length in bytes.
481
+ * @param[in] in1 Pointer to input block 1. Must NOT be NULL.
482
+ * @param in1len Length of input in1 in bytes.
483
+ * @param[in] in2 Pointer to input block 2. May be NULL if in2len == 0,
484
+ * in which case this block is ignored.
485
+ * @param in2len Length of input in2 in bytes.
486
+ * @param[in] in3 Pointer to input block 3. May be NULL if in3len == 0,
487
+ * in which case this block is ignored.
488
+ * @param in3len Length of input in3 in bytes.
489
+ */
490
+ static void mld_H(uint8_t *out, size_t outlen, const uint8_t *in1,
491
+ size_t in1len, const uint8_t *in2, size_t in2len,
492
+ const uint8_t *in3, size_t in3len)
493
+ __contract__(
494
+ requires(in1len <= MLD_MAX_BUFFER_SIZE)
495
+ requires(in2len <= MLD_MAX_BUFFER_SIZE)
496
+ requires(in3len <= MLD_MAX_BUFFER_SIZE)
497
+ requires(outlen <= 8 * SHAKE256_RATE /* somewhat arbitrary bound */)
498
+ requires(memory_no_alias(in1, in1len))
499
+ requires(in2len == 0 || memory_no_alias(in2, in2len))
500
+ requires(in3len == 0 || memory_no_alias(in3, in3len))
501
+ requires(memory_no_alias(out, outlen))
502
+ assigns(memory_slice(out, outlen))
503
+ )
504
+ {
505
+ mld_shake256ctx state;
506
+ mld_shake256_init(&state);
507
+ mld_shake256_absorb(&state, in1, in1len);
508
+ if (in2len != 0)
509
+ {
510
+ mld_shake256_absorb(&state, in2, in2len);
511
+ }
512
+ if (in3len != 0)
513
+ {
514
+ mld_shake256_absorb(&state, in3, in3len);
515
+ }
516
+ mld_shake256_finalize(&state);
517
+ mld_shake256_squeeze(out, outlen, &state);
518
+ mld_shake256_release(&state);
519
+
520
+ /* @[FIPS204, Section 3.6.3] Destruction of intermediate values. */
521
+ mld_zeroize(&state, sizeof(state));
522
+ }
523
+ #endif /* !MLD_CONFIG_NO_SIGN_API || !MLD_CONFIG_NO_VERIFY_API */
524
+
525
+ #if !defined(MLD_CONFIG_NO_SIGN_API)
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. */
528
+
529
+ /**
530
+ * Compute z = y + s1*c, check that z has coefficients smaller than
531
+ * MLDSA_GAMMA1 - MLDSA_BETA, and pack z into the signature buffer.
532
+ *
533
+ * @reference{This function is inlined into mld_sign_signature in the
534
+ * reference implementation.}
535
+ *
536
+ * @param[in,out] sig Output signature.
537
+ * @param[in] cp Challenge polynomial.
538
+ * @param[in] s1hat Secret vector s1 in NTT domain.
539
+ * @param[in] y Masking vector y (or seed in REDUCE_RAM mode).
540
+ * @param[out] z Scratch polynomial for z computation.
541
+ * @param[out] tmp Scratch polynomial.
542
+ *
543
+ * @return - 0: Success (z has coefficients smaller than
544
+ * MLDSA_GAMMA1 - MLDSA_BETA).
545
+ * - MLD_ERR_FAIL: z rejected (norm check failed).
546
+ * - MLD_ERR_OUT_OF_MEMORY: If MLD_CONFIG_CUSTOM_ALLOC_FREE is used and
547
+ * an allocation via MLD_CUSTOM_ALLOC returned NULL.
548
+ */
549
+ MLD_MUST_CHECK_RETURN_VALUE
550
+ static int mld_compute_pack_z(uint8_t sig[MLDSA_CRYPTO_BYTES],
551
+ const mld_poly *cp, const mld_sk_s1hat *s1hat,
552
+ const mld_yvec *y, mld_poly *z, mld_poly *tmp)
553
+ __contract__(
554
+ requires(memory_no_alias(sig, MLDSA_CRYPTO_BYTES))
555
+ requires(memory_no_alias(cp, sizeof(mld_poly)))
556
+ requires(memory_no_alias(s1hat, sizeof(mld_sk_s1hat)))
557
+ requires(memory_no_alias(y, sizeof(mld_yvec)))
558
+ requires(memory_no_alias(z, sizeof(mld_poly)))
559
+ requires(memory_no_alias(tmp, sizeof(mld_poly)))
560
+ requires(array_abs_bound(cp->coeffs, 0, MLDSA_N, MLD_NTT_BOUND))
561
+ MLD_IF_NOT_REDUCE_RAM(
562
+ requires(forall(k0, 0, MLDSA_L,
563
+ array_bound(y->vec.vec[k0].coeffs, 0, MLDSA_N, -(MLDSA_GAMMA1 - 1), MLDSA_GAMMA1 + 1)))
564
+ requires(forall(k1, 0, MLDSA_L, array_abs_bound(s1hat->vec.vec[k1].coeffs, 0, MLDSA_N, MLD_NTT_BOUND)))
565
+ )
566
+ MLD_IF_REDUCE_RAM(
567
+ requires(memory_no_alias(s1hat->packed, MLDSA_L * MLDSA_POLYETA_PACKEDBYTES))
568
+ requires(memory_no_alias(y->rhoprime, MLDSA_CRHBYTES))
569
+ requires(y->kappa <= MLD_MAX_KAPPA)
570
+ )
571
+ assigns(memory_slice(sig, MLDSA_CRYPTO_BYTES))
572
+ assigns(memory_slice(z, sizeof(mld_poly)))
573
+ assigns(memory_slice(tmp, sizeof(mld_poly)))
574
+ ensures(return_value == 0 || return_value == MLD_ERR_FAIL ||
575
+ return_value == MLD_ERR_OUT_OF_MEMORY)
576
+ )
577
+ {
578
+ unsigned int i;
579
+ uint32_t z_invalid;
580
+ for (i = 0; i < MLDSA_L; i++)
581
+ __loop__(
582
+ assigns(i, memory_slice(z, sizeof(mld_poly)),
583
+ memory_slice(tmp, sizeof(mld_poly)),
584
+ memory_slice(sig, MLDSA_CRYPTO_BYTES))
585
+ invariant(i <= MLDSA_L)
586
+ decreases(MLDSA_L - i)
587
+ )
588
+ {
589
+ mld_sk_s1hat_get_poly(z, s1hat, i);
590
+ mld_poly_pointwise_montgomery(z, cp);
591
+ mld_poly_invntt_tomont(z);
592
+ mld_yvec_get_poly(tmp, y, i);
593
+ mld_poly_add(z, tmp);
594
+ mld_poly_reduce(z);
595
+
596
+ z_invalid = mld_poly_chknorm(z, MLDSA_GAMMA1 - MLDSA_BETA);
597
+ /* Constant time: It is fine (and prohibitively expensive to avoid)
598
+ * to leak the result of the norm check and which polynomial in z caused a
599
+ * rejection. It would even be okay to leak which coefficient led to
600
+ * rejection as the candidate signature will be discarded anyway.
601
+ * See Section 5.5 of @[Round3_Spec]. */
602
+ MLD_CT_TESTING_DECLASSIFY(&z_invalid, sizeof(uint32_t));
603
+ if (z_invalid)
604
+ {
605
+ return MLD_ERR_FAIL; /* reject */
606
+ }
607
+ /* If z is valid, then its coefficients are bounded by
608
+ * MLDSA_GAMMA1 - MLDSA_BETA. This will be needed below
609
+ * to prove the pre-condition of pack_sig_z() */
610
+ mld_assert_abs_bound(z, MLDSA_N, (MLDSA_GAMMA1 - MLDSA_BETA));
611
+
612
+ /* After the norm check, the distribution of each coefficient of z is
613
+ * independent of the secret key and it can, hence, be considered
614
+ * public. It is, hence, okay to immediately pack it into the user-provided
615
+ * signature buffer. */
616
+ mld_pack_sig_z(sig, z, i);
617
+ }
618
+ return 0;
619
+ }
620
+
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)
626
+
627
+ #if !defined(MLD_ALLOW_NONCOMPLIANT_SIGNING_BOUND) && \
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]
630
+ #endif
631
+
632
+ #if MLD_CONFIG_MAX_SIGNING_ATTEMPTS < 1
633
+ #error Bad configuration: MLD_CONFIG_MAX_SIGNING_ATTEMPTS must be >= 1
634
+ #endif
635
+
636
+ #if MLD_CONFIG_MAX_SIGNING_ATTEMPTS > MLD_MAX_KAPPA / MLDSA_L
637
+ #error Bad configuration: MLD_CONFIG_MAX_SIGNING_ATTEMPTS exceeds the maximum allowed value.
638
+ #endif
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
+
645
+ MLD_MUST_CHECK_RETURN_VALUE
646
+ static MLD_INLINE uint16_t mld_get_max_signing_attempts(void)
647
+ __contract__(
648
+ ensures(return_value >= 1)
649
+ ensures(return_value <= MLD_MAX_KAPPA / MLDSA_L)
650
+ )
651
+ {
652
+ /* cassert(0) ensures CBMC uses the contract rather than inlining the body,
653
+ * keeping proofs agnostic of the configured value. */
654
+ cassert(0);
655
+ return MLD_MAX_SIGNING_ATTEMPTS;
656
+ }
657
+
658
+ /**
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.}
669
+ *
670
+ * @reference{This code differs from the reference implementation in that it
671
+ * factors out the core signature generation step into a distinct function
672
+ * here in order to improve efficiency of CBMC proof.}
673
+ *
674
+ * @param[out] sig Pointer to output signature.
675
+ * @param[in] mu Pointer to message or hash of exactly MLDSA_CRHBYTES
676
+ * bytes.
677
+ * @param[in] rhoprime Pointer to randomness seed.
678
+ * @param kappa Counter for this iteration (= attempt*MLDSA_L).
679
+ * @param[in] mat Expanded matrix.
680
+ * @param[in] s1hat Secret vector s1 in NTT domain.
681
+ * @param[in] s2hat Secret vector s2 in NTT domain.
682
+ * @param[in] t0hat Vector t0 in NTT domain.
683
+ * @param context Application context. Only present when
684
+ * MLD_CONFIG_CONTEXT_PARAMETER is defined; type set by
685
+ * MLD_CONFIG_CONTEXT_PARAMETER_TYPE.
686
+ *
687
+ * @return - 0: Signature generation succeeded.
688
+ * - MLD_ERR_FAIL: Signature rejected (norm check failed).
689
+ * - MLD_ERR_OUT_OF_MEMORY: If MLD_CONFIG_CUSTOM_ALLOC_FREE is used and
690
+ * an allocation via MLD_CUSTOM_ALLOC returned NULL.
691
+ */
692
+ MLD_MUST_CHECK_RETURN_VALUE
693
+ /* NOLINTNEXTLINE(readability-function-cognitive-complexity) */
694
+ static int mld_attempt_signature_generation(
695
+ uint8_t sig[MLDSA_CRYPTO_BYTES], const uint8_t *mu,
696
+ const uint8_t rhoprime[MLDSA_CRHBYTES], uint16_t kappa, mld_polymat *mat,
697
+ const mld_sk_s1hat *s1hat, const mld_sk_s2hat *s2hat,
698
+ const mld_sk_t0hat *t0hat, MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
699
+ __contract__(
700
+ requires(memory_no_alias(sig, MLDSA_CRYPTO_BYTES))
701
+ requires(memory_no_alias(mu, MLDSA_CRHBYTES))
702
+ requires(memory_no_alias(rhoprime, MLDSA_CRHBYTES))
703
+ requires(memory_no_alias(mat, sizeof(mld_polymat)))
704
+ requires(memory_no_alias(s1hat, sizeof(mld_sk_s1hat)))
705
+ requires(memory_no_alias(s2hat, sizeof(mld_sk_s2hat)))
706
+ requires(memory_no_alias(t0hat, sizeof(mld_sk_t0hat)))
707
+ requires(kappa <= MLD_MAX_KAPPA)
708
+ MLD_IF_NOT_REDUCE_RAM(
709
+ requires(forall(k1, 0, MLDSA_K, forall(l1, 0, MLDSA_L,
710
+ array_bound(mat->vec[k1].vec[l1].coeffs, 0, MLDSA_N, 0, MLDSA_Q))))
711
+ requires(forall(k2, 0, MLDSA_K, array_abs_bound(t0hat->vec.vec[k2].coeffs, 0, MLDSA_N, MLD_NTT_BOUND)))
712
+ requires(forall(k3, 0, MLDSA_L, array_abs_bound(s1hat->vec.vec[k3].coeffs, 0, MLDSA_N, MLD_NTT_BOUND)))
713
+ requires(forall(k4, 0, MLDSA_K, array_abs_bound(s2hat->vec.vec[k4].coeffs, 0, MLDSA_N, MLD_NTT_BOUND)))
714
+ )
715
+ MLD_IF_REDUCE_RAM(
716
+ requires(memory_no_alias(s1hat->packed, MLDSA_L * MLDSA_POLYETA_PACKEDBYTES))
717
+ requires(memory_no_alias(s2hat->packed, MLDSA_K * MLDSA_POLYETA_PACKEDBYTES))
718
+ requires(memory_no_alias(t0hat->packed, MLDSA_K * MLDSA_POLYT0_PACKEDBYTES))
719
+ )
720
+ assigns(memory_slice(sig, MLDSA_CRYPTO_BYTES))
721
+ MLD_IF_REDUCE_RAM(
722
+ assigns(memory_slice(mat, sizeof(mld_polymat)))
723
+ )
724
+ ensures(return_value == 0 || return_value == MLD_ERR_FAIL ||
725
+ return_value == MLD_ERR_OUT_OF_MEMORY)
726
+ )
727
+ {
728
+ unsigned int k;
729
+ uint32_t w0_invalid, h_invalid;
730
+ int ret;
731
+
732
+ typedef union
733
+ {
734
+ mld_polyveck w1;
735
+ mld_polyvecl tmp;
736
+ } w1tmp_u;
737
+ mld_polyveck *w1;
738
+ mld_polyvecl *tmp;
739
+
740
+ MLD_ALLOC(challenge_bytes, uint8_t, MLDSA_CTILDEBYTES, context);
741
+ MLD_ALLOC(y, mld_yvec, 1, context);
742
+ MLD_ALLOC(z, mld_poly, 1, context);
743
+ MLD_ALLOC(w1tmp, w1tmp_u, 1, context);
744
+ MLD_ALLOC(w0, mld_polyveck, 1, context);
745
+ MLD_ALLOC(cp, mld_poly, 1, context);
746
+ MLD_ALLOC(t, mld_poly, 1, context);
747
+
748
+ if (challenge_bytes == NULL || y == NULL || z == NULL || w1tmp == NULL ||
749
+ w0 == NULL || cp == NULL || t == NULL)
750
+ {
751
+ ret = MLD_ERR_OUT_OF_MEMORY;
752
+ goto cleanup;
753
+ }
754
+ w1 = &w1tmp->w1;
755
+ tmp = &w1tmp->tmp;
756
+
757
+ /* @[FIPS204, Algorithm 7, line 11] y <- ExpandMask(rhoprime, kappa). */
758
+ mld_yvec_init(y, rhoprime, kappa);
759
+
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. */
764
+ mld_polyvec_matrix_pointwise_montgomery_yvec(w0, mat, y, tmp);
765
+
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). */
769
+ mld_polyveck_caddq(w0);
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. */
775
+ mld_polyveck_pack_w1(sig, w1);
776
+
777
+ mld_H(challenge_bytes, MLDSA_CTILDEBYTES, mu, MLDSA_CRHBYTES, sig,
778
+ MLDSA_K * MLDSA_POLYW1_PACKEDBYTES, NULL, 0);
779
+ /* Constant time: Leaking challenge_bytes does not reveal any information
780
+ * about the secret key as H() is modelled as random oracle.
781
+ * This also applies to challenges for rejected signatures.
782
+ * See Section 5.5 of @[Round3_Spec]. */
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). */
786
+ mld_poly_challenge(cp, challenge_bytes);
787
+ mld_poly_ntt(cp);
788
+
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. */
793
+ ret = mld_compute_pack_z(sig, cp, s1hat, y, t, z);
794
+ if (ret != 0)
795
+ {
796
+ goto cleanup;
797
+ }
798
+
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. */
834
+ for (k = 0; k < MLDSA_K; k++)
835
+ __loop__(
836
+ assigns(k,
837
+ object_whole(z),
838
+ object_whole(w0))
839
+ invariant(k <= MLDSA_K)
840
+ invariant(forall(k0, k, MLDSA_K,
841
+ array_abs_bound(w0->vec[k0].coeffs, 0, MLDSA_N, MLDSA_GAMMA2 + 1)))
842
+ decreases(MLDSA_K - k)
843
+ )
844
+ {
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]. */
847
+ mld_sk_s2hat_get_poly(z, s2hat, k);
848
+ mld_poly_pointwise_montgomery(z, cp);
849
+ mld_poly_invntt_tomont(z);
850
+
851
+ mld_poly_sub(&w0->vec[k], z);
852
+ mld_poly_reduce(&w0->vec[k]);
853
+
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. */
856
+ w0_invalid = mld_poly_chknorm(&w0->vec[k], MLDSA_GAMMA2 - MLDSA_BETA);
857
+ /* Constant time: w0_invalid may be leaked - see comment for z_invalid. */
858
+ MLD_CT_TESTING_DECLASSIFY(&w0_invalid, sizeof(uint32_t));
859
+ if (w0_invalid)
860
+ {
861
+ ret = MLD_ERR_FAIL; /* reject */
862
+ goto cleanup;
863
+ }
864
+
865
+ /* @[FIPS204, Algorithm 7, line 25] ct0[k] <- invNTT(c_hat o t0_hat)[k]. */
866
+ mld_sk_t0hat_get_poly(z, t0hat, k);
867
+ mld_poly_pointwise_montgomery(z, cp);
868
+ mld_poly_invntt_tomont(z);
869
+ mld_poly_reduce(z);
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). */
873
+ h_invalid = mld_poly_chknorm(z, MLDSA_GAMMA2);
874
+ /* Constant time: h_invalid may be leaked - see comment for z_invalid. */
875
+ MLD_CT_TESTING_DECLASSIFY(&h_invalid, sizeof(uint32_t));
876
+ if (h_invalid)
877
+ {
878
+ ret = MLD_ERR_FAIL; /* reject */
879
+ goto cleanup;
880
+ }
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). */
884
+ mld_poly_add(&w0->vec[k], z);
885
+ }
886
+
887
+ /* Constant time: At this point all norm checks have passed and we, hence,
888
+ * know that the signature does not leak any secret information.
889
+ * Consequently, any value that can be computed from the signature and public
890
+ * key is considered public.
891
+ * w0 and w1 are public as they can be computed from Az - ct = \alpha w1 + w0.
892
+ * h=c*t0 is public as both c and t0 are considered public.
893
+ * While t0 is not part of the public key, it can be reconstructed from
894
+ * a small number of signatures and need not be regarded as secret
895
+ * (see @[FIPS204, Section 6.1]).
896
+ */
897
+ MLD_CT_TESTING_DECLASSIFY(w0, sizeof(*w0));
898
+ MLD_CT_TESTING_DECLASSIFY(w1, sizeof(*w1));
899
+
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. */
903
+ mld_pack_sig_c(sig, challenge_bytes);
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. */
910
+ ret = mld_pack_sig_h(sig, w0, w1);
911
+ if (ret != 0)
912
+ {
913
+ goto cleanup;
914
+ }
915
+
916
+ /* Constant time: At this point it is clear that the signature is valid - it
917
+ * can, hence, be considered public. */
918
+ MLD_CT_TESTING_DECLASSIFY(sig, MLDSA_CRYPTO_BYTES);
919
+ ret = 0; /* success */
920
+
921
+ cleanup:
922
+ /* @[FIPS204, Section 3.6.3] Destruction of intermediate values. */
923
+ MLD_FREE(t, mld_poly, 1, context);
924
+ MLD_FREE(cp, mld_poly, 1, context);
925
+ MLD_FREE(w0, mld_polyveck, 1, context);
926
+ MLD_FREE(w1tmp, w1tmp_u, 1, context);
927
+ MLD_FREE(z, mld_poly, 1, context);
928
+ MLD_FREE(y, mld_yvec, 1, context);
929
+ MLD_FREE(challenge_bytes, uint8_t, MLDSA_CTILDEBYTES, context);
930
+
931
+ return ret;
932
+ }
933
+ MLD_MUST_CHECK_RETURN_VALUE
934
+ MLD_EXTERNAL_API
935
+ int mld_sign_signature_internal(uint8_t sig[MLDSA_CRYPTO_BYTES],
936
+ const uint8_t *m, size_t mlen,
937
+ const uint8_t *pre, size_t prelen,
938
+ const uint8_t rnd[MLDSA_RNDBYTES],
939
+ const uint8_t sk[MLDSA_CRYPTO_SECRETKEYBYTES],
940
+ int externalmu,
941
+ MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
942
+ {
943
+ int ret;
944
+ uint8_t *rho, *tr, *key, *mu, *rhoprime;
945
+ uint16_t attempt;
946
+ const uint16_t max_signing_attempts = mld_get_max_signing_attempts();
947
+ MLD_ALLOC(seedbuf, uint8_t,
948
+ 2 * MLDSA_SEEDBYTES + MLDSA_TRBYTES + 2 * MLDSA_CRHBYTES, context);
949
+ MLD_ALLOC(mat, mld_polymat, 1, context);
950
+ MLD_ALLOC(s1hat, mld_sk_s1hat, 1, context);
951
+ MLD_ALLOC(t0hat, mld_sk_t0hat, 1, context);
952
+ MLD_ALLOC(s2hat, mld_sk_s2hat, 1, context);
953
+
954
+ if (seedbuf == NULL || mat == NULL || s1hat == NULL || t0hat == NULL ||
955
+ s2hat == NULL)
956
+ {
957
+ ret = MLD_ERR_OUT_OF_MEMORY;
958
+ goto cleanup;
959
+ }
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
+
970
+ rho = seedbuf;
971
+ tr = rho + MLDSA_SEEDBYTES;
972
+ key = tr + MLDSA_TRBYTES;
973
+ mu = key + MLDSA_SEEDBYTES;
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. */
979
+ mld_unpack_sk(rho, tr, key, t0hat, s1hat, s2hat, sk);
980
+
981
+ if (!externalmu)
982
+ {
983
+ /* @[FIPS204, Algorithm 7, line 6] mu <- H(BytesToBits(tr) || M', 64). */
984
+ mld_H(mu, MLDSA_CRHBYTES, tr, MLDSA_TRBYTES, pre, prelen, m, mlen);
985
+ }
986
+ else
987
+ {
988
+ /* mu has been provided directly (external-mu variant; line 6 done by the
989
+ * caller in a separate cryptographic module). */
990
+ mld_memcpy(mu, m, MLDSA_CRHBYTES);
991
+ }
992
+
993
+ /* @[FIPS204, Algorithm 7, line 7] rhoprime <- H(K || rnd || mu, 64). */
994
+ mld_H(rhoprime, MLDSA_CRHBYTES, key, MLDSA_SEEDBYTES, rnd, MLDSA_RNDBYTES, mu,
995
+ MLDSA_CRHBYTES);
996
+
997
+ /* Constant time: rho is part of the public key and, hence, public. */
998
+ MLD_CT_TESTING_DECLASSIFY(rho, MLDSA_SEEDBYTES);
999
+ /* @[FIPS204, Algorithm 7, line 5] A_hat <- ExpandA(rho). */
1000
+ mld_polyvec_matrix_expand(mat, rho);
1001
+
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++)
1013
+ __loop__(
1014
+ MLD_IF_NOT_REDUCE_RAM(
1015
+ assigns(attempt, ret, memory_slice(sig, MLDSA_CRYPTO_BYTES))
1016
+ )
1017
+ MLD_IF_REDUCE_RAM(
1018
+ assigns(attempt, ret, memory_slice(sig, MLDSA_CRYPTO_BYTES),
1019
+ memory_slice(mat, sizeof(mld_polymat)))
1020
+ )
1021
+ invariant(attempt <= max_signing_attempts)
1022
+
1023
+ /* t0, s1, s2, and mat are initialized above and are NOT changed by this */
1024
+ /* loop. We can therefore re-assert their bounds here as part of the */
1025
+ /* loop invariant. This makes proof noticeably faster with CBMC */
1026
+ MLD_IF_NOT_REDUCE_RAM(
1027
+ invariant(forall(k1, 0, MLDSA_K, forall(l1, 0, MLDSA_L,
1028
+ array_bound(mat->vec[k1].vec[l1].coeffs, 0, MLDSA_N, 0, MLDSA_Q))))
1029
+ invariant(forall(k2, 0, MLDSA_K, array_abs_bound(t0hat->vec.vec[k2].coeffs, 0, MLDSA_N, MLD_NTT_BOUND)))
1030
+ invariant(forall(k3, 0, MLDSA_L, array_abs_bound(s1hat->vec.vec[k3].coeffs, 0, MLDSA_N, MLD_NTT_BOUND)))
1031
+ invariant(forall(k4, 0, MLDSA_K, array_abs_bound(s2hat->vec.vec[k4].coeffs, 0, MLDSA_N, MLD_NTT_BOUND)))
1032
+ )
1033
+ decreases(max_signing_attempts - attempt)
1034
+ )
1035
+ {
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)
1044
+ {
1045
+ ret = MLD_ERR_SIGNING_PAUSED;
1046
+ goto cleanup;
1047
+ }
1048
+
1049
+ ret = mld_attempt_signature_generation(sig, mu, rhoprime, kappa, mat, s1hat,
1050
+ s2hat, t0hat, context);
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. */
1059
+ if (ret == 0)
1060
+ {
1061
+ /* Signing succeeded: record the attempt that succeeded. No-op in the
1062
+ * default build. */
1063
+ mld_sign_finish(attempt, context);
1064
+ goto cleanup;
1065
+ }
1066
+ if (ret != MLD_ERR_FAIL)
1067
+ {
1068
+ goto cleanup;
1069
+ }
1070
+ }
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
+
1077
+ cleanup:
1078
+
1079
+ if (ret != 0)
1080
+ {
1081
+ /* To be on the safe-side, we zeroize the signature buffer. */
1082
+ mld_zeroize(sig, MLDSA_CRYPTO_BYTES);
1083
+ }
1084
+
1085
+ /* @[FIPS204, Section 3.6.3] Destruction of intermediate values. */
1086
+ MLD_FREE(s2hat, mld_sk_s2hat, 1, context);
1087
+ MLD_FREE(t0hat, mld_sk_t0hat, 1, context);
1088
+ MLD_FREE(s1hat, mld_sk_s1hat, 1, context);
1089
+ MLD_FREE(mat, mld_polymat, 1, context);
1090
+ MLD_FREE(seedbuf, uint8_t,
1091
+ 2 * MLDSA_SEEDBYTES + MLDSA_TRBYTES + 2 * MLDSA_CRHBYTES, context);
1092
+ return ret;
1093
+ }
1094
+
1095
+ #if !defined(MLD_CONFIG_CORE_API_ONLY)
1096
+ #if !defined(MLD_CONFIG_NO_RANDOMIZED_API)
1097
+ MLD_MUST_CHECK_RETURN_VALUE
1098
+ MLD_EXTERNAL_API
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,
1101
+ const uint8_t sk[MLDSA_CRYPTO_SECRETKEYBYTES],
1102
+ MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
1103
+ {
1104
+ size_t pre_len;
1105
+ int ret;
1106
+ MLD_ALLOC(pre, uint8_t, MLD_DOMAIN_SEPARATION_MAX_BYTES, context);
1107
+ MLD_ALLOC(rnd, uint8_t, MLDSA_RNDBYTES, context);
1108
+
1109
+ if (pre == NULL || rnd == NULL)
1110
+ {
1111
+ ret = MLD_ERR_OUT_OF_MEMORY;
1112
+ goto cleanup;
1113
+ }
1114
+
1115
+ /* Prepare domain separation prefix for pure ML-DSA */
1116
+ pre_len = mld_prepare_domain_separation_prefix(pre, NULL, 0, ctx, ctxlen,
1117
+ MLD_PREHASH_NONE);
1118
+ if (pre_len == 0)
1119
+ {
1120
+ ret = MLD_ERR_INVALID_ARG;
1121
+ goto cleanup;
1122
+ }
1123
+
1124
+ /* Randomized variant of ML-DSA. If you need the deterministic variant,
1125
+ * call mld_sign_signature_internal directly with all-zero rnd. */
1126
+ if (mld_randombytes(rnd, MLDSA_RNDBYTES) != 0)
1127
+ {
1128
+ ret = MLD_ERR_RNG_FAIL;
1129
+ goto cleanup;
1130
+ }
1131
+ MLD_CT_TESTING_SECRET(rnd, MLDSA_RNDBYTES);
1132
+
1133
+ ret = mld_sign_signature_internal(sig, m, mlen, pre, pre_len, rnd, sk, 0,
1134
+ context);
1135
+
1136
+ cleanup:
1137
+ if (ret != 0)
1138
+ {
1139
+ /* To be on the safe-side, make sure sig has a well-defined value, even in
1140
+ * the case of error.
1141
+ *
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);
1146
+ }
1147
+
1148
+ /* @[FIPS204, Section 3.6.3] Destruction of intermediate values. */
1149
+ MLD_FREE(rnd, uint8_t, MLDSA_RNDBYTES, context);
1150
+ MLD_FREE(pre, uint8_t, MLD_DOMAIN_SEPARATION_MAX_BYTES, context);
1151
+
1152
+ return ret;
1153
+ }
1154
+ #endif /* !MLD_CONFIG_NO_RANDOMIZED_API */
1155
+
1156
+ #if !defined(MLD_CONFIG_NO_RANDOMIZED_API)
1157
+ MLD_MUST_CHECK_RETURN_VALUE
1158
+ MLD_EXTERNAL_API
1159
+ int mld_sign_signature_extmu(uint8_t sig[MLDSA_CRYPTO_BYTES],
1160
+ const uint8_t mu[MLDSA_CRHBYTES],
1161
+ const uint8_t sk[MLDSA_CRYPTO_SECRETKEYBYTES],
1162
+ MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
1163
+ {
1164
+ int ret;
1165
+ MLD_ALLOC(rnd, uint8_t, MLDSA_RNDBYTES, context);
1166
+
1167
+ if (rnd == NULL)
1168
+ {
1169
+ ret = MLD_ERR_OUT_OF_MEMORY;
1170
+ goto cleanup;
1171
+ }
1172
+
1173
+ /* Randomized variant of ML-DSA. If you need the deterministic variant,
1174
+ * call mld_sign_signature_internal directly with all-zero rnd. */
1175
+ if (mld_randombytes(rnd, MLDSA_RNDBYTES) != 0)
1176
+ {
1177
+ ret = MLD_ERR_RNG_FAIL;
1178
+ goto cleanup;
1179
+ }
1180
+ MLD_CT_TESTING_SECRET(rnd, MLDSA_RNDBYTES);
1181
+
1182
+ ret = mld_sign_signature_internal(sig, mu, MLDSA_CRHBYTES, NULL, 0, rnd, sk,
1183
+ 1, context);
1184
+
1185
+ cleanup:
1186
+ /* @[FIPS204, Section 3.6.3] Destruction of intermediate values. */
1187
+ MLD_FREE(rnd, uint8_t, MLDSA_RNDBYTES, context);
1188
+
1189
+ return ret;
1190
+ }
1191
+ #endif /* !MLD_CONFIG_NO_RANDOMIZED_API */
1192
+ #endif /* !MLD_CONFIG_CORE_API_ONLY */
1193
+ #endif /* !MLD_CONFIG_NO_SIGN_API */
1194
+
1195
+ #if !defined(MLD_CONFIG_NO_VERIFY_API)
1196
+ MLD_MUST_CHECK_RETURN_VALUE
1197
+ MLD_EXTERNAL_API
1198
+ int mld_sign_verify_internal(const uint8_t sig[MLDSA_CRYPTO_BYTES],
1199
+ const uint8_t *m, size_t mlen, const uint8_t *pre,
1200
+ size_t prelen,
1201
+ const uint8_t pk[MLDSA_CRYPTO_PUBLICKEYBYTES],
1202
+ int externalmu,
1203
+ MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
1204
+ {
1205
+ int ret, cmp;
1206
+ unsigned int i;
1207
+
1208
+ MLD_ALLOC(buf, uint8_t, (MLDSA_K * MLDSA_POLYW1_PACKEDBYTES), context);
1209
+ MLD_ALLOC(mu, uint8_t, MLDSA_CRHBYTES, context);
1210
+ MLD_ALLOC(c, uint8_t, MLDSA_CTILDEBYTES, context);
1211
+ MLD_ALLOC(c2, uint8_t, MLDSA_CTILDEBYTES, context);
1212
+ MLD_ALLOC(z, mld_polyvecl, 1, context);
1213
+ MLD_ALLOC(cp, mld_poly, 1, context);
1214
+ MLD_ALLOC(mat, mld_polymat, 1, context);
1215
+ MLD_ALLOC(w1, mld_poly, 1, context);
1216
+ MLD_ALLOC(tmp, mld_poly, 1, context);
1217
+
1218
+ if (buf == NULL || mu == NULL || c == NULL || c2 == NULL || z == NULL ||
1219
+ cp == NULL || mat == NULL || w1 == NULL || tmp == NULL)
1220
+ {
1221
+ ret = MLD_ERR_OUT_OF_MEMORY;
1222
+ goto cleanup;
1223
+ }
1224
+
1225
+ mld_memcpy(c, sig, MLDSA_CTILDEBYTES);
1226
+ mld_polyvecl_unpack_z(z, sig + MLDSA_SIG_Z_OFFSET);
1227
+
1228
+ /* mld_polyvecl_chknorm signals failure through a single non-zero error code
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. */
1231
+ if (mld_polyvecl_chknorm(z, MLDSA_GAMMA1 - MLDSA_BETA))
1232
+ {
1233
+ ret = MLD_ERR_INVALID_SIGNATURE;
1234
+ goto cleanup;
1235
+ }
1236
+
1237
+ if (!externalmu)
1238
+ {
1239
+ /* Compute CRH(H(rho, t1), pre, msg) */
1240
+ MLD_ALIGN uint8_t hpk[MLDSA_CRHBYTES];
1241
+ mld_H(hpk, MLDSA_TRBYTES, pk, MLDSA_CRYPTO_PUBLICKEYBYTES, NULL, 0, NULL,
1242
+ 0);
1243
+ mld_H(mu, MLDSA_CRHBYTES, hpk, MLDSA_TRBYTES, pre, prelen, m, mlen);
1244
+
1245
+ /* @[FIPS204, Section 3.6.3] Destruction of intermediate values. */
1246
+ mld_zeroize(hpk, sizeof(hpk));
1247
+ }
1248
+ else
1249
+ {
1250
+ /* mu has been provided directly */
1251
+ mld_memcpy(mu, m, MLDSA_CRHBYTES);
1252
+ }
1253
+
1254
+ /* Matrix-vector multiplication and per-row reconstruction of w1. */
1255
+ mld_polyvecl_ntt(z);
1256
+ mld_polyvec_matrix_expand(mat, pk);
1257
+ mld_poly_challenge(cp, c);
1258
+ mld_poly_ntt(cp);
1259
+
1260
+ for (i = 0; i < MLDSA_K; ++i)
1261
+ __loop__(
1262
+ assigns(MLD_IF_REDUCE_RAM(memory_slice(mat, sizeof(mld_polymat)),)
1263
+ i, ret,
1264
+ memory_slice(w1, sizeof(mld_poly)),
1265
+ memory_slice(tmp, sizeof(mld_poly)),
1266
+ memory_slice(buf, MLDSA_K * MLDSA_POLYW1_PACKEDBYTES)
1267
+ )
1268
+ invariant(i <= MLDSA_K)
1269
+ decreases(MLDSA_K - i)
1270
+ )
1271
+ {
1272
+ /* w1 = (A * z)_i in NTT domain */
1273
+ mld_polyvec_matrix_pointwise_montgomery_row(w1, mat, z, i);
1274
+
1275
+ /* tmp = c * t1_i * 2^d in NTT domain */
1276
+ mld_unpack_pk_t1(tmp, pk, i);
1277
+ mld_poly_shiftl(tmp);
1278
+ mld_poly_ntt(tmp);
1279
+ mld_poly_pointwise_montgomery(tmp, cp);
1280
+
1281
+ /* w1 = invNTT(w1 - c * t1_i * 2^d) */
1282
+ mld_poly_sub(w1, tmp);
1283
+ mld_poly_reduce(w1);
1284
+ mld_poly_invntt_tomont(w1);
1285
+ mld_poly_caddq(w1);
1286
+
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)
1290
+ {
1291
+ ret = MLD_ERR_INVALID_SIGNATURE;
1292
+ goto cleanup;
1293
+ }
1294
+
1295
+ /* w1 = use_hint(w1, tmp), then pack into buf[i] */
1296
+ mld_poly_use_hint(w1, tmp);
1297
+ mld_polyw1_pack(buf + i * MLDSA_POLYW1_PACKEDBYTES, w1);
1298
+ }
1299
+
1300
+ /* Call random oracle and verify challenge */
1301
+ mld_H(c2, MLDSA_CTILDEBYTES, mu, MLDSA_CRHBYTES, buf,
1302
+ MLDSA_K * MLDSA_POLYW1_PACKEDBYTES, NULL, 0);
1303
+
1304
+ cmp = mld_ct_memcmp(c, c2, MLDSA_CTILDEBYTES);
1305
+
1306
+ /* Declassify the result of the verification. */
1307
+ MLD_CT_TESTING_DECLASSIFY(&cmp, sizeof(cmp));
1308
+
1309
+ ret = cmp == 0 ? 0 : MLD_ERR_INVALID_SIGNATURE;
1310
+
1311
+ cleanup:
1312
+ /* @[FIPS204, Section 3.6.3] Destruction of intermediate values. */
1313
+ MLD_FREE(tmp, mld_poly, 1, context);
1314
+ MLD_FREE(w1, mld_poly, 1, context);
1315
+ MLD_FREE(mat, mld_polymat, 1, context);
1316
+ MLD_FREE(cp, mld_poly, 1, context);
1317
+ MLD_FREE(z, mld_polyvecl, 1, context);
1318
+ MLD_FREE(c2, uint8_t, MLDSA_CTILDEBYTES, context);
1319
+ MLD_FREE(c, uint8_t, MLDSA_CTILDEBYTES, context);
1320
+ MLD_FREE(mu, uint8_t, MLDSA_CRHBYTES, context);
1321
+ MLD_FREE(buf, uint8_t, (MLDSA_K * MLDSA_POLYW1_PACKEDBYTES), context);
1322
+ return ret;
1323
+ }
1324
+
1325
+ #if !defined(MLD_CONFIG_CORE_API_ONLY)
1326
+ MLD_MUST_CHECK_RETURN_VALUE
1327
+ MLD_EXTERNAL_API
1328
+ int mld_sign_verify(const uint8_t sig[MLDSA_CRYPTO_BYTES], const uint8_t *m,
1329
+ size_t mlen, const uint8_t *ctx, size_t ctxlen,
1330
+ const uint8_t pk[MLDSA_CRYPTO_PUBLICKEYBYTES],
1331
+ MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
1332
+ {
1333
+ MLD_ALIGN uint8_t pre[MLD_DOMAIN_SEPARATION_MAX_BYTES];
1334
+ size_t pre_len;
1335
+ int ret;
1336
+
1337
+ pre_len = mld_prepare_domain_separation_prefix(pre, NULL, 0, ctx, ctxlen,
1338
+ MLD_PREHASH_NONE);
1339
+ if (pre_len == 0)
1340
+ {
1341
+ ret = MLD_ERR_INVALID_ARG;
1342
+ goto cleanup;
1343
+ }
1344
+
1345
+ ret = mld_sign_verify_internal(sig, m, mlen, pre, pre_len, pk, 0, context);
1346
+
1347
+ cleanup:
1348
+ /* @[FIPS204, Section 3.6.3] Destruction of intermediate values. */
1349
+ mld_zeroize(pre, sizeof(pre));
1350
+
1351
+ return ret;
1352
+ }
1353
+
1354
+ MLD_MUST_CHECK_RETURN_VALUE
1355
+ MLD_EXTERNAL_API
1356
+ int mld_sign_verify_extmu(const uint8_t sig[MLDSA_CRYPTO_BYTES],
1357
+ const uint8_t mu[MLDSA_CRHBYTES],
1358
+ const uint8_t pk[MLDSA_CRYPTO_PUBLICKEYBYTES],
1359
+ MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
1360
+ {
1361
+ return mld_sign_verify_internal(sig, mu, MLDSA_CRHBYTES, NULL, 0, pk, 1,
1362
+ context);
1363
+ }
1364
+ #endif /* !MLD_CONFIG_CORE_API_ONLY */
1365
+ #endif /* !MLD_CONFIG_NO_VERIFY_API */
1366
+
1367
+ #if !defined(MLD_CONFIG_CORE_API_ONLY)
1368
+ #if !defined(MLD_CONFIG_NO_SIGN_API)
1369
+ MLD_MUST_CHECK_RETURN_VALUE
1370
+ MLD_EXTERNAL_API
1371
+ int mld_sign_signature_pre_hash_internal(
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],
1374
+ const uint8_t sk[MLDSA_CRYPTO_SECRETKEYBYTES], int hashalg,
1375
+ MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
1376
+ {
1377
+ MLD_ALIGN uint8_t pre[MLD_DOMAIN_SEPARATION_MAX_BYTES];
1378
+ size_t pre_len;
1379
+ int ret;
1380
+
1381
+ if (hashalg == MLD_PREHASH_NONE)
1382
+ {
1383
+ ret = MLD_ERR_INVALID_ARG;
1384
+ goto cleanup;
1385
+ }
1386
+
1387
+ pre_len = mld_prepare_domain_separation_prefix(pre, ph, phlen, ctx, ctxlen,
1388
+ hashalg);
1389
+ if (pre_len == 0)
1390
+ {
1391
+ ret = MLD_ERR_INVALID_ARG;
1392
+ goto cleanup;
1393
+ }
1394
+
1395
+ ret = mld_sign_signature_internal(sig, pre, pre_len, NULL, 0, rnd, sk, 0,
1396
+ context);
1397
+ cleanup:
1398
+ if (ret != 0)
1399
+ {
1400
+ /* To be on the safe-side, make sure sig has a well-defined value, even in
1401
+ * the case of error.
1402
+ *
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);
1407
+ }
1408
+
1409
+ /* @[FIPS204, Section 3.6.3] Destruction of intermediate values. */
1410
+ mld_zeroize(pre, sizeof(pre));
1411
+ return ret;
1412
+ }
1413
+ #endif /* !MLD_CONFIG_NO_SIGN_API */
1414
+
1415
+ #if !defined(MLD_CONFIG_NO_VERIFY_API)
1416
+ MLD_MUST_CHECK_RETURN_VALUE
1417
+ MLD_EXTERNAL_API
1418
+ int mld_sign_verify_pre_hash_internal(
1419
+ const uint8_t sig[MLDSA_CRYPTO_BYTES], const uint8_t *ph, size_t phlen,
1420
+ const uint8_t *ctx, size_t ctxlen,
1421
+ const uint8_t pk[MLDSA_CRYPTO_PUBLICKEYBYTES], int hashalg,
1422
+ MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
1423
+ {
1424
+ MLD_ALIGN uint8_t pre[MLD_DOMAIN_SEPARATION_MAX_BYTES];
1425
+ size_t pre_len;
1426
+ int ret;
1427
+
1428
+ if (hashalg == MLD_PREHASH_NONE)
1429
+ {
1430
+ ret = MLD_ERR_INVALID_ARG;
1431
+ goto cleanup;
1432
+ }
1433
+
1434
+ pre_len = mld_prepare_domain_separation_prefix(pre, ph, phlen, ctx, ctxlen,
1435
+ hashalg);
1436
+ if (pre_len == 0)
1437
+ {
1438
+ ret = MLD_ERR_INVALID_ARG;
1439
+ goto cleanup;
1440
+ }
1441
+
1442
+ ret = mld_sign_verify_internal(sig, pre, pre_len, NULL, 0, pk, 0, context);
1443
+
1444
+ cleanup:
1445
+ /* @[FIPS204, Section 3.6.3] Destruction of intermediate values. */
1446
+ mld_zeroize(pre, sizeof(pre));
1447
+ return ret;
1448
+ }
1449
+ #endif /* !MLD_CONFIG_NO_VERIFY_API */
1450
+
1451
+ #if !defined(MLD_CONFIG_NO_SIGN_API)
1452
+ MLD_MUST_CHECK_RETURN_VALUE
1453
+ MLD_EXTERNAL_API
1454
+ int mld_sign_signature_pre_hash_shake256(
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],
1457
+ const uint8_t sk[MLDSA_CRYPTO_SECRETKEYBYTES],
1458
+ MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
1459
+ {
1460
+ MLD_ALIGN uint8_t ph[64];
1461
+ int ret;
1462
+ mld_shake256(ph, sizeof(ph), m, mlen);
1463
+ ret = mld_sign_signature_pre_hash_internal(sig, ph, sizeof(ph), ctx, ctxlen,
1464
+ rnd, sk, MLD_PREHASH_SHAKE_256,
1465
+ context);
1466
+ /* @[FIPS204, Section 3.6.3] Destruction of intermediate values. */
1467
+ mld_zeroize(ph, sizeof(ph));
1468
+ return ret;
1469
+ }
1470
+ #endif /* !MLD_CONFIG_NO_SIGN_API */
1471
+
1472
+ #if !defined(MLD_CONFIG_NO_VERIFY_API)
1473
+ MLD_MUST_CHECK_RETURN_VALUE
1474
+ MLD_EXTERNAL_API
1475
+ int mld_sign_verify_pre_hash_shake256(
1476
+ const uint8_t sig[MLDSA_CRYPTO_BYTES], const uint8_t *m, size_t mlen,
1477
+ const uint8_t *ctx, size_t ctxlen,
1478
+ const uint8_t pk[MLDSA_CRYPTO_PUBLICKEYBYTES],
1479
+ MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
1480
+ {
1481
+ MLD_ALIGN uint8_t ph[64];
1482
+ int ret;
1483
+ mld_shake256(ph, sizeof(ph), m, mlen);
1484
+ ret = mld_sign_verify_pre_hash_internal(sig, ph, sizeof(ph), ctx, ctxlen, pk,
1485
+ MLD_PREHASH_SHAKE_256, context);
1486
+ /* @[FIPS204, Section 3.6.3] Destruction of intermediate values. */
1487
+ mld_zeroize(ph, sizeof(ph));
1488
+ return ret;
1489
+ }
1490
+ #endif /* !MLD_CONFIG_NO_VERIFY_API */
1491
+
1492
+ #if !defined(MLD_CONFIG_NO_SIGN_API) || !defined(MLD_CONFIG_NO_VERIFY_API)
1493
+ #define MLD_PRE_HASH_OID_LEN 11
1494
+
1495
+ /**
1496
+ * Return the OID of a given SHA-2/SHA-3 hash function.
1497
+ *
1498
+ * @param[out] oid Pointer to output OID.
1499
+ * @param hashalg Hash algorithm constant (MLD_PREHASH_*).
1500
+ */
1501
+ static void mld_get_hash_oid(uint8_t oid[MLD_PRE_HASH_OID_LEN], int hashalg)
1502
+ {
1503
+ unsigned int i;
1504
+ static const struct
1505
+ {
1506
+ int alg;
1507
+ uint8_t oid[MLD_PRE_HASH_OID_LEN];
1508
+ } oid_map[] = {
1509
+ {MLD_PREHASH_SHA2_224,
1510
+ {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04}},
1511
+ {MLD_PREHASH_SHA2_256,
1512
+ {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01}},
1513
+ {MLD_PREHASH_SHA2_384,
1514
+ {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02}},
1515
+ {MLD_PREHASH_SHA2_512,
1516
+ {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03}},
1517
+ {MLD_PREHASH_SHA2_512_224,
1518
+ {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x05}},
1519
+ {MLD_PREHASH_SHA2_512_256,
1520
+ {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x06}},
1521
+ {MLD_PREHASH_SHA3_224,
1522
+ {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x07}},
1523
+ {MLD_PREHASH_SHA3_256,
1524
+ {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x08}},
1525
+ {MLD_PREHASH_SHA3_384,
1526
+ {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x09}},
1527
+ {MLD_PREHASH_SHA3_512,
1528
+ {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0A}},
1529
+ {MLD_PREHASH_SHAKE_128,
1530
+ {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0B}},
1531
+ {MLD_PREHASH_SHAKE_256,
1532
+ {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0C}}};
1533
+
1534
+ for (i = 0; i < sizeof(oid_map) / sizeof(oid_map[0]); i++)
1535
+ __loop__(
1536
+ invariant(i <= sizeof(oid_map) / sizeof(oid_map[0]))
1537
+ decreases(sizeof(oid_map) / sizeof(oid_map[0]) - i)
1538
+ )
1539
+ {
1540
+ if (oid_map[i].alg == hashalg)
1541
+ {
1542
+ mld_memcpy(oid, oid_map[i].oid, MLD_PRE_HASH_OID_LEN);
1543
+ return;
1544
+ }
1545
+ }
1546
+ }
1547
+
1548
+ static int mld_validate_hash_length(int hashalg, size_t len)
1549
+ {
1550
+ switch (hashalg)
1551
+ {
1552
+ case MLD_PREHASH_SHA2_224:
1553
+ return (len == 224 / 8) ? 0 : -1;
1554
+ case MLD_PREHASH_SHA2_256:
1555
+ return (len == 256 / 8) ? 0 : -1;
1556
+ case MLD_PREHASH_SHA2_384:
1557
+ return (len == 384 / 8) ? 0 : -1;
1558
+ case MLD_PREHASH_SHA2_512:
1559
+ return (len == 512 / 8) ? 0 : -1;
1560
+ case MLD_PREHASH_SHA2_512_224:
1561
+ return (len == 224 / 8) ? 0 : -1;
1562
+ case MLD_PREHASH_SHA2_512_256:
1563
+ return (len == 256 / 8) ? 0 : -1;
1564
+ case MLD_PREHASH_SHA3_224:
1565
+ return (len == 224 / 8) ? 0 : -1;
1566
+ case MLD_PREHASH_SHA3_256:
1567
+ return (len == 256 / 8) ? 0 : -1;
1568
+ case MLD_PREHASH_SHA3_384:
1569
+ return (len == 384 / 8) ? 0 : -1;
1570
+ case MLD_PREHASH_SHA3_512:
1571
+ return (len == 512 / 8) ? 0 : -1;
1572
+ case MLD_PREHASH_SHAKE_128:
1573
+ return (len == 256 / 8) ? 0 : -1;
1574
+ case MLD_PREHASH_SHAKE_256:
1575
+ return (len == 512 / 8) ? 0 : -1;
1576
+ default:
1577
+ return -1;
1578
+ }
1579
+ }
1580
+
1581
+ MLD_EXTERNAL_API
1582
+ size_t mld_prepare_domain_separation_prefix(
1583
+ uint8_t prefix[MLD_DOMAIN_SEPARATION_MAX_BYTES], const uint8_t *ph,
1584
+ size_t phlen, const uint8_t *ctx, size_t ctxlen, int hashalg)
1585
+ {
1586
+ if (ctxlen > 255)
1587
+ {
1588
+ return 0;
1589
+ }
1590
+
1591
+ if (hashalg != MLD_PREHASH_NONE)
1592
+ {
1593
+ if (ph == NULL || mld_validate_hash_length(hashalg, phlen) != 0)
1594
+ {
1595
+ return 0;
1596
+ }
1597
+ }
1598
+
1599
+ /* Common prefix: 0x00/0x01 || ctxlen || ctx */
1600
+ prefix[0] = (hashalg == MLD_PREHASH_NONE) ? 0 : 1;
1601
+ prefix[1] = (uint8_t)ctxlen;
1602
+ if (ctxlen > 0)
1603
+ {
1604
+ mld_memcpy(prefix + 2, ctx, ctxlen);
1605
+ }
1606
+
1607
+ if (hashalg == MLD_PREHASH_NONE)
1608
+ {
1609
+ return 2 + ctxlen;
1610
+ }
1611
+
1612
+ /* HashML-DSA: append oid || ph */
1613
+ mld_get_hash_oid(prefix + 2 + ctxlen, hashalg);
1614
+ mld_memcpy(prefix + 2 + ctxlen + MLD_PRE_HASH_OID_LEN, ph, phlen);
1615
+ return 2 + ctxlen + MLD_PRE_HASH_OID_LEN + phlen;
1616
+ }
1617
+ #endif /* !MLD_CONFIG_NO_SIGN_API || !MLD_CONFIG_NO_VERIFY_API */
1618
+
1619
+ #if !defined(MLD_CONFIG_NO_KEYPAIR_API)
1620
+ MLD_EXTERNAL_API
1621
+ int mld_sign_pk_from_sk(uint8_t pk[MLDSA_CRYPTO_PUBLICKEYBYTES],
1622
+ const uint8_t sk[MLDSA_CRYPTO_SECRETKEYBYTES],
1623
+ MLD_CONFIG_CONTEXT_PARAMETER_TYPE context)
1624
+ {
1625
+ uint8_t check, cmp0, cmp1, chk1, chk2;
1626
+ int ret;
1627
+ MLD_ALLOC(rho, uint8_t, MLDSA_SEEDBYTES, context);
1628
+ MLD_ALLOC(tr, uint8_t, MLDSA_TRBYTES, context);
1629
+ MLD_ALLOC(tr_computed, uint8_t, MLDSA_TRBYTES, context);
1630
+ MLD_ALLOC(key, uint8_t, MLDSA_SEEDBYTES, context);
1631
+ MLD_ALLOC(s1, mld_polyvecl, 1, context);
1632
+ MLD_ALLOC(s2, mld_polyveck, 1, context);
1633
+ MLD_ALLOC(t0_packed, uint8_t, MLDSA_K *MLDSA_POLYT0_PACKEDBYTES, context);
1634
+
1635
+ if (rho == NULL || tr == NULL || tr_computed == NULL || key == NULL ||
1636
+ s1 == NULL || s2 == NULL || t0_packed == NULL)
1637
+ {
1638
+ ret = MLD_ERR_OUT_OF_MEMORY;
1639
+ goto cleanup;
1640
+ }
1641
+
1642
+ /* Inline unpack_sk: mld_unpack_sk uses lazy types for s1/s2/t0 which
1643
+ * we cannot use here. t0 stays in packed form -- we compare it against
1644
+ * the recomputed value below. */
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);
1650
+
1651
+ /* Validate s1 and s2 coefficients are within [-MLDSA_ETA, MLDSA_ETA] */
1652
+ chk1 = mld_polyvecl_chknorm(s1, MLDSA_ETA + 1) & 0xFF;
1653
+ chk2 = mld_polyveck_chknorm(s2, MLDSA_ETA + 1) & 0xFF;
1654
+
1655
+ /* NTT s1 in place to use as s1hat */
1656
+ mld_polyvecl_ntt(s1);
1657
+
1658
+ /* Pack rho into pk */
1659
+ mld_memcpy(pk + MLDSA_PK_RHO_OFFSET, rho, MLDSA_SEEDBYTES);
1660
+
1661
+ /* Recompute t row by row, decompose, and pack t1 into pk and t0 into
1662
+ * t0_packed. */
1663
+ ret = mld_compute_pack_t0_t1(pk + MLDSA_PK_T1_OFFSET, t0_packed, s1, s2, rho,
1664
+ context);
1665
+ if (ret != 0)
1666
+ {
1667
+ goto cleanup;
1668
+ }
1669
+
1670
+ /* Compare recomputed packed t0 against the t0 region of sk. */
1671
+ cmp0 = mld_ct_memcmp(t0_packed, sk + MLDSA_SK_T0_OFFSET,
1672
+ MLDSA_K * MLDSA_POLYT0_PACKEDBYTES);
1673
+
1674
+ /* Compute tr_computed = H(pk) and compare to the stored tr */
1675
+ mld_shake256(tr_computed, MLDSA_TRBYTES, pk, MLDSA_CRYPTO_PUBLICKEYBYTES);
1676
+ cmp1 = mld_ct_memcmp((const uint8_t *)tr, (const uint8_t *)tr_computed,
1677
+ MLDSA_TRBYTES);
1678
+ check = mld_value_barrier_u8(cmp0 | cmp1 | chk1 | chk2);
1679
+
1680
+ /* Declassify the final result of the validity check. */
1681
+ MLD_CT_TESTING_DECLASSIFY(&check, sizeof(check));
1682
+ ret = (check != 0) ? MLD_ERR_INVALID_KEY : 0;
1683
+
1684
+ cleanup:
1685
+
1686
+ if (ret != 0)
1687
+ {
1688
+ mld_zeroize(pk, MLDSA_CRYPTO_PUBLICKEYBYTES);
1689
+ }
1690
+
1691
+ /* Constant time: pk is either the valid public key or zeroed on error */
1692
+ MLD_CT_TESTING_DECLASSIFY(pk, MLDSA_CRYPTO_PUBLICKEYBYTES);
1693
+
1694
+ /* @[FIPS204, Section 3.6.3] Destruction of intermediate values. */
1695
+ MLD_FREE(t0_packed, uint8_t, MLDSA_K *MLDSA_POLYT0_PACKEDBYTES, context);
1696
+ MLD_FREE(s2, mld_polyveck, 1, context);
1697
+ MLD_FREE(s1, mld_polyvecl, 1, context);
1698
+ MLD_FREE(key, uint8_t, MLDSA_SEEDBYTES, context);
1699
+ MLD_FREE(tr_computed, uint8_t, MLDSA_TRBYTES, context);
1700
+ MLD_FREE(tr, uint8_t, MLDSA_TRBYTES, context);
1701
+ MLD_FREE(rho, uint8_t, MLDSA_SEEDBYTES, context);
1702
+
1703
+ return ret;
1704
+ }
1705
+ #endif /* !MLD_CONFIG_NO_KEYPAIR_API */
1706
+ #endif /* !MLD_CONFIG_CORE_API_ONLY */
1707
+
1708
+ /* To facilitate single-compilation-unit (SCU) builds, undefine all macros.
1709
+ * Don't modify by hand -- this is auto-generated by scripts/autogen. */
1710
+ #undef mld_check_pct
1711
+ #undef mld_sample_s1_s2
1712
+ #undef mld_validate_hash_length
1713
+ #undef mld_get_hash_oid
1714
+ #undef mld_H
1715
+ #undef mld_compute_pack_z
1716
+ #undef mld_attempt_signature_generation
1717
+ #undef mld_compute_pack_t0_t1
1718
+ #undef mld_get_max_signing_attempts
1719
+ #undef MLD_MAX_SIGNING_ATTEMPTS
1720
+ #undef MLD_PRE_HASH_OID_LEN