Skip to content

Commit d81546a

Browse files
committed
sign: Consolidate make_hint and pack_sig_h
Replace mld_poly_make_hint + the per-polynomial mld_pack_sig_h_poly with a single mld_pack_sig_h that takes (w0, w1) polyvecks directly, computes hint bits via mld_make_hint, and writes hint indices into sig in one pass over all K rows. The function returns MLD_ERR_FAIL if the total number of hints exceeds MLDSA_OMEGA, in which case the caller must reject the signature. This removes the duplicated hint counting (previously make_hint returned the count and pack iterated the hint poly again without re-validating), drops the temporary scratch hint polynomial from sign.c, and lets sign.c emit a single call instead of a K-loop with per-row tally bookkeeping. Signed-off-by: Matthias J. Kannwischer <matthias@zerorisc.com>
1 parent 2c5f4ad commit d81546a

12 files changed

Lines changed: 82 additions & 189 deletions

File tree

mldsa/mldsa_native.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@
261261
/* mldsa/src/packing.h */
262262
#undef MLD_PACKING_H
263263
#undef mld_pack_sig_c
264-
#undef mld_pack_sig_h_poly
264+
#undef mld_pack_sig_h
265265
#undef mld_pack_sig_z
266266
#undef mld_pack_sk_rho_key_tr_s2
267267
#undef mld_pack_sk_s1
@@ -301,7 +301,6 @@
301301
#undef MLD_POLY_KL_H
302302
#undef mld_poly_challenge
303303
#undef mld_poly_decompose
304-
#undef mld_poly_make_hint
305304
#undef mld_poly_uniform_eta
306305
#undef mld_poly_uniform_eta_4x
307306
#undef mld_poly_uniform_gamma1

mldsa/mldsa_native_asm.S

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@
266266
/* mldsa/src/packing.h */
267267
#undef MLD_PACKING_H
268268
#undef mld_pack_sig_c
269-
#undef mld_pack_sig_h_poly
269+
#undef mld_pack_sig_h
270270
#undef mld_pack_sig_z
271271
#undef mld_pack_sk_rho_key_tr_s2
272272
#undef mld_pack_sk_s1
@@ -306,7 +306,6 @@
306306
#undef MLD_POLY_KL_H
307307
#undef mld_poly_challenge
308308
#undef mld_poly_decompose
309-
#undef mld_poly_make_hint
310309
#undef mld_poly_uniform_eta
311310
#undef mld_poly_uniform_eta_4x
312311
#undef mld_poly_uniform_gamma1

mldsa/src/packing.c

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "packing.h"
99
#include "poly.h"
1010
#include "polyvec.h"
11+
#include "rounding.h"
1112

1213
/* Parameter set namespacing
1314
* This is to facilitate building multiple instances
@@ -90,10 +91,10 @@ void mld_pack_sig_c(uint8_t sig[MLDSA_CRYPTO_BYTES],
9091
}
9192

9293
MLD_INTERNAL_API
93-
void mld_pack_sig_h_poly(uint8_t sig[MLDSA_CRYPTO_BYTES], const mld_poly *h,
94-
unsigned int k, unsigned int n)
94+
int mld_pack_sig_h(uint8_t sig[MLDSA_CRYPTO_BYTES], const mld_polyveck *w0,
95+
const mld_polyveck *w1)
9596
{
96-
unsigned int j;
97+
unsigned int j, k, n;
9798

9899
/* The hint section of sig[] is MLDSA_POLYVECH_PACKEDBYTES long, where
99100
* MLDSA_POLYVECH_PACKEDBYTES = MLDSA_OMEGA + MLDSA_K.
@@ -102,32 +103,54 @@ void mld_pack_sig_h_poly(uint8_t sig[MLDSA_CRYPTO_BYTES], const mld_poly *h,
102103
* that are not equal to 0.
103104
*
104105
* The final K bytes record a running tally of the number of hints
105-
* coming from each of the K polynomials in h. */
106+
* coming from each of the K polynomials. */
106107
uint8_t *sig_h = sig + MLDSA_CTILDEBYTES + MLDSA_L * MLDSA_POLYZ_PACKEDBYTES;
107108

