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 // === Lockout Timer (RAM only, not persistent) ===
92 static constexpr uint32_t LOCKOUT_DURATION_MS = 60000; // 60 seconds
93 void startLockout();
94 uint32_t getLockoutRemainingMs() const;
95 bool isLockoutActive() const;
96
104
105 // === OpenPGP PW1 (User PIN) ===
106 bool verifyPW1(const char* pin);
107 bool changePW1(const char* currentPin, const char* newPin);
108 bool setPW1(const char* newPin);
109 bool getPW1Hash(uint8_t* hashOut) const;
110 bool getPW1Salt(uint8_t* saltOut) const;
111 uint8_t getPW1Retries() const { return pw1Retries_; }
112 bool isPW1Blocked() const { return pw1Retries_ == 0; }
113 void resetPW1Retries();
114
115 // === OpenPGP PW3 (Admin PIN) ===
116 bool verifyPW3(const char* pin);
117 bool changePW3(const char* currentPin, const char* newPin);
118 bool setPW3(const char* newPin);
119 bool getPW3Hash(uint8_t* hashOut) const;
120 bool getPW3Salt(uint8_t* saltOut) const;
121 uint8_t getPW3Retries() const { return pw3Retries_; }
122 bool isPW3Blocked() const { return pw3Retries_ == 0; }
123 void resetPW3Retries();
124
125 // === Duress / Self-Destruct PIN (optional, default NOT set) ===
137 bool setDuressPin(const char* pin);
138
143 bool clearDuressPin();
144
146 bool hasDuressPin() const { return duressSet_; }
147
153 bool isDuressPin(const char* pin) const;
154
155 // === KDF Parameters (for OpenPGP KDF-DO) ===
156 uint8_t getKdfAlgorithm() const { return KDF_ITERSALTED_S2K; }
157 uint8_t getHashAlgorithm() const { return HASH_SHA256; }
158 uint32_t getIterationCount() const { return iterations_; }
159
160 // === Status ===
161 bool isPinSet() const { return badgePinIsSet_; }
162 bool isStorageAvailable() const;
163
164private:
165 PinManager() = default;
166
167 static constexpr uint8_t MAX_RETRIES = 3;
168 static constexpr uint8_t MAGIC = 0xE0;
169 static constexpr uint8_t SIGNATURE_SIZE = 64; // P-256 ECDSA raw R||S
170 static constexpr uint8_t PAYLOAD_SIZE = 147;
171 // Stored buffer: [PAYLOAD_SIZE bytes payload][SIGNATURE_SIZE bytes ECDSA sig]
172 static constexpr uint16_t STORAGE_SIZE = PAYLOAD_SIZE + SIGNATURE_SIZE;
173
174 // Badge/FIDO2 (retry counter is RAM-only)
175 uint8_t badgeHash_[BADGE_HASH_SIZE] = {};
176 uint8_t badgeRetries_ = MAX_RETRIES;
177 bool badgeLocked_ = false;
178
179 // OpenPGP KDF data
180 uint32_t iterations_ = DEFAULT_ITERATIONS;
181 uint8_t pw1Salt_[SALT_SIZE] = {};
182 uint8_t pw3Salt_[SALT_SIZE] = {};
183 uint8_t pw1Hash_[KDF_HASH_SIZE] = {};
184 uint8_t pw3Hash_[KDF_HASH_SIZE] = {};
185 uint8_t pw1Retries_ = MAX_RETRIES;
186 uint8_t pw3Retries_ = MAX_RETRIES;
187
188 // Duress / self-destruct PIN (optional, default not set). KDF-hashed with
189 // the same machinery as PW1/PW3 and covered by the same attestation.
190 bool duressSet_ = false;
191 uint8_t duressSalt_[SALT_SIZE] = {};
192 uint8_t duressHash_[KDF_HASH_SIZE] = {};
193
194 // Mirrors of what is currently persisted in R-Memory. Updated by
195 // saveToStorage() after a successful write. Used to skip redundant
196 // writes when the in-RAM state already matches the on-chip value.
197 bool persistedBadgeLocked_ = false;
198 uint8_t persistedPw1Retries_ = MAX_RETRIES;
199 uint8_t persistedPw3Retries_ = MAX_RETRIES;
200
201 bool pinLoaded_ = false;
202 bool badgePinIsSet_ = false;
203
204 // Badge recovery timer (RAM only). Runs from boot and after every
205 // transition of badgeRetries_ to zero. On expiry: badgeRetries_ is
206 // restored to MAX_RETRIES and badgeLocked_ is cleared (and persisted
207 // if it was set).
208 uint32_t lockoutStartMs_ = 0;
209 bool lockoutActive_ = false;
210
211 bool loadFromStorage();
212 bool saveToStorage();
213
214 // Badge hash: LEFT(SHA256(PIN), 16)
215 bool computeBadgeHash(const char* pin, uint8_t* hashOut);
216
217 // OpenPGP KDF hash: SHA256 iterated with salt
218 bool computeKdfHash(const char* pin, const uint8_t* salt, uint8_t* hashOut) const;
219
220 bool compareHash(const uint8_t* h1, const uint8_t* h2, size_t len) const;
221 void generateSalt(uint8_t* salt);
222 void loadDefaults();
223
227 enum class PinSlot : uint8_t {
228 BADGE,
229 PW1,
230 PW3
231 };
232
239 bool verifyPin(PinSlot slot, const char* pin);
240};
241
242} // 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
void resetPW1Retries()
Resets PW1 retry counter to maximum.
static constexpr uint32_t LOCKOUT_DURATION_MS
Definition PinManager.h:92
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:161
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:156
uint8_t getPW1Retries() const
Definition PinManager.h:111
static constexpr uint8_t BADGE_HASH_SIZE
Definition PinManager.h:64
uint8_t getHashAlgorithm() const
Definition PinManager.h:157
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)
Sets badge PIN directly with format validation.
bool clearDuressPin()
Clears the duress PIN, disarming the self-destruct trigger.
bool isPW1Blocked() const
Definition PinManager.h:112
void startLockout()
Starts the badge recovery timer.
bool hasDuressPin() const
Returns whether a duress PIN is currently armed.
Definition PinManager.h:146
bool isLockoutActive() const
Returns whether lockout is currently active without mutating state.
uint8_t getPW3Retries() const
Definition PinManager.h:121
static PinManager & instance()
Returns singleton PIN manager instance.
static constexpr uint8_t ATTESTATION_ECC_SLOT
Definition PinManager.h:61
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:122
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:158
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 getPW3Salt(uint8_t *saltOut) const
Copies stored PW3 salt into caller buffer.