ProtoCore v0.0.2
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
esp_aes128gcm.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file esp_aes128gcm.cpp
6 * @brief AES-128-GCM and the AES-128 block on Espressif silicon, through the vendor's own primitives.
7 *
8 * The AEAD is one mbedtls_gcm call. The core used to drive the block cipher 16 bytes at a time and fold
9 * GHASH in software on every target - on the AES-256 path that measured 616,567 cyc/KiB against the
10 * vendor AEAD's 81,085, and there is no reason to expect a different ratio here.
11 *
12 * The context is keyed and held for the life of the key: standing an mbedtls GCM context up and tearing
13 * it down costs ~9,200 cycles once the AES peripheral has been used, a FIXED cost per record that
14 * dominates QUIC and DTLS traffic, where records are small.
15 *
16 * Vendor headers are fine here: this is board_drivers, the partition vendor code is segregated to.
17 */
18
21#include "crypto/crypto_opt.h"
22#include "protocore_config.h" // PC_ENABLE_* gate the whole file; pc_platform.h does not pull this in
23#include "server/mmgr/secure.h"
24#include <string.h>
25
26#if (PC_ENABLE_HTTP3 || PC_ENABLE_DTLS || PC_ENABLE_SMB)
27#if PC_HAS_HW_AESGCM
28
29#include <mbedtls/aes.h>
30#include <mbedtls/gcm.h>
31
33
34// pc_aes128 - definition private to this backend; aes128gcm.h forward-declares the symbol only.
35struct pc_aes128
36{
37 mbedtls_aes_context mbed; ///< mbedtls context (HW-accelerated), key schedule loaded.
38};
39
40static_assert(sizeof(pc_aes128) <= PC_WORK_AES128, "pc_aes128 outgrew PC_WORK_AES128 - raise it in protocore_config.h");
41
42pc_aes128 *pc_aes128_wants(void)
43{
44 pc_span ws = pc_secure_span(sizeof(pc_aes128), alignof(pc_aes128));
45 return pc_span_ok(ws) ? reinterpret_cast<pc_aes128 *>(ws.buf) : nullptr;
46}
47
48void pc_aes128_init(pc_aes128 *ctx, const uint8_t key[16])
49{
50 mbedtls_aes_init(&ctx->mbed);
51 mbedtls_aes_setkey_enc(&ctx->mbed, key, 128);
52}
53
54void pc_aes128_encrypt_block(pc_aes128 *ctx, const uint8_t in[16], uint8_t out[16])
55{
56 mbedtls_aes_crypt_ecb(&ctx->mbed, MBEDTLS_AES_ENCRYPT, in, out);
57}
58
59void pc_aes128_wipe(pc_aes128 *ctx)
60{
61 mbedtls_aes_free(&ctx->mbed);
62}
63
64// The size assert is what keeps PC_WORK_AES128GCM honest against a vendor header we do not own.
65static_assert(sizeof(mbedtls_gcm_context) <= PC_WORK_AES128GCM,
66 "mbedtls_gcm_context outgrew PC_WORK_AES128GCM - raise it in protocore_config.h, which derives "
67 "PC_SECURE_ARENA_SIZE from it");
68
69pc_aes128gcm_key *pc_aes128gcm_key_init(void *storage, const uint8_t key[PC_AES128GCM_KEY_LEN])
70{
71 mbedtls_gcm_context *g = reinterpret_cast<mbedtls_gcm_context *>(storage);
72 mbedtls_gcm_init(g);
73 if (mbedtls_gcm_setkey(g, MBEDTLS_CIPHER_ID_AES, key, 128) != 0)
74 {
75 mbedtls_gcm_free(g);
76 return nullptr;
77 }
78 return reinterpret_cast<pc_aes128gcm_key *>(g);
79}
80
81void pc_aes128gcm_key_wipe(pc_aes128gcm_key *k)
82{
83 mbedtls_gcm_context *g = reinterpret_cast<mbedtls_gcm_context *>(k);
84 mbedtls_gcm_free(g); // releases whatever the vendor attached
85 pc_secure_wipe(reinterpret_cast<uint8_t *>(g), sizeof(mbedtls_gcm_context));
86}
87
88pc_cspan pc_aes128gcm_seal(pc_aes128gcm_key *k, const uint8_t nonce[PC_AES128GCM_IV_LEN], const uint8_t *aad,
89 size_t aad_len, const uint8_t *pt, size_t pt_len, uint8_t *ct_out,
90 uint8_t tag_out[PC_AES128GCM_TAG_LEN])
91{
92 mbedtls_gcm_context *g = reinterpret_cast<mbedtls_gcm_context *>(k);
93 if (mbedtls_gcm_crypt_and_tag(g, MBEDTLS_GCM_ENCRYPT, pt_len, nonce, PC_AES128GCM_IV_LEN, aad, aad_len, pt, ct_out,
94 PC_AES128GCM_TAG_LEN, tag_out) != 0)
95 {
96 return {};
97 }
98 return pc_cspan_from(ct_out, pt_len); // the tag rides in tag_out, not in this span
99}
100
101bool pc_aes128gcm_open(pc_aes128gcm_key *k, const uint8_t nonce[PC_AES128GCM_IV_LEN], const uint8_t *aad,
102 size_t aad_len, const uint8_t *ct, size_t ct_len, const uint8_t tag[PC_AES128GCM_TAG_LEN],
103 uint8_t *out)
104{
105 mbedtls_gcm_context *g = reinterpret_cast<mbedtls_gcm_context *>(k);
106 return mbedtls_gcm_auth_decrypt(g, ct_len, nonce, PC_AES128GCM_IV_LEN, aad, aad_len, tag, PC_AES128GCM_TAG_LEN, ct,
107 out) == 0;
108}
109
110#endif // PC_HAS_HW_AESGCM
111#endif // PC_ENABLE_HTTP3 || PC_ENABLE_DTLS || PC_ENABLE_SMB
AES-128 block cipher + AEAD_AES_128_GCM (RFC 5116 / NIST SP 800-38D).
Per-translation-unit optimization override for hot, pure-integer crypto.
The one vendor/die selector for the whole library.
User-facing configuration for ProtoCore.
#define PC_WORK_AES128
#define PC_WORK_AES128GCM
pc_span pc_secure_span(size_t n, size_t align)
Borrow n secure bytes as a span whose capacity is bound to the allocation.
Definition secure.cpp:142
Secure pool accessor - borrows that hold key material.
pc_cspan pc_cspan_from(const uint8_t *p, size_t len)
Bind a read-only span to memory whose extent is only known at run time.
Definition span.h:103
bool pc_span_ok(const pc_span &s)
True when the span refers to real storage and every write so far has fit.
Definition span.h:114
A read-only byte region.
Definition span.h:79
A writable byte region: the storage, the capacity that belongs to it, and what has been produced into...
Definition span.h:65
uint8_t * buf
first byte, or nullptr when the region could not be obtained
Definition span.h:66