@@ -67,6 +67,13 @@ static int s2n_generate_default_ecc_key_share(struct s2n_connection *conn, struc
6767 POSIX_GUARD (s2n_connection_get_ecc_preferences (conn , & ecc_pref ));
6868 POSIX_ENSURE_REF (ecc_pref );
6969
70+ printf (" DEBUG: s2n_generate_default_ecc_key_share\n" );
71+ /* Skip if the highest-priority curve is the mlkem_placeholder */
72+ if (ecc_pref -> count > 0 && ecc_pref -> ecc_curves [0 ] == & s2n_ecc_curve_mlkem_placeholder ) {
73+ printf (" DEBUG: Skipping standalone ECC key share for MLKEM placeholder\n" );
74+ return S2N_SUCCESS ;
75+ }
76+
7077 /* We only ever send a single EC key share: either the share requested by the server
7178 * during a retry, or the most preferred share according to local preferences.
7279 */
@@ -135,6 +142,32 @@ static int s2n_generate_pq_hybrid_key_share(struct s2n_stuffer *out, struct s2n_
135142 return S2N_SUCCESS ;
136143}
137144
145+ static int s2n_generate_pure_pq_key_share (struct s2n_stuffer * out ,
146+ struct s2n_kem_group_params * kem_group_params )
147+ {
148+ POSIX_ENSURE_REF (out );
149+ POSIX_ENSURE_REF (kem_group_params );
150+ POSIX_ENSURE_REF (kem_group_params -> kem_group );
151+ POSIX_ENSURE_REF (kem_group_params -> kem_group -> kem );
152+
153+ /* Write group ID */
154+ POSIX_GUARD (s2n_stuffer_write_uint16 (out , kem_group_params -> kem_group -> iana_id ));
155+
156+ /* Reserve space for total share size */
157+ struct s2n_stuffer_reservation total_share_size = { 0 };
158+ POSIX_GUARD (s2n_stuffer_reserve_uint16 (out , & total_share_size ));
159+
160+ /* Write KEM public key */
161+ struct s2n_kem_params * kem_params = & kem_group_params -> kem_params ;
162+ kem_params -> kem = kem_group_params -> kem_group -> kem ;
163+ POSIX_GUARD (s2n_kem_send_public_key (out , kem_params ));
164+
165+ /* Fill in the reserved size field */
166+ POSIX_GUARD (s2n_stuffer_write_vector_size (& total_share_size ));
167+
168+ return S2N_SUCCESS ;
169+ }
170+
138171static int s2n_generate_default_pq_hybrid_key_share (struct s2n_connection * conn , struct s2n_stuffer * out )
139172{
140173 POSIX_ENSURE_REF (conn );
@@ -187,7 +220,14 @@ static int s2n_generate_default_pq_hybrid_key_share(struct s2n_connection *conn,
187220 client_params -> kem_params .len_prefixed = s2n_tls13_client_must_use_hybrid_kem_length_prefix (kem_pref );
188221 }
189222
190- POSIX_GUARD (s2n_generate_pq_hybrid_key_share (out , client_params ));
223+ /* Special case: Pure MLKEM (no ECC portion) */
224+ if (client_params -> kem_group == & s2n_pure_mlkem_1024 ||
225+ client_params -> kem_group -> curve == & s2n_ecc_curve_mlkem_placeholder ) {
226+ printf (" DEBUG: Skipping ECC portion for Pure MLKEM\n" );
227+ POSIX_GUARD (s2n_generate_pure_pq_key_share (out , client_params ));
228+ } else {
229+ POSIX_GUARD (s2n_generate_pq_hybrid_key_share (out , client_params ));
230+ }
191231
192232 return S2N_SUCCESS ;
193233}
@@ -223,6 +263,12 @@ static int s2n_client_key_share_parse_ecc(struct s2n_stuffer *key_share, const s
223263 POSIX_ENSURE_REF (curve );
224264 POSIX_ENSURE_REF (ecc_params );
225265
266+ if (curve == & s2n_ecc_curve_mlkem_placeholder ) {
267+ printf ("DEBUG: Skipping KeyShare parse for MLKEM placeholder\n" );
268+ ecc_params -> negotiated_curve = curve ;
269+ return S2N_SUCCESS ;
270+ }
271+
226272 struct s2n_blob point_blob = { 0 };
227273 POSIX_GUARD (s2n_ecc_evp_read_params_point (key_share , curve -> share_size , & point_blob ));
228274
@@ -236,6 +282,56 @@ static int s2n_client_key_share_parse_ecc(struct s2n_stuffer *key_share, const s
236282 return S2N_SUCCESS ;
237283}
238284
285+ static int s2n_client_key_share_recv_pure_kem (
286+ struct s2n_connection * conn ,
287+ struct s2n_stuffer * key_share ,
288+ uint16_t kem_group_iana_id )
289+ {
290+ POSIX_ENSURE_REF (conn );
291+ POSIX_ENSURE_REF (key_share );
292+
293+ if (kem_group_iana_id != s2n_pure_mlkem_1024 .iana_id ) {
294+ return S2N_SUCCESS ; /* not a pure KEM share */
295+ }
296+
297+ printf (" DEBUG: Entering pure MLKEM recv: named_group=%u, expected_iana=%u\n" ,
298+ kem_group_iana_id , s2n_pure_mlkem_1024 .iana_id );
299+
300+ struct s2n_kem_group_params * client_params = & conn -> kex_params .client_kem_group_params ;
301+ client_params -> kem_group = & s2n_pure_mlkem_1024 ;
302+ client_params -> ecc_params .negotiated_curve = & s2n_ecc_curve_mlkem_placeholder ;
303+ client_params -> kem_params .kem = s2n_pure_mlkem_1024 .kem ;
304+
305+ uint16_t actual_share_size = key_share -> blob .size ;
306+ uint16_t unprefixed_size = client_params -> kem_params .kem -> public_key_length ;
307+ uint16_t prefixed_size = S2N_SIZE_OF_KEY_SHARE_SIZE + unprefixed_size ;
308+
309+ printf (" DEBUG: actual_share_size=%u, unprefixed_size=%u, prefixed_size=%u\n" ,
310+ actual_share_size , unprefixed_size , prefixed_size );
311+
312+ if (actual_share_size != unprefixed_size && actual_share_size != prefixed_size ) {
313+ printf (" DEBUG: Share size mismatch — ignoring key share\n" );
314+ return S2N_SUCCESS ;
315+ }
316+
317+ client_params -> kem_params .len_prefixed = (actual_share_size == prefixed_size );
318+ printf (" DEBUG: len_prefixed=%d\n" , client_params -> kem_params .len_prefixed );
319+
320+ POSIX_GUARD (s2n_kem_recv_public_key (key_share , & client_params -> kem_params ));
321+ printf (" DEBUG: KEM public key parsed, size=%u\n" ,
322+ client_params -> kem_params .public_key .size );
323+
324+ printf (" DEBUG: key_share.read_cursor=%u, key_share.write_cursor=%u\n" ,
325+ key_share -> read_cursor , key_share -> write_cursor );
326+
327+ printf (" DEBUG: remaining bytes in key_share=%u\n" ,
328+ s2n_stuffer_data_available (key_share ));
329+ POSIX_ENSURE (s2n_stuffer_data_available (key_share ) == 0 , S2N_ERR_BAD_MESSAGE );
330+
331+ printf (" DEBUG: Finished pure MLKEM recv successfully\n" );
332+ return S2N_SUCCESS ;
333+ }
334+
239335static int s2n_client_key_share_recv_ecc (struct s2n_connection * conn , struct s2n_stuffer * key_share , uint16_t curve_iana_id )
240336{
241337 POSIX_ENSURE_REF (conn );
@@ -286,6 +382,11 @@ static int s2n_client_key_share_recv_ecc(struct s2n_connection *conn, struct s2n
286382
287383 DEFER_CLEANUP (struct s2n_ecc_evp_params new_client_params = { 0 }, s2n_ecc_evp_params_free );
288384
385+ if (curve == & s2n_ecc_curve_mlkem_placeholder ) {
386+ printf ("DEBUG: Skipping ECC recv for MLKEM placeholder\n" );
387+ return S2N_SUCCESS ;
388+ }
389+
289390 POSIX_GUARD (s2n_client_key_share_parse_ecc (key_share , curve , & new_client_params ));
290391 /* negotiated_curve will be NULL if the key share was not parsed successfully */
291392 if (!new_client_params .negotiated_curve ) {
@@ -312,6 +413,11 @@ static int s2n_client_key_share_recv_hybrid_partial_ecc(struct s2n_stuffer *key_
312413 POSIX_ENSURE (ec_share_size == kem_group -> curve -> share_size , S2N_ERR_SIZE_MISMATCH );
313414 }
314415
416+ if (kem_group -> curve == & s2n_ecc_curve_mlkem_placeholder ) {
417+ printf ("DEBUG: Skipping Hybrid ECC recv for MLKEM placeholder\n" );
418+ return S2N_SUCCESS ;
419+ }
420+
315421 POSIX_GUARD (s2n_client_key_share_parse_ecc (key_share , kem_group -> curve , & new_client_params -> ecc_params ));
316422
317423 /* If we were unable to parse the EC portion of the share, negotiated_curve
@@ -430,11 +536,15 @@ static int s2n_client_key_share_recv_pq_hybrid(struct s2n_connection *conn, stru
430536 */
431537static int s2n_client_key_share_recv (struct s2n_connection * conn , struct s2n_stuffer * extension )
432538{
539+ printf ("DEBUG[key_share_recv]: ENTER\n" );
433540 POSIX_ENSURE_REF (conn );
434541 POSIX_ENSURE_REF (extension );
435542
436543 uint16_t key_shares_size = 0 ;
544+ printf ("DEBUG[key_share_recv]: Reading key_shares_size...\n" );
437545 POSIX_GUARD (s2n_stuffer_read_uint16 (extension , & key_shares_size ));
546+ printf ("DEBUG[key_share_recv]: key_shares_size=%u, available=%u\n" ,
547+ key_shares_size , s2n_stuffer_data_available (extension ));
438548 POSIX_ENSURE (s2n_stuffer_data_available (extension ) == key_shares_size , S2N_ERR_BAD_MESSAGE );
439549
440550 uint16_t named_group = 0 , share_size = 0 ;
@@ -443,8 +553,13 @@ static int s2n_client_key_share_recv(struct s2n_connection *conn, struct s2n_stu
443553
444554 uint16_t keyshare_count = 0 ;
445555 while (s2n_stuffer_data_available (extension ) > 0 ) {
556+ printf ("DEBUG[key_share_recv]: Loop start, available=%u\n" ,
557+ s2n_stuffer_data_available (extension ));
558+
446559 POSIX_GUARD (s2n_stuffer_read_uint16 (extension , & named_group ));
447560 POSIX_GUARD (s2n_stuffer_read_uint16 (extension , & share_size ));
561+ printf ("DEBUG[key_share_recv]: named_group=%u share_size=%u\n" , named_group , share_size );
562+
448563 POSIX_ENSURE (s2n_stuffer_data_available (extension ) >= share_size , S2N_ERR_BAD_MESSAGE );
449564
450565 POSIX_GUARD (s2n_blob_init (& key_share_blob ,
@@ -453,30 +568,53 @@ static int s2n_client_key_share_recv(struct s2n_connection *conn, struct s2n_stu
453568 POSIX_GUARD (s2n_stuffer_skip_write (& key_share , share_size ));
454569 keyshare_count ++ ;
455570
456- /* Try to parse the share as ECC, then as PQ/hybrid; will ignore
457- * shares for unrecognized groups. */
571+ printf ("DEBUG[key_share_recv]: kem_group=%s curve=%s\n" ,
572+ conn -> kex_params .server_kem_group_params .kem_group ?
573+ conn -> kex_params .server_kem_group_params .kem_group -> name : "NULL" ,
574+ conn -> kex_params .server_kem_group_params .kem_group &&
575+ conn -> kex_params .server_kem_group_params .kem_group -> curve ?
576+ conn -> kex_params .server_kem_group_params .kem_group -> curve -> name : "NULL" );
577+
578+ if (named_group == s2n_pure_mlkem_1024 .iana_id ) {
579+ printf ("DEBUG[key_share_recv]: Detected pure MLKEM group, calling pure_kem recv\n" );
580+ POSIX_GUARD (s2n_client_key_share_recv_pure_kem (conn , & key_share , named_group ));
581+
582+ printf ("DEBUG[key_share_recv]: After pure_kem -> extension_read=%u extension_write=%u extension_available=%u\n" ,
583+ extension -> read_cursor , extension -> write_cursor , s2n_stuffer_data_available (extension ));
584+
585+ if (s2n_stuffer_data_available (extension ) > 0 ) {
586+ printf ("DEBUG[key_share_recv]: Leftover bytes in extension: " );
587+ for (uint32_t i = 0 ; i < s2n_stuffer_data_available (extension ); i ++ ) {
588+ uint8_t b = extension -> blob .data [extension -> read_cursor + i ];
589+ printf ("%02x " , b );
590+ }
591+ printf ("\n" );
592+ }
593+
594+ printf ("DEBUG[key_share_recv]: pure_kem recv success\n" );
595+ continue ; /* skip ECC/hybrid parsing */
596+ }
597+
598+ printf ("DEBUG[key_share_recv]: Calling ECC recv...\n" );
458599 POSIX_GUARD (s2n_client_key_share_recv_ecc (conn , & key_share , named_group ));
600+ printf ("DEBUG[key_share_recv]: ECC recv done\n" );
601+
602+ printf ("DEBUG[key_share_recv]: Calling PQ/hybrid recv...\n" );
459603 POSIX_GUARD (s2n_client_key_share_recv_pq_hybrid (conn , & key_share , named_group ));
604+ printf ("DEBUG[key_share_recv]: PQ/hybrid recv done\n" );
460605 }
461606
462- /* During a retry, the client should only have sent one keyshare */
607+ printf ( "DEBUG[key_share_recv]: Loop done, keyshare_count=%u\n" , keyshare_count );
463608 POSIX_ENSURE (!s2n_is_hello_retry_handshake (conn ) || keyshare_count == 1 , S2N_ERR_BAD_MESSAGE );
464609
465- /**
466- * If there were no matching key shares, then we received an empty key share extension
467- * or we didn't match a key share with a supported group. We should send a retry.
468- *
469- *= https://www.rfc-editor.org/rfc/rfc8446#4.1.1
470- *# If the server selects an (EC)DHE group and the client did not offer a
471- *# compatible "key_share" extension in the initial ClientHello, the
472- *# server MUST respond with a HelloRetryRequest (Section 4.1.4) message.
473- **/
474610 struct s2n_ecc_evp_params * client_ecc_params = & conn -> kex_params .client_ecc_evp_params ;
475611 struct s2n_kem_group_params * client_pq_params = & conn -> kex_params .client_kem_group_params ;
476612 if (!client_pq_params -> kem_group && !client_ecc_params -> negotiated_curve ) {
613+ printf ("DEBUG[key_share_recv]: No matching key shares, setting HRR required\n" );
477614 POSIX_GUARD (s2n_set_hello_retry_required (conn ));
478615 }
479616
617+ printf ("DEBUG[key_share_recv]: EXIT SUCCESS\n" );
480618 return S2N_SUCCESS ;
481619}
482620
0 commit comments