Cluster 3 — TOTP / 2FA generator (RFC 6238 + QR import) [v2.0]#45
Merged
Conversation
PassKey 2.0 now stores TOTP seeds alongside password entries and renders a
live 6-digit code with a 30-second countdown directly inside the password
detail panel. The implementation follows RFC 6238 (Time-Based One-Time Password
Algorithm), defaulting to SHA1 / 6 digits / 30s window for compatibility with
Google Authenticator, Microsoft Authenticator, Authy, and every mainstream
2FA-enabled web service.
Core (PassKey.Core):
- New NuGet: Otp.NET 1.4.1 (MIT) for RFC 6238 / Base32 primitives.
- PasswordEntry gains four optional fields (TotpSecret, TotpAlgorithm,
TotpDigits, TotpPeriod) with safe defaults so v1.x vaults open unchanged —
the JSON properties simply remain absent when no seed is configured.
- ITotpService / TotpService:
* GenerateCode(entry) — current 6-10 digit code (empty when no secret)
* RemainingSeconds(entry) — countdown in [1, period]
* ParseOtpAuthUri(uri) — full otpauth://totp/... parser including
issuer/account label split, algorithm
coercion to SHA1/256/512, digits/period
validation, defensive fallbacks
* IsValidBase32(secret) — RFC 4648 alphabet check ignoring whitespace,
lowercase, and "=" padding
Desktop (PassKey.Desktop):
- New NuGet: ZXing.Net 0.16.11 (Apache 2.0). The platform binding subpackage
was avoided because the only current one requires SkiaSharp 3.x which
conflicts with our 2.88.9 pin override for the SkiaSharp CVE
(GHSA-j7hp-h8jx-5ppr). QR decoding instead reads raw BGRA8 pixels from
Windows.Graphics.Imaging.BitmapDecoder and feeds RGBLuminanceSource
directly into ZXing's QRCodeReader/HybridBinarizer pipeline.
- PasswordDetailViewModel exposes TotpSecret/Algorithm/Digits/Period plus
derived CurrentTotpCode and TotpRemainingSeconds, with public helpers
RefreshTotpDisplay / GetCurrentTotpCodeRaw / ApplyOtpAuthUri /
ApplyManualSeed / RemoveTotp used by the View.
- PasswordDetailView gains a new "Codice 2FA (TOTP)" section above the notes:
* Empty state — three buttons (Scansiona QR, Incolla URI, Inserisci seed).
* Filled state — formatted "xxx xxx" code, ProgressRing countdown ring
with seconds-remaining numeric overlay, copy/show-secret/remove buttons.
- A DispatcherQueueTimer (1 s interval) is started in SetViewModel and
stopped in Unloaded so the code refreshes live without leaking handlers.
- The "Copia codice" button routes through the existing IClipboardService so
the 30-second clipboard auto-clear and Windows clipboard-history suppression
apply to TOTP codes just like to passwords.
- DI registers TotpService as a singleton; BitwardenImporter / OnePuxImporter
now optionally receive ITotpService and use it to promote the
Bitwarden "login.totp" field and the 1Password 1pux one-time-password
section fields into structured TotpSecret values (with otpauth:// URI
detection) instead of dropping them into the notes blob.
- Localization: 5 new x:Uid keys (FieldTotp, TotpScanQrLabel, TotpPasteUriLabel,
TotpEnterSecretLabel, TotpCodeLabel) translated across all 6 supported
cultures (it-IT, en-GB, fr-FR, de-DE, es-ES, pt-PT).
Tests: 199 passed (was 182).
- TotpServiceTests: 17 cases — otpauth URI minimal/full parameter parsing,
unsupported-algorithm fallback, non-TOTP scheme rejection, invalid-Base32
rejection, IsValidBase32 alphabet checks, code-generation shape & numeric,
same-period idempotency, RemainingSeconds bounds, end-to-end URI→code round
trip.
Verified manually (user T3.GATE smoke test):
- Real GitHub 2FA setup QR scanned from a PNG screenshot via "Scansiona QR".
- Live code rendered ("461 408") and countdown ring active.
- Code matches the corresponding code shown by an authenticator app on the
user's phone, confirming RFC 6238 conformance against a known-good
implementation.
Backward compatibility: vaults written by v1.x deserialise unchanged
(TotpSecret == null suppresses the TOTP section in the UI). Backups in the
v0x02 (Argon2id) format introduced in Cluster 2 carry the new fields end-to-end
without further migration.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Third cluster of PassKey 2.0 — the first new-feature cluster (clusters 1 & 2 were refactor + security). See `piano-revisione-correzione-miglioramento.md` for the full plan.
Summary
PassKey can now store TOTP / 2FA seeds alongside password entries and render a live 6-digit code with a 30-second countdown directly inside the password detail panel. The implementation follows RFC 6238 and is interoperable with Google Authenticator, Microsoft Authenticator, Authy, Bitwarden, 1Password, and every mainstream 2FA-enabled service.
Core (`PassKey.Core`)
Desktop (`PassKey.Desktop`)
Backward compatibility
Test plan