CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
host_api_crypto.cpp
Go to the documentation of this file.
1
5
8#include "cdc_core/Crypto.h"
9#include "HexUtil.h"
10
11#include "mbedtls/sha256.h"
12#include "mbedtls/md.h"
13#include "mbedtls/base64.h"
14#include "esp_random.h"
15
16#include <cstdint>
17#include <cstring>
18#include <cstdio>
19#include <string>
20
21namespace {
22
23const char BASE32_ALPHABET[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
24
25} // namespace
26
27extern "C" {
28
29int host_random(uint8_t* buf, size_t len)
30{
31 if (!buf) return HOST_ERR_INVALID_ARG;
32 esp_fill_random(buf, len);
33 return HOST_OK;
34}
35
36int host_random_strict(uint8_t* buf, size_t len)
37{
38 if (!buf) return HOST_ERR_INVALID_ARG;
40 if (se && se->getRandomStrict(buf, len)) return HOST_OK;
42}
43
44int host_sha256(const uint8_t* data, size_t len, uint8_t out[32])
45{
46 if (!out || (!data && len > 0)) return HOST_ERR_INVALID_ARG;
47 if (mbedtls_sha256(data, len, out, 0) != 0) return HOST_ERR_GENERIC;
48 return HOST_OK;
49}
50
51int host_hmac_sha256(const uint8_t* key, size_t klen,
52 const uint8_t* data, size_t dlen, uint8_t out[32])
53{
54 if (!out || !key || !data) return HOST_ERR_INVALID_ARG;
55 const mbedtls_md_info_t* info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
56 if (!info) return HOST_ERR_GENERIC;
57 if (mbedtls_md_hmac(info, key, klen, data, dlen, out) != 0) return HOST_ERR_GENERIC;
58 return HOST_OK;
59}
60
61int host_aes_gcm_encrypt(const uint8_t* key, const uint8_t* iv,
62 const uint8_t* aad, size_t aad_len,
63 const uint8_t* pt, size_t pt_len,
64 uint8_t* ct, uint8_t tag[16])
65{
66 if (!key || !iv || !ct || !tag) return HOST_ERR_INVALID_ARG;
67 bool ok = cdc::core::aesGcm256Seal(key, iv, 12, aad, aad_len,
68 pt, pt_len, ct, tag);
69 return ok ? HOST_OK : HOST_ERR_GENERIC;
70}
71
72int host_aes_gcm_decrypt(const uint8_t* key, const uint8_t* iv,
73 const uint8_t* aad, size_t aad_len,
74 const uint8_t* ct, size_t ct_len,
75 const uint8_t tag[16], uint8_t* pt)
76{
77 if (!key || !iv || !ct || !tag || !pt) return HOST_ERR_INVALID_ARG;
78 bool ok = cdc::core::aesGcm256Open(key, iv, 12, aad, aad_len,
79 ct, ct_len, tag, pt);
80 return ok ? HOST_OK : HOST_ERR_GENERIC;
81}
82
83int host_base32_encode(const uint8_t* in, size_t in_len, char* out, size_t out_size)
84{
85 if (!out || (!in && in_len > 0)) return HOST_ERR_INVALID_ARG;
86 // RFC 4648 base32, no padding: ceil(in_len*8 / 5) symbols + NUL. Streaming
87 // emit (symmetric to host_base32_decode), so a partial final group yields
88 // exactly its symbols instead of a zero-padded full 8-symbol group.
89 size_t needed = (in_len * 8 + 4) / 5 + 1;
90 if (out_size < needed) return HOST_ERR_NO_MEMORY;
91 size_t out_pos = 0;
92 int bits = 0;
93 uint32_t buffer = 0;
94 for (size_t i = 0; i < in_len; ++i) {
95 buffer = (buffer << 8) | in[i];
96 bits += 8;
97 while (bits >= 5) {
98 out[out_pos++] = BASE32_ALPHABET[(buffer >> (bits - 5)) & 0x1F];
99 bits -= 5;
100 }
101 }
102 if (bits > 0) {
103 out[out_pos++] = BASE32_ALPHABET[(buffer << (5 - bits)) & 0x1F];
104 }
105 out[out_pos] = '\0';
106 return HOST_OK;
107}
108
109int host_base32_decode(const char* in, size_t in_len, uint8_t* out, size_t out_size)
110{
111 if (!in || !out) return HOST_ERR_INVALID_ARG;
112 size_t out_pos = 0;
113 int bits = 0;
114 uint32_t buffer = 0;
115 for (size_t i = 0; i < in_len; ++i) {
116 char c = in[i];
117 if (c == '=' || c == ' ' || c == '\n' || c == '\r') continue;
118 int v = -1;
119 if (c >= 'A' && c <= 'Z') v = c - 'A';
120 else if (c >= 'a' && c <= 'z') v = c - 'a';
121 else if (c >= '2' && c <= '7') v = c - '2' + 26;
122 else return HOST_ERR_INVALID_ARG;
123 buffer = (buffer << 5) | static_cast<uint32_t>(v);
124 bits += 5;
125 if (bits >= 8) {
126 if (out_pos >= out_size) return HOST_ERR_NO_MEMORY;
127 out[out_pos++] = static_cast<uint8_t>((buffer >> (bits - 8)) & 0xFF);
128 bits -= 8;
129 }
130 }
131 return static_cast<int>(out_pos);
132}
133
134int host_base64_encode(const uint8_t* in, size_t in_len, char* out, size_t out_size)
135{
136 if (!out || (!in && in_len > 0)) return HOST_ERR_INVALID_ARG;
137 size_t olen = 0;
138 int rc = mbedtls_base64_encode(reinterpret_cast<unsigned char*>(out), out_size,
139 &olen, in, in_len);
140 if (rc != 0) return HOST_ERR_NO_MEMORY;
141 // Matrix/vodozemac use unpadded base64 for crypto fields; strip '=' padding so
142 // the output round-trips with strict unpadded decoders on the receiving side.
143 while (olen > 0 && out[olen - 1] == '=') --olen;
144 out[olen] = '\0';
145 return HOST_OK;
146}
147
148int host_base64_decode(const char* in, size_t in_len, uint8_t* out, size_t out_size)
149{
150 if (!in || !out) return HOST_ERR_INVALID_ARG;
151 // mbedtls_base64_decode requires the input padded to a multiple of 4: it
152 // drops the trailing partial group otherwise. Some inputs (e.g. Matrix SSSS
153 // secrets) are unpadded, so normalise first - strip whitespace, then pad
154 // with '=' to a multiple of 4 - before decoding with mbedTLS.
155 std::string norm;
156 norm.reserve(in_len + 4);
157 for (size_t i = 0; i < in_len; ++i) {
158 char c = in[i];
159 if (c == ' ' || c == '\n' || c == '\r' || c == '\t') continue;
160 norm.push_back(c);
161 }
162 while (norm.size() % 4 != 0) norm.push_back('=');
163 size_t olen = 0;
164 int rc = mbedtls_base64_decode(out, out_size, &olen,
165 reinterpret_cast<const unsigned char*>(norm.data()),
166 norm.size());
167 if (rc != 0) return HOST_ERR_GENERIC;
168 return static_cast<int>(olen);
169}
170
171int host_hex_encode(const uint8_t* in, size_t in_len, char* out, size_t out_size)
172{
173 if (!out || (!in && in_len > 0)) return HOST_ERR_INVALID_ARG;
174 if (out_size < in_len * 2 + 1) return HOST_ERR_NO_MEMORY;
175 static const char HEX[] = "0123456789ABCDEF";
176 for (size_t i = 0; i < in_len; ++i) {
177 out[i * 2] = HEX[in[i] >> 4];
178 out[i * 2 + 1] = HEX[in[i] & 0x0F];
179 }
180 out[in_len * 2] = '\0';
181 return HOST_OK;
182}
183
184int host_hex_decode(const char* in, size_t in_len, uint8_t* out, size_t out_size)
185{
186 if (!in || !out) return HOST_ERR_INVALID_ARG;
187 if (in_len % 2 != 0) return HOST_ERR_INVALID_ARG;
188 size_t need = in_len / 2;
189 if (out_size < need) return HOST_ERR_NO_MEMORY;
190 for (size_t i = 0; i < need; ++i) {
191 int hi = cdc::plugin_manager::hex_val(in[i * 2]);
192 int lo = cdc::plugin_manager::hex_val(in[i * 2 + 1]);
193 if (hi < 0 || lo < 0) return HOST_ERR_INVALID_ARG;
194 out[i] = static_cast<uint8_t>((hi << 4) | lo);
195 }
196 return static_cast<int>(need);
197}
198
199} // extern "C"
Shared AES-256-GCM helpers built on mbedTLS.
Hex-digit decoding shared by the plugin_manager source files.
int host_hex_encode(const uint8_t *in, size_t in_len, char *out, size_t out_size)
Lowercase-hex-encode in into NUL-terminated out.
int host_random_strict(uint8_t *buf, size_t len)
Fill buf with hardware-RNG bytes only; fails without TRNG.
int host_base64_decode(const char *in, size_t in_len, uint8_t *out, size_t out_size)
Base64-decode in into raw bytes in out.
int host_base32_encode(const uint8_t *in, size_t in_len, char *out, size_t out_size)
Base32-encode in into NUL-terminated out.
int host_random(uint8_t *buf, size_t len)
Fill buf with hardware-RNG bytes; may fall back to PRNG.
int host_aes_gcm_decrypt(const uint8_t *key, const uint8_t *iv, const uint8_t *aad, size_t aad_len, const uint8_t *ct, size_t ct_len, const uint8_t tag[16], uint8_t *pt)
AES-256-GCM decrypt and verify.
int host_base64_encode(const uint8_t *in, size_t in_len, char *out, size_t out_size)
Base64-encode in into NUL-terminated out.
int host_aes_gcm_encrypt(const uint8_t *key, const uint8_t *iv, const uint8_t *aad, size_t aad_len, const uint8_t *pt, size_t pt_len, uint8_t *ct, uint8_t tag[16])
AES-256-GCM encrypt.
int host_sha256(const uint8_t *data, size_t len, uint8_t out[32])
SHA-256 hash of data into the 32-byte out.
int host_hmac_sha256(const uint8_t *key, size_t klen, const uint8_t *data, size_t dlen, uint8_t out[32])
HMAC-SHA-256 of data under key into the 32-byte out.
int host_base32_decode(const char *in, size_t in_len, uint8_t *out, size_t out_size)
Base32-decode in into raw bytes in out.
int host_hex_decode(const char *in, size_t in_len, uint8_t *out, size_t out_size)
Hex-decode in (case-insensitive) into raw bytes in out.
CDC Badge OS plugin host API - canonical C ABI contract.
#define HOST_ERR_NOT_SUPPORTED
Definition host_api.h:45
#define HOST_OK
Definition host_api.h:37
#define HOST_ERR_INVALID_ARG
Definition host_api.h:39
#define HOST_ERR_NO_MEMORY
Definition host_api.h:43
#define HOST_ERR_GENERIC
Definition host_api.h:38
bool aesGcm256Seal(const uint8_t key[32], const uint8_t *iv, size_t ivLen, const uint8_t *aad, size_t aadLen, const uint8_t *pt, size_t ptLen, uint8_t *ctOut, uint8_t tagOut[16])
Encrypts pt with AES-256-GCM and produces a 16-byte tag.
Definition Crypto.h:48
bool aesGcm256Open(const uint8_t key[32], const uint8_t *iv, size_t ivLen, const uint8_t *aad, size_t aadLen, const uint8_t *ct, size_t ctLen, const uint8_t tag[16], uint8_t *ptOut)
Authenticates and decrypts ct with AES-256-GCM.
Definition Crypto.h:79
ISecureElement * getSecureElementInstance()
Returns singleton secure-element stub instance.
int hex_val(char c)
Convert a single hex digit to its numeric value.
Definition HexUtil.h:15