From 173ae03abed0cd25d88a5a13efac00af96b75b87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 14 May 2024 10:42:15 +0200 Subject: [PATCH] switch from libsecp256k1 to k256 and update other deps --- Cargo.toml | 14 +++---- src/bip32.rs | 71 +++++++++++++++++++++------------- src/bip44.rs | 107 +++++++++++++++++++++++++++------------------------ src/lib.rs | 5 ++- 4 files changed, 111 insertions(+), 86 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 36f33a1..7f8205f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,12 +11,12 @@ repository = "https://github.com/maciejhirsz/tiny-hderive" keywords = ["bitcoin", "ethereum", "bip32", "bip39", "bip44"] [dependencies] -libsecp256k1 = "0.3.5" -base58 = "0.1.0" -sha2 = "0.8.0" -hmac = "0.7.0" -memzero = "0.1.0" +k256 = "0.13" +base58 = "0.2" +sha2 = "0.10" +hmac = "0.12" +memzero = "0.1" [dev-dependencies] -ethsign = { version = "0.3", default-features = false, features = ["secp256k1-rs"] } -tiny-bip39 = "0.6" +ethsign = { version = "0.9", default-features = false, features = ["pure-rust"] } +tiny-bip39 = "1.0" diff --git a/src/bip32.rs b/src/bip32.rs index 60fb62b..47be7d9 100644 --- a/src/bip32.rs +++ b/src/bip32.rs @@ -1,11 +1,11 @@ -use secp256k1::{SecretKey, PublicKey}; use base58::FromBase58; -use sha2::Sha512; use hmac::{Hmac, Mac}; +use k256::SecretKey; use memzero::Memzero; +use sha2::Sha512; +use std::fmt; use std::ops::Deref; use std::str::FromStr; -use std::fmt; use crate::bip44::{ChildNumber, IntoDerivationPath}; use crate::Error; @@ -49,14 +49,15 @@ impl ExtendedPrivKey { where Path: IntoDerivationPath, { - let mut hmac: Hmac = Hmac::new_varkey(b"Bitcoin seed").expect("seed is always correct; qed"); - hmac.input(seed); + let mut hmac: Hmac = + Hmac::new_from_slice(b"Bitcoin seed").expect("seed is always correct; qed"); + hmac.update(seed); - let result = hmac.result().code(); + let result = hmac.finalize().into_bytes(); let (secret_key, chain_code) = result.split_at(32); let mut sk = ExtendedPrivKey { - secret_key: SecretKey::parse_slice(secret_key).map_err(Error::Secp256k1)?, + secret_key: SecretKey::from_slice(secret_key).map_err(Error::Secp256k1)?, chain_code: Protected::from(chain_code), }; @@ -68,31 +69,35 @@ impl ExtendedPrivKey { } pub fn secret(&self) -> [u8; 32] { - self.secret_key.serialize() + self.secret_key.to_bytes().into() } pub fn child(&self, child: ChildNumber) -> Result { - let mut hmac: Hmac = Hmac::new_varkey(&self.chain_code) - .map_err(|_| Error::InvalidChildNumber)?; + let mut hmac: Hmac = + Hmac::new_from_slice(&self.chain_code).map_err(|_| Error::InvalidChildNumber)?; if child.is_normal() { - hmac.input(&PublicKey::from_secret_key(&self.secret_key).serialize_compressed()[..]); + hmac.update(&self.secret_key.public_key().to_sec1_bytes()); } else { - hmac.input(&[0]); - hmac.input(&self.secret_key.serialize()[..]); + hmac.update(&[0]); + hmac.update(&self.secret()); } - hmac.input(&child.to_bytes()); + hmac.update(&child.to_bytes()); - let result = hmac.result().code(); + let result = hmac.finalize().into_bytes(); let (secret_key, chain_code) = result.split_at(32); - let mut secret_key = SecretKey::parse_slice(&secret_key).map_err(Error::Secp256k1)?; - secret_key.tweak_add_assign(&self.secret_key).map_err(Error::Secp256k1)?; + let mut secret_key = SecretKey::from_slice(secret_key).map_err(Error::Secp256k1)?; + let raw = *secret_key.as_scalar_primitive() + self.secret_key.as_scalar_primitive(); + if raw.is_zero().into() { + return Err(Error::ZeroChildKey); + } + secret_key = SecretKey::new(raw); Ok(ExtendedPrivKey { secret_key, - chain_code: Protected::from(&chain_code) + chain_code: Protected::from(&chain_code), }) } } @@ -101,7 +106,9 @@ impl FromStr for ExtendedPrivKey { type Err = Error; fn from_str(xprv: &str) -> Result { - let data = xprv.from_base58().map_err(|_| Error::InvalidExtendedPrivKey)?; + let data = xprv + .from_base58() + .map_err(|_| Error::InvalidExtendedPrivKey)?; if data.len() != 82 { return Err(Error::InvalidExtendedPrivKey); @@ -109,7 +116,7 @@ impl FromStr for ExtendedPrivKey { Ok(ExtendedPrivKey { chain_code: Protected::from(&data[13..45]), - secret_key: SecretKey::parse_slice(&data[46..78]).map_err(|e| Error::Secp256k1(e))? + secret_key: SecretKey::from_slice(&data[46..78]).map_err(Error::Secp256k1)?, }) } } @@ -117,7 +124,7 @@ impl FromStr for ExtendedPrivKey { #[cfg(test)] mod tests { use super::*; - use bip39::{Mnemonic, Language, Seed}; + use bip39::{Language, Mnemonic, Seed}; use ethsign::SecretKey; #[test] @@ -125,14 +132,19 @@ mod tests { let phrase = "panda eyebrow bullet gorilla call smoke muffin taste mesh discover soft ostrich alcohol speed nation flash devote level hobby quick inner drive ghost inside"; let expected_secret_key = b"\xff\x1e\x68\xeb\x7b\xf2\xf4\x86\x51\xc4\x7e\xf0\x17\x7e\xb8\x15\x85\x73\x22\x25\x7c\x58\x94\xbb\x4c\xfd\x11\x76\xc9\x98\x93\x14"; - let expected_address: &[u8] = b"\x63\xF9\xA9\x2D\x8D\x61\xb4\x8a\x9f\xFF\x8d\x58\x08\x04\x25\xA3\x01\x2d\x05\xC8"; + let expected_address: &[u8] = + b"\x63\xF9\xA9\x2D\x8D\x61\xb4\x8a\x9f\xFF\x8d\x58\x08\x04\x25\xA3\x01\x2d\x05\xC8"; let mnemonic = Mnemonic::from_phrase(phrase, Language::English).unwrap(); let seed = Seed::new(&mnemonic, ""); let account = ExtendedPrivKey::derive(seed.as_bytes(), "m/44'/60'/0'/0/0").unwrap(); - assert_eq!(expected_secret_key, &account.secret(), "Secret key is invalid"); + assert_eq!( + expected_secret_key, + &account.secret(), + "Secret key is invalid" + ); let secret_key = SecretKey::from_raw(&account.secret()).unwrap(); let public_key = secret_key.public(); @@ -140,9 +152,16 @@ mod tests { assert_eq!(expected_address, public_key.address(), "Address is invalid"); // Test child method - let account = ExtendedPrivKey::derive(seed.as_bytes(), "m/44'/60'/0'/0").unwrap().child(ChildNumber::from_str("0").unwrap()).unwrap(); - - assert_eq!(expected_secret_key, &account.secret(), "Secret key is invalid"); + let account = ExtendedPrivKey::derive(seed.as_bytes(), "m/44'/60'/0'/0") + .unwrap() + .child(ChildNumber::from_str("0").unwrap()) + .unwrap(); + + assert_eq!( + expected_secret_key, + &account.secret(), + "Secret key is invalid" + ); let secret_key = SecretKey::from_raw(&account.secret()).unwrap(); let public_key = secret_key.public(); diff --git a/src/bip44.rs b/src/bip44.rs index b75b7e9..f3ca302 100644 --- a/src/bip44.rs +++ b/src/bip44.rs @@ -9,43 +9,43 @@ const HARDENED_BIT: u32 = 1 << 31; pub struct ChildNumber(u32); impl ChildNumber { - pub fn is_hardened(&self) -> bool { - self.0 & HARDENED_BIT == HARDENED_BIT - } + pub fn is_hardened(&self) -> bool { + self.0 & HARDENED_BIT == HARDENED_BIT + } - pub fn is_normal(&self) -> bool { - self.0 & HARDENED_BIT == 0 - } + pub fn is_normal(&self) -> bool { + self.0 & HARDENED_BIT == 0 + } - pub fn to_bytes(&self) -> [u8; 4] { - self.0.to_be_bytes() - } + pub fn to_bytes(&self) -> [u8; 4] { + self.0.to_be_bytes() + } - pub fn hardened_from_u32(index: u32) -> Self { - ChildNumber(index | HARDENED_BIT) - } + pub fn hardened_from_u32(index: u32) -> Self { + ChildNumber(index | HARDENED_BIT) + } - pub fn non_hardened_from_u32(index: u32) -> Self { - ChildNumber(index) - } + pub fn non_hardened_from_u32(index: u32) -> Self { + ChildNumber(index) + } } impl FromStr for ChildNumber { type Err = Error; fn from_str(child: &str) -> Result { - let (child, mask) = if child.ends_with('\'') { - (&child[..child.len() - 1], HARDENED_BIT) - } else { - (child, 0) - }; + let (child, mask) = if let Some(child) = child.strip_suffix('\'') { + (child, HARDENED_BIT) + } else { + (child, 0) + }; let index: u32 = child.parse().map_err(|_| Error::InvalidChildNumber)?; if index & HARDENED_BIT == 0 { - Ok(ChildNumber(index | mask)) + Ok(ChildNumber(index | mask)) } else { - Err(Error::InvalidChildNumber) + Err(Error::InvalidChildNumber) } } } @@ -66,52 +66,57 @@ impl FromStr for DerivationPath { } Ok(DerivationPath { - path: path.map(str::parse).collect::, Error>>()? + path: path + .map(str::parse) + .collect::, Error>>()?, }) } } impl DerivationPath { - pub fn as_ref(&self) -> &[ChildNumber] { - &self.path - } + pub fn as_ref(&self) -> &[ChildNumber] { + &self.path + } - pub fn iter(&self) -> impl Iterator { - self.path.iter() - } + pub fn iter(&self) -> impl Iterator { + self.path.iter() + } } pub trait IntoDerivationPath { - fn into(self) -> Result; + fn into(self) -> Result; } impl IntoDerivationPath for DerivationPath { - fn into(self) -> Result { - Ok(self) - } + fn into(self) -> Result { + Ok(self) + } } impl IntoDerivationPath for &str { - fn into(self) -> Result { - self.parse() - } + fn into(self) -> Result { + self.parse() + } } #[cfg(test)] mod tests { - use super::*; - - #[test] - fn derive_path() { - let path: DerivationPath = "m/44'/60'/0'/0".parse().unwrap(); - - assert_eq!(path, DerivationPath { - path: vec![ - ChildNumber(44 | HARDENED_BIT), - ChildNumber(60 | HARDENED_BIT), - ChildNumber(0 | HARDENED_BIT), - ChildNumber(0), - ], - }); - } + use super::*; + + #[test] + fn derive_path() { + let path: DerivationPath = "m/44'/60'/0'/0".parse().unwrap(); + + assert_eq!( + path, + DerivationPath { + path: vec![ + ChildNumber(44 | HARDENED_BIT), + ChildNumber(60 | HARDENED_BIT), + ChildNumber(0 | HARDENED_BIT), + ChildNumber(0), + ], + } + ); + } } diff --git a/src/lib.rs b/src/lib.rs index 70e4fee..2e0438c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,13 +13,14 @@ //! assert_eq!(&ext.secret(), b"\x98\x84\xbf\x56\x24\xfa\xdd\x7f\xb2\x80\x4c\xfb\x0c\xb6\xf7\x1f\x28\x9e\x21\x1f\xcf\x0d\xe8\x36\xa3\x84\x17\x57\xda\xd9\x70\xd0"); //! ``` -pub mod bip44; pub mod bip32; +pub mod bip44; #[derive(Clone, PartialEq, Eq, Debug)] pub enum Error { - Secp256k1(secp256k1::Error), + Secp256k1(k256::elliptic_curve::Error), InvalidChildNumber, InvalidDerivationPath, InvalidExtendedPrivKey, + ZeroChildKey, }