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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +88 -0
- data/ext/mldsa_gh_native/extconf.rb +14 -0
- data/ext/mldsa_gh_native/mldsa_gh_native.c +222 -0
- data/ext/mldsa_gh_native/mldsa_gh_native_all.c +24 -0
- data/ext/mldsa_gh_native/mldsa_gh_native_all.h +26 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/BUILDING.md +108 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/LICENSE +305 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/README.md +247 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/README.md +23 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/mldsa_native.c +803 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/mldsa_native.h +956 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/mldsa_native_asm.S +830 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/mldsa_native_config.h +855 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/cbmc.h +233 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/common.h +301 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/context.h +152 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/ct.c +21 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/ct.h +373 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/debug.c +75 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/debug.h +125 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/fips202.c +270 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/fips202.h +224 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/fips202x4.c +187 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/fips202x4.h +125 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/keccakf1600.c +510 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/keccakf1600.h +110 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/auto.h +85 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/fips202_native_aarch64.h +69 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccak_f1600_x1_scalar_aarch64_asm.S +378 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccak_f1600_x1_v84a_aarch64_asm.S +207 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccak_f1600_x2_v84a_aarch64_asm.S +262 -0
- 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
- 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
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/src/keccakf1600_round_constants.c +47 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x1_scalar.h +27 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x1_v84a.h +36 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x2_v84a.h +40 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x4_v8a_scalar.h +32 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/aarch64/x4_v8a_v84a_scalar.h +37 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/api.h +129 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/README.md +10 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/mve.h +67 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/src/fips202_native_armv81m.h +37 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/src/keccak_f1600_x4_mve.S +717 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/src/keccak_f1600_x4_mve.c +42 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/src/keccak_f1600_x4_state_extract_bytes_mve.S +334 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/src/keccak_f1600_x4_state_xor_bytes_mve.S +355 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/armv81m/src/keccakf1600_round_constants.c +53 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/auto.h +35 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/x86_64/keccak_f1600_x4_avx2.h +34 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/x86_64/src/fips202_native_x86_64.h +45 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/x86_64/src/keccak_f1600_x4_avx2_asm.S +488 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/fips202/native/x86_64/src/keccakf1600_constants.c +52 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/meta.h +314 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/aarch64_zetas.c +248 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/arith_native_aarch64.h +367 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_intt_aarch64_asm.S +786 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_ntt_aarch64_asm.S +686 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_pointwise_montgomery_aarch64_asm.S +106 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_poly_caddq_aarch64_asm.S +69 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_poly_chknorm_aarch64_asm.S +76 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_poly_decompose_32_aarch64_asm.S +108 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_poly_decompose_88_aarch64_asm.S +108 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_poly_use_hint_32_aarch64_asm.S +125 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_poly_use_hint_88_aarch64_asm.S +133 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_polyvecl_pointwise_acc_montgomery_l4_aarch64_asm.S +157 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_polyvecl_pointwise_acc_montgomery_l5_aarch64_asm.S +173 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_polyvecl_pointwise_acc_montgomery_l7_aarch64_asm.S +205 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_polyz_unpack_17_aarch64_asm.S +103 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_polyz_unpack_19_aarch64_asm.S +100 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_rej_uniform_aarch64_asm.S +222 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_rej_uniform_eta2_aarch64_asm.S +170 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/mldsa_rej_uniform_eta4_aarch64_asm.S +163 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/polyz_unpack_table.c +52 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/rej_uniform_eta_table.c +547 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/aarch64/src/rej_uniform_table.c +63 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/api.h +617 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/meta.h +24 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/meta.h +323 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/arith_native_x86_64.h +330 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/consts.c +157 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/consts.h +27 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_intt_avx2_asm.S +2333 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_ntt_avx2_asm.S +2405 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_nttunpack_avx2_asm.S +254 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_pointwise_acc_l4_avx2_asm.S +173 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_pointwise_acc_l5_avx2_asm.S +189 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_pointwise_acc_l7_avx2_asm.S +221 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_pointwise_avx2_asm.S +158 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_poly_caddq_avx2_asm.S +199 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_poly_chknorm_avx2_asm.S +176 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_poly_decompose_32_avx2_asm.S +490 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_poly_decompose_88_avx2_asm.S +489 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_poly_use_hint_32_avx2_asm.S +123 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_poly_use_hint_88_avx2_asm.S +125 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_polyz_unpack_17_avx2_asm.S +355 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_polyz_unpack_19_avx2_asm.S +355 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_rej_uniform_avx2_asm.S +132 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_rej_uniform_eta2_avx2_asm.S +205 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/mldsa_rej_uniform_eta4_avx2_asm.S +176 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/native/x86_64/src/rej_uniform_table.c +161 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/packing.c +213 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/packing.h +277 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/params.h +153 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/poly.c +1066 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/poly.h +464 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/poly_kl.c +910 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/poly_kl.h +367 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/polyvec.c +509 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/polyvec.h +435 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/polyvec_lazy.c +311 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/polyvec_lazy.h +652 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/randombytes.h +26 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/reduce.h +144 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/rounding.h +265 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/sign.c +1720 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/sign.h +850 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/symmetric.h +68 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/sys.h +327 -0
- data/ext/mldsa_gh_native/vendor/mldsa-native/mldsa/src/zetas.inc +55 -0
- data/lib/mldsa/parameter_set.rb +82 -0
- data/lib/mldsa/signing_key.rb +147 -0
- data/lib/mldsa/verify_key.rb +72 -0
- data/lib/mldsa/version.rb +7 -0
- data/lib/mldsa_gh.rb +93 -0
- 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
|