While implementing a BIP32 path parse target for bitcoinfuzz. In embit, I encountered multiple inconsistencies and edge cases that lead to unexpected behavior or crashes.
These issues mainly stem from insufficient input validation in path segment parsing.
1. Negative index handling leads to runtime exception
Path segments are parsed using int(e), which allows negative values.
This leads to an OverflowError later during derivation in index.to_bytes(4, "big")
Example:
from embit import bip32
data = bytes.fromhex("deadbeef")
root = bip32.HDKey.from_seed(data)
root.derive("m/-1")
Result:
data = sec + index.to_bytes(4, "big")
^^^^^^^^^^^^^^^^^^^^^^^^
OverflowError: can't convert negative int to unsigned
Expected behavior:
Path parsing should reject negative indices early (e.g., ValueError)
(BIP32 indices must be in range [0, 2^32 - 1])
2. Hardened derivation with negative indices produces incorrect positive indices
Hardened indices are computed as:
if e[-1] in {"h", "H", "'"}:
return int(e[:-1]) + HARDENED_INDEX
Due to negative values not being rejected early, negative values like -10h are allowed, resulting in:
(-10) + 2^31 = 2147483638
This is:
a valid positive integer
but not actually a hardened index, since it is < HARDENED_INDEX
Example:
from embit import bip32
data = bytes.fromhex("deadbeef")
root = bip32.HDKey.from_seed(data)
xpub = root.derive("m/-10h").to_public()
print(xpub.child_number == 2**31 - 10)
Result:
True
Impact:
A hardened derivation was indicated (via h), but a non-hardened derivation was performed.
Expected behavior:
Reject negative indices early
3. Insufficient validation of path segments (whitespace, empty segments, signs)
Path parsing does not validate or normalize input segments properly.
This leads to multiple edge cases:
Whitespace handling is inconsistent:
"m/1 h" → OK
"m/1h " → (trailing space breaks e[-1]) → ValueError
"m/\n1/2" → OK
Empty segments are not handled:
"m//1/" → produces "" → e[-1] → IndexError
Sign handling:
"m/+1/"→ OK
"m/-1" → accepted during parsing, but fails later (see issue 1)
Expected behavior:
Reject troublesome segments explicitly and consistently
Avoid runtime crashes (IndexError)
Validate each segment before accessing e[-1]
Reject explicit plus sign or document as allowed
These issues were discovered during work on bitcoinfuzz and may lead to:
- crashes (potential for DoS attacks)
- non-compliant derivation results
- divergence from other BIP32 implementations behavior
Thank you for maintaining embit. I hope this report is of help.
While implementing a BIP32 path parse target for bitcoinfuzz. In embit, I encountered multiple inconsistencies and edge cases that lead to unexpected behavior or crashes.
These issues mainly stem from insufficient input validation in path segment parsing.
1. Negative index handling leads to runtime exception
Path segments are parsed using
int(e), which allows negative values.This leads to an
OverflowErrorlater during derivation inindex.to_bytes(4, "big")Example:
Result:
Expected behavior:
Path parsing should reject negative indices early (e.g.,
ValueError)(BIP32 indices must be in range
[0, 2^32 - 1])2. Hardened derivation with negative indices produces incorrect positive indices
Hardened indices are computed as:
Due to negative values not being rejected early, negative values like
-10hare allowed, resulting in:(-10) + 2^31 = 2147483638This is:
a valid positive integer
but not actually a hardened index, since it is <
HARDENED_INDEXExample:
Result:
TrueImpact:
A hardened derivation was indicated (via
h), but a non-hardened derivation was performed.Expected behavior:
Reject negative indices early
3. Insufficient validation of path segments (whitespace, empty segments, signs)
Path parsing does not validate or normalize input segments properly.
This leads to multiple edge cases:
Whitespace handling is inconsistent:
"m/1 h"→ OK"m/1h "→ (trailing space breakse[-1]) →ValueError"m/\n1/2"→ OKEmpty segments are not handled:
"m//1/"→ produces""→e[-1]→IndexErrorSign handling:
"m/+1/"→ OK"m/-1"→ accepted during parsing, but fails later (see issue 1)Expected behavior:
Reject troublesome segments explicitly and consistently
Avoid runtime crashes (
IndexError)Validate each segment before accessing e[-1]
Reject explicit plus sign or document as allowed
These issues were discovered during work on bitcoinfuzz and may lead to:
Thank you for maintaining embit. I hope this report is of help.