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