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(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.alg = P2P_SIGNATURE_ALGORITHM;
115        params.key_pair = Some(certificate_keypair);
116        rcgen::Certificate::from_params(params)?
117    };
118
119    let rustls_certificate = rustls::pki_types::CertificateDer::from(certificate.serialize_der()?);
120
121    Ok((rustls_certificate, rustls_key))
122}
123
124/// Attempts to parse the provided bytes as a [`P2pCertificate`].
125///
126/// For this to succeed, the certificate must contain the specified extension and the signature must
127/// match the embedded public key.
128pub fn parse<'a>(
129    certificate: &'a rustls::pki_types::CertificateDer<'a>,
130) -> Result<P2pCertificate<'a>, ParseError> {
131    let certificate = parse_unverified(certificate.as_ref())?;
132
133    certificate.verify()?;
134
135    Ok(certificate)
136}
137
138/// An X.509 certificate with a libp2p-specific extension
139/// is used to secure libp2p connections.
140#[derive(Debug)]
141pub struct P2pCertificate<'a> {
142    certificate: X509Certificate<'a>,
143    /// This is a specific libp2p Public Key Extension with two values:
144    /// * the public host key
145    /// * a signature performed using the private host key
146    extension: P2pExtension,
147}
148
149/// The contents of the specific libp2p extension, containing the public host key
150/// and a signature performed using the private host key.
151#[derive(Debug)]
152pub struct P2pExtension {
153    public_key: identity::PublicKey,
154    /// This signature provides cryptographic proof that the peer was
155    /// in possession of the private host key at the time the certificate was signed.
156    signature: Vec<u8>,
157}
158
159#[derive(Debug, thiserror::Error)]
160#[error(transparent)]
161pub struct GenError(#[from] rcgen::RcgenError);
162
163#[derive(Debug, thiserror::Error)]
164#[error(transparent)]
165pub struct ParseError(#[from] pub(crate) webpki::Error);
166
167#[derive(Debug, thiserror::Error)]
168#[error(transparent)]
169pub struct VerificationError(#[from] pub(crate) webpki::Error);
170
171/// Internal function that only parses but does not verify the certificate.
172///
173/// Useful for testing but unsuitable for production.
174fn parse_unverified(der_input: &[u8]) -> Result<P2pCertificate, webpki::Error> {
175    let x509 = X509Certificate::from_der(der_input)
176        .map(|(_rest_input, x509)| x509)
177        .map_err(|_| webpki::Error::BadDer)?;
178
179    let p2p_ext_oid = der_parser::oid::Oid::from(&P2P_EXT_OID)
180        .expect("This is a valid OID of p2p extension; qed");
181
182    let mut libp2p_extension = None;
183
184    for ext in x509.extensions() {
185        let oid = &ext.oid;
186        if oid == &p2p_ext_oid && libp2p_extension.is_some() {
187            // The extension was already parsed
188            return Err(webpki::Error::BadDer);
189        }
190
191        if oid == &p2p_ext_oid {
192            // The public host key and the signature are ANS.1-encoded
193            // into the SignedKey data structure, which is carried
194            // in the libp2p Public Key Extension.
195            // SignedKey ::= SEQUENCE {
196            //    publicKey OCTET STRING,
197            //    signature OCTET STRING
198            // }
199            let (public_key, signature): (Vec<u8>, Vec<u8>) =
200                yasna::decode_der(ext.value).map_err(|_| webpki::Error::ExtensionValueInvalid)?;
201            // The publicKey field of SignedKey contains the public host key
202            // of the endpoint, encoded using the following protobuf:
203            // enum KeyType {
204            //    RSA = 0;
205            //    Ed25519 = 1;
206            //    Secp256k1 = 2;
207            //    ECDSA = 3;
208            // }
209            // message PublicKey {
210            //    required KeyType Type = 1;
211            //    required bytes Data = 2;
212            // }
213            let public_key = identity::PublicKey::try_decode_protobuf(&public_key)
214                .map_err(|_| webpki::Error::UnknownIssuer)?;
215            let ext = P2pExtension {
216                public_key,
217                signature,
218            };
219            libp2p_extension = Some(ext);
220            continue;
221        }
222
223        if ext.critical {
224            // Endpoints MUST abort the connection attempt if the certificate
225            // contains critical extensions that the endpoint does not understand.
226            return Err(webpki::Error::UnsupportedCriticalExtension);
227        }
228
229        // Implementations MUST ignore non-critical extensions with unknown OIDs.
230    }
231
232    // The certificate MUST contain the libp2p Public Key Extension.
233    // If this extension is missing, endpoints MUST abort the connection attempt.
234    let extension = libp2p_extension.ok_or(webpki::Error::BadDer)?;
235
236    let certificate = P2pCertificate {
237        certificate: x509,
238        extension,
239    };
240
241    Ok(certificate)
242}
243
244fn make_libp2p_extension(
245    identity_keypair: &identity::Keypair,
246    certificate_keypair: &rcgen::KeyPair,
247) -> Result<rcgen::CustomExtension, rcgen::RcgenError> {
248    // The peer signs the concatenation of the string `libp2p-tls-handshake:`
249    // and the public key that it used to generate the certificate carrying
250    // the libp2p Public Key Extension, using its private host key.
251    let signature = {
252        let mut msg = vec![];
253        msg.extend(P2P_SIGNING_PREFIX);
254        msg.extend(certificate_keypair.public_key_der());
255
256        identity_keypair
257            .sign(&msg)
258            .map_err(|_| rcgen::RcgenError::RingUnspecified)?
259    };
260
261    // The public host key and the signature are ANS.1-encoded
262    // into the SignedKey data structure, which is carried
263    // in the libp2p Public Key Extension.
264    // SignedKey ::= SEQUENCE {
265    //    publicKey OCTET STRING,
266    //    signature OCTET STRING
267    // }
268    let extension_content = {
269        let serialized_pubkey = identity_keypair.public().encode_protobuf();
270        yasna::encode_der(&(serialized_pubkey, signature))
271    };
272
273    // This extension MAY be marked critical.
274    let mut ext = rcgen::CustomExtension::from_oid_content(&P2P_EXT_OID, extension_content);
275    ext.set_criticality(true);
276
277    Ok(ext)
278}
279
280impl P2pCertificate<'_> {
281    /// The [`PeerId`] of the remote peer.
282    pub fn peer_id(&self) -> PeerId {
283        self.extension.public_key.to_peer_id()
284    }
285
286    /// Verify the `signature` of the `message` signed by the private key corresponding to the
287    /// public key stored in the certificate.
288    pub fn verify_signature(
289        &self,
290        signature_scheme: rustls::SignatureScheme,
291        message: &[u8],
292        signature: &[u8],
293    ) -> Result<(), VerificationError> {
294        let pk = self.public_key(signature_scheme)?;
295        pk.verify(message, signature)
296            .map_err(|_| webpki::Error::InvalidSignatureForPublicKey)?;
297
298        Ok(())
299    }
300
301    /// Get a [`ring::signature::UnparsedPublicKey`] for this `signature_scheme`.
302    /// Return `Error` if the `signature_scheme` does not match the public key signature
303    /// and hashing algorithm or if the `signature_scheme` is not supported.
304    fn public_key(
305        &self,
306        signature_scheme: rustls::SignatureScheme,
307    ) -> Result<ring::signature::UnparsedPublicKey<&[u8]>, webpki::Error> {
308        use ring::signature;
309        use rustls::SignatureScheme::*;
310
311        let current_signature_scheme = self.signature_scheme()?;
312        if signature_scheme != current_signature_scheme {
313            // This certificate was signed with a different signature scheme
314            return Err(webpki::Error::UnsupportedSignatureAlgorithmForPublicKey);
315        }
316
317        let verification_algorithm: &dyn signature::VerificationAlgorithm = match signature_scheme {
318            RSA_PKCS1_SHA256 => &signature::RSA_PKCS1_2048_8192_SHA256,
319            RSA_PKCS1_SHA384 => &signature::RSA_PKCS1_2048_8192_SHA384,
320            RSA_PKCS1_SHA512 => &signature::RSA_PKCS1_2048_8192_SHA512,
321            ECDSA_NISTP256_SHA256 => &signature::ECDSA_P256_SHA256_ASN1,
322            ECDSA_NISTP384_SHA384 => &signature::ECDSA_P384_SHA384_ASN1,
323            ECDSA_NISTP521_SHA512 => {
324                // See https://github.com/briansmith/ring/issues/824
325                return Err(webpki::Error::UnsupportedSignatureAlgorithm);
326            }
327            RSA_PSS_SHA256 => &signature::RSA_PSS_2048_8192_SHA256,
328            RSA_PSS_SHA384 => &signature::RSA_PSS_2048_8192_SHA384,
329            RSA_PSS_SHA512 => &signature::RSA_PSS_2048_8192_SHA512,
330            ED25519 => &signature::ED25519,
331            ED448 => {
332                // See https://github.com/briansmith/ring/issues/463
333                return Err(webpki::Error::UnsupportedSignatureAlgorithm);
334            }
335            // Similarly, hash functions with an output length less than 256 bits
336            // MUST NOT be used, due to the possibility of collision attacks.
337            // In particular, MD5 and SHA1 MUST NOT be used.
338            RSA_PKCS1_SHA1 => return Err(webpki::Error::UnsupportedSignatureAlgorithm),
339            ECDSA_SHA1_Legacy => return Err(webpki::Error::UnsupportedSignatureAlgorithm),
340            _ => return Err(webpki::Error::UnsupportedSignatureAlgorithm),
341        };
342        let spki = &self.certificate.tbs_certificate.subject_pki;
343        let key = signature::UnparsedPublicKey::new(
344            verification_algorithm,
345            spki.subject_public_key.as_ref(),
346        );
347
348        Ok(key)
349    }
350
351    /// This method validates the certificate according to libp2p TLS 1.3 specs.
352    /// The certificate MUST:
353    /// 1. be valid at the time it is received by the peer;
354    /// 2. use the NamedCurve encoding;
355    /// 3. use hash functions with an output length not less than 256 bits;
356    /// 4. be self signed;
357    /// 5. contain a valid signature in the specific libp2p extension.
358    fn verify(&self) -> Result<(), webpki::Error> {
359        use webpki::Error;
360        // The certificate MUST have NotBefore and NotAfter fields set
361        // such that the certificate is valid at the time it is received by the peer.
362        if !self.certificate.validity().is_valid() {
363            return Err(Error::InvalidCertValidity);
364        }
365
366        // Certificates MUST use the NamedCurve encoding for elliptic curve parameters.
367        // Similarly, hash functions with an output length less than 256 bits
368        // MUST NOT be used, due to the possibility of collision attacks.
369        // In particular, MD5 and SHA1 MUST NOT be used.
370        // Endpoints MUST abort the connection attempt if it is not used.
371        let signature_scheme = self.signature_scheme()?;
372        // Endpoints MUST abort the connection attempt if the certificate’s
373        // self-signature is not valid.
374        let raw_certificate = self.certificate.tbs_certificate.as_ref();
375        let signature = self.certificate.signature_value.as_ref();
376        // check if self signed
377        self.verify_signature(signature_scheme, raw_certificate, signature)
378            .map_err(|_| Error::SignatureAlgorithmMismatch)?;
379
380        let subject_pki = self.certificate.public_key().raw;
381
382        // The peer signs the concatenation of the string `libp2p-tls-handshake:`
383        // and the public key that it used to generate the certificate carrying
384        // the libp2p Public Key Extension, using its private host key.
385        let mut msg = vec![];
386        msg.extend(P2P_SIGNING_PREFIX);
387        msg.extend(subject_pki);
388
389        // This signature provides cryptographic proof that the peer was in possession
390        // of the private host key at the time the certificate was signed.
391        // Peers MUST verify the signature, and abort the connection attempt
392        // if signature verification fails.
393        let user_owns_sk = self
394            .extension
395            .public_key
396            .verify(&msg, &self.extension.signature);
397        if !user_owns_sk {
398            return Err(Error::UnknownIssuer);
399        }
400
401        Ok(())
402    }
403
404    /// Return the signature scheme corresponding to [`AlgorithmIdentifier`]s
405    /// of `subject_pki` and `signature_algorithm`
406    /// according to <https://www.rfc-editor.org/rfc/rfc8446.html#section-4.2.3>.
407    fn signature_scheme(&self) -> Result<rustls::SignatureScheme, webpki::Error> {
408        // Certificates MUST use the NamedCurve encoding for elliptic curve parameters.
409        // Endpoints MUST abort the connection attempt if it is not used.
410        use oid_registry::*;
411        use rustls::SignatureScheme::*;
412
413        let signature_algorithm = &self.certificate.signature_algorithm;
414        let pki_algorithm = &self.certificate.tbs_certificate.subject_pki.algorithm;
415
416        if pki_algorithm.algorithm == OID_PKCS1_RSAENCRYPTION {
417            if signature_algorithm.algorithm == OID_PKCS1_SHA256WITHRSA {
418                return Ok(RSA_PKCS1_SHA256);
419            }
420            if signature_algorithm.algorithm == OID_PKCS1_SHA384WITHRSA {
421                return Ok(RSA_PKCS1_SHA384);
422            }
423            if signature_algorithm.algorithm == OID_PKCS1_SHA512WITHRSA {
424                return Ok(RSA_PKCS1_SHA512);
425            }
426            if signature_algorithm.algorithm == OID_PKCS1_RSASSAPSS {
427                // According to https://datatracker.ietf.org/doc/html/rfc4055#section-3.1:
428                // Inside of params there should be a sequence of:
429                // - Hash Algorithm
430                // - Mask Algorithm
431                // - Salt Length
432                // - Trailer Field
433
434                // We are interested in Hash Algorithm only
435
436                if let Ok(SignatureAlgorithm::RSASSA_PSS(params)) =
437                    SignatureAlgorithm::try_from(signature_algorithm)
438                {
439                    let hash_oid = params.hash_algorithm_oid();
440                    if hash_oid == &OID_NIST_HASH_SHA256 {
441                        return Ok(RSA_PSS_SHA256);
442                    }
443                    if hash_oid == &OID_NIST_HASH_SHA384 {
444                        return Ok(RSA_PSS_SHA384);
445                    }
446                    if hash_oid == &OID_NIST_HASH_SHA512 {
447                        return Ok(RSA_PSS_SHA512);
448                    }
449                }
450
451                // Default hash algo is SHA-1, however:
452                // In particular, MD5 and SHA1 MUST NOT be used.
453                return Err(webpki::Error::UnsupportedSignatureAlgorithm);
454            }
455        }
456
457        if pki_algorithm.algorithm == OID_KEY_TYPE_EC_PUBLIC_KEY {
458            let signature_param = pki_algorithm
459                .parameters
460                .as_ref()
461                .ok_or(webpki::Error::BadDer)?
462                .as_oid()
463                .map_err(|_| webpki::Error::BadDer)?;
464            if signature_param == OID_EC_P256
465                && signature_algorithm.algorithm == OID_SIG_ECDSA_WITH_SHA256
466            {
467                return Ok(ECDSA_NISTP256_SHA256);
468            }
469            if signature_param == OID_NIST_EC_P384
470                && signature_algorithm.algorithm == OID_SIG_ECDSA_WITH_SHA384
471            {
472                return Ok(ECDSA_NISTP384_SHA384);
473            }
474            if signature_param == OID_NIST_EC_P521
475                && signature_algorithm.algorithm == OID_SIG_ECDSA_WITH_SHA512
476            {
477                return Ok(ECDSA_NISTP521_SHA512);
478            }
479            return Err(webpki::Error::UnsupportedSignatureAlgorithm);
480        }
481
482        if signature_algorithm.algorithm == OID_SIG_ED25519 {
483            return Ok(ED25519);
484        }
485        if signature_algorithm.algorithm == OID_SIG_ED448 {
486            return Ok(ED448);
487        }
488
489        Err(webpki::Error::UnsupportedSignatureAlgorithm)
490    }
491}
492
493#[cfg(test)]
494mod tests {
495    use hex_literal::hex;
496
497    use super::*;
498
499    #[test]
500    fn sanity_check() {
501        let keypair = identity::Keypair::generate_ed25519();
502
503        let (cert, _) = generate(&keypair).unwrap();
504        let parsed_cert = parse(&cert).unwrap();
505
506        assert!(parsed_cert.verify().is_ok());
507        assert_eq!(keypair.public(), parsed_cert.extension.public_key);
508    }
509
510    macro_rules! check_cert {
511        ($name:ident, $path:literal, $scheme:path) => {
512            #[test]
513            fn $name() {
514                let cert: &[u8] = include_bytes!($path);
515
516                let cert = parse_unverified(cert).unwrap();
517                assert!(cert.verify().is_err()); // Because p2p extension
518                                                 // was not signed with the private key
519                                                 // of the certificate.
520                assert_eq!(cert.signature_scheme(), Ok($scheme));
521            }
522        };
523    }
524
525    check_cert! {ed448, "./test_assets/ed448.der", rustls::SignatureScheme::ED448}
526    check_cert! {ed25519, "./test_assets/ed25519.der", rustls::SignatureScheme::ED25519}
527    check_cert! {rsa_pkcs1_sha256, "./test_assets/rsa_pkcs1_sha256.der", rustls::SignatureScheme::RSA_PKCS1_SHA256}
528    check_cert! {rsa_pkcs1_sha384, "./test_assets/rsa_pkcs1_sha384.der", rustls::SignatureScheme::RSA_PKCS1_SHA384}
529    check_cert! {rsa_pkcs1_sha512, "./test_assets/rsa_pkcs1_sha512.der", rustls::SignatureScheme::RSA_PKCS1_SHA512}
530    check_cert! {nistp256_sha256, "./test_assets/nistp256_sha256.der", rustls::SignatureScheme::ECDSA_NISTP256_SHA256}
531    check_cert! {nistp384_sha384, "./test_assets/nistp384_sha384.der", rustls::SignatureScheme::ECDSA_NISTP384_SHA384}
532    check_cert! {nistp521_sha512, "./test_assets/nistp521_sha512.der", rustls::SignatureScheme::ECDSA_NISTP521_SHA512}
533
534    #[test]
535    fn rsa_pss_sha384() {
536        let cert = rustls::pki_types::CertificateDer::from(
537            include_bytes!("./test_assets/rsa_pss_sha384.der").to_vec(),
538        );
539
540        let cert = parse(&cert).unwrap();
541
542        assert_eq!(
543            cert.signature_scheme(),
544            Ok(rustls::SignatureScheme::RSA_PSS_SHA384)
545        );
546    }
547
548    #[test]
549    fn nistp384_sha256() {
550        let cert: &[u8] = include_bytes!("./test_assets/nistp384_sha256.der");
551
552        let cert = parse_unverified(cert).unwrap();
553
554        assert!(cert.signature_scheme().is_err());
555    }
556
557    #[test]
558    fn can_parse_certificate_with_ed25519_keypair() {
559        let certificate = rustls::pki_types::CertificateDer::from(hex!("308201773082011ea003020102020900f5bd0debaa597f52300a06082a8648ce3d04030230003020170d3735303130313030303030305a180f34303936303130313030303030305a30003059301306072a8648ce3d020106082a8648ce3d030107034200046bf9871220d71dcb3483ecdfcbfcc7c103f8509d0974b3c18ab1f1be1302d643103a08f7a7722c1b247ba3876fe2c59e26526f479d7718a85202ddbe47562358a37f307d307b060a2b0601040183a25a01010101ff046a30680424080112207fda21856709c5ae12fd6e8450623f15f11955d384212b89f56e7e136d2e17280440aaa6bffabe91b6f30c35e3aa4f94b1188fed96b0ffdd393f4c58c1c047854120e674ce64c788406d1c2c4b116581fd7411b309881c3c7f20b46e54c7e6fe7f0f300a06082a8648ce3d040302034700304402207d1a1dbd2bda235ff2ec87daf006f9b04ba076a5a5530180cd9c2e8f6399e09d0220458527178c7e77024601dbb1b256593e9b96d961b96349d1f560114f61a87595").to_vec());
560
561        let peer_id = parse(&certificate).unwrap().peer_id();
562
563        assert_eq!(
564            "12D3KooWJRSrypvnpHgc6ZAgyCni4KcSmbV7uGRaMw5LgMKT18fq"
565                .parse::<PeerId>()
566                .unwrap(),
567            peer_id
568        );
569    }
570
571    #[test]
572    fn fails_to_parse_bad_certificate_with_ed25519_keypair() {
573        let certificate = rustls::pki_types::CertificateDer::from(hex!("308201773082011da003020102020830a73c5d896a1109300a06082a8648ce3d04030230003020170d3735303130313030303030305a180f34303936303130313030303030305a30003059301306072a8648ce3d020106082a8648ce3d03010703420004bbe62df9a7c1c46b7f1f21d556deec5382a36df146fb29c7f1240e60d7d5328570e3b71d99602b77a65c9b3655f62837f8d66b59f1763b8c9beba3be07778043a37f307d307b060a2b0601040183a25a01010101ff046a3068042408011220ec8094573afb9728088860864f7bcea2d4fd412fef09a8e2d24d482377c20db60440ecabae8354afa2f0af4b8d2ad871e865cb5a7c0c8d3dbdbf42de577f92461a0ebb0a28703e33581af7d2a4f2270fc37aec6261fcc95f8af08f3f4806581c730a300a06082a8648ce3d040302034800304502202dfb17a6fa0f94ee0e2e6a3b9fb6e986f311dee27392058016464bd130930a61022100ba4b937a11c8d3172b81e7cd04aedb79b978c4379c2b5b24d565dd5d67d3cb3c").to_vec());
574
575        let error = parse(&certificate).unwrap_err();
576
577        assert_eq!(format!("{error}"), "UnknownIssuer");
578    }
579
580    #[test]
581    fn can_parse_certificate_with_ecdsa_keypair() {
582        let certificate = rustls::pki_types::CertificateDer::from(hex!("308201c030820166a003020102020900eaf419a6e3edb4a6300a06082a8648ce3d04030230003020170d3735303130313030303030305a180f34303936303130313030303030305a30003059301306072a8648ce3d020106082a8648ce3d030107034200048dbf1116c7c608d6d5292bd826c3feb53483a89fce434bf64538a359c8e07538ff71f6766239be6a146dcc1a5f3bb934bcd4ae2ae1d4da28ac68b4a20593f06ba381c63081c33081c0060a2b0601040183a25a01010101ff0481ae3081ab045f0803125b3059301306072a8648ce3d020106082a8648ce3d0301070342000484b93fa456a74bd0153919f036db7bc63c802f055bc7023395d0203de718ee0fc7b570b767cdd858aca6c7c4113ff002e78bd2138ac1a3b26dde3519e06979ad04483046022100bc84014cea5a41feabdf4c161096564b9ccf4b62fbef4fe1cd382c84e11101780221009204f086a84cb8ed8a9ddd7868dc90c792ee434adf62c66f99a08a5eba11615b300a06082a8648ce3d0403020348003045022054b437be9a2edf591312d68ff24bf91367ad4143f76cf80b5658f232ade820da022100e23b48de9df9c25d4c83ddddf75d2676f0b9318ee2a6c88a736d85eab94a912f").to_vec());
583
584        let peer_id = parse(&certificate).unwrap().peer_id();
585
586        assert_eq!(
587            "QmZcrvr3r4S3QvwFdae3c2EWTfo792Y14UpzCZurhmiWeX"
588                .parse::<PeerId>()
589                .unwrap(),
590            peer_id
591        );
592    }
593
594    #[test]
595    fn can_parse_certificate_with_secp256k1_keypair() {
596        let certificate = rustls::pki_types::CertificateDer::from(hex!("3082018230820128a003020102020900f3b305f55622cfdf300a06082a8648ce3d04030230003020170d3735303130313030303030305a180f34303936303130313030303030305a30003059301306072a8648ce3d020106082a8648ce3d0301070342000458f7e9581748ff9bdd933b655cc0e5552a1248f840658cc221dec2186b5a2fe4641b86ab7590a3422cdbb1000cf97662f27e5910d7569f22feed8829c8b52e0fa38188308185308182060a2b0601040183a25a01010101ff0471306f042508021221026b053094d1112bce799dc8026040ae6d4eb574157929f1598172061f753d9b1b04463044022040712707e97794c478d93989aaa28ae1f71c03af524a8a4bd2d98424948a782302207b61b7f074b696a25fb9e0059141a811cccc4cc28042d9301b9b2a4015e87470300a06082a8648ce3d04030203480030450220143ae4d86fdc8675d2480bb6912eca5e39165df7f572d836aa2f2d6acfab13f8022100831d1979a98f0c4a6fb5069ca374de92f1a1205c962a6d90ad3d7554cb7d9df4").to_vec());
597
598        let peer_id = parse(&certificate).unwrap().peer_id();
599
600        assert_eq!(
601            "16Uiu2HAm2dSCBFxuge46aEt7U1oejtYuBUZXxASHqmcfVmk4gsbx"
602                .parse::<PeerId>()
603                .unwrap(),
604            peer_id
605        );
606    }
607}