Skip to content

fix(backup): decrypt seed words before restoring account storage - #4

Draft
mptfire wants to merge 2 commits into
masterfrom
sec/amb-006-seed-restore
Draft

mptfire wants to merge 2 commits into
masterfrom
sec/amb-006-seed-restore

Conversation

@mptfire

@mptfire mptfire commented Oct 13, 2026

Copy link
Copy Markdown
Owner

Published as a draft for owner review. Local Android project validation remains blocked; do not merge until checks pass.

Finding: AMB-006 / Med. Branch: sec/amb-006-seed-restore. Commit: b5450c3ba6cabbddfc4c136e3111473c25004354.

Problem and threat model

A normal encrypted JSONL backup contains an own-key NIP-44-encrypted mnemonic, but import stores that ciphertext directly as seed words. Restore can replace the recovery mnemonic with ciphertext; the NIP-49 nsec itself is still restored.

Source: AccountExportService.kt:54; AccountExportService.kt:192. Shared with pinned upstream master; no fork-only regression.

Fix

Decrypt and validate the mnemonic before storing it; require metadata npub to match the restored key's identity; use that derived identity.

User behavior and risk

Only valid decrypted mnemonic text is saved. A backup without seed words does not overwrite an existing seed. Identity mismatch or invalid mnemonic rejects that account import.

Malformed backups that previously appeared to import now fail that account. Import still processes separate JSONL accounts independently.

Tests and validation

Added regression coverage:

  • app/src/test/java/com/greenart7c3/nostrsigner/service/AccountExportSeedRestoreTest.kt

AccountExportSeedRestoreTest.kt was added for decrypted mnemonic, absent seed, and invalid mnemonic cases. Mocked tests and a native NIP-44/NIP-49 export/import round trip did not run because Gradle is blocked.

git diff --check passes. Patch applies cleanly to upstream 43bb743f6523676c942bba1c0790a80294f53a86.

Attempted ./gradlew ktlintCheck test lint :app:assembleFreeRelease --no-daemon: blocked before tasks, AGP 9.3.2 plugin resolution fails. GitHub CI status is tracked on this draft PR. Project lint/tests/build must pass before merging. For Android instrumentation, run the connected free-debug tests with a device once a build is available.

Migration and release notes

Backup format is unchanged. To recover an already incorrectly stored seed, re-import the intact encrypted JSONL backup or use the original mnemonic. No automatic destructive migration is attempted.

No version bump. Repository changelogs are version-based; add accepted user-visible behavior to the next release notes at release preparation.

Prepared by Codex security review.
Original local review commit: 908ded786de8fb3c17bbe688e7a2d33471acae18
@mptfire

mptfire commented Oct 13, 2026

Copy link
Copy Markdown
Owner Author

Reopening to trigger CI checks on the fork (Actions were not enabled when this draft was opened, so no checks have run yet).

@mptfire mptfire closed this Oct 13, 2026
@mptfire mptfire reopened this Oct 13, 2026
The npub-vs-restored-key require is the import gate that stops a backup
whose metadata names a different identity; extract it next to
decryptExportedSeedWords and cover both branches.
@mptfire

mptfire commented Oct 13, 2026

Copy link
Copy Markdown
Owner Author

Fixup commit 7e96ce7d, pre-CI: the identity-match requirement (metadata npub must equal the restored key's derived npub) had no test case. Extracted as requireBackupIdentityMatches next to decryptExportedSeedWords and covered for both the mismatch (throws) and match (passes) branches.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant