diff options
Diffstat (limited to 'vendor/openssl/src/pkcs12.rs')
-rw-r--r-- | vendor/openssl/src/pkcs12.rs | 392 |
1 files changed, 392 insertions, 0 deletions
diff --git a/vendor/openssl/src/pkcs12.rs b/vendor/openssl/src/pkcs12.rs new file mode 100644 index 0000000..d74705e --- /dev/null +++ b/vendor/openssl/src/pkcs12.rs @@ -0,0 +1,392 @@ +//! PKCS #12 archives. + +use foreign_types::{ForeignType, ForeignTypeRef}; +use libc::c_int; +use std::ffi::CString; +use std::ptr; + +use crate::error::ErrorStack; +#[cfg(not(boringssl))] +use crate::hash::MessageDigest; +use crate::nid::Nid; +use crate::pkey::{HasPrivate, PKey, PKeyRef, Private}; +use crate::stack::Stack; +use crate::util::ForeignTypeExt; +use crate::x509::{X509Ref, X509}; +use crate::{cvt, cvt_p}; +use openssl_macros::corresponds; + +foreign_type_and_impl_send_sync! { + type CType = ffi::PKCS12; + fn drop = ffi::PKCS12_free; + + pub struct Pkcs12; + pub struct Pkcs12Ref; +} + +impl Pkcs12Ref { + to_der! { + /// Serializes the `Pkcs12` to its standard DER encoding. + #[corresponds(i2d_PKCS12)] + to_der, + ffi::i2d_PKCS12 + } + + /// Deprecated. + #[deprecated(note = "Use parse2 instead", since = "0.10.46")] + #[allow(deprecated)] + pub fn parse(&self, pass: &str) -> Result<ParsedPkcs12, ErrorStack> { + let parsed = self.parse2(pass)?; + + Ok(ParsedPkcs12 { + pkey: parsed.pkey.unwrap(), + cert: parsed.cert.unwrap(), + chain: parsed.ca, + }) + } + + /// Extracts the contents of the `Pkcs12`. + #[corresponds(PKCS12_parse)] + pub fn parse2(&self, pass: &str) -> Result<ParsedPkcs12_2, ErrorStack> { + unsafe { + let pass = CString::new(pass.as_bytes()).unwrap(); + + let mut pkey = ptr::null_mut(); + let mut cert = ptr::null_mut(); + let mut ca = ptr::null_mut(); + + cvt(ffi::PKCS12_parse( + self.as_ptr(), + pass.as_ptr(), + &mut pkey, + &mut cert, + &mut ca, + ))?; + + let pkey = PKey::from_ptr_opt(pkey); + let cert = X509::from_ptr_opt(cert); + let ca = Stack::from_ptr_opt(ca); + + Ok(ParsedPkcs12_2 { pkey, cert, ca }) + } + } +} + +impl Pkcs12 { + from_der! { + /// Deserializes a DER-encoded PKCS#12 archive. + #[corresponds(d2i_PKCS12)] + from_der, + Pkcs12, + ffi::d2i_PKCS12 + } + + /// Creates a new builder for a protected pkcs12 certificate. + /// + /// This uses the defaults from the OpenSSL library: + /// + /// * `nid_key` - `AES_256_CBC` (3.0.0+) or `PBE_WITHSHA1AND3_KEY_TRIPLEDES_CBC` + /// * `nid_cert` - `AES_256_CBC` (3.0.0+) or `PBE_WITHSHA1AND40BITRC2_CBC` + /// * `iter` - `2048` + /// * `mac_iter` - `2048` + /// * `mac_md` - `SHA-256` (3.0.0+) or `SHA-1` (`SHA-1` only for BoringSSL) + pub fn builder() -> Pkcs12Builder { + ffi::init(); + + Pkcs12Builder { + name: None, + pkey: None, + cert: None, + ca: None, + nid_key: Nid::UNDEF, + nid_cert: Nid::UNDEF, + iter: ffi::PKCS12_DEFAULT_ITER, + mac_iter: ffi::PKCS12_DEFAULT_ITER, + #[cfg(not(boringssl))] + mac_md: None, + } + } +} + +#[deprecated(note = "Use ParsedPkcs12_2 instead", since = "0.10.46")] +pub struct ParsedPkcs12 { + pub pkey: PKey<Private>, + pub cert: X509, + pub chain: Option<Stack<X509>>, +} + +pub struct ParsedPkcs12_2 { + pub pkey: Option<PKey<Private>>, + pub cert: Option<X509>, + pub ca: Option<Stack<X509>>, +} + +pub struct Pkcs12Builder { + // FIXME borrow + name: Option<CString>, + pkey: Option<PKey<Private>>, + cert: Option<X509>, + ca: Option<Stack<X509>>, + nid_key: Nid, + nid_cert: Nid, + iter: c_int, + mac_iter: c_int, + // FIXME remove + #[cfg(not(boringssl))] + mac_md: Option<MessageDigest>, +} + +impl Pkcs12Builder { + /// The `friendlyName` used for the certificate and private key. + pub fn name(&mut self, name: &str) -> &mut Self { + self.name = Some(CString::new(name).unwrap()); + self + } + + /// The private key. + pub fn pkey<T>(&mut self, pkey: &PKeyRef<T>) -> &mut Self + where + T: HasPrivate, + { + let new_pkey = unsafe { PKeyRef::from_ptr(pkey.as_ptr()) }; + self.pkey = Some(new_pkey.to_owned()); + self + } + + /// The certificate. + pub fn cert(&mut self, cert: &X509Ref) -> &mut Self { + self.cert = Some(cert.to_owned()); + self + } + + /// An additional set of certificates to include in the archive beyond the one provided to + /// `build`. + pub fn ca(&mut self, ca: Stack<X509>) -> &mut Self { + self.ca = Some(ca); + self + } + + /// The encryption algorithm that should be used for the key + pub fn key_algorithm(&mut self, nid: Nid) -> &mut Self { + self.nid_key = nid; + self + } + + /// The encryption algorithm that should be used for the cert + pub fn cert_algorithm(&mut self, nid: Nid) -> &mut Self { + self.nid_cert = nid; + self + } + + /// Key iteration count, default is 2048 as of this writing + pub fn key_iter(&mut self, iter: u32) -> &mut Self { + self.iter = iter as c_int; + self + } + + /// MAC iteration count, default is the same as key_iter. + /// + /// Old implementations don't understand MAC iterations greater than 1, (pre 1.0.1?), if such + /// compatibility is required this should be set to 1. + pub fn mac_iter(&mut self, mac_iter: u32) -> &mut Self { + self.mac_iter = mac_iter as c_int; + self + } + + /// MAC message digest type + #[cfg(not(boringssl))] + pub fn mac_md(&mut self, md: MessageDigest) -> &mut Self { + self.mac_md = Some(md); + self + } + + /// Deprecated. + #[deprecated( + note = "Use Self::{name, pkey, cert, build2} instead.", + since = "0.10.46" + )] + pub fn build<T>( + mut self, + password: &str, + friendly_name: &str, + pkey: &PKeyRef<T>, + cert: &X509Ref, + ) -> Result<Pkcs12, ErrorStack> + where + T: HasPrivate, + { + self.name(friendly_name) + .pkey(pkey) + .cert(cert) + .build2(password) + } + + /// Builds the PKCS#12 object. + #[corresponds(PKCS12_create)] + pub fn build2(&self, password: &str) -> Result<Pkcs12, ErrorStack> { + unsafe { + let pass = CString::new(password).unwrap(); + let pass = pass.as_ptr(); + let friendly_name = self.name.as_ref().map_or(ptr::null(), |p| p.as_ptr()); + let pkey = self.pkey.as_ref().map_or(ptr::null(), |p| p.as_ptr()); + let cert = self.cert.as_ref().map_or(ptr::null(), |p| p.as_ptr()); + let ca = self + .ca + .as_ref() + .map(|ca| ca.as_ptr()) + .unwrap_or(ptr::null_mut()); + let nid_key = self.nid_key.as_raw(); + let nid_cert = self.nid_cert.as_raw(); + + // According to the OpenSSL docs, keytype is a non-standard extension for MSIE, + // It's values are KEY_SIG or KEY_EX, see the OpenSSL docs for more information: + // https://www.openssl.org/docs/manmaster/crypto/PKCS12_create.html + let keytype = 0; + + let pkcs12 = cvt_p(ffi::PKCS12_create( + pass as *mut _, + friendly_name as *mut _, + pkey as *mut _, + cert as *mut _, + ca, + nid_key, + nid_cert, + self.iter, + self.mac_iter, + keytype, + )) + .map(Pkcs12)?; + + #[cfg(not(boringssl))] + // BoringSSL does not support overriding the MAC and will always + // use SHA-1 + { + let md_type = self + .mac_md + .map(|md_type| md_type.as_ptr()) + .unwrap_or(ptr::null()); + + cvt(ffi::PKCS12_set_mac( + pkcs12.as_ptr(), + pass, + -1, + ptr::null_mut(), + 0, + self.mac_iter, + md_type, + ))?; + } + + Ok(pkcs12) + } + } +} + +#[cfg(test)] +mod test { + use crate::asn1::Asn1Time; + use crate::hash::MessageDigest; + use crate::nid::Nid; + use crate::pkey::PKey; + use crate::rsa::Rsa; + use crate::x509::extension::KeyUsage; + use crate::x509::{X509Name, X509}; + + use super::*; + + #[test] + fn parse() { + #[cfg(ossl300)] + let _provider = crate::provider::Provider::try_load(None, "legacy", true).unwrap(); + + let der = include_bytes!("../test/identity.p12"); + let pkcs12 = Pkcs12::from_der(der).unwrap(); + let parsed = pkcs12.parse2("mypass").unwrap(); + + assert_eq!( + hex::encode(parsed.cert.unwrap().digest(MessageDigest::sha1()).unwrap()), + "59172d9313e84459bcff27f967e79e6e9217e584" + ); + + let chain = parsed.ca.unwrap(); + assert_eq!(chain.len(), 1); + assert_eq!( + hex::encode(chain[0].digest(MessageDigest::sha1()).unwrap()), + "c0cbdf7cdd03c9773e5468e1f6d2da7d5cbb1875" + ); + } + + #[test] + fn parse_empty_chain() { + #[cfg(ossl300)] + let _provider = crate::provider::Provider::try_load(None, "legacy", true).unwrap(); + + let der = include_bytes!("../test/keystore-empty-chain.p12"); + let pkcs12 = Pkcs12::from_der(der).unwrap(); + let parsed = pkcs12.parse2("cassandra").unwrap(); + if let Some(stack) = parsed.ca { + assert_eq!(stack.len(), 0); + } + } + + #[test] + fn create() { + let subject_name = "ns.example.com"; + let rsa = Rsa::generate(2048).unwrap(); + let pkey = PKey::from_rsa(rsa).unwrap(); + + let mut name = X509Name::builder().unwrap(); + name.append_entry_by_nid(Nid::COMMONNAME, subject_name) + .unwrap(); + let name = name.build(); + + let key_usage = KeyUsage::new().digital_signature().build().unwrap(); + + let mut builder = X509::builder().unwrap(); + builder.set_version(2).unwrap(); + builder + .set_not_before(&Asn1Time::days_from_now(0).unwrap()) + .unwrap(); + builder + .set_not_after(&Asn1Time::days_from_now(365).unwrap()) + .unwrap(); + builder.set_subject_name(&name).unwrap(); + builder.set_issuer_name(&name).unwrap(); + builder.append_extension(key_usage).unwrap(); + builder.set_pubkey(&pkey).unwrap(); + builder.sign(&pkey, MessageDigest::sha256()).unwrap(); + let cert = builder.build(); + + let pkcs12 = Pkcs12::builder() + .name(subject_name) + .pkey(&pkey) + .cert(&cert) + .build2("mypass") + .unwrap(); + let der = pkcs12.to_der().unwrap(); + + let pkcs12 = Pkcs12::from_der(&der).unwrap(); + let parsed = pkcs12.parse2("mypass").unwrap(); + + assert_eq!( + &*parsed.cert.unwrap().digest(MessageDigest::sha1()).unwrap(), + &*cert.digest(MessageDigest::sha1()).unwrap() + ); + assert!(parsed.pkey.unwrap().public_eq(&pkey)); + } + + #[test] + fn create_only_ca() { + let ca = include_bytes!("../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + let mut chain = Stack::new().unwrap(); + chain.push(ca).unwrap(); + + let pkcs12 = Pkcs12::builder().ca(chain).build2("hunter2").unwrap(); + let parsed = pkcs12.parse2("hunter2").unwrap(); + + assert!(parsed.cert.is_none()); + assert!(parsed.pkey.is_none()); + assert_eq!(parsed.ca.unwrap().len(), 1); + } +} |