375 lines
10 KiB
Rust
375 lines
10 KiB
Rust
use libc::c_int;
|
|
use std::ptr;
|
|
use std::mem;
|
|
use std::ffi::CString;
|
|
use ffi;
|
|
use foreign_types::{ForeignType, ForeignTypeRef};
|
|
|
|
use {cvt, cvt_p};
|
|
use bio::MemBioSlice;
|
|
use dh::Dh;
|
|
use dsa::Dsa;
|
|
use ec::EcKey;
|
|
use rsa::Rsa;
|
|
use error::ErrorStack;
|
|
use util::{invoke_passwd_cb, CallbackState};
|
|
|
|
/// 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 {}
|
|
|
|
/// 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;
|
|
|
|
pub struct PKey<T>;
|
|
pub struct PKeyRef<T>;
|
|
}
|
|
|
|
impl<T> PKeyRef<T> {
|
|
/// Returns a copy of the internal RSA key.
|
|
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.
|
|
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.
|
|
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.
|
|
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))
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> PKeyRef<T>
|
|
where
|
|
T: HasPublic,
|
|
{
|
|
public_key_to_pem!(ffi::PEM_write_bio_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.
|
|
pub fn bits(&self) -> u32 {
|
|
unsafe { ffi::EVP_PKEY_bits(self.as_ptr()) as u32 }
|
|
}
|
|
|
|
/// Compares the public component of this key with another.
|
|
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 }
|
|
}
|
|
}
|
|
|
|
impl<T> PKeyRef<T>
|
|
where
|
|
T: HasPrivate,
|
|
{
|
|
private_key_to_pem!(ffi::PEM_write_bio_PKCS8PrivateKey);
|
|
private_key_to_der!(ffi::i2d_PrivateKey);
|
|
}
|
|
|
|
impl<T> PKey<T> {
|
|
/// Creates a new `PKey` containing an RSA key.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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))
|
|
}
|
|
}
|
|
|
|
private_key_from_pem!(PKey<Private>, ffi::PEM_read_bio_PrivateKey);
|
|
private_key_from_der!(PKey<Public>, ffi::d2i_AutoPrivateKey);
|
|
|
|
/// Deserializes a DER-formatted PKCS#8 private key, using a callback to retrieve the password
|
|
/// if the key is encrpyted.
|
|
///
|
|
/// The callback should copy the password into the provided buffer and return the number of
|
|
/// bytes written.
|
|
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.
|
|
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))
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PKey<Public> {
|
|
public_key_from_pem!(PKey<Public>, ffi::PEM_read_bio_PUBKEY);
|
|
public_key_from_der!(PKey<Public>, ffi::d2i_PUBKEY);
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use symm::Cipher;
|
|
use dh::Dh;
|
|
use dsa::Dsa;
|
|
use ec::EcKey;
|
|
use rsa::Rsa;
|
|
use nid::Nid;
|
|
|
|
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_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_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().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!(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!(pkey.rsa().is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn test_dh_accessor() {
|
|
let dh = include_bytes!("../test/dhparams.pem");
|
|
let dh = Dh::from_pem(dh).unwrap();
|
|
let pkey = PKey::from_dh(dh).unwrap();
|
|
pkey.dh().unwrap();
|
|
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!(pkey.rsa().is_err());
|
|
}
|
|
}
|