Skip to main content

rama_boring/
pkey.rs

1//! Public/private key processing.
2//!
3//! Asymmetric public key algorithms solve the problem of establishing and sharing
4//! secret keys to securely send and receive messages.
5//! This system uses a pair of keys: a public key, which can be freely
6//! distributed, and a private key, which is kept to oneself. An entity may
7//! encrypt information using a user's public key. The encrypted information can
8//! only be deciphered using that user's private key.
9//!
10//! This module offers support for five popular algorithms:
11//!
12//! * RSA
13//!
14//! * DSA
15//!
16//! * Diffie-Hellman
17//!
18//! * Elliptic Curves
19//!
20//! * HMAC
21//!
22//! These algorithms rely on hard mathematical problems - namely integer factorization,
23//! discrete logarithms, and elliptic curve relationships - that currently do not
24//! yield efficient solutions. This property ensures the security of these
25//! cryptographic algorithms.
26//!
27//! # Example
28//!
29//! Generate a 2048-bit RSA public/private key pair and print the public key.
30//!
31//! ```rust
32//! use rama_boring::rsa::Rsa;
33//! use rama_boring::pkey::PKey;
34//! use std::str;
35//!
36//! let rsa = Rsa::generate(2048).unwrap();
37//! let pkey = PKey::from_rsa(rsa).unwrap();
38//!
39//! let pub_key: Vec<u8> = pkey.public_key_to_pem().unwrap();
40//! println!("{:?}", str::from_utf8(pub_key.as_slice()).unwrap());
41//! ```
42
43use crate::ffi;
44use crate::libc_types::{c_int, c_long};
45use foreign_types::{ForeignType, ForeignTypeRef};
46use openssl_macros::corresponds;
47use std::ffi::CString;
48use std::fmt;
49use std::mem;
50use std::ptr;
51
52use crate::bio::MemBioSlice;
53use crate::dh::Dh;
54use crate::dsa::Dsa;
55use crate::ec::EcKey;
56use crate::error::ErrorStack;
57use crate::rsa::Rsa;
58use crate::util::{invoke_passwd_cb, CallbackState};
59use crate::{cvt, cvt_0i, cvt_p};
60
61/// A tag type indicating that a key only has parameters.
62pub enum Params {}
63
64/// A tag type indicating that a key only has public components.
65pub enum Public {}
66
67/// A tag type indicating that a key has private components.
68pub enum Private {}
69
70/// An identifier of a kind of key.
71#[derive(Debug, Copy, Clone, PartialEq, Eq)]
72pub struct Id(c_int);
73
74impl Id {
75    pub const RSA: Id = Id(ffi::EVP_PKEY_RSA);
76    pub const RSAPSS: Id = Id(ffi::EVP_PKEY_RSA_PSS);
77    pub const DSA: Id = Id(ffi::EVP_PKEY_DSA);
78    pub const DH: Id = Id(ffi::EVP_PKEY_DH);
79    pub const EC: Id = Id(ffi::EVP_PKEY_EC);
80    pub const ED25519: Id = Id(ffi::EVP_PKEY_ED25519);
81    pub const ED448: Id = Id(ffi::EVP_PKEY_ED448);
82    pub const X25519: Id = Id(ffi::EVP_PKEY_X25519);
83    pub const X448: Id = Id(ffi::EVP_PKEY_X448);
84
85    /// Creates a `Id` from an integer representation.
86    #[must_use]
87    pub fn from_raw(value: c_int) -> Id {
88        Id(value)
89    }
90
91    /// Returns the integer representation of the `Id`.
92    #[allow(clippy::trivially_copy_pass_by_ref)]
93    #[must_use]
94    pub fn as_raw(&self) -> c_int {
95        self.0
96    }
97}
98
99/// A trait indicating that a key has parameters.
100#[allow(clippy::missing_safety_doc)]
101pub unsafe trait HasParams {}
102
103unsafe impl HasParams for Params {}
104
105unsafe impl<T> HasParams for T where T: HasPublic {}
106
107/// A trait indicating that a key has public components.
108#[allow(clippy::missing_safety_doc)]
109pub unsafe trait HasPublic {}
110
111unsafe impl HasPublic for Public {}
112
113unsafe impl<T> HasPublic for T where T: HasPrivate {}
114
115/// A trait indicating that a key has private components.
116#[allow(clippy::missing_safety_doc)]
117pub unsafe trait HasPrivate {}
118
119unsafe impl HasPrivate for Private {}
120
121generic_foreign_type_and_impl_send_sync! {
122    type CType = ffi::EVP_PKEY;
123    fn drop = ffi::EVP_PKEY_free;
124
125    /// A public or private key.
126    pub struct PKey<T>;
127    /// Reference to [`PKey`].
128    pub struct PKeyRef<T>;
129}
130
131impl<T> ToOwned for PKeyRef<T> {
132    type Owned = PKey<T>;
133
134    fn to_owned(&self) -> PKey<T> {
135        unsafe {
136            EVP_PKEY_up_ref(self.as_ptr());
137            PKey::from_ptr(self.as_ptr())
138        }
139    }
140}
141
142impl<T> PKeyRef<T> {
143    /// Returns a copy of the internal RSA key.
144    #[corresponds(EVP_PKEY_get1_RSA)]
145    pub fn rsa(&self) -> Result<Rsa<T>, ErrorStack> {
146        unsafe {
147            let rsa = cvt_p(ffi::EVP_PKEY_get1_RSA(self.as_ptr()))?;
148            Ok(Rsa::from_ptr(rsa))
149        }
150    }
151
152    /// Returns a copy of the internal DSA key.
153    #[corresponds(EVP_PKEY_get1_DSA)]
154    pub fn dsa(&self) -> Result<Dsa<T>, ErrorStack> {
155        unsafe {
156            let dsa = cvt_p(ffi::EVP_PKEY_get1_DSA(self.as_ptr()))?;
157            Ok(Dsa::from_ptr(dsa))
158        }
159    }
160
161    /// Returns a copy of the internal DH key.
162    #[corresponds(EVP_PKEY_get1_DH)]
163    pub fn dh(&self) -> Result<Dh<T>, ErrorStack> {
164        unsafe {
165            let dh = cvt_p(ffi::EVP_PKEY_get1_DH(self.as_ptr()))?;
166            Ok(Dh::from_ptr(dh))
167        }
168    }
169
170    /// Returns a copy of the internal elliptic curve key.
171    #[corresponds(EVP_PKEY_get1_EC_KEY)]
172    pub fn ec_key(&self) -> Result<EcKey<T>, ErrorStack> {
173        unsafe {
174            let ec_key = cvt_p(ffi::EVP_PKEY_get1_EC_KEY(self.as_ptr()))?;
175            Ok(EcKey::from_ptr(ec_key))
176        }
177    }
178
179    /// Returns the `Id` that represents the type of this key.
180    #[corresponds(EVP_PKEY_id)]
181    #[must_use]
182    pub fn id(&self) -> Id {
183        unsafe { Id::from_raw(ffi::EVP_PKEY_id(self.as_ptr())) }
184    }
185
186    /// Returns the maximum size of a signature in bytes.
187    #[corresponds(EVP_PKEY_size)]
188    #[must_use]
189    pub fn size(&self) -> usize {
190        unsafe { ffi::EVP_PKEY_size(self.as_ptr()) as usize }
191    }
192}
193
194impl<T> PKeyRef<T>
195where
196    T: HasPublic,
197{
198    to_pem! {
199        /// Serializes the public key into a PEM-encoded SubjectPublicKeyInfo structure.
200        ///
201        /// The output will have a header of `-----BEGIN PUBLIC KEY-----`.
202        #[corresponds(PEM_write_bio_PUBKEY)]
203        public_key_to_pem,
204        ffi::PEM_write_bio_PUBKEY
205    }
206
207    to_der! {
208        /// Serializes the public key into a DER-encoded SubjectPublicKeyInfo structure.
209        #[corresponds(i2d_PUBKEY)]
210        public_key_to_der,
211        ffi::i2d_PUBKEY
212    }
213
214    /// Returns the size of the key.
215    ///
216    /// This corresponds to the bit length of the modulus of an RSA key, and the bit length of the
217    /// group order for an elliptic curve key, for example.
218    #[must_use]
219    pub fn bits(&self) -> u32 {
220        unsafe { ffi::EVP_PKEY_bits(self.as_ptr()) as u32 }
221    }
222
223    /// Compares the public component of this key with another.
224    #[must_use]
225    pub fn public_eq<U>(&self, other: &PKeyRef<U>) -> bool
226    where
227        U: HasPublic,
228    {
229        unsafe { ffi::EVP_PKEY_cmp(self.as_ptr(), other.as_ptr()) == 1 }
230    }
231
232    /// Returns the length of the "raw" form of the public key. Only supported for certain key types.
233    #[corresponds(EVP_PKEY_get_raw_public_key)]
234    pub fn raw_public_key_len(&self) -> Result<usize, ErrorStack> {
235        unsafe {
236            let mut size = 0;
237            _ = cvt_0i(ffi::EVP_PKEY_get_raw_public_key(
238                self.as_ptr(),
239                std::ptr::null_mut(),
240                &mut size,
241            ))?;
242            Ok(size)
243        }
244    }
245
246    /// Outputs a copy of the "raw" form of the public key. Only supported for certain key types.
247    ///
248    /// Returns the used portion of `out`.
249    #[corresponds(EVP_PKEY_get_raw_public_key)]
250    pub fn raw_public_key<'a>(&self, out: &'a mut [u8]) -> Result<&'a [u8], ErrorStack> {
251        unsafe {
252            let mut size = out.len();
253            _ = cvt_0i(ffi::EVP_PKEY_get_raw_public_key(
254                self.as_ptr(),
255                out.as_mut_ptr(),
256                &mut size,
257            ))?;
258            Ok(&out[..size])
259        }
260    }
261}
262
263impl<T> PKeyRef<T>
264where
265    T: HasPrivate,
266{
267    private_key_to_pem! {
268        /// Serializes the private key to a PEM-encoded PKCS#8 PrivateKeyInfo structure.
269        ///
270        /// The output will have a header of `-----BEGIN PRIVATE KEY-----`.
271        #[corresponds(PEM_write_bio_PKCS8PrivateKey)]
272        private_key_to_pem_pkcs8,
273        /// Serializes the private key to a PEM-encoded PKCS#8 EncryptedPrivateKeyInfo structure.
274        ///
275        /// The output will have a header of `-----BEGIN ENCRYPTED PRIVATE KEY-----`.
276        #[corresponds(PEM_write_bio_PKCS8PrivateKey)]
277        private_key_to_pem_pkcs8_passphrase,
278        ffi::PEM_write_bio_PKCS8PrivateKey
279    }
280
281    to_der! {
282        /// Serializes the private key to a DER-encoded key type specific format.
283        #[corresponds(i2d_PrivateKey)]
284        private_key_to_der,
285        ffi::i2d_PrivateKey
286    }
287
288    // This isn't actually PEM output, but `i2d_PKCS8PrivateKey_bio` is documented to be
289    // "identical to the corresponding PEM function", and it's declared in pem.h.
290    private_key_to_pem! {
291        /// Serializes the private key to a DER-encoded PKCS#8 PrivateKeyInfo structure.
292        #[corresponds(i2d_PKCS8PrivateKey_bio)]
293        private_key_to_der_pkcs8,
294        /// Serializes the private key to a DER-encoded PKCS#8 EncryptedPrivateKeyInfo structure.
295        #[corresponds(i2d_PKCS8PrivateKey_bio)]
296        private_key_to_der_pkcs8_passphrase,
297        ffi::i2d_PKCS8PrivateKey_bio
298    }
299
300    /// Returns the length of the "raw" form of the private key. Only supported for certain key types.
301    #[corresponds(EVP_PKEY_get_raw_private_key)]
302    pub fn raw_private_key_len(&self) -> Result<usize, ErrorStack> {
303        unsafe {
304            let mut size = 0;
305            _ = cvt_0i(ffi::EVP_PKEY_get_raw_private_key(
306                self.as_ptr(),
307                std::ptr::null_mut(),
308                &mut size,
309            ))?;
310            Ok(size)
311        }
312    }
313
314    /// Outputs a copy of the "raw" form of the private key. Only supported for certain key types.
315    ///
316    /// Returns the used portion of `out`.
317    #[corresponds(EVP_PKEY_get_raw_private_key)]
318    pub fn raw_private_key<'a>(&self, out: &'a mut [u8]) -> Result<&'a [u8], ErrorStack> {
319        unsafe {
320            let mut size = out.len();
321            _ = cvt_0i(ffi::EVP_PKEY_get_raw_private_key(
322                self.as_ptr(),
323                out.as_mut_ptr(),
324                &mut size,
325            ))?;
326            Ok(&out[..size])
327        }
328    }
329}
330
331impl<T> fmt::Debug for PKey<T> {
332    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
333        let alg = match self.id() {
334            Id::RSA => "RSA",
335            Id::RSAPSS => "RSAPSS",
336            Id::DSA => "DSA",
337            Id::DH => "DH",
338            Id::EC => "EC",
339            Id::ED25519 => "Ed25519",
340            Id::ED448 => "Ed448",
341            _ => "unknown",
342        };
343        fmt.debug_struct("PKey").field("algorithm", &alg).finish()
344        // TODO: Print details for each specific type of key
345    }
346}
347
348impl<T> Clone for PKey<T> {
349    fn clone(&self) -> PKey<T> {
350        PKeyRef::to_owned(self)
351    }
352}
353
354impl<T> PKey<T> {
355    /// Creates a new `PKey` containing an RSA key.
356    #[corresponds(EVP_PKEY_assign_RSA)]
357    pub fn from_rsa(rsa: Rsa<T>) -> Result<PKey<T>, ErrorStack> {
358        unsafe {
359            let evp = cvt_p(ffi::EVP_PKEY_new())?;
360            let pkey = PKey::from_ptr(evp);
361            cvt(ffi::EVP_PKEY_assign(
362                pkey.0,
363                ffi::EVP_PKEY_RSA,
364                rsa.as_ptr().cast(),
365            ))?;
366            mem::forget(rsa);
367            Ok(pkey)
368        }
369    }
370
371    /// Creates a new `PKey` containing an elliptic curve key.
372    #[corresponds(EVP_PKEY_assign_EC_KEY)]
373    pub fn from_ec_key(ec_key: EcKey<T>) -> Result<PKey<T>, ErrorStack> {
374        unsafe {
375            let evp = cvt_p(ffi::EVP_PKEY_new())?;
376            let pkey = PKey::from_ptr(evp);
377            cvt(ffi::EVP_PKEY_assign(
378                pkey.0,
379                ffi::EVP_PKEY_EC,
380                ec_key.as_ptr().cast(),
381            ))?;
382            mem::forget(ec_key);
383            Ok(pkey)
384        }
385    }
386}
387
388impl PKey<Private> {
389    private_key_from_pem! {
390        /// Deserializes a private key from a PEM-encoded key type specific format.
391        #[corresponds(PEM_read_bio_PrivateKey)]
392        private_key_from_pem,
393
394        /// Deserializes a private key from a PEM-encoded encrypted key type specific format.
395        #[corresponds(PEM_read_bio_PrivateKey)]
396        private_key_from_pem_passphrase,
397
398        /// Deserializes a private key from a PEM-encoded encrypted key type specific format.
399        ///
400        /// The callback should fill the password into the provided buffer and return its length.
401        #[corresponds(PEM_read_bio_PrivateKey)]
402        private_key_from_pem_callback,
403        PKey<Private>,
404        ffi::PEM_read_bio_PrivateKey
405    }
406
407    from_der! {
408        /// Decodes a DER-encoded private key.
409        ///
410        /// This function will automatically attempt to detect the underlying key format, and
411        /// supports the unencrypted PKCS#8 PrivateKeyInfo structures as well as key type specific
412        /// formats.
413        #[corresponds(d2i_AutoPrivateKey)]
414        private_key_from_der,
415        PKey<Private>,
416        ffi::d2i_AutoPrivateKey,
417        ::libc::c_long
418    }
419
420    /// Deserializes a DER-formatted PKCS#8 unencrypted private key.
421    ///
422    /// This method is mainly for interoperability reasons. Encrypted keyfiles should be preferred.
423    pub fn private_key_from_pkcs8(der: &[u8]) -> Result<PKey<Private>, ErrorStack> {
424        unsafe {
425            ffi::init();
426            let len = der.len().min(c_long::MAX as usize) as c_long;
427            let p8inf = cvt_p(ffi::d2i_PKCS8_PRIV_KEY_INFO(
428                ptr::null_mut(),
429                &mut der.as_ptr(),
430                len,
431            ))?;
432            let res = cvt_p(ffi::EVP_PKCS82PKEY(p8inf)).map(|p| PKey::from_ptr(p));
433            ffi::PKCS8_PRIV_KEY_INFO_free(p8inf);
434            res
435        }
436    }
437
438    /// Deserializes a DER-formatted PKCS#8 private key, using a callback to retrieve the password
439    /// if the key is encrypted.
440    ///
441    /// The callback should copy the password into the provided buffer and return the number of
442    /// bytes written.
443    pub fn private_key_from_pkcs8_callback<F>(
444        der: &[u8],
445        callback: F,
446    ) -> Result<PKey<Private>, ErrorStack>
447    where
448        F: FnOnce(&mut [u8]) -> Result<usize, ErrorStack>,
449    {
450        unsafe {
451            ffi::init();
452            let mut cb = CallbackState::new(callback);
453            let bio = MemBioSlice::new(der)?;
454            cvt_p(ffi::d2i_PKCS8PrivateKey_bio(
455                bio.as_ptr(),
456                ptr::null_mut(),
457                Some(invoke_passwd_cb::<F>),
458                std::ptr::addr_of_mut!(cb).cast(),
459            ))
460            .map(|p| PKey::from_ptr(p))
461        }
462    }
463
464    /// Deserializes a DER-formatted PKCS#8 private key, using the supplied password if the key is
465    /// encrypted.
466    ///
467    /// # Panics
468    ///
469    /// Panics if `passphrase` contains an embedded null.
470    pub fn private_key_from_pkcs8_passphrase(
471        der: &[u8],
472        passphrase: &[u8],
473    ) -> Result<PKey<Private>, ErrorStack> {
474        unsafe {
475            ffi::init();
476            let bio = MemBioSlice::new(der)?;
477            let passphrase = CString::new(passphrase).map_err(ErrorStack::internal_error)?;
478            cvt_p(ffi::d2i_PKCS8PrivateKey_bio(
479                bio.as_ptr(),
480                ptr::null_mut(),
481                None,
482                passphrase.as_ptr().cast_mut().cast(),
483            ))
484            .map(|p| PKey::from_ptr(p))
485        }
486    }
487}
488
489impl PKey<Public> {
490    from_pem! {
491        /// Decodes a PEM-encoded SubjectPublicKeyInfo structure.
492        ///
493        /// The input should have a header of `-----BEGIN PUBLIC KEY-----`.
494        #[corresponds(PEM_read_bio_PUBKEY)]
495        public_key_from_pem,
496        PKey<Public>,
497        ffi::PEM_read_bio_PUBKEY
498    }
499
500    from_der! {
501        /// Decodes a DER-encoded SubjectPublicKeyInfo structure.
502        #[corresponds(d2i_PUBKEY)]
503        public_key_from_der,
504        PKey<Public>,
505        ffi::d2i_PUBKEY,
506        ::libc::c_long
507    }
508}
509
510use crate::ffi::EVP_PKEY_up_ref;
511
512#[cfg(test)]
513mod tests {
514    use hex::FromHex as _;
515
516    use crate::ec::EcKey;
517    use crate::nid::Nid;
518    use crate::rsa::Rsa;
519    use crate::symm::Cipher;
520
521    use super::*;
522
523    #[test]
524    fn test_to_password() {
525        let rsa = Rsa::generate(2048).unwrap();
526        let pkey = PKey::from_rsa(rsa).unwrap();
527        let pem = pkey
528            .private_key_to_pem_pkcs8_passphrase(Cipher::aes_128_cbc(), b"foobar")
529            .unwrap();
530        PKey::private_key_from_pem_passphrase(&pem, b"foobar").unwrap();
531        assert!(PKey::private_key_from_pem_passphrase(&pem, b"fizzbuzz").is_err());
532    }
533
534    #[test]
535    fn test_unencrypted_pkcs8() {
536        let key = include_bytes!("../test/pkcs8-nocrypt.der");
537        PKey::private_key_from_pkcs8(key).unwrap();
538    }
539
540    #[test]
541    fn test_encrypted_pkcs8_passphrase() {
542        let key = include_bytes!("../test/pkcs8.der");
543        PKey::private_key_from_pkcs8_passphrase(key, b"mypass").unwrap();
544    }
545
546    #[test]
547    fn test_encrypted_pkcs8_callback() {
548        let mut password_queried = false;
549        let key = include_bytes!("../test/pkcs8.der");
550        PKey::private_key_from_pkcs8_callback(key, |password| {
551            password_queried = true;
552            password[..6].copy_from_slice(b"mypass");
553            Ok(6)
554        })
555        .unwrap();
556        assert!(password_queried);
557    }
558
559    #[test]
560    fn test_private_key_from_pem() {
561        let key = include_bytes!("../test/key.pem");
562        PKey::private_key_from_pem(key).unwrap();
563    }
564
565    #[test]
566    fn test_public_key_from_pem() {
567        let key = include_bytes!("../test/key.pem.pub");
568        PKey::public_key_from_pem(key).unwrap();
569    }
570
571    #[test]
572    fn test_public_key_from_der() {
573        let key = include_bytes!("../test/key.der.pub");
574        PKey::public_key_from_der(key).unwrap();
575    }
576
577    #[test]
578    fn test_private_key_from_der() {
579        let key = include_bytes!("../test/key.der");
580        PKey::private_key_from_der(key).unwrap();
581    }
582
583    #[test]
584    fn test_pem() {
585        let key = include_bytes!("../test/key.pem");
586        let key = PKey::private_key_from_pem(key).unwrap();
587
588        let priv_key = key.private_key_to_pem_pkcs8().unwrap();
589        let pub_key = key.public_key_to_pem().unwrap();
590
591        // As a super-simple verification, just check that the buffers contain
592        // the `PRIVATE KEY` or `PUBLIC KEY` strings.
593        assert!(priv_key.windows(11).any(|s| s == b"PRIVATE KEY"));
594        assert!(pub_key.windows(10).any(|s| s == b"PUBLIC KEY"));
595    }
596
597    #[test]
598    fn test_der_pkcs8() {
599        let key = include_bytes!("../test/key.der");
600        let key = PKey::private_key_from_der(key).unwrap();
601
602        let priv_key = key.private_key_to_der_pkcs8().unwrap();
603
604        // Check that this has the correct PKCS#8 version number and algorithm.
605        assert_eq!(hex::encode(&priv_key[4..=6]), "020100"); // Version 0
606        assert_eq!(hex::encode(&priv_key[9..=19]), "06092a864886f70d010101"); // Algorithm RSA/PKCS#1
607    }
608
609    #[test]
610    fn test_rsa_accessor() {
611        let rsa = Rsa::generate(2048).unwrap();
612        let pkey = PKey::from_rsa(rsa).unwrap();
613        pkey.rsa().unwrap();
614        assert_eq!(pkey.id(), Id::RSA);
615        assert!(pkey.dsa().is_err());
616    }
617
618    #[test]
619    fn test_ec_key_accessor() {
620        let ec_key = EcKey::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
621        let pkey = PKey::from_ec_key(ec_key).unwrap();
622        pkey.ec_key().unwrap();
623        assert_eq!(pkey.id(), Id::EC);
624        assert!(pkey.rsa().is_err());
625    }
626
627    #[test]
628    fn test_raw_accessors() {
629        const ED25519_PRIVATE_KEY_DER: &str = concat!(
630            "302e020100300506032b6570042204207c8c6497f9960d5595d7815f550569e5",
631            "f77764ac97e63e339aaa68cc1512b683"
632        );
633        let pkey =
634            PKey::private_key_from_der(&Vec::from_hex(ED25519_PRIVATE_KEY_DER).unwrap()).unwrap();
635        assert_eq!(pkey.id(), Id::ED25519);
636
637        let priv_len = pkey.raw_private_key_len().unwrap();
638        assert_eq!(priv_len, 32);
639        let mut raw_private_key_buf = [0; 40];
640        let raw_private_key = pkey.raw_private_key(&mut raw_private_key_buf).unwrap();
641        assert_eq!(raw_private_key.len(), 32);
642        assert_ne!(raw_private_key, [0; 32]);
643        pkey.raw_private_key(&mut [0; 5])
644            .expect_err("buffer too small");
645
646        let pub_len = pkey.raw_public_key_len().unwrap();
647        assert_eq!(pub_len, 32);
648        let mut raw_public_key_buf = [0; 40];
649        let raw_public_key = pkey.raw_public_key(&mut raw_public_key_buf).unwrap();
650        assert_eq!(raw_public_key.len(), 32);
651        assert_ne!(raw_public_key, [0; 32]);
652        assert_ne!(raw_public_key, raw_private_key);
653        pkey.raw_public_key(&mut [0; 5])
654            .expect_err("buffer too small");
655    }
656}