ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_rot.c
Go to the documentation of this file.
1
29
30#include "ra8_rot.h"
31
32#ifdef RA8_ENABLE_ROOT_OF_TRUST
33
34#include "ra8_check.h"
35#include "ra8_psa_crypto.h"
36#include "ra8_rsip_core.h"
37
47static const char* s_tag = "ROT";
48
76static const uint8_t s_rot_root_pubkey[k_ra8_rot_pubkey_bytes] = {
77 0x04U, 0xC8U, 0xDCU, 0xA2U, 0xF2U, 0x02U, 0x50U, 0x15U, 0xF2U, 0xFEU, 0x39U, 0xD1U, 0xBDU,
78 0x9AU, 0xB9U, 0xAFU, 0x14U, 0x6AU, 0x76U, 0xA6U, 0x26U, 0x67U, 0x1DU, 0xE7U, 0xFDU, 0xDAU,
79 0x53U, 0x11U, 0xF6U, 0xEAU, 0xC5U, 0x85U, 0xE6U, 0x8EU, 0x53U, 0x0DU, 0x1EU, 0x52U, 0x45U,
80 0xBBU, 0x37U, 0x96U, 0xA4U, 0xF1U, 0x8CU, 0xFAU, 0x83U, 0x22U, 0x43U, 0x0BU, 0xAEU, 0x74U,
81 0xD4U, 0xB5U, 0x53U, 0xE5U, 0xCDU, 0xC7U, 0x94U, 0xBAU, 0x57U, 0x49U, 0x22U, 0x94U, 0xDDU,
82};
83
108RA8_INTERNAL static bool internal_ct_equal(const uint8_t* a, const uint8_t* b, uint32_t len)
109{
110 if ((a == nullptr) || (b == nullptr)) {
111 return false;
112 }
113 if (len == 0U) {
114 return false;
115 }
116 uint8_t diff = 0U;
117 /* Bounded by ``len`` (<= k_ra8_rot_digest_bytes at the only call site):
118 * a statically bounded fold -- NASA Rule 2 compliant. */
119 for (uint32_t i = 0U; i < len; ++i) {
120 diff |= (uint8_t)(a[i] ^ b[i]);
121 }
122 return diff == 0U;
123}
124
151internal_compute_digest(const uint8_t* body, uint32_t body_len, uint8_t* digest)
152{
153 RA8_CHECK_NULL_PTR(body, s_tag, "body");
154 RA8_CHECK_NULL_PTR(digest, s_tag, "digest");
155
156#ifdef RA8_OFF_TARGET
157 size_t produced = 0U;
159 body,
160 (size_t)body_len,
161 digest,
163 &produced);
164#else
165 /* HUM Ch 52.2.3 "Hash Generator" p 3306 -- RSIP HASH engine, fully
166 * abstracted by ra8_rsip_sha256; no raw register access is added here. */
167 return ra8_rsip_sha256(body, body_len, digest);
168#endif
169}
170
178typedef enum : uint8_t {
179 k_rot_octet_bits = 8U,
180} rot_serial_t;
181
211internal_bind_version(uint32_t img_version, const uint8_t* digest, uint8_t* out)
212{
213 RA8_CHECK_NULL_PTR(digest, s_tag, "digest");
214 RA8_CHECK_NULL_PTR(out, s_tag, "out");
215
216 uint8_t combined[sizeof(uint32_t) + k_ra8_rot_digest_bytes] = {};
217 for (uint8_t i = 0U; i < (uint8_t)sizeof(uint32_t); ++i) {
218 combined[i] = (uint8_t)(img_version >> ((uint32_t)i * (uint32_t)k_rot_octet_bits));
219 }
220 for (uint8_t i = 0U; i < (uint8_t)k_ra8_rot_digest_bytes; ++i) {
221 combined[(size_t)sizeof(uint32_t) + (size_t)i] = digest[i];
222 }
223 return internal_compute_digest(combined, (uint32_t)sizeof(combined), out);
224}
225
254internal_verify_sig(const uint8_t* digest, const uint8_t* sig, uint32_t sig_len)
255{
256 RA8_CHECK_NULL_PTR(digest, s_tag, "digest");
257 RA8_CHECK_NULL_PTR(sig, s_tag, "sig");
258
259 const ra8_psa_key_attr_t attr = {
262 .usage = k_ra8_psa_usage_verify,
263 };
264 ra8_psa_key_t key = nullptr;
265 const ra8_err_t imp_err =
266 ra8_psa_key_import(&key, &attr, s_rot_root_pubkey, (size_t)sizeof(s_rot_root_pubkey));
267 RA8_RETURN_ON_ERROR(imp_err, s_tag, "rot: import root key failed");
268
269 const ra8_err_t ver_err = ra8_psa_verify_hash(key,
271 digest,
273 sig,
274 (size_t)sig_len);
275 (void)ra8_psa_key_destroy(key);
276 return ver_err;
277}
278
280ra8_rot_verify_image(const uint8_t* body, uint32_t body_len, const ra8_rot_trailer_t* trailer)
281{
282 RA8_CHECK_NULL_PTR(body, s_tag, "body");
283 RA8_CHECK_NULL_PTR(trailer, s_tag, "trailer");
284
285 /* Trailer header: a missing or malformed trailer is a default-deny. */
286 if ((trailer->magic != (uint32_t)k_ra8_rot_trailer_magic) ||
287 (trailer->version != (uint32_t)k_ra8_rot_version)) {
288 ra8_log_error(s_tag, "rot: trailer magic/version invalid");
290 }
291
292 /* Body-length sanity + cross-check against the trailer's recorded length. */
293 if ((body_len == 0U) || (body_len > (uint32_t)k_ra8_rot_body_max) ||
294 (trailer->body_len != body_len)) {
295 ra8_log_error(s_tag, "rot: body_len out of range / mismatch");
297 }
298
299 /* Signature-length sanity (bounds the bytes handed to the verifier). */
300 if ((trailer->sig_len == 0U) || (trailer->sig_len > (uint32_t)k_ra8_rot_sig_bytes)) {
301 ra8_log_error(s_tag, "rot: sig_len invalid");
303 }
304
305 /* Ensure the PSA facade is ready (the fake hash and the ECDSA verify both
306 * route through it). Already-initialized is fine. */
307 const ra8_err_t psa_err = ra8_psa_crypto_init();
308 // mcdc-deactivated: DO-178C 6.4.4.3 -- under RA8_OFF_TARGET (the only host-testable build) ra8_psa_crypto_init() returns exactly k_ra8_ok (first init) or k_ra8_err_exists (already initialized) and never a backend-fault code, so both conditions cannot be true together on the host; the fail-closed abort on a genuine PSA/RSIP init fault is exercisable only on target, where the crypto backend can fault, and is not host-instrumentable.
309 if ((psa_err != k_ra8_ok) && (psa_err != k_ra8_err_exists)) {
310 ra8_log_error(s_tag, "rot: psa init failed");
311 return psa_err;
312 }
313
314 /* Re-compute the body digest. The signature's authority is asserted over
315 * THIS freshly-computed digest, never over the trailer's stored digest. */
316 uint8_t digest[k_ra8_rot_digest_bytes] = {};
317 const ra8_err_t hash_err = internal_compute_digest(body, body_len, digest);
318 RA8_RETURN_ON_ERROR(hash_err, s_tag, "rot: hash compute failed");
319
320 /* Tamper pre-check: the re-computed digest must equal the trailer digest. */
321 if (!internal_ct_equal(digest, trailer->digest, (uint32_t)k_ra8_rot_digest_bytes)) {
322 ra8_log_error(s_tag, "rot: body digest mismatch (tampered)");
324 }
325
326 /* Bind the anti-rollback img_version into the signed material so a forged
327 * version riding on a validly-signed body fails verification (T5-05): the
328 * signature's authority is asserted over SHA-256(img_version_le || digest),
329 * not the bare body digest. */
330 uint8_t signed_material[k_ra8_rot_digest_bytes] = {};
331 const ra8_err_t bind_err = internal_bind_version(trailer->img_version, digest, signed_material);
332 RA8_RETURN_ON_ERROR(bind_err, s_tag, "rot: version-bind hash failed");
333
334 /* Authority: ECDSA-P256 verify over the version-bound digest. */
335 const ra8_err_t sig_err = internal_verify_sig(signed_material, trailer->sig, trailer->sig_len);
336 RA8_RETURN_ON_ERROR(sig_err, s_tag, "rot: signature verify failed");
337
338 return k_ra8_ok; /* image is authentic -- launch permitted */
339}
340
341const ra8_rot_trailer_t* ra8_rot_trailer_after(const void* image_base, uint32_t body_len)
342{
343 if (image_base == nullptr) {
344 return nullptr;
345 }
346 if ((body_len == 0U) || (body_len > (uint32_t)k_ra8_rot_body_max)) {
347 return nullptr;
348 }
349 const uint8_t* base = (const uint8_t*)image_base;
350 /* The trailer sits immediately after the body; image bodies are placed at
351 * 32-byte (page) multiples, so this address satisfies the struct alignment.
352 * The void* hop documents the intent and silences -Wcast-align. */
353 return (const ra8_rot_trailer_t*)(const void*)(base + body_len);
354}
355
356ra8_err_t ra8_rot_root_public_key(const uint8_t** out_key, uint32_t* out_len)
357{
358 RA8_CHECK_NULL_PTR(out_key, s_tag, "out_key");
359 RA8_CHECK_NULL_PTR(out_len, s_tag, "out_len");
360 *out_key = s_rot_root_pubkey;
361 *out_len = (uint32_t)sizeof(s_rot_root_pubkey);
362 return k_ra8_ok;
363}
364
365#endif /* RA8_ENABLE_ROOT_OF_TRUST */
static const char * s_tag
Logging / check tag.
Definition ra8_app.c:17
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Validation and Error-Checking Macros for ra8-firmware.
#define RA8_RETURN_ON_ERROR(err, tag, message)
Early return on error, propagating the code upward.
Definition ra8_check.h:184
#define RA8_CHECK_NULL_PTR(ptr, tag, message)
Reject nullptr pointer, returning k_ra8_err_null_ptr.
Definition ra8_check.h:243
@ k_ra8_err_checksum_mismatch
Stored / transmitted checksum does not match computed value.
Definition ra8_err.h:465
@ k_ra8_err_exists
Item already exists – cannot create again.
Definition ra8_err.h:216
@ k_ra8_err_validation_failed
Validation rule failed (caller-supplied invariant not satisfied).
Definition ra8_err.h:459
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ 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
#define ra8_log_error(tag, message)
RA8 log error.
Definition ra8_log.h:335
Application-level PSA Crypto facade over tf-psa-crypto.
@ k_ra8_psa_usage_verify
Allow ra8_psa_verify_hash.
ra8_err_t ra8_psa_hash_compute(ra8_psa_alg_t alg, const uint8_t *input, size_t input_len, uint8_t *out, size_t out_cap, size_t *out_len)
Compute a one-shot SHA-256 digest.
ra8_err_t ra8_psa_key_destroy(ra8_psa_key_t handle)
Destroy a previously-imported key.
ra8_err_t ra8_psa_crypto_init(void)
One-shot facade initialisation.
ra8_err_t ra8_psa_key_import(ra8_psa_key_t *out_handle, const ra8_psa_key_attr_t *attr, const uint8_t *data, size_t data_len)
Import a raw-byte key into the static pool.
struct ra8_psa_key_handle * ra8_psa_key_t
Opaque PSA key handle (typed pointer into the static pool).
ra8_err_t ra8_psa_verify_hash(ra8_psa_key_t handle, ra8_psa_alg_t alg, const uint8_t *hash, size_t hash_len, const uint8_t *sig, size_t sig_len)
Verify an ECDSA signature over a pre-computed hash.
@ k_ra8_psa_key_type_ecc_p256_pub
ECDSA P-256 public key (uncompressed).
@ k_ra8_psa_alg_ecdsa_sha_256
ECDSA over SHA-256 (FIPS 186-4).
@ k_ra8_psa_alg_sha_256
SHA-256 hash (FIPS 180-4).
Root-of-trust signed-image verifier (SHA-256 + ECDSA-P256, default-deny).
@ k_ra8_rot_trailer_magic
ASCII "ROT1" – signed-image marker.
Definition ra8_rot.h:117
@ k_ra8_rot_body_max
Max signable body length (1 MiB cap).
Definition ra8_rot.h:119
@ k_ra8_rot_version
Trailer format version.
Definition ra8_rot.h:118
@ k_ra8_rot_pubkey_bytes
Uncompressed P-256 public key (0x04||X||Y).
Definition ra8_rot.h:103
@ k_ra8_rot_sig_bytes
ECDSA-P256 raw r||s signature length.
Definition ra8_rot.h:102
@ k_ra8_rot_digest_bytes
SHA-256 digest length (FIPS 180-4).
Definition ra8_rot.h:101
ra8_err_t ra8_rot_verify_image(const uint8_t *body, uint32_t body_len, const ra8_rot_trailer_t *trailer)
Authenticate a signed image: SHA-256 + ECDSA-P256, default-deny.
const ra8_rot_trailer_t * ra8_rot_trailer_after(const void *image_base, uint32_t body_len)
Locate the trailer that immediately follows a signed image body.
ra8_err_t ra8_rot_root_public_key(const uint8_t **out_key, uint32_t *out_len)
Expose the provisioned root public key (host / test only).
Renesas Secure IP (RSIP-E50D) HAL – core lifecycle / TRNG / hash API.
ra8_err_t ra8_rsip_sha256(const uint8_t *msg, uint32_t msg_len, uint8_t *digest)
Compute SHA-256 of an in-memory buffer.
Definition ra8_rsip.c:397
Attributes describing a key being imported.
Authenticity trailer appended after a signed image body.
Definition ra8_rot.h:150
uint32_t sig_len
Active signature length, bytes.
Definition ra8_rot.h:155
uint32_t body_len
Body length the digest covers.
Definition ra8_rot.h:154
uint32_t img_version
Monotonic anti-rollback image ver.
Definition ra8_rot.h:153
uint8_t sig[k_ra8_rot_sig_bytes]
ECDSA-P256 raw r||s over the digest.
Definition ra8_rot.h:157
uint32_t version
k_ra8_rot_version (trailer format).
Definition ra8_rot.h:152
uint32_t magic
k_ra8_rot_trailer_magic.
Definition ra8_rot.h:151
uint8_t digest[k_ra8_rot_digest_bytes]
SHA-256 of the body (pre-check).
Definition ra8_rot.h:156