fix(otp): parse KeeOTP TOTP query fields#10
Conversation
crazytan
left a comment
There was a problem hiding this comment.
Thanks for the focused compatibility work. I found two blocking correctness issues that should be addressed before merge. I also ran the relevant unit-test slice locally; it completed with one failure in KDBXParserTests.testKeeOTPSupportsEachDeclaredEncodingWithoutFallback() (the over-padded Base64 case). TOTPGeneratorTests and EncryptedValueTests passed. There are currently no GitHub workflow runs/statuses attached to this commit.
Please also add the required CHANGELOG.md entry under ## Unreleased for this bug fix.
| guard let normalized = normalizeStrictUnpaddedBase32(key) else { return nil } | ||
| return TOTPGenerator.base32Decode(normalized).flatMap { $0.isEmpty ? nil : $0 } | ||
| case "base64": | ||
| return Data(base64Encoded: key).flatMap { $0.isEmpty ? nil : $0 } |
There was a problem hiding this comment.
[P1] Make Base64 decoding strict. Data(base64Encoded:) accepts the over-padded JBSWY3DP====, so this branch creates a TOTPConfig even though the declared Base64 spelling is malformed. This is why the PR's own testKeeOTPSupportsEachDeclaredEncodingWithoutFallback fails. Please validate canonical Base64 characters/padding (or round-trip decoded bytes to the canonical representation) before accepting the secret.
| struct TOTPConfig: Sendable { | ||
| let secret: EncryptedValue | ||
| /// Pre-decoded secret bytes for formats whose declared encoding is not Base32. | ||
| let decodedSecret: EncryptedValue? |
There was a problem hiding this comment.
[P1] Preserve the decoded secret/source encoding through edits. EntryDraftPayload and DatabaseDraft.makeTOTPConfig do not carry decodedSecret, so an unrelated entry edit reconstructs Base64/Hex/UTF-8 secrets as ordinary Base32 and breaks TOTP generation. For uppercase OTP/Otp, serialization also writes conflicting TimeOtp-* fields alongside the original custom field, and those fields take precedence on reload. Please preserve the KeeOTP source/encoding or decoded bytes through the edit and serialization pipeline, with an edit/save regression test.
Closes #9
Summary
Changes
KeeForge/Models/EncryptedValue.swiftKeeForge/Models/Entry.swiftKeeForge/Models/KDBXParser.swiftKeeForge/Models/TOTPGenerator.swiftKeeForgeTests/KDBXParserTests.swiftKeeForgeTests/TOTPGeneratorTests.swiftTest Plan
Checklist
Co-Authored-Bytrailers