108-
/* For each coefficient in this polynomial, record it as a hint */
109-
/* if its value is not zero. */
110-
for (j = 0; j < MLDSA_N; j++)
109+
mld_memset(sig_h, 0, MLDSA_POLYVECH_PACKEDBYTES);
110+
n = 0;
111+
112+
/* For each coefficient of each polynomial, compute its hint bit and, if
113+
* non-zero, record the index in the hint section of sig. If recording the
114+
* hint would overflow the OMEGA-sized index array, abort early and return
115+
* MLD_ERR_FAIL. The caller is expected to reject the signature in that case.
116+
*
117+
* Constant time: At this point w0/w1 are public (see comment in sign.c
118+
* before the call), so a data-dependent early return is fine. */
119+
for (k = 0; k < MLDSA_K; k++)
111120
__loop__(
112-
assigns(j, n, memory_slice(sig_h, MLDSA_POLYVECH_PACKEDBYTES))
113-
invariant(j <= MLDSA_N)
114-
invariant(n <= MLDSA_OMEGA)
115-
decreases(MLDSA_N - j)
121+
assigns(k, j, n, memory_slice(sig_h, MLDSA_POLYVECH_PACKEDBYTES))
122+
invariant(k <= MLDSA_K && n <= MLDSA_OMEGA)
123+
decreases(MLDSA_K - k)
116124
)
117125
{
118-
/* The reference implementation implicitly relies on the total */
119-
/* number of hints being less than OMEGA, assuming h is valid. */
120-
/* In mldsa-native, we check this explicitly to ease proof of */
121-
/* type safety. */
122-
if (h->coeffs[j] != 0 && n < MLDSA_OMEGA)
126+
for (j = 0; j < MLDSA_N; j++)
127+
__loop__(
128+
assigns(j, n, memory_slice(sig_h, MLDSA_POLYVECH_PACKEDBYTES))
129+
invariant(j <= MLDSA_N && n <= MLDSA_OMEGA)
130+
decreases(MLDSA_N - j)
131+
)
123132
{
124-
sig_h[n] = (uint8_t)j;
125-
n++;
133+
const unsigned int hint_bit =
134+
mld_make_hint(w0->vec[k].coeffs[j], w1->vec[k].coeffs[j]);
135+
if (hint_bit)
136+
{
137+
if (n == MLDSA_OMEGA)
138+
{
139+
return MLD_ERR_FAIL;
140+
}
141+
/* Safety: branch above ensures n < MLDSA_OMEGA so n is a valid index
142+
* into the OMEGA-sized index array; j < MLDSA_N <= 256 fits in
143+
* uint8_t. */
144+
sig_h[n] = (uint8_t)j;
145+
n++;
146+
}
126147
}
148+
/* Record the running tally into the correct slot for this polynomial.
149+
* Safety: k < MLDSA_K, so MLDSA_OMEGA + k is a valid index into the
150+
* K-byte tally tail; n <= MLDSA_OMEGA fits in uint8_t. */
151+
sig_h[MLDSA_OMEGA + k] = (uint8_t)n;
127152
}
128-
/* Record the running tally into the correct slot for this */
129-
/* polynomial in the final K bytes. */
130-
sig_h[MLDSA_OMEGA + k] = (uint8_t)n;
153+
return 0;
131154
}
132155

133156
MLD_INTERNAL_API

mldsa/src/packing.h

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -81,29 +81,35 @@ __contract__(
8181
assigns(memory_slice(sig, MLDSA_CRYPTO_BYTES))
8282
);
8383

84-
#define mld_pack_sig_h_poly MLD_NAMESPACE_KL(pack_sig_h_poly)
84+
#define mld_pack_sig_h MLD_NAMESPACE_KL(pack_sig_h)
8585
/*************************************************
86-
* Name: mld_pack_sig_h_poly
86+
* Name: mld_pack_sig_h
8787
*
88-
* Description: Pack hints for one polynomial into the hint section of sig.
89-
* Must be called once per polynomial in order k = 0, ..., K-1.
90-
* The hint section of sig must be zeroed before the first call.
88+
* Description: Compute hints from (w0, w1) and pack them into the hint
89+
* section of sig.
9190
*
9291
* Arguments: - uint8_t sig[]: byte array containing signature
93-
* - const mld_poly *h: pointer to hint polynomial (0/1 coeffs)
94-
* - unsigned int k: index of polynomial in vector (0..K-1)
95-
* - unsigned int n: total number of hints written so far
92+
* - const mld_polyveck *w0: pointer to low part of input vector
93+
* - const mld_polyveck *w1: pointer to high part of input vector
94+
*
95+
* Returns: - 0 on success;
96+
* - MLD_ERR_FAIL if the total number of hints exceeds
97+
* MLDSA_OMEGA. In this case the hint section of sig is
98+
* left in a partially-written state and the caller must
99+
* reject the signature.
96100
**************************************************/
97101
MLD_INTERNAL_API
98-
void mld_pack_sig_h_poly(uint8_t sig[MLDSA_CRYPTO_BYTES], const mld_poly *h,
99-
unsigned int k, unsigned int n)
102+
MLD_MUST_CHECK_RETURN_VALUE
103+
int mld_pack_sig_h(uint8_t sig[MLDSA_CRYPTO_BYTES], const mld_polyveck *w0,
104+
const mld_polyveck *w1)
100105
__contract__(
101106
requires(memory_no_alias(sig, MLDSA_CRYPTO_BYTES))
102-
requires(memory_no_alias(h, sizeof(mld_poly)))
103-
requires(k < MLDSA_K)
104-
requires(n <= MLDSA_OMEGA)
105-
requires(array_bound(h->coeffs, 0, MLDSA_N, 0, 2))
106-
assigns(memory_slice(sig, MLDSA_CRYPTO_BYTES))
107+
requires(memory_no_alias(w0, sizeof(mld_polyveck)))
108+
requires(memory_no_alias(w1, sizeof(mld_polyveck)))
109+
assigns(memory_slice(
110+
sig + MLDSA_CTILDEBYTES + MLDSA_L * MLDSA_POLYZ_PACKEDBYTES,
111+
MLDSA_POLYVECH_PACKEDBYTES))
112+
ensures(return_value == 0 || return_value == MLD_ERR_FAIL)
107113
);
108114

