Commit 4f3e572b5b for strongswan.org
commit 4f3e572b5bf2df169fa46fb040dc8ab0a0e19e4a
Author: Tobias Brunner <tobias@strongswan.org>
Date: Tue Jul 14 17:50:26 2026 +0200
ml: Properly implement ByteDecode with d=12
I've overlooked the "m = q if d = 12" note in the pseudo-code for
Algorithm 6. The text further up in the section actually clarifies
this:
For d = 12, ByteDecode produces integers modulo q as output...(it)
converts each 12-bit segment of its input into an integer modulo 4096
and then reduces the result modulo q. This is no longer a one-to-one
operation.
This did not have any practical impact (other than accepting public keys
that are technically non-compliant) because the values would get properly
reduced anyway by the Barrett reduction used in `mul_modq()`.
Fixes: 89f4b345e336 ("ml: Add software implementation of ML-KEM")
diff --git a/src/libstrongswan/plugins/ml/ml_kem.c b/src/libstrongswan/plugins/ml/ml_kem.c
index e257e0c1fc..861ef08768 100644
--- a/src/libstrongswan/plugins/ml/ml_kem.c
+++ b/src/libstrongswan/plugins/ml/ml_kem.c
@@ -402,18 +402,24 @@ static void encode_poly_arr(uint8_t k, ml_poly_t *a, uint8_t *out)
* Decode k polynomials from a byte array (12-bit version that unpacks 2
* coefficients from 3 bytes, not using ml_bitpacker_t for performance reasons).
*
+ * As described in section 4.2.1, d=12 is a special version of ByteDecode that
+ * does a reduction mod q.
+ *
* Algorithm 6 in FIPS 203.
*/
static void decode_poly_arr(uint8_t k, uint8_t *in, ml_poly_t *a)
{
+ uint16_t f0, f1;
int i, j;
for (i = 0; i < k; i++)
{
for (j = 0; j < ML_KEM_N / 2; j++)
{
- a[i].f[2*j] = (in[3*j] | ((uint16_t)in[3*j+1] << 8)) & 0xfff;
- a[i].f[2*j+1] = ((in[3*j+1] >> 4) | ((uint16_t)in[3*j+2] << 4)) & 0xfff;
+ f0 = ( in[3*j] | ((uint16_t)in[3*j+1] << 8)) & 0xfff;
+ f1 = ((in[3*j+1] >> 4) | ((uint16_t)in[3*j+2] << 4)) & 0xfff;
+ a[i].f[2*j] = ml_reduce_modq(f0);
+ a[i].f[2*j+1] = ml_reduce_modq(f1);
}
in += ML_KEM_POLY_LEN;
}