Zion Boggan
repos/Oversight/docs/HARDWARE_KEYS.md
zionboggan.com ↗
236 lines · markdown
History for this file →
1
# Hardware Security Keys for Oversight
2
 
3
Vendor-neutral guide for storing Oversight recipient private keys on a hardware
4
device (YubiKey, OnlyKey, Nitrokey) rather than a disk file.
5
 
6
## Why
7
 
8
When a recipient's `.key` file lives on disk, full compromise of that
9
recipient's laptop gives an attacker the private key forever. That attacker
10
can decrypt every sealed file addressed to that recipient, past and future,
11
with no way to tell the issuer it happened.
12
 
13
A hardware-backed key eliminates this. The private key is generated inside
14
the device's secure element and never leaves it. All ECDH (X25519) and
15
signing (Ed25519) operations happen on-device. The host OS gets ECDH
16
outputs, never the raw key. To decrypt, an adversary needs physical
17
possession of the device - and typically a touch, PIN, or biometric.
18
 
19
This doesn't give you enclave-grade guarantees (a compromised client
20
running while the YubiKey is plugged in can still open files via the device).
21
What it does give you:
22
 
23
- **Vendor-neutral** - any FIDO2 / PIV device works.
24
- **Theft is discrete** - physical device loss is noticeable; disk theft may not be.
25
- **Revocation is simple** - deauthorize the device's pubkey in the registry.
26
- **Works offline** - no cloud service.
27
- **No recurring cost** - $50-$80 once.
28
 
29
## Supported devices
30
 
31
Any device exposing **PIV** (Personal Identity Verification, PKCS#11-compatible)
32
slots works. Tested:
33
 
34
| Device | Cost (USD) | PIV slots | Notes |
35
|---|---|---|---|
36
| YubiKey 5C NFC | ~$75 | yes | Most tested; widely available |
37
| YubiKey 5 NFC | ~$55 | yes | USB-A version |
38
| YubiKey Security Key NFC | ~$29 | FIDO2 only | Cheapest but limited |
39
| Nitrokey 3 NFC | ~$80 | yes | Fully open-source firmware |
40
| OnlyKey | ~$50 | yes | Open hardware + firmware |
41
 
42
Recommendation: **YubiKey 5C NFC** for most users (best tooling), **Nitrokey
43
3** if firmware openness matters more than ecosystem support.
44
 
45
## First-time setup
46
 
47
### 1. Install the tooling
48
 
49
```bash
50
# Debian / Ubuntu
51
sudo apt install yubikey-manager pcscd opensc
52
sudo systemctl enable --now pcscd
53
 
54
# macOS
55
brew install yubikey-manager opensc
56
 
57
# Arch
58
sudo pacman -S yubikey-manager opensc ccid
59
```
60
 
61
### 2. Verify the device is seen
62
 
63
```bash
64
ykman info
65
# Should print serial, firmware version, and enabled applications.
66
 
67
pkcs11-tool --list-slots --module /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so
68
# Should list the YubiKey as slot 0.
69
```
70
 
71
### 3. Set a PIN and management key
72
 
73
**Do not skip this.** The factory defaults (PIN 123456, PUK 12345678) are
74
publicly known. Change both now.
75
 
76
```bash
77
# PIV PIN (6-8 digits)
78
ykman piv access change-pin
79
 
80
# PIV PUK (used to unblock if you lock yourself out)
81
ykman piv access change-puk
82
 
83
# Management key (used for admin ops; 24-byte hex)
84
ykman piv access change-management-key --generate --protect
85
# --protect stashes the new key in PIV slot so you don't need to manage it
86
```
87
 
88
### 4. Generate an Oversight recipient key on-device
89
 
90
PIV has four main slots. Use **slot 9d (Key Management)** for Oversight - it's
91
meant for decryption operations and doesn't require PIN on every use (only
92
first use per session, via cached auth).
93
 
94
```bash
95
# Generate an ECC P-256 key in slot 9d
96
ykman piv keys generate 9d --algorithm ECCP256 -
97
# Note: P-256, not Curve25519. See "Curve choice" below.
98
 
99
# Self-sign a cert so PIV treats the slot as initialized
100
ykman piv certificates generate 9d \
101
    --subject "CN=oversight-recipient" \
102
    --valid-days 3650 -
103
```
104
 
105
### 5. Export the public key in Oversight format
106
 
107
Oversight identities are JSON. We need to convert the PIV slot's public key
108
to the format Oversight uses.
109
 
110
```bash
111
# Export the cert, extract the pubkey
112
ykman piv certificates export 9d - | \
113
    openssl x509 -pubkey -noout -in - | \
114
    openssl ec -pubin -text -noout
115
```
116
 
117
Write the resulting pubkey hex into an Oversight identity file with a
118
`hardware: true` marker:
119
 
120
```json
121
{
122
  "hardware": true,
123
  "provider": "piv",
124
  "piv_slot": "9d",
125
  "x25519_pub_equivalent": "<hex>",
126
  "ed25519_pub": null,
127
  "device_serial": "<yubikey-serial>"
128
}
129
```
130
 
131
The `x25519_pub_equivalent` is the P-256 pubkey. Oversight's hardware mode
132
uses **P-256 ECDH** instead of X25519 for this recipient, because P-256 is
133
what PIV supports natively (Curve25519 PIV support exists but is limited -
134
see below).
135
 