109115
#define mld_pack_sig_z MLD_NAMESPACE_KL(pack_sig_z)
@@ -112,7 +118,7 @@ __contract__(
112118
*
113119
* Description: Bit-pack single polynomial of z component of sig = (c, z, h).
114120
* The c and h components are packed separately using
115-
* mld_pack_sig_c and mld_pack_sig_h_poly.
121+
* mld_pack_sig_c and mld_pack_sig_h.
116122
*
117123
* Arguments: - uint8_t sig[]: output byte array
118124
* - const mld_poly *zi: pointer to a single polynomial in z

mldsa/src/poly_kl.c

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -101,29 +101,6 @@ void mld_poly_decompose(mld_poly *a1, mld_poly *a0)
101101
mld_poly_decompose_c(a1, a0);
102102
}
103103

104-
MLD_INTERNAL_API
105-
unsigned int mld_poly_make_hint(mld_poly *h, const mld_poly *a0,
106-
const mld_poly *a1)
107-
{
108-
unsigned int i, s = 0;
109-
110-
for (i = 0; i < MLDSA_N; ++i)
111-
__loop__(
112-
invariant(i <= MLDSA_N)
113-
invariant(s <= i)
114-
invariant(array_bound(h->coeffs, 0, i, 0, 2))
115-
decreases(MLDSA_N - i)
116-
)
117-
{
118-
const unsigned int hint_bit = mld_make_hint(a0->coeffs[i], a1->coeffs[i]);
119-
h->coeffs[i] = (int32_t)hint_bit;
120-
s += hint_bit;
121-
}
122-
123-
mld_assert(s <= MLDSA_N);
124-
mld_assert_bound(h->coeffs, MLDSA_N, 0, 2);
125-
return s;
126-
}
127104
#endif /* !MLD_CONFIG_NO_SIGN_API */
128105

129106
#if !defined(MLD_CONFIG_NO_VERIFY_API)

mldsa/src/poly_kl.h

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -44,32 +44,6 @@ __contract__(
4444
);
4545

4646

47-
#define mld_poly_make_hint MLD_NAMESPACE_KL(poly_make_hint)
48-
/*************************************************
49-
* Name: mld_poly_make_hint
50-
*
51-
* Description: Compute hint polynomial. The coefficients of which indicate
52-
* whether the low bits of the corresponding coefficient of
53-
* the input polynomial overflow into the high bits.
54-
*
55-
* Arguments: - mld_poly *h: pointer to output hint polynomial
56-
* - const mld_poly *a0: pointer to low part of input polynomial
57-
* - const mld_poly *a1: pointer to high part of input polynomial
58-
*
59-
* Returns number of 1 bits.
60-
**************************************************/
61-
MLD_INTERNAL_API
62-
MLD_MUST_CHECK_RETURN_VALUE
63-
unsigned int mld_poly_make_hint(mld_poly *h, const mld_poly *a0,
64-
const mld_poly *a1)
65-
__contract__(
66-
requires(memory_no_alias(h, sizeof(mld_poly)))
67-
requires(memory_no_alias(a0, sizeof(mld_poly)))
68-
requires(memory_no_alias(a1, sizeof(mld_poly)))
69-
assigns(memory_slice(h, sizeof(mld_poly)))
70-
ensures(return_value <= MLDSA_N)
71-
ensures(array_bound(h->coeffs, 0, MLDSA_N, 0, 2))
72-
);
7347
#endif /* !MLD_CONFIG_NO_SIGN_API */
7448

7549
#if !defined(MLD_CONFIG_NO_VERIFY_API)

