19void put32be(uint8_t *p, uint32_t v)
21 p[0] = (uint8_t)(v >> 24);
22 p[1] = (uint8_t)(v >> 16);
23 p[2] = (uint8_t)(v >> 8);
26uint32_t get32be(
const uint8_t *p)
28 return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | (uint32_t)p[3];
30void esp_nonce(uint8_t nonce[
PC_AESGCM_IV_LEN],
const uint8_t *salt,
const uint8_t *iv)
32 memcpy(nonce, salt, PC_ESP_SALT_LEN);
33 memcpy(nonce + PC_ESP_SALT_LEN, iv, PC_ESP_IV_LEN);
36#define PC_ESP_CT_OFF (PC_ESP_HDR_LEN + PC_ESP_IV_LEN)
39size_t pc_esp_gcm_encapsulate(uint32_t spi, uint32_t seq,
const uint8_t key[PC_ESP_KEY_LEN],
40 const uint8_t salt[PC_ESP_SALT_LEN],
const uint8_t iv[PC_ESP_IV_LEN], uint8_t next_header,
41 const uint8_t *payload,
size_t payload_len, uint8_t *out,
size_t out_cap)
43 if (!key || !salt || !iv || !out || (payload_len && !payload))
50 size_t padn = (4 - (payload_len + 2) % 4) % 4;
51 size_t pt_len = payload_len + padn + 2;
52 size_t total = PC_ESP_CT_OFF + pt_len + PC_ESP_ICV_LEN;
59 put32be(out + 4, seq);
60 memcpy(out + PC_ESP_HDR_LEN, iv, PC_ESP_IV_LEN);
62 uint8_t *pt = out + PC_ESP_CT_OFF;
65 memcpy(pt, payload, payload_len);
67 for (
size_t i = 0; i < padn; i++)
69 pt[payload_len + i] = (uint8_t)(i + 1);
71 pt[payload_len + padn] = (uint8_t)padn;
72 pt[payload_len + padn + 1] = next_header;
76 esp_nonce(nonce, salt, iv);
83 pc_aesgcm_seal(gcm, nonce, out, PC_ESP_HDR_LEN, pt, pt_len, pt, pt + pt_len);
89bool pc_esp_gcm_decapsulate(
const uint8_t key[PC_ESP_KEY_LEN],
const uint8_t salt[PC_ESP_SALT_LEN], uint8_t *packet,
90 size_t len, uint32_t *spi_out, uint32_t *seq_out, uint8_t *next_header_out,
91 const uint8_t **payload_out,
size_t *payload_len_out)
93 if (!key || !salt || !packet || !payload_out || !payload_len_out)
98 if (len < PC_ESP_CT_OFF + 2 + PC_ESP_ICV_LEN)
103 const uint8_t *iv = packet + PC_ESP_HDR_LEN;
104 uint8_t *ct = packet + PC_ESP_CT_OFF;
105 size_t ct_len = len - PC_ESP_CT_OFF - PC_ESP_ICV_LEN;
106 const uint8_t *tag = ct + ct_len;
109 esp_nonce(nonce, salt, iv);
117 ok =
pc_aesgcm_open(gcm, nonce, packet, PC_ESP_HDR_LEN, ct, ct_len, tag, ct);
126 uint8_t next_header = ct[ct_len - 1];
127 uint8_t pad_len = ct[ct_len - 2];
128 if ((
size_t)pad_len + 2 > ct_len)
135 *spi_out = get32be(packet);
139 *seq_out = get32be(packet + 4);
143 *next_header_out = next_header;
146 *payload_len_out = ct_len - 2 - pad_len;
152void pc_esp_replay_init(EspReplay *r)
163bool pc_esp_replay_check(EspReplay *r, uint32_t seq)
178 if (seq > r->highest)
181 uint32_t shift = seq - r->highest;
182 r->bitmap = (shift >= PC_ESP_REPLAY_WINDOW) ? 0u : (r->bitmap << shift);
188 uint32_t offset = r->highest - seq;
189 if (offset >= PC_ESP_REPLAY_WINDOW)
193 uint64_t
mask = (uint64_t)1 << offset;
194 if (r->bitmap & mask)
AES-256-GCM AEAD (RFC 5116) - stateless, detached-tag API.
#define PC_AESGCM_IV_LEN
GCM nonce length (bytes) = fixed_field(4) || invocation_counter(8).
A secure borrow whose acquire and release are one call each.
ESP (RFC 4303) packet transform with AES-256-GCM (RFC 4106) - the IPsec datapath's crypto core.
uint32_t mask(uint8_t width)
Mask of width low bits (width 32 handled without a 32-bit shift, which is UB).
bool pc_aesgcm_open(pc_aesgcm_key *k, const uint8_t nonce[PC_AESGCM_IV_LEN], const uint8_t *aad, size_t aad_len, const uint8_t *ct, size_t ct_len, const uint8_t tag[PC_AESGCM_TAG_LEN], uint8_t *out)
Open one record: verify tag over aad || ct in constant time, then (only on success) decrypt ct into o...
pc_aesgcm_key * pc_aesgcm_key_init(void *storage, const uint8_t key[PC_AESGCM_KEY_LEN])
Bind storage as a context keyed with key.
pc_cspan pc_aesgcm_seal(pc_aesgcm_key *k, const uint8_t nonce[PC_AESGCM_IV_LEN], const uint8_t *aad, size_t aad_len, const uint8_t *pt, size_t pt_len, uint8_t *ct_out, uint8_t tag_out[PC_AESGCM_TAG_LEN])
Seal one record under k and nonce.
void pc_aesgcm_key_wipe(pc_aesgcm_key *k)
Wipe the expanded schedule. Call on rekey and on close; the storage stays the caller's.
Secure pool accessor - borrows that hold key material.