21const uint8_t ALERT_UNEXPECTED_MESSAGE = 10;
22const uint8_t ALERT_HANDSHAKE_FAILURE = 40;
23const uint8_t ALERT_DECODE_ERROR = 50;
24const uint8_t ALERT_DECRYPT_ERROR = 51;
25const uint8_t ALERT_PROTOCOL_VERSION = 70;
26const uint8_t ALERT_INTERNAL_ERROR = 80;
30const uint64_t DTLS_HRR_COOKIE_MAX_AGE_MS = 60000;
33bool is_ciphertext(uint8_t b0)
35 return (b0 & 0xE0) == 0x20;
41size_t ciphertext_record_len(
const uint8_t *rec,
size_t avail,
size_t cid_len)
49 off += (b0 & 0x08) ? 2 : 1;
54 size_t enc = ((size_t)rec[off] << 8) | rec[off + 1];
56 if (off + enc > avail)
63int fail(DtlsConn *c, uint8_t alert)
65 c->state = DtlsConnState::FAILED;
78void flight_reset(DtlsConn *c)
89bool flight_add(DtlsConn *c, uint16_t epoch,
const uint8_t *tls_msg,
size_t tls_len)
91 if (tls_len < 4 || c->flight_count >= DTLS_FLIGHT_MSGS)
93 uint8_t msg_type = tls_msg[0];
94 uint32_t body_len = (uint32_t)(tls_len - 4);
95 uint16_t msg_seq = c->tx_msg_seq++;
96 size_t flen = dtls_hs_frag_build(msg_type, msg_seq, body_len, 0, tls_msg + 4, body_len,
97 c->flight_buf + c->flight_len,
sizeof(c->flight_buf) - c->flight_len);
100 c->flight_msgs[c->flight_count].off = c->flight_len;
101 c->flight_msgs[c->flight_count].len = (uint16_t)flen;
102 c->flight_msgs[c->flight_count].epoch = (uint8_t)epoch;
104 c->flight_len = (uint16_t)(c->flight_len + flen);
112bool flight_transmit(DtlsConn *c, uint8_t *out,
size_t out_cap,
size_t *out_len)
114 for (uint8_t i = 0; i < c->flight_count; i++)
116 const uint8_t *frag = c->flight_buf + c->flight_msgs[i].off;
117 size_t flen = c->flight_msgs[i].len;
118 uint8_t epoch = c->flight_msgs[i].epoch;
123 seq = c->tx_seq_ep0++;
124 rn = dtls_plaintext_build(DTLS_CT_HANDSHAKE, 0, seq, frag, flen, out + *out_len, out_cap - *out_len);
128 seq = c->tx_seq_ep2++;
129 rn = dtls_ciphertext_protect(&c->ep2_srv, seq, DTLS_CT_HANDSHAKE, frag, flen, out + *out_len,
130 out_cap - *out_len, c->cid_negotiated ? c->peer_cid : nullptr,
131 c->cid_negotiated ? c->peer_cid_len : 0);
136 c->flight_rec[i].epoch = epoch;
137 c->flight_rec[i].seq = seq;
143void flight_arm(DtlsConn *c)
145 c->awaiting_reply =
true;
147 c->pto_ms = DTLS_PTO_INITIAL_MS;
152void flight_disarm(DtlsConn *c)
154 c->awaiting_reply =
false;
161int send_hello_retry(DtlsConn *c,
const Tls13ClientHello *ch,
const uint8_t *ch1,
size_t ch1_len, uint8_t *out,
162 size_t out_cap,
size_t *out_len)
171 size_t n = tls13_build_message_hash(c->msgbuf,
sizeof(c->msgbuf), ch1_hash);
173 return fail(c, ALERT_INTERNAL_ERROR);
178 uint8_t cookie[DTLS_COOKIE_MAX];
179 size_t clen = dtls_cookie_make(c->cfg.cookie_key,
detws_millis(),
nullptr, 0, c->peer_addr, c->peer_addr_len,
180 cookie,
sizeof(cookie));
182 return fail(c, ALERT_INTERNAL_ERROR);
184 n = tls13_build_hello_retry_request(c->msgbuf,
sizeof(c->msgbuf), ch->session_id, ch->session_id_len,
185 TLS_GROUP_X25519, cookie, clen,
true);
187 return fail(c, ALERT_INTERNAL_ERROR);
190 if (!flight_add(c, 0, c->msgbuf, n) || !flight_transmit(c, out, out_cap, out_len))
191 return fail(c, ALERT_INTERNAL_ERROR);
199bool dtls_hrr_cookie_ok(
const DtlsConn *c,
const Tls13ClientHello *ch)
206 dtls_cookie_verify(c->cfg.cookie_key,
detws_millis(), DTLS_HRR_COOKIE_MAX_AGE_MS, c->peer_addr,
207 c->peer_addr_len, ch->cookie, ch->cookie_len, payload,
sizeof(payload), &plen);
213void dtls_negotiate_conn_id(DtlsConn *c,
const Tls13ClientHello *ch)
215 if (!ch->has_conn_id || ch->conn_id_len > DTLS_CID_MAX)
217 c->cid_negotiated =
true;
218 c->peer_cid_len = (uint8_t)ch->conn_id_len;
220 memcpy(c->peer_cid, ch->conn_id, ch->conn_id_len);
221 c->local_cid_len = DTLS_CONN_LOCAL_CID_LEN;
222 memcpy(c->local_cid, c->cfg.server_random, DTLS_CONN_LOCAL_CID_LEN);
229int handle_client_hello(DtlsConn *c,
const uint8_t *msg,
size_t msg_len, uint8_t *out,
size_t out_cap,
size_t *out_len)
232 if (!tls13_parse_client_hello(msg, msg_len, &ch,
true))
233 return fail(c, ALERT_DECODE_ERROR);
234 if (!ch.offers_tls13)
235 return fail(c, ALERT_PROTOCOL_VERSION);
236 if (!ch.offers_ed25519 || !ch.offers_x25519)
237 return fail(c, ALERT_HANDSHAKE_FAILURE);
239 uint16_t ch_seq = c->reasm.msg_seq;
244 if (!ch.has_key_share)
247 return fail(c, ALERT_HANDSHAKE_FAILURE);
248 if (send_hello_retry(c, &ch, msg, msg_len, out, out_cap, out_len) < 0)
250 c->next_recv_msg_seq = (uint16_t)(ch_seq + 1);
251 dtls_hs_reasm_init(&c->reasm, c->next_recv_msg_seq, c->reasm_buf + 4, DTLS_CONN_REASM_CAP);
257 if (!dtls_hrr_cookie_ok(c, &ch))
258 return fail(c, ALERT_HANDSHAKE_FAILURE);
260 dtls_negotiate_conn_id(c, &ch);
264 uint8_t server_share[32];
265 ssh_x25519(ecdhe, c->cfg.ephemeral_priv, ch.client_x25519);
274 tls13_build_server_hello(c->msgbuf,
sizeof(c->msgbuf), c->cfg.server_random, ch.session_id, ch.session_id_len,
275 server_share, 32, TLS_GROUP_X25519,
true,
276 c->cid_negotiated ? c->local_cid : nullptr, c->cid_negotiated ? c->local_cid_len : 0);
278 return fail(c, ALERT_INTERNAL_ERROR);
280 if (!flight_add(c, 0, c->msgbuf, n))
281 return fail(c, ALERT_INTERNAL_ERROR);
285 snapshot(&c->transcript, hash);
286 tls13_ks_early(&DTLS13_KDF, &c->ks);
287 tls13_ks_handshake(&c->ks, ecdhe, hash, 32);
288 dtls_record_keys_derive(&c->ep2_srv, DtlsCipher::AES_128_GCM_SHA256, 2, c->ks.server_hs_traffic);
289 dtls_record_keys_derive(&c->ep2_cli, DtlsCipher::AES_128_GCM_SHA256, 2, c->ks.client_hs_traffic);
293 n = tls13_build_encrypted_extensions_empty(c->msgbuf,
sizeof(c->msgbuf));
295 if (!flight_add(c, 2, c->msgbuf, n))
296 return fail(c, ALERT_INTERNAL_ERROR);
299 n = tls13_build_certificate(c->msgbuf,
sizeof(c->msgbuf), c->cfg.cert_der, c->cfg.cert_len);
301 return fail(c, ALERT_INTERNAL_ERROR);
303 if (!flight_add(c, 2, c->msgbuf, n))
304 return fail(c, ALERT_INTERNAL_ERROR);
307 snapshot(&c->transcript, hash);
308 n = tls13_build_cert_verify(c->msgbuf,
sizeof(c->msgbuf), hash, c->cfg.ed25519_seed);
310 return fail(c, ALERT_INTERNAL_ERROR);
312 if (!flight_add(c, 2, c->msgbuf, n))
313 return fail(c, ALERT_INTERNAL_ERROR);
316 snapshot(&c->transcript, hash);
318 tls13_finished_mac(&DTLS13_KDF, c->ks.server_hs_traffic, hash, verify);
319 n = tls13_build_finished(c->msgbuf,
sizeof(c->msgbuf), verify);
321 if (!flight_add(c, 2, c->msgbuf, n))
322 return fail(c, ALERT_INTERNAL_ERROR);
326 snapshot(&c->transcript, c->hs_finished_hash);
327 tls13_ks_master(&c->ks, c->hs_finished_hash);
328 dtls_record_keys_derive(&c->ep3_srv, DtlsCipher::AES_128_GCM_SHA256, 3, c->ks.server_ap_traffic);
329 dtls_record_keys_derive(&c->ep3_cli, DtlsCipher::AES_128_GCM_SHA256, 3, c->ks.client_ap_traffic);
332 if (!flight_transmit(c, out, out_cap, out_len))
333 return fail(c, ALERT_INTERNAL_ERROR);
335 c->state = DtlsConnState::WAIT_FINISHED;
336 c->next_recv_msg_seq = (uint16_t)(ch_seq + 1);
337 dtls_hs_reasm_init(&c->reasm, c->next_recv_msg_seq, c->reasm_buf + 4, DTLS_CONN_REASM_CAP);
342int handle_client_finished(DtlsConn *c,
const uint8_t *msg,
size_t msg_len)
345 return fail(c, ALERT_DECODE_ERROR);
347 tls13_finished_mac(&DTLS13_KDF, c->ks.client_hs_traffic, c->hs_finished_hash, expected);
350 diff |= (uint8_t)(expected[i] ^ msg[4 + i]);
352 return fail(c, ALERT_DECRYPT_ERROR);
354 c->state = DtlsConnState::DONE;
358 dtls_hs_reasm_init(&c->reasm, c->next_recv_msg_seq, c->reasm_buf + 4, DTLS_CONN_REASM_CAP);
362int dispatch_message(DtlsConn *c,
const uint8_t *tls_msg,
size_t tls_len, uint8_t *out,
size_t out_cap,
size_t *out_len)
364 if (c->state == DtlsConnState::START && tls_msg[0] == TlsHs::TLS_HS_CLIENT_HELLO)
365 return handle_client_hello(c, tls_msg, tls_len, out, out_cap, out_len);
366 if (c->state == DtlsConnState::WAIT_FINISHED && tls_msg[0] == TlsHs::TLS_HS_FINISHED)
367 return handle_client_finished(c, tls_msg, tls_len);
368 if (c->state == DtlsConnState::DONE && tls_msg[0] == TlsHs::TLS_HS_FINISHED)
370 c->hs_ack_sent =
false;
371 dtls_hs_reasm_init(&c->reasm, c->next_recv_msg_seq, c->reasm_buf + 4,
372 DTLS_CONN_REASM_CAP);
375 return fail(c, ALERT_UNEXPECTED_MESSAGE);
380int drive_handshake(DtlsConn *c,
const uint8_t *payload,
size_t plen, uint8_t *out,
size_t out_cap,
size_t *out_len)
386 size_t used = dtls_hs_header_parse(payload + p, plen - p, &hh);
390 int r = dtls_hs_reasm_add(&c->reasm, &hh);
392 return fail(c, ALERT_DECODE_ERROR);
396 c->reasm_buf[0] = c->reasm.msg_type;
397 c->reasm_buf[1] = (uint8_t)(c->reasm.length >> 16);
398 c->reasm_buf[2] = (uint8_t)(c->reasm.length >> 8);
399 c->reasm_buf[3] = (uint8_t)c->reasm.length;
400 if (dispatch_message(c, c->reasm_buf, 4 + c->reasm.length, out, out_cap, out_len) < 0)
410void process_ack(DtlsConn *c,
const uint8_t *body,
size_t len)
412 if (!c->awaiting_reply)
414 DtlsRecordNumber acked[16];
416 if (!dtls_ack_parse(body, len, acked, 16, &count))
418 for (uint8_t i = 0; i < c->flight_count; i++)
421 for (
size_t j = 0; j < count; j++)
422 if (acked[j].epoch == c->flight_rec[i].epoch && acked[j].seq == c->flight_rec[i].seq)
434enum class DtlsRecStep
442DtlsRecStep process_ciphertext_record(DtlsConn *c,
const uint8_t *dgram,
size_t len,
size_t *off, uint8_t *out,
443 size_t out_cap,
size_t *out_len)
445 size_t rlen = ciphertext_record_len(dgram + *off, len - *off, c->cid_negotiated ? c->local_cid_len : 0);
447 return DtlsRecStep::STOP;
450 fail(c, ALERT_UNEXPECTED_MESSAGE);
451 return DtlsRecStep::FATAL;
453 uint8_t inner[DTLS_CONN_REASM_CAP + DTLS_TAG_LEN];
455 uint64_t next = c->replay_ep2.seeded ? c->replay_ep2.highest + 1 : 0;
456 if (!dtls_ciphertext_unprotect(&c->ep2_cli, next, dgram + *off, rlen, inner,
sizeof(inner), &info,
457 c->cid_negotiated ? c->local_cid : nullptr,
458 c->cid_negotiated ? c->local_cid_len : 0))
460 fail(c, ALERT_DECRYPT_ERROR);
461 return DtlsRecStep::FATAL;
464 if (!dtls_replay_check(&c->replay_ep2, info.seq))
465 return DtlsRecStep::NEXT;
466 dtls_replay_mark(&c->replay_ep2, info.seq);
467 bool is_hs = (info.content_type == DTLS_CT_HANDSHAKE);
469 c->rx_ep2_seq = info.seq;
470 if (info.content_type == DTLS_CT_ACK)
471 process_ack(c, inner, info.pt_len);
472 else if (is_hs && drive_handshake(c, inner, info.pt_len, out, out_cap, out_len) < 0)
473 return DtlsRecStep::FATAL;
474 return DtlsRecStep::NEXT;
478DtlsRecStep process_plaintext_record(DtlsConn *c,
const uint8_t *dgram,
size_t len,
size_t *off, uint8_t *out,
479 size_t out_cap,
size_t *out_len)
482 size_t rlen = dtls_plaintext_parse(dgram + *off, len - *off, &pt);
484 return DtlsRecStep::STOP;
486 if (pt.content_type == DTLS_CT_HANDSHAKE && drive_handshake(c, pt.fragment, pt.frag_len, out, out_cap, out_len) < 0)
487 return DtlsRecStep::FATAL;
488 return DtlsRecStep::NEXT;
494void maybe_send_completion_ack(DtlsConn *c, uint8_t *out,
size_t out_cap,
size_t *out_len)
496 if (!dtls_conn_established(c) || c->hs_ack_sent)
498 DtlsRecordNumber rn = {2, c->rx_ep2_seq};
499 uint8_t ack_body[2 + 16];
500 size_t bl = dtls_ack_build(&rn, 1, ack_body,
sizeof(ack_body));
501 size_t rec = dtls_ciphertext_protect(&c->ep3_srv, c->tx_seq_ep3++, DTLS_CT_ACK, ack_body, bl, out + *out_len,
502 out_cap - *out_len, c->cid_negotiated ? c->peer_cid : nullptr,
503 c->cid_negotiated ? c->peer_cid_len : 0);
507 c->hs_ack_sent =
true;
512void dtls_conn_init(DtlsConn *c,
const DtlsServerConfig *cfg,
const uint8_t *peer_addr,
size_t peer_addr_len)
514 memset(c, 0,
sizeof(*c));
516 c->state = DtlsConnState::START;
517 if (peer_addr && peer_addr_len)
519 if (peer_addr_len > DTLS_PEER_ADDR_MAX)
520 peer_addr_len = DTLS_PEER_ADDR_MAX;
521 memcpy(c->peer_addr, peer_addr, peer_addr_len);
522 c->peer_addr_len = (uint8_t)peer_addr_len;
525 dtls_replay_init(&c->replay_ep2);
526 dtls_replay_init(&c->replay_ep3);
527 c->next_recv_msg_seq = 0;
528 dtls_hs_reasm_init(&c->reasm, 0, c->reasm_buf + 4, DTLS_CONN_REASM_CAP);
531int dtls_conn_process(DtlsConn *c,
const uint8_t *dgram,
size_t len, uint8_t *out,
size_t out_cap)
533 if (c->state == DtlsConnState::FAILED)
539 DtlsRecStep step = is_ciphertext(dgram[off])
540 ? process_ciphertext_record(c, dgram, len, &off, out, out_cap, &out_len)
541 : process_plaintext_record(c, dgram, len, &off, out, out_cap, &out_len);
542 if (step == DtlsRecStep::FATAL)
544 if (step == DtlsRecStep::STOP)
548 maybe_send_completion_ack(c, out, out_cap, &out_len);
552int dtls_conn_timeout_ms(
const DtlsConn *c)
554 if (!c->awaiting_reply || c->state == DtlsConnState::FAILED || c->state == DtlsConnState::DONE)
557 int32_t remaining = (int32_t)(c->flight_sent_ms + c->pto_ms -
detws_millis());
558 return remaining > 0 ? remaining : 0;
561int dtls_conn_on_timeout(DtlsConn *c, uint8_t *out,
size_t out_cap)
563 if (!c->awaiting_reply || c->state == DtlsConnState::FAILED || c->state == DtlsConnState::DONE)
565 if ((int32_t)(
detws_millis() - (c->flight_sent_ms + c->pto_ms)) < 0)
567 if (c->retransmits >= DTLS_MAX_RETRANSMITS)
570 c->state = DtlsConnState::FAILED;
571 c->awaiting_reply =
false;
575 if (!flight_transmit(c, out, out_cap, &out_len))
578 c->pto_ms = c->pto_ms >= DTLS_PTO_MAX_MS / 2 ? DTLS_PTO_MAX_MS : c->pto_ms * 2;
583bool dtls_conn_established(
const DtlsConn *c)
585 return c->state == DtlsConnState::DONE && c->ep3_ready;
588uint8_t dtls_conn_alert(
const DtlsConn *c)
593const DtlsRecordKeys *dtls_conn_app_write_keys(
const DtlsConn *c)
595 return c->ep3_ready ? &c->ep3_srv :
nullptr;
598const DtlsRecordKeys *dtls_conn_app_read_keys(
const DtlsConn *c)
600 return c->ep3_ready ? &c->ep3_cli :
nullptr;
603size_t dtls_conn_local_cid(
const DtlsConn *c, uint8_t *out)
605 if (!c->cid_negotiated || c->local_cid_len == 0)
607 memcpy(out, c->local_cid, c->local_cid_len);
608 return c->local_cid_len;
611bool dtls_conn_open_app(DtlsConn *c,
const uint8_t *rec,
size_t rec_len, uint8_t *out,
size_t out_cap,
size_t *out_len)
613 if (!dtls_conn_established(c))
616 uint64_t next = c->replay_ep3.seeded ? c->replay_ep3.highest + 1 : 0;
617 if (!dtls_ciphertext_unprotect(&c->ep3_cli, next, rec, rec_len, out, out_cap, &info,
618 c->cid_negotiated ? c->local_cid : nullptr,
619 c->cid_negotiated ? c->local_cid_len : 0))
621 if (!dtls_replay_check(&c->replay_ep3, info.seq))
623 dtls_replay_mark(&c->replay_ep3, info.seq);
624 if (info.content_type != DTLS_CT_APPLICATION_DATA)
626 *out_len = info.pt_len;
630size_t dtls_conn_seal_app(DtlsConn *c,
const uint8_t *data,
size_t len, uint8_t *out,
size_t out_cap)
632 if (!dtls_conn_established(c))
635 return dtls_ciphertext_protect(&c->ep3_srv, c->tx_seq_ep3++, DTLS_CT_APPLICATION_DATA, data, len, out, out_cap,
636 c->cid_negotiated ? c->peer_cid : nullptr, c->cid_negotiated ? c->peer_cid_len : 0);
Pluggable monotonic clock for all library timing.
uint32_t detws_millis(void)
The library's monotonic time at 1000 Hz (milliseconds).
DTLS 1.3 server handshake state machine (RFC 9147 §5-6).
void ssh_x25519_base(uint8_t out[32], const uint8_t scalar[32])
X25519 with the standard base point u=9: out = scalar * G.
void ssh_x25519(uint8_t out[32], const uint8_t scalar[32], const uint8_t point[32])
X25519 scalar multiplication: out = scalar * point (RFC 7748 §5).
Curve25519 field arithmetic + X25519 (RFC 7748) for the curve25519-sha256 KEX.
void ssh_sha256_init(SshSha256Ctx *ctx)
Initialize a streaming SHA-256 context.
void ssh_sha256_update(SshSha256Ctx *ctx, const uint8_t *data, size_t len)
Feed len bytes of data into the running hash.
void ssh_sha256_final(SshSha256Ctx *ctx, uint8_t digest[SSH_SHA256_DIGEST_LEN])
Finalize the hash and write the 32-byte digest.
#define SSH_SHA256_DIGEST_LEN
SHA-256 digest length in bytes.
Streaming SHA-256 context.
TLS 1.3 handshake messages for the QUIC handshake (RFC 8446 sec 4).