Skip to main content

primitives/types/identifiers/
peer_id.rs

1use std::ops::BitXor;
2
3use derive_more::derive::AsRef;
4use ed25519_dalek::{
5    pkcs8::{DecodePrivateKey, DecodePublicKey},
6    SigningKey,
7    Verifier,
8    VerifyingKey,
9};
10use quinn::rustls::{server::ParsedCertificate, Error as RustlsError};
11pub use rand::Rng;
12use rustls_pki_types::CertificateDer;
13use serde::{Deserialize, Serialize};
14
15#[cfg(any(test, feature = "dev"))]
16use crate::random::{CryptoRngCore, Random};
17use crate::{errors::PrimitiveError, izip_eq};
18pub const PEER_ID_LENGTH: usize = ed25519_dalek::PUBLIC_KEY_LENGTH; // 32 Bytes
19
20#[derive(Copy, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, PartialOrd, Ord, AsRef)]
21#[as_ref([u8])]
22#[repr(transparent)]
23/// A unique identifier for a peer in the network.
24/// It corresponds to the ed25519 public key of the peer in big-endian format.
25pub struct PeerId([u8; PEER_ID_LENGTH]);
26
27impl Verifier<ed25519_dalek::Signature> for PeerId {
28    fn verify(
29        &self,
30        message: &[u8],
31        signature: &ed25519_dalek::Signature,
32    ) -> Result<(), ed25519_dalek::SignatureError> {
33        let public_key =
34            VerifyingKey::try_from(*self).map_err(ed25519_dalek::SignatureError::from_source)?;
35        public_key.verify_strict(message, signature)
36    }
37}
38
39#[cfg(not(any(test, feature = "dev")))]
40impl std::fmt::Display for PeerId {
41    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42        write!(f, "{}", hex::encode(self.0))
43    }
44}
45
46#[cfg(not(any(test, feature = "dev")))]
47impl std::fmt::Debug for PeerId {
48    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49        write!(f, "{}", hex::encode(self.0))
50    }
51}
52
53#[cfg(any(test, feature = "dev"))]
54impl std::fmt::Display for PeerId {
55    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56        write!(f, "{}", &hex::encode(self.0)[0..6])
57    }
58}
59
60#[cfg(any(test, feature = "dev"))]
61impl std::fmt::Debug for PeerId {
62    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63        write!(f, "{}", &hex::encode(self.0)[0..6])
64    }
65}
66
67// ------------- Failsafe Conversions -------------- //
68
69impl<'pid> From<&'pid PeerId> for &'pid [u8; PEER_ID_LENGTH] {
70    fn from(peer_id: &'pid PeerId) -> Self {
71        &peer_id.0
72    }
73}
74
75impl From<PeerId> for [u8; PEER_ID_LENGTH] {
76    fn from(peer_id: PeerId) -> Self {
77        peer_id.0
78    }
79}
80
81impl From<&PeerId> for String {
82    fn from(peer_id: &PeerId) -> String {
83        hex::encode(peer_id.0)
84    }
85}
86
87impl From<VerifyingKey> for PeerId {
88    fn from(verifying_key: VerifyingKey) -> PeerId {
89        Self(verifying_key.to_bytes())
90    }
91}
92
93impl TryFrom<PeerId> for VerifyingKey {
94    type Error = PrimitiveError;
95
96    fn try_from(peer_id: PeerId) -> Result<VerifyingKey, PrimitiveError> {
97        VerifyingKey::from_bytes(&peer_id.0).map_err(|_| PrimitiveError::InvalidPeerId(peer_id))
98    }
99}
100
101impl From<&SigningKey> for PeerId {
102    fn from(signing_key: &SigningKey) -> PeerId {
103        Self(signing_key.verifying_key().to_bytes())
104    }
105}
106
107// ------------- Conversions -------------- //
108
109impl TryFrom<&[u8; PEER_ID_LENGTH]> for PeerId {
110    type Error = PrimitiveError;
111
112    fn try_from(bytes: &[u8; PEER_ID_LENGTH]) -> Result<Self, Self::Error> {
113        let public_key = VerifyingKey::from_bytes(bytes).map_err(|_| {
114            PrimitiveError::InvalidParameters(
115                "Could not convert it to an ed25519 public key".to_string(),
116            )
117        })?;
118        Ok(Self(public_key.to_bytes()))
119    }
120}
121
122impl TryFrom<CertificateDer<'static>> for PeerId {
123    type Error = PrimitiveError;
124
125    fn try_from(certificate: CertificateDer<'static>) -> Result<Self, Self::Error> {
126        Self::from_certificate(certificate)
127    }
128}
129
130impl TryFrom<&quinn::Connection> for PeerId {
131    type Error = PrimitiveError;
132
133    fn try_from(connection: &quinn::Connection) -> Result<Self, Self::Error> {
134        Self::from_connection(connection)
135    }
136}
137
138impl PeerId {
139    /// Construct a peer ID from raw bytes without validating Ed25519 encoding.
140    ///
141    /// Prefer `TryFrom<&[u8; 32]>` when the bytes come from untrusted input.
142    #[cfg(any(test, feature = "dev"))]
143    pub const fn from_bytes_unchecked(bytes: [u8; PEER_ID_LENGTH]) -> Self {
144        Self(bytes)
145    }
146
147    /// Construct a peer ID from the given Ed25519 public key in an rcgen keypair.
148    pub fn from_keypair(keypair: &rcgen::KeyPair) -> Result<Self, PrimitiveError> {
149        if keypair.algorithm() != &rcgen::PKCS_ED25519 {
150            return Err(PrimitiveError::InvalidParameters(
151                "Keypair is not Ed25519".to_string(),
152            ));
153        }
154        let public_key_bytes: [u8; PEER_ID_LENGTH] =
155            keypair.public_key_raw().to_vec().try_into().unwrap();
156        Ok(Self(public_key_bytes))
157    }
158
159    /// Construct a peer ID from the given secret key in DER format.
160    pub fn from_secret_key_der(bytes: &[u8]) -> Result<Self, PrimitiveError> {
161        let secret_key = SigningKey::from_pkcs8_der(bytes).map_err(|e| {
162            PrimitiveError::InvalidParameters(format!(
163                "Failed to decode secret key in DER format: {e}",
164            ))
165        })?;
166        Ok(Self(*secret_key.verifying_key().as_bytes()))
167    }
168
169    /// Construct a peer ID from the given public key in DER format.
170    pub fn from_public_key_der(bytes: &[u8]) -> Result<Self, PrimitiveError> {
171        let public_key = VerifyingKey::from_public_key_der(bytes).map_err(|e| {
172            PrimitiveError::InvalidParameters(format!(
173                "Failed to decode public key in DER format: {e}",
174            ))
175        })?;
176        Ok(Self(public_key.to_bytes()))
177    }
178
179    /// Construct a peer ID from the given Rustls certificate in DER format.
180    pub(crate) fn from_certificate(
181        certificate: CertificateDer<'static>,
182    ) -> Result<Self, PrimitiveError> {
183        let cert = ParsedCertificate::try_from(&certificate)?;
184        let spki = cert.subject_public_key_info();
185
186        // Decode the SPKI to obtain the PeerId
187        Self::from_public_key_der(spki.as_ref())
188    }
189
190    /// Retrieve the peer ID corresponding to the given quinn connection.
191    pub fn from_connection(connection: &quinn::Connection) -> Result<Self, PrimitiveError> {
192        let certificate = connection
193            .peer_identity()
194            .ok_or(RustlsError::NoCertificatesPresented)?
195            .downcast::<Vec<CertificateDer<'static>>>()
196            .map_err(|_| RustlsError::General("Peer identity didn't downcast to a certificate. Check quinn session (should be default rustls).".to_string()))?
197            .into_iter()
198            .next()
199            .ok_or(RustlsError::NoCertificatesPresented)?;
200        Self::from_certificate(certificate)
201    }
202}
203
204#[macros::op_variants(owned, borrowed, flipped_commutative)]
205impl BitXor<&PeerId> for PeerId {
206    type Output = PeerId;
207
208    fn bitxor(mut self, rhs: &Self) -> Self::Output {
209        izip_eq!(self.0.iter_mut(), rhs.0.iter()).for_each(|(a, b)| {
210            *a ^= *b;
211        });
212        self
213    }
214}
215
216#[cfg(any(test, feature = "dev"))]
217impl Random for PeerId {
218    fn random(mut rng: impl CryptoRngCore) -> Self {
219        let mut secret_key = <[u8; PEER_ID_LENGTH]>::default();
220        rng.fill_bytes(&mut secret_key);
221        let public_key = SigningKey::from_bytes(&secret_key)
222            .verifying_key()
223            .to_bytes();
224        PeerId(public_key)
225    }
226}
227
228#[cfg(test)]
229mod tests {
230    use ed25519_dalek::{pkcs8::DecodePrivateKey, Signer, SigningKey, Verifier};
231    use rand::SeedableRng;
232
233    use super::*;
234    use crate::{random::BaseRng, utils::codec::bincode_io};
235
236    const SAMPLE_CERTIFICATE_DER: &str = "3082011d3081d0a00302010202146403206e30248a32e568a26239c434d23df91131300506032b65703021311f301d06035504030c16726367656e2073656c66207369676e656420636572743020170d3735303130313030303030305a180f34303936303130313030303030305a3021311f301d06035504030c16726367656e2073656c66207369676e65642063657274302a300506032b6570032100a965eea43375c9ecff6ac3a324ee77cd3d0ceca3b1a203881bc61a16203593dea318301630140603551d11040d300b82096173796e632d6d7063300506032b657003410014ff443071b7ad0a0f540f3e5a083f5873d045f2a7302a63c171da750dea288b216beb1f599ca5db2696b7236f6d69652ab46a0845a5a6b6aa73608fb439010e";
237    const SAMPLE_SKEY_DER: &str = "3051020101300506032b657004220420c66ea2001a6b5b4e0b4ef51265c74829bb34e1952e9ed399c4158acf8df4521f812100a965eea43375c9ecff6ac3a324ee77cd3d0ceca3b1a203881bc61a16203593de";
238    const SAMPLE_PKEY_DER: &str =
239        "302a300506032b6570032100a965eea43375c9ecff6ac3a324ee77cd3d0ceca3b1a203881bc61a16203593de";
240    const SAMPLE_SKEY_BYTES: &str =
241        "c66ea2001a6b5b4e0b4ef51265c74829bb34e1952e9ed399c4158acf8df4521f";
242    const SAMPLE_PKEY_BYTES: &str =
243        "a965eea43375c9ecff6ac3a324ee77cd3d0ceca3b1a203881bc61a16203593de";
244
245    #[test]
246    fn test_happypath_peer_id_from_keys() {
247        // Load the secret key and sign a message
248        let secret_key =
249            SigningKey::from_bytes(&hex::decode(SAMPLE_SKEY_BYTES).unwrap().try_into().unwrap());
250        let message = b"Hello, world!";
251        let signature = secret_key.sign(message);
252
253        // Create a PeerId from the public key bytes and verify the signature
254        let pkey_bytes: [u8; 32] = hex::decode(SAMPLE_PKEY_BYTES).unwrap().try_into().unwrap();
255        let peer_id = PeerId::try_from(&pkey_bytes).unwrap();
256        peer_id.verify(message, &signature).unwrap();
257        assert_eq!(peer_id.as_ref(), secret_key.verifying_key().as_bytes());
258
259        // Create a PeerId from the ed25519 public key and verify the signature
260        let pkey =
261            VerifyingKey::from_public_key_der(&hex::decode(SAMPLE_PKEY_DER).unwrap()).unwrap();
262        let peer_id = PeerId::from(pkey);
263        peer_id.verify(message, &signature).unwrap();
264        assert_eq!(secret_key.verifying_key().as_bytes(), peer_id.as_ref());
265        assert_eq!(secret_key.verifying_key(), peer_id.try_into().unwrap());
266
267        // Create a PeerId from the ed25519 secret key and verify the signature
268        let skey = SigningKey::from_pkcs8_der(&hex::decode(SAMPLE_SKEY_DER).unwrap()).unwrap();
269        let peer_id = PeerId::from(&skey);
270        peer_id.verify(message, &signature).unwrap();
271        assert_eq!(peer_id.as_ref(), secret_key.verifying_key().as_bytes());
272    }
273
274    #[test]
275    fn test_happypath_peer_id_from_serialized_keys() {
276        // Load the secret key and sign a message
277        let secret_key =
278            SigningKey::from_bytes(&hex::decode(SAMPLE_SKEY_BYTES).unwrap().try_into().unwrap());
279        let message = b"Hello, serialized keys!";
280        let signature = secret_key.sign(message);
281
282        // Create a PeerId from the serialized public key in DER format and verify the signature
283        let peer_id = PeerId::from_secret_key_der(&hex::decode(SAMPLE_SKEY_DER).unwrap()).unwrap();
284        peer_id.verify(message, &signature).unwrap();
285        assert_eq!(peer_id.as_ref(), secret_key.verifying_key().as_bytes());
286
287        // Create a PeerId from the serialized secret key in DER format and verify the signature
288        let peer_id = PeerId::from_secret_key_der(&hex::decode(SAMPLE_SKEY_DER).unwrap()).unwrap();
289        peer_id.verify(message, &signature).unwrap();
290        assert_eq!(peer_id.as_ref(), secret_key.verifying_key().as_bytes());
291    }
292
293    #[test]
294    fn test_happypath_peer_id_from_certificate() {
295        // Load the secret key and sign a message
296        let secret_key =
297            SigningKey::from_bytes(&hex::decode(SAMPLE_SKEY_BYTES).unwrap().try_into().unwrap());
298        let message = b"Hello, certificate!";
299        let signature = secret_key.sign(message);
300
301        // Create a PeerId from the certificate and verify the signature
302        let certificate = CertificateDer::from(hex::decode(SAMPLE_CERTIFICATE_DER).unwrap());
303        let peer_id = PeerId::from_certificate(certificate).unwrap();
304        peer_id.verify(message, &signature).unwrap();
305        assert_eq!(peer_id.as_ref(), secret_key.verifying_key().as_bytes());
306    }
307
308    #[test]
309    fn test_happypath_two_peer_ids_cross_verify() {
310        let mut rng = BaseRng::from_seed(*b"Look Sam, the eagles are coming!");
311        let secret_key_1 = SigningKey::generate(&mut rng);
312        let secret_key_2 = SigningKey::generate(&mut rng);
313
314        let message = b"Hello, cross verify!";
315
316        let signature_1 = secret_key_1.sign(message);
317        let signature_2 = secret_key_2.sign(message);
318
319        let peer_id_1 = PeerId::from(&secret_key_1);
320        let peer_id_2 = PeerId::from(&secret_key_2);
321        assert_eq!(peer_id_1.as_ref(), secret_key_1.verifying_key().as_bytes());
322        assert_eq!(peer_id_2.as_ref(), secret_key_2.verifying_key().as_bytes());
323
324        peer_id_1.verify(message, &signature_1).unwrap();
325        peer_id_2.verify(message, &signature_2).unwrap();
326
327        peer_id_2.verify(message, &signature_1).unwrap_err();
328        peer_id_1.verify(message, &signature_2).unwrap_err();
329    }
330
331    #[test]
332    fn test_invalid_deserialized_peer_id_verify_never_panics() {
333        let mut rng = BaseRng::from_seed(*b"Look Sam, the eagles are coming!");
334        let signing_key = SigningKey::generate(&mut rng);
335        let message = b"Hello, invalid peer id!";
336        let signature = signing_key.sign(message);
337
338        // Invalid compressed Edwards-Y encoding for an Ed25519 public key.
339        let invalid_peer_id_bytes = [0xFFu8; PEER_ID_LENGTH];
340
341        fn assert_no_panic_verify_error<E>(
342            decoded: Result<PeerId, E>,
343            message: &[u8],
344            signature: &ed25519_dalek::Signature,
345        ) {
346            // If deserialization is tightened later, rejecting invalid bytes directly is also
347            // acceptable for this test.
348            if let Ok(invalid_peer_id) = decoded {
349                let verify_result =
350                    std::panic::catch_unwind(|| invalid_peer_id.verify(message, signature));
351                assert!(
352                    verify_result.is_ok(),
353                    "verify() must return an error instead of panicking for invalid PeerId bytes"
354                );
355                assert!(verify_result.unwrap().is_err());
356            }
357        }
358
359        let bincode_encoded = bincode_io::serialize(&invalid_peer_id_bytes).unwrap();
360        let bincode_decoded: Result<PeerId, _> = bincode_io::deserialize(&bincode_encoded);
361        assert_no_panic_verify_error(bincode_decoded, message, &signature);
362    }
363}