Skip to main content

dpp/identity/identity_public_key/v0/methods/
mod.rs

1use crate::identity::identity_public_key::methods::hash::IdentityPublicKeyHashMethodsV0;
2use crate::identity::identity_public_key::v0::IdentityPublicKeyV0;
3use crate::identity::KeyType;
4use crate::util::hash::ripemd160_sha256;
5use crate::ProtocolError;
6use anyhow::anyhow;
7#[cfg(feature = "ed25519-dalek")]
8use dashcore::ed25519_dalek;
9use dashcore::hashes::Hash;
10use dashcore::key::Secp256k1;
11use dashcore::secp256k1::SecretKey;
12use dashcore::{Network, PublicKey as ECDSAPublicKey};
13use platform_value::{BinaryData, Bytes20};
14#[cfg(feature = "bls-signatures")]
15use {crate::bls_signatures, dashcore::blsful::Bls12381G2Impl};
16impl IdentityPublicKeyHashMethodsV0 for IdentityPublicKeyV0 {
17    /// Get the original public key hash
18    fn public_key_hash(&self) -> Result<[u8; 20], ProtocolError> {
19        public_key_hash_for_key_data(self.key_type, &self.data)
20    }
21
22    fn validate_private_key_bytes(
23        &self,
24        private_key_bytes: &[u8; 32],
25        network: Network,
26    ) -> Result<bool, ProtocolError> {
27        validate_private_key_bytes_for_key_data(
28            self.key_type,
29            &self.data,
30            private_key_bytes,
31            network,
32        )
33    }
34}
35
36/// The public key hash for a key of the given type and data. Shared by every key version.
37pub(in crate::identity::identity_public_key) fn public_key_hash_for_key_data(
38    key_type: KeyType,
39    data: &BinaryData,
40) -> Result<[u8; 20], ProtocolError> {
41    if data.is_empty() {
42        return Err(ProtocolError::EmptyPublicKeyDataError);
43    }
44
45    match key_type {
46        KeyType::ECDSA_SECP256K1 => {
47            let key = match data.len() {
48                // TODO: We need to update schema and tests for 65 len keys
49                65 | 33 => ECDSAPublicKey::from_slice(data.as_slice())
50                    .map_err(|e| anyhow!("unable to create pub key - {}", e))?,
51                _ => {
52                    return Err(ProtocolError::ParsingError(format!(
53                        "the key length is invalid: {} Allowed sizes: 33 or 65 bytes for ecdsa key",
54                        data.len()
55                    )));
56                }
57            };
58            Ok(key.pubkey_hash().to_byte_array())
59        }
60        KeyType::BLS12_381 => {
61            if data.len() != 48 {
62                Err(ProtocolError::ParsingError(format!(
63                    "the key length is invalid: {} Allowed sizes: 48 bytes for bls key",
64                    data.len()
65                )))
66            } else {
67                Ok(ripemd160_sha256(data.as_slice()))
68            }
69        }
70        KeyType::ECDSA_HASH160 | KeyType::BIP13_SCRIPT_HASH | KeyType::EDDSA_25519_HASH160 => {
71            Ok(Bytes20::from_vec(data.to_vec())?.into_buffer())
72        }
73    }
74}
75
76/// Verifies that the private key bytes match a key of the given type and data. Shared by every
77/// key version.
78pub(in crate::identity::identity_public_key) fn validate_private_key_bytes_for_key_data(
79    key_type: KeyType,
80    data: &BinaryData,
81    private_key_bytes: &[u8; 32],
82    network: Network,
83) -> Result<bool, ProtocolError> {
84    match key_type {
85        KeyType::ECDSA_SECP256K1 => {
86            let secp = Secp256k1::new();
87            let secret_key = match SecretKey::from_byte_array(private_key_bytes) {
88                Ok(secret_key) => secret_key,
89                Err(_) => return Ok(false),
90            };
91            let private_key = dashcore::PrivateKey::new(secret_key, network);
92
93            Ok(private_key.public_key(&secp).to_bytes() == data.as_slice())
94        }
95        KeyType::BLS12_381 => {
96            #[cfg(feature = "bls-signatures")]
97            {
98                let private_key: Option<bls_signatures::SecretKey<Bls12381G2Impl>> =
99                    bls_signatures::SecretKey::<Bls12381G2Impl>::from_be_bytes(private_key_bytes)
100                        .into();
101                if private_key.is_none() {
102                    return Ok(false);
103                }
104                let private_key = private_key.expect("expected private key");
105
106                Ok(private_key.public_key().0.to_compressed() == data.as_slice())
107            }
108            #[cfg(not(feature = "bls-signatures"))]
109            return Err(ProtocolError::NotSupported(
110                "Converting a private key to a bls public key is not supported without the bls-signatures feature".to_string(),
111            ));
112        }
113        KeyType::ECDSA_HASH160 => {
114            let secp = Secp256k1::new();
115            let secret_key = match SecretKey::from_byte_array(private_key_bytes) {
116                Ok(secret_key) => secret_key,
117                Err(_) => return Ok(false),
118            };
119            let private_key = dashcore::PrivateKey::new(secret_key, network);
120
121            Ok(
122                ripemd160_sha256(private_key.public_key(&secp).to_bytes().as_slice()).as_slice()
123                    == data.as_slice(),
124            )
125        }
126        KeyType::EDDSA_25519_HASH160 => {
127            #[cfg(feature = "ed25519-dalek")]
128            {
129                let key_pair = ed25519_dalek::SigningKey::from_bytes(private_key_bytes);
130                Ok(
131                    ripemd160_sha256(key_pair.verifying_key().to_bytes().as_slice()).as_slice()
132                        == data.as_slice(),
133                )
134            }
135            #[cfg(not(feature = "ed25519-dalek"))]
136            return Err(ProtocolError::NotSupported(
137                "Converting a private key to a eddsa hash 160 is not supported without the ed25519-dalek feature".to_string(),
138            ));
139        }
140        KeyType::BIP13_SCRIPT_HASH => Err(ProtocolError::NotSupported(
141            "Converting a private key to a script hash is not supported".to_string(),
142        )),
143    }
144}
145
146#[cfg(test)]
147mod tests {
148    use super::*;
149    use crate::identity::{Purpose, SecurityLevel};
150    use dashcore::blsful::{Bls12381G2Impl, Pairing, Signature, SignatureSchemes};
151    use dashcore::Network;
152    use dpp::version::PlatformVersion;
153    use rand::rngs::StdRng;
154    use rand::SeedableRng;
155
156    #[test]
157    fn test_bls_serialization_deserialization() {
158        let mut rng = StdRng::seed_from_u64(5);
159        let (public_key_data, secret_key) = KeyType::BLS12_381
160            .random_public_and_private_key_data(&mut rng, PlatformVersion::latest())
161            .expect("expected to get keys");
162        let decoded_secret_key =
163            dashcore::blsful::SecretKey::<Bls12381G2Impl>::from_be_bytes(&secret_key)
164                .expect("expected to get secret key");
165        let public_key = decoded_secret_key.public_key();
166        let decoded_public_key_data = public_key.0.to_compressed();
167        assert_eq!(
168            public_key_data.as_slice(),
169            decoded_public_key_data.as_slice()
170        )
171    }
172
173    #[test]
174    fn test_bls_serialization_deserialization_signature() {
175        let mut rng = StdRng::seed_from_u64(5);
176        let (_, secret_key) = KeyType::BLS12_381
177            .random_public_and_private_key_data(&mut rng, PlatformVersion::latest())
178            .expect("expected to get keys");
179        let decoded_secret_key =
180            dashcore::blsful::SecretKey::<Bls12381G2Impl>::from_be_bytes(&secret_key)
181                .expect("expected to get secret key");
182        let signature = decoded_secret_key
183            .sign(SignatureSchemes::Basic, b"hello")
184            .expect("expected to sign");
185        let compressed = signature.as_raw_value().to_compressed();
186        let g2 = <Bls12381G2Impl as Pairing>::Signature::from_compressed(&compressed)
187            .expect("G2 projective");
188        let decoded_signature = Signature::<Bls12381G2Impl>::Basic(g2);
189        assert_eq!(
190            compressed.as_slice(),
191            decoded_signature.as_raw_value().to_compressed().as_slice()
192        )
193    }
194
195    #[cfg(feature = "random-public-keys")]
196    #[test]
197    fn test_validate_private_key_bytes_with_random_keys() {
198        let platform_version = PlatformVersion::latest();
199        let mut rng = StdRng::from_entropy();
200
201        // Test for ECDSA_SECP256K1
202        let key_type = KeyType::ECDSA_SECP256K1;
203        let (public_key_data, private_key_data) = key_type
204            .random_public_and_private_key_data(&mut rng, platform_version)
205            .expect("expected to generate random keys");
206
207        let identity_public_key = IdentityPublicKeyV0 {
208            id: 1,
209            purpose: Purpose::AUTHENTICATION,
210            security_level: SecurityLevel::HIGH,
211            contract_bounds: None,
212            key_type,
213            data: public_key_data.into(),
214            read_only: false,
215            disabled_at: None,
216        };
217
218        // Validate that the private key matches the public key
219        assert!(identity_public_key
220            .validate_private_key_bytes(&private_key_data, Network::Testnet)
221            .unwrap(),);
222
223        // Test with an invalid private key
224        let invalid_private_key_bytes = [0u8; 32];
225        assert!(!identity_public_key
226            .validate_private_key_bytes(&invalid_private_key_bytes, Network::Testnet)
227            .unwrap());
228    }
229
230    #[cfg(all(feature = "random-public-keys", feature = "bls-signatures"))]
231    #[test]
232    fn test_validate_private_key_bytes_with_random_keys_bls12_381() {
233        let platform_version = PlatformVersion::latest();
234        let mut rng = StdRng::from_entropy();
235
236        // Test for BLS12_381
237        let key_type = KeyType::BLS12_381;
238        let (public_key_data, private_key_data) = key_type
239            .random_public_and_private_key_data(&mut rng, platform_version)
240            .expect("expected to generate random keys");
241
242        let identity_public_key = IdentityPublicKeyV0 {
243            id: 2,
244            purpose: Purpose::AUTHENTICATION,
245            security_level: SecurityLevel::HIGH,
246            contract_bounds: None,
247            key_type,
248            data: public_key_data.into(),
249            read_only: false,
250            disabled_at: None,
251        };
252
253        // Validate that the private key matches the public key
254        assert!(identity_public_key
255            .validate_private_key_bytes(&private_key_data, Network::Testnet)
256            .unwrap());
257
258        // Test with an invalid private key
259        let invalid_private_key_bytes = [0u8; 32];
260        assert!(!identity_public_key
261            .validate_private_key_bytes(&invalid_private_key_bytes, Network::Testnet)
262            .unwrap());
263    }
264
265    // -- public_key_hash error paths --
266
267    #[test]
268    fn test_public_key_hash_empty_data_errors() {
269        use platform_value::BinaryData;
270        let key = IdentityPublicKeyV0 {
271            id: 0,
272            purpose: Purpose::AUTHENTICATION,
273            security_level: SecurityLevel::HIGH,
274            contract_bounds: None,
275            key_type: KeyType::ECDSA_SECP256K1,
276            data: BinaryData::new(vec![]),
277            read_only: false,
278            disabled_at: None,
279        };
280        let err = key.public_key_hash().unwrap_err();
281        assert!(matches!(err, ProtocolError::EmptyPublicKeyDataError));
282    }
283
284    #[test]
285    fn test_public_key_hash_ecdsa_wrong_length_errors() {
286        use platform_value::BinaryData;
287        // ECDSA_SECP256K1 accepts only 33 or 65 bytes. 32 should fail with ParsingError.
288        let key = IdentityPublicKeyV0 {
289            id: 0,
290            purpose: Purpose::AUTHENTICATION,
291            security_level: SecurityLevel::HIGH,
292            contract_bounds: None,
293            key_type: KeyType::ECDSA_SECP256K1,
294            data: BinaryData::new(vec![1u8; 32]),
295            read_only: false,
296            disabled_at: None,
297        };
298        let err = key.public_key_hash().unwrap_err();
299        match err {
300            ProtocolError::ParsingError(msg) => assert!(msg.contains("key length is invalid")),
301            other => panic!("expected ParsingError, got {:?}", other),
302        }
303    }
304
305    #[test]
306    fn test_public_key_hash_bls_wrong_length_errors() {
307        use platform_value::BinaryData;
308        // BLS12_381 expects exactly 48 bytes.
309        let key = IdentityPublicKeyV0 {
310            id: 0,
311            purpose: Purpose::AUTHENTICATION,
312            security_level: SecurityLevel::HIGH,
313            contract_bounds: None,
314            key_type: KeyType::BLS12_381,
315            data: BinaryData::new(vec![1u8; 40]),
316            read_only: false,
317            disabled_at: None,
318        };
319        let err = key.public_key_hash().unwrap_err();
320        match err {
321            ProtocolError::ParsingError(msg) => assert!(msg.contains("48 bytes for bls key")),
322            other => panic!("expected ParsingError, got {:?}", other),
323        }
324    }
325
326    #[test]
327    fn test_public_key_hash_bls_returns_ripemd160_sha256_of_data() {
328        use crate::util::hash::ripemd160_sha256;
329        use platform_value::BinaryData;
330        let data = vec![7u8; 48];
331        let key = IdentityPublicKeyV0 {
332            id: 0,
333            purpose: Purpose::AUTHENTICATION,
334            security_level: SecurityLevel::HIGH,
335            contract_bounds: None,
336            key_type: KeyType::BLS12_381,
337            data: BinaryData::new(data.clone()),
338            read_only: false,
339            disabled_at: None,
340        };
341        let hash = key
342            .public_key_hash()
343            .expect("expected hash for 48-byte bls");
344        assert_eq!(hash, ripemd160_sha256(data.as_slice()));
345    }
346
347    #[test]
348    fn test_public_key_hash_ecdsa_hash160_returns_data_itself() {
349        use platform_value::BinaryData;
350        let data = vec![9u8; 20];
351        let key = IdentityPublicKeyV0 {
352            id: 0,
353            purpose: Purpose::AUTHENTICATION,
354            security_level: SecurityLevel::HIGH,
355            contract_bounds: None,
356            key_type: KeyType::ECDSA_HASH160,
357            data: BinaryData::new(data.clone()),
358            read_only: false,
359            disabled_at: None,
360        };
361        let hash = key.public_key_hash().expect("expected hash");
362        assert_eq!(hash.as_slice(), data.as_slice());
363    }
364
365    #[test]
366    fn test_public_key_hash_bip13_script_hash_returns_data_itself() {
367        use platform_value::BinaryData;
368        let data = vec![3u8; 20];
369        let key = IdentityPublicKeyV0 {
370            id: 0,
371            purpose: Purpose::AUTHENTICATION,
372            security_level: SecurityLevel::HIGH,
373            contract_bounds: None,
374            key_type: KeyType::BIP13_SCRIPT_HASH,
375            data: BinaryData::new(data.clone()),
376            read_only: false,
377            disabled_at: None,
378        };
379        let hash = key.public_key_hash().expect("expected hash");
380        assert_eq!(hash.as_slice(), data.as_slice());
381    }
382
383    #[test]
384    fn test_public_key_hash_hash160_wrong_length_errors() {
385        use platform_value::BinaryData;
386        // Non-ECDSA hash variants route through Bytes20::from_vec, which should reject != 20.
387        let key = IdentityPublicKeyV0 {
388            id: 0,
389            purpose: Purpose::AUTHENTICATION,
390            security_level: SecurityLevel::HIGH,
391            contract_bounds: None,
392            key_type: KeyType::ECDSA_HASH160,
393            data: BinaryData::new(vec![0u8; 19]),
394            read_only: false,
395            disabled_at: None,
396        };
397        assert!(key.public_key_hash().is_err());
398    }
399
400    // -- validate_private_key_bytes: BIP13 is unsupported and always errors --
401    #[test]
402    fn test_validate_private_key_bytes_bip13_script_hash_is_unsupported() {
403        use platform_value::BinaryData;
404        let key = IdentityPublicKeyV0 {
405            id: 0,
406            purpose: Purpose::AUTHENTICATION,
407            security_level: SecurityLevel::HIGH,
408            contract_bounds: None,
409            key_type: KeyType::BIP13_SCRIPT_HASH,
410            data: BinaryData::new(vec![0u8; 20]),
411            read_only: false,
412            disabled_at: None,
413        };
414        let err = key
415            .validate_private_key_bytes(&[0u8; 32], Network::Testnet)
416            .unwrap_err();
417        match err {
418            ProtocolError::NotSupported(msg) => {
419                assert!(msg.contains("script hash"));
420            }
421            other => panic!("expected NotSupported, got {:?}", other),
422        }
423    }
424
425    // -- validate_private_key_bytes for ECDSA: bad secret key bytes are handled (Ok(false)) --
426    #[test]
427    fn test_validate_private_key_bytes_ecdsa_secret_key_parse_error_returns_false() {
428        use platform_value::BinaryData;
429        // All-zeroes is not a valid secp256k1 secret key; the code maps that
430        // to Ok(false) rather than Err.
431        let key = IdentityPublicKeyV0 {
432            id: 0,
433            purpose: Purpose::AUTHENTICATION,
434            security_level: SecurityLevel::HIGH,
435            contract_bounds: None,
436            key_type: KeyType::ECDSA_SECP256K1,
437            // The actual stored public key is irrelevant here because we never get past
438            // the secret-key parse step.
439            data: BinaryData::new(vec![0u8; 33]),
440            read_only: false,
441            disabled_at: None,
442        };
443        let ok = key
444            .validate_private_key_bytes(&[0u8; 32], Network::Testnet)
445            .unwrap();
446        assert!(!ok);
447    }
448
449    #[test]
450    fn test_validate_private_key_bytes_ecdsa_hash160_secret_key_parse_error_returns_false() {
451        use platform_value::BinaryData;
452        let key = IdentityPublicKeyV0 {
453            id: 0,
454            purpose: Purpose::AUTHENTICATION,
455            security_level: SecurityLevel::HIGH,
456            contract_bounds: None,
457            key_type: KeyType::ECDSA_HASH160,
458            data: BinaryData::new(vec![0u8; 20]),
459            read_only: false,
460            disabled_at: None,
461        };
462        let ok = key
463            .validate_private_key_bytes(&[0u8; 32], Network::Testnet)
464            .unwrap();
465        assert!(!ok);
466    }
467
468    #[cfg(all(feature = "random-public-keys", feature = "ed25519-dalek"))]
469    #[test]
470    fn test_validate_private_key_bytes_with_random_keys_eddsa_25519_hash160() {
471        let platform_version = PlatformVersion::latest();
472        let mut rng = StdRng::from_entropy();
473
474        // Test for EDDSA_25519_HASH160
475        let key_type = KeyType::EDDSA_25519_HASH160;
476        let (public_key_data, private_key_data) = key_type
477            .random_public_and_private_key_data(&mut rng, platform_version)
478            .expect("expected to generate random keys");
479
480        let identity_public_key = IdentityPublicKeyV0 {
481            id: 3,
482            purpose: Purpose::AUTHENTICATION,
483            security_level: SecurityLevel::HIGH,
484            contract_bounds: None,
485            key_type,
486            data: public_key_data.into(),
487            read_only: false,
488            disabled_at: None,
489        };
490
491        // Validate that the private key matches the public key
492        assert!(identity_public_key
493            .validate_private_key_bytes(&private_key_data, Network::Testnet)
494            .unwrap());
495
496        // Test with an invalid private key
497        let invalid_private_key_bytes = [0u8; 32];
498        assert!(!identity_public_key
499            .validate_private_key_bytes(&invalid_private_key_bytes, Network::Testnet)
500            .unwrap());
501    }
502}