Reputation: 5343
parent_key has 31
bytes after processing i
, instead of 32
.
This is due to buffer overflow, as I understand it.
How can I fix my Rust code to match the python code?
This function derive_bip32childkey is used to calculate the descendants of a BIP32 key, which is a hierarchical deterministic wallet. The function takes the parent key, the parent chaining code and the index (i) of the descendant key. The function then uses HMAC-SHA512 to generate a hash, which is split into two keys: the derived descendant key and the new descendant chain code. The function also uses the BIP32_PRIVDEV binary flag to determine whether the derived descendant is private (i.e., can be used to create other descendants) or public (i.e., can only be used for inspection). If the BIP32_PRIVDEV flag block is set to i, the deterministic key is reproduced from the parent key. Otherwise, the derived key is generated from the public key and then serialized into a byte sequence. The return value of the function is a pair of values, the derived descendant key and the new descendant chain code.
fn derive_bip32childkey(
parent_key: &[u8],
parent_chain_code: &[u8],
i: u32,
) -> Result<(Vec<u8>, Vec<u8>), Box<dyn Error>> {
debug!("Parent key: {:?}", parent_key);
debug!("Parent chain code: {:?}", parent_chain_code);
debug!("Index: {}", i);
let k = hmac::Key::new(hmac::HMAC_SHA512, parent_chain_code);
let mut d = if (i & BIP32_PRIVDEV) != 0 {
let mut d = Vec::with_capacity(37);
d.push(0x00);
d.extend_from_slice(parent_key);
d
} else {
let secp = Secp256k1::new();
let sk = SecretKey::from_slice(parent_key)?;
let pk = PublicKey::from_secret_key(&secp, &sk);
pk.serialize().to_vec()
};
d.extend_from_slice(&i.to_be_bytes());
let h = hmac::sign(&k, &d);
let h_bytes = h.as_ref();
let key_bytes = &h_bytes[..32];
let chain_code = h_bytes[32..].to_vec();
debug!("Key bytes: {:?}", key_bytes);
debug!("Chain code: {:?}", chain_code);
let a = BigUint::from_bytes_be(key_bytes);
let b = BigUint::from_bytes_be(parent_key);
let order = BigUint::parse_bytes(
b"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141",
16,
)
.ok_or("Failed to parse order")?;
let key = (a.clone() + b.clone()) % order.clone();
if a < order && key != BigUint::zero() {
let key_bytes = key.to_bytes_be();
info!("Derived key: {:?}", key_bytes);
Ok((key_bytes, chain_code))
} else {
Err("Invalid key".into())
}
}
I rewrote this code myself from the Python code:
def derive_public_key(private_key):
""" Public key from a private key.
Logic adapted from https://github.com/satoshilabs/slips/blob/master/slip-0010/testvectors.py. """
Q = int.from_bytes(private_key, byteorder='big') * BIP32_CURVE.generator
xstr = int(Q.x()).to_bytes(32, byteorder='big')
parity = int(Q.y()) & 1
return (2 + parity).to_bytes(1, byteorder='big') + xstr
def derive_bip32childkey(parent_key, parent_chain_code, i):
""" Derives a child key from an existing key, i is current derivation parameter.
Logic adapted from https://github.com/satoshilabs/slips/blob/master/slip-0010/testvectors.py. """
assert len(parent_key) == 32
assert len(parent_chain_code) == 32
k = parent_chain_code
if (i & BIP32_PRIVDEV) != 0:
key = b'\x00' + parent_key
else:
key = derive_public_key(parent_key)
d = key + struct.pack('>L', i)
while True:
h = hmac.new(k, d, hashlib.sha512).digest()
key, chain_code = h[:32], h[32:]
a = int.from_bytes(key, byteorder='big')
b = int.from_bytes(parent_key, byteorder='big')
key = int((a + b) % BIP32_CURVE.order)
if a < BIP32_CURVE.order and key != 0:
key = key.to_bytes(32, byteorder='big')
break
d = b'\x01' + h[32:] + struct.pack('>L', i)
return key, chain_code
Upvotes: 0
Views: 102