You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
initrd/gpg: refactor OEM card management into shared functions for reuse, add reprovision flow
Extract shared OpenPGP smartcard management functions from
initrd/bin/oem-factory-reset.sh into initrd/etc/gpg_functions.sh
so they can be reused by multiple callers. OEM functions become
thin wrappers passing global variables as positional args; the
reprovision flow calls the same shared functions to restore GPG
subkeys from a LUKS-encrypted backup USB onto a replacement USB
Security dongle.
Shared functions (replacing OEM inline code):
- gpg_card_factory_reset(): factory-reset + forcesig + key-attr
(RSA and ECC/P-256), parameterized on admin PIN
- gpg_keytocard_subkeys(): enable USB, verify card, move subkeys
to slots 1-3 (sign, encrypt, auth)
- gpg_set_card_identity(): cardholder name and login fields
- gpg_reset_nk3_secret_app(): Nitrokey 3 Secrets app reset
- gpg_card_change_pin(): GPG User PIN change
New reprovision flow (reprovision_smartcard_from_backup()):
10-phase flow: mount LUKS backup -> detect key type -> factory-
reset card -> import subkeys -> set identity -> sign /boot -> flash
GUI integration:
- k option in GPG Management Menu (gpg-gui.sh), always visible
- K option in prompt_missing_gpg_key_action (gui-init.sh)
- Clean boot wizard: OEM reset, reprovision, or ignore
Code quality fixes from review:
- Fix 6 sites where $? was clobbered by TRACE_FUNC/DEBUG calls
- Replace --passphrase-file with --passphrase-fd 3 3< to avoid
leaking PIN through /proc/<pid>/cmdline
- Fix algo_code/bit_len extraction: head -1 for multi-key backups
- Fix STATUS_OK mount emission: only on success
- Fix gpg_reset_nk3_secret_app: return actual error code from
hotp_verification, explicit return 0 for non-NK3
- Remove incorrect || [ $? -eq 2 ] patch: gpg --import exit code 2
is a fatal error, not unchanged
- Fix pubkey.asc import: check success properly, fall back to
keyring export on failure
- Fix partition derivation for NVMe/MMC: stable [0-9]+$ regex
- Add _luks_cleanup helper for every error return path (replaces
broken EXIT trap pattern)
- Re-run NK3 Secrets app reset with custom PIN on factory-reset
retry
- Add chmod 600 on /tmp/secret/gpg_pin
- Write backup passphrase to /tmp/secret/backup_pass, use
mount-usb.sh --pass-file to avoid argv leak
- Shred /tmp/secret/backup_pass on mount success and failure
- Shred /tmp/secret/gpg_pin after all GPG operations complete
- Limit key_id derivation to first match (head -1) to prevent
multi-line values breaking gpg --edit-key calls
- Strip trailing <email> from cardholder name when no comment
is present in UID line
- Reset card_admin_pin to default after factory-reset step so
subsequent forcesig/key-attr use the correct PIN
- Ensure /tmp/secret exists before writing backup_pass (mkdir -p)
- Export public key from keyring instead of referencing mount
file: after successful pubkey.asc import, re-export from the
keyring to /tmp/reprovision_pubkey.asc rather than pointing
PUBKEY at the potentially-stale /media/pubkey.asc
- Fix gpg_reset_nk3_secret_app header comment: removes
inaccurate "to factory default" phrasing
- Remove unused uid_decoded local variable from reprovision_smartcard_from_backup
- Fix key_email extraction: add head -1 to prevent multi-line
values when UID contains multiple <...> blocks
- Fix parent_disk derivation for NVMe/MMC: use p?[0-9]+$ regex
that handles both regular (sda1) and p-suffixed (nvme0n1p1) partitions
- Fix pub_partition derivation for p-suffixed devices: detect whether
the original partition used a p<num> separator and reconstruct the
public partition as parent_disk+p2 vs parent_disk+2 accordingly
that handles both regular (sda1) and p-suffixed (nvme0n1p1) partitions
- Fix ownertrust import regex: use [0-9A-Fa-f] to handle lowercase
hex fingerprints from gpg --with-colons
- Add --pass-file option to mount-usb.sh: use PASS_FILE variable
and pass path directly to cryptsetup --key-file instead of
reading into PASS and echoing via process substitution,
eliminating the argv leak that --pass-file was meant to fix
Documentation:
- doc/gpg.md, doc/configuring-keys.md, doc/architecture.md:
updated for the new recovery path
- doc/qemu.md: USB flash drive workflow, hardlink preservation,
reprovision test
- doc/recovery-shell.md: Authentication and Resetting Config
sections
Extends PR #1515 (2023): authentication via gpg_auth can now be
enforced with key material restored from a backup.
Tested on Nitrokey Pro 2 (reprovision end-to-end: factory reset,
keytocard, PIN change, public key import, flash, reboot, gpg_auth
works). Tested on Nitrokey 3 (OEM factory reset creates LUKS
backup + provisions dongle).
Signed-off-by: Thierry Laurion <insurgo@riseup.net>
Copy file name to clipboardExpand all lines: doc/architecture.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -132,7 +132,7 @@ The CI pipeline's workspace and cache behavior is documented in
132
132
133
133
-**No network at boot** — all verification is local; no certificate authorities
134
134
- **Hardware root of trust** — the coreboot bootblock (IBB) is the Static Core Root of Trust for Measurement (S-CRTM): the first code executed by the CPU, directly from SPI flash. Coreboot implements a transitive measurement chain: the CRTM measures FMAP and the bootblock image into the preram log, then each subsequent stage measures the next before executing it — bootblock measures romstage, romstage measures ramstage, ramstage measures the Heads payload. Measurements are taken during CBFS file loading, before decompression, and are recorded in TPM PCR 2 (SRTM) once the TPM hardware is initialized (`tpm_setup()`). Measurements taken before TPM init are cached in the preram log and flushed to PCR 2 by `tspi_measure_cache_to_pcr()` during `tpm_setup()`. The full chain — bootblock → romstage → ramstage → Heads Linux kernel + initrd — is recorded into PCR 2. PCRs 0, 1, and 3 remain zero as policy anchors. See [tpm.md](tpm.md#srtm-in-coreboot) for TPM init timing per board. See [wp-notes.md](wp-notes.md#pr0-chipset-locking) for SPI write-protection and PR0 chipset locking details.
135
-
-**Fail-closed**— failed integrity verification drops to a recovery shell. Recovery shell authentication via GPG smartcard is enforced when GPG key backup has been configured (`CONFIG_HAVE_GPG_KEY_BACKUP=y`), which is set by answering "y" to `"Would you like to format an encrypted USB Thumb drive to store GPG key material? (Required to enable GPG authentication)"` during OEM Factory Reset / Re-Ownership. Otherwise the recovery shell is unauthenticated. An "Ignore tampering and force a boot (Unsafe!)" option is available to override this.
135
+
-**Fail-closed**-- failed integrity verification drops to a recovery shell. Recovery shell authentication via GPG smartcard is enforced when GPG key backup has been configured (`CONFIG_HAVE_GPG_KEY_BACKUP=y`), which is set by answering "y" to `"Would you like to format an encrypted USB Thumb drive to store GPG key material? (Required to enable GPG authentication)"` during OEM Factory Reset / Re-Ownership, or by running "Reprovision smartcard from GPG key backup" from the GPG Management Menu. Otherwise the recovery shell is unauthenticated. An "Ignore tampering and force a boot (Unsafe!)" option is available to override this. See [recovery-shell.md](recovery-shell.md#authentication) for details.
136
136
-**Separation of duties** — the public key that verifies `/boot` signatures is stored in CBFS (ROM). The private key that signs `/boot` stays on a USB security dongle and never leaves it.
137
137
-**Auditability** — all source is open, builds are reproducible, ROM images are verifiable
retry_msg="Cannot sign /boot because no private GPG signing key is available ($DONGLE_BRAND not inserted, wiped, or key not set up).\n\nInsert your $DONGLE_BRAND and retry.\n\nHow would you like to proceed?"
243
243
fi
244
+
menu_options=(
245
+
'r'"$retry_label"
246
+
'F'' OEM Factory Reset / Re-Ownership'
247
+
'K'' Reprovision USB Security dongle from GPG key backup'
gpg_error_msg="ERROR: $CONFIG_BRAND_NAME couldn't find any GPG keys in your keyring.\n\nIf this is the first time the system has booted, you should add a public GPG key to the BIOS now.\n\nIf you just reflashed a new BIOS, you'll need to add at least one public key to the keyring.\n\nIf you have not just reflashed your BIOS, THIS COULD INDICATE TAMPERING!\n\nHow would you like to proceed?"
587
+
menu_options=(
588
+
'g'' Add a GPG key to the running BIOS'
589
+
'F'' OEM Factory Reset / Re-Ownership'
590
+
'K'' Reprovision USB Security dongle from GPG key backup'
0 commit comments