use crate::ffi;
use crate::libc_types::{c_int, c_long};
use foreign_types::{ForeignType, ForeignTypeRef};
use openssl_macros::corresponds;
use std::ffi::CString;
use std::fmt;
use std::mem;
use std::ptr;
use crate::bio::MemBioSlice;
use crate::dh::Dh;
use crate::dsa::Dsa;
use crate::ec::EcKey;
use crate::error::ErrorStack;
use crate::rsa::Rsa;
use crate::util::{invoke_passwd_cb, CallbackState};
use crate::{cvt, cvt_0i, cvt_p};
pub enum Params {}
pub enum Public {}
pub enum Private {}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Id(c_int);
impl Id {
pub const RSA: Id = Id(ffi::EVP_PKEY_RSA);
pub const RSAPSS: Id = Id(ffi::EVP_PKEY_RSA_PSS);
pub const DSA: Id = Id(ffi::EVP_PKEY_DSA);
pub const DH: Id = Id(ffi::EVP_PKEY_DH);
pub const EC: Id = Id(ffi::EVP_PKEY_EC);
pub const ED25519: Id = Id(ffi::EVP_PKEY_ED25519);
pub const ED448: Id = Id(ffi::EVP_PKEY_ED448);
pub const X25519: Id = Id(ffi::EVP_PKEY_X25519);
pub const X448: Id = Id(ffi::EVP_PKEY_X448);
#[must_use]
pub fn from_raw(value: c_int) -> Id {
Id(value)
}
#[allow(clippy::trivially_copy_pass_by_ref)]
#[must_use]
pub fn as_raw(&self) -> c_int {
self.0
}
}
#[allow(clippy::missing_safety_doc)]
pub unsafe trait HasParams {}
unsafe impl HasParams for Params {}
unsafe impl<T> HasParams for T where T: HasPublic {}
#[allow(clippy::missing_safety_doc)]
pub unsafe trait HasPublic {}
unsafe impl HasPublic for Public {}
unsafe impl<T> HasPublic for T where T: HasPrivate {}
#[allow(clippy::missing_safety_doc)]
pub unsafe trait HasPrivate {}
unsafe impl HasPrivate for Private {}
generic_foreign_type_and_impl_send_sync! {
type CType = ffi::EVP_PKEY;
fn drop = ffi::EVP_PKEY_free;
pub struct PKey<T>;
pub struct PKeyRef<T>;
}
impl<T> ToOwned for PKeyRef<T> {
type Owned = PKey<T>;
fn to_owned(&self) -> PKey<T> {
unsafe {
EVP_PKEY_up_ref(self.as_ptr());
PKey::from_ptr(self.as_ptr())
}
}
}
impl<T> PKeyRef<T> {
#[corresponds(EVP_PKEY_get1_RSA)]
pub fn rsa(&self) -> Result<Rsa<T>, ErrorStack> {
unsafe {
let rsa = cvt_p(ffi::EVP_PKEY_get1_RSA(self.as_ptr()))?;
Ok(Rsa::from_ptr(rsa))
}
}
#[corresponds(EVP_PKEY_get1_DSA)]
pub fn dsa(&self) -> Result<Dsa<T>, ErrorStack> {
unsafe {
let dsa = cvt_p(ffi::EVP_PKEY_get1_DSA(self.as_ptr()))?;
Ok(Dsa::from_ptr(dsa))
}
}
#[corresponds(EVP_PKEY_get1_DH)]
pub fn dh(&self) -> Result<Dh<T>, ErrorStack> {
unsafe {
let dh = cvt_p(ffi::EVP_PKEY_get1_DH(self.as_ptr()))?;
Ok(Dh::from_ptr(dh))
}
}
#[corresponds(EVP_PKEY_get1_EC_KEY)]
pub fn ec_key(&self) -> Result<EcKey<T>, ErrorStack> {
unsafe {
let ec_key = cvt_p(ffi::EVP_PKEY_get1_EC_KEY(self.as_ptr()))?;
Ok(EcKey::from_ptr(ec_key))
}
}
#[corresponds(EVP_PKEY_id)]
#[must_use]
pub fn id(&self) -> Id {
unsafe { Id::from_raw(ffi::EVP_PKEY_id(self.as_ptr())) }
}
#[corresponds(EVP_PKEY_size)]
#[must_use]
pub fn size(&self) -> usize {
unsafe { ffi::EVP_PKEY_size(self.as_ptr()) as usize }
}
}
impl<T> PKeyRef<T>
where
T: HasPublic,
{
to_pem! {
#[corresponds(PEM_write_bio_PUBKEY)]
public_key_to_pem,
ffi::PEM_write_bio_PUBKEY
}
to_der! {
#[corresponds(i2d_PUBKEY)]
public_key_to_der,
ffi::i2d_PUBKEY
}
#[must_use]
pub fn bits(&self) -> u32 {
unsafe { ffi::EVP_PKEY_bits(self.as_ptr()) as u32 }
}
#[must_use]
pub fn public_eq<U>(&self, other: &PKeyRef<U>) -> bool
where
U: HasPublic,
{
unsafe { ffi::EVP_PKEY_cmp(self.as_ptr(), other.as_ptr()) == 1 }
}
#[corresponds(EVP_PKEY_get_raw_public_key)]
pub fn raw_public_key_len(&self) -> Result<usize, ErrorStack> {
unsafe {
let mut size = 0;
_ = cvt_0i(ffi::EVP_PKEY_get_raw_public_key(
self.as_ptr(),
std::ptr::null_mut(),
&mut size,
))?;
Ok(size)
}
}
#[corresponds(EVP_PKEY_get_raw_public_key)]
pub fn raw_public_key<'a>(&self, out: &'a mut [u8]) -> Result<&'a [u8], ErrorStack> {
unsafe {
let mut size = out.len();
_ = cvt_0i(ffi::EVP_PKEY_get_raw_public_key(
self.as_ptr(),
out.as_mut_ptr(),
&mut size,
))?;
Ok(&out[..size])
}
}
}
impl<T> PKeyRef<T>
where
T: HasPrivate,
{
private_key_to_pem! {
#[corresponds(PEM_write_bio_PKCS8PrivateKey)]
private_key_to_pem_pkcs8,
#[corresponds(PEM_write_bio_PKCS8PrivateKey)]
private_key_to_pem_pkcs8_passphrase,
ffi::PEM_write_bio_PKCS8PrivateKey
}
to_der! {
#[corresponds(i2d_PrivateKey)]
private_key_to_der,
ffi::i2d_PrivateKey
}
private_key_to_pem! {
#[corresponds(i2d_PKCS8PrivateKey_bio)]
private_key_to_der_pkcs8,
#[corresponds(i2d_PKCS8PrivateKey_bio)]
private_key_to_der_pkcs8_passphrase,
ffi::i2d_PKCS8PrivateKey_bio
}
#[corresponds(EVP_PKEY_get_raw_private_key)]
pub fn raw_private_key_len(&self) -> Result<usize, ErrorStack> {
unsafe {
let mut size = 0;
_ = cvt_0i(ffi::EVP_PKEY_get_raw_private_key(
self.as_ptr(),
std::ptr::null_mut(),
&mut size,
))?;
Ok(size)
}
}
#[corresponds(EVP_PKEY_get_raw_private_key)]
pub fn raw_private_key<'a>(&self, out: &'a mut [u8]) -> Result<&'a [u8], ErrorStack> {
unsafe {
let mut size = out.len();
_ = cvt_0i(ffi::EVP_PKEY_get_raw_private_key(
self.as_ptr(),
out.as_mut_ptr(),
&mut size,
))?;
Ok(&out[..size])
}
}
}
impl<T> fmt::Debug for PKey<T> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let alg = match self.id() {
Id::RSA => "RSA",
Id::RSAPSS => "RSAPSS",
Id::DSA => "DSA",
Id::DH => "DH",
Id::EC => "EC",
Id::ED25519 => "Ed25519",
Id::ED448 => "Ed448",
_ => "unknown",
};
fmt.debug_struct("PKey").field("algorithm", &alg).finish()
}
}
impl<T> Clone for PKey<T> {
fn clone(&self) -> PKey<T> {
PKeyRef::to_owned(self)
}
}
impl<T> PKey<T> {
#[corresponds(EVP_PKEY_assign_RSA)]
pub fn from_rsa(rsa: Rsa<T>) -> Result<PKey<T>, ErrorStack> {
unsafe {
let evp = cvt_p(ffi::EVP_PKEY_new())?;
let pkey = PKey::from_ptr(evp);
cvt(ffi::EVP_PKEY_assign(
pkey.0,
ffi::EVP_PKEY_RSA,
rsa.as_ptr().cast(),
))?;
mem::forget(rsa);
Ok(pkey)
}
}
#[corresponds(EVP_PKEY_assign_EC_KEY)]
pub fn from_ec_key(ec_key: EcKey<T>) -> Result<PKey<T>, ErrorStack> {
unsafe {
let evp = cvt_p(ffi::EVP_PKEY_new())?;
let pkey = PKey::from_ptr(evp);
cvt(ffi::EVP_PKEY_assign(
pkey.0,
ffi::EVP_PKEY_EC,
ec_key.as_ptr().cast(),
))?;
mem::forget(ec_key);
Ok(pkey)
}
}
#[corresponds(EVP_PKEY_assign_DSA)]
pub fn from_dsa(dsa: Dsa<T>) -> Result<PKey<T>, ErrorStack> {
unsafe {
let evp = cvt_p(ffi::EVP_PKEY_new())?;
let pkey = PKey::from_ptr(evp);
cvt(ffi::EVP_PKEY_assign(
pkey.0,
ffi::EVP_PKEY_DSA,
dsa.as_ptr().cast(),
))?;
mem::forget(dsa);
Ok(pkey)
}
}
#[corresponds(EVP_PKEY_assign_DH)]
pub fn from_dh(dh: Dh<T>) -> Result<PKey<T>, ErrorStack>
where
T: HasParams,
{
unsafe {
let evp = cvt_p(ffi::EVP_PKEY_new())?;
let pkey = PKey::from_ptr(evp);
cvt(ffi::EVP_PKEY_assign(
pkey.0,
ffi::EVP_PKEY_DH,
dh.as_ptr().cast(),
))?;
mem::forget(dh);
Ok(pkey)
}
}
}
impl PKey<Private> {
fn from_raw_private_key(id: Id, key: &[u8]) -> Result<PKey<Private>, ErrorStack> {
unsafe {
ffi::init();
let alg = raw_key_algorithm(id)?;
cvt_p(ffi::EVP_PKEY_from_raw_private_key(
alg,
key.as_ptr(),
key.len(),
))
.map(|p| PKey::from_ptr(p))
}
}
pub fn from_ed25519_private_key(key: &[u8]) -> Result<PKey<Private>, ErrorStack> {
Self::from_raw_private_key(Id::ED25519, key)
}
pub fn from_x25519_private_key(key: &[u8]) -> Result<PKey<Private>, ErrorStack> {
Self::from_raw_private_key(Id::X25519, key)
}
private_key_from_pem! {
#[corresponds(PEM_read_bio_PrivateKey)]
private_key_from_pem,
#[corresponds(PEM_read_bio_PrivateKey)]
private_key_from_pem_passphrase,
#[corresponds(PEM_read_bio_PrivateKey)]
private_key_from_pem_callback,
PKey<Private>,
ffi::PEM_read_bio_PrivateKey
}
from_der! {
#[corresponds(d2i_AutoPrivateKey)]
private_key_from_der,
PKey<Private>,
ffi::d2i_AutoPrivateKey,
::libc::c_long
}
pub fn private_key_from_pkcs8(der: &[u8]) -> Result<PKey<Private>, ErrorStack> {
unsafe {
ffi::init();
let len = der.len().min(c_long::MAX as usize) as c_long;
let p8inf = cvt_p(ffi::d2i_PKCS8_PRIV_KEY_INFO(
ptr::null_mut(),
&mut der.as_ptr(),
len,
))?;
let res = cvt_p(ffi::EVP_PKCS82PKEY(p8inf)).map(|p| PKey::from_ptr(p));
ffi::PKCS8_PRIV_KEY_INFO_free(p8inf);
res
}
}
pub fn private_key_from_pkcs8_callback<F>(
der: &[u8],
callback: F,
) -> Result<PKey<Private>, ErrorStack>
where
F: FnOnce(&mut [u8]) -> Result<usize, ErrorStack>,
{
unsafe {
ffi::init();
let mut cb = CallbackState::new(callback);
let bio = MemBioSlice::new(der)?;
cvt_p(ffi::d2i_PKCS8PrivateKey_bio(
bio.as_ptr(),
ptr::null_mut(),
Some(invoke_passwd_cb::<F>),
std::ptr::addr_of_mut!(cb).cast(),
))
.map(|p| PKey::from_ptr(p))
}
}
pub fn private_key_from_pkcs8_passphrase(
der: &[u8],
passphrase: &[u8],
) -> Result<PKey<Private>, ErrorStack> {
unsafe {
ffi::init();
let bio = MemBioSlice::new(der)?;
let passphrase = CString::new(passphrase).map_err(ErrorStack::internal_error)?;
cvt_p(ffi::d2i_PKCS8PrivateKey_bio(
bio.as_ptr(),
ptr::null_mut(),
None,
passphrase.as_ptr().cast_mut().cast(),
))
.map(|p| PKey::from_ptr(p))
}
}
}
impl PKey<Public> {
fn from_raw_public_key(id: Id, key: &[u8]) -> Result<PKey<Public>, ErrorStack> {
unsafe {
ffi::init();
let alg = raw_key_algorithm(id)?;
cvt_p(ffi::EVP_PKEY_from_raw_public_key(
alg,
key.as_ptr(),
key.len(),
))
.map(|p| PKey::from_ptr(p))
}
}
pub fn from_ed25519_public_key(key: &[u8]) -> Result<PKey<Public>, ErrorStack> {
Self::from_raw_public_key(Id::ED25519, key)
}
pub fn from_x25519_public_key(key: &[u8]) -> Result<PKey<Public>, ErrorStack> {
Self::from_raw_public_key(Id::X25519, key)
}
from_pem! {
#[corresponds(PEM_read_bio_PUBKEY)]
public_key_from_pem,
PKey<Public>,
ffi::PEM_read_bio_PUBKEY
}
from_der! {
#[corresponds(d2i_PUBKEY)]
public_key_from_der,
PKey<Public>,
ffi::d2i_PUBKEY,
::libc::c_long
}
}
use crate::ffi::EVP_PKEY_up_ref;
fn raw_key_algorithm(id: Id) -> Result<*const ffi::EVP_PKEY_ALG, ErrorStack> {
let alg = unsafe {
match id {
Id::ED25519 => ffi::EVP_pkey_ed25519(),
Id::X25519 => ffi::EVP_pkey_x25519(),
_ => {
return Err(ErrorStack::internal_error_str(
"unsupported raw key algorithm",
));
}
}
};
if alg.is_null() {
Err(ErrorStack::internal_error_str("missing raw key algorithm"))
} else {
Ok(alg)
}
}
#[cfg(test)]
mod tests {
use hex::FromHex as _;
use crate::bn::BigNum;
use crate::dh::Dh;
use crate::dsa::Dsa;
use crate::ec::EcKey;
use crate::nid::Nid;
use crate::rsa::Rsa;
use crate::symm::Cipher;
use super::*;
#[test]
fn test_to_password() {
let rsa = Rsa::generate(2048).unwrap();
let pkey = PKey::from_rsa(rsa).unwrap();
let pem = pkey
.private_key_to_pem_pkcs8_passphrase(Cipher::aes_128_cbc(), b"foobar")
.unwrap();
PKey::private_key_from_pem_passphrase(&pem, b"foobar").unwrap();
assert!(PKey::private_key_from_pem_passphrase(&pem, b"fizzbuzz").is_err());
}
#[test]
fn test_unencrypted_pkcs8() {
let key = include_bytes!("../test/pkcs8-nocrypt.der");
PKey::private_key_from_pkcs8(key).unwrap();
}
#[test]
fn test_encrypted_pkcs8_passphrase() {
let key = include_bytes!("../test/pkcs8.der");
PKey::private_key_from_pkcs8_passphrase(key, b"mypass").unwrap();
}
#[test]
fn test_encrypted_pkcs8_callback() {
let mut password_queried = false;
let key = include_bytes!("../test/pkcs8.der");
PKey::private_key_from_pkcs8_callback(key, |password| {
password_queried = true;
password[..6].copy_from_slice(b"mypass");
Ok(6)
})
.unwrap();
assert!(password_queried);
}
#[test]
fn test_private_key_from_pem() {
let key = include_bytes!("../test/key.pem");
PKey::private_key_from_pem(key).unwrap();
}
#[test]
fn test_public_key_from_pem() {
let key = include_bytes!("../test/key.pem.pub");
PKey::public_key_from_pem(key).unwrap();
}
#[test]
fn test_public_key_from_der() {
let key = include_bytes!("../test/key.der.pub");
PKey::public_key_from_der(key).unwrap();
}
#[test]
fn test_private_key_from_der() {
let key = include_bytes!("../test/key.der");
PKey::private_key_from_der(key).unwrap();
}
#[test]
fn test_pem() {
let key = include_bytes!("../test/key.pem");
let key = PKey::private_key_from_pem(key).unwrap();
let priv_key = key.private_key_to_pem_pkcs8().unwrap();
let pub_key = key.public_key_to_pem().unwrap();
assert!(priv_key.windows(11).any(|s| s == b"PRIVATE KEY"));
assert!(pub_key.windows(10).any(|s| s == b"PUBLIC KEY"));
}
#[test]
fn test_der_pkcs8() {
let key = include_bytes!("../test/key.der");
let key = PKey::private_key_from_der(key).unwrap();
let priv_key = key.private_key_to_der_pkcs8().unwrap();
assert_eq!(hex::encode(&priv_key[4..=6]), "020100"); assert_eq!(hex::encode(&priv_key[9..=19]), "06092a864886f70d010101"); }
#[test]
fn test_rsa_accessor() {
let rsa = Rsa::generate(2048).unwrap();
let pkey = PKey::from_rsa(rsa).unwrap();
pkey.rsa().unwrap();
assert_eq!(pkey.id(), Id::RSA);
assert!(pkey.dsa().is_err());
}
#[test]
fn test_ec_key_accessor() {
let ec_key = EcKey::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
let pkey = PKey::from_ec_key(ec_key).unwrap();
pkey.ec_key().unwrap();
assert_eq!(pkey.id(), Id::EC);
assert!(pkey.rsa().is_err());
}
#[test]
fn test_dsa_from_and_accessor() {
let dsa = Dsa::generate(2048).unwrap();
let pkey = PKey::from_dsa(dsa).unwrap();
pkey.dsa().unwrap();
assert_eq!(pkey.id(), Id::DSA);
assert!(pkey.rsa().is_err());
assert!(pkey.ec_key().is_err());
}
#[test]
fn test_dh_from_and_accessor() {
let p = BigNum::from_hex_str(
"87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F25D2CEED4435E3B00E00DF8F1D61957D4FAF7DF\
4561B2AA3016C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD5A8A9D306BCF67ED91F9E6725B47\
58C022E0B1EF4275BF7B6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C4FDB70C581B23F76B6\
3ACAE1CAA6B7902D52526735488A0EF13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D967E144E5\
140564251CCACB83E6B486F6B3CA3F7971506026C0B857F689962856DED4010ABD0BE621C3A3960A54E710\
C375F26375D7014103A4B54330C198AF126116D2276E11715F693877FAD7EF09CADB094AE91E1A1597",
)
.unwrap();
let g = BigNum::from_hex_str(
"3FB32C9B73134D0B2E77506660EDBD484CA7B18F21EF205407F4793A1A0BA12510DBC15077BE463FFF4FED\
4AAC0BB555BE3A6C1B0C6B47B1BC3773BF7E8C6F62901228F8C28CBB18A55AE31341000A650196F931C77A\
57F2DDF463E5E9EC144B777DE62AAAB8A8628AC376D282D6ED3864E67982428EBC831D14348F6F2F9193B5\
045AF2767164E1DFC967C1FB3F2E55A4BD1BFFE83B9C80D052B985D182EA0ADB2A3B7313D3FE14C8484B1E\
052588B9B7D2BBD2DF016199ECD06E1557CD0915B3353BBB64E0EC377FD028370DF92B52C7891428CDC67E\
B6184B523D1DB246C32F63078490F00EF8D647D148D47954515E2327CFEF98C582664B4C0F6CC41659",
)
.unwrap();
let q = BigNum::from_hex_str(
"8CF83642A709A097B447997640129DA299B1A47D1EB3750BA308B0FE64F5FBD3",
)
.unwrap();
let dh = Dh::from_params(p, g, q).unwrap();
let pkey = PKey::from_dh(dh).unwrap();
pkey.dh().unwrap();
assert_eq!(pkey.id(), Id::DH);
assert!(pkey.rsa().is_err());
assert!(pkey.ec_key().is_err());
}
#[test]
fn test_raw_accessors() {
const ED25519_PRIVATE_KEY_DER: &str = concat!(
"302e020100300506032b6570042204207c8c6497f9960d5595d7815f550569e5",
"f77764ac97e63e339aaa68cc1512b683"
);
let pkey =
PKey::private_key_from_der(&Vec::from_hex(ED25519_PRIVATE_KEY_DER).unwrap()).unwrap();
assert_eq!(pkey.id(), Id::ED25519);
let priv_len = pkey.raw_private_key_len().unwrap();
assert_eq!(priv_len, 32);
let mut raw_private_key_buf = [0; 40];
let raw_private_key = pkey.raw_private_key(&mut raw_private_key_buf).unwrap();
assert_eq!(raw_private_key.len(), 32);
assert_ne!(raw_private_key, [0; 32]);
pkey.raw_private_key(&mut [0; 5])
.expect_err("buffer too small");
let pub_len = pkey.raw_public_key_len().unwrap();
assert_eq!(pub_len, 32);
let mut raw_public_key_buf = [0; 40];
let raw_public_key = pkey.raw_public_key(&mut raw_public_key_buf).unwrap();
assert_eq!(raw_public_key.len(), 32);
assert_ne!(raw_public_key, [0; 32]);
assert_ne!(raw_public_key, raw_private_key);
pkey.raw_public_key(&mut [0; 5])
.expect_err("buffer too small");
}
#[test]
fn test_ed25519_from_raw_private_public() {
const ED25519_PRIVATE_KEY_DER: &str = concat!(
"302e020100300506032b6570042204207c8c6497f9960d5595d7815f550569e5",
"f77764ac97e63e339aaa68cc1512b683"
);
let pkey =
PKey::private_key_from_der(&Vec::from_hex(ED25519_PRIVATE_KEY_DER).unwrap()).unwrap();
let mut raw_private = [0_u8; 32];
pkey.raw_private_key(&mut raw_private).unwrap();
let mut raw_public = [0_u8; 32];
pkey.raw_public_key(&mut raw_public).unwrap();
let from_private = PKey::from_ed25519_private_key(&raw_private).unwrap();
assert_eq!(from_private.id(), Id::ED25519);
let from_public = PKey::from_ed25519_public_key(&raw_public).unwrap();
assert_eq!(from_public.id(), Id::ED25519);
}
#[test]
fn test_x25519_from_raw_private_public() {
let private_a = [1_u8; 32];
let private_b = [2_u8; 32];
let pkey_a = PKey::from_x25519_private_key(&private_a).unwrap();
let pkey_b = PKey::from_x25519_private_key(&private_b).unwrap();
assert_eq!(pkey_a.id(), Id::X25519);
assert_eq!(pkey_b.id(), Id::X25519);
let mut public_a = [0_u8; 32];
let mut public_b = [0_u8; 32];
pkey_a.raw_public_key(&mut public_a).unwrap();
pkey_b.raw_public_key(&mut public_b).unwrap();
let public_key_a = PKey::from_x25519_public_key(&public_a).unwrap();
let public_key_b = PKey::from_x25519_public_key(&public_b).unwrap();
assert_eq!(public_key_a.id(), Id::X25519);
assert_eq!(public_key_b.id(), Id::X25519);
}
}