fix(bitlocker): add recovery-password protector + save the key (was unrecoverable)
All checks were successful
Build SilverMetal Enhanced - Windows ISO / build (pull_request) Successful in 6m17s

VM e2e: full wizard ran end-to-end and enrolled TPM+PIN, but BitLockerService only
created TPM+PIN with NO recovery protector — a forgotten/mistyped PIN bricks the
drive (hit exactly that on the VM). Add a RecoveryPassword protector and save the
48-digit key to ProgramData AND the unencrypted EFI System Partition (readable even
when the OS volume is locked, e.g. for offline recovery/verification).

PRODUCT TODO (follow-up): escrow the recovery key to SilverSync + display it in the
wizard's Done step so the end-user records it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
sysadmin
2026-06-09 20:15:49 +01:00
parent e3b010530c
commit 3f1ea6aa63

View File

@@ -38,6 +38,16 @@ public sealed class BitLockerService(IProcessRunner runner) : IBitLockerService
"if ($kp | Where-Object { $_.KeyProtectorType -eq 'TpmPin' }) { ",
"$kp | Where-Object { $_.KeyProtectorType -eq 'Tpm' } | ForEach-Object { ",
"Remove-BitLockerKeyProtector -MountPoint $mp -KeyProtectorId $_.KeyProtectorId | Out-Null } }; ",
// 4. Add a RECOVERY-PASSWORD protector so a forgotten/mistyped PIN is recoverable —
// TPM+PIN alone is an unrecoverable brick. Save the 48-digit key to ProgramData
// AND to the unencrypted EFI System Partition (readable even when the OS volume is
// locked). PRODUCT TODO: escrow to SilverSync + show it in the wizard's Done step.
"if (-not ((Get-BitLockerVolume -MountPoint $mp).KeyProtector | Where-Object { $_.KeyProtectorType -eq 'RecoveryPassword' })) { ",
"Add-BitLockerKeyProtector -MountPoint $mp -RecoveryPasswordProtector | Out-Null }; ",
"$rp=((Get-BitLockerVolume -MountPoint $mp).KeyProtector | Where-Object { $_.KeyProtectorType -eq 'RecoveryPassword' } | Select-Object -First 1).RecoveryPassword; ",
"if ($rp) { New-Item -ItemType Directory -Force 'C:\\ProgramData\\SilverMetal' | Out-Null; ",
"Set-Content -Path 'C:\\ProgramData\\SilverMetal\\bitlocker-recovery.txt' -Value $rp; ",
"try { mountvol Q: /S; Set-Content -Path 'Q:\\SilverMetal-Recovery.txt' -Value $rp; mountvol Q: /D } catch {} }; ",
// Outcome check: fail loudly (non-zero exit) if a TPM+PIN protector is not present at
// the end — this is what actually matters (a benign non-terminating warning alone
// must not pass, and a real failure must not stay silent).