cryptnox-sdk-esp32 1.0.0
ESP32 SDK for Cryptnox Hardware Wallet
Loading...
Searching...
No Matches
esp32_crypto_provider.cpp
Go to the documentation of this file.
1/*
2 * SPDX-License-Identifier: LGPL-3.0-or-later
3 * Copyright (c) 2026 Cryptnox SA
4 */
5
14
16#include "CW_Utils.h"
17#include "uECC.h"
18#include "mbedtls/sha256.h"
19#include "mbedtls/sha512.h"
20#include "mbedtls/aes.h"
21
22/******************************************************************
23 * 1b. Internal curve translator
24 ******************************************************************/
25
26static const uECC_Curve_t* toCurve(CW_Curve curve) {
27 const uECC_Curve_t* result = NULL;
28 switch (curve) {
29 case CW_CURVE_SECP256R1:
30 result = uECC_secp256r1();
31 break;
32 case CW_CURVE_SECP256K1:
33 result = uECC_secp256k1();
34 break;
35 default:
36 result = NULL;
37 break;
38 }
39 return result;
40}
41
42/******************************************************************
43 * 1. Module constants
44 ******************************************************************/
45
46/* AES */
47#define AES_BLOCK_SIZE_BYTES (16U) /* bytes in one AES block */
48#define AES_KEY_BITS_PER_BYTE (8U) /* multiplier: key bytes → key bits */
49/* Maximum AES plaintext that can be padded: covers CW_USER_DATA_PAGE_SIZE + one block. */
50#define AES_PAD_BUF_MAX_INPUT (256U)
51/* Pad buffer: max input + one extra block for the bit-padding byte. */
52#define AES_PAD_BUF_SIZE (AES_PAD_BUF_MAX_INPUT + AES_BLOCK_SIZE_BYTES)
53
54/* ISO/IEC 9797-1 Method 2 (bit padding) */
55#define BIT_PADDING_MARKER (0x80U) /* mandatory leading 1-bit as a full byte */
56#define PADDING_ZERO_FILL (0x00U) /* fill value for padding bytes after marker */
57
58/* mbedTLS SHA mode selectors */
59#define MBEDTLS_SHA256_MODE (0) /* 0 = SHA-256, 1 = SHA-224 */
60#define MBEDTLS_SHA512_MODE (0) /* 0 = SHA-512, 1 = SHA-384 */
61
62/* mbedTLS return code for success */
63#define MBEDTLS_OK (0)
64
65/* uECC return code for success */
66#define UECC_SUCCESS (1)
67
68/******************************************************************
69 * 2. SHA methods
70 ******************************************************************/
71
73bool ESP32CryptoProvider::sha256(const uint8_t* data, size_t len, uint8_t* out) {
74 int ret = mbedtls_sha256(data, len, out, MBEDTLS_SHA256_MODE);
75 return (ret == MBEDTLS_OK);
76}
77
79bool ESP32CryptoProvider::sha512(const uint8_t* data, size_t len, uint8_t* out) {
80 int ret = mbedtls_sha512(data, len, out, MBEDTLS_SHA512_MODE);
81 return (ret == MBEDTLS_OK);
82}
83
84/******************************************************************
85 * 3. AES-CBC encrypt
86 ******************************************************************/
87
89uint16_t ESP32CryptoProvider::aesCbcEncrypt(const uint8_t* in, uint16_t len, uint8_t* out,
90 const uint8_t* key, uint8_t keyLen,
91 uint8_t* iv, bool bitPadding) {
92 uint16_t result = 0U;
93
94 if ((in != NULL) && (out != NULL) && (key != NULL) && (iv != NULL)) {
95 uint8_t padBuf[AES_PAD_BUF_SIZE] = { PADDING_ZERO_FILL };
96 uint16_t encLen = 0U;
97
98 if (bitPadding) {
99 /* Round up to next block boundary after appending the 0x80 marker. */
100 uint32_t paddedLen32 = ((static_cast<uint32_t>(len) + 1U + (AES_BLOCK_SIZE_BYTES - 1U)) / AES_BLOCK_SIZE_BYTES) * AES_BLOCK_SIZE_BYTES;
101 uint16_t paddedLen = static_cast<uint16_t>(paddedLen32);
102
103 if (paddedLen <= static_cast<uint16_t>(AES_PAD_BUF_SIZE)) {
104 (void)CW_Utils::safe_memcpy(padBuf, sizeof(padBuf), in, static_cast<size_t>(len));
105 padBuf[static_cast<size_t>(len)] = BIT_PADDING_MARKER;
106 /* Remaining bytes already zero from initialisation. */
107 encLen = paddedLen;
108 }
109 } else {
110 if (len <= static_cast<uint16_t>(AES_PAD_BUF_SIZE)) {
111 (void)CW_Utils::safe_memcpy(padBuf, sizeof(padBuf), in, static_cast<size_t>(len));
112 encLen = len;
113 }
114 }
115
116 if (encLen > 0U) {
117 mbedtls_aes_context ctx = {};
118 mbedtls_aes_init(&ctx);
119
120 unsigned int keyBits = static_cast<unsigned int>(
121 static_cast<uint32_t>(keyLen) * static_cast<uint32_t>(AES_KEY_BITS_PER_BYTE));
122
123 int ret = mbedtls_aes_setkey_enc(&ctx, key, keyBits);
124
125 if (ret == MBEDTLS_OK) {
126 ret = mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_ENCRYPT,
127 static_cast<size_t>(encLen),
128 iv, padBuf, out);
129 }
130
131 mbedtls_aes_free(&ctx);
132
133 if (ret == MBEDTLS_OK) {
134 result = encLen;
135 }
136 }
137 }
138
139 return result;
140}
141
142/******************************************************************
143 * 4. AES-CBC decrypt
144 ******************************************************************/
145
147uint16_t ESP32CryptoProvider::aesCbcDecrypt(uint8_t* in, uint16_t len, uint8_t* out,
148 const uint8_t* key, uint8_t keyLen,
149 uint8_t* iv, bool bitPadding) {
150 uint16_t result = 0U;
151
152 if ((in != NULL) && (out != NULL) && (key != NULL) && (iv != NULL) && (len > 0U)) {
153 bool blockAligned = ((static_cast<uint32_t>(len) % AES_BLOCK_SIZE_BYTES) == 0U);
154
155 if (blockAligned) {
156 mbedtls_aes_context ctx = {};
157 mbedtls_aes_init(&ctx);
158
159 unsigned int keyBits = static_cast<unsigned int>(
160 static_cast<uint32_t>(keyLen) * static_cast<uint32_t>(AES_KEY_BITS_PER_BYTE));
161
162 int ret = mbedtls_aes_setkey_dec(&ctx, key, keyBits);
163
164 if (ret == MBEDTLS_OK) {
165 ret = mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_DECRYPT,
166 static_cast<size_t>(len),
167 iv, in, out);
168 }
169
170 mbedtls_aes_free(&ctx);
171
172 if (ret == MBEDTLS_OK) {
173 if (bitPadding) {
174 /* Strip ISO/IEC 9797-1 Method 2 padding: scan backward,
175 * skip 0x00 bytes, then expect the 0x80 marker byte.
176 * padPos ends at the index of the 0x80 byte on success. */
177 uint16_t padPos = len;
178 bool found = false;
179 bool searching = true;
180
181 while ((padPos != static_cast<uint16_t>(0U)) && searching) {
182 padPos--;
183 if (out[padPos] == BIT_PADDING_MARKER) {
184 found = true;
185 searching = false;
186 } else if (out[padPos] != PADDING_ZERO_FILL) {
187 searching = false;
188 } else {
189 /* byte is 0x00 — continue scanning toward the marker */
190 }
191 }
192
193 if (found) {
194 result = padPos; /* bytes 0 .. padPos-1 are the plaintext */
195 }
196 } else {
197 result = len;
198 }
199 }
200 }
201 }
202
203 return result;
204}
205
206/******************************************************************
207 * 5. ECDH and key generation (delegated to uECC shim)
208 ******************************************************************/
209
211bool ESP32CryptoProvider::ecdh(const uint8_t* pubKey, const uint8_t* privKey,
212 uint8_t* secret, CW_Curve curve) {
213 bool result = false;
214 const uECC_Curve_t* ueccCurve = toCurve(curve);
215 if (ueccCurve != NULL) {
216 int ret = uECC_shared_secret(pubKey, privKey, secret, ueccCurve);
217 result = (ret == UECC_SUCCESS);
218 }
219 return result;
220}
221
223bool ESP32CryptoProvider::makeKey(uint8_t* pubKey, uint8_t* privKey,
224 CW_Curve curve) {
225 bool result = false;
226 const uECC_Curve_t* ueccCurve = toCurve(curve);
227 if (ueccCurve != NULL) {
228 int ret = uECC_make_key(pubKey, privKey, ueccCurve);
229 result = (ret == UECC_SUCCESS);
230 }
231 return result;
232}
233
234/******************************************************************
235 * 6. Random bytes from ESP32 hardware True RNG
236 ******************************************************************/
237
239bool ESP32CryptoProvider::random(uint8_t* dest, unsigned size) {
240 bool result = false;
241
242 if ((dest != NULL) && (size > 0U)) {
243 result = CW_Utils::fill_secure_random(dest, static_cast<size_t>(size));
244 }
245
246 return result;
247}
248
250bool ESP32CryptoProvider::ecdsaVerify(const uint8_t* pubKey64, const uint8_t* hash,
251 size_t hashLen, const uint8_t* sig,
252 CW_Curve curve) {
253 bool result = false;
254 const uECC_Curve_t* ueccCurve = toCurve(curve);
255 if (ueccCurve != NULL) {
256 int ret = uECC_verify(pubKey64, hash, static_cast<unsigned>(hashLen),
257 sig, ueccCurve);
258 result = (ret == UECC_SUCCESS);
259 }
260 return result;
261}
bool ecdh(const uint8_t *pubKey, const uint8_t *privKey, uint8_t *secret, CW_Curve curve) override
Compute an ECDH shared secret.
bool makeKey(uint8_t *pubKey, uint8_t *privKey, CW_Curve curve) override
Generate an ephemeral EC key pair.
bool random(uint8_t *dest, unsigned size) override
Fill a buffer with cryptographically random bytes.
bool ecdsaVerify(const uint8_t *pubKey64, const uint8_t *hash, size_t hashLen, const uint8_t *sig, CW_Curve curve) override
Verify an ECDSA signature.
uint16_t aesCbcDecrypt(uint8_t *in, uint16_t len, uint8_t *out, const uint8_t *key, uint8_t keyLen, uint8_t *iv, bool bitPadding) override
Decrypt a buffer with AES-CBC.
bool sha512(const uint8_t *data, size_t len, uint8_t *out) override
Compute SHA-512 over a contiguous buffer.
bool sha256(const uint8_t *data, size_t len, uint8_t *out) override
Compute SHA-256 over a contiguous buffer.
uint16_t aesCbcEncrypt(const uint8_t *in, uint16_t len, uint8_t *out, const uint8_t *key, uint8_t keyLen, uint8_t *iv, bool bitPadding) override
Encrypt a buffer with AES-CBC.
#define AES_BLOCK_SIZE_BYTES
#define MBEDTLS_SHA256_MODE
#define BIT_PADDING_MARKER
#define MBEDTLS_OK
#define PADDING_ZERO_FILL
#define UECC_SUCCESS
#define AES_PAD_BUF_SIZE
static const uECC_Curve_t * toCurve(CW_Curve curve)
#define MBEDTLS_SHA512_MODE
#define AES_KEY_BITS_PER_BYTE
CW_CryptoProvider implementation for ESP32 using mbedTLS and the hardware TRNG.
int uECC_shared_secret(const uint8_t *public_key, const uint8_t *private_key, uint8_t *secret, const uECC_Curve_t *curve)
Compute ECDH shared secret (X-coordinate of privKey * pubKey).
const uECC_Curve_t * uECC_secp256k1(void)
Return the static secp256k1 curve descriptor.
const uECC_Curve_t * uECC_secp256r1(void)
Return the static secp256r1 curve descriptor.
int uECC_make_key(uint8_t *public_key, uint8_t *private_key, const uECC_Curve_t *curve)
Generate an ECC key pair using mbedTLS and the ESP32 hardware RNG.
int uECC_verify(const uint8_t *public_key, const uint8_t *hash, unsigned hash_size, const uint8_t *signature, const uECC_Curve_t *curve)
Verify an ECDSA signature (raw 64-byte r||s) against a hash.