diff --git a/src/crypto/cosmos.rs b/src/crypto/cosmos.rs index 5c99d9d..7d18436 100644 --- a/src/crypto/cosmos.rs +++ b/src/crypto/cosmos.rs @@ -2,24 +2,31 @@ use cosmwasm_std::Addr; use crate::crypto::encoding::encode_bech32; use crate::crypto::hashing::{ripemd160, sha256}; +use crate::crypto::secp256k1::to_compressed_key; +use cosmwasm_std::StdResult; pub type RawCosmosAddress = [u8; 20]; -pub fn cosmos_raw_address(pubkey: &[u8]) -> RawCosmosAddress { - let hash = ripemd160(&sha256(pubkey)); +pub fn cosmos_raw_address_from_pubkey_secp256k1(pubkey: &[u8]) -> StdResult { + let compressed_pubkey = to_compressed_key(pubkey)?; + + let hash = ripemd160(&sha256(&compressed_pubkey)); let mut addr = [0u8; 20]; addr.copy_from_slice(&hash[..]); - addr + Ok(addr) } pub fn cosmos_address(raw_address: &RawCosmosAddress, prefix: &str) -> Addr { Addr::unchecked(encode_bech32(prefix, raw_address).unwrap()) } -pub fn cosmos_address_from_pubkey(pubkey: &[u8], prefix: &str) -> Addr { - cosmos_address(&cosmos_raw_address(pubkey), prefix) +pub fn cosmos_address_from_pubkey_secp256k1(pubkey: &[u8], prefix: &str) -> StdResult { + Ok(cosmos_address( + &cosmos_raw_address_from_pubkey_secp256k1(pubkey)?, + prefix, + )) } #[cfg(test)] @@ -38,7 +45,7 @@ mod tests { // Get pubkey in bytes let pubkey_bytes = parse_bech32(&pubkey_str, "pub").unwrap(); // Convert pubkey bytes to address - let recovered_addr = cosmos_address_from_pubkey(&pubkey_bytes, "fetch"); + let recovered_addr = cosmos_address_from_pubkey_secp256k1(&pubkey_bytes, "fetch").unwrap(); assert_eq!(recovered_addr, address); } diff --git a/src/crypto/ethereum.rs b/src/crypto/ethereum.rs index eb4e409..7933e68 100644 --- a/src/crypto/ethereum.rs +++ b/src/crypto/ethereum.rs @@ -64,6 +64,11 @@ pub fn parse_eth_address(eth_address: &str) -> Result { Ok(address) } +pub fn raw_eth_address_to_string(eth_address: &EthAddress) -> String { + let hex_string = hex::encode(eth_address); + format!("0x{}", hex_string) +} + /// Parse the input ETH style signature and split into raw signature and recovery code /// /// # Arguments @@ -169,8 +174,8 @@ pub fn addresses_error(err: &T) -> StdError { #[cfg(test)] mod tests { use super::*; - use crate::crypto::cosmos::cosmos_address_from_pubkey; - use crate::crypto::hashing::compress_pubkey_secp256k1; + use crate::crypto::cosmos::cosmos_address_from_pubkey_secp256k1; + use crate::crypto::secp256k1::to_compressed_key; use cosmwasm_std::testing::mock_dependencies; #[test] @@ -287,9 +292,19 @@ mod tests { let eth_pubkey = hex::decode("5c084296dfaeaf815a3a7e4e8688ed4140e403f1cd2d2f545c7a3822007763ae0e547eb989d5eecbfc5acd0204531b38e3bcfab232c506db7a9353d68932ca61").unwrap(); let expected_fetch_address = "fetch1e6lpplutmnxae8u7le9xsr7r9r4y9rukaf4lx8"; - let compressed_pubkey = compress_pubkey_secp256k1(ð_pubkey).unwrap(); + let compressed_pubkey = to_compressed_key(ð_pubkey).unwrap(); - let fetch_address = cosmos_address_from_pubkey(&compressed_pubkey, "fetch"); + let fetch_address = + cosmos_address_from_pubkey_secp256k1(&compressed_pubkey, "fetch").unwrap(); assert_eq!(fetch_address, expected_fetch_address); } + + #[test] + fn test_address_parsing() { + let eth_address = "0xbaCf56506032e9f1BF4c9C92925460DE929fa8d8"; + let raw_eth_address = parse_eth_address(eth_address).unwrap(); + let converted_address = raw_eth_address_to_string(&raw_eth_address); + + assert_eq!(eth_address.to_lowercase(), converted_address.to_lowercase()); + } } diff --git a/src/crypto/hashing.rs b/src/crypto/hashing.rs index 7e66579..aa75a16 100644 --- a/src/crypto/hashing.rs +++ b/src/crypto/hashing.rs @@ -1,4 +1,3 @@ -use cosmwasm_std::StdError; use ripemd::Ripemd160; use sha2::{Digest, Sha256}; use tiny_keccak::Hasher; @@ -32,29 +31,3 @@ pub fn ripemd160(data: &[u8]) -> Vec { ripemd160.update(data); ripemd160.finalize().to_vec() } - -pub fn compress_pubkey_secp256k1(uncompressed_pubkey_bytes: &[u8]) -> Result, StdError> { - if uncompressed_pubkey_bytes.len() != 64 { - return Err(pubkey_error(&"Wrong len")); - } - - // The first byte is the prefix, followed by 32 bytes for X and 32 bytes for Y - let x_bytes = &uncompressed_pubkey_bytes[0..32]; - let y_bytes = &uncompressed_pubkey_bytes[32..64]; - - // Determine if Y is even or odd for the prefix - // Y's last byte's least significant bit determines its evenness or oddness - let prefix_byte = if (y_bytes[31] & 1) == 0 { 0x02 } else { 0x03 }; - - // Create the compressed public key - let mut compressed_pubkey: Vec = Vec::with_capacity(33); - compressed_pubkey.push(prefix_byte); - compressed_pubkey.extend_from_slice(x_bytes); - - Ok(compressed_pubkey) -} - -// Error -pub fn pubkey_error(err: &T) -> StdError { - StdError::generic_err(format!("Eth pubkey error {}", err)) -} diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index d94e630..07863dd 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -2,4 +2,5 @@ pub mod cosmos; pub mod encoding; pub mod ethereum; pub mod hashing; +pub mod secp256k1; pub mod uagents; diff --git a/src/crypto/secp256k1.rs b/src/crypto/secp256k1.rs new file mode 100644 index 0000000..1238396 --- /dev/null +++ b/src/crypto/secp256k1.rs @@ -0,0 +1,41 @@ +use cosmwasm_std::{StdError, StdResult}; + +pub type CompressedPubkey = [u8; 33]; + +fn compress_pubkey(uncompressed_pubkey_bytes: &[u8]) -> CompressedPubkey { + // The first byte is the prefix, followed by 32 bytes for X and 32 bytes for Y + let x_bytes = &uncompressed_pubkey_bytes[0..32]; + let y_bytes = &uncompressed_pubkey_bytes[32..64]; + + // Determine if Y is even or odd for the prefix + // Y's last byte's least significant bit determines its evenness or oddness + let prefix_byte = if (y_bytes[31] & 1) == 0 { 0x02 } else { 0x03 }; + + // Create the compressed public key array + let mut compressed_pubkey = [0u8; 33]; + compressed_pubkey[0] = prefix_byte; + compressed_pubkey[1..].copy_from_slice(x_bytes); + + compressed_pubkey +} + +pub fn to_compressed_key(pubkey: &[u8]) -> StdResult { + match pubkey.len() { + // Compressed pubkey + 33 => pubkey + .try_into() + .map_err(|_| pubkey_error(&"Conversion error")), + + // Uncompressed without checksum + 64 => Ok(compress_pubkey(pubkey)), + + // Uncompressed with checksum + 65 => Ok(compress_pubkey(&pubkey[1..])), + _ => Err(pubkey_error(&"Wrong len")), + } +} + +// Error +pub fn pubkey_error(err: &T) -> StdError { + StdError::generic_err(format!("Secp256k1 pubkey error {}", err)) +}