Commit 19f3b8a1 authored by Steven Fackler's avatar Steven Fackler
Browse files

Support PKCS#8 private key deserialization

Closes #581
parent 06065ddc
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -1639,7 +1639,7 @@ extern {
                                e: *mut ENGINE,
                                key: *const c_uchar,
                                keylen: c_int) -> *mut EVP_PKEY;

    pub fn d2i_PKCS8PrivateKey_bio(bp: *mut BIO, x: *mut *mut EVP_PKEY, cb: Option<PasswordCallback>, u: *mut c_void) -> *mut EVP_PKEY;

    pub fn EVP_PKEY_CTX_ctrl(ctx: *mut EVP_PKEY_CTX, keytype: c_int, optype: c_int, cmd: c_int, p1: c_int, p2: *mut c_void) -> c_int;

+62 −1
Original line number Diff line number Diff line
use libc::{c_void, c_char, c_int};
use std::ptr;
use std::mem;
use std::ffi::CString;
use ffi;
use foreign_types::{Opaque, ForeignType, ForeignTypeRef};

@@ -11,7 +12,7 @@ use dsa::Dsa;
use ec::EcKey;
use rsa::{Rsa, Padding};
use error::ErrorStack;
use util::{CallbackState, invoke_passwd_cb_old};
use util::{CallbackState, invoke_passwd_cb, invoke_passwd_cb_old};

foreign_type! {
    type CType = ffi::EVP_PKEY;
@@ -140,6 +141,47 @@ impl PKey {
    private_key_from_pem!(PKey, ffi::PEM_read_bio_PrivateKey);
    public_key_from_pem!(PKey, ffi::PEM_read_bio_PUBKEY);

    /// 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, ErrorStack>
        where F: FnOnce(&mut [u8]) -> Result<usize, ErrorStack>
    {
        unsafe {
            ffi::init();
            let mut cb = CallbackState::new(callback);
            let bio = try!(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(PKey)
        }
    }

    /// 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, ErrorStack> {
        unsafe {
            ffi::init();
            let bio = try!(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(PKey)
        }
    }

    #[deprecated(since = "0.9.2", note = "use private_key_from_pem_callback")]
    pub fn private_key_from_pem_cb<F>(buf: &[u8], pass_cb: F) -> Result<PKey, ErrorStack>
        where F: FnOnce(&mut [c_char]) -> usize
@@ -200,6 +242,25 @@ mod tests {
        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");

openssl/test/pkcs8.der

0 → 100644
+1.27 KiB

File added.

No diff preview for this file type.