CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
AttestationKeyService.cpp
Go to the documentation of this file.
2#include "cdc_core/Raii.h"
3#include "cdc_log.h"
4#include <mbedtls/sha256.h>
5#include <nvs_flash.h>
6#include <nvs.h>
7#include <cstring>
8
9static const char* TAG = "AttestKey";
10static constexpr const char* NVS_NAMESPACE = "attest";
11static constexpr const char* NVS_KEY_PUBHASH = "pubhash";
12static constexpr uint32_t RETRY_INTERVAL_MS = 3000;
13
15static constexpr size_t SHA256_DIGEST_SIZE = 32;
17static constexpr size_t P256_PUBKEY_RAW_SIZE = 64;
18
19namespace cdc::core {
20
26 if (state_ != ServiceState::UNINITIALIZED) {
27 return state_ == ServiceState::INITIALIZED || state_ == ServiceState::STARTED;
28 }
30 return true;
31}
32
41 if (state_ == ServiceState::UNINITIALIZED) {
42 if (!init()) return false;
43 }
44 state_ = ServiceState::STARTED;
45 if (ensureKey()) {
46 ready_ = true;
47 LOG_I(TAG, "Attestation key ready");
48 }
49 return true;
50}
51
58
63void AttestationKeyService::onTick(uint32_t nowMs) {
64 if (state_ != ServiceState::STARTED || ready_) return;
65 if (nowMs - lastAttemptMs_ < RETRY_INTERVAL_MS) return;
66 lastAttemptMs_ = nowMs;
67 if (ensureKey()) {
68 ready_ = true;
69 LOG_I(TAG, "Attestation key ready");
70 }
71}
72
79bool AttestationKeyService::loadStoredHash(uint8_t* out, size_t outLen) {
80 if (!out || outLen == 0) return false;
81 NvsScope nvs(NVS_NAMESPACE, NVS_READONLY);
82 if (!nvs) return false;
83 size_t len = outLen;
84 esp_err_t err = nvs_get_blob(nvs, NVS_KEY_PUBHASH, out, &len);
85 return err == ESP_OK && len == outLen;
86}
87
94bool AttestationKeyService::saveStoredHash(const uint8_t* data, size_t len) {
95 if (!data || len == 0) return false;
96 NvsScope nvs(NVS_NAMESPACE, NVS_READWRITE);
97 if (!nvs) return false;
98 esp_err_t err = nvs_set_blob(nvs, NVS_KEY_PUBHASH, data, len);
99 if (err == ESP_OK) err = nvs.commit();
100 return err == ESP_OK;
101}
102
107bool AttestationKeyService::ensureKey() {
108 if (!secureElement_) {
109 LOG_W(TAG, "Secure element not set");
110 return false;
111 }
112 if (!secureElement_->isSessionActive()) {
113 if (!secureElement_->sessionStart()) {
114 LOG_W(TAG, "Secure element session not active");
115 return false;
116 }
117 }
118
119 uint8_t pubkey[P256_PUBKEY_RAW_SIZE] = {};
121 hal::SeResult res = secureElement_->eccGetPublicKey(ATTESTATION_ECC_SLOT, pubkey, &curve);
122
123 if (res == hal::SeResult::SLOT_EMPTY) {
124 LOG_I(TAG, "Attestation slot empty, generating key");
125 if (secureElement_->eccGenerate(ATTESTATION_ECC_SLOT, hal::EccCurve::P256) !=
127 LOG_E(TAG, "Failed to generate attestation key");
128 return false;
129 }
130 res = secureElement_->eccGetPublicKey(ATTESTATION_ECC_SLOT, pubkey, &curve);
131 }
132
133 if (res != hal::SeResult::OK) {
134 LOG_W(TAG, "Attestation key read failed: %d", static_cast<int>(res));
135 return false;
136 }
137
138 if (curve != hal::EccCurve::P256) {
139 LOG_W(TAG, "Attestation key wrong curve, regenerating");
140 secureElement_->eccDelete(ATTESTATION_ECC_SLOT);
141 if (secureElement_->eccGenerate(ATTESTATION_ECC_SLOT, hal::EccCurve::P256) !=
143 LOG_E(TAG, "Failed to regenerate attestation key");
144 return false;
145 }
146 res = secureElement_->eccGetPublicKey(ATTESTATION_ECC_SLOT, pubkey, &curve);
147 if (res != hal::SeResult::OK) return false;
148 }
149
150 uint8_t hash[SHA256_DIGEST_SIZE] = {};
151 mbedtls_sha256(pubkey, sizeof(pubkey), hash, 0);
152
153 uint8_t stored[SHA256_DIGEST_SIZE] = {};
154 if (loadStoredHash(stored, sizeof(stored))) {
155 if (memcmp(stored, hash, sizeof(hash)) == 0) {
156 return true;
157 }
158 LOG_W(TAG, "Attestation key mismatch, regenerating");
159 secureElement_->eccDelete(ATTESTATION_ECC_SLOT);
160 if (secureElement_->eccGenerate(ATTESTATION_ECC_SLOT, hal::EccCurve::P256) !=
162 LOG_E(TAG, "Failed to regenerate attestation key");
163 return false;
164 }
165 res = secureElement_->eccGetPublicKey(ATTESTATION_ECC_SLOT, pubkey, &curve);
166 if (res != hal::SeResult::OK) return false;
167 mbedtls_sha256(pubkey, sizeof(pubkey), hash, 0);
168 }
169
170 if (!saveStoredHash(hash, sizeof(hash))) {
171 LOG_W(TAG, "Failed to store attestation key hash");
172 }
173
174 return true;
175}
176
177} // namespace cdc::core
static constexpr uint32_t RETRY_INTERVAL_MS
static constexpr size_t P256_PUBKEY_RAW_SIZE
Uncompressed P-256 public key, raw X||Y coordinates (no SEC1 0x04 prefix).
static const char * TAG
static constexpr const char * NVS_KEY_PUBHASH
Shared RAII wrappers for firmware resources.
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_W(tag, fmt,...)
Definition cdc_log.h:146
#define LOG_I(tag, fmt,...)
Definition cdc_log.h:147
#define LOG_E(tag, fmt,...)
Definition cdc_log.h:145
bool start() override
Starts service, ensures initialized state, and attempts to provision the attestation key synchronousl...
void stop() override
Stops attestation-key background processing.
void onTick(uint32_t nowMs)
Periodically attempts to ensure attestation key exists and is valid.
bool init() override
Initializes service state.
static constexpr uint8_t ATTESTATION_ECC_SLOT
RAII wrapper for an NVS handle.
Definition Raii.h:106
#define SHA256_DIGEST_SIZE
SHA-256 digest output size in bytes (FIPS 180-4).
Definition constants.h:37
uint8_t curve
#define NVS_NAMESPACE
static constexpr const char * NVS_NAMESPACE