Skip to content

Commit d737fde

Browse files
committed
passing pq handshake
1 parent c46c75e commit d737fde

File tree

9 files changed

+425
-252
lines changed

9 files changed

+425
-252
lines changed

crypto/s2n_ecc_evp.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,11 @@ static int s2n_ecc_evp_compute_shared_secret(EVP_PKEY *own_key, EVP_PKEY *peer_p
253253
int s2n_ecc_evp_generate_ephemeral_key(struct s2n_ecc_evp_params *ecc_evp_params)
254254
{
255255
POSIX_ENSURE_REF(ecc_evp_params->negotiated_curve);
256+
/* Skip ECDHE key generation for MLKEM placeholder curve */
257+
if (ecc_evp_params->negotiated_curve == &s2n_ecc_curve_mlkem_placeholder) {
258+
return S2N_SUCCESS;
259+
}
260+
256261
S2N_ERROR_IF(ecc_evp_params->evp_pkey != NULL, S2N_ERR_ECDHE_GEN_KEY);
257262
S2N_ERROR_IF(s2n_ecc_evp_generate_own_key(ecc_evp_params->negotiated_curve, &ecc_evp_params->evp_pkey) != 0,
258263
S2N_ERR_ECDHE_GEN_KEY);
@@ -423,6 +428,12 @@ int s2n_ecc_evp_write_params_point(struct s2n_ecc_evp_params *ecc_evp_params, st
423428
{
424429
POSIX_ENSURE_REF(ecc_evp_params);
425430
POSIX_ENSURE_REF(ecc_evp_params->negotiated_curve);
431+
432+
/* Skip EC point write for MLKEM placeholder curve */
433+
if (ecc_evp_params->negotiated_curve == &s2n_ecc_curve_mlkem_placeholder) {
434+
return S2N_SUCCESS;
435+
}
436+
426437
POSIX_ENSURE_REF(ecc_evp_params->evp_pkey);
427438
POSIX_ENSURE_REF(out);
428439

tests/unit/s2n_tls13_pq_handshake_test.c

Lines changed: 77 additions & 174 deletions
Large diffs are not rendered by default.

tls/extensions/s2n_client_key_share.c

Lines changed: 151 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
138171
static 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+
239335
static 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
*/
431537
static 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

tls/extensions/s2n_client_supported_groups.c

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,19 +61,36 @@ static int s2n_client_supported_groups_send(struct s2n_connection *conn, struct
6161
struct s2n_stuffer_reservation group_list_len = { 0 };
6262
POSIX_GUARD(s2n_stuffer_reserve_uint16(out, &group_list_len));
6363

64+
printf(" DEBUG: Client sending supported_groups:\n");
65+
6466
/* Send KEM groups list first */
6567
if (s2n_connection_get_protocol_version(conn) >= S2N_TLS13 && s2n_pq_is_enabled()) {
6668
for (size_t i = 0; i < kem_pref->tls13_kem_group_count; i++) {
6769
if (!s2n_kem_group_is_available(kem_pref->tls13_kem_groups[i])) {
6870
continue;
6971
}
72+
73+
printf(" KEM group[%zu]: %s (iana=%d)\n",
74+
i,
75+
kem_pref->tls13_kem_groups[i]->name,
76+
kem_pref->tls13_kem_groups[i]->iana_id);
77+
7078
POSIX_GUARD(s2n_stuffer_write_uint16(out, kem_pref->tls13_kem_groups[i]->iana_id));
7179
}
7280
}
7381

7482
/* Then send curve list */
7583
for (size_t i = 0; i < ecc_pref->count; i++) {
76-
POSIX_GUARD(s2n_stuffer_write_uint16(out, ecc_pref->ecc_curves[i]->iana_id));
84+
const struct s2n_ecc_named_curve *curve = ecc_pref->ecc_curves[i];
85+
86+
if (curve == &s2n_ecc_curve_mlkem_placeholder) {
87+
printf(" Skipping ECC placeholder curve: %s\n", curve->name);
88+
continue;
89+
}
90+
91+
printf(" ECC curve[%zu]: %s (iana=%d)\n",
92+
i, curve->name, curve->iana_id);
93+
POSIX_GUARD(s2n_stuffer_write_uint16(out, curve->iana_id));
7794
}
7895

7996
POSIX_GUARD(s2n_stuffer_write_vector_size(&group_list_len));

tls/extensions/s2n_key_share.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ S2N_RESULT s2n_ecdhe_send_public_key(struct s2n_ecc_evp_params *ecc_evp_params,
2626
RESULT_ENSURE_REF(ecc_evp_params);
2727
RESULT_ENSURE_REF(ecc_evp_params->negotiated_curve);
2828

29+
/* Skip ECDHE KeyShare for pure ML-KEM placeholder curve */
30+
if (ecc_evp_params->negotiated_curve == &s2n_ecc_curve_mlkem_placeholder) {
31+
return S2N_RESULT_OK;
32+
}
33+
2934
if (len_prefixed) {
3035
RESULT_GUARD_POSIX(s2n_stuffer_write_uint16(out, ecc_evp_params->negotiated_curve->share_size));
3136
}

0 commit comments

Comments
 (0)