mldsa/src/sign.c

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -681,7 +681,7 @@ __contract__(
681681
return_value == MLD_ERR_OUT_OF_MEMORY)
682682
)
683683
{
684-
unsigned int k, n;
684+
unsigned int k;
685685
uint32_t w0_invalid, h_invalid;
686686
int ret;
687687

@@ -808,31 +808,13 @@ __contract__(
808808
MLD_CT_TESTING_DECLASSIFY(w0, sizeof(*w0));
809809
MLD_CT_TESTING_DECLASSIFY(w1, sizeof(*w1));
810810

811-
/* Pack challenge bytes and initialize hint section */
811+
/* Pack challenge bytes and hints. */
812812
mld_pack_sig_c(sig, challenge_bytes);
813-
mld_memset(sig + MLDSA_CTILDEBYTES + MLDSA_L * MLDSA_POLYZ_PACKEDBYTES, 0,
814-
MLDSA_POLYVECH_PACKEDBYTES);
815813

816-
/* Compute hints per-component and pack incrementally */
817-
n = 0;
818-
for (k = 0; k < MLDSA_K; k++)
819-
__loop__(
820-
assigns(k, n,
821-
memory_slice(z, sizeof(mld_poly)),
822-
memory_slice(sig, MLDSA_CRYPTO_BYTES))
823-
invariant(k <= MLDSA_K)
824-
invariant(n <= MLDSA_OMEGA)
825-
decreases(MLDSA_K - k)
826-
)
814+
ret = mld_pack_sig_h(sig, w0, w1);
815+
if (ret != 0)
827816
{
828-
unsigned int hints = mld_poly_make_hint(z, &w0->vec[k], &w1->vec[k]);
829-
if (n + hints > MLDSA_OMEGA)
830-
{
831-
ret = MLD_ERR_FAIL; /* reject */
832-
goto cleanup;
833-
}
834-
mld_pack_sig_h_poly(sig, z, k, n);
835-
n += hints;
817+
goto cleanup;
836818
}
837819

838820
/* Constant time: At this point it is clear that the signature is valid - it

proofs/cbmc/attempt_signature_generation/Makefile

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,8 @@ USE_FUNCTION_CONTRACTS+=mld_poly_sub
3737
USE_FUNCTION_CONTRACTS+=mld_poly_reduce
3838
USE_FUNCTION_CONTRACTS+=mld_poly_chknorm
3939
USE_FUNCTION_CONTRACTS+=mld_poly_add
40-
USE_FUNCTION_CONTRACTS+=mld_poly_make_hint
4140
USE_FUNCTION_CONTRACTS+=mld_pack_sig_c
42-
USE_FUNCTION_CONTRACTS+=mld_pack_sig_h_poly
41+
USE_FUNCTION_CONTRACTS+=mld_pack_sig_h
4342
USE_FUNCTION_CONTRACTS+=mld_zeroize
4443

4544
APPLY_LOOP_CONTRACTS=on
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
include ../Makefile_params.common
55

66
HARNESS_ENTRY = harness
7-
HARNESS_FILE = pack_sig_h_poly_harness
7+
HARNESS_FILE = pack_sig_h_harness
88

99
# This should be a unique identifier for this proof, and will appear on the
1010
# Litani dashboard. It can be human-readable and contain spaces if you wish.
11-
PROOF_UID = pack_sig_h_poly
11+
PROOF_UID = pack_sig_h
1212

1313
DEFINES +=
1414
INCLUDES +=
@@ -19,16 +19,16 @@ UNWINDSET +=
1919
PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE).c
2020
PROJECT_SOURCES += $(SRCDIR)/mldsa/src/packing.c
2121

22-
CHECK_FUNCTION_CONTRACTS=mld_pack_sig_h_poly
23-
USE_FUNCTION_CONTRACTS=
22+
CHECK_FUNCTION_CONTRACTS=mld_pack_sig_h
23+
USE_FUNCTION_CONTRACTS=mld_make_hint
2424
APPLY_LOOP_CONTRACTS=on
2525
USE_DYNAMIC_FRAMES=1
2626

2727
# Disable any setting of EXTERNAL_SAT_SOLVER, and choose SMT backend instead
2828
EXTERNAL_SAT_SOLVER=
2929
CBMCFLAGS=--external-smt2-solver $(PROOF_ROOT)/lib/z3_no_bv_extract --z3
3030

31-
FUNCTION_NAME = pack_sig_h_poly
31+
FUNCTION_NAME = pack_sig_h
3232

3333
# If this proof is found to consume huge amounts of RAM, you can set the
3434
# EXPENSIVE variable. With new enough versions of the proof tools, this will

proofs/cbmc/pack_sig_h_poly/pack_sig_h_poly_harness.c renamed to proofs/cbmc/pack_sig_h/pack_sig_h_harness.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
void harness(void)
77
{
88
uint8_t *sig;
9-
mld_poly *h;
10-
unsigned int k, n;
11-
mld_pack_sig_h_poly(sig, h, k, n);
9+
mld_polyveck *w0, *w1;
10+
int r;
11+
r = mld_pack_sig_h(sig, w0, w1);
1212
}

0 commit comments

Comments
 (0)