ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
aes_cmac.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 aes_cmac.cpp
6 * @brief AES-128-CMAC implementation (see aes_cmac.h).
7 *
8 * Arduino path: the AES-128 block runs on mbedtls_aes_crypt_ecb() (HW AES accelerator on ESP32).
9 * Native path: the shared table-free software AES-128 (crypto/cipher/aes_block.h). The CMAC
10 * construction (subkey derivation + CBC-MAC + last-block handling) is identical on both.
11 */
12
13#include "crypto/mac/aes_cmac.h"
14#include "crypto/crypto_opt.h"
15#include <string.h>
16#ifdef ARDUINO
17#include <mbedtls/aes.h> // AES-128 single-block via the ESP32 AES peripheral
18#else
19#include "crypto/cipher/aes_block.h" // native software AES-128 block
20#endif
22
23// ---------------------------------------------------------------------------
24// AES-128 single-block encrypt seam - one small wrapper, two platform bodies
25// ---------------------------------------------------------------------------
26
27#ifdef ARDUINO
28
29namespace
30{
31struct AesBlk
32{
33 mbedtls_aes_context c;
34};
35inline void blk_init(AesBlk *b, const uint8_t key[16])
36{
37 mbedtls_aes_init(&b->c);
38 mbedtls_aes_setkey_enc(&b->c, key, 128);
39}
40inline void blk_enc(AesBlk *b, const uint8_t in[16], uint8_t out[16])
41{
42 mbedtls_aes_crypt_ecb(&b->c, MBEDTLS_AES_ENCRYPT, in, out);
43}
44inline void blk_free(AesBlk *b)
45{
46 mbedtls_aes_free(&b->c);
47}
48} // namespace
49
50#else
51
52namespace
53{
54struct AesBlk
55{
56 uint32_t rk[44]; ///< AES-128 expanded round-key schedule (44 words).
57};
58inline void blk_init(AesBlk *b, const uint8_t key[16])
59{
60 pc_aes_key_expand(key, 4, b->rk);
61}
62inline void blk_enc(AesBlk *b, const uint8_t in[16], uint8_t out[16])
63{
64 pc_aes_encrypt_block(b->rk, 10, in, out);
65}
66inline void blk_free(AesBlk *)
67{
68}
69} // namespace
70
71#endif // ARDUINO
72
73// ---------------------------------------------------------------------------
74// CMAC construction (RFC 4493 / NIST SP800-38B)
75// ---------------------------------------------------------------------------
76
77namespace
78{
79// Left-shift a 16-byte big-endian value by one bit; return the bit shifted out of the MSB.
80uint8_t shl1(const uint8_t in[16], uint8_t out[16])
81{
82 uint8_t carry = 0;
83 for (int i = 15; i >= 0; i--)
84 {
85 uint8_t next = (uint8_t)(in[i] >> 7);
86 out[i] = (uint8_t)((in[i] << 1) | carry);
87 carry = next;
88 }
89 return carry; // the bit that left the MSB
90}
91
92// RFC 4493 subkey generation: L = AES(key, 0^128); K1 = L<<1 (^Rb if MSB(L)); K2 = K1<<1 (^Rb if MSB(K1)).
93void subkeys(AesBlk *blk, uint8_t k1[16], uint8_t k2[16])
94{
95 static constexpr uint8_t RB = 0x87; // the 128-bit-block CMAC constant
96 uint8_t zero[16] = {0};
97 uint8_t l[16];
98 blk_enc(blk, zero, l);
99 if (shl1(l, k1))
100 {
101 k1[15] ^= RB;
102 }
103 if (shl1(k1, k2))
104 {
105 k2[15] ^= RB;
106 }
107}
108} // namespace
109
110void pc_aes_cmac(const uint8_t key[16], const uint8_t *msg, size_t msg_len, uint8_t mac[PC_AES_CMAC_LEN])
111{
112 AesBlk blk;
113 blk_init(&blk, key);
114 uint8_t k1[16];
115 uint8_t k2[16];
116 subkeys(&blk, k1, k2);
117
118 // n = number of blocks; the message is a whole number of blocks iff msg_len > 0 && msg_len % 16 == 0.
119 const size_t n = msg_len == 0 ? 1 : (msg_len + 15) / 16;
120 const bool complete = msg_len != 0 && (msg_len % 16) == 0;
121
122 // Build the last block: the final 16 bytes XOR K1 when complete, else the 10*-padded remainder XOR K2.
123 uint8_t last[16];
124 if (complete)
125 {
126 for (int i = 0; i < 16; i++)
127 {
128 last[i] = (uint8_t)(msg[(n - 1) * 16 + i] ^ k1[i]);
129 }
130 }
131 else
132 {
133 const size_t rem = msg_len - (n - 1) * 16; // 0..15 bytes in the final partial block
134 for (size_t i = 0; i < 16; i++)
135 {
136 uint8_t m = i < rem ? msg[(n - 1) * 16 + i] : (i == rem ? 0x80 : 0x00); // pad 10*
137 last[i] = (uint8_t)(m ^ k2[i]);
138 }
139 }
140
141 // CBC-MAC: X starts at 0; fold blocks 1..n-1, then the prepared last block.
142 uint8_t x[16] = {0};
143 uint8_t y[16];
144 for (size_t i = 0; i + 1 < n; i++)
145 {
146 for (int j = 0; j < 16; j++)
147 {
148 y[j] = (uint8_t)(x[j] ^ msg[i * 16 + j]);
149 }
150 blk_enc(&blk, y, x);
151 }
152 for (int j = 0; j < 16; j++)
153 {
154 y[j] = (uint8_t)(x[j] ^ last[j]);
155 }
156 blk_enc(&blk, y, mac);
157
158 blk_free(&blk);
159}
Compact table-free software AES key schedule + single-block encrypt (FIPS 197) - one source of truth.
void pc_aes_key_expand(const uint8_t *key, int nk, uint32_t *rk)
AES key expansion (FIPS 197 sec 5.2). nk key words (4=AES-128, 8=AES-256); rk receives 4*(nk + 7) rou...
Definition aes_block.h:52
void pc_aes_encrypt_block(const uint32_t *rk, int nr, const uint8_t in[16], uint8_t out[16])
AES single-block encrypt (FIPS 197 sec 5.1), nr rounds (10=AES-128, 14=AES-256). State is column-majo...
Definition aes_block.h:82
void pc_aes_cmac(const uint8_t key[16], const uint8_t *msg, size_t msg_len, uint8_t mac[PC_AES_CMAC_LEN])
Compute the AES-128-CMAC of msg under key (RFC 4493).
Definition aes_cmac.cpp:110
AES-128-CMAC (RFC 4493 / NIST SP800-38B) one-shot MAC.
#define PC_AES_CMAC_LEN
AES-128-CMAC output length (one AES block).
Definition aes_cmac.h:29
Per-translation-unit optimization override for hot, pure-integer crypto.
void blk_free(AesBlk *b)
Definition aes_cmac.cpp:44
void blk_enc(AesBlk *b, const uint8_t in[16], uint8_t out[16])
Definition aes_cmac.cpp:40
void blk_init(AesBlk *b, const uint8_t key[16])
Definition aes_cmac.cpp:35
mbedtls_aes_context c
Definition aes_cmac.cpp:33