@@ -67,6 +67,12 @@ static int s2n_generate_default_ecc_key_share(struct s2n_connection *conn, struc
67
67
POSIX_GUARD (s2n_connection_get_ecc_preferences (conn , & ecc_pref ));
68
68
POSIX_ENSURE_REF (ecc_pref );
69
69
70
+ /* Skip if the highest-priority curve is the mlkem_placeholder */
71
+ if (ecc_pref -> count > 0 && ecc_pref -> ecc_curves [0 ] == & s2n_ecc_curve_mlkem_placeholder ) {
72
+ printf (" DEBUG: Skipping standalone ECC key share for MLKEM placeholder\n" );
73
+ return S2N_SUCCESS ;
74
+ }
75
+
70
76
/* We only ever send a single EC key share: either the share requested by the server
71
77
* during a retry, or the most preferred share according to local preferences.
72
78
*/
@@ -135,6 +141,32 @@ static int s2n_generate_pq_hybrid_key_share(struct s2n_stuffer *out, struct s2n_
135
141
return S2N_SUCCESS ;
136
142
}
137
143
144
+ static int s2n_generate_pure_pq_key_share (struct s2n_stuffer * out ,
145
+ struct s2n_kem_group_params * kem_group_params )
146
+ {
147
+ POSIX_ENSURE_REF (out );
148
+ POSIX_ENSURE_REF (kem_group_params );
149
+ POSIX_ENSURE_REF (kem_group_params -> kem_group );
150
+ POSIX_ENSURE_REF (kem_group_params -> kem_group -> kem );
151
+
152
+ /* Write group ID */
153
+ POSIX_GUARD (s2n_stuffer_write_uint16 (out , kem_group_params -> kem_group -> iana_id ));
154
+
155
+ /* Reserve space for total share size */
156
+ struct s2n_stuffer_reservation total_share_size = { 0 };
157
+ POSIX_GUARD (s2n_stuffer_reserve_uint16 (out , & total_share_size ));
158
+
159
+ /* Write KEM public key */
160
+ struct s2n_kem_params * kem_params = & kem_group_params -> kem_params ;
161
+ kem_params -> kem = kem_group_params -> kem_group -> kem ;
162
+ POSIX_GUARD (s2n_kem_send_public_key (out , kem_params ));
163
+
164
+ /* Fill in the reserved size field */
165
+ POSIX_GUARD (s2n_stuffer_write_vector_size (& total_share_size ));
166
+
167
+ return S2N_SUCCESS ;
168
+ }
169
+
138
170
static int s2n_generate_default_pq_hybrid_key_share (struct s2n_connection * conn , struct s2n_stuffer * out )
139
171
{
140
172
POSIX_ENSURE_REF (conn );
@@ -187,7 +219,14 @@ static int s2n_generate_default_pq_hybrid_key_share(struct s2n_connection *conn,
187
219
client_params -> kem_params .len_prefixed = s2n_tls13_client_must_use_hybrid_kem_length_prefix (kem_pref );
188
220
}
189
221
190
- POSIX_GUARD (s2n_generate_pq_hybrid_key_share (out , client_params ));
222
+ /* Special case: Pure MLKEM (no ECC portion) */
223
+ if (client_params -> kem_group == & s2n_pure_mlkem_1024 ||
224
+ client_params -> kem_group -> curve == & s2n_ecc_curve_mlkem_placeholder ) {
225
+ printf (" DEBUG: Skipping ECC portion for Pure MLKEM\n" );
226
+ POSIX_GUARD (s2n_generate_pure_pq_key_share (out , client_params ));
227
+ } else {
228
+ POSIX_GUARD (s2n_generate_pq_hybrid_key_share (out , client_params ));
229
+ }
191
230
192
231
return S2N_SUCCESS ;
193
232
}
@@ -223,6 +262,12 @@ static int s2n_client_key_share_parse_ecc(struct s2n_stuffer *key_share, const s
223
262
POSIX_ENSURE_REF (curve );
224
263
POSIX_ENSURE_REF (ecc_params );
225
264
265
+ if (curve == & s2n_ecc_curve_mlkem_placeholder ) {
266
+ printf ("DEBUG: Skipping KeyShare parse for MLKEM placeholder\n" );
267
+ ecc_params -> negotiated_curve = curve ;
268
+ return S2N_SUCCESS ;
269
+ }
270
+
226
271
struct s2n_blob point_blob = { 0 };
227
272
POSIX_GUARD (s2n_ecc_evp_read_params_point (key_share , curve -> share_size , & point_blob ));
228
273
@@ -236,6 +281,56 @@ static int s2n_client_key_share_parse_ecc(struct s2n_stuffer *key_share, const s
236
281
return S2N_SUCCESS ;
237
282
}
238
283
284
+ static int s2n_client_key_share_recv_pure_kem (
285
+ struct s2n_connection * conn ,
286
+ struct s2n_stuffer * key_share ,
287
+ uint16_t kem_group_iana_id )
288
+ {
289
+ POSIX_ENSURE_REF (conn );
290
+ POSIX_ENSURE_REF (key_share );
291
+
292
+ if (kem_group_iana_id != s2n_pure_mlkem_1024 .iana_id ) {
293
+ return S2N_SUCCESS ; /* not a pure KEM share */
294
+ }
295
+
296
+ printf (" DEBUG: Entering pure MLKEM recv: named_group=%u, expected_iana=%u\n" ,
297
+ kem_group_iana_id , s2n_pure_mlkem_1024 .iana_id );
298
+
299
+ struct s2n_kem_group_params * client_params = & conn -> kex_params .client_kem_group_params ;
300
+ client_params -> kem_group = & s2n_pure_mlkem_1024 ;
301
+ client_params -> ecc_params .negotiated_curve = & s2n_ecc_curve_mlkem_placeholder ;
302
+ client_params -> kem_params .kem = s2n_pure_mlkem_1024 .kem ;
303
+
304
+ uint16_t actual_share_size = key_share -> blob .size ;
305
+ uint16_t unprefixed_size = client_params -> kem_params .kem -> public_key_length ;
306
+ uint16_t prefixed_size = S2N_SIZE_OF_KEY_SHARE_SIZE + unprefixed_size ;
307
+
308
+ printf (" DEBUG: actual_share_size=%u, unprefixed_size=%u, prefixed_size=%u\n" ,
309
+ actual_share_size , unprefixed_size , prefixed_size );
310
+
311
+ if (actual_share_size != unprefixed_size && actual_share_size != prefixed_size ) {
312
+ printf (" DEBUG: Share size mismatch — ignoring key share\n" );
313
+ return S2N_SUCCESS ;
314
+ }
315
+
316
+ client_params -> kem_params .len_prefixed = (actual_share_size == prefixed_size );
317
+ printf (" DEBUG: len_prefixed=%d\n" , client_params -> kem_params .len_prefixed );
318
+
319
+ POSIX_GUARD (s2n_kem_recv_public_key (key_share , & client_params -> kem_params ));
320
+ printf (" DEBUG: KEM public key parsed, size=%u\n" ,
321
+ client_params -> kem_params .public_key .size );
322
+
323
+ printf (" DEBUG: key_share.read_cursor=%u, key_share.write_cursor=%u\n" ,
324
+ key_share -> read_cursor , key_share -> write_cursor );
325
+
326
+ printf (" DEBUG: remaining bytes in key_share=%u\n" ,
327
+ s2n_stuffer_data_available (key_share ));
328
+ POSIX_ENSURE (s2n_stuffer_data_available (key_share ) == 0 , S2N_ERR_BAD_MESSAGE );
329
+
330
+ printf (" DEBUG: Finished pure MLKEM recv successfully\n" );
331
+ return S2N_SUCCESS ;
332
+ }
333
+
239
334
static int s2n_client_key_share_recv_ecc (struct s2n_connection * conn , struct s2n_stuffer * key_share , uint16_t curve_iana_id )
240
335
{
241
336
POSIX_ENSURE_REF (conn );
@@ -286,6 +381,11 @@ static int s2n_client_key_share_recv_ecc(struct s2n_connection *conn, struct s2n
286
381
287
382
DEFER_CLEANUP (struct s2n_ecc_evp_params new_client_params = { 0 }, s2n_ecc_evp_params_free );
288
383
384
+ if (curve == & s2n_ecc_curve_mlkem_placeholder ) {
385
+ printf ("DEBUG: Skipping ECC recv for MLKEM placeholder\n" );
386
+ return S2N_SUCCESS ;
387
+ }
388
+
289
389
POSIX_GUARD (s2n_client_key_share_parse_ecc (key_share , curve , & new_client_params ));
290
390
/* negotiated_curve will be NULL if the key share was not parsed successfully */
291
391
if (!new_client_params .negotiated_curve ) {
@@ -312,6 +412,11 @@ static int s2n_client_key_share_recv_hybrid_partial_ecc(struct s2n_stuffer *key_
312
412
POSIX_ENSURE (ec_share_size == kem_group -> curve -> share_size , S2N_ERR_SIZE_MISMATCH );
313
413
}
314
414
415
+ if (kem_group -> curve == & s2n_ecc_curve_mlkem_placeholder ) {
416
+ printf ("DEBUG: Skipping Hybrid ECC recv for MLKEM placeholder\n" );
417
+ return S2N_SUCCESS ;
418
+ }
419
+
315
420
POSIX_GUARD (s2n_client_key_share_parse_ecc (key_share , kem_group -> curve , & new_client_params -> ecc_params ));
316
421
317
422
/* If we were unable to parse the EC portion of the share, negotiated_curve
@@ -453,8 +558,26 @@ static int s2n_client_key_share_recv(struct s2n_connection *conn, struct s2n_stu
453
558
POSIX_GUARD (s2n_stuffer_skip_write (& key_share , share_size ));
454
559
keyshare_count ++ ;
455
560
456
- /* Try to parse the share as ECC, then as PQ/hybrid; will ignore
457
- * shares for unrecognized groups. */
561
+ if (named_group == s2n_pure_mlkem_1024 .iana_id ) {
562
+ printf ("DEBUG[key_share_recv]: Detected pure MLKEM group, calling pure_kem recv\n" );
563
+ POSIX_GUARD (s2n_client_key_share_recv_pure_kem (conn , & key_share , named_group ));
564
+
565
+ printf ("DEBUG[key_share_recv]: After pure_kem -> extension_read=%u extension_write=%u extension_available=%u\n" ,
566
+ extension -> read_cursor , extension -> write_cursor , s2n_stuffer_data_available (extension ));
567
+
568
+ if (s2n_stuffer_data_available (extension ) > 0 ) {
569
+ printf ("DEBUG[key_share_recv]: Leftover bytes in extension: " );
570
+ for (uint32_t i = 0 ; i < s2n_stuffer_data_available (extension ); i ++ ) {
571
+ uint8_t b = extension -> blob .data [extension -> read_cursor + i ];
572
+ printf ("%02x " , b );
573
+ }
574
+ printf ("\n" );
575
+ }
576
+
577
+ printf ("DEBUG[key_share_recv]: pure_kem recv success\n" );
578
+ continue ; /* skip ECC/hybrid parsing */
579
+ }
580
+
458
581
POSIX_GUARD (s2n_client_key_share_recv_ecc (conn , & key_share , named_group ));
459
582
POSIX_GUARD (s2n_client_key_share_recv_pq_hybrid (conn , & key_share , named_group ));
460
583
}
0 commit comments