[Feature] Import legacy AES-256 encrypted seed backups via QR code#882
[Feature] Import legacy AES-256 encrypted seed backups via QR code#882berlinxray wants to merge 1 commit intoSeedSigner:devfrom
Conversation
cfb08ad to
739037d
Compare
…a QR Scan a QR code containing an OpenSSL-compatible AES-256-CBC encrypted seed (base64-encoded, starting with U2FsdGVkX1 / Salted__), decrypt with a user-provided passphrase, validate as BIP-39 or Electrum mnemonic, and import as a seed. - Add pure-Python AES-256-CBC decryptor (no pyaes dependency) with precomputed InvMixColumns lookup tables for Pi Zero performance - Add EncryptedSeedQrDecoder and QRType.SEED__ENCRYPTED for auto- detecting encrypted seed QRs - Add SETTING__ENCRYPTED_SEEDS toggle (advanced, disabled by default) - Add EncryptedSeedPassphraseView, EncryptedSeedDecryptView, and EncryptedSeedDecryptionFailedView for the decrypt/retry UI flow - Support both 10,000 and 100,000 PBKDF2 iterations (auto-detect) - Fix SeedAddPassphraseScreen to preserve custom title for decrypt flow - Add back button support for scan screen - Add 11 tests (AES unit + encrypted QR integration) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
412bf5a to
8b8809b
Compare
|
Checked out this branch and ran the tests locally. Went through the AES implementation- verified One thing I noticed with the tests though: every success-path test in For a hand-rolled AES on a signing device I think there should be at least one hardcoded vector that works without any external tools. Something like: def test_decrypt_hardcoded_vector():
"""Pre-computed OpenSSL vector- no openssl binary needed at test time."""
encrypted = "U2FsdGVkX1+5ER21G7dz8fbBzXbp4p3LiMqJuwXOJN29f+1djh0nhJovkaPluB7Vcm9yr9g5Ss65pFvt+UQTsiYiEwJSiL9D08dn5AXmI5UmPR2WJuBQD14tEMrX/eXXVNLtG3Gy7qbiJkidJepX0w=="
result = decrypt_openssl_aes256cbc(encrypted, "TestVector2026")
assert result == "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"I generated that with: Ran it against the branch and it passes. Just means the happy path is always covered regardless of the test environment. |
Summary
Scan a QR code containing an OpenSSL-compatible AES-256-CBC encrypted seed backup, decrypt it with a user-provided passphrase, validate as BIP-39 or Electrum mnemonic, and import as a seed.
Workflow
U2FsdGVkX1/Salted__prefix)Activated via Settings > Advanced > Encrypted seeds (
SETTING__ENCRYPTED_SEEDS, disabled by default).Changes (10 files)
Core (
src/seedsigner/helpers/aes_decrypt.py) — Newpyaesdependency)decrypt_openssl_aes256cbc()andDecryptionErrorexceptionQR Detection (
src/seedsigner/models/decode_qr.py,qr_type.py)QRType.SEED__ENCRYPTEDconstantEncryptedSeedQrDecoderfor base64-encoded encrypted seed QRsU2FsdGVkX1prefixSettings (
src/seedsigner/models/settings_definition.py)SETTING__ENCRYPTED_SEEDStoggle (advanced, disabled by default)Views (
src/seedsigner/views/seed_views.py)EncryptedSeedPassphraseView— Passphrase entry screenEncryptedSeedDecryptView— Decryption + BIP-39/Electrum validationEncryptedSeedDecryptionFailedView— Retry/discard on failureScan (
src/seedsigner/views/scan_views.py)SETTING__ENCRYPTED_SEEDStoggleGUI (
src/seedsigner/gui/screens/seed_screens.py)SeedAddPassphraseScreento preserve custom title ("Decryption Passphrase")Tests
tests/test_aes_decrypt.py— AES-256-CBC unit teststests/test_encrypted_seed_qr.py— Encrypted QR integration teststests/conftest.py— Shared test fixturesLegacy Workflow (reference)
Checklist
pytestpassesTest plan