1030 lines
31 KiB
Rust
1030 lines
31 KiB
Rust
//! Public/private key processing.
|
|
//!
|
|
//! Asymmetric public key algorithms solve the problem of establishing and sharing
|
|
//! secret keys to securely send and receive messages.
|
|
//! This system uses a pair of keys: a public key, which can be freely
|
|
//! distributed, and a private key, which is kept to oneself. An entity may
|
|
//! encrypt information using a user's public key. The encrypted information can
|
|
//! only be deciphered using that user's private key.
|
|
//!
|
|
//! This module offers support for five popular algorithms:
|
|
//!
|
|
//! * RSA
|
|
//!
|
|
//! * DSA
|
|
//!
|
|
//! * Diffie-Hellman
|
|
//!
|
|
//! * Elliptic Curves
|
|
//!
|
|
//! * HMAC
|
|
//!
|
|
//! These algorithms rely on hard mathematical problems - namely integer factorization,
|
|
//! discrete logarithms, and elliptic curve relationships - that currently do not
|
|
//! yield efficient solutions. This property ensures the security of these
|
|
//! cryptographic algorithms.
|
|
//!
|
|
//! # Example
|
|
//!
|
|
//! Generate a 2048-bit RSA public/private key pair and print the public key.
|
|
//!
|
|
//! ```rust
|
|
//! use openssl::rsa::Rsa;
|
|
//! use openssl::pkey::PKey;
|
|
//! use std::str;
|
|
//!
|
|
//! let rsa = Rsa::generate(2048).unwrap();
|
|
//! let pkey = PKey::from_rsa(rsa).unwrap();
|
|
//!
|
|
//! let pub_key: Vec<u8> = pkey.public_key_to_pem().unwrap();
|
|
//! println!("{:?}", str::from_utf8(pub_key.as_slice()).unwrap());
|
|
//! ```
|
|
#![allow(clippy::missing_safety_doc)]
|
|
use crate::bio::{MemBio, MemBioSlice};
|
|
#[cfg(ossl110)]
|
|
use crate::cipher::CipherRef;
|
|
use crate::dh::Dh;
|
|
use crate::dsa::Dsa;
|
|
use crate::ec::EcKey;
|
|
use crate::error::ErrorStack;
|
|
#[cfg(ossl110)]
|
|
use crate::pkey_ctx::PkeyCtx;
|
|
use crate::rsa::Rsa;
|
|
use crate::symm::Cipher;
|
|
use crate::util::{invoke_passwd_cb, CallbackState};
|
|
use crate::{cvt, cvt_p};
|
|
use cfg_if::cfg_if;
|
|
use foreign_types::{ForeignType, ForeignTypeRef};
|
|
use libc::{c_int, c_long};
|
|
use openssl_macros::corresponds;
|
|
use std::convert::TryFrom;
|
|
use std::ffi::CString;
|
|
use std::fmt;
|
|
use std::mem;
|
|
use std::ptr;
|
|
|
|
/// A tag type indicating that a key only has parameters.
|
|
pub enum Params {}
|
|
|
|
/// A tag type indicating that a key only has public components.
|
|
pub enum Public {}
|
|
|
|
/// A tag type indicating that a key has private components.
|
|
pub enum Private {}
|
|
|
|
/// An identifier of a kind of key.
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
pub struct Id(c_int);
|
|
|
|
impl Id {
|
|
pub const RSA: Id = Id(ffi::EVP_PKEY_RSA);
|
|
pub const HMAC: Id = Id(ffi::EVP_PKEY_HMAC);
|
|
pub const CMAC: Id = Id(ffi::EVP_PKEY_CMAC);
|
|
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);
|
|
|
|
#[cfg(ossl110)]
|
|
pub const HKDF: Id = Id(ffi::EVP_PKEY_HKDF);
|
|
|
|
#[cfg(ossl111)]
|
|
pub const ED25519: Id = Id(ffi::EVP_PKEY_ED25519);
|
|
#[cfg(ossl111)]
|
|
pub const ED448: Id = Id(ffi::EVP_PKEY_ED448);
|
|
#[cfg(ossl111)]
|
|
pub const X25519: Id = Id(ffi::EVP_PKEY_X25519);
|
|
#[cfg(ossl111)]
|
|
pub const X448: Id = Id(ffi::EVP_PKEY_X448);
|
|
|
|
/// Creates a `Id` from an integer representation.
|
|
pub fn from_raw(value: c_int) -> Id {
|
|
Id(value)
|
|
}
|
|
|
|
/// Returns the integer representation of the `Id`.
|
|
#[allow(clippy::trivially_copy_pass_by_ref)]
|
|
pub fn as_raw(&self) -> c_int {
|
|
self.0
|
|
}
|
|
}
|
|
|
|
/// A trait indicating that a key has parameters.
|
|
pub unsafe trait HasParams {}
|
|
|
|
unsafe impl HasParams for Params {}
|
|
|
|
unsafe impl<T> HasParams for T where T: HasPublic {}
|
|
|
|
/// A trait indicating that a key has public components.
|
|
pub unsafe trait HasPublic {}
|
|
|
|
unsafe impl HasPublic for Public {}
|
|
|
|
unsafe impl<T> HasPublic for T where T: HasPrivate {}
|
|
|
|
/// A trait indicating that a key has private components.
|
|
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;
|
|
|
|
/// A public or private key.
|
|
pub struct PKey<T>;
|
|
/// Reference to `PKey`.
|
|
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> {
|
|
/// Returns a copy of the internal RSA key.
|
|
#[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))
|
|
}
|
|
}
|
|
|
|
/// Returns a copy of the internal DSA key.
|
|
#[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))
|
|
}
|
|
}
|
|
|
|
/// Returns a copy of the internal DH key.
|
|
#[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))
|
|
}
|
|
}
|
|
|
|
/// Returns a copy of the internal elliptic curve key.
|
|
#[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))
|
|
}
|
|
}
|
|
|
|
/// Returns the `Id` that represents the type of this key.
|
|
#[corresponds(EVP_PKEY_id)]
|
|
pub fn id(&self) -> Id {
|
|
unsafe { Id::from_raw(ffi::EVP_PKEY_id(self.as_ptr())) }
|
|
}
|
|
|
|
/// Returns the maximum size of a signature in bytes.
|
|
#[corresponds(EVP_PKEY_size)]
|
|
pub fn size(&self) -> usize {
|
|
unsafe { ffi::EVP_PKEY_size(self.as_ptr()) as usize }
|
|
}
|
|
}
|
|
|
|
impl<T> PKeyRef<T>
|
|
where
|
|
T: HasPublic,
|
|
{
|
|
to_pem! {
|
|
/// Serializes the public key into a PEM-encoded SubjectPublicKeyInfo structure.
|
|
///
|
|
/// The output will have a header of `-----BEGIN PUBLIC KEY-----`.
|
|
#[corresponds(PEM_write_bio_PUBKEY)]
|
|
public_key_to_pem,
|
|
ffi::PEM_write_bio_PUBKEY
|
|
}
|
|
|
|
to_der! {
|
|
/// Serializes the public key into a DER-encoded SubjectPublicKeyInfo structure.
|
|
#[corresponds(i2d_PUBKEY)]
|
|
public_key_to_der,
|
|
ffi::i2d_PUBKEY
|
|
}
|
|
|
|
/// Returns the size of the key.
|
|
///
|
|
/// This corresponds to the bit length of the modulus of an RSA key, and the bit length of the
|
|
/// group order for an elliptic curve key, for example.
|
|
#[corresponds(EVP_PKEY_bits)]
|
|
pub fn bits(&self) -> u32 {
|
|
unsafe { ffi::EVP_PKEY_bits(self.as_ptr()) as u32 }
|
|
}
|
|
|
|
/// Compares the public component of this key with another.
|
|
#[corresponds(EVP_PKEY_cmp)]
|
|
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 }
|
|
}
|
|
|
|
/// Raw byte representation of a public key
|
|
///
|
|
/// This function only works for algorithms that support raw public keys.
|
|
/// Currently this is: X25519, ED25519, X448 or ED448
|
|
#[corresponds(EVP_PKEY_get_raw_public_key)]
|
|
#[cfg(ossl111)]
|
|
pub fn raw_public_key(&self) -> Result<Vec<u8>, ErrorStack> {
|
|
unsafe {
|
|
let mut len = 0;
|
|
cvt(ffi::EVP_PKEY_get_raw_public_key(
|
|
self.as_ptr(),
|
|
ptr::null_mut(),
|
|
&mut len,
|
|
))?;
|
|
let mut buf = vec![0u8; len];
|
|
cvt(ffi::EVP_PKEY_get_raw_public_key(
|
|
self.as_ptr(),
|
|
buf.as_mut_ptr(),
|
|
&mut len,
|
|
))?;
|
|
buf.truncate(len);
|
|
Ok(buf)
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> PKeyRef<T>
|
|
where
|
|
T: HasPrivate,
|
|
{
|
|
private_key_to_pem! {
|
|
/// Serializes the private key to a PEM-encoded PKCS#8 PrivateKeyInfo structure.
|
|
///
|
|
/// The output will have a header of `-----BEGIN PRIVATE KEY-----`.
|
|
#[corresponds(PEM_write_bio_PKCS8PrivateKey)]
|
|
private_key_to_pem_pkcs8,
|
|
/// Serializes the private key to a PEM-encoded PKCS#8 EncryptedPrivateKeyInfo structure.
|
|
///
|
|
/// The output will have a header of `-----BEGIN ENCRYPTED PRIVATE KEY-----`.
|
|
#[corresponds(PEM_write_bio_PKCS8PrivateKey)]
|
|
private_key_to_pem_pkcs8_passphrase,
|
|
ffi::PEM_write_bio_PKCS8PrivateKey
|
|
}
|
|
|
|
to_der! {
|
|
/// Serializes the private key to a DER-encoded key type specific format.
|
|
#[corresponds(i2d_PrivateKey)]
|
|
private_key_to_der,
|
|
ffi::i2d_PrivateKey
|
|
}
|
|
|
|
/// Raw byte representation of a private key
|
|
///
|
|
/// This function only works for algorithms that support raw private keys.
|
|
/// Currently this is: HMAC, X25519, ED25519, X448 or ED448
|
|
#[corresponds(EVP_PKEY_get_raw_private_key)]
|
|
#[cfg(ossl111)]
|
|
pub fn raw_private_key(&self) -> Result<Vec<u8>, ErrorStack> {
|
|
unsafe {
|
|
let mut len = 0;
|
|
cvt(ffi::EVP_PKEY_get_raw_private_key(
|
|
self.as_ptr(),
|
|
ptr::null_mut(),
|
|
&mut len,
|
|
))?;
|
|
let mut buf = vec![0u8; len];
|
|
cvt(ffi::EVP_PKEY_get_raw_private_key(
|
|
self.as_ptr(),
|
|
buf.as_mut_ptr(),
|
|
&mut len,
|
|
))?;
|
|
buf.truncate(len);
|
|
Ok(buf)
|
|
}
|
|
}
|
|
|
|
/// Serializes a private key into a DER-formatted PKCS#8, using the supplied password to
|
|
/// encrypt the key.
|
|
///
|
|
/// # Panics
|
|
///
|
|
/// Panics if `passphrase` contains an embedded null.
|
|
#[corresponds(i2d_PKCS8PrivateKey_bio)]
|
|
pub fn private_key_to_pkcs8_passphrase(
|
|
&self,
|
|
cipher: Cipher,
|
|
passphrase: &[u8],
|
|
) -> Result<Vec<u8>, ErrorStack> {
|
|
unsafe {
|
|
let bio = MemBio::new()?;
|
|
let len = passphrase.len();
|
|
let passphrase = CString::new(passphrase).unwrap();
|
|
cvt(ffi::i2d_PKCS8PrivateKey_bio(
|
|
bio.as_ptr(),
|
|
self.as_ptr(),
|
|
cipher.as_ptr(),
|
|
passphrase.as_ptr() as *const _ as *mut _,
|
|
len as ::libc::c_int,
|
|
None,
|
|
ptr::null_mut(),
|
|
))?;
|
|
|
|
Ok(bio.get_buf().to_owned())
|
|
}
|
|
}
|
|
}
|
|
|
|
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::HMAC => "HMAC",
|
|
Id::DSA => "DSA",
|
|
Id::DH => "DH",
|
|
Id::EC => "EC",
|
|
#[cfg(ossl111)]
|
|
Id::ED25519 => "Ed25519",
|
|
#[cfg(ossl111)]
|
|
Id::ED448 => "Ed448",
|
|
_ => "unknown",
|
|
};
|
|
fmt.debug_struct("PKey").field("algorithm", &alg).finish()
|
|
// TODO: Print details for each specific type of key
|
|
}
|
|
}
|
|
|
|
impl<T> Clone for PKey<T> {
|
|
fn clone(&self) -> PKey<T> {
|
|
PKeyRef::to_owned(self)
|
|
}
|
|
}
|
|
|
|
impl<T> PKey<T> {
|
|
/// Creates a new `PKey` containing an RSA key.
|
|
#[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() as *mut _,
|
|
))?;
|
|
mem::forget(rsa);
|
|
Ok(pkey)
|
|
}
|
|
}
|
|
|
|
/// Creates a new `PKey` containing a DSA key.
|
|
#[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() as *mut _,
|
|
))?;
|
|
mem::forget(dsa);
|
|
Ok(pkey)
|
|
}
|
|
}
|
|
|
|
/// Creates a new `PKey` containing a Diffie-Hellman key.
|
|
#[corresponds(EVP_PKEY_assign_DH)]
|
|
pub fn from_dh(dh: Dh<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_DH,
|
|
dh.as_ptr() as *mut _,
|
|
))?;
|
|
mem::forget(dh);
|
|
Ok(pkey)
|
|
}
|
|
}
|
|
|
|
/// Creates a new `PKey` containing an elliptic curve key.
|
|
#[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() as *mut _,
|
|
))?;
|
|
mem::forget(ec_key);
|
|
Ok(pkey)
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PKey<Private> {
|
|
/// Creates a new `PKey` containing an HMAC key.
|
|
///
|
|
/// # Note
|
|
///
|
|
/// To compute HMAC values, use the `sign` module.
|
|
#[corresponds(EVP_PKEY_new_mac_key)]
|
|
pub fn hmac(key: &[u8]) -> Result<PKey<Private>, ErrorStack> {
|
|
unsafe {
|
|
assert!(key.len() <= c_int::max_value() as usize);
|
|
let key = cvt_p(ffi::EVP_PKEY_new_mac_key(
|
|
ffi::EVP_PKEY_HMAC,
|
|
ptr::null_mut(),
|
|
key.as_ptr() as *const _,
|
|
key.len() as c_int,
|
|
))?;
|
|
Ok(PKey::from_ptr(key))
|
|
}
|
|
}
|
|
|
|
/// Creates a new `PKey` containing a CMAC key.
|
|
///
|
|
/// Requires OpenSSL 1.1.0 or newer.
|
|
///
|
|
/// # Note
|
|
///
|
|
/// To compute CMAC values, use the `sign` module.
|
|
#[cfg(ossl110)]
|
|
#[allow(clippy::trivially_copy_pass_by_ref)]
|
|
pub fn cmac(cipher: &Cipher, key: &[u8]) -> Result<PKey<Private>, ErrorStack> {
|
|
let mut ctx = PkeyCtx::new_id(Id::CMAC)?;
|
|
ctx.keygen_init()?;
|
|
ctx.set_keygen_cipher(unsafe { CipherRef::from_ptr(cipher.as_ptr() as *mut _) })?;
|
|
ctx.set_keygen_mac_key(key)?;
|
|
ctx.keygen()
|
|
}
|
|
|
|
#[cfg(ossl111)]
|
|
fn generate_eddsa(id: Id) -> Result<PKey<Private>, ErrorStack> {
|
|
let mut ctx = PkeyCtx::new_id(id)?;
|
|
ctx.keygen_init()?;
|
|
ctx.keygen()
|
|
}
|
|
|
|
/// Generates a new private Ed25519 key
|
|
#[cfg(ossl111)]
|
|
pub fn generate_x25519() -> Result<PKey<Private>, ErrorStack> {
|
|
PKey::generate_eddsa(Id::X25519)
|
|
}
|
|
|
|
/// Generates a new private Ed448 key
|
|
#[cfg(ossl111)]
|
|
pub fn generate_x448() -> Result<PKey<Private>, ErrorStack> {
|
|
PKey::generate_eddsa(Id::X448)
|
|
}
|
|
|
|
/// Generates a new private Ed25519 key
|
|
#[cfg(ossl111)]
|
|
pub fn generate_ed25519() -> Result<PKey<Private>, ErrorStack> {
|
|
PKey::generate_eddsa(Id::ED25519)
|
|
}
|
|
|
|
/// Generates a new private Ed448 key
|
|
#[cfg(ossl111)]
|
|
pub fn generate_ed448() -> Result<PKey<Private>, ErrorStack> {
|
|
PKey::generate_eddsa(Id::ED448)
|
|
}
|
|
|
|
/// Generates a new EC key using the provided curve.
|
|
///
|
|
/// Requires OpenSSL 3.0.0 or newer.
|
|
#[corresponds(EVP_EC_gen)]
|
|
#[cfg(ossl300)]
|
|
pub fn ec_gen(curve: &str) -> Result<PKey<Private>, ErrorStack> {
|
|
let curve = CString::new(curve).unwrap();
|
|
unsafe {
|
|
let ptr = cvt_p(ffi::EVP_EC_gen(curve.as_ptr()))?;
|
|
Ok(PKey::from_ptr(ptr))
|
|
}
|
|
}
|
|
|
|
private_key_from_pem! {
|
|
/// Deserializes a private key from a PEM-encoded key type specific format.
|
|
#[corresponds(PEM_read_bio_PrivateKey)]
|
|
private_key_from_pem,
|
|
|
|
/// Deserializes a private key from a PEM-encoded encrypted key type specific format.
|
|
#[corresponds(PEM_read_bio_PrivateKey)]
|
|
private_key_from_pem_passphrase,
|
|
|
|
/// Deserializes a private key from a PEM-encoded encrypted key type specific format.
|
|
///
|
|
/// The callback should fill the password into the provided buffer and return its length.
|
|
#[corresponds(PEM_read_bio_PrivateKey)]
|
|
private_key_from_pem_callback,
|
|
PKey<Private>,
|
|
ffi::PEM_read_bio_PrivateKey
|
|
}
|
|
|
|
from_der! {
|
|
/// Decodes a DER-encoded private key.
|
|
///
|
|
/// This function will attempt to automatically detect the underlying key format, and
|
|
/// supports the unencrypted PKCS#8 PrivateKeyInfo structures as well as key type specific
|
|
/// formats.
|
|
#[corresponds(d2i_AutoPrivateKey)]
|
|
private_key_from_der,
|
|
PKey<Private>,
|
|
ffi::d2i_AutoPrivateKey
|
|
}
|
|
|
|
/// Deserializes a DER-formatted PKCS#8 unencrypted private key.
|
|
///
|
|
/// This method is mainly for interoperability reasons. Encrypted keyfiles should be preferred.
|
|
pub fn private_key_from_pkcs8(der: &[u8]) -> Result<PKey<Private>, ErrorStack> {
|
|
unsafe {
|
|
ffi::init();
|
|
let len = der.len().min(c_long::max_value() 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
|
|
}
|
|
}
|
|
|
|
/// Deserializes a DER-formatted PKCS#8 private key, using a callback to retrieve the password
|
|
/// if the key is encrypted.
|
|
///
|
|
/// The callback should copy the password into the provided buffer and return the number of
|
|
/// bytes written.
|
|
#[corresponds(d2i_PKCS8PrivateKey_bio)]
|
|
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>),
|
|
&mut cb as *mut _ as *mut _,
|
|
))
|
|
.map(|p| PKey::from_ptr(p))
|
|
}
|
|
}
|
|
|
|
/// Deserializes a DER-formatted PKCS#8 private key, using the supplied password if the key is
|
|
/// encrypted.
|
|
///
|
|
/// # Panics
|
|
///
|
|
/// Panics if `passphrase` contains an embedded null.
|
|
#[corresponds(d2i_PKCS8PrivateKey_bio)]
|
|
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).unwrap();
|
|
cvt_p(ffi::d2i_PKCS8PrivateKey_bio(
|
|
bio.as_ptr(),
|
|
ptr::null_mut(),
|
|
None,
|
|
passphrase.as_ptr() as *const _ as *mut _,
|
|
))
|
|
.map(|p| PKey::from_ptr(p))
|
|
}
|
|
}
|
|
|
|
/// Creates a private key from its raw byte representation
|
|
///
|
|
/// Algorithm types that support raw private keys are HMAC, X25519, ED25519, X448 or ED448
|
|
#[corresponds(EVP_PKEY_new_raw_private_key)]
|
|
#[cfg(ossl111)]
|
|
pub fn private_key_from_raw_bytes(
|
|
bytes: &[u8],
|
|
key_type: Id,
|
|
) -> Result<PKey<Private>, ErrorStack> {
|
|
unsafe {
|
|
ffi::init();
|
|
cvt_p(ffi::EVP_PKEY_new_raw_private_key(
|
|
key_type.as_raw(),
|
|
ptr::null_mut(),
|
|
bytes.as_ptr(),
|
|
bytes.len(),
|
|
))
|
|
.map(|p| PKey::from_ptr(p))
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PKey<Public> {
|
|
from_pem! {
|
|
/// Decodes a PEM-encoded SubjectPublicKeyInfo structure.
|
|
///
|
|
/// The input should have a header of `-----BEGIN PUBLIC KEY-----`.
|
|
#[corresponds(PEM_read_bio_PUBKEY)]
|
|
public_key_from_pem,
|
|
PKey<Public>,
|
|
ffi::PEM_read_bio_PUBKEY
|
|
}
|
|
|
|
from_der! {
|
|
/// Decodes a DER-encoded SubjectPublicKeyInfo structure.
|
|
#[corresponds(d2i_PUBKEY)]
|
|
public_key_from_der,
|
|
PKey<Public>,
|
|
ffi::d2i_PUBKEY
|
|
}
|
|
|
|
/// Creates a public key from its raw byte representation
|
|
///
|
|
/// Algorithm types that support raw public keys are X25519, ED25519, X448 or ED448
|
|
#[corresponds(EVP_PKEY_new_raw_public_key)]
|
|
#[cfg(ossl111)]
|
|
pub fn public_key_from_raw_bytes(
|
|
bytes: &[u8],
|
|
key_type: Id,
|
|
) -> Result<PKey<Public>, ErrorStack> {
|
|
unsafe {
|
|
ffi::init();
|
|
cvt_p(ffi::EVP_PKEY_new_raw_public_key(
|
|
key_type.as_raw(),
|
|
ptr::null_mut(),
|
|
bytes.as_ptr(),
|
|
bytes.len(),
|
|
))
|
|
.map(|p| PKey::from_ptr(p))
|
|
}
|
|
}
|
|
}
|
|
|
|
cfg_if! {
|
|
if #[cfg(any(ossl110, libressl270))] {
|
|
use ffi::EVP_PKEY_up_ref;
|
|
} else {
|
|
#[allow(bad_style)]
|
|
unsafe extern "C" fn EVP_PKEY_up_ref(pkey: *mut ffi::EVP_PKEY) {
|
|
ffi::CRYPTO_add_lock(
|
|
&mut (*pkey).references,
|
|
1,
|
|
ffi::CRYPTO_LOCK_EVP_PKEY,
|
|
"pkey.rs\0".as_ptr() as *const _,
|
|
line!() as c_int,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> TryFrom<EcKey<T>> for PKey<T> {
|
|
type Error = ErrorStack;
|
|
|
|
fn try_from(ec_key: EcKey<T>) -> Result<PKey<T>, ErrorStack> {
|
|
PKey::from_ec_key(ec_key)
|
|
}
|
|
}
|
|
|
|
impl<T> TryFrom<PKey<T>> for EcKey<T> {
|
|
type Error = ErrorStack;
|
|
|
|
fn try_from(pkey: PKey<T>) -> Result<EcKey<T>, ErrorStack> {
|
|
pkey.ec_key()
|
|
}
|
|
}
|
|
|
|
impl<T> TryFrom<Rsa<T>> for PKey<T> {
|
|
type Error = ErrorStack;
|
|
|
|
fn try_from(rsa: Rsa<T>) -> Result<PKey<T>, ErrorStack> {
|
|
PKey::from_rsa(rsa)
|
|
}
|
|
}
|
|
|
|
impl<T> TryFrom<PKey<T>> for Rsa<T> {
|
|
type Error = ErrorStack;
|
|
|
|
fn try_from(pkey: PKey<T>) -> Result<Rsa<T>, ErrorStack> {
|
|
pkey.rsa()
|
|
}
|
|
}
|
|
|
|
impl<T> TryFrom<Dsa<T>> for PKey<T> {
|
|
type Error = ErrorStack;
|
|
|
|
fn try_from(dsa: Dsa<T>) -> Result<PKey<T>, ErrorStack> {
|
|
PKey::from_dsa(dsa)
|
|
}
|
|
}
|
|
|
|
impl<T> TryFrom<PKey<T>> for Dsa<T> {
|
|
type Error = ErrorStack;
|
|
|
|
fn try_from(pkey: PKey<T>) -> Result<Dsa<T>, ErrorStack> {
|
|
pkey.dsa()
|
|
}
|
|
}
|
|
|
|
impl<T> TryFrom<Dh<T>> for PKey<T> {
|
|
type Error = ErrorStack;
|
|
|
|
fn try_from(dh: Dh<T>) -> Result<PKey<T>, ErrorStack> {
|
|
PKey::from_dh(dh)
|
|
}
|
|
}
|
|
|
|
impl<T> TryFrom<PKey<T>> for Dh<T> {
|
|
type Error = ErrorStack;
|
|
|
|
fn try_from(pkey: PKey<T>) -> Result<Dh<T>, ErrorStack> {
|
|
pkey.dh()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use std::convert::TryInto;
|
|
|
|
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::*;
|
|
|
|
#[cfg(ossl111)]
|
|
use crate::rand::rand_bytes;
|
|
|
|
#[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();
|
|
|
|
let rsa = Rsa::generate(2048).unwrap();
|
|
let pkey = PKey::from_rsa(rsa).unwrap();
|
|
let der = pkey
|
|
.private_key_to_pkcs8_passphrase(Cipher::aes_128_cbc(), b"mypass")
|
|
.unwrap();
|
|
let pkey2 = PKey::private_key_from_pkcs8_passphrase(&der, b"mypass").unwrap();
|
|
assert_eq!(
|
|
pkey.private_key_to_der().unwrap(),
|
|
pkey2.private_key_to_der().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();
|
|
|
|
// As a super-simple verification, just check that the buffers contain
|
|
// the `PRIVATE KEY` or `PUBLIC KEY` strings.
|
|
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_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_dsa_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());
|
|
}
|
|
|
|
#[test]
|
|
fn test_dh_accessor() {
|
|
let dh = include_bytes!("../test/dhparams.pem");
|
|
let dh = Dh::params_from_pem(dh).unwrap();
|
|
let pkey = PKey::from_dh(dh).unwrap();
|
|
pkey.dh().unwrap();
|
|
assert_eq!(pkey.id(), Id::DH);
|
|
assert!(pkey.rsa().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_rsa_conversion() {
|
|
let rsa = Rsa::generate(2048).unwrap();
|
|
let pkey: PKey<Private> = rsa.clone().try_into().unwrap();
|
|
let rsa_: Rsa<Private> = pkey.try_into().unwrap();
|
|
// Eq is missing
|
|
assert_eq!(rsa.p(), rsa_.p());
|
|
assert_eq!(rsa.q(), rsa_.q());
|
|
}
|
|
|
|
#[test]
|
|
fn test_dsa_conversion() {
|
|
let dsa = Dsa::generate(2048).unwrap();
|
|
let pkey: PKey<Private> = dsa.clone().try_into().unwrap();
|
|
let dsa_: Dsa<Private> = pkey.try_into().unwrap();
|
|
// Eq is missing
|
|
assert_eq!(dsa.priv_key(), dsa_.priv_key());
|
|
}
|
|
|
|
#[test]
|
|
fn test_ec_key_conversion() {
|
|
let group = crate::ec::EcGroup::from_curve_name(crate::nid::Nid::X9_62_PRIME256V1).unwrap();
|
|
let ec_key = EcKey::generate(&group).unwrap();
|
|
let pkey: PKey<Private> = ec_key.clone().try_into().unwrap();
|
|
let ec_key_: EcKey<Private> = pkey.try_into().unwrap();
|
|
// Eq is missing
|
|
assert_eq!(ec_key.private_key(), ec_key_.private_key());
|
|
}
|
|
|
|
#[test]
|
|
fn test_dh_conversion() {
|
|
let dh_params = include_bytes!("../test/dhparams.pem");
|
|
let dh_params = Dh::params_from_pem(dh_params).unwrap();
|
|
let dh = dh_params.generate_key().unwrap();
|
|
|
|
// Clone is missing for Dh, save the parameters
|
|
let p = dh.prime_p().to_owned().unwrap();
|
|
let q = dh.prime_q().map(|q| q.to_owned().unwrap());
|
|
let g = dh.generator().to_owned().unwrap();
|
|
|
|
let pkey: PKey<Private> = dh.try_into().unwrap();
|
|
let dh_: Dh<Private> = pkey.try_into().unwrap();
|
|
|
|
// Eq is missing
|
|
assert_eq!(&p, dh_.prime_p());
|
|
assert_eq!(q, dh_.prime_q().map(|q| q.to_owned().unwrap()));
|
|
assert_eq!(&g, dh_.generator());
|
|
}
|
|
|
|
#[cfg(ossl111)]
|
|
fn test_raw_public_key(gen: fn() -> Result<PKey<Private>, ErrorStack>, key_type: Id) {
|
|
// Generate a new key
|
|
let key = gen().unwrap();
|
|
|
|
// Get the raw bytes, and create a new key from the raw bytes
|
|
let raw = key.raw_public_key().unwrap();
|
|
let from_raw = PKey::public_key_from_raw_bytes(&raw, key_type).unwrap();
|
|
|
|
// Compare the der encoding of the original and raw / restored public key
|
|
assert_eq!(
|
|
key.public_key_to_der().unwrap(),
|
|
from_raw.public_key_to_der().unwrap()
|
|
);
|
|
}
|
|
|
|
#[cfg(ossl111)]
|
|
fn test_raw_private_key(gen: fn() -> Result<PKey<Private>, ErrorStack>, key_type: Id) {
|
|
// Generate a new key
|
|
let key = gen().unwrap();
|
|
|
|
// Get the raw bytes, and create a new key from the raw bytes
|
|
let raw = key.raw_private_key().unwrap();
|
|
let from_raw = PKey::private_key_from_raw_bytes(&raw, key_type).unwrap();
|
|
|
|
// Compare the der encoding of the original and raw / restored public key
|
|
assert_eq!(
|
|
key.private_key_to_der().unwrap(),
|
|
from_raw.private_key_to_der().unwrap()
|
|
);
|
|
}
|
|
|
|
#[cfg(ossl111)]
|
|
#[test]
|
|
fn test_raw_public_key_bytes() {
|
|
test_raw_public_key(PKey::generate_x25519, Id::X25519);
|
|
test_raw_public_key(PKey::generate_ed25519, Id::ED25519);
|
|
test_raw_public_key(PKey::generate_x448, Id::X448);
|
|
test_raw_public_key(PKey::generate_ed448, Id::ED448);
|
|
}
|
|
|
|
#[cfg(ossl111)]
|
|
#[test]
|
|
fn test_raw_private_key_bytes() {
|
|
test_raw_private_key(PKey::generate_x25519, Id::X25519);
|
|
test_raw_private_key(PKey::generate_ed25519, Id::ED25519);
|
|
test_raw_private_key(PKey::generate_x448, Id::X448);
|
|
test_raw_private_key(PKey::generate_ed448, Id::ED448);
|
|
}
|
|
|
|
#[cfg(ossl111)]
|
|
#[test]
|
|
fn test_raw_hmac() {
|
|
let mut test_bytes = vec![0u8; 32];
|
|
rand_bytes(&mut test_bytes).unwrap();
|
|
|
|
let hmac_key = PKey::hmac(&test_bytes).unwrap();
|
|
assert!(hmac_key.raw_public_key().is_err());
|
|
|
|
let key_bytes = hmac_key.raw_private_key().unwrap();
|
|
assert_eq!(key_bytes, test_bytes);
|
|
}
|
|
|
|
#[cfg(ossl111)]
|
|
#[test]
|
|
fn test_raw_key_fail() {
|
|
// Getting a raw byte representation will not work with Nist curves
|
|
let group = crate::ec::EcGroup::from_curve_name(Nid::SECP256K1).unwrap();
|
|
let ec_key = EcKey::generate(&group).unwrap();
|
|
let pkey = PKey::from_ec_key(ec_key).unwrap();
|
|
assert!(pkey.raw_private_key().is_err());
|
|
assert!(pkey.raw_public_key().is_err());
|
|
}
|
|
|
|
#[cfg(ossl300)]
|
|
#[test]
|
|
fn test_ec_gen() {
|
|
let key = PKey::ec_gen("prime256v1").unwrap();
|
|
assert!(key.ec_key().is_ok());
|
|
}
|
|
}
|