fix(welcome): BitLocker PIN works first boot (drop -SkipHardwareTest) + show recovery key
All checks were successful
Build SilverMetal Enhanced - Windows ISO / build (pull_request) Successful in 7m5s
All checks were successful
Build SilverMetal Enhanced - Windows ISO / build (pull_request) Successful in 7m5s
- BitLocker: remove -SkipHardwareTest so BitLocker validates the TPM+PIN unseal via its hardware test on the next reboot (the wizard's end-of-flow reboot) before encrypting — fixes the E_FVE_SECURE_BOOT_CHANGED / PCR-11 drop-to-recovery on the first post-enroll boot. The PIN now works first time instead of needing recovery. - Done step now DISPLAYS the 48-digit BitLocker recovery key (read from the file the enrollment saves) with a 'save this' warning — previously it was never surfaced. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -877,3 +877,27 @@ h1:focus { outline: none; }
|
||||
padding-left: env(safe-area-inset-left);
|
||||
}
|
||||
}
|
||||
|
||||
/* ── BitLocker recovery key (Done step) ─────────────────────────────── */
|
||||
.recovery-panel {
|
||||
margin: 1.25rem 0;
|
||||
padding: 1rem 1.25rem;
|
||||
border: 1px solid var(--clr-accent);
|
||||
border-radius: var(--radius-sm, 8px);
|
||||
background: var(--clr-accent-glow, rgba(0,212,255,0.10));
|
||||
}
|
||||
.recovery-panel h3 { margin: 0 0 0.5rem; color: var(--clr-accent); font-family: var(--font-mono); }
|
||||
.recovery-key {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 1.05rem;
|
||||
letter-spacing: 0.04em;
|
||||
color: var(--clr-text-hi);
|
||||
background: rgba(0,0,0,0.30);
|
||||
padding: 0.75rem 1rem;
|
||||
border-radius: var(--radius-sm, 8px);
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
user-select: all;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
.recovery-note { color: var(--clr-text-lo); }
|
||||
|
||||
@@ -31,7 +31,12 @@ public sealed class BitLockerService(IProcessRunner runner) : IBitLockerService
|
||||
"$p=ConvertTo-SecureString '", p, "' -AsPlainText -Force; ",
|
||||
"$v=Get-BitLockerVolume -MountPoint $mp; ",
|
||||
"if ($v.VolumeStatus -eq 'FullyDecrypted') { ",
|
||||
"Enable-BitLocker -MountPoint $mp -EncryptionMethod XtsAes256 -TpmAndPinProtector -Pin $p -SkipHardwareTest } ",
|
||||
// NO -SkipHardwareTest: let BitLocker run its hardware test on the next reboot so it
|
||||
// VALIDATES the TPM+PIN unseal against the real boot measurements before encrypting.
|
||||
// -SkipHardwareTest seals immediately against possibly-wrong PCRs -> drops to recovery
|
||||
// on first boot (E_FVE_SECURE_BOOT_CHANGED, PCR 11). The wizard's end-of-flow reboot
|
||||
// is that validation pass, so the PIN works on first boot instead of bouncing.
|
||||
"Enable-BitLocker -MountPoint $mp -EncryptionMethod XtsAes256 -TpmAndPinProtector -Pin $p } ",
|
||||
"elseif (-not ($v.KeyProtector | Where-Object { $_.KeyProtectorType -eq 'TpmPin' })) { ",
|
||||
"Add-BitLockerKeyProtector -MountPoint $mp -TpmAndPinProtector -Pin $p }; ",
|
||||
"$kp=(Get-BitLockerVolume -MountPoint $mp).KeyProtector; ",
|
||||
|
||||
@@ -2,11 +2,40 @@
|
||||
|
||||
<div class="step done-step">
|
||||
<h1>All Done!</h1>
|
||||
<p>Your SilverOS device is configured and ready. Click below to restart and start using it.</p>
|
||||
<p>Your SilverMetal device is configured and ready.</p>
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(_recoveryKey))
|
||||
{
|
||||
<div class="recovery-panel">
|
||||
<h3>⚠ Save your BitLocker recovery key</h3>
|
||||
<p class="step-subtitle">
|
||||
This is the <strong>only</strong> way back into your drive if you ever forget your PIN.
|
||||
Write it down or photograph it now and keep it somewhere safe and separate from this device.
|
||||
</p>
|
||||
<pre class="recovery-key">@_recoveryKey</pre>
|
||||
<p class="recovery-note"><small>Also saved to <code>C:\ProgramData\SilverMetal\bitlocker-recovery.txt</code> on this device.</small></p>
|
||||
</div>
|
||||
}
|
||||
|
||||
<p>Click below to restart and start using it.</p>
|
||||
<button class="btn-primary btn-restart" @onclick="RestartNow">Restart Now</button>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private string? _recoveryKey;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
// The BitLocker step saved the 48-digit recovery key to ProgramData; surface it
|
||||
// here so the user records it before finishing (TPM+PIN alone is unrecoverable).
|
||||
try
|
||||
{
|
||||
const string path = @"C:\ProgramData\SilverMetal\bitlocker-recovery.txt";
|
||||
if (File.Exists(path)) _recoveryKey = File.ReadAllText(path).Trim();
|
||||
}
|
||||
catch { /* best-effort display */ }
|
||||
}
|
||||
|
||||
private async Task RestartNow()
|
||||
{
|
||||
await ProcessRunner.RunAsync("cmd.exe", "/c shutdown /r /t 5", CancellationToken.None);
|
||||
|
||||
Reference in New Issue
Block a user