diff --git a/packages/rs-dpp/src/identity/identity_public_key/key_type.rs b/packages/rs-dpp/src/identity/identity_public_key/key_type.rs index 908f913969..2d64045452 100644 --- a/packages/rs-dpp/src/identity/identity_public_key/key_type.rs +++ b/packages/rs-dpp/src/identity/identity_public_key/key_type.rs @@ -305,7 +305,7 @@ impl KeyType { KeyType::EDDSA_25519_HASH160 => { let key_pair = ed25519_dalek::SigningKey::generate(rng); ( - key_pair.verifying_key().to_bytes().to_vec(), + ripemd160_sha256(key_pair.verifying_key().to_bytes().as_slice()).to_vec(), key_pair.to_bytes().to_vec(), ) } diff --git a/packages/rs-dpp/src/identity/identity_public_key/methods/hash/mod.rs b/packages/rs-dpp/src/identity/identity_public_key/methods/hash/mod.rs index 6978a3035d..b22a3f0c26 100644 --- a/packages/rs-dpp/src/identity/identity_public_key/methods/hash/mod.rs +++ b/packages/rs-dpp/src/identity/identity_public_key/methods/hash/mod.rs @@ -2,6 +2,7 @@ mod v0; use crate::identity::IdentityPublicKey; use crate::ProtocolError; +use dashcore::Network; pub use v0::*; impl IdentityPublicKeyHashMethodsV0 for IdentityPublicKey { @@ -10,4 +11,14 @@ impl IdentityPublicKeyHashMethodsV0 for IdentityPublicKey { IdentityPublicKey::V0(v0) => v0.public_key_hash(), } } + + fn validate_private_key_bytes( + &self, + private_key_bytes: &[u8], + network: Network, + ) -> Result { + match self { + IdentityPublicKey::V0(v0) => v0.validate_private_key_bytes(private_key_bytes, network), + } + } } diff --git a/packages/rs-dpp/src/identity/identity_public_key/methods/hash/v0/mod.rs b/packages/rs-dpp/src/identity/identity_public_key/methods/hash/v0/mod.rs index 7b656292e3..b11c79c31f 100644 --- a/packages/rs-dpp/src/identity/identity_public_key/methods/hash/v0/mod.rs +++ b/packages/rs-dpp/src/identity/identity_public_key/methods/hash/v0/mod.rs @@ -1,6 +1,14 @@ use crate::ProtocolError; +use dashcore::Network; pub trait IdentityPublicKeyHashMethodsV0 { /// Get the original public key hash fn public_key_hash(&self) -> Result<[u8; 20], ProtocolError>; + + /// Verifies that the private key bytes match this identity public key + fn validate_private_key_bytes( + &self, + private_key_bytes: &[u8], + network: Network, + ) -> Result; } diff --git a/packages/rs-dpp/src/identity/identity_public_key/v0/methods/mod.rs b/packages/rs-dpp/src/identity/identity_public_key/v0/methods/mod.rs index 05232ed22a..891bd5ed82 100644 --- a/packages/rs-dpp/src/identity/identity_public_key/v0/methods/mod.rs +++ b/packages/rs-dpp/src/identity/identity_public_key/v0/methods/mod.rs @@ -5,7 +5,9 @@ use crate::util::hash::ripemd160_sha256; use crate::ProtocolError; use anyhow::anyhow; use dashcore::hashes::Hash; -use dashcore::PublicKey as ECDSAPublicKey; +use dashcore::key::Secp256k1; +use dashcore::secp256k1::SecretKey; +use dashcore::{Network, PublicKey as ECDSAPublicKey}; use platform_value::Bytes20; impl IdentityPublicKeyHashMethodsV0 for IdentityPublicKeyV0 { @@ -45,4 +47,213 @@ impl IdentityPublicKeyHashMethodsV0 for IdentityPublicKeyV0 { } } } + + fn validate_private_key_bytes( + &self, + private_key_bytes: &[u8], + network: Network, + ) -> Result { + match self.key_type { + KeyType::ECDSA_SECP256K1 => { + let secp = Secp256k1::new(); + let secret_key = match SecretKey::from_slice(private_key_bytes) { + Ok(secret_key) => secret_key, + Err(_) => return Ok(false), + }; + let private_key = dashcore::PrivateKey::new(secret_key, network); + + Ok(private_key.public_key(&secp).to_bytes() == self.data.as_slice()) + } + KeyType::BLS12_381 => { + #[cfg(feature = "bls-signatures")] + { + let private_key = + match bls_signatures::PrivateKey::from_bytes(private_key_bytes, false) { + Ok(secret_key) => secret_key, + Err(_) => return Ok(false), + }; + let g1_element = match private_key.g1_element() { + Ok(g1_element) => g1_element, + Err(_) => return Ok(false), + }; + + Ok(g1_element.to_bytes().as_slice() == self.data.as_slice()) + } + #[cfg(not(feature = "bls-signatures"))] + return Err(ProtocolError::NotSupported( + "Converting a private key to a bls public key is not supported without the bls-signatures feature".to_string(), + )); + } + KeyType::ECDSA_HASH160 => { + let secp = Secp256k1::new(); + let secret_key = match SecretKey::from_slice(private_key_bytes) { + Ok(secret_key) => secret_key, + Err(_) => return Ok(false), + }; + let private_key = dashcore::PrivateKey::new(secret_key, network); + + Ok( + ripemd160_sha256(private_key.public_key(&secp).to_bytes().as_slice()) + .as_slice() + == self.data.as_slice(), + ) + } + KeyType::EDDSA_25519_HASH160 => { + #[cfg(feature = "ed25519-dalek")] + { + let secret_key = match private_key_bytes.try_into() { + Ok(secret_key) => secret_key, + Err(_) => return Ok(false), + }; + let key_pair = ed25519_dalek::SigningKey::from_bytes(&secret_key); + Ok( + ripemd160_sha256(key_pair.verifying_key().to_bytes().as_slice()).as_slice() + == self.data.as_slice(), + ) + } + #[cfg(not(feature = "ed25519-dalek"))] + return Err(ProtocolError::NotSupported( + "Converting a private key to a eddsa hash 160 is not supported without the ed25519-dalek feature".to_string(), + )); + } + KeyType::BIP13_SCRIPT_HASH => { + return Err(ProtocolError::NotSupported( + "Converting a private key to a script hash is not supported".to_string(), + )); + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::identity::{Purpose, SecurityLevel}; + use dashcore::Network; + use dpp::version::PlatformVersion; + use rand::rngs::StdRng; + use rand::SeedableRng; + + #[cfg(feature = "random-public-keys")] + #[test] + fn test_validate_private_key_bytes_with_random_keys() { + let platform_version = PlatformVersion::latest(); + let mut rng = StdRng::from_entropy(); + + // Test for ECDSA_SECP256K1 + let key_type = KeyType::ECDSA_SECP256K1; + let (public_key_data, private_key_data) = key_type + .random_public_and_private_key_data(&mut rng, &platform_version) + .expect("expected to generate random keys"); + + let identity_public_key = IdentityPublicKeyV0 { + id: 1, + purpose: Purpose::AUTHENTICATION, + security_level: SecurityLevel::HIGH, + contract_bounds: None, + key_type, + data: public_key_data.into(), + read_only: false, + disabled_at: None, + }; + + // Validate that the private key matches the public key + assert_eq!( + identity_public_key + .validate_private_key_bytes(&private_key_data, Network::Testnet) + .unwrap(), + true + ); + + // Test with an invalid private key + let invalid_private_key_bytes = vec![0u8; private_key_data.len()]; + assert_eq!( + identity_public_key + .validate_private_key_bytes(&invalid_private_key_bytes, Network::Testnet) + .unwrap(), + false + ); + } + + #[cfg(all(feature = "random-public-keys", feature = "bls-signatures"))] + #[test] + fn test_validate_private_key_bytes_with_random_keys_bls12_381() { + let platform_version = PlatformVersion::latest(); + let mut rng = StdRng::from_entropy(); + + // Test for BLS12_381 + let key_type = KeyType::BLS12_381; + let (public_key_data, private_key_data) = key_type + .random_public_and_private_key_data(&mut rng, &platform_version) + .expect("expected to generate random keys"); + + let identity_public_key = IdentityPublicKeyV0 { + id: 2, + purpose: Purpose::AUTHENTICATION, + security_level: SecurityLevel::HIGH, + contract_bounds: None, + key_type, + data: public_key_data.into(), + read_only: false, + disabled_at: None, + }; + + // Validate that the private key matches the public key + assert_eq!( + identity_public_key + .validate_private_key_bytes(&private_key_data, Network::Testnet) + .unwrap(), + true + ); + + // Test with an invalid private key + let invalid_private_key_bytes = vec![0u8; private_key_data.len()]; + assert_eq!( + identity_public_key + .validate_private_key_bytes(&invalid_private_key_bytes, Network::Testnet) + .unwrap(), + false + ); + } + + #[cfg(all(feature = "random-public-keys", feature = "ed25519-dalek"))] + #[test] + fn test_validate_private_key_bytes_with_random_keys_eddsa_25519_hash160() { + let platform_version = PlatformVersion::latest(); + let mut rng = StdRng::from_entropy(); + + // Test for EDDSA_25519_HASH160 + let key_type = KeyType::EDDSA_25519_HASH160; + let (public_key_data, private_key_data) = key_type + .random_public_and_private_key_data(&mut rng, &platform_version) + .expect("expected to generate random keys"); + + let identity_public_key = IdentityPublicKeyV0 { + id: 3, + purpose: Purpose::AUTHENTICATION, + security_level: SecurityLevel::HIGH, + contract_bounds: None, + key_type, + data: public_key_data.into(), + read_only: false, + disabled_at: None, + }; + + // Validate that the private key matches the public key + assert_eq!( + identity_public_key + .validate_private_key_bytes(&private_key_data, Network::Testnet) + .unwrap(), + true + ); + + // Test with an invalid private key + let invalid_private_key_bytes = vec![0u8; private_key_data.len()]; + assert_eq!( + identity_public_key + .validate_private_key_bytes(&invalid_private_key_bytes, Network::Testnet) + .unwrap(), + false + ); + } } diff --git a/packages/rs-platform-value/src/types/identifier.rs b/packages/rs-platform-value/src/types/identifier.rs index 19bfbf82b2..f2f173b7d7 100644 --- a/packages/rs-platform-value/src/types/identifier.rs +++ b/packages/rs-platform-value/src/types/identifier.rs @@ -11,7 +11,6 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "json")] use serde_json::Value as JsonValue; -use crate::string_encoding::Encoding::Base58; use crate::string_encoding::{Encoding, ALL_ENCODINGS}; use crate::types::encoding_string_to_encoding; use crate::{string_encoding, Error, Value}; diff --git a/packages/rs-sdk/src/platform/types/evonode.rs b/packages/rs-sdk/src/platform/types/evonode.rs index 77e893dfd5..af0fee4210 100644 --- a/packages/rs-sdk/src/platform/types/evonode.rs +++ b/packages/rs-sdk/src/platform/types/evonode.rs @@ -10,7 +10,7 @@ use futures::{FutureExt, TryFutureExt}; use rs_dapi_client::transport::{ AppliedRequestSettings, PlatformGrpcClient, TransportClient, TransportRequest, }; -use rs_dapi_client::{Address, ConnectionPool, DapiClientError, RequestSettings}; +use rs_dapi_client::{Address, ConnectionPool, RequestSettings}; #[cfg(feature = "mocks")] use serde::{Deserialize, Serialize}; use std::fmt::Debug;