CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
PinManager.h
Go to the documentation of this file.
1#pragma once
2
3#include <cstdint>
4#include <cstddef>
5
6namespace cdc::core {
7
46class PinManager {
47public:
48 // PIN constraints
49 static constexpr uint8_t BADGE_PIN_MIN = 4;
50 static constexpr uint8_t BADGE_PIN_MAX = 8;
51 static constexpr uint8_t PW1_MIN = 6;
52 static constexpr uint8_t PW3_MIN = 8;
53 static constexpr uint8_t PIN_MAX = 16;
54
55 // Storage
56 static constexpr uint16_t RMEM_SLOT_PIN = 0;
57
58 // Chip-bound attestation key in ECC slot 0 (managed by AttestationKeyService).
59 // Used by saveToStorage / loadFromStorage to sign and verify the PIN payload
60 // so a tampered or regenerated slot triggers a silent reset to defaults.
61 static constexpr uint8_t ATTESTATION_ECC_SLOT = 0;
62
63 // Hash sizes
64 static constexpr uint8_t BADGE_HASH_SIZE = 16; // LEFT(SHA256, 16)
65 static constexpr uint8_t KDF_HASH_SIZE = 32; // Full SHA256
66 static constexpr uint8_t SALT_SIZE = 8;
67
68 // KDF parameters (OpenPGP spec)
69 static constexpr uint8_t KDF_ITERSALTED_S2K = 0x03;
70 static constexpr uint8_t HASH_SHA256 = 0x08;
71 static constexpr uint32_t DEFAULT_ITERATIONS = 100000;
72
73 // Defaults
74 static constexpr const char* DEFAULT_BADGE_PIN = "123456";
75 static constexpr const char* DEFAULT_PW1 = "123456";
76 static constexpr const char* DEFAULT_PW3 = "12345678";
77
78 static PinManager& instance();
79 bool init();
80
81 // === Badge/FIDO2 PIN ===
82 bool verifyBadgePin(const char* pin);
83 bool changeBadgePin(const char* currentPin, const char* newPin);
84 bool setBadgePin(const char* newPin);
85 bool getBadgePinHash(uint8_t* hashOut) const;
86 bool verifyBadgePinHash(const uint8_t* hashIn) const;
87 uint8_t getBadgeRetries() const { return badgeRetries_; }
88 bool isBadgeBlocked() const; // Checks retries=0 OR time lockout active
89 void resetBadgeRetries();
90
91 // === CTAP2 setMinPINLength policy floor (RAM; re-applied from NVS at boot) ===
96 void setMinPinLengthFloor(uint8_t minLen);
97 uint8_t minPinLengthFloor() const { return minPinFloor_; }
98
99 // === Lockout Timer (RAM only, not persistent) ===
100 static constexpr uint32_t LOCKOUT_DURATION_MS = 60000; // 60 seconds
101 void startLockout();
102 uint32_t getLockoutRemainingMs() const;
103 bool isLockoutActive() const;
104
112
113 // === OpenPGP PW1 (User PIN) ===
114 bool verifyPW1(const char* pin);
115 bool changePW1(const char* currentPin, const char* newPin);
116 bool setPW1(const char* newPin);
117 bool getPW1Hash(uint8_t* hashOut) const;
118 bool getPW1Salt(uint8_t* saltOut) const;
119 uint8_t getPW1Retries() const { return pw1Retries_; }
120 bool isPW1Blocked() const { return pw1Retries_ == 0; }
121 void resetPW1Retries();
122
123 // OpenPGP KDF-DO path: PW1 reference is a host-supplied pre-hash (binary,
124 // may contain NUL bytes, up to 64 bytes). Same stored format and retry
125 // semantics as the cleartext path; only the input differs.
126 bool verifyPW1Raw(const uint8_t* data, size_t len);
127 bool setPW1Raw(const uint8_t* data, size_t len);
128
129 // === OpenPGP PW3 (Admin PIN) ===
130 bool verifyPW3(const char* pin);
131 bool changePW3(const char* currentPin, const char* newPin);
132 bool setPW3(const char* newPin);
133 bool getPW3Hash(uint8_t* hashOut) const;
134 bool getPW3Salt(uint8_t* saltOut) const;
135 uint8_t getPW3Retries() const { return pw3Retries_; }
136 bool isPW3Blocked() const { return pw3Retries_ == 0; }
137 void resetPW3Retries();
138
140 bool verifyPW3Raw(const uint8_t* data, size_t len);
141 bool setPW3Raw(const uint8_t* data, size_t len);
142
143 // === Duress / Self-Destruct PIN (optional, default NOT set) ===
155 bool setDuressPin(const char* pin);
156
161 bool clearDuressPin();
162
164 bool hasDuressPin() const { return duressSet_; }
165
171 bool isDuressPin(const char* pin) const;
172
173 // === KDF Parameters (for OpenPGP KDF-DO) ===
174 uint8_t getKdfAlgorithm() const { return KDF_ITERSALTED_S2K; }
175 uint8_t getHashAlgorithm() const { return HASH_SHA256; }
176 uint32_t getIterationCount() const { return iterations_; }
177
178 // === Status ===
179 bool isPinSet() const { return badgePinIsSet_; }
180 bool isStorageAvailable() const;
181
182private:
183 PinManager() = default;
184
185 static constexpr uint8_t MAX_RETRIES = 3;
186 static constexpr uint8_t MAGIC = 0xE0;
187 static constexpr uint8_t SIGNATURE_SIZE = 64; // P-256 ECDSA raw R||S
188 static constexpr uint8_t PAYLOAD_SIZE = 147;
189 // Stored buffer: [PAYLOAD_SIZE bytes payload][SIGNATURE_SIZE bytes ECDSA sig]
190 static constexpr uint16_t STORAGE_SIZE = PAYLOAD_SIZE + SIGNATURE_SIZE;
191
192 // Badge/FIDO2 (retry counter is RAM-only)
193 uint8_t badgeHash_[BADGE_HASH_SIZE] = {};
194 uint8_t badgeRetries_ = MAX_RETRIES;
195 bool badgeLocked_ = false;
196 uint8_t minPinFloor_ = BADGE_PIN_MIN;
197
198 // OpenPGP KDF data
199 uint32_t iterations_ = DEFAULT_ITERATIONS;
200 uint8_t pw1Salt_[SALT_SIZE] = {};
201 uint8_t pw3Salt_[SALT_SIZE] = {};
202 uint8_t pw1Hash_[KDF_HASH_SIZE] = {};
203 uint8_t pw3Hash_[KDF_HASH_SIZE] = {};
204 uint8_t pw1Retries_ = MAX_RETRIES;
205 uint8_t pw3Retries_ = MAX_RETRIES;
206
207 // Duress / self-destruct PIN (optional, default not set). KDF-hashed with
208 // the same machinery as PW1/PW3 and covered by the same attestation.
209 bool duressSet_ = false;
210 uint8_t duressSalt_[SALT_SIZE] = {};
211 uint8_t duressHash_[KDF_HASH_SIZE] = {};
212
213 // Mirrors of what is currently persisted in R-Memory. Updated by
214 // saveToStorage() after a successful write. Used to skip redundant
215 // writes when the in-RAM state already matches the on-chip value.
216 bool persistedBadgeLocked_ = false;
217 uint8_t persistedPw1Retries_ = MAX_RETRIES;
218 uint8_t persistedPw3Retries_ = MAX_RETRIES;
219
220 bool pinLoaded_ = false;
221 bool badgePinIsSet_ = false;
222
223 // Badge recovery timer (RAM only). Runs from boot and after every
224 // transition of badgeRetries_ to zero. On expiry: badgeRetries_ is
225 // restored to MAX_RETRIES and badgeLocked_ is cleared (and persisted
226 // if it was set).
227 uint32_t lockoutStartMs_ = 0;
228 bool lockoutActive_ = false;
229
230 bool loadFromStorage();
231 bool saveToStorage();
232
233 // Badge hash: LEFT(SHA256(PIN), 16)
234 bool computeBadgeHash(const char* pin, uint8_t* hashOut);
235
236 // OpenPGP KDF hash: SHA256 iterated with salt
237 bool computeKdfHash(const char* pin, const uint8_t* salt, uint8_t* hashOut) const;
238 // Length-aware variant accepting binary input (NUL-safe, up to 64 bytes),
239 // used by the KDF-DO pre-hashed PIN path.
240 bool computeKdfHash(const uint8_t* data, size_t len, const uint8_t* salt,
241 uint8_t* hashOut) const;
242
243 bool compareHash(const uint8_t* h1, const uint8_t* h2, size_t len) const;
244 void generateSalt(uint8_t* salt);
245 void loadDefaults();
246
250 enum class PinSlot : uint8_t {
251 BADGE,
252 PW1,
253 PW3
254 };
255
262 bool verifyPin(PinSlot slot, const char* pin);
263
271 bool verifyPinRaw(PinSlot slot, const uint8_t* data, size_t len);
272};
273
274} // namespace cdc::core
static constexpr uint8_t PIN_MAX
Definition PinManager.h:53
bool verifyPW1(const char *pin)
OpenPGP PW1 (user PIN) workflow.
bool changeBadgePin(const char *currentPin, const char *newPin)
Changes badge PIN after validating current PIN.
static constexpr uint8_t KDF_HASH_SIZE
Definition PinManager.h:65
bool getPW1Hash(uint8_t *hashOut) const
Copies stored PW1 hash into caller buffer.
static constexpr uint32_t DEFAULT_ITERATIONS
Definition PinManager.h:71
bool setPW3Raw(const uint8_t *data, size_t len)
void resetPW1Retries()
Resets PW1 retry counter to maximum.
static constexpr uint32_t LOCKOUT_DURATION_MS
Definition PinManager.h:100
bool changePW3(const char *currentPin, const char *newPin)
Changes PW3 after validating the current value.
void resetBadgeRetries()
Resets badge retry counter to maximum.
static constexpr uint16_t RMEM_SLOT_PIN
Definition PinManager.h:56
static constexpr uint8_t HASH_SHA256
Definition PinManager.h:70
bool isDuressPin(const char *pin) const
Constant-time check whether a candidate matches the duress PIN.
bool isPinSet() const
Definition PinManager.h:179
static constexpr uint8_t BADGE_PIN_MAX
Definition PinManager.h:50
bool getPW1Salt(uint8_t *saltOut) const
Copies stored PW1 salt into caller buffer.
static constexpr const char * DEFAULT_BADGE_PIN
Definition PinManager.h:74
uint8_t getBadgeRetries() const
Definition PinManager.h:87
bool isStorageAvailable() const
Returns whether secure storage access is currently available.
bool setPW3(const char *newPin)
Sets PW3 directly and refreshes salt/hash material.
bool verifyBadgePin(const char *pin)
Verifies badge PIN, updates retries, and handles lockout transitions.
uint8_t getKdfAlgorithm() const
Definition PinManager.h:174
uint8_t getPW1Retries() const
Definition PinManager.h:119
static constexpr uint8_t BADGE_HASH_SIZE
Definition PinManager.h:64
uint8_t getHashAlgorithm() const
Definition PinManager.h:175
static constexpr const char * DEFAULT_PW1
Definition PinManager.h:75
bool changePW1(const char *currentPin, const char *newPin)
Changes PW1 after validating the current value.
static constexpr uint8_t PW3_MIN
Definition PinManager.h:52
static constexpr uint8_t PW1_MIN
Definition PinManager.h:51
void resetPW3Retries()
Resets PW3 retry counter to maximum.
bool isBadgeBlocked() const
Lockout timer handling.
static constexpr uint8_t BADGE_PIN_MIN
Definition PinManager.h:49
static constexpr uint8_t KDF_ITERSALTED_S2K
Definition PinManager.h:69
static constexpr const char * DEFAULT_PW3
Definition PinManager.h:76
bool setBadgePin(const char *newPin)
bool clearDuressPin()
Clears the duress PIN, disarming the self-destruct trigger.
void setMinPinLengthFloor(uint8_t minLen)
Sets the minimum badge-PIN length floor enforced on changes.
bool isPW1Blocked() const
Definition PinManager.h:120
bool verifyPW3Raw(const uint8_t *data, size_t len)
OpenPGP KDF-DO path: PW3 reference is a host-supplied pre-hash.
void startLockout()
Starts the badge recovery timer.
bool hasDuressPin() const
Returns whether a duress PIN is currently armed.
Definition PinManager.h:164
bool setPW1Raw(const uint8_t *data, size_t len)
bool isLockoutActive() const
Returns whether lockout is currently active without mutating state.
uint8_t getPW3Retries() const
Definition PinManager.h:135
static PinManager & instance()
Returns singleton PIN manager instance.
static constexpr uint8_t ATTESTATION_ECC_SLOT
Definition PinManager.h:61
uint8_t minPinLengthFloor() const
Definition PinManager.h:97
bool getPW3Hash(uint8_t *hashOut) const
Copies stored PW3 hash into caller buffer.
uint32_t getLockoutRemainingMs() const
Returns remaining badge lockout duration.
bool isPW3Blocked() const
Definition PinManager.h:136
bool setPW1(const char *newPin)
Sets PW1 directly and refreshes salt/hash material.
bool setDuressPin(const char *pin)
Sets the duress PIN, arming the self-destruct trigger.
bool verifyBadgePinHash(const uint8_t *hashIn) const
Verifies provided hash against stored badge hash.
bool getBadgePinHash(uint8_t *hashOut) const
Copies stored badge PIN hash into caller buffer.
static constexpr uint8_t SALT_SIZE
Definition PinManager.h:66
uint32_t getIterationCount() const
Definition PinManager.h:176
bool init()
Initializes PIN state from secure storage or defaults.
bool verifyPW3(const char *pin)
OpenPGP PW3 (admin PIN) workflow.
void checkAndResetExpiredLockout()
Clears expired lockout state and resets retry counter.
bool verifyPW1Raw(const uint8_t *data, size_t len)
bool getPW3Salt(uint8_t *saltOut) const
Copies stored PW3 salt into caller buffer.