BIP 32 binary encoding functions are extracted from base58

This commit is contained in:
Dr Maxim Orlovsky 2020-09-10 02:37:10 +02:00
parent 3f465d56eb
commit 259259eabf
No known key found for this signature in database
GPG Key ID: FFC0250947E5C6F7
1 changed files with 122 additions and 73 deletions

View File

@ -388,6 +388,12 @@ pub enum Error {
InvalidChildNumberFormat,
/// Invalid derivation path format.
InvalidDerivationPathFormat,
/// Unknown version magic bytes
UnknownVersion([u8; 4]),
/// Encoded extended key data have wrong length
WrongExtendedKeyLength(usize),
/// Base58 encoding error
Base58(base58::Error)
}
impl fmt::Display for Error {
@ -399,6 +405,9 @@ impl fmt::Display for Error {
Error::RngError(ref s) => write!(f, "rng error {}", s),
Error::InvalidChildNumberFormat => f.write_str("invalid child number format"),
Error::InvalidDerivationPathFormat => f.write_str("invalid derivation path format"),
Error::UnknownVersion(ref bytes) => write!(f, "unknown version magic bytes: {:?}", bytes),
Error::WrongExtendedKeyLength(ref len) => write!(f, "encoded extended key data have wrong length {}", len),
Error::Base58(ref err) => write!(f, "base58 encoding error: {}", err),
}
}
}
@ -417,6 +426,18 @@ impl From<secp256k1::Error> for Error {
fn from(e: secp256k1::Error) -> Error { Error::Ecdsa(e) }
}
impl From<base58::Error> for Error {
fn from(err: base58::Error) -> Self {
Error::Base58(err)
}
}
impl From<Error> for base58::Error {
fn from(err: Error) -> Self {
base58::Error::Other(err.to_string())
}
}
impl ExtendedPrivKey {
/// Construct a new master key from a seed value
pub fn new_master(network: Network, seed: &[u8]) -> Result<ExtendedPrivKey, Error> {
@ -459,11 +480,11 @@ impl ExtendedPrivKey {
pub fn ckd_priv<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>, i: ChildNumber) -> Result<ExtendedPrivKey, Error> {
let mut hmac_engine: HmacEngine<sha512::Hash> = HmacEngine::new(&self.chain_code[..]);
match i {
ChildNumber::Normal {..} => {
ChildNumber::Normal { .. } => {
// Non-hardened key: compute public data and use that
hmac_engine.input(&PublicKey::from_private_key(secp, &self.private_key).key.serialize()[..]);
}
ChildNumber::Hardened {..} => {
ChildNumber::Hardened { .. } => {
// Hardened key: use only secret data to prevent public derivation
hmac_engine.input(&[0u8]);
hmac_engine.input(&self.private_key[..]);
@ -489,6 +510,57 @@ impl ExtendedPrivKey {
})
}
/// Decoding extended private key from binary data according to BIP 32
pub fn decode(data: &[u8]) -> Result<ExtendedPrivKey, Error> {
if data.len() != 78 {
return Err(Error::WrongExtendedKeyLength(data.len()))
}
let cn_int: u32 = endian::slice_to_u32_be(&data[9..13]);
let child_number: ChildNumber = ChildNumber::from(cn_int);
let network = if data[0..4] == [0x04u8, 0x88, 0xAD, 0xE4] {
Network::Bitcoin
} else if data[0..4] == [0x04u8, 0x35, 0x83, 0x94] {
Network::Testnet
} else {
return Err(base58::Error::InvalidVersion((&data[0..4]).to_vec()).into());
};
Ok(ExtendedPrivKey {
network: network,
depth: data[4],
parent_fingerprint: Fingerprint::from(&data[5..9]),
child_number: child_number,
chain_code: ChainCode::from(&data[13..45]),
private_key: PrivateKey {
compressed: true,
network: network,
key: secp256k1::SecretKey::from_slice(
&data[46..78]
).map_err(|e|
base58::Error::Other(e.to_string())
)?,
},
})
}
/// Extended private key binary encoding according to BIP 32
pub fn encode(&self) -> [u8; 78] {
let mut ret = [0; 78];
ret[0..4].copy_from_slice(&match self.network {
Network::Bitcoin => [0x04, 0x88, 0xAD, 0xE4],
Network::Testnet | Network::Signet | Network::Regtest => [0x04, 0x35, 0x83, 0x94],
}[..]);
ret[4] = self.depth as u8;
ret[5..9].copy_from_slice(&self.parent_fingerprint[..]);
ret[9..13].copy_from_slice(&endian::u32_to_array_be(u32::from(self.child_number)));
ret[13..45].copy_from_slice(&self.chain_code[..]);
ret[45] = 0;
ret[46..78].copy_from_slice(&self.private_key[..]);
ret
}
/// Returns the HASH160 of the public key belonging to the xpriv
pub fn identifier<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>) -> XpubIdentifier {
ExtendedPubKey::from_private(secp, self).identifier()
@ -531,7 +603,7 @@ impl ExtendedPubKey {
/// Compute the scalar tweak added to this key to get a child key
pub fn ckd_pub_tweak(&self, i: ChildNumber) -> Result<(PrivateKey, ChainCode), Error> {
match i {
ChildNumber::Hardened {..} => {
ChildNumber::Hardened { .. } => {
Err(Error::CannotDeriveFromHardenedKey)
}
ChildNumber::Normal { index: n } => {
@ -572,7 +644,49 @@ impl ExtendedPubKey {
})
}
/// Returns the HASH160 of the public key of the xpub
/// Decoding extended public key from binary data according to BIP 32
pub fn decode(data: &[u8]) -> Result<ExtendedPubKey, Error> {
if data.len() != 78 {
return Err(Error::WrongExtendedKeyLength(data.len()))
}
let cn_int: u32 = endian::slice_to_u32_be(&data[9..13]);
let child_number: ChildNumber = ChildNumber::from(cn_int);
Ok(ExtendedPubKey {
network: if data[0..4] == [0x04u8, 0x88, 0xB2, 0x1E] {
Network::Bitcoin
} else if data[0..4] == [0x04u8, 0x35, 0x87, 0xCF] {
Network::Testnet
} else {
return Err(base58::Error::InvalidVersion((&data[0..4]).to_vec()).into());
},
depth: data[4],
parent_fingerprint: Fingerprint::from(&data[5..9]),
child_number: child_number,
chain_code: ChainCode::from(&data[13..45]),
public_key: PublicKey::from_slice(
&data[45..78]).map_err(|e|
base58::Error::Other(e.to_string()))?,
})
}
/// Extended public key binary encoding according to BIP 32
pub fn encode(&self) -> [u8; 78] {
let mut ret = [0; 78];
ret[0..4].copy_from_slice(&match self.network {
Network::Bitcoin => [0x04u8, 0x88, 0xB2, 0x1E],
Network::Testnet | Network::Signet | Network::Regtest => [0x04u8, 0x35, 0x87, 0xCF],
}[..]);
ret[4] = self.depth as u8;
ret[5..9].copy_from_slice(&self.parent_fingerprint[..]);
ret[9..13].copy_from_slice(&endian::u32_to_array_be(u32::from(self.child_number)));
ret[13..45].copy_from_slice(&self.chain_code[..]);
ret[45..78].copy_from_slice(&self.public_key.key.serialize()[..]);
ret
}
/// Returns the HASH160 of the chaincode
pub fn identifier(&self) -> XpubIdentifier {
let mut engine = XpubIdentifier::engine();
self.public_key.write_into(&mut engine).expect("engines don't error");
@ -587,18 +701,7 @@ impl ExtendedPubKey {
impl fmt::Display for ExtendedPrivKey {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let mut ret = [0; 78];
ret[0..4].copy_from_slice(&match self.network {
Network::Bitcoin => [0x04, 0x88, 0xAD, 0xE4],
Network::Testnet | Network::Signet | Network::Regtest => [0x04, 0x35, 0x83, 0x94],
}[..]);
ret[4] = self.depth as u8;
ret[5..9].copy_from_slice(&self.parent_fingerprint[..]);
ret[9..13].copy_from_slice(&endian::u32_to_array_be(u32::from(self.child_number)));
ret[13..45].copy_from_slice(&self.chain_code[..]);
ret[45] = 0;
ret[46..78].copy_from_slice(&self.private_key[..]);
fmt.write_str(&base58::check_encode_slice(&ret[..]))
fmt.write_str(&base58::check_encode_slice(&self.encode()[..]))
}
}
@ -612,49 +715,13 @@ impl FromStr for ExtendedPrivKey {
return Err(base58::Error::InvalidLength(data.len()));
}
let cn_int: u32 = endian::slice_to_u32_be(&data[9..13]);
let child_number: ChildNumber = ChildNumber::from(cn_int);
let network = if data[0..4] == [0x04u8, 0x88, 0xAD, 0xE4] {
Network::Bitcoin
} else if data[0..4] == [0x04u8, 0x35, 0x83, 0x94] {
Network::Testnet
} else {
return Err(base58::Error::InvalidVersion((&data[0..4]).to_vec()));
};
Ok(ExtendedPrivKey {
network: network,
depth: data[4],
parent_fingerprint: Fingerprint::from(&data[5..9]),
child_number: child_number,
chain_code: ChainCode::from(&data[13..45]),
private_key: PrivateKey {
compressed: true,
network: network,
key: secp256k1::SecretKey::from_slice(
&data[46..78]
).map_err(|e|
base58::Error::Other(e.to_string())
)?,
},
})
Ok(ExtendedPrivKey::decode(&data[..])?)
}
}
impl fmt::Display for ExtendedPubKey {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let mut ret = [0; 78];
ret[0..4].copy_from_slice(&match self.network {
Network::Bitcoin => [0x04u8, 0x88, 0xB2, 0x1E],
Network::Testnet | Network::Signet | Network::Regtest => [0x04u8, 0x35, 0x87, 0xCF],
}[..]);
ret[4] = self.depth as u8;
ret[5..9].copy_from_slice(&self.parent_fingerprint[..]);
ret[9..13].copy_from_slice(&endian::u32_to_array_be(u32::from(self.child_number)));
ret[13..45].copy_from_slice(&self.chain_code[..]);
ret[45..78].copy_from_slice(&self.public_key.key.serialize()[..]);
fmt.write_str(&base58::check_encode_slice(&ret[..]))
fmt.write_str(&base58::check_encode_slice(&self.encode()[..]))
}
}
@ -668,25 +735,7 @@ impl FromStr for ExtendedPubKey {
return Err(base58::Error::InvalidLength(data.len()));
}
let cn_int: u32 = endian::slice_to_u32_be(&data[9..13]);
let child_number: ChildNumber = ChildNumber::from(cn_int);
Ok(ExtendedPubKey {
network: if data[0..4] == [0x04u8, 0x88, 0xB2, 0x1E] {
Network::Bitcoin
} else if data[0..4] == [0x04u8, 0x35, 0x87, 0xCF] {
Network::Testnet
} else {
return Err(base58::Error::InvalidVersion((&data[0..4]).to_vec()));
},
depth: data[4],
parent_fingerprint: Fingerprint::from(&data[5..9]),
child_number: child_number,
chain_code: ChainCode::from(&data[13..45]),
public_key: PublicKey::from_slice(
&data[45..78]).map_err(|e|
base58::Error::Other(e.to_string()))?
})
Ok(ExtendedPubKey::decode(&data[..])?)
}
}