22const uint8_t INITIAL_SALT[20] = {0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17,
23 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a};
26const uint8_t RETRY_KEY[16] = {0xbe, 0x0c, 0x69, 0x0b, 0x9f, 0x66, 0x57, 0x5a,
27 0x1d, 0x76, 0x6b, 0x54, 0xe3, 0x68, 0xc8, 0x4e};
28const uint8_t RETRY_NONCE[12] = {0x46, 0x15, 0x99, 0xd3, 0x5d, 0x63, 0x2b, 0xf2, 0x23, 0x98, 0x25, 0xbb};
31void build_nonce(
const uint8_t iv[12], uint64_t full_pn, uint8_t nonce[12])
33 memcpy(nonce, iv, 12);
34 for (
int i = 0; i < 8; i++)
36 nonce[11 - i] ^= (uint8_t)(full_pn >> (8 * i));
41void pc_quic_keys_from_secret(
const uint8_t secret[PC_HKDF_HASH_LEN], QuicPacketKeys *out)
50 uint8_t *k = kb.span().buf;
51 pc_hkdf_expand_label(secret,
"quic key", k, PC_AES128GCM_KEY_LEN);
52 pc_aes128gcm_key_init(out->gcm, k);
53 pc_hkdf_expand_label(secret,
"quic iv", out->iv,
sizeof(out->iv));
55 uint8_t *hpk = hpb.span().buf;
56 pc_hkdf_expand_label(secret,
"quic hp", hpk, PC_AES128GCM_KEY_LEN);
57 pc_aes128_init(
reinterpret_cast<pc_aes128 *
>(out->hp), hpk);
60void pc_quic_derive_initial_secrets(
const uint8_t *dcid,
size_t dcid_len, QuicInitialSecrets *out)
62 uint8_t initial_secret[PC_HKDF_HASH_LEN];
63 pc_hkdf_extract(INITIAL_SALT,
sizeof(INITIAL_SALT), dcid, dcid_len, initial_secret);
65 uint8_t client_secret[PC_HKDF_HASH_LEN];
66 uint8_t server_secret[PC_HKDF_HASH_LEN];
67 pc_hkdf_expand_label(initial_secret,
"client in", client_secret,
sizeof(client_secret));
68 pc_hkdf_expand_label(initial_secret,
"server in", server_secret,
sizeof(server_secret));
70 pc_quic_keys_from_secret(client_secret, &out->client);
71 pc_quic_keys_from_secret(server_secret, &out->server);
74size_t pc_quic_packet_protect(uint8_t *pkt,
size_t cap,
size_t pn_offset, uint8_t pn_len, uint64_t full_pn,
75 size_t payload_len, QuicPacketKeys &keys,
bool is_long)
77 if (pn_len < 1 || pn_len > 4)
81 size_t hdr_len = pn_offset + pn_len;
82 size_t total = hdr_len + payload_len + PC_AES128GCM_TAG_LEN;
90 build_nonce(keys.iv, full_pn, nonce);
91 pc_aes128gcm_seal(
reinterpret_cast<pc_aes128gcm_key *
>(keys.gcm), nonce, pkt, hdr_len, pkt + hdr_len, payload_len,
92 pkt + hdr_len, pkt + hdr_len + payload_len);
100 pc_aes128_encrypt_block(
reinterpret_cast<pc_aes128 *
>(keys.hp), pkt + pn_offset + 4, mask);
102 pkt[0] ^=
mask[0] & (is_long ? 0x0f : 0x1f);
103 for (uint8_t i = 0; i < pn_len; i++)
105 pkt[pn_offset + i] ^=
mask[1 + i];
111size_t pc_quic_packet_unprotect(uint8_t *pkt,
size_t pn_offset,
size_t length, uint64_t largest_pn,
112 QuicPacketKeys &keys,
bool is_long, uint8_t *out, uint64_t *out_pn)
116 if (length < 4 + PC_AES128GCM_TAG_LEN)
124 pc_aes128_encrypt_block(
reinterpret_cast<pc_aes128 *
>(keys.hp), pkt + pn_offset + 4, mask);
126 pkt[0] ^=
mask[0] & (is_long ? 0x0f : 0x1f);
127 uint8_t pn_len = (uint8_t)((pkt[0] & 0x03) + 1);
129 uint64_t truncated_pn = 0;
130 for (uint8_t i = 0; i < pn_len; i++)
132 pkt[pn_offset + i] ^=
mask[1 + i];
133 truncated_pn = (truncated_pn << 8) | pkt[pn_offset + i];
135 uint64_t full_pn = pc_quic_pn_decode(largest_pn, truncated_pn, (uint8_t)(pn_len * 8));
141 size_t hdr_len = pn_offset + pn_len;
142 size_t ct_len = length - pn_len;
147 if (ct_len < PC_AES128GCM_TAG_LEN)
152 build_nonce(keys.iv, full_pn, nonce);
153 const size_t pt_len = ct_len - PC_AES128GCM_TAG_LEN;
154 if (!pc_aes128gcm_open(
reinterpret_cast<pc_aes128gcm_key *
>(keys.gcm), nonce, pkt, hdr_len, pkt + hdr_len, pt_len,
155 pkt + hdr_len + pt_len, out))
160 return ct_len - PC_AES128GCM_TAG_LEN;
163void pc_quic_retry_integrity_tag(
const uint8_t *odcid,
size_t odcid_len,
const uint8_t *retry,
size_t retry_len,
168 uint8_t aad[1 + QUIC_MAX_CID_LEN + 256];
170 aad[p++] = (uint8_t)odcid_len;
171 if (odcid_len > QUIC_MAX_CID_LEN || 1 + odcid_len + retry_len >
sizeof(aad))
176 memcpy(aad + p, odcid, odcid_len);
178 memcpy(aad + p, retry, retry_len);
184 pc_aes128gcm_key *rk = pc_aes128gcm_key_init(rk_b.span().buf, RETRY_KEY);
185 pc_aes128gcm_seal(rk, RETRY_NONCE, aad, p,
nullptr, 0, tag, tag);
186 pc_aes128gcm_key_wipe(rk);
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.
HKDF-SHA256 (RFC 5869) and TLS 1.3 HKDF-Expand-Label (RFC 8446 sec 7.1).
uint32_t mask(uint8_t width)
Mask of width low bits (width 32 handled without a 32-bit shift, which is UB).
#define PC_WORK_AES128GCM
Secure pool accessor - borrows that hold key material.