Skip to content

Commit

Permalink
feat(dpp): added identity public key private key validation methods (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
QuantumExplorer authored Oct 10, 2024
1 parent 3ca878d commit 4199607
Show file tree
Hide file tree
Showing 6 changed files with 233 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod v0;

use crate::identity::IdentityPublicKey;
use crate::ProtocolError;
use dashcore::Network;
pub use v0::*;

impl IdentityPublicKeyHashMethodsV0 for IdentityPublicKey {
Expand All @@ -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<bool, ProtocolError> {
match self {
IdentityPublicKey::V0(v0) => v0.validate_private_key_bytes(private_key_bytes, network),
}
}
}
Original file line number Diff line number Diff line change
@@ -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<bool, ProtocolError>;
}
213 changes: 212 additions & 1 deletion packages/rs-dpp/src/identity/identity_public_key/v0/methods/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -45,4 +47,213 @@ impl IdentityPublicKeyHashMethodsV0 for IdentityPublicKeyV0 {
}
}
}

fn validate_private_key_bytes(
&self,
private_key_bytes: &[u8],
network: Network,
) -> Result<bool, ProtocolError> {
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
);
}
}
1 change: 0 additions & 1 deletion packages/rs-platform-value/src/types/identifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
2 changes: 1 addition & 1 deletion packages/rs-sdk/src/platform/types/evonode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 4199607

Please sign in to comment.