From 5c98e6f2c5364f455e36632163862c806c73379e Mon Sep 17 00:00:00 2001 From: Michele Esposito <34438276+mikesposito@users.noreply.github.com> Date: Fri, 22 Sep 2023 21:19:20 +0200 Subject: [PATCH] refactor: remove dependencies (#6) * refactor: remove bitcoin_hashes * refactor: remove sha2 * chore: run rustfmt --- Cargo.lock | 24 ------- Cargo.toml | 18 +++-- src/account/keychain/account.rs | 114 +++---------------------------- src/account/signer/signable.rs | 9 ++- src/utils/crypto/mod.rs | 1 + src/utils/crypto/sha3.rs | 30 ++++++++ src/utils/hex/hex.rs | 95 ++++++++++++++++++++++++++ src/utils/hex/mod.rs | 2 + src/utils/mod.rs | 2 + src/utils/safe/encryption_key.rs | 6 +- tests/signable.rs | 2 +- 11 files changed, 155 insertions(+), 148 deletions(-) create mode 100644 src/utils/crypto/mod.rs create mode 100644 src/utils/crypto/sha3.rs create mode 100644 src/utils/hex/hex.rs create mode 100644 src/utils/hex/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 7c5c3ed..673543c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,22 +35,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "bitcoin-internals" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" - -[[package]] -name = "bitcoin_hashes" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" -dependencies = [ - "bitcoin-internals", - "hex-conservative", -] - [[package]] name = "block-buffer" version = "0.10.4" @@ -257,12 +241,6 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -[[package]] -name = "hex-conservative" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ed443af458ccb6d81c1e7e661545f94d3176752fb1df2f543b902a1e0f51e2" - [[package]] name = "hmac" version = "0.12.1" @@ -455,14 +433,12 @@ name = "walleth" version = "0.1.0" dependencies = [ "bip32", - "bitcoin_hashes", "chacha20poly1305", "hex", "hmac", "pbkdf2", "rand_core", "secp256k1", - "sha2", "sha3", ] diff --git a/Cargo.toml b/Cargo.toml index 2848d8e..2562242 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,13 +16,11 @@ keywords = [ ] [dependencies] -hex = "0.4.3" -bitcoin_hashes = { version = ">= 0.12, <= 0.13" } -sha3 = "0.10.8" -bip32 = { version = "0.5.1" } -secp256k1 = "0.27.0" -rand_core = { version = "0.6", features = ["std"] } -chacha20poly1305 = { version = "0.9.0", features = ["stream"] } -pbkdf2 = { version = "0.12.2" } -sha2 = { version = "0.10.7" } -hmac = { version = "0.12.1" } +bip32 = "~0.5.1" +chacha20poly1305 = "~0.9.0" +hex = "~0.4.3" +hmac = "~0.12.1" +pbkdf2 = "~0.12.2" +rand_core = { version = "~0.6.0", features = ["std"] } +secp256k1 = "~0.27.0" +sha3 = "~0.10.8" diff --git a/src/account/keychain/account.rs b/src/account/keychain/account.rs index 4199daa..435e9e1 100644 --- a/src/account/keychain/account.rs +++ b/src/account/keychain/account.rs @@ -1,6 +1,9 @@ use bip32::XPub; -use hex::decode; -use sha3::{Digest, Keccak256}; + +use crate::{ + hex::{add0x, assert_is_valid_hex_address, encode}, + utils::crypto::sha3::keccak256, +}; #[derive(Clone, Debug)] pub struct Account { @@ -11,113 +14,14 @@ pub struct Account { impl Account { /// Create a new `Account` from an extended public key pub fn from_extended_public_key(extended_public_key: &XPub) -> Result { - let address = extended_public_key_to_address(extended_public_key)?; + let extended_address = encode(&keccak256(&extended_public_key.to_bytes())); + let address = extended_address[extended_address.len() - 40..].to_string(); - assert_is_valid_hex_address(address.as_str())?; + assert_is_valid_hex_address(&address)?; Ok(Account { - address: add0x(address), + address: add0x(&address).to_owned(), public_key: extended_public_key.to_owned(), }) } } - -/// Assert that a string is a valid hex address -/// -/// # Example -/// -/// ``` -/// use walleth::account::assert_is_valid_hex_address; -/// -/// println!("{}", assert_is_valid_hex_address("0x00C08c440DbDC3A2a9C9D99b30077a53Ba7eDEAD").is_ok()); -/// -/// assert_eq!(assert_is_valid_hex_address("0x00C08c440DbDC3A2a9C9D99b30077a53Ba7eDEAD").is_ok(), true); -/// ``` -pub fn assert_is_valid_hex_address(value: &str) -> Result<(), String> { - let unprefixed = remove0x(value); - - assert_is_hex(&unprefixed)?; - - if unprefixed.len() != 40 { - return Err(format!( - "String passed into assert_is_valid_hex_address is {} hex chars long instead of 40.", - value.len() - )); - } - - Ok(()) -} - -/// Assert that a string is a valid hex -/// -/// # Example -/// -/// ``` -/// use walleth::account::assert_is_hex; -/// -/// let assertion = assert_is_hex("00C08c440DbDC3A2a9C9D99b30077a53Ba7eDEAD"); -/// -/// assert!(assertion.is_ok()); -/// ``` -pub fn assert_is_hex(value: &str) -> Result<(), String> { - match decode(value) { - Ok(_) => Ok(()), - Err(e) => Err(e.to_string()), - } -} - -/// Remove the 0x prefix from a string -/// -/// # Example -/// -/// ``` -/// use walleth::remove0x; -/// -/// let unprefixed = remove0x("0x00C08c440DbDC3A2a9C9D99b30077a53Ba7eDEAD"); -/// assert_eq!( -/// unprefixed, -/// "00C08c440DbDC3A2a9C9D99b30077a53Ba7eDEAD", -/// ); -/// ``` -pub fn remove0x(value: &str) -> &str { - match value.starts_with("0x") { - true => &value[2..], - _ => value, - } -} - -/// Add the 0x prefix to a string -/// -/// # Example -/// -/// ``` -/// use walleth::add0x; -/// -/// assert_eq!( -/// add0x("00C08c440DbDC3A2a9C9D99b30077a53Ba7eDEAD".to_string()), -/// "0x00C08c440DbDC3A2a9C9D99b30077a53Ba7eDEAD".to_string(), -/// ); -/// ``` -pub fn add0x(value: String) -> String { - match value.starts_with("0x") { - true => value, - _ => format!("0x{}", value), - } -} - -/// Convert an extended public key to an ethereum address -pub fn extended_public_key_to_address(extended_public_key: &XPub) -> Result { - let address = keccak_hash(&extended_public_key.to_bytes()); - Ok(address[(address.len() - 40)..].to_string()) -} - -/// Hash data using the keccak256 algorithm -fn keccak_hash(data: &T) -> String -where - T: ?Sized + AsRef<[u8]>, -{ - let mut hasher = Keccak256::new(); - hasher.update(data); - let result = hasher.finalize(); - hex::encode(result) -} diff --git a/src/account/signer/signable.rs b/src/account/signer/signable.rs index ed4f080..7cadbbb 100644 --- a/src/account/signer/signable.rs +++ b/src/account/signer/signable.rs @@ -1,6 +1,7 @@ -use bitcoin_hashes::{sha256, Hash}; use secp256k1::Message; +use crate::utils::crypto::sha3::keccak256; + #[derive(Debug, Clone)] pub struct Signable { message: Message, @@ -37,13 +38,11 @@ impl Signable { /// Digest a message string pub fn digest_str(message: &str) -> Message { - let hash = sha256::Hash::hash(message.as_bytes()); - Message::from_slice(hash.as_byte_array()).unwrap() + Message::from_slice(&keccak256(message.as_bytes())).unwrap() } /// Digest message bytes pub fn digest_bytes(message: &[u8]) -> Message { - let hash = sha256::Hash::hash(message); // Unwrap is safe because the hash is always 32 bytes - Message::from_slice(hash.as_byte_array()).unwrap() + Message::from_slice(&keccak256(message)).unwrap() } diff --git a/src/utils/crypto/mod.rs b/src/utils/crypto/mod.rs new file mode 100644 index 0000000..38df78d --- /dev/null +++ b/src/utils/crypto/mod.rs @@ -0,0 +1 @@ +pub mod sha3; diff --git a/src/utils/crypto/sha3.rs b/src/utils/crypto/sha3.rs new file mode 100644 index 0000000..bce0fab --- /dev/null +++ b/src/utils/crypto/sha3.rs @@ -0,0 +1,30 @@ +use sha3::{Digest, Keccak256}; + +/// Computes the Keccak-256 hash of the input data. +/// +/// # Example +/// +/// ``` +/// use walleth::utils::crypto::sha3::keccak256; +/// +/// let hash = keccak256(&"Hello, world!".as_bytes()); +/// +/// assert_eq!( +/// hash, +/// [ +/// 182, 225, 109, 39, 172, +/// 90, 180, 39, 167, 246, +/// 137, 0, 172, 85, 89, +/// 206, 39, 45, 198, 195, +/// 124, 130, 179, 224, 82, +/// 36, 108, 130, 36, 76, +/// 80, 228 +/// ] +/// ); +/// ``` +/// +pub fn keccak256(data: &[u8]) -> [u8; 32] { + let mut hasher = Keccak256::new(); + hasher.update(data); + hasher.finalize().into() +} diff --git a/src/utils/hex/hex.rs b/src/utils/hex/hex.rs new file mode 100644 index 0000000..f43cd9e --- /dev/null +++ b/src/utils/hex/hex.rs @@ -0,0 +1,95 @@ +use hex; + +/// Encode a byte array into a hex string +pub fn encode(data: &[u8]) -> String { + hex::encode(data) +} + +/// Decode a hex string into a byte array +pub fn decode(data: &str) -> Result, String> { + match hex::decode(data) { + Ok(bytes) => Ok(bytes), + Err(e) => Err(e.to_string()), + } +} + +/// Assert that a string is a valid hex address +/// +/// # Example +/// +/// ``` +/// use walleth::utils::hex::assert_is_valid_hex_address; +/// +/// assert!(assert_is_valid_hex_address(&"0x00C08c440DbDC3A2a9C9D99b30077a53Ba7eDEAD".to_string()).is_ok()); +/// ``` +pub fn assert_is_valid_hex_address(value: &String) -> Result<(), String> { + let unprefixed = remove0x(value); + + assert_is_hex(&unprefixed)?; + + if unprefixed.len() != 40 { + return Err(format!( + "String passed into assert_is_valid_hex_address is {} hex chars long instead of 40.", + value.len() + )); + } + + Ok(()) +} + +/// Assert that a string is a valid hex +/// +/// # Example +/// +/// ``` +/// use walleth::utils::hex::assert_is_hex; +/// +/// let assertion = assert_is_hex(&"00C08c440DbDC3A2a9C9D99b30077a53Ba7eDEAD".to_string()); +/// +/// assert!(assertion.is_ok()); +/// ``` +pub fn assert_is_hex(value: &str) -> Result<(), String> { + match decode(value) { + Ok(_) => Ok(()), + Err(e) => Err(e.to_string()), + } +} + +/// Remove the 0x prefix from a string +/// +/// # Example +/// +/// ``` +/// use walleth::utils::hex::remove0x; +/// +/// let unprefixed = remove0x(&"0x00C08c440DbDC3A2a9C9D99b30077a53Ba7eDEAD".to_string()); +/// assert_eq!( +/// unprefixed, +/// "00C08c440DbDC3A2a9C9D99b30077a53Ba7eDEAD".to_string(), +/// ); +/// ``` +pub fn remove0x(value: &String) -> String { + match value.starts_with("0x") { + true => String::from(&value[2..]), + _ => value.to_string(), + } +} + +/// Add the 0x prefix to a string +/// +/// # Example +/// +/// ``` +/// use walleth::utils::hex::add0x; +/// +/// assert_eq!( +/// add0x(&"00C08c440DbDC3A2a9C9D99b30077a53Ba7eDEAD".to_string()), +/// "0x00C08c440DbDC3A2a9C9D99b30077a53Ba7eDEAD", +/// ); +/// ``` +pub fn add0x(value: &String) -> String { + match value.starts_with("0x") { + true => value.to_string(), + _ => format!("0x{}", value), + } +} diff --git a/src/utils/hex/mod.rs b/src/utils/hex/mod.rs new file mode 100644 index 0000000..692e913 --- /dev/null +++ b/src/utils/hex/mod.rs @@ -0,0 +1,2 @@ +pub mod hex; +pub use hex::*; diff --git a/src/utils/mod.rs b/src/utils/mod.rs index ef79da0..85dd22a 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,5 +1,7 @@ pub mod controller; +pub mod crypto; pub mod hdwallet; +pub mod hex; pub mod observable; pub mod safe; diff --git a/src/utils/safe/encryption_key.rs b/src/utils/safe/encryption_key.rs index a02efe9..44ca3c8 100644 --- a/src/utils/safe/encryption_key.rs +++ b/src/utils/safe/encryption_key.rs @@ -1,7 +1,7 @@ use hmac::Hmac; use pbkdf2::pbkdf2; use rand_core::{OsRng, RngCore}; -use sha2::Sha256; +use sha3::Keccak256; /// A Public Key & Salt pair that can be used for simmetric encryption, /// compatible with ChaCha20Poly1305 @@ -27,7 +27,7 @@ impl EncryptionKey { // Key derivation let mut pubk = [0; 32]; - if let Err(_) = pbkdf2::>(password, &salt, rounds, &mut pubk) { + if let Err(_) = pbkdf2::>(password, &salt, rounds, &mut pubk) { panic!("Key derivation failed") } @@ -39,7 +39,7 @@ impl EncryptionKey { pub fn with_salt(password: &[u8], salt: [u8; 16], rounds: u32) -> Self { // Key derivation let mut pubk = [0; 32]; - if let Err(_) = pbkdf2::>(password, &salt, rounds, &mut pubk) { + if let Err(_) = pbkdf2::>(password, &salt, rounds, &mut pubk) { panic!("Key derivation failed") } diff --git a/tests/signable.rs b/tests/signable.rs index 45fed77..e5de166 100644 --- a/tests/signable.rs +++ b/tests/signable.rs @@ -1,6 +1,6 @@ use walleth::Signable; -const MESSAGE_DIGEST: &str = "c0535e4be2b79ffd93291305436bf889314e4a3faec05ecffcbb7df31ad9e51a"; +const MESSAGE_DIGEST: &str = "ecd0e108a98e192af1d2c25055f4e3bed784b5c877204e73219a5203251feaab"; mod new { use super::*;