libp2p_tls/
certificate.rs

1// Copyright 2021 Parity Technologies (UK) Ltd.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the "Software"),
5// to deal in the Software without restriction, including without limitation
6// the rights to use, copy, modify, merge, publish, distribute, sublicense,
7// and/or sell copies of the Software, and to permit persons to whom the
8// Software is furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19// DEALINGS IN THE SOFTWARE.
20
21//! X.509 certificate handling for libp2p
22//!
23//! This module handles generation, signing, and verification of certificates.
24
25use std::sync::Arc;
26
27use libp2p_identity as identity;
28use libp2p_identity::PeerId;
29use x509_parser::{prelude::*, signature_algorithm::SignatureAlgorithm};
30
31/// The libp2p Public Key Extension is a X.509 extension
32/// with the Object Identifier 1.3.6.1.4.1.53594.1.1,
33/// allocated by IANA to the libp2p project at Protocol Labs.
34const P2P_EXT_OID: [u64; 9] = [1, 3, 6, 1, 4, 1, 53594, 1, 1];
35
36/// The peer signs the concatenation of the string `libp2p-tls-handshake:`
37/// and the public key that it used to generate the certificate carrying
38/// the libp2p Public Key Extension, using its private host key.
39/// This signature provides cryptographic proof that the peer was
40/// in possession of the private host key at the time the certificate was signed.
41const P2P_SIGNING_PREFIX: [u8; 21] = *b"libp2p-tls-handshake:";
42
43// Certificates MUST use the NamedCurve encoding for elliptic curve parameters.
44// Similarly, hash functions with an output length less than 256 bits MUST NOT be used.
45static P2P_SIGNATURE_ALGORITHM: &rcgen::SignatureAlgorithm = &rcgen::PKCS_ECDSA_P256_SHA256;
46
47#[derive(Debug)]
48pub(crate) struct AlwaysResolvesCert(Arc<rustls::sign::CertifiedKey>);
49
50impl AlwaysResolvesCert {
51    pub(crate) fn new(
52        cert: rustls::pki_types::CertificateDer<'static>,
53        key: &rustls::pki_types::PrivateKeyDer<'_>,
54    ) -> Result<Self, rustls::Error> {
55        let certified_key = rustls::sign::CertifiedKey::new(
56            vec![cert],
57            rustls::crypto::ring::sign::any_ecdsa_type(key)?,
58        );
59        Ok(Self(Arc::new(certified_key)))
60    }
61}
62
63impl rustls::client::ResolvesClientCert for AlwaysResolvesCert {
64    fn resolve(
65        &self,
66        _root_hint_subjects: &[&[u8]],
67        _sigschemes: &[rustls::SignatureScheme],
68    ) -> Option<Arc<rustls::sign::CertifiedKey>> {
69        Some(Arc::clone(&self.0))
70    }
71
72    fn has_certs(&self) -> bool {
73        true
74    }
75}
76
77impl rustls::server::ResolvesServerCert for AlwaysResolvesCert {
78    fn resolve(
79        &self,
80        _client_hello: rustls::server::ClientHello<'_>,
81    ) -> Option<Arc<rustls::sign::CertifiedKey>> {
82        Some(Arc::clone(&self.0))
83    }
84}
85
86/// Generates a self-signed TLS certificate that includes a libp2p-specific
87/// certificate extension containing the public key of the given keypair.
88pub fn generate(
89    identity_keypair: &identity::Keypair,
90) -> Result<
91    (
92        rustls::pki_types::CertificateDer<'static>,
93        rustls::pki_types::PrivateKeyDer<'static>,
94    ),
95    GenError,
96> {
97    // Keypair used to sign the certificate.
98    // SHOULD NOT be related to the host's key.
99    // Endpoints MAY generate a new key and certificate
100    // for every connection attempt, or they MAY reuse the same key
101    // and certificate for multiple connections.
102    let certificate_keypair = rcgen::KeyPair::generate_for(P2P_SIGNATURE_ALGORITHM)?;
103    let rustls_key = rustls::pki_types::PrivateKeyDer::from(
104        rustls::pki_types::PrivatePkcs8KeyDer::from(certificate_keypair.serialize_der()),
105    );
106
107    let certificate = {
108        let mut params = rcgen::CertificateParams::new(vec![])?;
109        params.distinguished_name = rcgen::DistinguishedName::new();
110        params.custom_extensions.push(make_libp2p_extension(
111            identity_keypair,
112            &certificate_keypair,
113        )?);
114        params.self_signed(&certificate_keypair)?
115    };
116
117    Ok((certificate.into(), rustls_key))
118}
119
120/// Attempts to parse the provided bytes as a [`P2pCertificate`].
121///
122/// For this to succeed, the certificate must contain the specified extension and the signature must
123/// match the embedded public key.
124pub fn parse<'a>(
125    certificate: &'a rustls::pki_types::CertificateDer<'a>,
126) -> Result<P2pCertificate<'a>, ParseError> {
127    let certificate = parse_unverified(certificate.as_ref())?;
128
129    certificate.verify()?;
130
131    Ok(certificate)
132}
133
134/// An X.509 certificate with a libp2p-specific extension
135/// is used to secure libp2p connections.
136#[derive(Debug)]
137pub struct P2pCertificate<'a> {
138    certificate: X509Certificate<'a>,
139    /// This is a specific libp2p Public Key Extension with two values:
140    /// * the public host key
141    /// * a signature performed using the private host key
142    extension: P2pExtension,
143}
144
145/// The contents of the specific libp2p extension, containing the public host key
146/// and a signature performed using the private host key.
147#[derive(Debug)]
148pub struct P2pExtension {
149    public_key: identity::PublicKey,
150    /// This signature provides cryptographic proof that the peer was
151    /// in possession of the private host key at the time the certificate was signed.
152    signature: Vec<u8>,
153}
154
155#[derive(Debug, thiserror::Error)]
156#[error(transparent)]
157pub struct GenError(#[from] rcgen::Error);
158
159#[derive(Debug, thiserror::Error)]
160#[error(transparent)]
161pub struct ParseError(#[from] pub(crate) webpki::Error);
162
163#[derive(Debug, thiserror::Error)]
164#[error(transparent)]
165pub struct VerificationError(#[from] pub(crate) webpki::Error);
166
167/// Internal function that only parses but does not verify the certificate.
168///
169/// Useful for testing but unsuitable for production.
170fn parse_unverified(der_input: &[u8]) -> Result<P2pCertificate, webpki::Error> {
171    let x509 = X509Certificate::from_der(der_input)
172        .map(|(_rest_input, x509)| x509)
173        .map_err(|_| webpki::Error::BadDer)?;
174
175    let p2p_ext_oid = der_parser::oid::Oid::from(&P2P_EXT_OID)
176        .expect("This is a valid OID of p2p extension; qed");
177
178    let mut libp2p_extension = None;
179
180    for ext in x509.extensions() {
181        let oid = &ext.oid;
182        if oid == &p2p_ext_oid && libp2p_extension.is_some() {
183            // The extension was already parsed
184            return Err(webpki::Error::BadDer);
185        }
186
187        if oid == &p2p_ext_oid {
188            // The public host key and the signature are ANS.1-encoded
189            // into the SignedKey data structure, which is carried
190            // in the libp2p Public Key Extension.
191            // SignedKey ::= SEQUENCE {
192            //    publicKey OCTET STRING,
193            //    signature OCTET STRING
194            // }
195            let (public_key, signature): (Vec<u8>, Vec<u8>) =
196                yasna::decode_der(ext.value).map_err(|_| webpki::Error::ExtensionValueInvalid)?;
197            // The publicKey field of SignedKey contains the public host key
198            // of the endpoint, encoded using the following protobuf:
199            // enum KeyType {
200            //    RSA = 0;
201            //    Ed25519 = 1;
202            //    Secp256k1 = 2;
203            //    ECDSA = 3;
204            // }
205            // message PublicKey {
206            //    required KeyType Type = 1;
207            //    required bytes Data = 2;
208            // }
209            let public_key = identity::PublicKey::try_decode_protobuf(&public_key)
210                .map_err(|_| webpki::Error::UnknownIssuer)?;
211            let ext = P2pExtension {
212                public_key,
213                signature,
214            };
215            libp2p_extension = Some(ext);
216            continue;
217        }
218
219        if ext.critical {
220            // Endpoints MUST abort the connection attempt if the certificate
221            // contains critical extensions that the endpoint does not understand.
222            return Err(webpki::Error::UnsupportedCriticalExtension);
223        }
224
225        // Implementations MUST ignore non-critical extensions with unknown OIDs.
226    }
227
228    // The certificate MUST contain the libp2p Public Key Extension.
229    // If this extension is missing, endpoints MUST abort the connection attempt.
230    let extension = libp2p_extension.ok_or(webpki::Error::BadDer)?;
231
232    let certificate = P2pCertificate {
233        certificate: x509,
234        extension,
235    };
236
237    Ok(certificate)
238}
239
240fn make_libp2p_extension(
241    identity_keypair: &identity::Keypair,
242    certificate_keypair: &rcgen::KeyPair,
243) -> Result<rcgen::CustomExtension, rcgen::Error> {
244    // The peer signs the concatenation of the string `libp2p-tls-handshake:`
245    // and the public key that it used to generate the certificate carrying
246    // the libp2p Public Key Extension, using its private host key.
247    let signature = {
248        let mut msg = vec![];
249        msg.extend(P2P_SIGNING_PREFIX);
250        msg.extend(certificate_keypair.public_key_der());
251
252        identity_keypair
253            .sign(&msg)
254            .map_err(|_| rcgen::Error::RingUnspecified)?
255    };
256
257    // The public host key and the signature are ANS.1-encoded
258    // into the SignedKey data structure, which is carried
259    // in the libp2p Public Key Extension.
260    // SignedKey ::= SEQUENCE {
261    //    publicKey OCTET STRING,
262    //    signature OCTET STRING
263    // }
264    let extension_content = {
265        let serialized_pubkey = identity_keypair.public().encode_protobuf();
266        yasna::encode_der(&(serialized_pubkey, signature))
267    };
268
269    // This extension MAY be marked critical.
270    let mut ext = rcgen::CustomExtension::from_oid_content(&P2P_EXT_OID, extension_content);
271    ext.set_criticality(true);
272
273    Ok(ext)
274}
275
276impl P2pCertificate<'_> {
277    /// The [`PeerId`] of the remote peer.
278    pub fn peer_id(&self) -> PeerId {
279        self.extension.public_key.to_peer_id()
280    }
281
282    /// Verify the `signature` of the `message` signed by the private key corresponding to the
283    /// public key stored in the certificate.
284    pub fn verify_signature(
285        &self,
286        signature_scheme: rustls::SignatureScheme,
287        message: &[u8],
288        signature: &[u8],
289    ) -> Result<(), VerificationError> {
290        let pk = self.public_key(signature_scheme)?;
291        pk.verify(message, signature)
292            .map_err(|_| webpki::Error::InvalidSignatureForPublicKey)?;
293
294        Ok(())
295    }
296
297    /// Get a [`ring::signature::UnparsedPublicKey`] for this `signature_scheme`.
298    /// Return `Error` if the `signature_scheme` does not match the public key signature
299    /// and hashing algorithm or if the `signature_scheme` is not supported.
300    fn public_key(
301        &self,
302        signature_scheme: rustls::SignatureScheme,
303    ) -> Result<ring::signature::UnparsedPublicKey<&[u8]>, webpki::Error> {
304        use ring::signature;
305        use rustls::SignatureScheme::*;
306
307        let current_signature_scheme = self.signature_scheme()?;
308        if signature_scheme != current_signature_scheme {
309            // This certificate was signed with a different signature scheme
310            return Err(webpki::Error::UnsupportedSignatureAlgorithmForPublicKey);
311        }
312
313        let verification_algorithm: &dyn signature::VerificationAlgorithm = match signature_scheme {
314            RSA_PKCS1_SHA256 => &signature::RSA_PKCS1_2048_8192_SHA256,
315            RSA_PKCS1_SHA384 => &signature::RSA_PKCS1_2048_8192_SHA384,
316            RSA_PKCS1_SHA512 => &signature::RSA_PKCS1_2048_8192_SHA512,
317            ECDSA_NISTP256_SHA256 => &signature::ECDSA_P256_SHA256_ASN1,
318            ECDSA_NISTP384_SHA384 => &signature::ECDSA_P384_SHA384_ASN1,
319            ECDSA_NISTP521_SHA512 => {
320                // See https://github.com/briansmith/ring/issues/824
321                return Err(webpki::Error::UnsupportedSignatureAlgorithm);
322            }
323            RSA_PSS_SHA256 => &signature::RSA_PSS_2048_8192_SHA256,
324            RSA_PSS_SHA384 => &signature::RSA_PSS_2048_8192_SHA384,
325            RSA_PSS_SHA512 => &signature::RSA_PSS_2048_8192_SHA512,
326            ED25519 => &signature::ED25519,
327            ED448 => {
328                // See https://github.com/briansmith/ring/issues/463
329                return Err(webpki::Error::UnsupportedSignatureAlgorithm);
330            }
331            // Similarly, hash functions with an output length less than 256 bits
332            // MUST NOT be used, due to the possibility of collision attacks.
333            // In particular, MD5 and SHA1 MUST NOT be used.
334            RSA_PKCS1_SHA1 => return Err(webpki::Error::UnsupportedSignatureAlgorithm),
335            ECDSA_SHA1_Legacy => return Err(webpki::Error::UnsupportedSignatureAlgorithm),
336            _ => return Err(webpki::Error::UnsupportedSignatureAlgorithm),
337        };
338        let spki = &self.certificate.tbs_certificate.subject_pki;
339        let key = signature::UnparsedPublicKey::new(
340            verification_algorithm,
341            spki.subject_public_key.as_ref(),
342        );
343
344        Ok(key)
345    }
346
347    /// This method validates the certificate according to libp2p TLS 1.3 specs.
348    /// The certificate MUST:
349    /// 1. be valid at the time it is received by the peer;
350    /// 2. use the NamedCurve encoding;
351    /// 3. use hash functions with an output length not less than 256 bits;
352    /// 4. be self signed;
353    /// 5. contain a valid signature in the specific libp2p extension.
354    fn verify(&self) -> Result<(), webpki::Error> {
355        use webpki::Error;
356        // The certificate MUST have NotBefore and NotAfter fields set
357        // such that the certificate is valid at the time it is received by the peer.
358        if !self.certificate.validity().is_valid() {
359            return Err(Error::InvalidCertValidity);
360        }
361
362        // Certificates MUST use the NamedCurve encoding for elliptic curve parameters.
363        // Similarly, hash functions with an output length less than 256 bits
364        // MUST NOT be used, due to the possibility of collision attacks.
365        // In particular, MD5 and SHA1 MUST NOT be used.
366        // Endpoints MUST abort the connection attempt if it is not used.
367        let signature_scheme = self.signature_scheme()?;
368        // Endpoints MUST abort the connection attempt if the certificate’s
369        // self-signature is not valid.
370        let raw_certificate = self.certificate.tbs_certificate.as_ref();
371        let signature = self.certificate.signature_value.as_ref();
372        // check if self signed
373        self.verify_signature(signature_scheme, raw_certificate, signature)
374            .map_err(|_| Error::SignatureAlgorithmMismatch)?;
375
376        let subject_pki = self.certificate.public_key().raw;
377
378        // The peer signs the concatenation of the string `libp2p-tls-handshake:`
379        // and the public key that it used to generate the certificate carrying
380        // the libp2p Public Key Extension, using its private host key.
381        let mut msg = vec![];
382        msg.extend(P2P_SIGNING_PREFIX);
383        msg.extend(subject_pki);
384
385        // This signature provides cryptographic proof that the peer was in possession
386        // of the private host key at the time the certificate was signed.
387        // Peers MUST verify the signature, and abort the connection attempt
388        // if signature verification fails.
389        let user_owns_sk = self
390            .extension
391            .public_key
392            .verify(&msg, &self.extension.signature);
393        if !user_owns_sk {
394            return Err(Error::UnknownIssuer);
395        }
396
397        Ok(())
398    }
399
400    /// Return the signature scheme corresponding to [`AlgorithmIdentifier`]s
401    /// of `subject_pki` and `signature_algorithm`
402    /// according to <https://www.rfc-editor.org/rfc/rfc8446.html#section-4.2.3>.
403    fn signature_scheme(&self) -> Result<rustls::SignatureScheme, webpki::Error> {
404        // Certificates MUST use the NamedCurve encoding for elliptic curve parameters.
405        // Endpoints MUST abort the connection attempt if it is not used.
406        use oid_registry::*;
407        use rustls::SignatureScheme::*;
408
409        let signature_algorithm = &self.certificate.signature_algorithm;
410        let pki_algorithm = &self.certificate.tbs_certificate.subject_pki.algorithm;
411
412        if pki_algorithm.algorithm == OID_PKCS1_RSAENCRYPTION {
413            if signature_algorithm.algorithm == OID_PKCS1_SHA256WITHRSA {
414                return Ok(RSA_PKCS1_SHA256);
415            }
416            if signature_algorithm.algorithm == OID_PKCS1_SHA384WITHRSA {
417                return Ok(RSA_PKCS1_SHA384);
418            }
419            if signature_algorithm.algorithm == OID_PKCS1_SHA512WITHRSA {
420                return Ok(RSA_PKCS1_SHA512);
421            }
422            if signature_algorithm.algorithm == OID_PKCS1_RSASSAPSS {
423                // According to https://datatracker.ietf.org/doc/html/rfc4055#section-3.1:
424                // Inside of params there should be a sequence of:
425                // - Hash Algorithm
426                // - Mask Algorithm
427                // - Salt Length
428                // - Trailer Field
429
430                // We are interested in Hash Algorithm only
431
432                if let Ok(SignatureAlgorithm::RSASSA_PSS(params)) =
433                    SignatureAlgorithm::try_from(signature_algorithm)
434                {
435                    let hash_oid = params.hash_algorithm_oid();
436                    if hash_oid == &OID_NIST_HASH_SHA256 {
437                        return Ok(RSA_PSS_SHA256);
438                    }
439                    if hash_oid == &OID_NIST_HASH_SHA384 {
440                        return Ok(RSA_PSS_SHA384);
441                    }
442                    if hash_oid == &OID_NIST_HASH_SHA512 {
443                        return Ok(RSA_PSS_SHA512);
444                    }
445                }
446
447                // Default hash algo is SHA-1, however:
448                // In particular, MD5 and SHA1 MUST NOT be used.
449                return Err(webpki::Error::UnsupportedSignatureAlgorithm);
450            }
451        }
452
453        if pki_algorithm.algorithm == OID_KEY_TYPE_EC_PUBLIC_KEY {
454            let signature_param = pki_algorithm
455                .parameters
456                .as_ref()
457                .ok_or(webpki::Error::BadDer)?
458                .as_oid()
459                .map_err(|_| webpki::Error::BadDer)?;
460            if signature_param == OID_EC_P256
461                && signature_algorithm.algorithm == OID_SIG_ECDSA_WITH_SHA256
462            {
463                return Ok(ECDSA_NISTP256_SHA256);
464            }
465            if signature_param == OID_NIST_EC_P384
466                && signature_algorithm.algorithm == OID_SIG_ECDSA_WITH_SHA384
467            {
468                return Ok(ECDSA_NISTP384_SHA384);
469            }
470            if signature_param == OID_NIST_EC_P521
471                && signature_algorithm.algorithm == OID_SIG_ECDSA_WITH_SHA512
472            {
473                return Ok(ECDSA_NISTP521_SHA512);
474            }
475            return Err(webpki::Error::UnsupportedSignatureAlgorithm);
476        }
477
478        if signature_algorithm.algorithm == OID_SIG_ED25519 {
479            return Ok(ED25519);
480        }
481        if signature_algorithm.algorithm == OID_SIG_ED448 {
482            return Ok(ED448);
483        }
484
485        Err(webpki::Error::UnsupportedSignatureAlgorithm)
486    }
487}
488
489#[cfg(test)]
490mod tests {
491    use hex_literal::hex;
492
493    use super::*;
494
495    #[test]
496    fn sanity_check() {
497        let keypair = identity::Keypair::generate_ed25519();
498
499        let (cert, _) = generate(&keypair).unwrap();
500        let parsed_cert = parse(&cert).unwrap();
501
502        assert!(parsed_cert.verify().is_ok());
503        assert_eq!(keypair.public(), parsed_cert.extension.public_key);
504    }
505
506    macro_rules! check_cert {
507        ($name:ident, $path:literal, $scheme:path) => {
508            #[test]
509            fn $name() {
510                let cert: &[u8] = include_bytes!($path);
511
512                let cert = parse_unverified(cert).unwrap();
513                assert!(cert.verify().is_err()); // Because p2p extension
514                                                 // was not signed with the private key
515                                                 // of the certificate.
516                assert_eq!(cert.signature_scheme(), Ok($scheme));
517            }
518        };
519    }
520
521    check_cert! {ed448, "./test_assets/ed448.der", rustls::SignatureScheme::ED448}
522    check_cert! {ed25519, "./test_assets/ed25519.der", rustls::SignatureScheme::ED25519}
523    check_cert! {rsa_pkcs1_sha256, "./test_assets/rsa_pkcs1_sha256.der", rustls::SignatureScheme::RSA_PKCS1_SHA256}
524    check_cert! {rsa_pkcs1_sha384, "./test_assets/rsa_pkcs1_sha384.der", rustls::SignatureScheme::RSA_PKCS1_SHA384}
525    check_cert! {rsa_pkcs1_sha512, "./test_assets/rsa_pkcs1_sha512.der", rustls::SignatureScheme::RSA_PKCS1_SHA512}
526    check_cert! {nistp256_sha256, "./test_assets/nistp256_sha256.der", rustls::SignatureScheme::ECDSA_NISTP256_SHA256}
527    check_cert! {nistp384_sha384, "./test_assets/nistp384_sha384.der", rustls::SignatureScheme::ECDSA_NISTP384_SHA384}
528    check_cert! {nistp521_sha512, "./test_assets/nistp521_sha512.der", rustls::SignatureScheme::ECDSA_NISTP521_SHA512}
529
530    #[test]
531    fn rsa_pss_sha384() {
532        let cert = rustls::pki_types::CertificateDer::from(
533            include_bytes!("./test_assets/rsa_pss_sha384.der").to_vec(),
534        );
535
536        let cert = parse(&cert).unwrap();
537
538        assert_eq!(
539            cert.signature_scheme(),
540            Ok(rustls::SignatureScheme::RSA_PSS_SHA384)
541        );
542    }
543
544    #[test]
545    fn nistp384_sha256() {
546        let cert: &[u8] = include_bytes!("./test_assets/nistp384_sha256.der");
547
548        let cert = parse_unverified(cert).unwrap();
549
550        assert!(cert.signature_scheme().is_err());
551    }
552
553    #[test]
554    fn can_parse_certificate_with_ed25519_keypair() {
555        let certificate = rustls::pki_types::CertificateDer::from(hex!("308201773082011ea003020102020900f5bd0debaa597f52300a06082a8648ce3d04030230003020170d3735303130313030303030305a180f34303936303130313030303030305a30003059301306072a8648ce3d020106082a8648ce3d030107034200046bf9871220d71dcb3483ecdfcbfcc7c103f8509d0974b3c18ab1f1be1302d643103a08f7a7722c1b247ba3876fe2c59e26526f479d7718a85202ddbe47562358a37f307d307b060a2b0601040183a25a01010101ff046a30680424080112207fda21856709c5ae12fd6e8450623f15f11955d384212b89f56e7e136d2e17280440aaa6bffabe91b6f30c35e3aa4f94b1188fed96b0ffdd393f4c58c1c047854120e674ce64c788406d1c2c4b116581fd7411b309881c3c7f20b46e54c7e6fe7f0f300a06082a8648ce3d040302034700304402207d1a1dbd2bda235ff2ec87daf006f9b04ba076a5a5530180cd9c2e8f6399e09d0220458527178c7e77024601dbb1b256593e9b96d961b96349d1f560114f61a87595").to_vec());
556
557        let peer_id = parse(&certificate).unwrap().peer_id();
558
559        assert_eq!(
560            "12D3KooWJRSrypvnpHgc6ZAgyCni4KcSmbV7uGRaMw5LgMKT18fq"
561                .parse::<PeerId>()
562                .unwrap(),
563            peer_id
564        );
565    }
566
567    #[test]
568    fn fails_to_parse_bad_certificate_with_ed25519_keypair() {
569        let certificate = rustls::pki_types::CertificateDer::from(hex!("308201773082011da003020102020830a73c5d896a1109300a06082a8648ce3d04030230003020170d3735303130313030303030305a180f34303936303130313030303030305a30003059301306072a8648ce3d020106082a8648ce3d03010703420004bbe62df9a7c1c46b7f1f21d556deec5382a36df146fb29c7f1240e60d7d5328570e3b71d99602b77a65c9b3655f62837f8d66b59f1763b8c9beba3be07778043a37f307d307b060a2b0601040183a25a01010101ff046a3068042408011220ec8094573afb9728088860864f7bcea2d4fd412fef09a8e2d24d482377c20db60440ecabae8354afa2f0af4b8d2ad871e865cb5a7c0c8d3dbdbf42de577f92461a0ebb0a28703e33581af7d2a4f2270fc37aec6261fcc95f8af08f3f4806581c730a300a06082a8648ce3d040302034800304502202dfb17a6fa0f94ee0e2e6a3b9fb6e986f311dee27392058016464bd130930a61022100ba4b937a11c8d3172b81e7cd04aedb79b978c4379c2b5b24d565dd5d67d3cb3c").to_vec());
570
571        let error = parse(&certificate).unwrap_err();
572
573        assert_eq!(format!("{error}"), "UnknownIssuer");
574    }
575
576    #[test]
577    fn can_parse_certificate_with_ecdsa_keypair() {
578        let certificate = rustls::pki_types::CertificateDer::from(hex!("308201c030820166a003020102020900eaf419a6e3edb4a6300a06082a8648ce3d04030230003020170d3735303130313030303030305a180f34303936303130313030303030305a30003059301306072a8648ce3d020106082a8648ce3d030107034200048dbf1116c7c608d6d5292bd826c3feb53483a89fce434bf64538a359c8e07538ff71f6766239be6a146dcc1a5f3bb934bcd4ae2ae1d4da28ac68b4a20593f06ba381c63081c33081c0060a2b0601040183a25a01010101ff0481ae3081ab045f0803125b3059301306072a8648ce3d020106082a8648ce3d0301070342000484b93fa456a74bd0153919f036db7bc63c802f055bc7023395d0203de718ee0fc7b570b767cdd858aca6c7c4113ff002e78bd2138ac1a3b26dde3519e06979ad04483046022100bc84014cea5a41feabdf4c161096564b9ccf4b62fbef4fe1cd382c84e11101780221009204f086a84cb8ed8a9ddd7868dc90c792ee434adf62c66f99a08a5eba11615b300a06082a8648ce3d0403020348003045022054b437be9a2edf591312d68ff24bf91367ad4143f76cf80b5658f232ade820da022100e23b48de9df9c25d4c83ddddf75d2676f0b9318ee2a6c88a736d85eab94a912f").to_vec());
579
580        let peer_id = parse(&certificate).unwrap().peer_id();
581
582        assert_eq!(
583            "QmZcrvr3r4S3QvwFdae3c2EWTfo792Y14UpzCZurhmiWeX"
584                .parse::<PeerId>()
585                .unwrap(),
586            peer_id
587        );
588    }
589
590    #[test]
591    fn can_parse_certificate_with_secp256k1_keypair() {
592        let certificate = rustls::pki_types::CertificateDer::from(hex!("3082018230820128a003020102020900f3b305f55622cfdf300a06082a8648ce3d04030230003020170d3735303130313030303030305a180f34303936303130313030303030305a30003059301306072a8648ce3d020106082a8648ce3d0301070342000458f7e9581748ff9bdd933b655cc0e5552a1248f840658cc221dec2186b5a2fe4641b86ab7590a3422cdbb1000cf97662f27e5910d7569f22feed8829c8b52e0fa38188308185308182060a2b0601040183a25a01010101ff0471306f042508021221026b053094d1112bce799dc8026040ae6d4eb574157929f1598172061f753d9b1b04463044022040712707e97794c478d93989aaa28ae1f71c03af524a8a4bd2d98424948a782302207b61b7f074b696a25fb9e0059141a811cccc4cc28042d9301b9b2a4015e87470300a06082a8648ce3d04030203480030450220143ae4d86fdc8675d2480bb6912eca5e39165df7f572d836aa2f2d6acfab13f8022100831d1979a98f0c4a6fb5069ca374de92f1a1205c962a6d90ad3d7554cb7d9df4").to_vec());
593
594        let peer_id = parse(&certificate).unwrap().peer_id();
595
596        assert_eq!(
597            "16Uiu2HAm2dSCBFxuge46aEt7U1oejtYuBUZXxASHqmcfVmk4gsbx"
598                .parse::<PeerId>()
599                .unwrap(),
600            peer_id
601        );
602    }
603}