blob: 9ea8bda870d5590bf22a05e805cfb5f521bad8db [file] [log] [blame]
// 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 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));
//! ```
use core::ffi::{c_char, c_int, c_void};
use core::panic::AssertUnwindSafe;
use core::ptr::NonNull;
use core::ptr::null_mut;
use bssl_crypto::FfiSlice;
use bssl_macros::bssl_enum;
use crate::ffi::maybe_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.
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
};
maybe_panic(get_password)
}
let evp_pkey = unsafe {
// Safety:
// - the BIO is still valid;
// - the `&raw mut priv_key` pointer is not null, so the function will allocate
// a new structure;
// - the return value can be discarded since we provided a location to hold the handle,
// whose lifetime will be managed from this function.
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.
///
/// This method should only be used for cross-language interoperability.
pub fn into_raw(self) -> *mut bssl_sys::EVP_PKEY {
let ptr = self.ptr();
core::mem::forget(self);
ptr
}
pub(crate) fn ptr(&self) -> *mut bssl_sys::EVP_PKEY {
self.0.as_ptr()
}
/// Extract the handle
///
/// # Safety
///
/// `self` **must** outlive the uses of the returned handle.
/// Verify the callsite contract to honour the lifetime contracts.
pub unsafe fn as_raw(&self) -> *mut bssl_sys::EVP_PKEY {
self.ptr()
}
}
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());
}
}
}