fix(lite): classify imported keys before calling the backend
importKeyCore guessed the key type from its first character, then
retried a failed import with the other command. That retry is what sent
shielded keys to the backend's transparent-key decoder, which panicked
on them and poisoned the wallet lock. A WIF typo that was still
valid base58 also reached the backend unchecked.
- The key is classified first, and exactly one backend command runs, or
none:
- secret-extended-key-... and zxview... go to "import";
- a Base58Check WIF with version 0xBC or 0x80 and the compressed flag
goes to "timport";
- everything else is refused before any backend call.
There is never a retry with the other command. A testnet (0xEF) WIF
is not accepted, because the Lite app is mainnet-only.
- New fixed, untranslated reasons: "uncompressed transparent keys are
not supported by the Lite wallet", a full-node incoming viewing key
(zivks) is explained as such, and anything else is "unrecognized key
format". The key is never echoed.
- The backend's refusal while the wallet is encrypted now maps to "wallet
is encrypted - remove encryption to import a private key". It is
checked before "locked", because unlocking would not help.
- base58Decode reserves its buffers up front, and it and
decodeBase58Check wipe their copies of the decoded bytes with
sodium_memzero on every return path, since the input can be a private
key.
- exportSeed rejects the all-zero-entropy phrase (23 x "abandon" + "art").
A backend built before the seed-export fix returns it for a wallet
whose lock failed halfway (seed wiped, wallet still unlocked); it is
never a real backup and must not seed a chat identity.
- An open that the backend recovered from the .bak ("OK: recovered from
backup; damaged file kept as <name>") is written to the lite log.
Tests: the retry control now expects one "import" and no "timport", and
"typo-key" makes no backend call; the controller's transparent-import
test uses a real compressed WIF. A new classifier test covers both WIF
versions, the shielded and viewing keys, a single call on failure, the
uncompressed, zivks, testnet, bad-flag and bad-checksum refusals, the
encrypted reason and its lite-log line, the zero-entropy seed and the
recovery log line.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -49,6 +49,7 @@ inline std::atomic<long> g_liteFakeBalanceEntered{0}; // calls that reached "
|
||||
inline std::atomic<bool> g_liteFakeBlockEncrypt{false}; // "encrypt" blocks (before changing state)
|
||||
inline std::atomic<bool> g_liteFakeBlockImport{false}; // "import"/"timport" block
|
||||
inline std::atomic<bool> g_liteFakeImportFails{false}; // "import"/"timport" reject the key (real do_import_key shape)
|
||||
inline std::atomic<bool> g_liteFakeImportEncrypted{false}; // "import"/"timport" refuse: the wallet is encrypted
|
||||
inline std::atomic<bool> g_liteFakeLogEnabled{false};
|
||||
inline std::mutex g_liteFakeLogMutex;
|
||||
inline std::vector<std::string> g_liteFakeCommandLog; // execute() command names, in order, while enabled
|
||||
@@ -73,6 +74,10 @@ inline long liteFakeCount(const std::vector<std::string>& log, const char* comma
|
||||
inline std::string g_liteFakeDeadServerSubstr;
|
||||
// initialize_existing returns a warming-up (-28) error for URLs containing this substring.
|
||||
inline std::string g_liteFakeWarmupServerSubstr;
|
||||
// When non-empty, initialize_existing returns "OK: <note>" (a wallet recovered from its backup, s2-D14).
|
||||
inline std::string g_liteFakeOpenRecoveredNote;
|
||||
// When non-empty, "seed" returns this phrase instead of the default one.
|
||||
inline std::string g_liteFakeSeedPhrase;
|
||||
|
||||
inline void resetLiteFakeCounters()
|
||||
{
|
||||
@@ -81,6 +86,8 @@ inline void resetLiteFakeCounters()
|
||||
g_liteFakeShutdownCalled = false;
|
||||
g_liteFakeDeadServerSubstr.clear();
|
||||
g_liteFakeWarmupServerSubstr.clear();
|
||||
g_liteFakeOpenRecoveredNote.clear();
|
||||
g_liteFakeSeedPhrase.clear();
|
||||
g_liteFakeBlockNew = false;
|
||||
g_liteFakeBlockSave = false;
|
||||
g_liteFakeBlockBalance = false;
|
||||
@@ -88,6 +95,7 @@ inline void resetLiteFakeCounters()
|
||||
g_liteFakeBlockEncrypt = false;
|
||||
g_liteFakeBlockImport = false;
|
||||
g_liteFakeImportFails = false;
|
||||
g_liteFakeImportEncrypted = false;
|
||||
g_liteFakeLogEnabled = false;
|
||||
(void)liteFakeTakeLog();
|
||||
}
|
||||
@@ -142,6 +150,7 @@ inline char* liteFakeInitExisting(bool, const char* server)
|
||||
return liteFakeDup("Error: could not connect to server"); // bridge maps to ok=false
|
||||
}
|
||||
}
|
||||
if (!g_liteFakeOpenRecoveredNote.empty()) return liteFakeDup(("OK: " + g_liteFakeOpenRecoveredNote).c_str());
|
||||
return liteFakeDup("OK");
|
||||
}
|
||||
inline char* liteFakeExecute(const char* command, const char* args)
|
||||
@@ -207,11 +216,18 @@ inline char* liteFakeExecute(const char* command, const char* args)
|
||||
if (std::strcmp(c, "export") == 0)
|
||||
return liteFakeDup("[{\"address\":\"zs1fakeaddr\",\"private_key\":\"SECRET-ZKEY\","
|
||||
"\"viewing_key\":\"zxviewsFAKE\"}]");
|
||||
if (std::strcmp(c, "seed") == 0)
|
||||
if (std::strcmp(c, "seed") == 0) {
|
||||
if (!g_liteFakeSeedPhrase.empty())
|
||||
return liteFakeDup(("{\"seed\":\"" + g_liteFakeSeedPhrase + "\",\"birthday\":0}").c_str());
|
||||
return liteFakeDup("{\"seed\":\"fake seed phrase words\",\"birthday\":0}");
|
||||
}
|
||||
// import (shielded key) / timport (transparent WIF): do_import_* pretty-print JSON.
|
||||
if (std::strcmp(c, "import") == 0 || std::strcmp(c, "timport") == 0) {
|
||||
liteFakeWaitWhile(g_liteFakeBlockImport);
|
||||
// The real refusal (lightclient do_import_tk/do_import_sk, prefixed by the console command).
|
||||
if (g_liteFakeImportEncrypted.load())
|
||||
return liteFakeDup("Error: Can't import a private key while the wallet is encrypted - remove "
|
||||
"encryption, import, then encrypt again");
|
||||
// The real do_import_key error REPEATS the key: "Error: '<key>' was not recognized as ...".
|
||||
if (g_liteFakeImportFails.load())
|
||||
return liteFakeDup((std::string("Error: '") + (args ? args : "") +
|
||||
|
||||
Reference in New Issue
Block a user