-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBuildSeed.h
More file actions
94 lines (82 loc) · 2.96 KB
/
BuildSeed.h
File metadata and controls
94 lines (82 loc) · 2.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
/**
* @file BuildSeed.h
* @brief Compile-time seed for per-board password derivation.
* AUTO-GENERATED by generate_build_seed.py — DO NOT EDIT.
*
* The actual password is derived at runtime:
* password = KDF_light(seed, board_id)
* Even with access to the .uf2 and seed, the password cannot be
* recovered without the physical board_id of the chip.
*/
#pragma once
#include <Arduino.h>
#include <pico/unique_id.h>
/** @brief Seed length in bytes. */
#define BUILD_SEED_LEN 16
/** @brief Derived password length (alphanumeric characters). */
#define BUILD_PWD_DERIVED_LEN 16
/**
* @brief XOR key for seed de-obfuscation in the binary.
* @note Short names hinder identification via `strings`.
*/
static const uint8_t _bsk[BUILD_SEED_LEN] = {
0x20, 0x0F, 0x31, 0x51, 0xBA, 0x03, 0x3C, 0x3F, 0xA7, 0xF4, 0xA3, 0x1E, 0xFA, 0x72, 0x58, 0x63
};
/**
* @brief Obfuscated seed: _bsd[i] = seed[i] ^ _bsk[i].
*/
static const uint8_t _bsd[BUILD_SEED_LEN] = {
0x8C, 0x12, 0x7E, 0x18, 0xA3, 0x65, 0x74, 0x51, 0xE6, 0xCB, 0x19, 0x2D, 0x5F, 0xA1, 0xB9, 0xE2
};
/**
* @brief Derive build password from compiled seed + board_id.
*
* @param out Output buffer (minimum BUILD_PWD_DERIVED_LEN + 1 bytes).
*
* @details Irreversible 64-round mixing with multiply-shift-XOR
* operations. The board_id (8 bytes, unique per chip)
* makes derivation impossible without physical access.
*
* @note Caller MUST scrub the buffer after use:
* secure_memzero(out, BUILD_PWD_DERIVED_LEN + 1);
*/
static inline void deriveBuildPassword(char* out) {
/* 1. De-obfuscate the seed */
uint8_t seed[BUILD_SEED_LEN];
for (uint8_t i = 0; i < BUILD_SEED_LEN; i++) {
seed[i] = _bsd[i] ^ _bsk[i];
}
/* 2. Read the chip's unique board ID */
pico_unique_board_id_t bid;
pico_get_unique_board_id(&bid);
/* 3. Initialize state from seed */
uint32_t state[4];
memcpy(state, seed, 16);
/* 4. Irreversible mixing: seed + board_id (64 rounds) */
for (uint8_t round = 0; round < 64; round++) {
for (uint8_t i = 0; i < 8; i++) {
uint8_t si = (i + round) % 4;
state[si] ^= ((uint32_t)bid.id[i]) << ((round % 4) * 8);
state[si] *= 0x5BD1E995;
state[si] ^= state[si] >> 15;
}
/* Avalanche across all state words */
state[0] += state[3] ^ (round * 0x9E3779B9);
state[1] ^= state[0] >> 7;
state[2] += state[1] << 13;
state[3] ^= state[2] >> 11;
}
/* 5. Encode as alphanumeric (base62) */
static const char b62[] =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
uint8_t* raw = (uint8_t*)state;
for (uint8_t i = 0; i < BUILD_PWD_DERIVED_LEN; i++) {
out[i] = b62[raw[i] % 62];
}
out[BUILD_PWD_DERIVED_LEN] = '\0';
/* 6. Scrub seed and state from stack (volatile writes, not optimizable) */
secure_memzero(seed, BUILD_SEED_LEN);
secure_memzero(state, 16);
}