21const uint8_t DTLS_UH_FIXED = 0x20;
22const uint8_t DTLS_UH_FIXED_MASK = 0xE0;
23const uint8_t DTLS_UH_CID = 0x10;
24const uint8_t DTLS_UH_SEQ16 = 0x08;
25const uint8_t DTLS_UH_LENGTH = 0x04;
26const uint8_t DTLS_UH_EPOCH_MASK = 0x03;
30void build_nonce(
const uint8_t iv[12], uint64_t seq, uint8_t nonce[12])
32 memcpy(nonce, iv, 12);
33 for (
int i = 0; i < 8; i++)
35 nonce[11 - i] ^= (uint8_t)(seq >> (8 * i));
41uint64_t seq_decode(uint64_t expected, uint64_t truncated,
unsigned bits)
46 if (bits == 0 || bits >= 64)
51 uint64_t win = (uint64_t)1 << bits;
52 uint64_t hwin = win >> 1;
53 uint64_t
mask = win - 1;
54 uint64_t candidate = (expected & ~mask) | (truncated & mask);
55 if (candidate + hwin <= expected && candidate + win > candidate)
57 return candidate + win;
59 if (candidate > expected + hwin && candidate >= win)
61 return candidate - win;
67void pc_dtls_record_keys_derive(DtlsRecordKeys *out, DtlsCipher cipher, uint16_t epoch,
const uint8_t secret[32])
78 uint8_t *k = kb.span().buf;
79 pc_tls13_kdf_expand_label(&DTLS13_KDF, secret,
"key", k, PC_AES128GCM_KEY_LEN);
80 pc_aes128gcm_key_init(out->gcm, k);
81 pc_tls13_kdf_expand_label(&DTLS13_KDF, secret,
"iv", out->iv,
sizeof(out->iv));
83 uint8_t *snk = snb.span().buf;
84 pc_tls13_kdf_expand_label(&DTLS13_KDF, secret,
"sn", snk, PC_AES128GCM_KEY_LEN);
85 pc_aes128_init(
reinterpret_cast<pc_aes128 *
>(out->sn_key), snk);
92size_t pc_dtls_plaintext_build(uint8_t content_type, uint16_t epoch, uint64_t seq,
const uint8_t *fragment,
93 size_t frag_len, uint8_t *out,
size_t out_cap)
95 size_t total = PC_DTLS_PLAINTEXT_HDR_LEN + frag_len;
96 if (total > out_cap || frag_len > 0xFFFF)
100 out[0] = content_type;
101 out[1] = (uint8_t)(PC_DTLS_LEGACY_VERSION >> 8);
102 out[2] = (uint8_t)PC_DTLS_LEGACY_VERSION;
103 out[3] = (uint8_t)(epoch >> 8);
104 out[4] = (uint8_t)epoch;
105 out[5] = (uint8_t)(seq >> 40);
106 out[6] = (uint8_t)(seq >> 32);
107 out[7] = (uint8_t)(seq >> 24);
108 out[8] = (uint8_t)(seq >> 16);
109 out[9] = (uint8_t)(seq >> 8);
110 out[10] = (uint8_t)seq;
111 out[11] = (uint8_t)(frag_len >> 8);
112 out[12] = (uint8_t)frag_len;
115 memcpy(out + PC_DTLS_PLAINTEXT_HDR_LEN, fragment, frag_len);
120size_t pc_dtls_plaintext_parse(
const uint8_t *rec,
size_t rec_len, DtlsPlaintext *out)
122 if (rec_len < PC_DTLS_PLAINTEXT_HDR_LEN)
126 if (rec[1] != (uint8_t)(PC_DTLS_LEGACY_VERSION >> 8) || rec[2] != (uint8_t)PC_DTLS_LEGACY_VERSION)
130 out->content_type = rec[0];
131 out->epoch = (uint16_t)(((uint16_t)rec[3] << 8) | rec[4]);
132 out->seq = ((uint64_t)rec[5] << 40) | ((uint64_t)rec[6] << 32) | ((uint64_t)rec[7] << 24) |
133 ((uint64_t)rec[8] << 16) | ((uint64_t)rec[9] << 8) | (uint64_t)rec[10];
134 size_t length = ((size_t)rec[11] << 8) | rec[12];
135 if (PC_DTLS_PLAINTEXT_HDR_LEN + length > rec_len)
139 out->fragment = rec + PC_DTLS_PLAINTEXT_HDR_LEN;
140 out->frag_len = length;
141 return PC_DTLS_PLAINTEXT_HDR_LEN + length;
148size_t pc_dtls_ciphertext_protect(DtlsRecordKeys &keys, uint64_t seq, uint8_t content_type,
const uint8_t *plaintext,
149 size_t pt_len, uint8_t *out,
size_t out_cap,
const uint8_t *cid,
size_t cid_len)
151 if (keys.cipher != DtlsCipher::AES_128_GCM_SHA256)
155 if (cid_len > PC_DTLS_CID_MAX || (cid_len && !cid))
161 const size_t hdr_len = 1 + cid_len + 2 + 2;
162 size_t inner_len = pt_len + 1;
163 size_t enc_len = inner_len + PC_DTLS_TAG_LEN;
164 size_t total = hdr_len + enc_len;
170 uint8_t flags = (uint8_t)(DTLS_UH_FIXED | DTLS_UH_SEQ16 | DTLS_UH_LENGTH | (keys.epoch & DTLS_UH_EPOCH_MASK));
173 flags |= DTLS_UH_CID;
178 memcpy(out + 1, cid, cid_len);
180 size_t seq_off = 1 + cid_len;
181 out[seq_off] = (uint8_t)(seq >> 8);
182 out[seq_off + 1] = (uint8_t)seq;
183 out[seq_off + 2] = (uint8_t)(enc_len >> 8);
184 out[seq_off + 3] = (uint8_t)enc_len;
187 memcpy(out + hdr_len, plaintext, pt_len);
188 out[hdr_len + pt_len] = content_type;
191 build_nonce(keys.iv, seq, nonce);
194 pc_aes128gcm_seal(
reinterpret_cast<pc_aes128gcm_key *
>(keys.gcm), nonce, out, hdr_len, out + hdr_len, inner_len,
195 out + hdr_len, out + hdr_len + inner_len);
202 pc_aes128_encrypt_block(
reinterpret_cast<pc_aes128 *
>(keys.sn_key), out + hdr_len, mask);
203 out[seq_off] ^=
mask[0];
204 out[seq_off + 1] ^=
mask[1];
208bool pc_dtls_ciphertext_unprotect(DtlsRecordKeys &keys, uint64_t next_seq,
const uint8_t *rec,
size_t rec_len,
209 uint8_t *out,
size_t out_cap, DtlsCiphertext *info,
const uint8_t *expected_cid,
210 size_t expected_cid_len)
212 if (keys.cipher != DtlsCipher::AES_128_GCM_SHA256 || rec_len < 1)
216 if (expected_cid_len > PC_DTLS_CID_MAX)
221 if ((b0 & DTLS_UH_FIXED_MASK) != DTLS_UH_FIXED)
225 if ((b0 & DTLS_UH_EPOCH_MASK) != (keys.epoch & DTLS_UH_EPOCH_MASK))
231 if (b0 & DTLS_UH_CID)
235 if (expected_cid_len == 0 || off + expected_cid_len > rec_len ||
236 memcmp(rec + off, expected_cid, expected_cid_len) != 0)
240 off += expected_cid_len;
242 else if (expected_cid_len != 0)
247 size_t seq_len = (b0 & DTLS_UH_SEQ16) ? 2 : 1;
248 if (off + seq_len > rec_len)
252 size_t seq_off = off;
256 if (b0 & DTLS_UH_LENGTH)
258 if (off + 2 > rec_len)
262 enc_len = ((size_t)rec[off] << 8) | rec[off + 1];
267 enc_len = rec_len - off;
269 if (off + enc_len > rec_len || enc_len < 16 || enc_len < PC_DTLS_TAG_LEN + 1)
274 const uint8_t *enc = rec + off;
275 size_t hdr_len = off;
278 uint8_t hdr[1 + PC_DTLS_CID_MAX + 4];
279 memcpy(hdr, rec, hdr_len);
285 pc_aes128_encrypt_block(
reinterpret_cast<pc_aes128 *
>(keys.sn_key), enc, mask);
287 for (
size_t i = 0; i < seq_len; i++)
289 hdr[seq_off + i] ^=
mask[i];
290 trunc = (trunc << 8) | hdr[seq_off + i];
292 uint64_t full_seq = seq_decode(next_seq, trunc, (
unsigned)(seq_len * 8));
298 if (enc_len < PC_DTLS_TAG_LEN)
302 size_t inner_len = enc_len - PC_DTLS_TAG_LEN;
303 if (inner_len > out_cap)
309 build_nonce(keys.iv, full_seq, nonce);
310 const size_t pt_len = inner_len;
311 if (!pc_aes128gcm_open(
reinterpret_cast<pc_aes128gcm_key *
>(keys.gcm), nonce, hdr, hdr_len, enc, pt_len,
318 size_t n = inner_len;
319 while (n > 0 && out[n - 1] == 0)
327 info->content_type = out[n - 1];
328 info->pt_len = n - 1;
329 info->seq = full_seq;
330 info->epoch = keys.epoch;
338void pc_dtls_replay_init(DtlsReplayWindow *w)
345bool pc_dtls_replay_check(
const DtlsReplayWindow *w, uint64_t seq)
347 if (!w->seeded || seq > w->highest)
351 uint64_t diff = w->highest - seq;
356 return ((w->bitmap >> diff) & 1u) == 0;
359void pc_dtls_replay_mark(DtlsReplayWindow *w, uint64_t seq)
368 if (seq > w->highest)
370 uint64_t shift = seq - w->highest;
371 w->bitmap = (shift >= 64) ? 1u : ((w->bitmap << shift) | 1u);
375 uint64_t diff = w->highest - seq;
378 w->bitmap |= ((uint64_t)1 << diff);
AES-128 block cipher + AEAD_AES_128_GCM (RFC 5116 / NIST SP 800-38D).
A secure borrow whose acquire and release are one call each.
uint32_t mask(uint8_t width)
Mask of width low bits (width 32 handled without a 32-bit shift, which is UB).
Secure pool accessor - borrows that hold key material.
TLS 1.3 key schedule (RFC 8446 sec 7.1) for the QUIC handshake.