cryptnox-sdk-esp32 1.0.0
ESP32 SDK for Cryptnox Hardware Wallet
Loading...
Searching...
No Matches
test_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
6#include "unity.h"
8#include "CW_Defs.h"
9#include "CW_Utils.h"
10#include <string.h>
11#include <stdio.h>
12
13/******************************************************************
14 * 1. Sizes used across all test cases
15 ******************************************************************/
16
17#define TV_SHA256_OUT_BYTES (32U)
18#define TV_SHA512_OUT_BYTES (64U)
19#define TV_AES_KEY_BYTES (16U)
20#define TV_AES_IV_BYTES (16U)
21#define TV_AES_BLOCK_BYTES (16U)
22#define TV_EC_COORD_BYTES (32U)
23#define TV_EC_PUBKEY_BYTES (64U)
24#define TV_RANDOM_BYTES (32U)
25#define TV_BIT_PAD_INPUT_BYTES (3U) /* plaintext shorter than one AES block */
26
27/******************************************************************
28 * 2. Static provider instance (default-constructed, no heap)
29 ******************************************************************/
30
32
33/******************************************************************
34 * 3. SHA-256 — NIST FIPS 180-4, message "abc"
35 ******************************************************************/
36
37TEST_CASE("sha256 NIST abc vector", "[crypto_provider]")
38{
39 static const uint8_t input[] = { 'a', 'b', 'c' };
40 /* NIST FIPS 180-4 SHA-256("abc") = ba7816bf8f01cfea414140de5dae2ec7
41 * 3d380fb00bbb35e2b9f27d3c0eaed7c3 */
42 static const uint8_t expected[TV_SHA256_OUT_BYTES] = {
43 0xbaU, 0x78U, 0x16U, 0xbfU, 0x8fU, 0x01U, 0xcfU, 0xeaU,
44 0x41U, 0x41U, 0x40U, 0xdeU, 0x5dU, 0xaeU, 0x2eU, 0xc7U,
45 0x3dU, 0x38U, 0x0fU, 0xb0U, 0x0bU, 0xbbU, 0x35U, 0xe2U,
46 0xb9U, 0xf2U, 0x7dU, 0x3cU, 0x0eU, 0xaeU, 0xd7U, 0xc3U
47 };
48 uint8_t out[TV_SHA256_OUT_BYTES] = { 0U };
49
50 s_provider.sha256(input, sizeof(input), out);
51
52 printf("[sha256 via provider] ");
53 for (size_t i = 0U; i < TV_SHA256_OUT_BYTES; i++) {
54 printf("%02x", static_cast<unsigned int>(out[i]));
55 }
56 printf("\n");
57
58 TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, out, TV_SHA256_OUT_BYTES);
59}
60
61/******************************************************************
62 * 4. SHA-512 — NIST FIPS 180-4, message "abc"
63 ******************************************************************/
64
65TEST_CASE("sha512 NIST abc vector", "[crypto_provider]")
66{
67 static const uint8_t input[] = { 'a', 'b', 'c' };
68 static const uint8_t expected[TV_SHA512_OUT_BYTES] = {
69 0xddU, 0xafU, 0x35U, 0xa1U, 0x93U, 0x61U, 0x7aU, 0xbaU,
70 0xccU, 0x41U, 0x73U, 0x49U, 0xaeU, 0x20U, 0x41U, 0x31U,
71 0x12U, 0xe6U, 0xfaU, 0x4eU, 0x89U, 0xa9U, 0x7eU, 0xa2U,
72 0x0aU, 0x9eU, 0xeeU, 0xe6U, 0x4bU, 0x55U, 0xd3U, 0x9aU,
73 0x21U, 0x92U, 0x99U, 0x2aU, 0x27U, 0x4fU, 0xc1U, 0xa8U,
74 0x36U, 0xbaU, 0x3cU, 0x23U, 0xa3U, 0xfeU, 0xebU, 0xbdU,
75 0x45U, 0x4dU, 0x44U, 0x23U, 0x64U, 0x3cU, 0xe8U, 0x0eU,
76 0x2aU, 0x9aU, 0xc9U, 0x4fU, 0xa5U, 0x4cU, 0xa4U, 0x9fU
77 };
78 uint8_t out[TV_SHA512_OUT_BYTES] = { 0U };
79
80 s_provider.sha512(input, sizeof(input), out);
81
82 TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, out, TV_SHA512_OUT_BYTES);
83}
84
85/******************************************************************
86 * 5. AES-128-CBC encrypt — NIST SP 800-38A F.2.1, one block
87 ******************************************************************/
88
89TEST_CASE("aesCbcEncrypt NIST SP800-38A F.2.1 one block", "[crypto_provider]")
90{
91 static const uint8_t key[TV_AES_KEY_BYTES] = {
92 0x2bU, 0x7eU, 0x15U, 0x16U, 0x28U, 0xaeU, 0xd2U, 0xa6U,
93 0xabU, 0xf7U, 0x15U, 0x88U, 0x09U, 0xcfU, 0x4fU, 0x3cU
94 };
95 static const uint8_t plaintext[TV_AES_BLOCK_BYTES] = {
96 0x6bU, 0xc1U, 0xbeU, 0xe2U, 0x2eU, 0x40U, 0x9fU, 0x96U,
97 0xe9U, 0x3dU, 0x7eU, 0x11U, 0x73U, 0x93U, 0x17U, 0x2aU
98 };
99 static const uint8_t expected[TV_AES_BLOCK_BYTES] = {
100 0x76U, 0x49U, 0xabU, 0xacU, 0x81U, 0x19U, 0xb2U, 0x46U,
101 0xceU, 0xe9U, 0x8eU, 0x9bU, 0x12U, 0xe9U, 0x19U, 0x7dU
102 };
103 uint8_t iv[TV_AES_IV_BYTES] = {
104 0x00U, 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x06U, 0x07U,
105 0x08U, 0x09U, 0x0aU, 0x0bU, 0x0cU, 0x0dU, 0x0eU, 0x0fU
106 };
107 uint8_t out[TV_AES_BLOCK_BYTES] = { 0U };
108
109 uint16_t encLen = s_provider.aesCbcEncrypt(
110 plaintext,
111 static_cast<uint16_t>(sizeof(plaintext)),
112 out,
113 key,
114 static_cast<uint8_t>(sizeof(key)),
115 iv,
116 false);
117
118 TEST_ASSERT_EQUAL_UINT16(static_cast<uint16_t>(TV_AES_BLOCK_BYTES), encLen);
119 TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, out, TV_AES_BLOCK_BYTES);
120}
121
122/******************************************************************
123 * 6. AES-128-CBC decrypt — NIST SP 800-38A F.2.2, one block
124 ******************************************************************/
125
126TEST_CASE("aesCbcDecrypt NIST SP800-38A F.2.2 one block", "[crypto_provider]")
127{
128 static const uint8_t key[TV_AES_KEY_BYTES] = {
129 0x2bU, 0x7eU, 0x15U, 0x16U, 0x28U, 0xaeU, 0xd2U, 0xa6U,
130 0xabU, 0xf7U, 0x15U, 0x88U, 0x09U, 0xcfU, 0x4fU, 0x3cU
131 };
132 uint8_t ciphertext[TV_AES_BLOCK_BYTES] = {
133 0x76U, 0x49U, 0xabU, 0xacU, 0x81U, 0x19U, 0xb2U, 0x46U,
134 0xceU, 0xe9U, 0x8eU, 0x9bU, 0x12U, 0xe9U, 0x19U, 0x7dU
135 };
136 static const uint8_t expected[TV_AES_BLOCK_BYTES] = {
137 0x6bU, 0xc1U, 0xbeU, 0xe2U, 0x2eU, 0x40U, 0x9fU, 0x96U,
138 0xe9U, 0x3dU, 0x7eU, 0x11U, 0x73U, 0x93U, 0x17U, 0x2aU
139 };
140 uint8_t iv[TV_AES_IV_BYTES] = {
141 0x00U, 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x06U, 0x07U,
142 0x08U, 0x09U, 0x0aU, 0x0bU, 0x0cU, 0x0dU, 0x0eU, 0x0fU
143 };
144 uint8_t out[TV_AES_BLOCK_BYTES] = { 0U };
145
146 uint16_t decLen = s_provider.aesCbcDecrypt(
147 ciphertext,
148 static_cast<uint16_t>(sizeof(ciphertext)),
149 out,
150 key,
151 static_cast<uint8_t>(sizeof(key)),
152 iv,
153 false);
154
155 TEST_ASSERT_EQUAL_UINT16(static_cast<uint16_t>(TV_AES_BLOCK_BYTES), decLen);
156 TEST_ASSERT_EQUAL_HEX8_ARRAY(expected, out, TV_AES_BLOCK_BYTES);
157}
158
159/******************************************************************
160 * 7. AES-128-CBC bit-padding round-trip
161 * Input shorter than one block → padded to 16 bytes on encrypt,
162 * stripped back to original length on decrypt.
163 ******************************************************************/
164
165TEST_CASE("aesCbc bit-padding round-trip", "[crypto_provider]")
166{
167 static const uint8_t key[TV_AES_KEY_BYTES] = {
168 0x2bU, 0x7eU, 0x15U, 0x16U, 0x28U, 0xaeU, 0xd2U, 0xa6U,
169 0xabU, 0xf7U, 0x15U, 0x88U, 0x09U, 0xcfU, 0x4fU, 0x3cU
170 };
171 static const uint8_t plaintext[TV_BIT_PAD_INPUT_BYTES] = {
172 0x01U, 0x02U, 0x03U
173 };
174
175 uint8_t iv_enc[TV_AES_IV_BYTES] = {
176 0x00U, 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x06U, 0x07U,
177 0x08U, 0x09U, 0x0aU, 0x0bU, 0x0cU, 0x0dU, 0x0eU, 0x0fU
178 };
179 uint8_t iv_dec[TV_AES_IV_BYTES] = {
180 0x00U, 0x01U, 0x02U, 0x03U, 0x04U, 0x05U, 0x06U, 0x07U,
181 0x08U, 0x09U, 0x0aU, 0x0bU, 0x0cU, 0x0dU, 0x0eU, 0x0fU
182 };
183 uint8_t ciphertext[TV_AES_BLOCK_BYTES] = { 0U };
184 uint8_t recovered[TV_AES_BLOCK_BYTES] = { 0U };
185
186 uint16_t encLen = s_provider.aesCbcEncrypt(
187 plaintext,
188 static_cast<uint16_t>(sizeof(plaintext)),
189 ciphertext,
190 key,
191 static_cast<uint8_t>(sizeof(key)),
192 iv_enc,
193 true);
194
195 /* 3 bytes + 0x80 marker → padded to one full block */
196 TEST_ASSERT_EQUAL_UINT16(static_cast<uint16_t>(TV_AES_BLOCK_BYTES), encLen);
197
198 uint16_t decLen = s_provider.aesCbcDecrypt(
199 ciphertext,
200 encLen,
201 recovered,
202 key,
203 static_cast<uint8_t>(sizeof(key)),
204 iv_dec,
205 true);
206
207 TEST_ASSERT_EQUAL_UINT16(static_cast<uint16_t>(TV_BIT_PAD_INPUT_BYTES), decLen);
208 TEST_ASSERT_EQUAL_HEX8_ARRAY(plaintext, recovered, TV_BIT_PAD_INPUT_BYTES);
209}
210
211/******************************************************************
212 * 8. ECDH — two-party shared secret symmetry on secp256r1
213 ******************************************************************/
214
215TEST_CASE("ecdh shared secret symmetry secp256r1", "[crypto_provider]")
216{
217 uint8_t pubA[TV_EC_PUBKEY_BYTES] = { 0U };
218 uint8_t privA[TV_EC_COORD_BYTES] = { 0U };
219 uint8_t pubB[TV_EC_PUBKEY_BYTES] = { 0U };
220 uint8_t privB[TV_EC_COORD_BYTES] = { 0U };
221 uint8_t secretA[TV_EC_COORD_BYTES] = { 0U };
222 uint8_t secretB[TV_EC_COORD_BYTES] = { 0U };
223
224 CW_Curve curve = CW_CURVE_SECP256R1;
225
226 bool okA = s_provider.makeKey(pubA, privA, curve);
227 bool okB = s_provider.makeKey(pubB, privB, curve);
228
229 TEST_ASSERT_TRUE(okA);
230 TEST_ASSERT_TRUE(okB);
231
232 bool ecdhA = s_provider.ecdh(pubB, privA, secretA, curve);
233 bool ecdhB = s_provider.ecdh(pubA, privB, secretB, curve);
234
235 TEST_ASSERT_TRUE(ecdhA);
236 TEST_ASSERT_TRUE(ecdhB);
237 TEST_ASSERT_EQUAL_HEX8_ARRAY(secretA, secretB, TV_EC_COORD_BYTES);
238}
239
240/******************************************************************
241 * 9. random — returns true; two draws from the TRNG must differ
242 ******************************************************************/
243
244TEST_CASE("random returns true and produces distinct draws", "[crypto_provider]")
245{
246 uint8_t buf1[TV_RANDOM_BYTES] = { 0U };
247 uint8_t buf2[TV_RANDOM_BYTES] = { 0U };
248
249 bool randomGeneration1Succeeded = s_provider.random(buf1, TV_RANDOM_BYTES);
250 bool randomGeneration2Succeeded = s_provider.random(buf2, TV_RANDOM_BYTES);
251
252 TEST_ASSERT_TRUE(randomGeneration1Succeeded);
253 TEST_ASSERT_TRUE(randomGeneration2Succeeded);
254 /* P(collision of two independent 32-byte TRNG draws) < 2^-256. */
255 TEST_ASSERT_FALSE(CW_Utils::secure_compare(buf1, buf2, TV_RANDOM_BYTES));
256}
CW_CryptoProvider backed by mbedTLS and the ESP32 hardware TRNG.
CW_CryptoProvider implementation for ESP32 using mbedTLS and the hardware TRNG.
#define TV_EC_COORD_BYTES
TEST_CASE("sha256 NIST abc vector", "[crypto_provider]")
#define TV_RANDOM_BYTES
#define TV_AES_KEY_BYTES
#define TV_EC_PUBKEY_BYTES
#define TV_SHA512_OUT_BYTES
#define TV_AES_BLOCK_BYTES
#define TV_BIT_PAD_INPUT_BYTES
#define TV_AES_IV_BYTES
#define TV_SHA256_OUT_BYTES
static ESP32CryptoProvider s_provider