ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
key_vault.c
Go to the documentation of this file.
1
27
28#include "key_vault.h"
29
30#include <stdint.h>
31
32#include "ra8_attributes.h"
33#include "ra8_check.h"
34#include "ra8_err.h"
35#include "ra8_secure.h"
36
37static const char* s_tag = "KEYV";
38
39/*
40 * Fail-closed stub-crypto gate (issue #180). The SHA-256 below is real
41 * (FIPS 180-4), but the surrounding key vault is a software placeholder that
42 * holds symmetric keys in a plain secure-SRAM array with no hardware-backed
43 * protection -- a stand-in for a real key store. It is only meant for an
44 * off-target build or an explicitly-declared insecure dev/eval image. A real
45 * production/HIL image (neither flag set) compiles the #else branch, where
46 * every entry point hard-errors so the placeholder vault cannot be relied on.
47 * scripts/checks/check_stub_crypto_guarded.py enforces the guard.
48 */
49#if defined(RA8_INSECURE_STUB_CRYPTO) || defined(RA8_OFF_TARGET)
50
52typedef enum : uint16_t {
53 k_sha256_msg_bytes = 32U,
54 k_sha256_words = 64U,
55 k_sha256_block_bytes = 64U,
56} sha256_dim_t;
57
59typedef enum : uint8_t {
60 k_sha256_pad_pos = 32U,
61 k_sha256_pad_marker = 0x80U,
62 k_sha256_pad_zero_start = 33U,
63 k_sha256_len_b0 = 56U,
64 k_sha256_len_b1 = 57U,
65 k_sha256_len_b2 = 58U,
66 k_sha256_len_b3 = 59U,
67 k_sha256_len_b4 = 60U,
68 k_sha256_len_b5 = 61U,
69 k_sha256_len_b6 = 62U,
70 k_sha256_len_b7 = 63U,
71 k_sha256_len_hi = 0x01U,
72 k_sha256_byte_shift = 24U,
73} sha256_pad_t;
74
76typedef enum : uint8_t {
77 k_sha256_ssig0_r0 = 7U,
78 k_sha256_ssig0_r1 = 18U,
79 k_sha256_ssig0_sh = 3U,
80 k_sha256_ssig1_r0 = 17U,
81 k_sha256_ssig1_r1 = 19U,
82 k_sha256_ssig1_sh = 10U,
83 k_sha256_bsig1_r0 = 6U,
84 k_sha256_bsig1_r1 = 11U,
85 k_sha256_bsig1_r2 = 25U,
86 k_sha256_bsig0_r0 = 2U,
87 k_sha256_bsig0_r1 = 13U,
88 k_sha256_bsig0_r2 = 22U,
89 k_sha256_back15 = 15U,
90 k_sha256_back7 = 7U,
91} sha256_rot_t;
92
94typedef enum : uint8_t {
95 k_sha256_st_a = 0U,
96 k_sha256_st_b = 1U,
97 k_sha256_st_c = 2U,
98 k_sha256_st_d = 3U,
99 k_sha256_st_e = 4U,
100 k_sha256_st_f = 5U,
101 k_sha256_st_g = 6U,
102 k_sha256_st_h = 7U,
103} sha256_state_idx_t;
104
109typedef struct {
110 uint8_t key[k_ra8_key_vault_key_bytes];
111} ra8_key_vault_slot_t;
112
113static ra8_key_vault_slot_t s_vault[k_ra8_key_vault_slots];
114
116typedef enum : uint16_t {
117 k_kv_mac_key_128 = 16U,
118 k_kv_mac_key_256 = 32U,
119} ra8_kv_mac_key_len_t;
120
134static uint8_t s_mac_key[k_ra8_key_vault_mac_key_bytes];
135
144static uint16_t s_mac_key_len = 0U;
145
146/* =============================================================================
147 * Tiny SHA-256 (single-block, 32-byte input)
148 * =============================================================================
149 *
150 * Implements just enough of FIPS 180-4 to compute SHA-256 over a
151 * 32-byte input. Used by ra8_key_vault_sha256_xor_challenge to
152 * scramble the key in a way that does not reveal the key bytes.
153 *
154 * The SHA-256 sponge is a transcription of the FIPS 180-4 spec.
155 * Every magic number below comes directly from the standard;
156 * giving them names would inflate the code without adding clarity.
157 */
158
159static const uint32_t k_sha256_k[64] = {
160 0x428a2f98U, 0x71374491U, 0xb5c0fbcfU, 0xe9b5dba5U, 0x3956c25bU, 0x59f111f1U, 0x923f82a4U,
161 0xab1c5ed5U, 0xd807aa98U, 0x12835b01U, 0x243185beU, 0x550c7dc3U, 0x72be5d74U, 0x80deb1feU,
162 0x9bdc06a7U, 0xc19bf174U, 0xe49b69c1U, 0xefbe4786U, 0x0fc19dc6U, 0x240ca1ccU, 0x2de92c6fU,
163 0x4a7484aaU, 0x5cb0a9dcU, 0x76f988daU, 0x983e5152U, 0xa831c66dU, 0xb00327c8U, 0xbf597fc7U,
164 0xc6e00bf3U, 0xd5a79147U, 0x06ca6351U, 0x14292967U, 0x27b70a85U, 0x2e1b2138U, 0x4d2c6dfcU,
165 0x53380d13U, 0x650a7354U, 0x766a0abbU, 0x81c2c92eU, 0x92722c85U, 0xa2bfe8a1U, 0xa81a664bU,
166 0xc24b8b70U, 0xc76c51a3U, 0xd192e819U, 0xd6990624U, 0xf40e3585U, 0x106aa070U, 0x19a4c116U,
167 0x1e376c08U, 0x2748774cU, 0x34b0bcb5U, 0x391c0cb3U, 0x4ed8aa4aU, 0x5b9cca4fU, 0x682e6ff3U,
168 0x748f82eeU, 0x78a5636fU, 0x84c87814U, 0x8cc70208U, 0x90befffaU, 0xa4506cebU, 0xbef9a3f7U,
169 0xc67178f2U,
170};
171
172static const uint32_t k_sha256_h0[8] = {
173 0x6a09e667U,
174 0xbb67ae85U,
175 0x3c6ef372U,
176 0xa54ff53aU,
177 0x510e527fU,
178 0x9b05688cU,
179 0x1f83d9abU,
180 0x5be0cd19U,
181};
182
204RA8_INTERNAL static uint32_t internal_rotr(uint32_t x, uint32_t n)
205{
206 return (x >> n) | (x << (32U - n));
207}
208
209/* internal_sha256_32 is split into
210 * three helpers (build_block, schedule, compress). Each helper iterates a
211 * fixed loop count with no data-dependent branches; the only inputs whose
212 * values steer control flow are loop indices, which are compile-time
213 * constants. Side-channel posture is identical to the monolithic form. */
214
236RA8_INTERNAL static void internal_sha256_build_block(const uint8_t* in32, uint8_t* block)
237{
238 /* Build the 64-byte block: 32 input bytes, 0x80, zeros, then
239 * the 64-bit length in bits at the end (SHA-256 uses BIG-endian
240 * length). */
241 for (uint32_t i = 0U; i < k_sha256_msg_bytes; ++i) {
242 block[i] = in32[i];
243 }
244 block[k_sha256_pad_pos] = k_sha256_pad_marker;
245 for (uint32_t i = k_sha256_pad_zero_start; i < k_sha256_len_b0; ++i) {
246 block[i] = 0U;
247 }
248 /* Length = 32 bytes = 256 bits = 0x0100 big-endian. */
249 block[k_sha256_len_b0] = 0U;
250 block[k_sha256_len_b1] = 0U;
251 block[k_sha256_len_b2] = 0U;
252 block[k_sha256_len_b3] = 0U;
253 block[k_sha256_len_b4] = 0U;
254 block[k_sha256_len_b5] = 0U;
255 block[k_sha256_len_b6] = k_sha256_len_hi;
256 block[k_sha256_len_b7] = 0x00U;
257}
258
279RA8_INTERNAL static void internal_sha256_schedule(const uint8_t* block, uint32_t* w)
280{
281 for (uint32_t i = 0U; i < 16U; ++i) {
282 w[i] = ((uint32_t)block[(i * 4U) + 0U] << k_sha256_byte_shift) |
283 ((uint32_t)block[(i * 4U) + 1U] << 16U) | ((uint32_t)block[(i * 4U) + 2U] << 8U) |
284 ((uint32_t)block[(i * 4U) + 3U]);
285 }
286 for (uint32_t i = 16U; i < k_sha256_words; ++i) {
287 const uint32_t s0 = internal_rotr(w[i - k_sha256_back15], k_sha256_ssig0_r0) ^
288 internal_rotr(w[i - k_sha256_back15], k_sha256_ssig0_r1) ^
289 (w[i - k_sha256_back15] >> k_sha256_ssig0_sh);
290 const uint32_t s1 = internal_rotr(w[i - 2U], k_sha256_ssig1_r0) ^
291 internal_rotr(w[i - 2U], k_sha256_ssig1_r1) ^
292 (w[i - 2U] >> k_sha256_ssig1_sh);
293 w[i] = w[i - 16U] + s0 + w[i - k_sha256_back7] + s1;
294 }
295}
296
319RA8_INTERNAL static void internal_sha256_compress(const uint32_t* w, uint8_t* out32)
320{
321 uint32_t a = k_sha256_h0[k_sha256_st_a];
322 uint32_t b = k_sha256_h0[k_sha256_st_b];
323 uint32_t c = k_sha256_h0[k_sha256_st_c];
324 uint32_t d = k_sha256_h0[k_sha256_st_d];
325 uint32_t e = k_sha256_h0[k_sha256_st_e];
326 uint32_t f = k_sha256_h0[k_sha256_st_f];
327 uint32_t g = k_sha256_h0[k_sha256_st_g];
328 uint32_t h = k_sha256_h0[k_sha256_st_h];
329
330 for (uint32_t i = 0U; i < k_sha256_words; ++i) {
331 const uint32_t s1 = internal_rotr(e, k_sha256_bsig1_r0) ^ internal_rotr(e, k_sha256_bsig1_r1) ^
332 internal_rotr(e, k_sha256_bsig1_r2);
333 const uint32_t ch = (e & f) ^ (~e & g);
334 const uint32_t temp1 = h + s1 + ch + k_sha256_k[i] + w[i];
335 const uint32_t s0 = internal_rotr(a, k_sha256_bsig0_r0) ^ internal_rotr(a, k_sha256_bsig0_r1) ^
336 internal_rotr(a, k_sha256_bsig0_r2);
337 const uint32_t maj = (a & b) ^ (a & c) ^ (b & c);
338 const uint32_t temp2 = s0 + maj;
339 h = g;
340 g = f;
341 f = e;
342 e = d + temp1;
343 d = c;
344 c = b;
345 b = a;
346 a = temp1 + temp2;
347 }
348
349 const uint32_t hh[8] = {
350 a + k_sha256_h0[k_sha256_st_a],
351 b + k_sha256_h0[k_sha256_st_b],
352 c + k_sha256_h0[k_sha256_st_c],
353 d + k_sha256_h0[k_sha256_st_d],
354 e + k_sha256_h0[k_sha256_st_e],
355 f + k_sha256_h0[k_sha256_st_f],
356 g + k_sha256_h0[k_sha256_st_g],
357 h + k_sha256_h0[k_sha256_st_h],
358 };
359 for (uint32_t i = 0U; i < 8U; ++i) {
360 out32[(i * 4U) + 0U] = (uint8_t)(hh[i] >> k_sha256_byte_shift);
361 out32[(i * 4U) + 1U] = (uint8_t)(hh[i] >> 16U);
362 out32[(i * 4U) + 2U] = (uint8_t)(hh[i] >> 8U);
363 out32[(i * 4U) + 3U] = (uint8_t)(hh[i]);
364 }
365}
366
389RA8_INTERNAL static void internal_sha256_32(const uint8_t* in32, uint8_t* out32)
390{
391 uint8_t block[k_sha256_block_bytes];
392 uint32_t w[k_sha256_words];
393 internal_sha256_build_block(in32, block);
394 internal_sha256_schedule(block, w);
395 internal_sha256_compress(w, out32);
396}
397
398/* =============================================================================
399 * Public API
400 * =============================================================================
401 */
402
424{
425 for (uint16_t s = 0U; s < k_ra8_key_vault_slots; ++s) {
426 for (uint16_t i = 0U; i < k_ra8_key_vault_key_bytes; ++i) {
427 s_vault[s].key[i] = 0U;
428 }
429 }
430 for (uint16_t i = 0U; i < k_ra8_key_vault_mac_key_bytes; ++i) {
431 s_mac_key[i] = 0U;
432 }
433 s_mac_key_len = 0U;
434 return k_ra8_ok;
435}
436
462ra8_err_t ra8_key_vault_store(uint16_t slot, const uint8_t* key)
463{
464 RA8_CHECK_NULL_PTR(key, s_tag, "store: key");
465 if (slot >= k_ra8_key_vault_slots) {
467 }
468 for (uint16_t i = 0U; i < k_ra8_key_vault_key_bytes; ++i) {
469 s_vault[slot].key[i] = key[i];
470 }
471 return k_ra8_ok;
472}
473
502ra8_err_t ra8_key_vault_sha256_xor_challenge(uint16_t slot, const uint8_t* challenge, uint8_t* out)
503{
504 RA8_CHECK_NULL_PTR(challenge, s_tag, "challenge: challenge");
505 RA8_CHECK_NULL_PTR(out, s_tag, "challenge: out");
506 if (slot >= k_ra8_key_vault_slots) {
508 }
509 uint8_t scratch[k_ra8_key_vault_key_bytes] = {};
510 for (uint16_t i = 0U; i < k_ra8_key_vault_key_bytes; ++i) {
511 scratch[i] = (uint8_t)(s_vault[slot].key[i] ^ challenge[i]);
512 }
513 internal_sha256_32(scratch, out);
514 /* Wipe the key-XOR-challenge result from the secure stack before the frame is
515 * reused. ra8_secure_memzero writes through a volatile pointer, so -- unlike the
516 * plain zero loop it replaces, which the optimiser was free to elide as a dead
517 * store (the old code even suppressed cppcheck's unreadVariable) -- the erase
518 * is an observable side effect that survives -O2 (T5-12). */
520 return k_ra8_ok;
521}
522
523ra8_err_t ra8_key_vault_set_mac_key(const uint8_t* key, uint16_t key_len)
524{
525 RA8_CHECK_NULL_PTR(key, s_tag, "set_mac_key: key");
526 if ((key_len != (uint16_t)k_kv_mac_key_128) && (key_len != (uint16_t)k_kv_mac_key_256)) {
528 }
529 for (uint16_t i = 0U; i < k_ra8_key_vault_mac_key_bytes; ++i) {
530 s_mac_key[i] = (i < key_len) ? key[i] : 0U;
531 }
532 s_mac_key_len = key_len;
533 return k_ra8_ok;
534}
535
536ra8_err_t ra8_key_vault_load_mac_key(uint8_t* out, uint16_t out_cap, uint16_t* out_len)
537{
538 RA8_CHECK_NULL_PTR(out, s_tag, "load_mac_key: out");
539 RA8_CHECK_NULL_PTR(out_len, s_tag, "load_mac_key: out_len");
540 if (s_mac_key_len == 0U) {
541 return k_ra8_err_not_found;
542 }
543 if (out_cap < s_mac_key_len) {
545 }
546 for (uint16_t i = 0U; i < s_mac_key_len; ++i) {
547 out[i] = s_mac_key[i];
548 }
549 *out_len = s_mac_key_len;
550 return k_ra8_ok;
551}
552
553#else /* production build: neither RA8_INSECURE_STUB_CRYPTO nor RA8_OFF_TARGET */
554
555/*
556 * Fail-closed production variant. The software key store above is a placeholder
557 * for a hardware-backed vault, so every entry point returns a hard error
558 * (never k_ra8_ok). A production image that forgot to provide the real vault
559 * therefore cannot store keys in unprotected SRAM or answer challenges from it.
560 */
561
566
567ra8_err_t ra8_key_vault_store(uint16_t slot, const uint8_t* key)
568{
569 RA8_CHECK_NULL_PTR(key, s_tag, "store: key");
570 (void)slot;
572}
573
574ra8_err_t ra8_key_vault_sha256_xor_challenge(uint16_t slot, const uint8_t* challenge, uint8_t* out)
575{
576 RA8_CHECK_NULL_PTR(challenge, s_tag, "challenge: challenge");
577 RA8_CHECK_NULL_PTR(out, s_tag, "challenge: out");
578 (void)slot;
580}
581
582ra8_err_t ra8_key_vault_set_mac_key(const uint8_t* key, uint16_t key_len)
583{
584 RA8_CHECK_NULL_PTR(key, s_tag, "set_mac_key: key");
585 (void)key_len;
587}
588
589ra8_err_t ra8_key_vault_load_mac_key(uint8_t* out, uint16_t out_cap, uint16_t* out_len)
590{
591 RA8_CHECK_NULL_PTR(out, s_tag, "load_mac_key: out");
592 RA8_CHECK_NULL_PTR(out_len, s_tag, "load_mac_key: out_len");
593 (void)out_cap;
595}
596
597#endif /* RA8_INSECURE_STUB_CRYPTO || RA8_OFF_TARGET */
ra8_err_t ra8_key_vault_sha256_xor_challenge(uint16_t slot, const uint8_t *challenge, uint8_t *out)
Compute SHA-256(key XOR challenge) for slot.
Definition key_vault.c:574
ra8_err_t ra8_key_vault_set_mac_key(const uint8_t *key, uint16_t key_len)
Provision the key-authentication key (KAK) used to MAC key imports.
Definition key_vault.c:582
ra8_err_t ra8_key_vault_store(uint16_t slot, const uint8_t *key)
Programme a 256-bit symmetric key into a vault slot.
Definition key_vault.c:567
ra8_err_t ra8_key_vault_init(void)
Initialise the vault (zero every slot).
Definition key_vault.c:562
ra8_err_t ra8_key_vault_load_mac_key(uint8_t *out, uint16_t out_cap, uint16_t *out_len)
Copy the provisioned key-authentication key for a secure caller.
Definition key_vault.c:589
Secure-only symmetric key store.
@ k_ra8_key_vault_mac_key_bytes
Max key-authentication key.
Definition key_vault.h:61
@ k_ra8_key_vault_key_bytes
256-bit symmetric key.
Definition key_vault.h:58
@ k_ra8_key_vault_slots
Number of stored keys.
Definition key_vault.h:57
static const char * s_tag
Logging / check tag.
Definition ra8_app.c:17
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Validation and Error-Checking Macros for ra8-firmware.
#define RA8_CHECK_NULL_PTR(ptr, tag, message)
Reject nullptr pointer, returning k_ra8_err_null_ptr.
Definition ra8_check.h:243
Error Code Definitions for ra8-firmware.
@ k_ra8_err_not_supported
Requested feature not compiled in, not wired, or not supported by this MCU variant.
Definition ra8_err.h:180
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_not_found
Requested item not found (lookup / search missed).
Definition ra8_err.h:173
@ k_ra8_err_invalid_size
Invalid size parameter (too large, too small, or misaligned).
Definition ra8_err.h:167
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
Secure-comparison primitives for the crypto / secure-boot paths.
void ra8_secure_memzero(void *ptr, size_t len)
Securely zero a buffer such that the write cannot be optimised away.
Definition ra8_secure.c:37