22const uint8_t ALERT_UNEXPECTED_MESSAGE = 10;
23const uint8_t ALERT_HANDSHAKE_FAILURE = 40;
24const uint8_t ALERT_DECODE_ERROR = 50;
25const uint8_t ALERT_DECRYPT_ERROR = 51;
26const uint8_t ALERT_PROTOCOL_VERSION = 70;
27const uint8_t ALERT_INTERNAL_ERROR = 80;
31const uint64_t DTLS_HRR_COOKIE_MAX_AGE_MS = 60000;
34bool is_ciphertext(uint8_t b0)
36 return (b0 & 0xE0) == 0x20;
42size_t ciphertext_record_len(
const uint8_t *rec,
size_t avail,
size_t cid_len)
54 off += (b0 & 0x08) ? 2 : 1;
61 size_t enc = ((size_t)rec[off] << 8) | rec[off + 1];
63 if (off + enc > avail)
72int fail(DtlsConn *c, uint8_t alert)
74 c->state = DtlsConnState::FAILED;
87void flight_reset(DtlsConn *c)
98bool flight_add(DtlsConn *c, uint16_t epoch,
const uint8_t *tls_msg,
size_t tls_len)
109 if (tls_len < 4 || c->flight_count >= PC_DTLS_FLIGHT_MSGS)
113 uint8_t msg_type = tls_msg[0];
114 uint32_t body_len = (uint32_t)(tls_len - 4);
115 uint16_t msg_seq = c->tx_msg_seq++;
116 size_t flen = pc_dtls_hs_frag_build(msg_type, msg_seq, body_len, 0, tls_msg + 4, body_len,
117 c->flight_buf + c->flight_len,
sizeof(c->flight_buf) - c->flight_len);
122 c->flight_msgs[c->flight_count].off = c->flight_len;
123 c->flight_msgs[c->flight_count].len = (uint16_t)flen;
124 c->flight_msgs[c->flight_count].epoch = (uint8_t)epoch;
126 c->flight_len = (uint16_t)(c->flight_len + flen);
134bool flight_transmit(DtlsConn *c, uint8_t *out,
size_t out_cap,
size_t *out_len)
136 for (uint8_t i = 0; i < c->flight_count; i++)
138 const uint8_t *frag = c->flight_buf + c->flight_msgs[i].off;
139 size_t flen = c->flight_msgs[i].len;
140 uint8_t epoch = c->flight_msgs[i].epoch;
145 seq = c->tx_seq_ep0++;
146 rn = pc_dtls_plaintext_build(PC_DTLS_CT_HANDSHAKE, 0, seq, frag, flen, out + *out_len, out_cap - *out_len);
150 seq = c->tx_seq_ep2++;
151 rn = pc_dtls_ciphertext_protect(c->ep2_srv, seq, PC_DTLS_CT_HANDSHAKE, frag, flen, out + *out_len,
152 out_cap - *out_len, c->cid_negotiated ? c->peer_cid : nullptr,
153 c->cid_negotiated ? c->peer_cid_len : 0);
160 c->flight_rec[i].epoch = epoch;
161 c->flight_rec[i].seq = seq;
167void flight_arm(DtlsConn *c)
169 c->awaiting_reply =
true;
171 c->pto_ms = PC_DTLS_PTO_INITIAL_MS;
176void flight_disarm(DtlsConn *c)
178 c->awaiting_reply =
false;
185int send_hello_retry(DtlsConn *c,
const Tls13ClientHello *ch,
const uint8_t *ch1,
size_t ch1_len, uint8_t *out,
186 size_t out_cap,
size_t *out_len)
195 size_t n = pc_tls13_build_message_hash(c->msgbuf,
sizeof(c->msgbuf), ch1_hash);
198 return fail(c, ALERT_INTERNAL_ERROR);
204 uint8_t cookie[PC_DTLS_COOKIE_MAX];
205 size_t clen = pc_dtls_cookie_make(c->cfg.cookie_key,
pc_millis(),
nullptr, 0, c->peer_addr, c->peer_addr_len,
206 cookie,
sizeof(cookie));
209 return fail(c, ALERT_INTERNAL_ERROR);
212 n = pc_tls13_build_hello_retry_request(c->msgbuf,
sizeof(c->msgbuf), ch->session_id, ch->session_id_len,
213 TLS_GROUP_X25519, cookie, clen,
true);
216 return fail(c, ALERT_INTERNAL_ERROR);
222 if (!flight_add(c, 0, c->msgbuf, n) || !flight_transmit(c, out, out_cap, out_len))
224 return fail(c, ALERT_INTERNAL_ERROR);
233bool pc_dtls_hrr_cookie_ok(
const DtlsConn *c,
const Tls13ClientHello *ch)
242 pc_dtls_cookie_verify(c->cfg.cookie_key,
pc_millis(), DTLS_HRR_COOKIE_MAX_AGE_MS, c->peer_addr,
243 c->peer_addr_len, ch->cookie, ch->cookie_len, payload,
sizeof(payload), &plen);
249void pc_dtls_negotiate_conn_id(DtlsConn *c,
const Tls13ClientHello *ch)
251 if (!ch->has_conn_id || ch->conn_id_len > PC_DTLS_CID_MAX)
255 c->cid_negotiated =
true;
256 c->peer_cid_len = (uint8_t)ch->conn_id_len;
259 memcpy(c->peer_cid, ch->conn_id, ch->conn_id_len);
261 c->local_cid_len = PC_DTLS_CONN_LOCAL_CID_LEN;
262 memcpy(c->local_cid, c->cfg.server_random, PC_DTLS_CONN_LOCAL_CID_LEN);
269int handle_client_hello(DtlsConn *c,
const uint8_t *msg,
size_t msg_len, uint8_t *out,
size_t out_cap,
size_t *out_len)
272 if (!pc_tls13_parse_client_hello(msg, msg_len, &ch,
true))
274 return fail(c, ALERT_DECODE_ERROR);
276 if (!ch.offers_tls13)
278 return fail(c, ALERT_PROTOCOL_VERSION);
280 if (!ch.offers_ed25519 || !ch.offers_x25519)
282 return fail(c, ALERT_HANDSHAKE_FAILURE);
285 uint16_t ch_seq = c->reasm.msg_seq;
290 if (!ch.has_key_share)
294 return fail(c, ALERT_HANDSHAKE_FAILURE);
296 if (send_hello_retry(c, &ch, msg, msg_len, out, out_cap, out_len) < 0)
300 c->next_recv_msg_seq = (uint16_t)(ch_seq + 1);
301 pc_dtls_hs_reasm_init(&c->reasm, c->next_recv_msg_seq, c->reasm_buf + 4, PC_DTLS_CONN_REASM_CAP);
307 if (!pc_dtls_hrr_cookie_ok(c, &ch))
309 return fail(c, ALERT_HANDSHAKE_FAILURE);
312 pc_dtls_negotiate_conn_id(c, &ch);
316 uint8_t server_share[32];
317 pc_x25519(ecdhe, c->cfg.ephemeral_priv, ch.client_x25519);
325 size_t n = pc_tls13_build_server_hello(c->msgbuf,
sizeof(c->msgbuf), c->cfg.server_random, ch.session_id,
326 ch.session_id_len, server_share, 32, TLS_GROUP_X25519,
true,
327 c->cid_negotiated ? c->local_cid : nullptr,
328 c->cid_negotiated ? c->local_cid_len : 0);
331 return fail(c, ALERT_INTERNAL_ERROR);
334 if (!flight_add(c, 0, c->msgbuf, n))
336 return fail(c, ALERT_INTERNAL_ERROR);
341 snapshot(&c->transcript, hash);
342 pc_tls13_ks_early(&DTLS13_KDF, &c->ks);
343 pc_tls13_ks_handshake(&c->ks, ecdhe, hash, 32);
344 pc_dtls_record_keys_derive(&c->ep2_srv, DtlsCipher::AES_128_GCM_SHA256, 2, c->ks.server_hs_traffic);
345 pc_dtls_record_keys_derive(&c->ep2_cli, DtlsCipher::AES_128_GCM_SHA256, 2, c->ks.client_hs_traffic);
353 rpk = ch.offers_rpk_server_cert;
357 n = pc_tls13_build_encrypted_extensions_empty(c->msgbuf,
sizeof(c->msgbuf), rpk);
359 if (!flight_add(c, 2, c->msgbuf, n))
361 return fail(c, ALERT_INTERNAL_ERROR);
370 n = pc_tls13_build_certificate_rpk(c->msgbuf,
sizeof(c->msgbuf), ed_pub);
374 n = pc_tls13_build_certificate(c->msgbuf,
sizeof(c->msgbuf), c->cfg.cert_der, c->cfg.cert_len);
377 return fail(c, ALERT_INTERNAL_ERROR);
380 if (!flight_add(c, 2, c->msgbuf, n))
382 return fail(c, ALERT_INTERNAL_ERROR);
386 snapshot(&c->transcript, hash);
387 n = pc_tls13_build_cert_verify(c->msgbuf,
sizeof(c->msgbuf), hash, c->cfg.ed25519_seed);
390 return fail(c, ALERT_INTERNAL_ERROR);
393 if (!flight_add(c, 2, c->msgbuf, n))
395 return fail(c, ALERT_INTERNAL_ERROR);
399 snapshot(&c->transcript, hash);
401 pc_tls13_finished_mac(&DTLS13_KDF, c->ks.server_hs_traffic, hash, verify);
402 n = pc_tls13_build_finished(c->msgbuf,
sizeof(c->msgbuf), verify);
404 if (!flight_add(c, 2, c->msgbuf, n))
406 return fail(c, ALERT_INTERNAL_ERROR);
411 snapshot(&c->transcript, c->hs_finished_hash);
412 pc_tls13_ks_master(&c->ks, c->hs_finished_hash);
413 pc_dtls_record_keys_derive(&c->ep3_srv, DtlsCipher::AES_128_GCM_SHA256, 3, c->ks.server_ap_traffic);
414 pc_dtls_record_keys_derive(&c->ep3_cli, DtlsCipher::AES_128_GCM_SHA256, 3, c->ks.client_ap_traffic);
417 if (!flight_transmit(c, out, out_cap, out_len))
419 return fail(c, ALERT_INTERNAL_ERROR);
422 c->state = DtlsConnState::WAIT_FINISHED;
423 c->next_recv_msg_seq = (uint16_t)(ch_seq + 1);
424 pc_dtls_hs_reasm_init(&c->reasm, c->next_recv_msg_seq, c->reasm_buf + 4, PC_DTLS_CONN_REASM_CAP);
429int handle_client_finished(DtlsConn *c,
const uint8_t *msg,
size_t msg_len)
433 return fail(c, ALERT_DECODE_ERROR);
436 pc_tls13_finished_mac(&DTLS13_KDF, c->ks.client_hs_traffic, c->hs_finished_hash, expected);
440 diff |= (uint8_t)(expected[i] ^ msg[4 + i]);
444 return fail(c, ALERT_DECRYPT_ERROR);
447 c->state = DtlsConnState::DONE;
451 pc_dtls_hs_reasm_init(&c->reasm, c->next_recv_msg_seq, c->reasm_buf + 4, PC_DTLS_CONN_REASM_CAP);
455int dispatch_message(DtlsConn *c,
const uint8_t *tls_msg,
size_t tls_len, uint8_t *out,
size_t out_cap,
size_t *out_len)
457 if (c->state == DtlsConnState::START && tls_msg[0] == TlsHs::TLS_HS_CLIENT_HELLO)
459 return handle_client_hello(c, tls_msg, tls_len, out, out_cap, out_len);
461 if (c->state == DtlsConnState::WAIT_FINISHED && tls_msg[0] == TlsHs::TLS_HS_FINISHED)
463 return handle_client_finished(c, tls_msg, tls_len);
465 if (c->state == DtlsConnState::DONE && tls_msg[0] == TlsHs::TLS_HS_FINISHED)
467 c->hs_ack_sent =
false;
468 pc_dtls_hs_reasm_init(&c->reasm, c->next_recv_msg_seq, c->reasm_buf + 4,
469 PC_DTLS_CONN_REASM_CAP);
472 return fail(c, ALERT_UNEXPECTED_MESSAGE);
477int drive_handshake(DtlsConn *c,
const uint8_t *payload,
size_t plen, uint8_t *out,
size_t out_cap,
size_t *out_len)
483 size_t used = pc_dtls_hs_header_parse(payload + p, plen - p, &hh);
489 int r = pc_dtls_hs_reasm_add(&c->reasm, &hh);
492 return fail(c, ALERT_DECODE_ERROR);
497 c->reasm_buf[0] = c->reasm.msg_type;
498 c->reasm_buf[1] = (uint8_t)(c->reasm.length >> 16);
499 c->reasm_buf[2] = (uint8_t)(c->reasm.length >> 8);
500 c->reasm_buf[3] = (uint8_t)c->reasm.length;
501 if (dispatch_message(c, c->reasm_buf, 4 + c->reasm.length, out, out_cap, out_len) < 0)
513void process_ack(DtlsConn *c,
const uint8_t *body,
size_t len)
515 if (!c->awaiting_reply)
519 DtlsRecordNumber acked[16];
521 if (!pc_dtls_ack_parse(body, len, acked, 16, &count))
525 for (uint8_t i = 0; i < c->flight_count; i++)
528 for (
size_t j = 0; j < count; j++)
530 if (acked[j].epoch == c->flight_rec[i].epoch && acked[j].seq == c->flight_rec[i].seq)
545enum class DtlsRecStep
553DtlsRecStep process_ciphertext_record(DtlsConn *c,
const uint8_t *dgram,
size_t len,
size_t *off, uint8_t *out,
554 size_t out_cap,
size_t *out_len)
556 size_t rlen = ciphertext_record_len(dgram + *off, len - *off, c->cid_negotiated ? c->local_cid_len : 0);
559 return DtlsRecStep::STOP;
563 fail(c, ALERT_UNEXPECTED_MESSAGE);
564 return DtlsRecStep::FATAL;
566 uint8_t inner[PC_DTLS_CONN_REASM_CAP + PC_DTLS_TAG_LEN];
568 uint64_t next = c->replay_ep2.seeded ? c->replay_ep2.highest + 1 : 0;
569 if (!pc_dtls_ciphertext_unprotect(c->ep2_cli, next, dgram + *off, rlen, inner,
sizeof(inner), &info,
570 c->cid_negotiated ? c->local_cid : nullptr,
571 c->cid_negotiated ? c->local_cid_len : 0))
573 fail(c, ALERT_DECRYPT_ERROR);
574 return DtlsRecStep::FATAL;
577 if (!pc_dtls_replay_check(&c->replay_ep2, info.seq))
579 return DtlsRecStep::NEXT;
581 pc_dtls_replay_mark(&c->replay_ep2, info.seq);
582 bool is_hs = (info.content_type == PC_DTLS_CT_HANDSHAKE);
585 c->rx_ep2_seq = info.seq;
587 if (info.content_type == PC_DTLS_CT_ACK)
589 process_ack(c, inner, info.pt_len);
591 else if (is_hs && drive_handshake(c, inner, info.pt_len, out, out_cap, out_len) < 0)
593 return DtlsRecStep::FATAL;
595 return DtlsRecStep::NEXT;
599DtlsRecStep process_plaintext_record(DtlsConn *c,
const uint8_t *dgram,
size_t len,
size_t *off, uint8_t *out,
600 size_t out_cap,
size_t *out_len)
603 size_t rlen = pc_dtls_plaintext_parse(dgram + *off, len - *off, &pt);
606 return DtlsRecStep::STOP;
609 if (pt.content_type == PC_DTLS_CT_HANDSHAKE &&
610 drive_handshake(c, pt.fragment, pt.frag_len, out, out_cap, out_len) < 0)
612 return DtlsRecStep::FATAL;
614 return DtlsRecStep::NEXT;
620void maybe_send_completion_ack(DtlsConn *c, uint8_t *out,
size_t out_cap,
size_t *out_len)
622 if (!pc_dtls_conn_established(c) || c->hs_ack_sent)
626 DtlsRecordNumber rn = {2, c->rx_ep2_seq};
627 uint8_t ack_body[2 + 16];
628 size_t bl = pc_dtls_ack_build(&rn, 1, ack_body,
sizeof(ack_body));
629 size_t rec = pc_dtls_ciphertext_protect(c->ep3_srv, c->tx_seq_ep3++, PC_DTLS_CT_ACK, ack_body, bl, out + *out_len,
630 out_cap - *out_len, c->cid_negotiated ? c->peer_cid : nullptr,
631 c->cid_negotiated ? c->peer_cid_len : 0);
635 c->hs_ack_sent =
true;
640void pc_dtls_conn_init(DtlsConn *c,
const DtlsServerConfig *cfg,
const uint8_t *peer_addr,
size_t peer_addr_len)
642 memset(c, 0,
sizeof(*c));
644 c->state = DtlsConnState::START;
645 if (peer_addr && peer_addr_len)
647 if (peer_addr_len > PC_DTLS_PEER_ADDR_MAX)
649 peer_addr_len = PC_DTLS_PEER_ADDR_MAX;
651 memcpy(c->peer_addr, peer_addr, peer_addr_len);
652 c->peer_addr_len = (uint8_t)peer_addr_len;
655 pc_dtls_replay_init(&c->replay_ep2);
656 pc_dtls_replay_init(&c->replay_ep3);
657 c->next_recv_msg_seq = 0;
658 pc_dtls_hs_reasm_init(&c->reasm, 0, c->reasm_buf + 4, PC_DTLS_CONN_REASM_CAP);
661int pc_dtls_conn_process(DtlsConn *c,
const uint8_t *dgram,
size_t len, uint8_t *out,
size_t out_cap)
663 if (c->state == DtlsConnState::FAILED)
671 DtlsRecStep step = is_ciphertext(dgram[off])
672 ? process_ciphertext_record(c, dgram, len, &off, out, out_cap, &out_len)
673 : process_plaintext_record(c, dgram, len, &off, out, out_cap, &out_len);
674 if (step == DtlsRecStep::FATAL)
678 if (step == DtlsRecStep::STOP)
684 maybe_send_completion_ack(c, out, out_cap, &out_len);
688int pc_dtls_conn_timeout_ms(
const DtlsConn *c)
690 if (!c->awaiting_reply || c->state == DtlsConnState::FAILED || c->state == DtlsConnState::DONE)
695 int32_t remaining = (int32_t)(c->flight_sent_ms + c->pto_ms -
pc_millis());
696 return remaining > 0 ? remaining : 0;
699int pc_dtls_conn_on_timeout(DtlsConn *c, uint8_t *out,
size_t out_cap)
701 if (!c->awaiting_reply || c->state == DtlsConnState::FAILED || c->state == DtlsConnState::DONE)
705 if ((int32_t)(
pc_millis() - (c->flight_sent_ms + c->pto_ms)) < 0)
709 if (c->retransmits >= PC_DTLS_MAX_RETRANSMITS)
712 c->state = DtlsConnState::FAILED;
713 c->awaiting_reply =
false;
717 if (!flight_transmit(c, out, out_cap, &out_len))
722 c->pto_ms = c->pto_ms >= PC_DTLS_PTO_MAX_MS / 2 ? PC_DTLS_PTO_MAX_MS : c->pto_ms * 2;
727bool pc_dtls_conn_established(
const DtlsConn *c)
729 return c->state == DtlsConnState::DONE && c->ep3_ready;
732uint8_t pc_dtls_conn_alert(
const DtlsConn *c)
737DtlsRecordKeys *pc_dtls_conn_app_write_keys(DtlsConn *c)
739 return c->ep3_ready ? &c->ep3_srv :
nullptr;
742DtlsRecordKeys *pc_dtls_conn_app_read_keys(DtlsConn *c)
744 return c->ep3_ready ? &c->ep3_cli :
nullptr;
747size_t pc_dtls_conn_local_cid(
const DtlsConn *c, uint8_t *out)
749 if (!c->cid_negotiated || c->local_cid_len == 0)
753 memcpy(out, c->local_cid, c->local_cid_len);
754 return c->local_cid_len;
757bool pc_dtls_conn_open_app(DtlsConn *c,
const uint8_t *rec,
size_t rec_len, uint8_t *out,
size_t out_cap,
760 if (!pc_dtls_conn_established(c))
765 uint64_t next = c->replay_ep3.seeded ? c->replay_ep3.highest + 1 : 0;
766 if (!pc_dtls_ciphertext_unprotect(c->ep3_cli, next, rec, rec_len, out, out_cap, &info,
767 c->cid_negotiated ? c->local_cid : nullptr,
768 c->cid_negotiated ? c->local_cid_len : 0))
772 if (!pc_dtls_replay_check(&c->replay_ep3, info.seq))
776 pc_dtls_replay_mark(&c->replay_ep3, info.seq);
777 if (info.content_type != PC_DTLS_CT_APPLICATION_DATA)
781 *out_len = info.pt_len;
785size_t pc_dtls_conn_seal_app(DtlsConn *c,
const uint8_t *data,
size_t len, uint8_t *out,
size_t out_cap)
787 if (!pc_dtls_conn_established(c))
792 return pc_dtls_ciphertext_protect(c->ep3_srv, c->tx_seq_ep3++, PC_DTLS_CT_APPLICATION_DATA, data, len, out, out_cap,
793 c->cid_negotiated ? c->peer_cid : nullptr,
794 c->cid_negotiated ? c->peer_cid_len : 0);
Pluggable monotonic clock for all library timing.
uint32_t pc_millis(void)
The library's monotonic time at 1000 Hz (milliseconds).
void pc_x25519(uint8_t out[32], const uint8_t scalar[32], const uint8_t point[32])
X25519 scalar multiplication: out = scalar * point (RFC 7748 §5).
void pc_x25519_base(uint8_t out[32], const uint8_t scalar[32])
X25519 with the standard base point u=9: out = scalar * G.
Curve25519 field arithmetic + X25519 (RFC 7748) for the curve25519-sha256 KEX.
void pc_ed25519_pubkey(uint8_t pub[32], const uint8_t seed[32])
Ed25519 signatures (RFC 8032) for ssh-ed25519 host keys + client auth.
#define PC_ED25519_PUBKEY_LEN
Ed25519 public key length.
PC_CRYPTO_HOT void pc_sha256_init(pc_sha256_ctx *ctx)
Initialize a streaming SHA-256 context (ctx must not be NULL).
void pc_sha256_final(pc_sha256_ctx *ctx, uint8_t digest[PC_SHA256_DIGEST_LEN])
Finalize the hash and write the 32-byte digest. The context is undefined afterwards; call init() agai...
void pc_sha256_update(pc_sha256_ctx *ctx, const uint8_t *data, size_t len)
Feed len bytes of data into the running hash.
#define PC_SHA256_DIGEST_LEN
SHA-256 digest length in bytes.
Streaming SHA-256 context.