boring2/openssl/src/pkey.rs

562 lines
17 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
//!
//! extern crate openssl;
//!
//! use openssl::rsa::Rsa;
//! use openssl::pkey::PKey;
//! use std::str;
//!
//! fn main() {
//! 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());
//! }
//! ```
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;
/// A public or private key.
pub struct PKey<T>;
/// Reference to `PKey`.
pub struct PKeyRef<T>;
}
impl<T> PKeyRef<T> {
/// Returns a copy of the internal RSA key.
///
/// This corresponds to [`EVP_PKEY_get1_RSA`].
///
/// [`EVP_PKEY_get1_RSA`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_get1_RSA.html
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.
///
/// This corresponds to [`EVP_PKEY_get1_DSA`].
///
/// [`EVP_PKEY_get1_DSA`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_get1_DSA.html
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.
///
/// This corresponds to [`EVP_PKEY_get1_DH`].
///
/// [`EVP_PKEY_get1_DH`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_get1_DH.html
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.
///
/// This corresponds to [`EVP_PKEY_get1_EC_KEY`].
///
/// [`EVP_PKEY_get1_EC_KEY`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_get1_EC_KEY.html
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,
{
to_pem! {
/// Serializes the public key into a PEM-encoded SubjectPublicKeyInfo structure.
///
/// The output will have a header of `-----BEGIN PUBLIC KEY-----`.
///
/// This corresponds to [`PEM_write_bio_PUBKEY`].
///
/// [`PEM_write_bio_PUBKEY`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_write_bio_PUBKEY.html
public_key_to_pem,
ffi::PEM_write_bio_PUBKEY
}
to_der! {
/// Serializes the public key into a DER-encoded SubjectPublicKeyInfo structure.
///
/// This corresponds to [`i2d_PUBKEY`].
///
/// [`i2d_PUBKEY`]: https://www.openssl.org/docs/man1.1.0/crypto/i2d_PUBKEY.html
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! {
/// Serializes the private key to a PEM-encoded PKCS#8 PrivateKeyInfo structure.
///
/// The output will have a header of `-----BEGIN PRIVATE KEY-----`.
///
/// This corresponds to [`PEM_write_bio_PKCS8PrivateKey`].
///
/// [`PEM_write_bio_PKCS8PrivateKey`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_write_bio_PKCS8PrivateKey.html
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-----`.
///
/// This corresponds to [`PEM_write_bio_PKCS8PrivateKey`].
///
/// [`PEM_write_bio_PKCS8PrivateKey`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_write_bio_PKCS8PrivateKey.html
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.
///
/// This corresponds to [`i2d_PrivateKey`].
///
/// [`i2d_PrivateKey`]: https://www.openssl.org/docs/man1.0.2/crypto/i2d_PrivateKey.html
private_key_to_der,
ffi::i2d_PrivateKey
}
}
impl<T> PKey<T> {
/// Creates a new `PKey` containing an RSA key.
///
/// This corresponds to [`EVP_PKEY_assign_RSA`].
///
/// [`EVP_PKEY_assign_RSA`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_assign_RSA.html
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.
///
/// This corresponds to [`EVP_PKEY_assign_DSA`].
///
/// [`EVP_PKEY_assign_DSA`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_assign_DSA.html
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.
///
/// This corresponds to [`EVP_PKEY_assign_DH`].
///
/// [`EVP_PKEY_assign_DH`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_assign_DH.html
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.
///
/// This corresponds to [`EVP_PKEY_assign_EC_KEY`].
///
/// [`EVP_PKEY_assign_EC_KEY`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_assign_EC_KEY.html
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! {
/// Deserializes a private key from a PEM-encoded key type specific format.
///
/// This corresponds to [`PEM_read_bio_PrivateKey`].
///
/// [`PEM_read_bio_PrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_read_bio_PrivateKey.html
private_key_from_pem,
/// Deserializes a private key from a PEM-encoded encrypted key type specific format.
///
/// This corresponds to [`PEM_read_bio_PrivateKey`].
///
/// [`PEM_read_bio_PrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_read_bio_PrivateKey.html
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.
///
/// This corresponds to [`PEM_read_bio_PrivateKey`].
///
/// [`PEM_read_bio_PrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_read_bio_PrivateKey.html
private_key_from_pem_callback,
PKey<Private>,
ffi::PEM_read_bio_PrivateKey
}
from_der! {
/// Decodes a DER-encoded private key.
///
/// This function will automatically attempt to detect the underlying key format, and
/// supports the unencrypted PKCS#8 PrivateKeyInfo structures as well as key type specific
/// formats.
///
/// This corresponds to [`d2i_AutoPrivateKey`].
///
/// [`d2i_AutoPrivateKey`]: https://www.openssl.org/docs/man1.0.2/crypto/d2i_AutoPrivateKey.html
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> {
from_pem! {
/// Decodes a PEM-encoded SubjectPublicKeyInfo structure.
///
/// The input should have a header of `-----BEGIN PUBLIC KEY-----`.
///
/// This corresponds to [`PEM_read_bio_PUBKEY`].
///
/// [`PEM_read_bio_PUBKEY`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_read_bio_PUBKEY.html
public_key_from_pem,
PKey<Public>,
ffi::PEM_read_bio_PUBKEY
}
from_der! {
/// Decodes a DER-encoded SubjectPublicKeyInfo structure.
///
/// This corresponds to [`d2i_PUBKEY`].
///
/// [`d2i_PUBKEY`]: https://www.openssl.org/docs/man1.1.0/crypto/d2i_PUBKEY.html
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_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_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();
// 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::params_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());
}
}