#include "poly.h"
#include "common.h"
#include "ct.h"
#include "debug.h"
#include "reduce.h"
#include "rounding.h"
#include "symmetric.h"
#if !defined(MLD_CONFIG_MULTILEVEL_NO_SHARED)
#include "zetas.inc"
MLD_INTERNAL_API
void mld_poly_reduce(mld_poly *a)
{
unsigned int i;
mld_assert_bound(a->coeffs, MLDSA_N, INT32_MIN, MLD_REDUCE32_DOMAIN_MAX);
for (i = 0; i < MLDSA_N; ++i)
__loop__(
invariant(i <= MLDSA_N)
invariant(forall(k0, i, MLDSA_N, a->coeffs[k0] == loop_entry(*a).coeffs[k0]))
invariant(array_bound(a->coeffs, 0, i, -MLD_REDUCE32_RANGE_MAX, MLD_REDUCE32_RANGE_MAX))
decreases(MLDSA_N - i))
{
a->coeffs[i] = mld_reduce32(a->coeffs[i]);
}
mld_assert_bound(a->coeffs, MLDSA_N, -MLD_REDUCE32_RANGE_MAX,
MLD_REDUCE32_RANGE_MAX);
}
MLD_STATIC_TESTABLE void mld_poly_caddq_c(mld_poly *a)
__contract__(
requires(memory_no_alias(a, sizeof(mld_poly)))
requires(array_abs_bound(a->coeffs, 0, MLDSA_N, MLDSA_Q))
assigns(memory_slice(a, sizeof(mld_poly)))
ensures(array_bound(a->coeffs, 0, MLDSA_N, 0, MLDSA_Q))
)
{
unsigned int i;
mld_assert_abs_bound(a->coeffs, MLDSA_N, MLDSA_Q);
for (i = 0; i < MLDSA_N; ++i)
__loop__(
invariant(i <= MLDSA_N)
invariant(forall(k0, i, MLDSA_N, a->coeffs[k0] == loop_entry(*a).coeffs[k0]))
invariant(array_bound(a->coeffs, 0, i, 0, MLDSA_Q))
decreases(MLDSA_N - i)
)
{
a->coeffs[i] = mld_caddq(a->coeffs[i]);
}
mld_assert_bound(a->coeffs, MLDSA_N, 0, MLDSA_Q);
}
MLD_INTERNAL_API
void mld_poly_caddq(mld_poly *a)
{
#if defined(MLD_USE_NATIVE_POLY_CADDQ)
int ret;
mld_assert_abs_bound(a->coeffs, MLDSA_N, MLDSA_Q);
ret = mld_poly_caddq_native(a->coeffs);
if (ret == MLD_NATIVE_FUNC_SUCCESS)
{
mld_assert_bound(a->coeffs, MLDSA_N, 0, MLDSA_Q);
return;
}
#endif
mld_poly_caddq_c(a);
}
#if !defined(MLD_CONFIG_NO_KEYPAIR_API) || !defined(MLD_CONFIG_NO_SIGN_API) || \
defined(MLD_CONFIG_REDUCE_RAM) || defined(MLD_UNIT_TEST)
MLD_INTERNAL_API
void mld_poly_add(mld_poly *r, const mld_poly *b)
{
unsigned int i;
for (i = 0; i < MLDSA_N; ++i)
__loop__(
assigns(i, memory_slice(r, sizeof(mld_poly)))
invariant(i <= MLDSA_N)
invariant(forall(k0, i, MLDSA_N, r->coeffs[k0] == loop_entry(*r).coeffs[k0]))
invariant(forall(k1, 0, i, r->coeffs[k1] == loop_entry(*r).coeffs[k1] + b->coeffs[k1]))
invariant(forall(k2, 0, i, r->coeffs[k2] < MLD_REDUCE32_DOMAIN_MAX))
invariant(forall(k2, 0, i, r->coeffs[k2] >= INT32_MIN))
decreases(MLDSA_N - i)
)
{
r->coeffs[i] = r->coeffs[i] + b->coeffs[i];
}
}
#endif
#if !defined(MLD_CONFIG_NO_SIGN_API) || !defined(MLD_CONFIG_NO_VERIFY_API)
MLD_INTERNAL_API
void mld_poly_sub(mld_poly *r, const mld_poly *b)
{
unsigned int i;
mld_assert_abs_bound(b->coeffs, MLDSA_N, MLDSA_Q);
mld_assert_abs_bound(r->coeffs, MLDSA_N, MLDSA_Q);
for (i = 0; i < MLDSA_N; ++i)
__loop__(
invariant(i <= MLDSA_N)
invariant(array_bound(r->coeffs, 0, i, INT32_MIN, MLD_REDUCE32_DOMAIN_MAX))
invariant(forall(k0, i, MLDSA_N, r->coeffs[k0] == loop_entry(*r).coeffs[k0]))
decreases(MLDSA_N - i)
)
{
r->coeffs[i] = r->coeffs[i] - b->coeffs[i];
}
mld_assert_bound(r->coeffs, MLDSA_N, INT32_MIN, MLD_REDUCE32_DOMAIN_MAX);
}
#endif
#if !defined(MLD_CONFIG_NO_VERIFY_API)
MLD_INTERNAL_API
void mld_poly_shiftl(mld_poly *a)
{
unsigned int i;
mld_assert_bound(a->coeffs, MLDSA_N, 0, 1 << 10);
for (i = 0; i < MLDSA_N; i++)
__loop__(
invariant(i <= MLDSA_N)
invariant(array_bound(a->coeffs, 0, i, 0, MLDSA_Q))
invariant(forall(k0, i, MLDSA_N, a->coeffs[k0] == loop_entry(*a).coeffs[k0]))
decreases(MLDSA_N - i))
{
a->coeffs[i] *= (1 << MLDSA_D);
}
mld_assert_bound(a->coeffs, MLDSA_N, 0, MLDSA_Q);
}
#endif
static MLD_INLINE int32_t mld_fqmul(int32_t a, int32_t b)
__contract__(
requires(b > -MLDSA_Q_HALF && b < MLDSA_Q_HALF)
ensures(return_value > -MLD_FQMUL_BOUND && return_value < MLD_FQMUL_BOUND)
)
{
return mld_montgomery_reduce((int64_t)a * (int64_t)b);
}
static MLD_INLINE void mld_ntt_butterfly_block(int32_t r[MLDSA_N],
const int32_t zeta,
const unsigned start,
const unsigned len,
const uint32_t bound)
__contract__(
requires(start < MLDSA_N)
requires(1 <= len && len <= MLDSA_N / 2 && start + 2 * len <= MLDSA_N)
requires(0 <= bound && bound < INT32_MAX - MLD_FQMUL_BOUND)
requires(-MLDSA_Q_HALF < zeta && zeta < MLDSA_Q_HALF)
requires(memory_no_alias(r, sizeof(int32_t) * MLDSA_N))
requires(array_abs_bound(r, 0, start, bound + MLD_FQMUL_BOUND))
requires(array_abs_bound(r, start, MLDSA_N, bound))
assigns(memory_slice(r, sizeof(int32_t) * MLDSA_N))
ensures(array_abs_bound(r, 0, start + 2*len, bound + MLD_FQMUL_BOUND))
ensures(array_abs_bound(r, start + 2 * len, MLDSA_N, bound)))
{
unsigned j;
((void)bound);
for (j = start; j < start + len; j++)
__loop__(
invariant(start <= j && j <= start + len)
invariant(array_abs_bound(r, 0, j, bound + MLD_FQMUL_BOUND))
invariant(array_abs_bound(r, j, start + len, bound))
invariant(array_abs_bound(r, start + len, j + len, bound + MLD_FQMUL_BOUND))
invariant(array_abs_bound(r, j + len, MLDSA_N, bound))
decreases(start + len - j))
{
int32_t t;
t = mld_fqmul(r[j + len], zeta);
r[j + len] = r[j] - t;
r[j] = r[j] + t;
}
}
static MLD_INLINE void mld_ntt_layer(int32_t r[MLDSA_N], const unsigned layer)
__contract__(
requires(memory_no_alias(r, sizeof(int32_t) * MLDSA_N))
requires(1 <= layer && layer <= 8)
requires(array_abs_bound(r, 0, MLDSA_N, layer * MLD_FQMUL_BOUND))
assigns(memory_slice(r, sizeof(int32_t) * MLDSA_N))
ensures(array_abs_bound(r, 0, MLDSA_N, (layer + 1) * MLD_FQMUL_BOUND)))
{
unsigned start, k, len;
k = 1u << (layer - 1);
len = (unsigned)MLDSA_N >> layer;
for (start = 0; start < MLDSA_N; start += 2 * len)
__loop__(
invariant(start < MLDSA_N + 2 * len)
invariant(k <= MLDSA_N)
invariant(2 * len * k == start + MLDSA_N)
invariant(array_abs_bound(r, 0, start, layer * MLD_FQMUL_BOUND + MLD_FQMUL_BOUND))
invariant(array_abs_bound(r, start, MLDSA_N, layer * MLD_FQMUL_BOUND))
decreases(MLDSA_N - start))
{
int32_t zeta = mld_zetas[k++];
mld_ntt_butterfly_block(r, zeta, start, len, layer * MLD_FQMUL_BOUND);
}
}
MLD_STATIC_TESTABLE void mld_poly_ntt_c(mld_poly *a)
__contract__(
requires(memory_no_alias(a, sizeof(mld_poly)))
requires(array_abs_bound(a->coeffs, 0, MLDSA_N, MLDSA_Q))
assigns(memory_slice(a, sizeof(mld_poly)))
ensures(array_abs_bound(a->coeffs, 0, MLDSA_N, MLD_NTT_BOUND))
)
{
unsigned int layer;
int32_t *r;
mld_assert_abs_bound(a->coeffs, MLDSA_N, MLDSA_Q);
r = a->coeffs;
for (layer = 1; layer < 9; layer++)
__loop__(
invariant(1 <= layer && layer <= 9)
invariant(array_abs_bound(r, 0, MLDSA_N, layer * MLD_FQMUL_BOUND))
decreases(9 - layer)
)
{
mld_ntt_layer(r, layer);
}
mld_assert_abs_bound(a->coeffs, MLDSA_N, MLD_NTT_BOUND);
}
MLD_INTERNAL_API
void mld_poly_ntt(mld_poly *a)
{
#if defined(MLD_USE_NATIVE_NTT)
int ret;
mld_assert_abs_bound(a->coeffs, MLDSA_N, MLDSA_Q);
ret = mld_ntt_native(a->coeffs);
if (ret == MLD_NATIVE_FUNC_SUCCESS)
{
mld_assert_abs_bound(a->coeffs, MLDSA_N, MLD_NTT_BOUND);
return;
}
#endif
mld_poly_ntt_c(a);
}
static MLD_INLINE int32_t mld_fqscale(int32_t a)
__contract__(
requires(a > -256*MLDSA_Q && a < 256*MLDSA_Q)
ensures(return_value > -MLD_INTT_BOUND && return_value < MLD_INTT_BOUND)
)
{
const int32_t f = 41978;
return mld_montgomery_reduce((int64_t)a * f);
}
static MLD_INLINE void mld_invntt_layer(int32_t r[MLDSA_N], unsigned layer)
__contract__(
requires(memory_no_alias(r, sizeof(int32_t) * MLDSA_N))
requires(1 <= layer && layer <= 8)
requires(array_abs_bound(r, 0, MLDSA_N, (MLDSA_N >> layer) * MLDSA_Q))
assigns(memory_slice(r, sizeof(int32_t) * MLDSA_N))
ensures(array_abs_bound(r, 0, MLDSA_N, (MLDSA_N >> (layer - 1)) * MLDSA_Q)))
{
unsigned start, k, len;
len = (unsigned)MLDSA_N >> layer;
k = (1u << layer) - 1;
for (start = 0; start < MLDSA_N; start += 2 * len)
__loop__(
invariant(start <= MLDSA_N && k <= 255)
invariant(2 * len * k + start == 2 * MLDSA_N - 2 * len)
invariant(array_abs_bound(r, 0, start, (MLDSA_N >> (layer - 1)) * MLDSA_Q))
invariant(array_abs_bound(r, start, MLDSA_N, (MLDSA_N >> layer) * MLDSA_Q))
decreases(MLDSA_N - start))
{
unsigned j;
int32_t zeta = -mld_zetas[k--];
for (j = start; j < start + len; j++)
__loop__(
invariant(start <= j && j <= start + len)
invariant(array_abs_bound(r, 0, start, (MLDSA_N >> (layer - 1)) * MLDSA_Q))
invariant(array_abs_bound(r, start, j, (MLDSA_N >> (layer - 1)) * MLDSA_Q))
invariant(array_abs_bound(r, j, start + len, (MLDSA_N >> layer) * MLDSA_Q))
invariant(array_abs_bound(r, start + len, j + len, (MLDSA_N >> (layer - 1)) * MLDSA_Q))
invariant(array_abs_bound(r, j + len, MLDSA_N, (MLDSA_N >> layer) * MLDSA_Q))
decreases(start + len - j))
{
int32_t t = r[j];
r[j] = t + r[j + len];
r[j + len] = t - r[j + len];
r[j + len] = mld_fqmul(r[j + len], zeta);
}
}
}
MLD_STATIC_TESTABLE void mld_poly_invntt_tomont_c(mld_poly *a)
__contract__(
requires(memory_no_alias(a, sizeof(mld_poly)))
requires(array_abs_bound(a->coeffs, 0, MLDSA_N, MLDSA_Q))
assigns(memory_slice(a, sizeof(mld_poly)))
ensures(array_abs_bound(a->coeffs, 0, MLDSA_N, MLD_INTT_BOUND))
)
{
unsigned int layer, j;
int32_t *r;
mld_assert_abs_bound(a->coeffs, MLDSA_N, MLDSA_Q);
r = a->coeffs;
for (layer = 8; layer >= 1; layer--)
__loop__(
invariant(layer <= 8)
invariant(array_abs_bound(r, 0, MLDSA_N, (MLDSA_N >> layer) * MLDSA_Q))
decreases(layer))
{
mld_invntt_layer(r, layer);
}
for (j = 0; j < MLDSA_N; ++j)
__loop__(
invariant(j <= MLDSA_N)
invariant(array_abs_bound(r, 0, j, MLD_INTT_BOUND))
invariant(array_abs_bound(r, j, MLDSA_N, MLDSA_N * MLDSA_Q))
decreases(MLDSA_N - j)
)
{
r[j] = mld_fqscale(r[j]);
}
mld_assert_abs_bound(a->coeffs, MLDSA_N, MLD_INTT_BOUND);
}
MLD_INTERNAL_API
void mld_poly_invntt_tomont(mld_poly *a)
{
#if defined(MLD_USE_NATIVE_INTT)
int ret;
mld_assert_abs_bound(a->coeffs, MLDSA_N, MLDSA_Q);
ret = mld_intt_native(a->coeffs);
if (ret == MLD_NATIVE_FUNC_SUCCESS)
{
mld_assert_abs_bound(a->coeffs, MLDSA_N, MLD_INTT_BOUND);
return;
}
#endif
mld_poly_invntt_tomont_c(a);
}
#if !defined(MLD_CONFIG_NO_SIGN_API) || !defined(MLD_CONFIG_NO_VERIFY_API) || \
defined(MLD_CONFIG_REDUCE_RAM) || defined(MLD_UNIT_TEST)
MLD_STATIC_TESTABLE void mld_poly_pointwise_montgomery_c(mld_poly *a,
const mld_poly *b)
__contract__(
requires(memory_no_alias(a, sizeof(mld_poly)))
requires(memory_no_alias(b, sizeof(mld_poly)))
requires(array_abs_bound(a->coeffs, 0, MLDSA_N, MLD_NTT_BOUND))
requires(array_abs_bound(b->coeffs, 0, MLDSA_N, MLD_NTT_BOUND))
assigns(memory_slice(a, sizeof(mld_poly)))
ensures(array_abs_bound(a->coeffs, 0, MLDSA_N, MLDSA_Q))
)
{
unsigned int i;
mld_assert_abs_bound(a->coeffs, MLDSA_N, MLD_NTT_BOUND);
mld_assert_abs_bound(b->coeffs, MLDSA_N, MLD_NTT_BOUND);
for (i = 0; i < MLDSA_N; ++i)
__loop__(
invariant(i <= MLDSA_N)
invariant(array_abs_bound(a->coeffs, 0, i, MLDSA_Q))
invariant(array_abs_bound(a->coeffs, i, MLDSA_N, MLD_NTT_BOUND))
decreases(MLDSA_N - i)
)
{
a->coeffs[i] = mld_montgomery_reduce((int64_t)a->coeffs[i] * b->coeffs[i]);
}
mld_assert_abs_bound(a->coeffs, MLDSA_N, MLDSA_Q);
}
MLD_INTERNAL_API
void mld_poly_pointwise_montgomery(mld_poly *a, const mld_poly *b)
{
#if defined(MLD_USE_NATIVE_POINTWISE_MONTGOMERY)
int ret;
mld_assert_abs_bound(a->coeffs, MLDSA_N, MLD_NTT_BOUND);
mld_assert_abs_bound(b->coeffs, MLDSA_N, MLD_NTT_BOUND);
ret = mld_poly_pointwise_montgomery_native(a->coeffs, b->coeffs);
if (ret == MLD_NATIVE_FUNC_SUCCESS)
{
mld_assert_abs_bound(a->coeffs, MLDSA_N, MLDSA_Q);
return;
}
#endif
mld_poly_pointwise_montgomery_c(a, b);
}
#endif
#if !defined(MLD_CONFIG_NO_KEYPAIR_API)
MLD_INTERNAL_API
void mld_poly_power2round(mld_poly *a1, mld_poly *a0, const mld_poly *a)
{
unsigned int i;
mld_assert_bound(a->coeffs, MLDSA_N, 0, MLDSA_Q);
for (i = 0; i < MLDSA_N; ++i)
__loop__(
assigns(i, memory_slice(a0, sizeof(mld_poly)), memory_slice(a1, sizeof(mld_poly)))
invariant(i <= MLDSA_N)
invariant(forall(k0, i, MLDSA_N, a->coeffs[k0] == loop_entry(*a).coeffs[k0]))
invariant(array_bound(a0->coeffs, 0, i, -(MLD_2_POW_D/2)+1, (MLD_2_POW_D/2)+1))
invariant(array_bound(a1->coeffs, 0, i, 0, ((MLDSA_Q - 1) / MLD_2_POW_D) + 1))
decreases(MLDSA_N - i)
)
{
mld_power2round(&a0->coeffs[i], &a1->coeffs[i], a->coeffs[i]);
}
mld_assert_bound(a0->coeffs, MLDSA_N, -(MLD_2_POW_D / 2) + 1,
(MLD_2_POW_D / 2) + 1);
mld_assert_bound(a1->coeffs, MLDSA_N, 0, ((MLDSA_Q - 1) / MLD_2_POW_D) + 1);
}
#endif
#ifndef MLD_POLY_UNIFORM_NBLOCKS
#define MLD_POLY_UNIFORM_NBLOCKS \
((768 + MLD_STREAM128_BLOCKBYTES - 1) / MLD_STREAM128_BLOCKBYTES)
#endif
MLD_STATIC_TESTABLE unsigned int mld_rej_uniform_c(int32_t *a,
unsigned int target,
unsigned int offset,
const uint8_t *buf,
unsigned int buflen)
__contract__(
requires(offset <= target && target <= MLDSA_N)
requires(buflen <= (MLD_POLY_UNIFORM_NBLOCKS * MLD_STREAM128_BLOCKBYTES) && buflen % 3 == 0)
requires(memory_no_alias(a, sizeof(int32_t) * target))
requires(memory_no_alias(buf, buflen))
requires(array_bound(a, 0, offset, 0, MLDSA_Q))
assigns(memory_slice(a, sizeof(int32_t) * target))
ensures(offset <= return_value && return_value <= target)
ensures(array_bound(a, 0, return_value, 0, MLDSA_Q))
)
{
unsigned int ctr, pos;
uint32_t t;
mld_assert_bound(a, offset, 0, MLDSA_Q);
ctr = offset;
pos = 0;
while (ctr < target && pos + 3 <= buflen)
__loop__(
invariant(offset <= ctr && ctr <= target && pos <= buflen)
invariant(array_bound(a, 0, ctr, 0, MLDSA_Q))
decreases(buflen - pos))
{
t = buf[pos++];
t |= (uint32_t)buf[pos++] << 8;
t |= (uint32_t)buf[pos++] << 16;
t &= 0x7FFFFF;
if (t < MLDSA_Q)
{
a[ctr++] = (int32_t)t;
}
}
mld_assert_bound(a, ctr, 0, MLDSA_Q);
return ctr;
}
static unsigned int mld_rej_uniform(int32_t *a, unsigned int target,
unsigned int offset, const uint8_t *buf,
unsigned int buflen)
__contract__(
requires(offset <= target && target <= MLDSA_N)
requires(buflen <= (MLD_POLY_UNIFORM_NBLOCKS * MLD_STREAM128_BLOCKBYTES) && buflen % 3 == 0)
requires(memory_no_alias(a, sizeof(int32_t) * target))
requires(memory_no_alias(buf, buflen))
requires(array_bound(a, 0, offset, 0, MLDSA_Q))
assigns(memory_slice(a, sizeof(int32_t) * target))
ensures(offset <= return_value && return_value <= target)
ensures(array_bound(a, 0, return_value, 0, MLDSA_Q))
)
{
#if defined(MLD_USE_NATIVE_REJ_UNIFORM)
int ret;
mld_assert_bound(a, offset, 0, MLDSA_Q);
if (offset == 0)
{
ret = mld_rej_uniform_native(a, target, buf, buflen);
if (ret != MLD_NATIVE_FUNC_FALLBACK)
{
unsigned res = (unsigned)ret;
mld_assert_bound(a, res, 0, MLDSA_Q);
return res;
}
}
#endif
return mld_rej_uniform_c(a, target, offset, buf, buflen);
}
MLD_INTERNAL_API
void mld_poly_uniform(mld_poly *a, const uint8_t seed[MLDSA_SEEDBYTES + 2])
{
unsigned int ctr;
unsigned int buflen = MLD_POLY_UNIFORM_NBLOCKS * MLD_STREAM128_BLOCKBYTES;
MLD_ALIGN uint8_t buf[MLD_POLY_UNIFORM_NBLOCKS * MLD_STREAM128_BLOCKBYTES];
mld_xof128_ctx state;
mld_xof128_init(&state);
mld_xof128_absorb_once(&state, seed, MLDSA_SEEDBYTES + 2);
mld_xof128_squeezeblocks(buf, MLD_POLY_UNIFORM_NBLOCKS, &state);
ctr = mld_rej_uniform(a->coeffs, MLDSA_N, 0, buf, buflen);
buflen = MLD_STREAM128_BLOCKBYTES;
while (ctr < MLDSA_N)
__loop__(
assigns(ctr, state, memory_slice(a, sizeof(mld_poly)), object_whole(buf))
invariant(ctr <= MLDSA_N)
invariant(array_bound(a->coeffs, 0, ctr, 0, MLDSA_Q))
invariant(state.pos <= SHAKE128_RATE)
)
{
mld_xof128_squeezeblocks(buf, 1, &state);
ctr = mld_rej_uniform(a->coeffs, MLDSA_N, ctr, buf, buflen);
}
mld_xof128_release(&state);
mld_assert_bound(a->coeffs, MLDSA_N, 0, MLDSA_Q);
mld_zeroize(buf, sizeof(buf));
}
#if !defined(MLD_CONFIG_SERIAL_FIPS202_ONLY) && \
(!defined(MLD_CONFIG_REDUCE_RAM) || defined(MLD_UNIT_TEST))
MLD_INTERNAL_API
void mld_poly_uniform_4x(mld_poly *vec0, mld_poly *vec1, mld_poly *vec2,
mld_poly *vec3,
uint8_t seed[4][MLD_ALIGN_UP(MLDSA_SEEDBYTES + 2)])
{
MLD_ALIGN uint8_t
buf[4][MLD_ALIGN_UP(MLD_POLY_UNIFORM_NBLOCKS * MLD_STREAM128_BLOCKBYTES)];
unsigned ctr[4];
mld_xof128_x4_ctx state;
unsigned buflen;
mld_xof128_x4_init(&state);
mld_xof128_x4_absorb(&state, seed, MLDSA_SEEDBYTES + 2);
mld_xof128_x4_squeezeblocks(buf, MLD_POLY_UNIFORM_NBLOCKS, &state);
buflen = MLD_POLY_UNIFORM_NBLOCKS * MLD_STREAM128_BLOCKBYTES;
ctr[0] = mld_rej_uniform(vec0->coeffs, MLDSA_N, 0, buf[0], buflen);
ctr[1] = mld_rej_uniform(vec1->coeffs, MLDSA_N, 0, buf[1], buflen);
ctr[2] = mld_rej_uniform(vec2->coeffs, MLDSA_N, 0, buf[2], buflen);
ctr[3] = mld_rej_uniform(vec3->coeffs, MLDSA_N, 0, buf[3], buflen);
buflen = MLD_STREAM128_BLOCKBYTES;
while (ctr[0] < MLDSA_N || ctr[1] < MLDSA_N || ctr[2] < MLDSA_N ||
ctr[3] < MLDSA_N)
__loop__(
assigns(ctr, state, object_whole(buf),
memory_slice(vec0, sizeof(mld_poly)), memory_slice(vec1, sizeof(mld_poly)),
memory_slice(vec2, sizeof(mld_poly)), memory_slice(vec3, sizeof(mld_poly)))
invariant(ctr[0] <= MLDSA_N && ctr[1] <= MLDSA_N)
invariant(ctr[2] <= MLDSA_N && ctr[3] <= MLDSA_N)
invariant(array_bound(vec0->coeffs, 0, ctr[0], 0, MLDSA_Q))
invariant(array_bound(vec1->coeffs, 0, ctr[1], 0, MLDSA_Q))
invariant(array_bound(vec2->coeffs, 0, ctr[2], 0, MLDSA_Q))
invariant(array_bound(vec3->coeffs, 0, ctr[3], 0, MLDSA_Q)))
{
mld_xof128_x4_squeezeblocks(buf, 1, &state);
ctr[0] = mld_rej_uniform(vec0->coeffs, MLDSA_N, ctr[0], buf[0], buflen);
ctr[1] = mld_rej_uniform(vec1->coeffs, MLDSA_N, ctr[1], buf[1], buflen);
ctr[2] = mld_rej_uniform(vec2->coeffs, MLDSA_N, ctr[2], buf[2], buflen);
ctr[3] = mld_rej_uniform(vec3->coeffs, MLDSA_N, ctr[3], buf[3], buflen);
}
mld_xof128_x4_release(&state);
mld_assert_bound(vec0->coeffs, MLDSA_N, 0, MLDSA_Q);
mld_assert_bound(vec1->coeffs, MLDSA_N, 0, MLDSA_Q);
mld_assert_bound(vec2->coeffs, MLDSA_N, 0, MLDSA_Q);
mld_assert_bound(vec3->coeffs, MLDSA_N, 0, MLDSA_Q);
mld_zeroize(buf, sizeof(buf));
}
#endif
#if !defined(MLD_CONFIG_NO_KEYPAIR_API)
MLD_INTERNAL_API
void mld_polyt1_pack(uint8_t r[MLDSA_POLYT1_PACKEDBYTES], const mld_poly *a)
{
unsigned int i;
mld_assert_bound(a->coeffs, MLDSA_N, 0, 1 << 10);
for (i = 0; i < MLDSA_N / 4; ++i)
__loop__(
invariant(i <= MLDSA_N/4)
decreases(MLDSA_N / 4 - i))
{
r[5 * i + 0] = (uint8_t)((a->coeffs[4 * i + 0] >> 0) & 0xFF);
r[5 * i + 1] =
(uint8_t)(((a->coeffs[4 * i + 0] >> 8) | (a->coeffs[4 * i + 1] << 2)) &
0xFF);
r[5 * i + 2] =
(uint8_t)(((a->coeffs[4 * i + 1] >> 6) | (a->coeffs[4 * i + 2] << 4)) &
0xFF);
r[5 * i + 3] =
(uint8_t)(((a->coeffs[4 * i + 2] >> 4) | (a->coeffs[4 * i + 3] << 6)) &
0xFF);
r[5 * i + 4] = (uint8_t)((a->coeffs[4 * i + 3] >> 2) & 0xFF);
}
}
#endif
#if !defined(MLD_CONFIG_NO_VERIFY_API)
MLD_INTERNAL_API
void mld_polyt1_unpack(mld_poly *r, const uint8_t a[MLDSA_POLYT1_PACKEDBYTES])
{
unsigned int i;
for (i = 0; i < MLDSA_N / 4; ++i)
__loop__(
invariant(i <= MLDSA_N/4)
invariant(array_bound(r->coeffs, 0, i*4, 0, 1 << 10))
decreases(MLDSA_N / 4 - i))
{
r->coeffs[4 * i + 0] =
((a[5 * i + 0] >> 0) | ((int32_t)a[5 * i + 1] << 8)) & 0x3FF;
r->coeffs[4 * i + 1] =
((a[5 * i + 1] >> 2) | ((int32_t)a[5 * i + 2] << 6)) & 0x3FF;
r->coeffs[4 * i + 2] =
((a[5 * i + 2] >> 4) | ((int32_t)a[5 * i + 3] << 4)) & 0x3FF;
r->coeffs[4 * i + 3] =
((a[5 * i + 3] >> 6) | ((int32_t)a[5 * i + 4] << 2)) & 0x3FF;
}
mld_assert_bound(r->coeffs, MLDSA_N, 0, 1 << 10);
}
#endif
#if !defined(MLD_CONFIG_NO_KEYPAIR_API)
MLD_INTERNAL_API
void mld_polyt0_pack(uint8_t r[MLDSA_POLYT0_PACKEDBYTES], const mld_poly *a)
{
unsigned int i;
uint32_t t[8];
mld_assert_bound(a->coeffs, MLDSA_N, -(1 << (MLDSA_D - 1)) + 1,
(1 << (MLDSA_D - 1)) + 1);
for (i = 0; i < MLDSA_N / 8; ++i)
__loop__(
invariant(i <= MLDSA_N/8)
decreases(MLDSA_N / 8 - i))
{
t[0] = (uint32_t)((1 << (MLDSA_D - 1)) - a->coeffs[8 * i + 0]);
t[1] = (uint32_t)((1 << (MLDSA_D - 1)) - a->coeffs[8 * i + 1]);
t[2] = (uint32_t)((1 << (MLDSA_D - 1)) - a->coeffs[8 * i + 2]);
t[3] = (uint32_t)((1 << (MLDSA_D - 1)) - a->coeffs[8 * i + 3]);
t[4] = (uint32_t)((1 << (MLDSA_D - 1)) - a->coeffs[8 * i + 4]);
t[5] = (uint32_t)((1 << (MLDSA_D - 1)) - a->coeffs[8 * i + 5]);
t[6] = (uint32_t)((1 << (MLDSA_D - 1)) - a->coeffs[8 * i + 6]);
t[7] = (uint32_t)((1 << (MLDSA_D - 1)) - a->coeffs[8 * i + 7]);
r[13 * i + 0] = (uint8_t)((t[0]) & 0xFF);
r[13 * i + 1] = (uint8_t)((t[0] >> 8) & 0xFF);
r[13 * i + 1] |= (uint8_t)((t[1] << 5) & 0xFF);
r[13 * i + 2] = (uint8_t)((t[1] >> 3) & 0xFF);
r[13 * i + 3] = (uint8_t)((t[1] >> 11) & 0xFF);
r[13 * i + 3] |= (uint8_t)((t[2] << 2) & 0xFF);
r[13 * i + 4] = (uint8_t)((t[2] >> 6) & 0xFF);
r[13 * i + 4] |= (uint8_t)((t[3] << 7) & 0xFF);
r[13 * i + 5] = (uint8_t)((t[3] >> 1) & 0xFF);
r[13 * i + 6] = (uint8_t)((t[3] >> 9) & 0xFF);
r[13 * i + 6] |= (uint8_t)((t[4] << 4) & 0xFF);
r[13 * i + 7] = (uint8_t)((t[4] >> 4) & 0xFF);
r[13 * i + 8] = (uint8_t)((t[4] >> 12) & 0xFF);
r[13 * i + 8] |= (uint8_t)((t[5] << 1) & 0xFF);
r[13 * i + 9] = (uint8_t)((t[5] >> 7) & 0xFF);
r[13 * i + 9] |= (uint8_t)((t[6] << 6) & 0xFF);
r[13 * i + 10] = (uint8_t)((t[6] >> 2) & 0xFF);
r[13 * i + 11] = (uint8_t)((t[6] >> 10) & 0xFF);
r[13 * i + 11] |= (uint8_t)((t[7] << 3) & 0xFF);
r[13 * i + 12] = (uint8_t)((t[7] >> 5) & 0xFF);
}
}
#endif
#if !defined(MLD_CONFIG_NO_SIGN_API) || defined(MLD_UNIT_TEST)
MLD_INTERNAL_API
void mld_polyt0_unpack(mld_poly *r, const uint8_t a[MLDSA_POLYT0_PACKEDBYTES])
{
unsigned int i;
for (i = 0; i < MLDSA_N / 8; ++i)
__loop__(
invariant(i <= MLDSA_N/8)
invariant(array_bound(r->coeffs, 0, i*8, -(1<<(MLDSA_D-1)) + 1, (1<<(MLDSA_D-1)) + 1))
decreases(MLDSA_N / 8 - i))
{
r->coeffs[8 * i + 0] = a[13 * i + 0];
r->coeffs[8 * i + 0] |= (int32_t)a[13 * i + 1] << 8;
r->coeffs[8 * i + 0] &= 0x1FFF;
r->coeffs[8 * i + 1] = a[13 * i + 1] >> 5;
r->coeffs[8 * i + 1] |= (int32_t)a[13 * i + 2] << 3;
r->coeffs[8 * i + 1] |= (int32_t)a[13 * i + 3] << 11;
r->coeffs[8 * i + 1] &= 0x1FFF;
r->coeffs[8 * i + 2] = a[13 * i + 3] >> 2;
r->coeffs[8 * i + 2] |= (int32_t)a[13 * i + 4] << 6;
r->coeffs[8 * i + 2] &= 0x1FFF;
r->coeffs[8 * i + 3] = a[13 * i + 4] >> 7;
r->coeffs[8 * i + 3] |= (int32_t)a[13 * i + 5] << 1;
r->coeffs[8 * i + 3] |= (int32_t)a[13 * i + 6] << 9;
r->coeffs[8 * i + 3] &= 0x1FFF;
r->coeffs[8 * i + 4] = a[13 * i + 6] >> 4;
r->coeffs[8 * i + 4] |= (int32_t)a[13 * i + 7] << 4;
r->coeffs[8 * i + 4] |= (int32_t)a[13 * i + 8] << 12;
r->coeffs[8 * i + 4] &= 0x1FFF;
r->coeffs[8 * i + 5] = a[13 * i + 8] >> 1;
r->coeffs[8 * i + 5] |= (int32_t)a[13 * i + 9] << 7;
r->coeffs[8 * i + 5] &= 0x1FFF;
r->coeffs[8 * i + 6] = a[13 * i + 9] >> 6;
r->coeffs[8 * i + 6] |= (int32_t)a[13 * i + 10] << 2;
r->coeffs[8 * i + 6] |= (int32_t)a[13 * i + 11] << 10;
r->coeffs[8 * i + 6] &= 0x1FFF;
r->coeffs[8 * i + 7] = a[13 * i + 11] >> 3;
r->coeffs[8 * i + 7] |= (int32_t)a[13 * i + 12] << 5;
r->coeffs[8 * i + 7] &= 0x1FFF;
r->coeffs[8 * i + 0] = (1 << (MLDSA_D - 1)) - r->coeffs[8 * i + 0];
r->coeffs[8 * i + 1] = (1 << (MLDSA_D - 1)) - r->coeffs[8 * i + 1];
r->coeffs[8 * i + 2] = (1 << (MLDSA_D - 1)) - r->coeffs[8 * i + 2];
r->coeffs[8 * i + 3] = (1 << (MLDSA_D - 1)) - r->coeffs[8 * i + 3];
r->coeffs[8 * i + 4] = (1 << (MLDSA_D - 1)) - r->coeffs[8 * i + 4];
r->coeffs[8 * i + 5] = (1 << (MLDSA_D - 1)) - r->coeffs[8 * i + 5];
r->coeffs[8 * i + 6] = (1 << (MLDSA_D - 1)) - r->coeffs[8 * i + 6];
r->coeffs[8 * i + 7] = (1 << (MLDSA_D - 1)) - r->coeffs[8 * i + 7];
}
mld_assert_bound(r->coeffs, MLDSA_N, -(1 << (MLDSA_D - 1)) + 1,
(1 << (MLDSA_D - 1)) + 1);
}
#endif
MLD_STATIC_TESTABLE uint32_t mld_poly_chknorm_c(const mld_poly *a, int32_t B)
__contract__(
requires(memory_no_alias(a, sizeof(mld_poly)))
requires(0 <= B && B <= MLDSA_Q - MLD_REDUCE32_RANGE_MAX)
requires(array_bound(a->coeffs, 0, MLDSA_N, -MLD_REDUCE32_RANGE_MAX, MLD_REDUCE32_RANGE_MAX))
ensures(return_value == 0 || return_value == 0xFFFFFFFF)
ensures((return_value == 0) == array_abs_bound(a->coeffs, 0, MLDSA_N, B))
)
{
unsigned int i;
uint32_t t = 0;
mld_assert_bound(a->coeffs, MLDSA_N, -MLD_REDUCE32_RANGE_MAX,
MLD_REDUCE32_RANGE_MAX);
for (i = 0; i < MLDSA_N; ++i)
__loop__(
invariant(i <= MLDSA_N)
invariant(t == 0 || t == 0xFFFFFFFF)
invariant((t == 0) == array_abs_bound(a->coeffs, 0, i, B))
decreases(MLDSA_N - i)
)
{
mld_assert(a->coeffs[i] < B || a->coeffs[i] - MLDSA_Q <= -B);
mld_assert(a->coeffs[i] > -B || a->coeffs[i] + MLDSA_Q >= B);
t |= mld_ct_cmask_neg_i32(B - 1 - mld_ct_abs_i32(a->coeffs[i]));
}
return t;
}
MLD_INTERNAL_API
uint32_t mld_poly_chknorm(const mld_poly *a, int32_t B)
{
#if defined(MLD_USE_NATIVE_POLY_CHKNORM)
int ret;
int success;
mld_assert_bound(a->coeffs, MLDSA_N, -MLD_REDUCE32_RANGE_MAX,
MLD_REDUCE32_RANGE_MAX);
ret = mld_poly_chknorm_native(a->coeffs, B);
success = (ret != MLD_NATIVE_FUNC_FALLBACK);
MLD_CT_TESTING_DECLASSIFY(&success, sizeof(int));
if (success)
{
return mld_ct_cmask_nonzero_u32((uint32_t)ret);
}
#endif
return mld_poly_chknorm_c(a, B);
}
#if !defined(MLD_CONFIG_NO_SIGN_API) || !defined(MLD_CONFIG_NO_VERIFY_API)
#if defined(MLD_CONFIG_MULTILEVEL_WITH_SHARED) || \
MLD_CONFIG_PARAMETER_SET == 44
MLD_INTERNAL_API
void mld_polyw1_pack_88(uint8_t r[MLDSA_POLYW1_PACKEDBYTES_88],
const mld_poly *a)
{
unsigned int i;
mld_assert_bound(a->coeffs, MLDSA_N, 0,
(MLDSA_Q - 1) / (2 * MLDSA_GAMMA2_88));
for (i = 0; i < MLDSA_N / 4; ++i)
__loop__(
invariant(i <= MLDSA_N/4)
decreases(MLDSA_N / 4 - i))
{
r[3 * i + 0] = (uint8_t)((a->coeffs[4 * i + 0]) & 0xFF);
r[3 * i + 0] |= (uint8_t)((a->coeffs[4 * i + 1] << 6) & 0xFF);
r[3 * i + 1] = (uint8_t)((a->coeffs[4 * i + 1] >> 2) & 0xFF);
r[3 * i + 1] |= (uint8_t)((a->coeffs[4 * i + 2] << 4) & 0xFF);
r[3 * i + 2] = (uint8_t)((a->coeffs[4 * i + 2] >> 4) & 0xFF);
r[3 * i + 2] |= (uint8_t)((a->coeffs[4 * i + 3] << 2) & 0xFF);
}
}
#endif
#if defined(MLD_CONFIG_MULTILEVEL_WITH_SHARED) || \
(MLD_CONFIG_PARAMETER_SET == 65 || MLD_CONFIG_PARAMETER_SET == 87)
MLD_INTERNAL_API
void mld_polyw1_pack_32(uint8_t r[MLDSA_POLYW1_PACKEDBYTES_32],
const mld_poly *a)
{
unsigned int i;
mld_assert_bound(a->coeffs, MLDSA_N, 0,
(MLDSA_Q - 1) / (2 * MLDSA_GAMMA2_32));
for (i = 0; i < MLDSA_N / 2; ++i)
__loop__(
invariant(i <= MLDSA_N/2)
decreases(MLDSA_N / 2 - i))
{
r[i] =
(uint8_t)((a->coeffs[2 * i + 0] | (a->coeffs[2 * i + 1] << 4)) & 0xFF);
}
}
#endif
#endif
#else
MLD_EMPTY_CU(mld_poly)
#endif
#undef MLD_POLY_UNIFORM_NBLOCKS