3535 * of mlkem-native (e.g. with varying security levels)
3636 * within a single compilation unit. */
3737#define mlk_check_pct MLK_ADD_PARAM_SET(mlk_check_pct) MLK_CONTEXT_PARAMETERS_2
38- #define mlk_serialize_poly_16le MLK_ADD_PARAM_SET(mlk_serialize_poly_16le )
39- #define mlk_deserialize_poly_16le MLK_ADD_PARAM_SET(mlk_deserialize_poly_16le )
38+ #define mlk_serialize_epp MLK_ADD_PARAM_SET(mlk_serialize_epp )
39+ #define mlk_deserialize_epp MLK_ADD_PARAM_SET(mlk_deserialize_epp )
4040#define mlk_serialize_polyvec_16le MLK_ADD_PARAM_SET(mlk_serialize_polyvec_16le)
4141#define mlk_deserialize_polyvec_16le \
4242 MLK_ADD_PARAM_SET(mlk_deserialize_polyvec_16le)
@@ -289,10 +289,8 @@ int mlk_kem_keypair(uint8_t pk[MLKEM_INDCCA_PUBLICKEYBYTES],
289289#endif /* !MLK_CONFIG_NO_RANDOMIZED_API */
290290
291291/* Reference: `crypto_kem_enc_derand()` in the reference implementation @[REF]
292- * - We include public key check (via mlk_kem_enc_v)
293- * - We include stack buffer zeroization
294- * Implemented via mlk_kem_enc_derand_u + mlk_kem_enc_v to share
295- * a single code path with the incremental API. */
292+ * - We include public key check
293+ * - We include stack buffer zeroization */
296294MLK_EXTERNAL_API
297295int mlk_kem_enc_derand (uint8_t ct [MLKEM_INDCCA_CIPHERTEXTBYTES ],
298296 uint8_t ss [MLKEM_SSBYTES ],
@@ -301,36 +299,42 @@ int mlk_kem_enc_derand(uint8_t ct[MLKEM_INDCCA_CIPHERTEXTBYTES],
301299 MLK_CONFIG_CONTEXT_PARAMETER_TYPE context )
302300{
303301 int ret = 0 ;
304- MLK_ALLOC (hpk , uint8_t , MLKEM_SYMBYTES , context );
305- MLK_ALLOC (sp_serial , uint8_t , MLKEM_POLYVEC16_BYTES , context );
306- MLK_ALLOC (epp_serial , uint8_t , MLKEM_POLY16_BYTES , context );
302+ MLK_ALLOC (buf , uint8_t , 2 * MLKEM_SYMBYTES , context );
303+ MLK_ALLOC (kr , uint8_t , 2 * MLKEM_SYMBYTES , context );
307304
308- if (hpk == NULL || sp_serial == NULL || epp_serial == NULL )
305+ if (buf == NULL || kr == NULL )
309306 {
310307 ret = MLK_ERR_OUT_OF_MEMORY ;
311308 goto cleanup ;
312309 }
313310
314- mlk_hash_h (hpk , pk , MLKEM_INDCCA_PUBLICKEYBYTES );
311+ /* Specification: Implements @[FIPS203, Section 7.2, Modulus check] */
312+ ret = mlk_kem_check_pk (pk , context );
313+ if (ret != 0 )
314+ {
315+ goto cleanup ;
316+ }
317+
318+ mlk_memcpy (buf , coins , MLKEM_SYMBYTES );
315319
316- ret = mlk_kem_enc_derand_u (ct , ss , sp_serial , epp_serial ,
317- pk + MLKEM_POLYVECBYTES , hpk , coins , context );
320+ /* Multitarget countermeasure for coins + contributory KEM */
321+ mlk_hash_h (buf + MLKEM_SYMBYTES , pk , MLKEM_INDCCA_PUBLICKEYBYTES );
322+ mlk_hash_g (kr , buf , 2 * MLKEM_SYMBYTES );
323+
324+ /* coins are in kr+MLKEM_SYMBYTES */
325+ ret = mlk_indcpa_enc (ct , buf , pk , kr + MLKEM_SYMBYTES , context );
318326 if (ret != 0 )
319327 {
320328 goto cleanup ;
321329 }
322330
323- /* Specification: Modulus check @[FIPS203, Section 7.2] is
324- * performed inside mlk_kem_enc_v on pk (= ek_vector). */
325- ret = mlk_kem_enc_v (ct + MLKEM_POLYVECCOMPRESSEDBYTES_DU , sp_serial ,
326- epp_serial , coins , pk , context );
331+ mlk_memcpy (ss , kr , MLKEM_SYMBYTES );
327332
328333cleanup :
329334 /* Specification: Partially implements
330335 * @[FIPS203, Section 3.3, Destruction of intermediate values] */
331- MLK_FREE (epp_serial , uint8_t , MLKEM_POLY16_BYTES , context );
332- MLK_FREE (sp_serial , uint8_t , MLKEM_POLYVEC16_BYTES , context );
333- MLK_FREE (hpk , uint8_t , MLKEM_SYMBYTES , context );
336+ MLK_FREE (kr , uint8_t , 2 * MLKEM_SYMBYTES , context );
337+ MLK_FREE (buf , uint8_t , 2 * MLKEM_SYMBYTES , context );
334338 return ret ;
335339}
336340
@@ -370,56 +374,88 @@ int mlk_kem_enc(uint8_t ct[MLKEM_INDCCA_CIPHERTEXTBYTES],
370374}
371375#endif /* !MLK_CONFIG_NO_RANDOMIZED_API */
372376
373- /* 16 -bit little-endian serialization for intermediate state .
374- * Stores each int16_t coefficient as 2 bytes in LE order. */
375- static void mlk_serialize_poly_16le ( uint8_t out [ MLKEM_POLY16_BYTES ],
376- const mlk_poly * p )
377+ /* 4 -bit packing for noise polynomial e2 .
378+ * Coefficients in [-ETA2, ETA2] are stored as (ETA2 - x), fitting in 4 bits.
379+ * Two coefficients per byte (low nibble first). */
380+ static void mlk_serialize_epp ( uint8_t out [ MLKEM_EPP_BYTES ], const mlk_poly * p )
377381{
378382 unsigned j ;
379- for (j = 0 ; j < MLKEM_N ; j ++ )
383+ for (j = 0 ; j < MLKEM_N / 2 ; j ++ )
384+ __loop__ (
385+ assigns (j , memory_slice (out , MLKEM_EPP_BYTES ))
386+ invariant (j <= MLKEM_N / 2 ))
380387 {
381- uint16_t c = (uint16_t ) p -> coeffs [j ] ;
382- out [ 2 * j ] = (uint8_t )(c & 0xFF );
383- out [2 * j + 1 ] = (uint8_t )(c >> 8 );
388+ uint8_t lo = (uint8_t )( MLKEM_ETA2 - p -> coeffs [2 * j ]) ;
389+ uint8_t hi = (uint8_t )(MLKEM_ETA2 - p -> coeffs [ 2 * j + 1 ] );
390+ out [j ] = ( lo & 0xF ) | (uint8_t )(hi << 4 );
384391 }
385392}
386393
387- static void mlk_deserialize_poly_16le (mlk_poly * p ,
388- const uint8_t in [MLKEM_POLY16_BYTES ])
394+ static void mlk_deserialize_epp (mlk_poly * p ,
395+ const uint8_t in [MLKEM_EPP_BYTES ])
389396{
390397 unsigned j ;
391- for (j = 0 ; j < MLKEM_N ; j ++ )
398+ for (j = 0 ; j < MLKEM_N / 2 ; j ++ )
399+ __loop__ (
400+ assigns (j , memory_slice (p , sizeof (mlk_poly )))
401+ invariant (j <= MLKEM_N / 2 )
402+ invariant (array_abs_bound (p -> coeffs , 0 , 2 * j , 16 )))
392403 {
393- p -> coeffs [j ] =
394- ( int16_t )(( uint16_t ) in [2 * j ] | (( uint16_t ) in [2 * j + 1 ] << 8 ) );
404+ p -> coeffs [2 * j ] = ( int16_t ) MLKEM_ETA2 - ( int16_t )( in [ j ] & 0xF );
405+ p -> coeffs [2 * j + 1 ] = ( int16_t ) MLKEM_ETA2 - ( int16_t )( in [j ] >> 4 );
395406 }
396407}
397408
409+ /* 16-bit little-endian serialization for intermediate polyvec state.
410+ * Stores each int16_t coefficient as 2 bytes in LE order.
411+ * Coefficients must be non-negative (e.g., after reduce). */
398412static void mlk_serialize_polyvec_16le (uint8_t out [MLKEM_POLYVEC16_BYTES ],
399413 const mlk_polyvec * v )
400414{
401- unsigned i ;
415+ unsigned i , j ;
402416 for (i = 0 ; i < MLKEM_K ; i ++ )
417+ __loop__ (
418+ assigns (i , j , memory_slice (out , MLKEM_POLYVEC16_BYTES ))
419+ invariant (i <= MLKEM_K ))
403420 {
404- mlk_serialize_poly_16le (out + i * MLKEM_POLY16_BYTES , & v -> vec [i ]);
421+ for (j = 0 ; j < MLKEM_N ; j ++ )
422+ __loop__ (
423+ assigns (j , memory_slice (out , MLKEM_POLYVEC16_BYTES ))
424+ invariant (j <= MLKEM_N ))
425+ {
426+ uint16_t c = (uint16_t )v -> vec [i ].coeffs [j ];
427+ out [i * MLKEM_POLY16_BYTES + 2 * j ] = (uint8_t )(c & 0xFF );
428+ out [i * MLKEM_POLY16_BYTES + 2 * j + 1 ] = (uint8_t )(c >> 8 );
429+ }
405430 }
406431}
407432
408433static void mlk_deserialize_polyvec_16le (mlk_polyvec * v ,
409434 const uint8_t in [MLKEM_POLYVEC16_BYTES ])
410435{
411- unsigned i ;
436+ unsigned i , j ;
412437 for (i = 0 ; i < MLKEM_K ; i ++ )
438+ __loop__ (
439+ assigns (i , j , memory_slice (v , sizeof (mlk_polyvec )))
440+ invariant (i <= MLKEM_K ))
413441 {
414- mlk_deserialize_poly_16le (& v -> vec [i ], in + i * MLKEM_POLY16_BYTES );
442+ for (j = 0 ; j < MLKEM_N ; j ++ )
443+ __loop__ (
444+ assigns (j , memory_slice (v , sizeof (mlk_polyvec )))
445+ invariant (j <= MLKEM_N ))
446+ {
447+ v -> vec [i ].coeffs [j ] = mlk_cast_uint16_to_int16 ((uint16_t )(
448+ (unsigned )in [i * MLKEM_POLY16_BYTES + 2 * j ] |
449+ ((unsigned )in [i * MLKEM_POLY16_BYTES + 2 * j + 1 ] << 8 )));
450+ }
415451 }
416452}
417453
418454MLK_INTERNAL_API
419455int mlk_kem_enc_derand_u (uint8_t ct_u [MLKEM_POLYVECCOMPRESSEDBYTES_DU ],
420456 uint8_t ss [MLKEM_SSBYTES ],
421457 uint8_t sp_serial [MLKEM_POLYVEC16_BYTES ],
422- uint8_t epp_serial [MLKEM_POLY16_BYTES ],
458+ uint8_t epp_serial [MLKEM_EPP_BYTES ],
423459 const uint8_t seed [MLKEM_SYMBYTES ],
424460 const uint8_t hpk [MLKEM_SYMBYTES ],
425461 const uint8_t coins [MLKEM_SYMBYTES ],
@@ -449,9 +485,12 @@ int mlk_kem_enc_derand_u(uint8_t ct_u[MLKEM_POLYVECCOMPRESSEDBYTES_DU],
449485 goto cleanup ;
450486 }
451487
452- /* Serialize intermediate state (little endian) */
488+ /* Reduce sp to unsigned canonical form before serialization */
489+ mlk_polyvec_reduce (sp );
490+
491+ /* Serialize intermediate state */
453492 mlk_serialize_polyvec_16le (sp_serial , sp );
454- mlk_serialize_poly_16le (epp_serial , epp );
493+ mlk_serialize_epp (epp_serial , epp );
455494
456495 /* Shared secret K = first MLKEM_SYMBYTES bytes of G output */
457496 mlk_memcpy (ss , kr , MLKEM_SYMBYTES );
@@ -469,7 +508,7 @@ int mlk_kem_enc_derand_u(uint8_t ct_u[MLKEM_POLYVECCOMPRESSEDBYTES_DU],
469508MLK_INTERNAL_API
470509int mlk_kem_enc_v (uint8_t ct_v [MLKEM_POLYCOMPRESSEDBYTES_DV ],
471510 const uint8_t sp_serial [MLKEM_POLYVEC16_BYTES ],
472- const uint8_t epp_serial [MLKEM_POLY16_BYTES ],
511+ const uint8_t epp_serial [MLKEM_EPP_BYTES ],
473512 const uint8_t coins [MLKEM_SYMBYTES ],
474513 const uint8_t ek_vector [MLKEM_POLYVECBYTES ],
475514 MLK_CONFIG_CONTEXT_PARAMETER_TYPE context )
@@ -499,9 +538,9 @@ int mlk_kem_enc_v(uint8_t ct_v[MLKEM_POLYCOMPRESSEDBYTES_DV],
499538 goto cleanup ;
500539 }
501540
502- /* Deserialize intermediate state via 16-bit LE encoding */
541+ /* Deserialize intermediate state */
503542 mlk_deserialize_polyvec_16le (sp , sp_serial );
504- mlk_deserialize_poly_16le (epp , epp_serial );
543+ mlk_deserialize_epp (epp , epp_serial );
505544
506545 ret = mlk_indcpa_enc_v (ct_v , sp , epp , coins , ek_vector , context );
507546
@@ -590,7 +629,7 @@ int mlk_kem_dec(uint8_t ss[MLKEM_SSBYTES],
590629/* To facilitate single-compilation-unit (SCU) builds, undefine all macros.
591630 * Don't modify by hand -- this is auto-generated by scripts/autogen. */
592631#undef mlk_check_pct
593- #undef mlk_serialize_poly_16le
594- #undef mlk_deserialize_poly_16le
632+ #undef mlk_serialize_epp
633+ #undef mlk_deserialize_epp
595634#undef mlk_serialize_polyvec_16le
596635#undef mlk_deserialize_polyvec_16le
0 commit comments