136
## Curve choice: why P-256 for hardware-backed recipients
137
 
138
The default Oversight suite uses X25519 for key agreement. PIV-compatible
139
hardware devices historically only supported P-256 and P-384 for PIV slots.
140
YubiKey 5.7+ firmware does support Curve25519 via a dedicated OpenPGP
141
applet, but PIV itself does not.
142
 
143
To stay compatible with the broadest set of devices (Nitrokey, OnlyKey,
144
older YubiKeys), Oversight uses **P-256 ECDH** for hardware-backed
145
recipients. The suite identifier in the manifest becomes `OSGT-HW-P256-v1`
146
instead of `OSGT-CLASSIC-v1`. The crypto is just as strong - P-256 ECDH
147
is NIST-standardized, FIPS 140-3 compliant, and battle-tested.
148
 
149
Open clients that want to decrypt for hardware-backed recipients must
150
support both suites. The default file-backed provider stays on X25519.
151
 
152
## Opening a sealed file with a hardware-backed key (CLI)
153
 
154
```bash
155
# Insert YubiKey. You may be prompted for PIN.
156
oversight open --input secret.sealed --output secret.txt \
157
    --recipient-hw piv:9d
158
 
159
# First op prompts for PIN; subsequent ops within the session don't.
160
```
161
 
162
Under the hood, this calls PKCS#11 `C_DeriveKey` to run ECDH against the
163
on-device private key, then runs the standard Oversight HKDF + AEAD decrypt
164
on the host. The raw private key never leaves the device.
165
 
166
## Revocation
167
 
168
If a device is lost, stolen, or retired:
169
 
170
1. POST to the registry:
171
   ```
172
   POST /recipients/{recipient_id}/revoke
173
   Authorization: Bearer <issuer_token>
174
   {"reason": "device_lost", "replaced_by": "<new_pubkey_hex>"}
175
   ```
176
2. The registry appends a revocation event to the tlog with a qualified
177
   RFC 3161 timestamp. Anyone verifying future sealed files addressed to
178
   the old pubkey will see the revocation in the event history and reject
179
   the file.
180
3. Issue new sealed files to the recipient's new pubkey.
181
 
182
Note: the revocation does NOT un-seal already-delivered ciphertext. Any file
183
the lost device opened before it was lost is already out. Revocation
184
protects against *future* misuse of the device.
185
 
186
## Threat model for hardware-backed keys
187
 
188
**What hardware keys defend against:**
189
- Recipient laptop fully compromised, attacker has root, keylogger running:
190
  attacker cannot exfiltrate the private key. Can only ECDH while device is
191
  plugged in. Discrete events.
192
- Recipient's encrypted laptop is stolen while powered off. Attacker brute-
193
  forces disk. Gets nothing useful because the PIV key is on the YubiKey.
194
- Malware on recipient's machine installs a background decryption job.
195
  Hardware-backed means each ECDH requires the device to be plugged in and
196
  (optionally) a touch. Attacker can't do it passively.
197
 
198
**What hardware keys do NOT defend against:**
199
- Recipient's laptop compromised WHILE YubiKey is plugged in. Attacker can
200
  call PKCS#11 to do ECDH against any file the legitimate client could.
201
  Mitigation: require touch-to-decrypt (YubiKey PIV policy `always-require`).
202
- Physical theft of both laptop + YubiKey. Attacker has everything needed.
203
  Mitigation: strong PIN; device auto-locks after N wrong PINs.
204
- A supply-chain-compromised YubiKey. Vendor-independence is the only
205
  mitigation - and is why Oversight supports Nitrokey / OnlyKey alongside.
206
 
207
## Known hardware caveats
208
 
209
- **PIV key operations count against the device's attempt counter.** YubiKey
210
  PIV defaults to 3 attempts before locking. Set a reasonable limit and
211
  keep a PUK to recover.
212
- **Touch policy trade-off.** `always-require-touch` is more secure but
213
  requires user interaction on every open. `cached` touches (one per
214
  session) is the usual compromise.
215
- **No post-quantum yet.** Current hardware keys don't support ML-KEM /
216
  ML-DSA. Hardware-backed recipients are CLASSIC-only for now. For PQ
217
  protection, use a file-backed recipient with a PQ suite, or wait for
218
  hardware-native ML-KEM support (YubiKey and Nitrokey have hinted at
219
  late-2026 / 2027 firmware).
220
 
221
## Checklist before deploying to real recipients
222
 
223
- [ ] PIN and PUK changed from factory defaults.
224
- [ ] Management key rotated.
225
- [ ] Touch policy decided (always vs cached vs never).
226
- [ ] Device serial recorded in a separate, encrypted inventory.
227
- [ ] Recovery procedure documented (if device lost, who is notified and how).
228
- [ ] Backup strategy: issue each recipient TWO devices (primary + backup),
229
      register BOTH pubkeys, seal to both, store backup in a safe.
230
- [ ] Revocation playbook tested end-to-end on a test recipient.
231
 
232
## Further reading
233
 
234
- YubiKey PIV documentation: https://developers.yubico.com/PIV/
235
- NIST SP 800-73-4 (PIV Interfaces): https://csrc.nist.gov/pubs/sp/800/73/4
236
- Nitrokey 3 PIV: https://docs.nitrokey.com/nitrokey3/