aws_lc_rs/
ed25519.rs

1// Copyright 2015-2016 Brian Smith.
2// SPDX-License-Identifier: ISC
3// Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4// SPDX-License-Identifier: Apache-2.0 OR ISC
5
6use core::fmt;
7use core::fmt::{Debug, Formatter};
8use std::marker::PhantomData;
9
10#[cfg(feature = "ring-sig-verify")]
11use untrusted::Input;
12
13use crate::aws_lc::{EVP_PKEY, EVP_PKEY_ED25519};
14
15use crate::buffer::Buffer;
16use crate::encoding::{
17    AsBigEndian, AsDer, Curve25519SeedBin, Pkcs8V1Der, Pkcs8V2Der, PublicKeyX509Der,
18};
19use crate::error::{KeyRejected, Unspecified};
20use crate::evp_pkey::No_EVP_PKEY_CTX_consumer;
21use crate::pkcs8::{Document, Version};
22use crate::ptr::LcPtr;
23use crate::rand::SecureRandom;
24use crate::signature::{KeyPair, Signature, VerificationAlgorithm};
25use crate::{constant_time, hex, sealed};
26
27/// The length of an Ed25519 public key.
28pub const ED25519_PUBLIC_KEY_LEN: usize = crate::aws_lc::ED25519_PUBLIC_KEY_LEN as usize;
29const ED25519_SIGNATURE_LEN: usize = crate::aws_lc::ED25519_SIGNATURE_LEN as usize;
30const ED25519_SEED_LEN: usize = 32;
31
32/// Parameters for `EdDSA` signing and verification.
33#[derive(Debug)]
34pub struct EdDSAParameters;
35
36impl sealed::Sealed for EdDSAParameters {}
37
38impl VerificationAlgorithm for EdDSAParameters {
39    #[inline]
40    #[cfg(feature = "ring-sig-verify")]
41    fn verify(
42        &self,
43        public_key: Input<'_>,
44        msg: Input<'_>,
45        signature: Input<'_>,
46    ) -> Result<(), Unspecified> {
47        let evp_pkey = try_ed25519_public_key_from_bytes(public_key.as_slice_less_safe())?;
48        evp_pkey.verify(
49            msg.as_slice_less_safe(),
50            None,
51            No_EVP_PKEY_CTX_consumer,
52            signature.as_slice_less_safe(),
53        )
54    }
55
56    fn verify_sig(
57        &self,
58        public_key: &[u8],
59        msg: &[u8],
60        signature: &[u8],
61    ) -> Result<(), Unspecified> {
62        let evp_pkey = try_ed25519_public_key_from_bytes(public_key)?;
63        evp_pkey.verify(msg, None, No_EVP_PKEY_CTX_consumer, signature)
64    }
65}
66
67fn try_ed25519_public_key_from_bytes(key_bytes: &[u8]) -> Result<LcPtr<EVP_PKEY>, KeyRejected> {
68    // If the length of key bytes matches the raw public key size then it has to be that
69    if key_bytes.len() == ED25519_PUBLIC_KEY_LEN {
70        return LcPtr::<EVP_PKEY>::parse_raw_public_key(key_bytes, EVP_PKEY_ED25519);
71    }
72    // Otherwise we support X.509 SubjectPublicKeyInfo formatted keys which are inherently larger
73    LcPtr::<EVP_PKEY>::parse_rfc5280_public_key(key_bytes, EVP_PKEY_ED25519)
74}
75
76/// An Ed25519 key pair, for signing.
77#[allow(clippy::module_name_repetitions)]
78pub struct Ed25519KeyPair {
79    evp_pkey: LcPtr<EVP_PKEY>,
80    public_key: PublicKey,
81}
82
83impl Debug for Ed25519KeyPair {
84    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
85        f.write_str(&format!(
86            "Ed25519KeyPair {{ public_key: PublicKey(\"{}\") }}",
87            hex::encode(&self.public_key)
88        ))
89    }
90}
91
92#[derive(Clone)]
93#[allow(clippy::module_name_repetitions)]
94/// The seed value for the `EdDSA` signature scheme using Curve25519
95pub struct Seed<'a> {
96    bytes: Box<[u8]>,
97    phantom: PhantomData<&'a [u8]>,
98}
99
100impl AsBigEndian<Curve25519SeedBin<'static>> for Seed<'_> {
101    /// Exposes the seed encoded as a big-endian fixed-length integer.
102    ///
103    /// For most use-cases, `EcdsaKeyPair::to_pkcs8()` should be preferred.
104    ///
105    /// # Errors
106    /// `error::Unspecified` if serialization failed.
107    fn as_be_bytes(&self) -> Result<Curve25519SeedBin<'static>, Unspecified> {
108        Ok(Curve25519SeedBin::new(self.bytes.to_vec()))
109    }
110}
111
112impl Debug for Seed<'_> {
113    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
114        f.write_str("Ed25519Seed()")
115    }
116}
117
118#[derive(Clone)]
119#[allow(clippy::module_name_repetitions)]
120/// Ed25519 Public Key
121pub struct PublicKey {
122    evp_pkey: LcPtr<EVP_PKEY>,
123    public_key_bytes: [u8; ED25519_PUBLIC_KEY_LEN],
124}
125
126impl AsRef<[u8]> for PublicKey {
127    #[inline]
128    /// Returns the "raw" bytes of the ED25519 public key
129    fn as_ref(&self) -> &[u8] {
130        &self.public_key_bytes
131    }
132}
133
134impl Debug for PublicKey {
135    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
136        f.write_str(&format!(
137            "PublicKey(\"{}\")",
138            hex::encode(self.public_key_bytes)
139        ))
140    }
141}
142
143unsafe impl Send for PublicKey {}
144unsafe impl Sync for PublicKey {}
145
146impl AsDer<PublicKeyX509Der<'static>> for PublicKey {
147    /// Provides the public key as a DER-encoded (X.509) `SubjectPublicKeyInfo` structure.
148    /// # Errors
149    /// Returns an error if the public key fails to marshal to X.509.
150    fn as_der(&self) -> Result<PublicKeyX509Der<'static>, crate::error::Unspecified> {
151        // Initial size of 44 based on:
152        // 0:d=0  hl=2 l=  42 cons: SEQUENCE
153        // 2:d=1  hl=2 l=   5 cons:  SEQUENCE
154        // 4:d=2  hl=2 l=   3 prim:   OBJECT            :ED25519
155        // 9:d=1  hl=2 l=  33 prim:  BIT STRING
156        let der = self.evp_pkey.as_const().marshal_rfc5280_public_key()?;
157        Ok(PublicKeyX509Der::from(Buffer::new(der)))
158    }
159}
160
161impl KeyPair for Ed25519KeyPair {
162    type PublicKey = PublicKey;
163    #[inline]
164    fn public_key(&self) -> &Self::PublicKey {
165        &self.public_key
166    }
167}
168
169unsafe impl Send for Ed25519KeyPair {}
170unsafe impl Sync for Ed25519KeyPair {}
171
172pub(crate) fn generate_key() -> Result<LcPtr<EVP_PKEY>, Unspecified> {
173    LcPtr::<EVP_PKEY>::generate(EVP_PKEY_ED25519, No_EVP_PKEY_CTX_consumer)
174}
175
176impl Ed25519KeyPair {
177    /// Generates a new key pair and returns the key pair.
178    ///
179    /// # Errors
180    /// `error::Unspecified` if key generation fails.
181    pub fn generate() -> Result<Self, Unspecified> {
182        let evp_pkey = generate_key()?;
183
184        let mut public_key = [0u8; ED25519_PUBLIC_KEY_LEN];
185        let out_len: usize = evp_pkey
186            .as_const()
187            .marshal_raw_public_to_buffer(&mut public_key)?;
188        debug_assert_eq!(public_key.len(), out_len);
189
190        Ok(Self {
191            public_key: PublicKey {
192                public_key_bytes: public_key,
193                evp_pkey: evp_pkey.clone(),
194            },
195            evp_pkey,
196        })
197    }
198
199    /// Generates a new key pair and returns the key pair serialized as a
200    /// PKCS#8 document.
201    ///
202    /// The PKCS#8 document will be a v2 `OneAsymmetricKey` with the public key,
203    /// as described in [RFC 5958 Section 2]; see [RFC 8410 Section 10.3] for an
204    /// example.
205    ///
206    /// [RFC 5958 Section 2]: https://tools.ietf.org/html/rfc5958#section-2
207    /// [RFC 8410 Section 10.3]: https://tools.ietf.org/html/rfc8410#section-10.3
208    ///
209    /// # *ring* Compatibility
210    /// The ring 0.16.x API did not produce encoded v2 documents that were compliant with RFC 5958.
211    /// The aws-lc-ring implementation produces PKCS#8 v2 encoded documents that are compliant per
212    /// the RFC specification.
213    ///
214    /// Our implementation ignores the `SecureRandom` parameter.
215    ///
216    // # FIPS
217    // This function must not be used.
218    //
219    /// # Errors
220    /// `error::Unspecified` if `rng` cannot provide enough bits or if there's an internal error.
221    pub fn generate_pkcs8(_rng: &dyn SecureRandom) -> Result<Document, Unspecified> {
222        let evp_pkey = generate_key()?;
223        Ok(Document::new(
224            evp_pkey
225                .as_const()
226                .marshal_rfc5208_private_key(Version::V2)?,
227        ))
228    }
229
230    /// Serializes this `Ed25519KeyPair` into a PKCS#8 v2 document.
231    ///
232    /// # Errors
233    /// `error::Unspecified` on internal error.
234    ///
235    pub fn to_pkcs8(&self) -> Result<Document, Unspecified> {
236        Ok(Document::new(
237            self.evp_pkey
238                .as_const()
239                .marshal_rfc5208_private_key(Version::V2)?,
240        ))
241    }
242
243    /// Generates a `Ed25519KeyPair` using the `rng` provided, then serializes that key as a
244    /// PKCS#8 document.
245    ///
246    /// The PKCS#8 document will be a v1 `PrivateKeyInfo` structure (RFC5208). Use this method
247    /// when needing to produce documents that are compatible with the OpenSSL CLI.
248    ///
249    /// # *ring* Compatibility
250    ///  Our implementation ignores the `SecureRandom` parameter.
251    ///
252    // # FIPS
253    // This function must not be used.
254    //
255    /// # Errors
256    /// `error::Unspecified` if `rng` cannot provide enough bits or if there's an internal error.
257    pub fn generate_pkcs8v1(_rng: &dyn SecureRandom) -> Result<Document, Unspecified> {
258        let evp_pkey = generate_key()?;
259        Ok(Document::new(
260            evp_pkey
261                .as_const()
262                .marshal_rfc5208_private_key(Version::V1)?,
263        ))
264    }
265
266    /// Serializes this `Ed25519KeyPair` into a PKCS#8 v1 document.
267    ///
268    /// # Errors
269    /// `error::Unspecified` on internal error.
270    ///
271    pub fn to_pkcs8v1(&self) -> Result<Document, Unspecified> {
272        Ok(Document::new(
273            self.evp_pkey
274                .as_const()
275                .marshal_rfc5208_private_key(Version::V1)?,
276        ))
277    }
278
279    /// Constructs an Ed25519 key pair from the private key seed `seed` and its
280    /// public key `public_key`.
281    ///
282    /// It is recommended to use `Ed25519KeyPair::from_pkcs8()` instead.
283    ///
284    /// The private and public keys will be verified to be consistent with each
285    /// other. This helps avoid misuse of the key (e.g. accidentally swapping
286    /// the private key and public key, or using the wrong private key for the
287    /// public key). This also detects any corruption of the public or private
288    /// key.
289    ///
290    /// # Errors
291    /// `error::KeyRejected` if parse error, or if key is otherwise unacceptable.
292    pub fn from_seed_and_public_key(seed: &[u8], public_key: &[u8]) -> Result<Self, KeyRejected> {
293        let this = Self::from_seed_unchecked(seed)?;
294
295        constant_time::verify_slices_are_equal(public_key, &this.public_key.public_key_bytes)
296            .map_err(|_| KeyRejected::inconsistent_components())?;
297        Ok(this)
298    }
299
300    /// Constructs an Ed25519 key pair from the private key seed `seed`.
301    ///
302    /// It is recommended to use `Ed25519KeyPair::from_pkcs8()` instead. If the public key is
303    /// available, prefer to use `Ed25519KeyPair::from_seed_and_public_key()` as it will verify
304    /// the validity of the key pair.
305    ///
306    /// CAUTION: Both an Ed25519 seed and its public key are 32-bytes. If the bytes of a public key
307    /// are provided this function will create an (effectively) invalid `Ed25519KeyPair`. This
308    /// problem is undetectable by the API.
309    ///
310    /// # Errors
311    /// `error::KeyRejected` if parse error, or if key is otherwise unacceptable.
312    pub fn from_seed_unchecked(seed: &[u8]) -> Result<Self, KeyRejected> {
313        if seed.len() < ED25519_SEED_LEN {
314            return Err(KeyRejected::inconsistent_components());
315        }
316
317        let evp_pkey = LcPtr::<EVP_PKEY>::parse_raw_private_key(seed, EVP_PKEY_ED25519)?;
318
319        let mut derived_public_key = [0u8; ED25519_PUBLIC_KEY_LEN];
320        let out_len: usize = evp_pkey
321            .as_const()
322            .marshal_raw_public_to_buffer(&mut derived_public_key)?;
323        debug_assert_eq!(derived_public_key.len(), out_len);
324
325        Ok(Self {
326            public_key: PublicKey {
327                public_key_bytes: derived_public_key,
328                evp_pkey: evp_pkey.clone(),
329            },
330            evp_pkey,
331        })
332    }
333
334    /// Constructs an Ed25519 key pair by parsing an unencrypted PKCS#8 v1 or v2
335    /// Ed25519 private key.
336    ///
337    /// `openssl genpkey -algorithm ED25519` generates PKCS#8 v1 keys.
338    ///
339    /// # Ring Compatibility
340    /// * This method accepts either v1 or v2 encoded keys, if a v2 encoded key is provided, with the
341    ///   public key component present, it will be verified to match the one derived from the
342    ///   encoded private key.
343    /// * The ring 0.16.x API did not produce encoded v2 documents that were compliant with RFC 5958.
344    ///   The aws-lc-ring implementation produces PKCS#8 v2 encoded documents that are compliant per
345    ///   the RFC specification.
346    ///
347    /// # Errors
348    /// `error::KeyRejected` on parse error, or if key is otherwise unacceptable.
349    pub fn from_pkcs8(pkcs8: &[u8]) -> Result<Self, KeyRejected> {
350        Self::parse_pkcs8(pkcs8)
351    }
352
353    /// Constructs an Ed25519 key pair by parsing an unencrypted PKCS#8 v1 or v2
354    /// Ed25519 private key.
355    ///
356    /// `openssl genpkey -algorithm ED25519` generates PKCS# v1 keys.
357    ///
358    /// # Ring Compatibility
359    /// * This method accepts either v1 or v2 encoded keys, if a v2 encoded key is provided, with the
360    ///   public key component present, it will be verified to match the one derived from the
361    ///   encoded private key.
362    /// * The ring 0.16.x API did not produce encoded v2 documents that were compliant with RFC 5958.
363    ///   The aws-lc-ring implementation produces PKCS#8 v2 encoded documents that are compliant per
364    ///   the RFC specification.
365    ///
366    /// # Errors
367    /// `error::KeyRejected` on parse error, or if key is otherwise unacceptable.
368    pub fn from_pkcs8_maybe_unchecked(pkcs8: &[u8]) -> Result<Self, KeyRejected> {
369        Self::parse_pkcs8(pkcs8)
370    }
371
372    fn parse_pkcs8(pkcs8: &[u8]) -> Result<Self, KeyRejected> {
373        let evp_pkey = LcPtr::<EVP_PKEY>::parse_rfc5208_private_key(pkcs8, EVP_PKEY_ED25519)?;
374
375        evp_pkey.as_const().validate_as_ed25519()?;
376
377        let mut public_key = [0u8; ED25519_PUBLIC_KEY_LEN];
378        let out_len: usize = evp_pkey
379            .as_const()
380            .marshal_raw_public_to_buffer(&mut public_key)?;
381        debug_assert_eq!(public_key.len(), out_len);
382
383        Ok(Self {
384            public_key: PublicKey {
385                public_key_bytes: public_key,
386                evp_pkey: evp_pkey.clone(),
387            },
388            evp_pkey,
389        })
390    }
391
392    /// Returns the signature of the message msg.
393    ///
394    // # FIPS
395    // This method must not be used.
396    //
397    /// # Panics
398    /// Panics if the message is unable to be signed
399    #[inline]
400    #[must_use]
401    pub fn sign(&self, msg: &[u8]) -> Signature {
402        Self::try_sign(self, msg).expect("ED25519 signing failed")
403    }
404
405    /// Returns the signature of the message `msg`.
406    ///
407    // # FIPS
408    // This method must not be used.
409    //
410    /// # Errors
411    /// Returns `error::Unspecified` if the signing operation fails.
412    #[inline]
413    pub fn try_sign(&self, msg: &[u8]) -> Result<Signature, Unspecified> {
414        let sig_bytes = self.evp_pkey.sign(msg, None, No_EVP_PKEY_CTX_consumer)?;
415
416        Ok(Signature::new(|slice| {
417            slice[0..ED25519_SIGNATURE_LEN].copy_from_slice(&sig_bytes);
418            ED25519_SIGNATURE_LEN
419        }))
420    }
421
422    /// Provides the private key "seed" for this `Ed25519` key pair.
423    ///
424    /// For serialization of the key pair, `Ed25519KeyPair::to_pkcs8()` is preferred.
425    ///
426    /// # Errors
427    /// Currently the function cannot fail, but it might in future implementations.
428    pub fn seed(&self) -> Result<Seed<'static>, Unspecified> {
429        Ok(Seed {
430            bytes: self
431                .evp_pkey
432                .as_const()
433                .marshal_raw_private_key()?
434                .into_boxed_slice(),
435            phantom: PhantomData,
436        })
437    }
438}
439
440impl AsDer<Pkcs8V1Der<'static>> for Ed25519KeyPair {
441    /// Serializes this `Ed25519KeyPair` into a PKCS#8 v1 document.
442    ///
443    /// # Errors
444    /// `error::Unspecified` on internal error.
445    fn as_der(&self) -> Result<Pkcs8V1Der<'static>, crate::error::Unspecified> {
446        Ok(Pkcs8V1Der::new(
447            self.evp_pkey
448                .as_const()
449                .marshal_rfc5208_private_key(Version::V1)?,
450        ))
451    }
452}
453
454impl AsDer<Pkcs8V2Der<'static>> for Ed25519KeyPair {
455    /// Serializes this `Ed25519KeyPair` into a PKCS#8 v1 document.
456    ///
457    /// # Errors
458    /// `error::Unspecified` on internal error.
459    fn as_der(&self) -> Result<Pkcs8V2Der<'static>, crate::error::Unspecified> {
460        Ok(Pkcs8V2Der::new(
461            self.evp_pkey
462                .as_const()
463                .marshal_rfc5208_private_key(Version::V2)?,
464        ))
465    }
466}
467
468#[cfg(test)]
469mod tests {
470    use crate::ed25519::Ed25519KeyPair;
471    use crate::encoding::{AsBigEndian, AsDer, Pkcs8V1Der, Pkcs8V2Der, PublicKeyX509Der};
472    use crate::rand::SystemRandom;
473    use crate::signature::{KeyPair, UnparsedPublicKey, ED25519};
474    use crate::{hex, test};
475
476    #[test]
477    fn test_generate() {
478        const MESSAGE: &[u8] = b"test message";
479        let key_pair = Ed25519KeyPair::generate().unwrap();
480        let public_key = key_pair.public_key();
481        let signature = key_pair.sign(MESSAGE);
482        let unparsed_public_key = UnparsedPublicKey::new(&ED25519, public_key.as_ref());
483        unparsed_public_key
484            .verify(MESSAGE, signature.as_ref())
485            .unwrap();
486    }
487
488    #[test]
489    fn test_generate_pkcs8() {
490        let rng = SystemRandom::new();
491        let document = Ed25519KeyPair::generate_pkcs8(&rng).unwrap();
492        let kp1: Ed25519KeyPair = Ed25519KeyPair::from_pkcs8(document.as_ref()).unwrap();
493        assert_eq!(
494            document.as_ref(),
495            AsDer::<Pkcs8V2Der>::as_der(&kp1).unwrap().as_ref()
496        );
497        let kp2: Ed25519KeyPair =
498            Ed25519KeyPair::from_pkcs8_maybe_unchecked(document.as_ref()).unwrap();
499        assert_eq!(
500            kp1.seed().unwrap().as_be_bytes().unwrap().as_ref(),
501            kp2.seed().unwrap().as_be_bytes().unwrap().as_ref(),
502        );
503        assert_eq!(kp1.public_key.as_ref(), kp2.public_key.as_ref());
504
505        let document = Ed25519KeyPair::generate_pkcs8v1(&rng).unwrap();
506        let kp1: Ed25519KeyPair = Ed25519KeyPair::from_pkcs8(document.as_ref()).unwrap();
507        assert_eq!(
508            document.as_ref(),
509            AsDer::<Pkcs8V1Der>::as_der(&kp1).unwrap().as_ref()
510        );
511        let kp2: Ed25519KeyPair =
512            Ed25519KeyPair::from_pkcs8_maybe_unchecked(document.as_ref()).unwrap();
513        assert_eq!(
514            kp1.seed().unwrap().as_be_bytes().unwrap().as_ref(),
515            kp2.seed().unwrap().as_be_bytes().unwrap().as_ref(),
516        );
517        assert_eq!(kp1.public_key.as_ref(), kp2.public_key.as_ref());
518        let seed = kp1.seed().unwrap();
519        assert_eq!("Ed25519Seed()", format!("{seed:?}"));
520    }
521
522    #[test]
523    fn test_from_pkcs8() {
524        struct TestCase {
525            key: &'static str,
526            expected_public: &'static str,
527        }
528
529        for case in [
530            TestCase {
531                key: "302e020100300506032b6570042204209d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
532                expected_public: "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
533            },
534            TestCase {
535                key: "3051020101300506032b657004220420756434bd5b824753007a138d27abbc14b5cc786adb78fb62435e6419a2b2e72b8121000faccd81e57de15fa6343a7fbb43b2b93f28be6435100ae8bd633c6dfee3d198",
536                expected_public: "0faccd81e57de15fa6343a7fbb43b2b93f28be6435100ae8bd633c6dfee3d198",
537            },
538            TestCase {
539                key: "304f020100300506032b657004220420d4ee72dbf913584ad5b6d8f1f769f8ad3afe7c28cbf1d4fbe097a88f44755842a01f301d060a2a864886f70d01090914310f0c0d437572646c6520436861697273",
540                expected_public: "19bf44096984cdfe8541bac167dc3b96c85086aa30b6b6cb0c5c38ad703166e1",
541            },
542            TestCase {
543                key: "3072020101300506032b657004220420d4ee72dbf913584ad5b6d8f1f769f8ad3afe7c28cbf1d4fbe097a88f44755842a01f301d060a2a864886f70d01090914310f0c0d437572646c652043686169727381210019bf44096984cdfe8541bac167dc3b96c85086aa30b6b6cb0c5c38ad703166e1",
544                expected_public: "19bf44096984cdfe8541bac167dc3b96c85086aa30b6b6cb0c5c38ad703166e1",
545            }
546        ] {
547            let key_pair = Ed25519KeyPair::from_pkcs8(&test::from_dirty_hex(case.key)).unwrap();
548            assert_eq!(
549                format!(
550                    r#"Ed25519KeyPair {{ public_key: PublicKey("{}") }}"#,
551                    case.expected_public
552                ),
553                format!("{key_pair:?}")
554            );
555            let key_pair = Ed25519KeyPair::from_pkcs8_maybe_unchecked(&test::from_dirty_hex(case.key)).unwrap();
556            assert_eq!(
557                format!(
558                    r#"Ed25519KeyPair {{ public_key: PublicKey("{}") }}"#,
559                    case.expected_public
560                ),
561                format!("{key_pair:?}")
562            );
563        }
564    }
565
566    #[test]
567    fn test_public_key_as_der_x509() {
568        let key_pair = Ed25519KeyPair::from_pkcs8(&hex::decode("302e020100300506032b6570042204209d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60").unwrap()).unwrap();
569        let public_key = key_pair.public_key();
570        let x509der = AsDer::<PublicKeyX509Der>::as_der(public_key).unwrap();
571        assert_eq!(
572            x509der.as_ref(),
573            &[
574                0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x03, 0x21, 0x00, 0xd7, 0x5a,
575                0x98, 0x01, 0x82, 0xb1, 0x0a, 0xb7, 0xd5, 0x4b, 0xfe, 0xd3, 0xc9, 0x64, 0x07, 0x3a,
576                0x0e, 0xe1, 0x72, 0xf3, 0xda, 0xa6, 0x23, 0x25, 0xaf, 0x02, 0x1a, 0x68, 0xf7, 0x07,
577                0x51, 0x1a
578            ]
579        );
580    }
581}