| // Copyright 2026 The BoringSSL Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| //! Private and public keys |
| //! |
| //! The private keys processed here can be paired with certificates to perform further |
| //! authentication. |
| //! |
| //! ```rust |
| //! # use bssl_x509::certificates::X509Certificate; |
| //! # use bssl_x509::keys::PrivateKey; |
| //! # let pem = include_bytes!("tests/BoringSSLTestCA.key"); |
| //! # let crt = include_bytes!("tests/BoringSSLTestCA.crt"); |
| //! # let crt = X509Certificate::parse_one_from_pem(crt).unwrap(); |
| //! let key = PrivateKey::from_pem( |
| //! pem, /*password_callback=*/ || b"BoringSSL is awesome!").unwrap(); |
| //! assert!(crt.matches_private_key(&key)); |
| //! ``` |
| //! |
| //! The public keys can be derived from [`crate::certificates::X509Certificate`] or [`PrivateKey`]s. |
| //! |
| //! ```rust |
| //! # use bssl_x509::certificates::X509Certificate; |
| //! # use bssl_x509::keys::PrivateKey; |
| //! # let pem = include_bytes!("tests/BoringSSLTestCA.key"); |
| //! # let crt = include_bytes!("tests/BoringSSLTestCA.crt"); |
| //! # let crt = X509Certificate::parse_one_from_pem(crt).unwrap(); |
| //! # let key = PrivateKey::from_pem( |
| //! # pem, /*password_callback=*/ || b"BoringSSL is awesome!").unwrap(); |
| //! assert_eq!( |
| //! crt.public_key().unwrap().to_der(), |
| //! key.to_public_key().to_der() |
| //! ); |
| //! ``` |
| |
| use alloc::vec::Vec; |
| use core::{ |
| ffi::{c_char, c_int, c_void}, |
| mem::transmute, |
| panic::AssertUnwindSafe, |
| ptr::{NonNull, null_mut}, |
| }; |
| |
| use bssl_crypto::{FfiSlice, cbb_to_buffer}; |
| use bssl_macros::bssl_enum; |
| |
| use crate::ffi::abort_on_panic; |
| use crate::{errors::PkiError, ffi::Bio}; |
| |
| bssl_enum! { |
| /// EVP public key algorithm types. |
| #[derive(Debug, Copy, Clone, PartialEq, Eq)] |
| pub enum PrivateKeyAlgorithm: i32 { |
| /// RSA |
| Rsa = bssl_sys::EVP_PKEY_RSA as i32, |
| /// RSA-PSS |
| RsaPss = bssl_sys::EVP_PKEY_RSA_PSS as i32, |
| /// EC |
| Ec = bssl_sys::EVP_PKEY_EC as i32, |
| /// Ed25519 |
| Ed25519 = bssl_sys::EVP_PKEY_ED25519 as i32, |
| /// X25519 |
| X25519 = bssl_sys::EVP_PKEY_X25519 as i32, |
| /// DSA |
| Dsa = bssl_sys::EVP_PKEY_DSA as i32, |
| /// DH |
| Dh = bssl_sys::EVP_PKEY_DH as i32, |
| } |
| } |
| |
| /// A private key. |
| #[repr(transparent)] |
| pub struct PrivateKey(NonNull<bssl_sys::EVP_PKEY>); |
| // Safety: `PrivateKey` is locked as immutable at this type state. |
| unsafe impl Send for PrivateKey {} |
| unsafe impl Sync for PrivateKey {} |
| |
| impl PrivateKey { |
| /// Parse a [`PrivateKey`] from PEM encoding. |
| pub fn from_pem<'a, F: 'a + FnMut() -> &'a [u8]>( |
| pem: &[u8], |
| mut password_callback: F, |
| ) -> Result<Self, PkiError> { |
| let mut bio = Bio::from_bytes(pem)?; |
| |
| let mut priv_key = null_mut(); |
| |
| unsafe extern "C" fn write_password<'a, F: 'a + FnMut() -> &'a [u8]>( |
| out: *mut c_char, |
| size: c_int, |
| _rwflag: c_int, |
| ctxt: *mut c_void, |
| ) -> c_int { |
| if size < 0 { |
| return -1; |
| } |
| let password_callback = AssertUnwindSafe(unsafe { |
| // Safety: `ctxt` is a valid callback pointer and outlived by `'a`, so it must be |
| // valid when invoked. |
| &mut *(ctxt as *mut F) |
| }); |
| let get_password = move || { |
| let AssertUnwindSafe(pass_callback) = { password_callback }; |
| let password = pass_callback(); |
| let Ok(len) = password.len().try_into() else { |
| return -1; |
| }; |
| if len > size { |
| return -1; |
| } |
| unsafe { |
| // Safety: |
| // - `src` is valid and not larger than `out`. |
| // - `out` is valid per BoringSSL specification. |
| // - `src` and `out` are both 1-aligned. |
| core::ptr::copy(password.as_ffi_void_ptr(), out as _, password.len()); |
| } |
| len |
| }; |
| abort_on_panic(get_password) |
| } |
| |
| let evp_pkey = unsafe { |
| // Safety: |
| // - the BIO is still valid. |
| // - the `priv_key` pointer is null, so the function will allocate |
| // a new structure. |
| bssl_sys::PEM_read_bio_PrivateKey( |
| bio.ptr(), |
| &raw mut priv_key, |
| Some(write_password::<'a, F>), |
| &raw mut password_callback as _, |
| ) |
| }; |
| NonNull::new(evp_pkey) |
| .map(Self) |
| .ok_or_else(PkiError::extract_lib_err) |
| } |
| |
| /// Get the algorithm ID of the private key. |
| /// |
| /// This method returns [`None`] if the key algorithm is unrecognised. |
| pub fn algorithm(&self) -> Option<PrivateKeyAlgorithm> { |
| let id = unsafe { |
| // Safety: self.0 is valid. |
| bssl_sys::EVP_PKEY_id(self.ptr()) |
| }; |
| let id = i32::try_from(id).ok()?; |
| PrivateKeyAlgorithm::try_from(id).ok() |
| } |
| |
| /// This method releases ownership of the internal key handle. |
| /// |
| /// # Safety |
| /// - This method should only be used for cross-language interoperability, |
| /// so the function that accepts an `EVP_PKEY*` handle must uses exactly the same |
| /// BoringSSL as this crate is linked to. |
| pub fn into_raw(self) -> *mut bssl_sys::EVP_PKEY { |
| let ptr = self.ptr(); |
| core::mem::forget(self); |
| ptr |
| } |
| |
| /// This method returns the internal key handle. |
| /// # Safety |
| /// - This method should only be used for cross-language interoperability, |
| /// so the function that accepts an `EVP_PKEY*` handle must uses exactly the same |
| /// BoringSSL as this crate is linked to. |
| /// - The caller must ensure that all future uses of the key handle does not mutate the content. |
| /// - All subsequent uses of the returned handle must be outlived by `self`. |
| /// - All subsequent uses of the returned handle must not race with uses of `self`. |
| /// unless only refcount is adjusted, one must use locks when necessary. |
| pub unsafe fn as_mut_ptr(&mut self) -> *mut bssl_sys::EVP_PKEY { |
| self.ptr() |
| } |
| |
| pub(crate) fn ptr(&self) -> *mut bssl_sys::EVP_PKEY { |
| self.0.as_ptr() |
| } |
| |
| /// Encode the private key to a DER-encoded `PrivateKeyInfo` structure as per [RFC 5208]. |
| /// |
| /// [RFC 5208]: <https://datatracker.ietf.org/doc/html/rfc5208#section-5> |
| pub fn private_key_to_der(&self) -> Vec<u8> { |
| cbb_to_buffer(0, |cbb| unsafe { |
| // Safety: this call does not mutate the private key internals |
| // and only allocation happens. |
| assert_eq!(1, bssl_sys::EVP_marshal_private_key(cbb, self.ptr())) |
| }) |
| .as_ref() |
| .to_vec() |
| } |
| |
| /// Get the public key. |
| pub fn to_public_key(&self) -> &PublicKey { |
| unsafe { |
| // Safety: `self.0` is still valid. |
| debug_assert_eq!(1, bssl_sys::EVP_PKEY_has_public(self.ptr())); |
| } |
| unsafe { |
| // Safety: |
| // - the `EVP_PKEY*` in `PrivateKey` always contains a public key. |
| // - `PublicKey` is a transparent wrapper of `EVP_PKEY*` |
| transmute(self) |
| } |
| } |
| } |
| |
| impl Clone for PrivateKey { |
| fn clone(&self) -> Self { |
| unsafe { |
| // Safety: `self.0` is still valid at cloning. |
| bssl_sys::EVP_PKEY_up_ref(self.ptr()); |
| } |
| Self(self.0) |
| } |
| } |
| |
| impl Drop for PrivateKey { |
| fn drop(&mut self) { |
| unsafe { |
| // Safety: `self.0` is still valid at dropping. |
| bssl_sys::EVP_PKEY_free(self.ptr()); |
| } |
| } |
| } |
| |
| /// A public key. |
| #[repr(transparent)] |
| pub struct PublicKey(pub(crate) NonNull<bssl_sys::EVP_PKEY>); |
| // Safety: `PublicKey` is locked as immutable at this type state. |
| unsafe impl Send for PublicKey {} |
| unsafe impl Sync for PublicKey {} |
| |
| impl PublicKey { |
| /// Parse a [`PublicKey`] from PEM encoding. |
| pub fn from_pem(pem: &[u8]) -> Result<Self, PkiError> { |
| let mut bio = Bio::from_bytes(pem)?; |
| |
| let mut pub_key = null_mut(); |
| let evp_pkey = unsafe { |
| // Safety: |
| // - the BIO is still valid. |
| // - the `pub_key` pointer is null, so the function will allocate |
| // a new structure. |
| bssl_sys::PEM_read_bio_PUBKEY(bio.ptr(), &raw mut pub_key, None, null_mut()) |
| }; |
| NonNull::new(evp_pkey) |
| .map(Self) |
| .ok_or_else(PkiError::extract_lib_err) |
| } |
| |
| /// Serialize the public key into a DER-encoded `SubjectPublicKeyInfo` structure. |
| pub fn to_der(&self) -> Vec<u8> { |
| cbb_to_buffer(0, |cbb| unsafe { |
| // Safety: this call does not mutate the private key internals |
| // and only allocation happens. |
| assert_eq!(1, bssl_sys::EVP_marshal_public_key(cbb, self.ptr())) |
| }) |
| .as_ref() |
| .to_vec() |
| } |
| |
| pub(crate) fn ptr(&self) -> *mut bssl_sys::EVP_PKEY { |
| self.0.as_ptr() |
| } |
| |
| /// This method returns the internal key handle. |
| /// # Safety |
| /// - This method should only be used for cross-language interoperability, |
| /// so the function that accepts an `EVP_PKEY*` handle must uses exactly the same |
| /// BoringSSL as this crate is linked to. |
| /// - The caller must ensure that all future uses of the key handle does not mutate the content. |
| /// - All subsequent uses of the returned handle must be outlived by `self`. |
| /// - All subsequent uses of the returned handle must not race with uses of `self`. |
| /// unless only refcount is adjusted, one must use locks when necessary. |
| pub unsafe fn as_mut_ptr(&mut self) -> *mut bssl_sys::EVP_PKEY { |
| self.ptr() |
| } |
| } |
| |
| impl Clone for PublicKey { |
| fn clone(&self) -> Self { |
| unsafe { |
| // Safety: `self.0` is still valid at cloning. |
| bssl_sys::EVP_PKEY_up_ref(self.ptr()); |
| } |
| Self(self.0) |
| } |
| } |
| |
| impl Drop for PublicKey { |
| fn drop(&mut self) { |
| unsafe { |
| // Safety: `self.0` is still valid at dropping. |
| bssl_sys::EVP_PKEY_free(self.ptr()); |
| } |
| } |
| } |