core: reject a key index BIP-32 cannot harden

A.decimal wraps into Word32 and hardened leaves anything at or above 2^31
alone, so /_wallet export 4294967296 0 returned account 0's secret under the
path m/44'/60'/0'/0/0. Handing out a different account's key for a mistyped
index is the wrong failure for this command.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rvc3HbiWBTqbAvRT45G5oX
This commit is contained in:
Alain Brenzikofer
2026-09-10 11:15:02 +00:00
co-authored by Claude Opus 5
parent db1b6413e0
commit ab50d9f4b1
2 changed files with 8 additions and 1 deletions
+5 -1
View File
@@ -5590,7 +5590,7 @@ chatCommandP =
"/_service_response " *> (APISendServiceResponse <$> A.decimal <* A.space <*> strP <* A.space <*> jsonP),
"/_wallet create" $> APIWalletCreate,
"/_wallet import " *> (APIWalletImport <$> textP),
"/_wallet export " *> (APIWalletExportDerivedSecret <$> A.decimal <* A.space <*> A.decimal),
"/_wallet export " *> (APIWalletExportDerivedSecret <$> keyIndexP <* A.space <*> keyIndexP),
"/_wallet export" $> APIWalletExportSeedMnemonic,
"/_wallet delete" $> APIWalletDelete,
"/_wallet" $> APIWallet,
@@ -6134,6 +6134,10 @@ chatCommandP =
quotedP = safeDecodeUtf8 <$> (A.char '"' *> A.takeTill (== '"') <* A.char '"')
text1P = safeDecodeUtf8 <$> A.takeTill (== ' ')
char_ = optional . A.char
-- BIP-32 hardens at 2^31, and Word32 would wrap
keyIndexP = do
i <- A.decimal :: Parser Integer
if i < 0x80000000 then pure (fromIntegral i) else fail "key index too large"
displayNameP :: Parser Text
displayNameP = safeDecodeUtf8 <$> displayNameP_
+3
View File
@@ -126,6 +126,9 @@ testWalletExportDerivedSecret ps = withNewTestChat ps "alice" aliceProfile $ \al
_ <- accountRows alice "alice, active" 0
alice ##> "/_wallet export 0 0"
alice <## "m/44'/60'/0'/0/0 0x9858EfFD232B4033E47d90003D41EC34EcaEda94 0x1ab42cc412b618bdea3a599e3c9bae199ebf030895b039e9db1e30dafb12b727"
-- an index BIP-32 cannot harden is rejected, not wrapped into another account
alice ##> "/_wallet export 4294967296 0"
alice <## "bad chat command: Failed reading: empty"
-- any path derives, whether or not a profile holds that account
alice ##> "/_wallet export 3 7"
l <- getTermLine alice