diff options
Diffstat (limited to 'vendor/openssl/src')
56 files changed, 33623 insertions, 0 deletions
diff --git a/vendor/openssl/src/aes.rs b/vendor/openssl/src/aes.rs new file mode 100644 index 0000000..cbc4999 --- /dev/null +++ b/vendor/openssl/src/aes.rs @@ -0,0 +1,319 @@ +//! Low level AES IGE and key wrapping functionality +//! +//! AES ECB, CBC, XTS, CTR, CFB, GCM and other conventional symmetric encryption +//! modes are found in [`symm`]. This is the implementation of AES IGE and key wrapping +//! +//! Advanced Encryption Standard (AES) provides symmetric key cipher that +//! the same key is used to encrypt and decrypt data. This implementation +//! uses 128, 192, or 256 bit keys. This module provides functions to +//! create a new key with [`new_encrypt`] and perform an encryption/decryption +//! using that key with [`aes_ige`]. +//! +//! [`new_encrypt`]: struct.AesKey.html#method.new_encrypt +//! [`aes_ige`]: fn.aes_ige.html +//! +//! The [`symm`] module should be used in preference to this module in most cases. +//! The IGE block cipher is a non-traditional cipher mode. More traditional AES +//! encryption methods are found in the [`Crypter`] and [`Cipher`] structs. +//! +//! [`symm`]: ../symm/index.html +//! [`Crypter`]: ../symm/struct.Crypter.html +//! [`Cipher`]: ../symm/struct.Cipher.html +//! +//! # Examples + +#![cfg_attr( + all(not(boringssl), not(osslconf = "OPENSSL_NO_DEPRECATED_3_0")), + doc = r#"\ +## AES IGE +```rust +use openssl::aes::{AesKey, aes_ige}; +use openssl::symm::Mode; + +let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"; +let plaintext = b"\x12\x34\x56\x78\x90\x12\x34\x56\x12\x34\x56\x78\x90\x12\x34\x56"; +let mut iv = *b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\ + \x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"; + + let key = AesKey::new_encrypt(key).unwrap(); + let mut output = [0u8; 16]; + aes_ige(plaintext, &mut output, &key, &mut iv, Mode::Encrypt); + assert_eq!(output, *b"\xa6\xad\x97\x4d\x5c\xea\x1d\x36\xd2\xf3\x67\x98\x09\x07\xed\x32"); +```"# +)] + +//! +//! ## Key wrapping +//! ```rust +//! use openssl::aes::{AesKey, unwrap_key, wrap_key}; +//! +//! let kek = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"; +//! let key_to_wrap = b"\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF"; +//! +//! let enc_key = AesKey::new_encrypt(kek).unwrap(); +//! let mut ciphertext = [0u8; 24]; +//! wrap_key(&enc_key, None, &mut ciphertext, &key_to_wrap[..]).unwrap(); +//! let dec_key = AesKey::new_decrypt(kek).unwrap(); +//! let mut orig_key = [0u8; 16]; +//! unwrap_key(&dec_key, None, &mut orig_key, &ciphertext[..]).unwrap(); +//! +//! assert_eq!(&orig_key[..], &key_to_wrap[..]); +//! ``` +//! +use cfg_if::cfg_if; +use libc::{c_int, c_uint}; +use std::mem::MaybeUninit; +use std::ptr; + +#[cfg(not(boringssl))] +use crate::symm::Mode; +use openssl_macros::corresponds; + +/// Provides Error handling for parsing keys. +#[derive(Debug)] +pub struct KeyError(()); + +/// The key used to encrypt or decrypt cipher blocks. +pub struct AesKey(ffi::AES_KEY); + +cfg_if! { + if #[cfg(boringssl)] { + type AesBitType = c_uint; + type AesSizeType = usize; + } else { + type AesBitType = c_int; + type AesSizeType = c_uint; + } +} + +impl AesKey { + /// Prepares a key for encryption. + /// + /// # Failure + /// + /// Returns an error if the key is not 128, 192, or 256 bits. + #[corresponds(AES_set_encrypt_key)] + pub fn new_encrypt(key: &[u8]) -> Result<AesKey, KeyError> { + unsafe { + assert!(key.len() <= c_int::max_value() as usize / 8); + + let mut aes_key = MaybeUninit::uninit(); + let r = ffi::AES_set_encrypt_key( + key.as_ptr() as *const _, + key.len() as AesBitType * 8, + aes_key.as_mut_ptr(), + ); + if r == 0 { + Ok(AesKey(aes_key.assume_init())) + } else { + Err(KeyError(())) + } + } + } + + /// Prepares a key for decryption. + /// + /// # Failure + /// + /// Returns an error if the key is not 128, 192, or 256 bits. + #[corresponds(AES_set_decrypt_key)] + pub fn new_decrypt(key: &[u8]) -> Result<AesKey, KeyError> { + unsafe { + assert!(key.len() <= c_int::max_value() as usize / 8); + + let mut aes_key = MaybeUninit::uninit(); + let r = ffi::AES_set_decrypt_key( + key.as_ptr() as *const _, + key.len() as AesBitType * 8, + aes_key.as_mut_ptr(), + ); + + if r == 0 { + Ok(AesKey(aes_key.assume_init())) + } else { + Err(KeyError(())) + } + } + } +} + +/// Performs AES IGE encryption or decryption +/// +/// AES IGE (Infinite Garble Extension) is a form of AES block cipher utilized in +/// OpenSSL. Infinite Garble refers to propagating forward errors. IGE, like other +/// block ciphers implemented for AES requires an initialization vector. The IGE mode +/// allows a stream of blocks to be encrypted or decrypted without having the entire +/// plaintext available. For more information, visit [AES IGE Encryption]. +/// +/// This block cipher uses 16 byte blocks. The rust implementation will panic +/// if the input or output does not meet this 16-byte boundary. Attention must +/// be made in this low level implementation to pad the value to the 128-bit boundary. +/// +/// [AES IGE Encryption]: http://www.links.org/files/openssl-ige.pdf +/// +/// # Panics +/// +/// Panics if `in_` is not the same length as `out`, if that length is not a multiple of 16, or if +/// `iv` is not at least 32 bytes. +#[cfg(not(boringssl))] +#[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] +#[corresponds(AES_ige_encrypt)] +pub fn aes_ige(in_: &[u8], out: &mut [u8], key: &AesKey, iv: &mut [u8], mode: Mode) { + unsafe { + assert!(in_.len() == out.len()); + assert!(in_.len() % ffi::AES_BLOCK_SIZE as usize == 0); + assert!(iv.len() >= ffi::AES_BLOCK_SIZE as usize * 2); + + let mode = match mode { + Mode::Encrypt => ffi::AES_ENCRYPT, + Mode::Decrypt => ffi::AES_DECRYPT, + }; + ffi::AES_ige_encrypt( + in_.as_ptr() as *const _, + out.as_mut_ptr() as *mut _, + in_.len(), + &key.0, + iv.as_mut_ptr() as *mut _, + mode, + ); + } +} + +/// Wrap a key, according to [RFC 3394](https://tools.ietf.org/html/rfc3394) +/// +/// * `key`: The key-encrypting-key to use. Must be a encrypting key +/// * `iv`: The IV to use. You must use the same IV for both wrapping and unwrapping +/// * `out`: The output buffer to store the ciphertext +/// * `in_`: The input buffer, storing the key to be wrapped +/// +/// Returns the number of bytes written into `out` +/// +/// # Panics +/// +/// Panics if either `out` or `in_` do not have sizes that are a multiple of 8, or if +/// `out` is not 8 bytes longer than `in_` +#[corresponds(AES_wrap_key)] +pub fn wrap_key( + key: &AesKey, + iv: Option<[u8; 8]>, + out: &mut [u8], + in_: &[u8], +) -> Result<usize, KeyError> { + unsafe { + assert!(out.len() >= in_.len() + 8); // Ciphertext is 64 bits longer (see 2.2.1) + + let written = ffi::AES_wrap_key( + &key.0 as *const _ as *mut _, // this is safe, the implementation only uses the key as a const pointer. + iv.as_ref() + .map_or(ptr::null(), |iv| iv.as_ptr() as *const _), + out.as_ptr() as *mut _, + in_.as_ptr() as *const _, + in_.len() as AesSizeType, + ); + if written <= 0 { + Err(KeyError(())) + } else { + Ok(written as usize) + } + } +} + +/// Unwrap a key, according to [RFC 3394](https://tools.ietf.org/html/rfc3394) +/// +/// * `key`: The key-encrypting-key to decrypt the wrapped key. Must be a decrypting key +/// * `iv`: The same IV used for wrapping the key +/// * `out`: The buffer to write the unwrapped key to +/// * `in_`: The input ciphertext +/// +/// Returns the number of bytes written into `out` +/// +/// # Panics +/// +/// Panics if either `out` or `in_` do not have sizes that are a multiple of 8, or +/// if `in_` is not 8 bytes longer than `out` +#[corresponds(AES_unwrap_key)] +pub fn unwrap_key( + key: &AesKey, + iv: Option<[u8; 8]>, + out: &mut [u8], + in_: &[u8], +) -> Result<usize, KeyError> { + unsafe { + assert!(out.len() + 8 <= in_.len()); + + let written = ffi::AES_unwrap_key( + &key.0 as *const _ as *mut _, // this is safe, the implementation only uses the key as a const pointer. + iv.as_ref() + .map_or(ptr::null(), |iv| iv.as_ptr() as *const _), + out.as_ptr() as *mut _, + in_.as_ptr() as *const _, + in_.len() as AesSizeType, + ); + + if written <= 0 { + Err(KeyError(())) + } else { + Ok(written as usize) + } + } +} + +#[cfg(test)] +mod test { + use hex::FromHex; + + use super::*; + #[cfg(not(boringssl))] + use crate::symm::Mode; + + // From https://www.mgp25.com/AESIGE/ + #[test] + #[cfg(not(boringssl))] + #[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] + fn ige_vector_1() { + let raw_key = "000102030405060708090A0B0C0D0E0F"; + let raw_iv = "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"; + let raw_pt = "0000000000000000000000000000000000000000000000000000000000000000"; + let raw_ct = "1A8519A6557BE652E9DA8E43DA4EF4453CF456B4CA488AA383C79C98B34797CB"; + + let key = AesKey::new_encrypt(&Vec::from_hex(raw_key).unwrap()).unwrap(); + let mut iv = Vec::from_hex(raw_iv).unwrap(); + let pt = Vec::from_hex(raw_pt).unwrap(); + let ct = Vec::from_hex(raw_ct).unwrap(); + + let mut ct_actual = vec![0; ct.len()]; + aes_ige(&pt, &mut ct_actual, &key, &mut iv, Mode::Encrypt); + assert_eq!(ct_actual, ct); + + let key = AesKey::new_decrypt(&Vec::from_hex(raw_key).unwrap()).unwrap(); + let mut iv = Vec::from_hex(raw_iv).unwrap(); + let mut pt_actual = vec![0; pt.len()]; + aes_ige(&ct, &mut pt_actual, &key, &mut iv, Mode::Decrypt); + assert_eq!(pt_actual, pt); + } + + // from the RFC https://tools.ietf.org/html/rfc3394#section-2.2.3 + #[test] + fn test_wrap_unwrap() { + let raw_key = Vec::from_hex("000102030405060708090A0B0C0D0E0F").unwrap(); + let key_data = Vec::from_hex("00112233445566778899AABBCCDDEEFF").unwrap(); + let expected_ciphertext = + Vec::from_hex("1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5").unwrap(); + + let enc_key = AesKey::new_encrypt(&raw_key).unwrap(); + let mut wrapped = [0; 24]; + assert_eq!( + wrap_key(&enc_key, None, &mut wrapped, &key_data).unwrap(), + 24 + ); + assert_eq!(&wrapped[..], &expected_ciphertext[..]); + + let dec_key = AesKey::new_decrypt(&raw_key).unwrap(); + let mut unwrapped = [0; 16]; + assert_eq!( + unwrap_key(&dec_key, None, &mut unwrapped, &wrapped).unwrap(), + 16 + ); + assert_eq!(&unwrapped[..], &key_data[..]); + } +} diff --git a/vendor/openssl/src/asn1.rs b/vendor/openssl/src/asn1.rs new file mode 100644 index 0000000..801310d --- /dev/null +++ b/vendor/openssl/src/asn1.rs @@ -0,0 +1,911 @@ +#![deny(missing_docs)] + +//! Defines the format of certificates +//! +//! This module is used by [`x509`] and other certificate building functions +//! to describe time, strings, and objects. +//! +//! Abstract Syntax Notation One is an interface description language. +//! The specification comes from [X.208] by OSI, and rewritten in X.680. +//! ASN.1 describes properties of an object with a type set. Those types +//! can be atomic, structured, choice, and other (CHOICE and ANY). These +//! types are expressed as a number and the assignment operator ::= gives +//! the type a name. +//! +//! The implementation here provides a subset of the ASN.1 types that OpenSSL +//! uses, especially in the properties of a certificate used in HTTPS. +//! +//! [X.208]: https://www.itu.int/rec/T-REC-X.208-198811-W/en +//! [`x509`]: ../x509/struct.X509Builder.html +//! +//! ## Examples +//! +//! ``` +//! use openssl::asn1::Asn1Time; +//! let tomorrow = Asn1Time::days_from_now(1); +//! ``` +use cfg_if::cfg_if; +use foreign_types::{ForeignType, ForeignTypeRef}; +use libc::{c_char, c_int, c_long, time_t}; +use std::cmp::Ordering; +use std::convert::TryInto; +use std::ffi::CString; +use std::fmt; +use std::ptr; +use std::slice; +use std::str; + +use crate::bio::MemBio; +use crate::bn::{BigNum, BigNumRef}; +use crate::error::ErrorStack; +use crate::nid::Nid; +use crate::stack::Stackable; +use crate::string::OpensslString; +use crate::{cvt, cvt_p}; +use openssl_macros::corresponds; + +foreign_type_and_impl_send_sync! { + type CType = ffi::ASN1_GENERALIZEDTIME; + fn drop = ffi::ASN1_GENERALIZEDTIME_free; + + /// Non-UTC representation of time + /// + /// If a time can be represented by UTCTime, UTCTime is used + /// otherwise, ASN1_GENERALIZEDTIME is used. This would be, for + /// example outside the year range of 1950-2049. + /// + /// [ASN1_GENERALIZEDTIME_set] documentation from OpenSSL provides + /// further details of implementation. Note: these docs are from the master + /// branch as documentation on the 1.1.0 branch did not include this page. + /// + /// [ASN1_GENERALIZEDTIME_set]: https://www.openssl.org/docs/manmaster/man3/ASN1_GENERALIZEDTIME_set.html + pub struct Asn1GeneralizedTime; + /// Reference to a [`Asn1GeneralizedTime`] + /// + /// [`Asn1GeneralizedTime`]: struct.Asn1GeneralizedTime.html + pub struct Asn1GeneralizedTimeRef; +} + +impl fmt::Display for Asn1GeneralizedTimeRef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + unsafe { + let mem_bio = match MemBio::new() { + Err(_) => return f.write_str("error"), + Ok(m) => m, + }; + let print_result = cvt(ffi::ASN1_GENERALIZEDTIME_print( + mem_bio.as_ptr(), + self.as_ptr(), + )); + match print_result { + Err(_) => f.write_str("error"), + Ok(_) => f.write_str(str::from_utf8_unchecked(mem_bio.get_buf())), + } + } + } +} + +/// The type of an ASN.1 value. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct Asn1Type(c_int); + +#[allow(missing_docs)] // no need to document the constants +impl Asn1Type { + pub const EOC: Asn1Type = Asn1Type(ffi::V_ASN1_EOC); + + pub const BOOLEAN: Asn1Type = Asn1Type(ffi::V_ASN1_BOOLEAN); + + pub const INTEGER: Asn1Type = Asn1Type(ffi::V_ASN1_INTEGER); + + pub const BIT_STRING: Asn1Type = Asn1Type(ffi::V_ASN1_BIT_STRING); + + pub const OCTET_STRING: Asn1Type = Asn1Type(ffi::V_ASN1_OCTET_STRING); + + pub const NULL: Asn1Type = Asn1Type(ffi::V_ASN1_NULL); + + pub const OBJECT: Asn1Type = Asn1Type(ffi::V_ASN1_OBJECT); + + pub const OBJECT_DESCRIPTOR: Asn1Type = Asn1Type(ffi::V_ASN1_OBJECT_DESCRIPTOR); + + pub const EXTERNAL: Asn1Type = Asn1Type(ffi::V_ASN1_EXTERNAL); + + pub const REAL: Asn1Type = Asn1Type(ffi::V_ASN1_REAL); + + pub const ENUMERATED: Asn1Type = Asn1Type(ffi::V_ASN1_ENUMERATED); + + pub const UTF8STRING: Asn1Type = Asn1Type(ffi::V_ASN1_UTF8STRING); + + pub const SEQUENCE: Asn1Type = Asn1Type(ffi::V_ASN1_SEQUENCE); + + pub const SET: Asn1Type = Asn1Type(ffi::V_ASN1_SET); + + pub const NUMERICSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_NUMERICSTRING); + + pub const PRINTABLESTRING: Asn1Type = Asn1Type(ffi::V_ASN1_PRINTABLESTRING); + + pub const T61STRING: Asn1Type = Asn1Type(ffi::V_ASN1_T61STRING); + + pub const TELETEXSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_TELETEXSTRING); + + pub const VIDEOTEXSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_VIDEOTEXSTRING); + + pub const IA5STRING: Asn1Type = Asn1Type(ffi::V_ASN1_IA5STRING); + + pub const UTCTIME: Asn1Type = Asn1Type(ffi::V_ASN1_UTCTIME); + + pub const GENERALIZEDTIME: Asn1Type = Asn1Type(ffi::V_ASN1_GENERALIZEDTIME); + + pub const GRAPHICSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_GRAPHICSTRING); + + pub const ISO64STRING: Asn1Type = Asn1Type(ffi::V_ASN1_ISO64STRING); + + pub const VISIBLESTRING: Asn1Type = Asn1Type(ffi::V_ASN1_VISIBLESTRING); + + pub const GENERALSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_GENERALSTRING); + + pub const UNIVERSALSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_UNIVERSALSTRING); + + pub const BMPSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_BMPSTRING); + + /// Constructs an `Asn1Type` from a raw OpenSSL value. + pub fn from_raw(value: c_int) -> Self { + Asn1Type(value) + } + + /// Returns the raw OpenSSL value represented by this type. + pub fn as_raw(&self) -> c_int { + self.0 + } +} + +/// Difference between two ASN1 times. +/// +/// This `struct` is created by the [`diff`] method on [`Asn1TimeRef`]. See its +/// documentation for more. +/// +/// [`diff`]: struct.Asn1TimeRef.html#method.diff +/// [`Asn1TimeRef`]: struct.Asn1TimeRef.html +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg(ossl102)] +pub struct TimeDiff { + /// Difference in days + pub days: c_int, + /// Difference in seconds. + /// + /// This is always less than the number of seconds in a day. + pub secs: c_int, +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::ASN1_TIME; + fn drop = ffi::ASN1_TIME_free; + /// Time storage and comparison + /// + /// Asn1Time should be used to store and share time information + /// using certificates. If Asn1Time is set using a string, it must + /// be in either YYMMDDHHMMSSZ, YYYYMMDDHHMMSSZ, or another ASN.1 format. + /// + /// [ASN_TIME_set] documentation at OpenSSL explains the ASN.1 implementation + /// used by OpenSSL. + /// + /// [ASN_TIME_set]: https://www.openssl.org/docs/manmaster/crypto/ASN1_TIME_set.html + pub struct Asn1Time; + /// Reference to an [`Asn1Time`] + /// + /// [`Asn1Time`]: struct.Asn1Time.html + pub struct Asn1TimeRef; +} + +impl Asn1TimeRef { + /// Find difference between two times + #[corresponds(ASN1_TIME_diff)] + #[cfg(ossl102)] + pub fn diff(&self, compare: &Self) -> Result<TimeDiff, ErrorStack> { + let mut days = 0; + let mut secs = 0; + let other = compare.as_ptr(); + + let err = unsafe { ffi::ASN1_TIME_diff(&mut days, &mut secs, self.as_ptr(), other) }; + + match err { + 0 => Err(ErrorStack::get()), + _ => Ok(TimeDiff { days, secs }), + } + } + + /// Compare two times + #[corresponds(ASN1_TIME_compare)] + #[cfg(ossl102)] + pub fn compare(&self, other: &Self) -> Result<Ordering, ErrorStack> { + let d = self.diff(other)?; + if d.days > 0 || d.secs > 0 { + return Ok(Ordering::Less); + } + if d.days < 0 || d.secs < 0 { + return Ok(Ordering::Greater); + } + + Ok(Ordering::Equal) + } +} + +#[cfg(ossl102)] +impl PartialEq for Asn1TimeRef { + fn eq(&self, other: &Asn1TimeRef) -> bool { + self.diff(other) + .map(|t| t.days == 0 && t.secs == 0) + .unwrap_or(false) + } +} + +#[cfg(ossl102)] +impl PartialEq<Asn1Time> for Asn1TimeRef { + fn eq(&self, other: &Asn1Time) -> bool { + self.diff(other) + .map(|t| t.days == 0 && t.secs == 0) + .unwrap_or(false) + } +} + +#[cfg(ossl102)] +impl<'a> PartialEq<Asn1Time> for &'a Asn1TimeRef { + fn eq(&self, other: &Asn1Time) -> bool { + self.diff(other) + .map(|t| t.days == 0 && t.secs == 0) + .unwrap_or(false) + } +} + +#[cfg(ossl102)] +impl PartialOrd for Asn1TimeRef { + fn partial_cmp(&self, other: &Asn1TimeRef) -> Option<Ordering> { + self.compare(other).ok() + } +} + +#[cfg(ossl102)] +impl PartialOrd<Asn1Time> for Asn1TimeRef { + fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> { + self.compare(other).ok() + } +} + +#[cfg(ossl102)] +impl<'a> PartialOrd<Asn1Time> for &'a Asn1TimeRef { + fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> { + self.compare(other).ok() + } +} + +impl fmt::Display for Asn1TimeRef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + unsafe { + let mem_bio = match MemBio::new() { + Err(_) => return f.write_str("error"), + Ok(m) => m, + }; + let print_result = cvt(ffi::ASN1_TIME_print(mem_bio.as_ptr(), self.as_ptr())); + match print_result { + Err(_) => f.write_str("error"), + Ok(_) => f.write_str(str::from_utf8_unchecked(mem_bio.get_buf())), + } + } + } +} + +impl fmt::Debug for Asn1TimeRef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.to_string()) + } +} + +impl Asn1Time { + #[corresponds(ASN1_TIME_new)] + fn new() -> Result<Asn1Time, ErrorStack> { + ffi::init(); + + unsafe { + let handle = cvt_p(ffi::ASN1_TIME_new())?; + Ok(Asn1Time::from_ptr(handle)) + } + } + + #[corresponds(X509_gmtime_adj)] + fn from_period(period: c_long) -> Result<Asn1Time, ErrorStack> { + ffi::init(); + + unsafe { + let handle = cvt_p(ffi::X509_gmtime_adj(ptr::null_mut(), period))?; + Ok(Asn1Time::from_ptr(handle)) + } + } + + /// Creates a new time on specified interval in days from now + pub fn days_from_now(days: u32) -> Result<Asn1Time, ErrorStack> { + Asn1Time::from_period(days as c_long * 60 * 60 * 24) + } + + /// Creates a new time from the specified `time_t` value + #[corresponds(ASN1_TIME_set)] + pub fn from_unix(time: time_t) -> Result<Asn1Time, ErrorStack> { + ffi::init(); + + unsafe { + let handle = cvt_p(ffi::ASN1_TIME_set(ptr::null_mut(), time))?; + Ok(Asn1Time::from_ptr(handle)) + } + } + + /// Creates a new time corresponding to the specified ASN1 time string. + #[corresponds(ASN1_TIME_set_string)] + #[allow(clippy::should_implement_trait)] + pub fn from_str(s: &str) -> Result<Asn1Time, ErrorStack> { + unsafe { + let s = CString::new(s).unwrap(); + + let time = Asn1Time::new()?; + cvt(ffi::ASN1_TIME_set_string(time.as_ptr(), s.as_ptr()))?; + + Ok(time) + } + } + + /// Creates a new time corresponding to the specified X509 time string. + /// + /// Requires OpenSSL 1.1.1 or newer. + #[corresponds(ASN1_TIME_set_string_X509)] + #[cfg(ossl111)] + pub fn from_str_x509(s: &str) -> Result<Asn1Time, ErrorStack> { + unsafe { + let s = CString::new(s).unwrap(); + + let time = Asn1Time::new()?; + cvt(ffi::ASN1_TIME_set_string_X509(time.as_ptr(), s.as_ptr()))?; + + Ok(time) + } + } +} + +#[cfg(ossl102)] +impl PartialEq for Asn1Time { + fn eq(&self, other: &Asn1Time) -> bool { + self.diff(other) + .map(|t| t.days == 0 && t.secs == 0) + .unwrap_or(false) + } +} + +#[cfg(ossl102)] +impl PartialEq<Asn1TimeRef> for Asn1Time { + fn eq(&self, other: &Asn1TimeRef) -> bool { + self.diff(other) + .map(|t| t.days == 0 && t.secs == 0) + .unwrap_or(false) + } +} + +#[cfg(ossl102)] +impl<'a> PartialEq<&'a Asn1TimeRef> for Asn1Time { + fn eq(&self, other: &&'a Asn1TimeRef) -> bool { + self.diff(other) + .map(|t| t.days == 0 && t.secs == 0) + .unwrap_or(false) + } +} + +#[cfg(ossl102)] +impl PartialOrd for Asn1Time { + fn partial_cmp(&self, other: &Asn1Time) -> Option<Ordering> { + self.compare(other).ok() + } +} + +#[cfg(ossl102)] +impl PartialOrd<Asn1TimeRef> for Asn1Time { + fn partial_cmp(&self, other: &Asn1TimeRef) -> Option<Ordering> { + self.compare(other).ok() + } +} + +#[cfg(ossl102)] +impl<'a> PartialOrd<&'a Asn1TimeRef> for Asn1Time { + fn partial_cmp(&self, other: &&'a Asn1TimeRef) -> Option<Ordering> { + self.compare(other).ok() + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::ASN1_STRING; + fn drop = ffi::ASN1_STRING_free; + /// Primary ASN.1 type used by OpenSSL + /// + /// Almost all ASN.1 types in OpenSSL are represented by ASN1_STRING + /// structures. This implementation uses [ASN1_STRING-to_UTF8] to preserve + /// compatibility with Rust's String. + /// + /// [ASN1_STRING-to_UTF8]: https://www.openssl.org/docs/manmaster/crypto/ASN1_STRING_to_UTF8.html + pub struct Asn1String; + /// A reference to an [`Asn1String`]. + pub struct Asn1StringRef; +} + +impl Asn1StringRef { + /// Converts the ASN.1 underlying format to UTF8 + /// + /// ASN.1 strings may utilize UTF-16, ASCII, BMP, or UTF8. This is important to + /// consume the string in a meaningful way without knowing the underlying + /// format. + #[corresponds(ASN1_STRING_to_UTF8)] + pub fn as_utf8(&self) -> Result<OpensslString, ErrorStack> { + unsafe { + let mut ptr = ptr::null_mut(); + let len = ffi::ASN1_STRING_to_UTF8(&mut ptr, self.as_ptr()); + if len < 0 { + return Err(ErrorStack::get()); + } + + Ok(OpensslString::from_ptr(ptr as *mut c_char)) + } + } + + /// Return the string as an array of bytes. + /// + /// The bytes do not directly correspond to UTF-8 encoding. To interact with + /// strings in rust, it is preferable to use [`as_utf8`] + /// + /// [`as_utf8`]: struct.Asn1String.html#method.as_utf8 + #[corresponds(ASN1_STRING_get0_data)] + pub fn as_slice(&self) -> &[u8] { + unsafe { slice::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr()), self.len()) } + } + + /// Returns the number of bytes in the string. + #[corresponds(ASN1_STRING_length)] + pub fn len(&self) -> usize { + unsafe { ffi::ASN1_STRING_length(self.as_ptr()) as usize } + } + + /// Determines if the string is empty. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +impl fmt::Debug for Asn1StringRef { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.as_utf8() { + Ok(openssl_string) => openssl_string.fmt(fmt), + Err(_) => fmt.write_str("error"), + } + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::ASN1_INTEGER; + fn drop = ffi::ASN1_INTEGER_free; + + /// Numeric representation + /// + /// Integers in ASN.1 may include BigNum, int64 or uint64. BigNum implementation + /// can be found within [`bn`] module. + /// + /// OpenSSL documentation includes [`ASN1_INTEGER_set`]. + /// + /// [`bn`]: ../bn/index.html + /// [`ASN1_INTEGER_set`]: https://www.openssl.org/docs/manmaster/crypto/ASN1_INTEGER_set.html + pub struct Asn1Integer; + /// A reference to an [`Asn1Integer`]. + pub struct Asn1IntegerRef; +} + +impl Asn1Integer { + /// Converts a bignum to an `Asn1Integer`. + /// + /// Corresponds to [`BN_to_ASN1_INTEGER`]. Also see + /// [`BigNumRef::to_asn1_integer`]. + /// + /// [`BN_to_ASN1_INTEGER`]: https://www.openssl.org/docs/manmaster/crypto/BN_to_ASN1_INTEGER.html + /// [`BigNumRef::to_asn1_integer`]: ../bn/struct.BigNumRef.html#method.to_asn1_integer + pub fn from_bn(bn: &BigNumRef) -> Result<Self, ErrorStack> { + bn.to_asn1_integer() + } +} + +impl Ord for Asn1Integer { + fn cmp(&self, other: &Self) -> Ordering { + Asn1IntegerRef::cmp(self, other) + } +} +impl PartialOrd for Asn1Integer { + fn partial_cmp(&self, other: &Asn1Integer) -> Option<Ordering> { + Some(self.cmp(other)) + } +} +impl Eq for Asn1Integer {} +impl PartialEq for Asn1Integer { + fn eq(&self, other: &Asn1Integer) -> bool { + Asn1IntegerRef::eq(self, other) + } +} + +impl Asn1IntegerRef { + #[allow(missing_docs, clippy::unnecessary_cast)] + #[deprecated(since = "0.10.6", note = "use to_bn instead")] + pub fn get(&self) -> i64 { + unsafe { ffi::ASN1_INTEGER_get(self.as_ptr()) as i64 } + } + + /// Converts the integer to a `BigNum`. + #[corresponds(ASN1_INTEGER_to_BN)] + pub fn to_bn(&self) -> Result<BigNum, ErrorStack> { + unsafe { + cvt_p(ffi::ASN1_INTEGER_to_BN(self.as_ptr(), ptr::null_mut())) + .map(|p| BigNum::from_ptr(p)) + } + } + + /// Sets the ASN.1 value to the value of a signed 32-bit integer, for larger numbers + /// see [`bn`]. + /// + /// [`bn`]: ../bn/struct.BigNumRef.html#method.to_asn1_integer + #[corresponds(ASN1_INTEGER_set)] + pub fn set(&mut self, value: i32) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::ASN1_INTEGER_set(self.as_ptr(), value as c_long)).map(|_| ()) } + } + + /// Creates a new Asn1Integer with the same value. + #[corresponds(ASN1_INTEGER_dup)] + pub fn to_owned(&self) -> Result<Asn1Integer, ErrorStack> { + unsafe { cvt_p(ffi::ASN1_INTEGER_dup(self.as_ptr())).map(|p| Asn1Integer::from_ptr(p)) } + } +} + +impl Ord for Asn1IntegerRef { + fn cmp(&self, other: &Self) -> Ordering { + let res = unsafe { ffi::ASN1_INTEGER_cmp(self.as_ptr(), other.as_ptr()) }; + res.cmp(&0) + } +} +impl PartialOrd for Asn1IntegerRef { + fn partial_cmp(&self, other: &Asn1IntegerRef) -> Option<Ordering> { + Some(self.cmp(other)) + } +} +impl Eq for Asn1IntegerRef {} +impl PartialEq for Asn1IntegerRef { + fn eq(&self, other: &Asn1IntegerRef) -> bool { + self.cmp(other) == Ordering::Equal + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::ASN1_BIT_STRING; + fn drop = ffi::ASN1_BIT_STRING_free; + /// Sequence of bytes + /// + /// Asn1BitString is used in [`x509`] certificates for the signature. + /// The bit string acts as a collection of bytes. + /// + /// [`x509`]: ../x509/struct.X509.html#method.signature + pub struct Asn1BitString; + /// A reference to an [`Asn1BitString`]. + pub struct Asn1BitStringRef; +} + +impl Asn1BitStringRef { + /// Returns the Asn1BitString as a slice. + #[corresponds(ASN1_STRING_get0_data)] + pub fn as_slice(&self) -> &[u8] { + unsafe { slice::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr() as *mut _), self.len()) } + } + + /// Returns the number of bytes in the string. + #[corresponds(ASN1_STRING_length)] + pub fn len(&self) -> usize { + unsafe { ffi::ASN1_STRING_length(self.as_ptr() as *const _) as usize } + } + + /// Determines if the string is empty. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::ASN1_OCTET_STRING; + fn drop = ffi::ASN1_OCTET_STRING_free; + /// ASN.1 OCTET STRING type + pub struct Asn1OctetString; + /// A reference to an [`Asn1OctetString`]. + pub struct Asn1OctetStringRef; +} + +impl Asn1OctetString { + /// Creates an Asn1OctetString from bytes + pub fn new_from_bytes(value: &[u8]) -> Result<Self, ErrorStack> { + ffi::init(); + unsafe { + let s = cvt_p(ffi::ASN1_OCTET_STRING_new())?; + ffi::ASN1_OCTET_STRING_set(s, value.as_ptr(), value.len().try_into().unwrap()); + Ok(Self::from_ptr(s)) + } + } +} + +impl Asn1OctetStringRef { + /// Returns the octet string as an array of bytes. + #[corresponds(ASN1_STRING_get0_data)] + pub fn as_slice(&self) -> &[u8] { + unsafe { slice::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr().cast()), self.len()) } + } + + /// Returns the number of bytes in the octet string. + #[corresponds(ASN1_STRING_length)] + pub fn len(&self) -> usize { + unsafe { ffi::ASN1_STRING_length(self.as_ptr().cast()) as usize } + } + + /// Determines if the string is empty. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::ASN1_OBJECT; + fn drop = ffi::ASN1_OBJECT_free; + fn clone = ffi::OBJ_dup; + + /// Object Identifier + /// + /// Represents an ASN.1 Object. Typically, NIDs, or numeric identifiers + /// are stored as a table within the [`Nid`] module. These constants are + /// used to determine attributes of a certificate, such as mapping the + /// attribute "CommonName" to "CN" which is represented as the OID of 13. + /// This attribute is a constant in the [`nid::COMMONNAME`]. + /// + /// OpenSSL documentation at [`OBJ_nid2obj`] + /// + /// [`Nid`]: ../nid/index.html + /// [`nid::COMMONNAME`]: ../nid/constant.COMMONNAME.html + /// [`OBJ_nid2obj`]: https://www.openssl.org/docs/manmaster/crypto/OBJ_obj2nid.html + pub struct Asn1Object; + /// A reference to an [`Asn1Object`]. + pub struct Asn1ObjectRef; +} + +impl Stackable for Asn1Object { + type StackType = ffi::stack_st_ASN1_OBJECT; +} + +impl Asn1Object { + /// Constructs an ASN.1 Object Identifier from a string representation of the OID. + #[corresponds(OBJ_txt2obj)] + #[allow(clippy::should_implement_trait)] + pub fn from_str(txt: &str) -> Result<Asn1Object, ErrorStack> { + unsafe { + ffi::init(); + let txt = CString::new(txt).unwrap(); + let obj: *mut ffi::ASN1_OBJECT = cvt_p(ffi::OBJ_txt2obj(txt.as_ptr() as *const _, 0))?; + Ok(Asn1Object::from_ptr(obj)) + } + } + + /// Return the OID as an DER encoded array of bytes. This is the ASN.1 + /// value, not including tag or length. + /// + /// Requires OpenSSL 1.1.1 or newer. + #[corresponds(OBJ_get0_data)] + #[cfg(ossl111)] + pub fn as_slice(&self) -> &[u8] { + unsafe { + let len = ffi::OBJ_length(self.as_ptr()); + slice::from_raw_parts(ffi::OBJ_get0_data(self.as_ptr()), len) + } + } +} + +impl Asn1ObjectRef { + /// Returns the NID associated with this OID. + pub fn nid(&self) -> Nid { + unsafe { Nid::from_raw(ffi::OBJ_obj2nid(self.as_ptr())) } + } +} + +impl fmt::Display for Asn1ObjectRef { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + unsafe { + let mut buf = [0; 80]; + let len = ffi::OBJ_obj2txt( + buf.as_mut_ptr() as *mut _, + buf.len() as c_int, + self.as_ptr(), + 0, + ); + match str::from_utf8(&buf[..len as usize]) { + Err(_) => fmt.write_str("error"), + Ok(s) => fmt.write_str(s), + } + } + } +} + +impl fmt::Debug for Asn1ObjectRef { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.write_str(self.to_string().as_str()) + } +} + +cfg_if! { + if #[cfg(any(ossl110, libressl273, boringssl))] { + use ffi::ASN1_STRING_get0_data; + } else { + #[allow(bad_style)] + unsafe fn ASN1_STRING_get0_data(s: *mut ffi::ASN1_STRING) -> *const ::libc::c_uchar { + ffi::ASN1_STRING_data(s) + } + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::ASN1_ENUMERATED; + fn drop = ffi::ASN1_ENUMERATED_free; + + /// An ASN.1 enumerated. + pub struct Asn1Enumerated; + /// A reference to an [`Asn1Enumerated`]. + pub struct Asn1EnumeratedRef; +} + +impl Asn1EnumeratedRef { + /// Get the value, if it fits in the required bounds. + #[corresponds(ASN1_ENUMERATED_get_int64)] + #[cfg(ossl110)] + pub fn get_i64(&self) -> Result<i64, ErrorStack> { + let mut crl_reason = 0; + unsafe { + cvt(ffi::ASN1_ENUMERATED_get_int64( + &mut crl_reason, + self.as_ptr(), + ))?; + } + Ok(crl_reason) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::bn::BigNum; + use crate::nid::Nid; + + /// Tests conversion between BigNum and Asn1Integer. + #[test] + fn bn_cvt() { + fn roundtrip(bn: BigNum) { + let large = Asn1Integer::from_bn(&bn).unwrap(); + assert_eq!(large.to_bn().unwrap(), bn); + } + + roundtrip(BigNum::from_dec_str("1000000000000000000000000000000000").unwrap()); + roundtrip(-BigNum::from_dec_str("1000000000000000000000000000000000").unwrap()); + roundtrip(BigNum::from_u32(1234).unwrap()); + roundtrip(-BigNum::from_u32(1234).unwrap()); + } + + #[test] + fn time_from_str() { + Asn1Time::from_str("99991231235959Z").unwrap(); + #[cfg(ossl111)] + Asn1Time::from_str_x509("99991231235959Z").unwrap(); + } + + #[test] + fn time_from_unix() { + let t = Asn1Time::from_unix(0).unwrap(); + assert_eq!("Jan 1 00:00:00 1970 GMT", t.to_string()); + } + + #[test] + #[cfg(ossl102)] + fn time_eq() { + let a = Asn1Time::from_str("99991231235959Z").unwrap(); + let b = Asn1Time::from_str("99991231235959Z").unwrap(); + let c = Asn1Time::from_str("99991231235958Z").unwrap(); + let a_ref = a.as_ref(); + let b_ref = b.as_ref(); + let c_ref = c.as_ref(); + assert!(a == b); + assert!(a != c); + assert!(a == b_ref); + assert!(a != c_ref); + assert!(b_ref == a); + assert!(c_ref != a); + assert!(a_ref == b_ref); + assert!(a_ref != c_ref); + } + + #[test] + #[cfg(ossl102)] + fn time_ord() { + let a = Asn1Time::from_str("99991231235959Z").unwrap(); + let b = Asn1Time::from_str("99991231235959Z").unwrap(); + let c = Asn1Time::from_str("99991231235958Z").unwrap(); + let a_ref = a.as_ref(); + let b_ref = b.as_ref(); + let c_ref = c.as_ref(); + assert!(a >= b); + assert!(a > c); + assert!(b <= a); + assert!(c < a); + + assert!(a_ref >= b); + assert!(a_ref > c); + assert!(b_ref <= a); + assert!(c_ref < a); + + assert!(a >= b_ref); + assert!(a > c_ref); + assert!(b <= a_ref); + assert!(c < a_ref); + + assert!(a_ref >= b_ref); + assert!(a_ref > c_ref); + assert!(b_ref <= a_ref); + assert!(c_ref < a_ref); + } + + #[test] + fn integer_to_owned() { + let a = Asn1Integer::from_bn(&BigNum::from_dec_str("42").unwrap()).unwrap(); + let b = a.to_owned().unwrap(); + assert_eq!( + a.to_bn().unwrap().to_dec_str().unwrap().to_string(), + b.to_bn().unwrap().to_dec_str().unwrap().to_string(), + ); + assert_ne!(a.as_ptr(), b.as_ptr()); + } + + #[test] + fn integer_cmp() { + let a = Asn1Integer::from_bn(&BigNum::from_dec_str("42").unwrap()).unwrap(); + let b = Asn1Integer::from_bn(&BigNum::from_dec_str("42").unwrap()).unwrap(); + let c = Asn1Integer::from_bn(&BigNum::from_dec_str("43").unwrap()).unwrap(); + assert!(a == b); + assert!(a != c); + assert!(a < c); + assert!(c > b); + } + + #[test] + fn object_from_str() { + let object = Asn1Object::from_str("2.16.840.1.101.3.4.2.1").unwrap(); + assert_eq!(object.nid(), Nid::SHA256); + } + + #[test] + fn object_from_str_with_invalid_input() { + Asn1Object::from_str("NOT AN OID") + .map(|object| object.to_string()) + .expect_err("parsing invalid OID should fail"); + } + + #[test] + #[cfg(ossl111)] + fn object_to_slice() { + let object = Asn1Object::from_str("2.16.840.1.101.3.4.2.1").unwrap(); + assert_eq!( + object.as_slice(), + &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01], + ); + } + + #[test] + fn asn1_octet_string() { + let octet_string = Asn1OctetString::new_from_bytes(b"hello world").unwrap(); + assert_eq!(octet_string.as_slice(), b"hello world"); + assert_eq!(octet_string.len(), 11); + } +} diff --git a/vendor/openssl/src/base64.rs b/vendor/openssl/src/base64.rs new file mode 100644 index 0000000..bfa8cbc --- /dev/null +++ b/vendor/openssl/src/base64.rs @@ -0,0 +1,128 @@ +//! Base64 encoding support. +use crate::error::ErrorStack; +use crate::{cvt_n, LenType}; +use libc::c_int; +use openssl_macros::corresponds; + +/// Encodes a slice of bytes to a base64 string. +/// +/// # Panics +/// +/// Panics if the input length or computed output length overflow a signed C integer. +#[corresponds(EVP_EncodeBlock)] +pub fn encode_block(src: &[u8]) -> String { + assert!(src.len() <= c_int::max_value() as usize); + let src_len = src.len() as LenType; + + let len = encoded_len(src_len).unwrap(); + let mut out = Vec::with_capacity(len as usize); + + // SAFETY: `encoded_len` ensures space for 4 output characters + // for every 3 input bytes including padding and nul terminator. + // `EVP_EncodeBlock` will write only single byte ASCII characters. + // `EVP_EncodeBlock` will only write to not read from `out`. + unsafe { + let out_len = ffi::EVP_EncodeBlock(out.as_mut_ptr(), src.as_ptr(), src_len); + out.set_len(out_len as usize); + String::from_utf8_unchecked(out) + } +} + +/// Decodes a base64-encoded string to bytes. +/// +/// # Panics +/// +/// Panics if the input length or computed output length overflow a signed C integer. +#[corresponds(EVP_DecodeBlock)] +pub fn decode_block(src: &str) -> Result<Vec<u8>, ErrorStack> { + let src = src.trim(); + + // https://github.com/openssl/openssl/issues/12143 + if src.is_empty() { + return Ok(vec![]); + } + + assert!(src.len() <= c_int::max_value() as usize); + let src_len = src.len() as LenType; + + let len = decoded_len(src_len).unwrap(); + let mut out = Vec::with_capacity(len as usize); + + // SAFETY: `decoded_len` ensures space for 3 output bytes + // for every 4 input characters including padding. + // `EVP_DecodeBlock` can write fewer bytes after stripping + // leading and trailing whitespace, but never more. + // `EVP_DecodeBlock` will only write to not read from `out`. + unsafe { + let out_len = cvt_n(ffi::EVP_DecodeBlock( + out.as_mut_ptr(), + src.as_ptr(), + src_len, + ))?; + out.set_len(out_len as usize); + } + + if src.ends_with('=') { + out.pop(); + if src.ends_with("==") { + out.pop(); + } + } + + Ok(out) +} + +fn encoded_len(src_len: LenType) -> Option<LenType> { + let mut len = (src_len / 3).checked_mul(4)?; + + if src_len % 3 != 0 { + len = len.checked_add(4)?; + } + + len = len.checked_add(1)?; + + Some(len) +} + +fn decoded_len(src_len: LenType) -> Option<LenType> { + let mut len = (src_len / 4).checked_mul(3)?; + + if src_len % 4 != 0 { + len = len.checked_add(3)?; + } + + Some(len) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_encode_block() { + assert_eq!("".to_string(), encode_block(b"")); + assert_eq!("Zg==".to_string(), encode_block(b"f")); + assert_eq!("Zm8=".to_string(), encode_block(b"fo")); + assert_eq!("Zm9v".to_string(), encode_block(b"foo")); + assert_eq!("Zm9vYg==".to_string(), encode_block(b"foob")); + assert_eq!("Zm9vYmE=".to_string(), encode_block(b"fooba")); + assert_eq!("Zm9vYmFy".to_string(), encode_block(b"foobar")); + } + + #[test] + fn test_decode_block() { + assert_eq!(b"".to_vec(), decode_block("").unwrap()); + assert_eq!(b"f".to_vec(), decode_block("Zg==").unwrap()); + assert_eq!(b"fo".to_vec(), decode_block("Zm8=").unwrap()); + assert_eq!(b"foo".to_vec(), decode_block("Zm9v").unwrap()); + assert_eq!(b"foob".to_vec(), decode_block("Zm9vYg==").unwrap()); + assert_eq!(b"fooba".to_vec(), decode_block("Zm9vYmE=").unwrap()); + assert_eq!(b"foobar".to_vec(), decode_block("Zm9vYmFy").unwrap()); + } + + #[test] + fn test_strip_whitespace() { + assert_eq!(b"foobar".to_vec(), decode_block(" Zm9vYmFy\n").unwrap()); + assert_eq!(b"foob".to_vec(), decode_block(" Zm9vYg==\n").unwrap()); + } +} diff --git a/vendor/openssl/src/bio.rs b/vendor/openssl/src/bio.rs new file mode 100644 index 0000000..0f54935 --- /dev/null +++ b/vendor/openssl/src/bio.rs @@ -0,0 +1,85 @@ +use cfg_if::cfg_if; +use libc::c_int; +use std::marker::PhantomData; +use std::ptr; +use std::slice; + +use crate::cvt_p; +use crate::error::ErrorStack; + +pub struct MemBioSlice<'a>(*mut ffi::BIO, PhantomData<&'a [u8]>); + +impl<'a> Drop for MemBioSlice<'a> { + fn drop(&mut self) { + unsafe { + ffi::BIO_free_all(self.0); + } + } +} + +impl<'a> MemBioSlice<'a> { + pub fn new(buf: &'a [u8]) -> Result<MemBioSlice<'a>, ErrorStack> { + ffi::init(); + + assert!(buf.len() <= c_int::max_value() as usize); + let bio = unsafe { + cvt_p(BIO_new_mem_buf( + buf.as_ptr() as *const _, + buf.len() as crate::SLenType, + ))? + }; + + Ok(MemBioSlice(bio, PhantomData)) + } + + pub fn as_ptr(&self) -> *mut ffi::BIO { + self.0 + } +} + +pub struct MemBio(*mut ffi::BIO); + +impl Drop for MemBio { + fn drop(&mut self) { + unsafe { + ffi::BIO_free_all(self.0); + } + } +} + +impl MemBio { + pub fn new() -> Result<MemBio, ErrorStack> { + ffi::init(); + + let bio = unsafe { cvt_p(ffi::BIO_new(ffi::BIO_s_mem()))? }; + Ok(MemBio(bio)) + } + + pub fn as_ptr(&self) -> *mut ffi::BIO { + self.0 + } + + pub fn get_buf(&self) -> &[u8] { + unsafe { + let mut ptr = ptr::null_mut(); + let len = ffi::BIO_get_mem_data(self.0, &mut ptr); + slice::from_raw_parts(ptr as *const _ as *const _, len as usize) + } + } + + #[cfg(not(boringssl))] + pub unsafe fn from_ptr(bio: *mut ffi::BIO) -> MemBio { + MemBio(bio) + } +} + +cfg_if! { + if #[cfg(any(ossl102, boringssl))] { + use ffi::BIO_new_mem_buf; + } else { + #[allow(bad_style)] + unsafe fn BIO_new_mem_buf(buf: *const ::libc::c_void, len: ::libc::c_int) -> *mut ffi::BIO { + ffi::BIO_new_mem_buf(buf as *mut _, len) + } + } +} diff --git a/vendor/openssl/src/bn.rs b/vendor/openssl/src/bn.rs new file mode 100644 index 0000000..c75fac1 --- /dev/null +++ b/vendor/openssl/src/bn.rs @@ -0,0 +1,1518 @@ +//! BigNum implementation +//! +//! Large numbers are important for a cryptographic library. OpenSSL implementation +//! of BigNum uses dynamically assigned memory to store an array of bit chunks. This +//! allows numbers of any size to be compared and mathematical functions performed. +//! +//! OpenSSL wiki describes the [`BIGNUM`] data structure. +//! +//! # Examples +//! +//! ``` +//! use openssl::bn::BigNum; +//! use openssl::error::ErrorStack; +//! +//! fn main() -> Result<(), ErrorStack> { +//! let a = BigNum::new()?; // a = 0 +//! let b = BigNum::from_dec_str("1234567890123456789012345")?; +//! let c = &a * &b; +//! assert_eq!(a, c); +//! Ok(()) +//! } +//! ``` +//! +//! [`BIGNUM`]: https://wiki.openssl.org/index.php/Manual:Bn_internal(3) +use cfg_if::cfg_if; +use foreign_types::{ForeignType, ForeignTypeRef}; +use libc::c_int; +use std::cmp::Ordering; +use std::ffi::CString; +use std::ops::{Add, Deref, Div, Mul, Neg, Rem, Shl, Shr, Sub}; +use std::{fmt, ptr}; + +use crate::asn1::Asn1Integer; +use crate::error::ErrorStack; +use crate::string::OpensslString; +use crate::{cvt, cvt_n, cvt_p, LenType}; +use openssl_macros::corresponds; + +cfg_if! { + if #[cfg(any(ossl110, libressl350))] { + use ffi::{ + BN_get_rfc2409_prime_1024, BN_get_rfc2409_prime_768, BN_get_rfc3526_prime_1536, + BN_get_rfc3526_prime_2048, BN_get_rfc3526_prime_3072, BN_get_rfc3526_prime_4096, + BN_get_rfc3526_prime_6144, BN_get_rfc3526_prime_8192, BN_is_negative, + }; + } else if #[cfg(boringssl)] { + use ffi::BN_is_negative; + } else { + use ffi::{ + get_rfc2409_prime_1024 as BN_get_rfc2409_prime_1024, + get_rfc2409_prime_768 as BN_get_rfc2409_prime_768, + get_rfc3526_prime_1536 as BN_get_rfc3526_prime_1536, + get_rfc3526_prime_2048 as BN_get_rfc3526_prime_2048, + get_rfc3526_prime_3072 as BN_get_rfc3526_prime_3072, + get_rfc3526_prime_4096 as BN_get_rfc3526_prime_4096, + get_rfc3526_prime_6144 as BN_get_rfc3526_prime_6144, + get_rfc3526_prime_8192 as BN_get_rfc3526_prime_8192, + }; + + #[allow(bad_style)] + unsafe fn BN_is_negative(bn: *const ffi::BIGNUM) -> c_int { + (*bn).neg + } + } +} + +/// Options for the most significant bits of a randomly generated `BigNum`. +pub struct MsbOption(c_int); + +impl MsbOption { + /// The most significant bit of the number may be 0. + pub const MAYBE_ZERO: MsbOption = MsbOption(-1); + + /// The most significant bit of the number must be 1. + pub const ONE: MsbOption = MsbOption(0); + + /// The most significant two bits of the number must be 1. + /// + /// The number of bits in the product of two such numbers will always be exactly twice the + /// number of bits in the original numbers. + pub const TWO_ONES: MsbOption = MsbOption(1); +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::BN_CTX; + fn drop = ffi::BN_CTX_free; + + /// Temporary storage for BigNums on the secure heap + /// + /// BigNum values are stored dynamically and therefore can be expensive + /// to allocate. BigNumContext and the OpenSSL [`BN_CTX`] structure are used + /// internally when passing BigNum values between subroutines. + /// + /// [`BN_CTX`]: https://www.openssl.org/docs/manmaster/crypto/BN_CTX_new.html + pub struct BigNumContext; + /// Reference to [`BigNumContext`] + /// + /// [`BigNumContext`]: struct.BigNumContext.html + pub struct BigNumContextRef; +} + +impl BigNumContext { + /// Returns a new `BigNumContext`. + #[corresponds(BN_CTX_new)] + pub fn new() -> Result<BigNumContext, ErrorStack> { + unsafe { + ffi::init(); + cvt_p(ffi::BN_CTX_new()).map(BigNumContext) + } + } + + /// Returns a new secure `BigNumContext`. + #[corresponds(BN_CTX_secure_new)] + #[cfg(ossl110)] + pub fn new_secure() -> Result<BigNumContext, ErrorStack> { + unsafe { + ffi::init(); + cvt_p(ffi::BN_CTX_secure_new()).map(BigNumContext) + } + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::BIGNUM; + fn drop = ffi::BN_free; + + /// Dynamically sized large number implementation + /// + /// Perform large number mathematics. Create a new BigNum + /// with [`new`]. Perform standard mathematics on large numbers using + /// methods from [`Dref<Target = BigNumRef>`] + /// + /// OpenSSL documentation at [`BN_new`]. + /// + /// [`new`]: struct.BigNum.html#method.new + /// [`Dref<Target = BigNumRef>`]: struct.BigNum.html#deref-methods + /// [`BN_new`]: https://www.openssl.org/docs/manmaster/crypto/BN_new.html + /// + /// # Examples + /// ``` + /// use openssl::bn::BigNum; + /// # use openssl::error::ErrorStack; + /// # fn bignums() -> Result< (), ErrorStack > { + /// let little_big = BigNum::from_u32(std::u32::MAX)?; + /// assert_eq!(*&little_big.num_bytes(), 4); + /// # Ok(()) + /// # } + /// # fn main () { bignums(); } + /// ``` + pub struct BigNum; + /// Reference to a [`BigNum`] + /// + /// [`BigNum`]: struct.BigNum.html + pub struct BigNumRef; +} + +impl BigNumRef { + /// Erases the memory used by this `BigNum`, resetting its value to 0. + /// + /// This can be used to destroy sensitive data such as keys when they are no longer needed. + #[corresponds(BN_clear)] + pub fn clear(&mut self) { + unsafe { ffi::BN_clear(self.as_ptr()) } + } + + /// Adds a `u32` to `self`. + #[corresponds(BN_add_word)] + pub fn add_word(&mut self, w: u32) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_add_word(self.as_ptr(), w as ffi::BN_ULONG)).map(|_| ()) } + } + + /// Subtracts a `u32` from `self`. + #[corresponds(BN_sub_word)] + pub fn sub_word(&mut self, w: u32) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_sub_word(self.as_ptr(), w as ffi::BN_ULONG)).map(|_| ()) } + } + + /// Multiplies a `u32` by `self`. + #[corresponds(BN_mul_word)] + pub fn mul_word(&mut self, w: u32) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_mul_word(self.as_ptr(), w as ffi::BN_ULONG)).map(|_| ()) } + } + + /// Divides `self` by a `u32`, returning the remainder. + #[corresponds(BN_div_word)] + #[allow(clippy::useless_conversion)] + pub fn div_word(&mut self, w: u32) -> Result<u64, ErrorStack> { + unsafe { + let r = ffi::BN_div_word(self.as_ptr(), w.into()); + if r == ffi::BN_ULONG::max_value() { + Err(ErrorStack::get()) + } else { + Ok(r.into()) + } + } + } + + /// Returns the result of `self` modulo `w`. + #[corresponds(BN_mod_word)] + #[allow(clippy::useless_conversion)] + pub fn mod_word(&self, w: u32) -> Result<u64, ErrorStack> { + unsafe { + let r = ffi::BN_mod_word(self.as_ptr(), w.into()); + if r == ffi::BN_ULONG::max_value() { + Err(ErrorStack::get()) + } else { + Ok(r.into()) + } + } + } + + /// Places a cryptographically-secure pseudo-random nonnegative + /// number less than `self` in `rnd`. + #[corresponds(BN_rand_range)] + pub fn rand_range(&self, rnd: &mut BigNumRef) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_rand_range(rnd.as_ptr(), self.as_ptr())).map(|_| ()) } + } + + /// The cryptographically weak counterpart to `rand_in_range`. + #[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] + #[corresponds(BN_pseudo_rand_range)] + pub fn pseudo_rand_range(&self, rnd: &mut BigNumRef) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_pseudo_rand_range(rnd.as_ptr(), self.as_ptr())).map(|_| ()) } + } + + /// Sets bit `n`. Equivalent to `self |= (1 << n)`. + /// + /// When setting a bit outside of `self`, it is expanded. + #[corresponds(BN_set_bit)] + #[allow(clippy::useless_conversion)] + pub fn set_bit(&mut self, n: i32) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_set_bit(self.as_ptr(), n.into())).map(|_| ()) } + } + + /// Clears bit `n`, setting it to 0. Equivalent to `self &= ~(1 << n)`. + /// + /// When clearing a bit outside of `self`, an error is returned. + #[corresponds(BN_clear_bit)] + #[allow(clippy::useless_conversion)] + pub fn clear_bit(&mut self, n: i32) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_clear_bit(self.as_ptr(), n.into())).map(|_| ()) } + } + + /// Returns `true` if the `n`th bit of `self` is set to 1, `false` otherwise. + #[corresponds(BN_is_bit_set)] + #[allow(clippy::useless_conversion)] + pub fn is_bit_set(&self, n: i32) -> bool { + unsafe { ffi::BN_is_bit_set(self.as_ptr(), n.into()) == 1 } + } + + /// Truncates `self` to the lowest `n` bits. + /// + /// An error occurs if `self` is already shorter than `n` bits. + #[corresponds(BN_mask_bits)] + #[allow(clippy::useless_conversion)] + pub fn mask_bits(&mut self, n: i32) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_mask_bits(self.as_ptr(), n.into())).map(|_| ()) } + } + + /// Places `a << 1` in `self`. Equivalent to `self * 2`. + #[corresponds(BN_lshift1)] + pub fn lshift1(&mut self, a: &BigNumRef) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_lshift1(self.as_ptr(), a.as_ptr())).map(|_| ()) } + } + + /// Places `a >> 1` in `self`. Equivalent to `self / 2`. + #[corresponds(BN_rshift1)] + pub fn rshift1(&mut self, a: &BigNumRef) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_rshift1(self.as_ptr(), a.as_ptr())).map(|_| ()) } + } + + /// Places `a + b` in `self`. [`core::ops::Add`] is also implemented for `BigNumRef`. + /// + /// [`core::ops::Add`]: struct.BigNumRef.html#method.add + #[corresponds(BN_add)] + pub fn checked_add(&mut self, a: &BigNumRef, b: &BigNumRef) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_add(self.as_ptr(), a.as_ptr(), b.as_ptr())).map(|_| ()) } + } + + /// Places `a - b` in `self`. [`core::ops::Sub`] is also implemented for `BigNumRef`. + /// + /// [`core::ops::Sub`]: struct.BigNumRef.html#method.sub + #[corresponds(BN_sub)] + pub fn checked_sub(&mut self, a: &BigNumRef, b: &BigNumRef) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_sub(self.as_ptr(), a.as_ptr(), b.as_ptr())).map(|_| ()) } + } + + /// Places `a << n` in `self`. Equivalent to `a * 2 ^ n`. + #[corresponds(BN_lshift)] + #[allow(clippy::useless_conversion)] + pub fn lshift(&mut self, a: &BigNumRef, n: i32) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_lshift(self.as_ptr(), a.as_ptr(), n.into())).map(|_| ()) } + } + + /// Places `a >> n` in `self`. Equivalent to `a / 2 ^ n`. + #[corresponds(BN_rshift)] + #[allow(clippy::useless_conversion)] + pub fn rshift(&mut self, a: &BigNumRef, n: i32) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_rshift(self.as_ptr(), a.as_ptr(), n.into())).map(|_| ()) } + } + + /// Creates a new BigNum with the same value. + #[corresponds(BN_dup)] + pub fn to_owned(&self) -> Result<BigNum, ErrorStack> { + unsafe { cvt_p(ffi::BN_dup(self.as_ptr())).map(|b| BigNum::from_ptr(b)) } + } + + /// Sets the sign of `self`. Pass true to set `self` to a negative. False sets + /// `self` positive. + #[corresponds(BN_set_negative)] + pub fn set_negative(&mut self, negative: bool) { + unsafe { ffi::BN_set_negative(self.as_ptr(), negative as c_int) } + } + + /// Compare the absolute values of `self` and `oth`. + /// + /// # Examples + /// + /// ``` + /// # use openssl::bn::BigNum; + /// # use std::cmp::Ordering; + /// let s = -BigNum::from_u32(8).unwrap(); + /// let o = BigNum::from_u32(8).unwrap(); + /// + /// assert_eq!(s.ucmp(&o), Ordering::Equal); + /// ``` + #[corresponds(BN_ucmp)] + pub fn ucmp(&self, oth: &BigNumRef) -> Ordering { + unsafe { ffi::BN_ucmp(self.as_ptr(), oth.as_ptr()).cmp(&0) } + } + + /// Returns `true` if `self` is negative. + #[corresponds(BN_is_negative)] + pub fn is_negative(&self) -> bool { + unsafe { BN_is_negative(self.as_ptr()) == 1 } + } + + /// Returns `true` is `self` is even. + #[corresponds(BN_is_even)] + #[cfg(any(ossl110, boringssl, libressl350))] + pub fn is_even(&self) -> bool { + !self.is_odd() + } + + /// Returns `true` is `self` is odd. + #[corresponds(BN_is_odd)] + #[cfg(any(ossl110, boringssl, libressl350))] + pub fn is_odd(&self) -> bool { + unsafe { ffi::BN_is_odd(self.as_ptr()) == 1 } + } + + /// Returns the number of significant bits in `self`. + #[corresponds(BN_num_bits)] + #[allow(clippy::unnecessary_cast)] + pub fn num_bits(&self) -> i32 { + unsafe { ffi::BN_num_bits(self.as_ptr()) as i32 } + } + + /// Returns the size of `self` in bytes. Implemented natively. + pub fn num_bytes(&self) -> i32 { + (self.num_bits() + 7) / 8 + } + + /// Generates a cryptographically strong pseudo-random `BigNum`, placing it in `self`. + /// + /// # Parameters + /// + /// * `bits`: Length of the number in bits. + /// * `msb`: The desired properties of the most significant bit. See [`constants`]. + /// * `odd`: If `true`, the generated number will be odd. + /// + /// # Examples + /// + /// ``` + /// use openssl::bn::{BigNum, MsbOption}; + /// use openssl::error::ErrorStack; + /// + /// fn generate_random() -> Result< BigNum, ErrorStack > { + /// let mut big = BigNum::new()?; + /// + /// // Generates a 128-bit odd random number + /// big.rand(128, MsbOption::MAYBE_ZERO, true); + /// Ok((big)) + /// } + /// ``` + /// + /// [`constants`]: index.html#constants + #[corresponds(BN_rand)] + #[allow(clippy::useless_conversion)] + pub fn rand(&mut self, bits: i32, msb: MsbOption, odd: bool) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_rand( + self.as_ptr(), + bits.into(), + msb.0, + odd as c_int, + )) + .map(|_| ()) + } + } + + /// The cryptographically weak counterpart to `rand`. Not suitable for key generation. + #[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] + #[corresponds(BN_pseudo_rand)] + #[allow(clippy::useless_conversion)] + pub fn pseudo_rand(&mut self, bits: i32, msb: MsbOption, odd: bool) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_pseudo_rand( + self.as_ptr(), + bits.into(), + msb.0, + odd as c_int, + )) + .map(|_| ()) + } + } + + /// Generates a prime number, placing it in `self`. + /// + /// # Parameters + /// + /// * `bits`: The length of the prime in bits (lower bound). + /// * `safe`: If true, returns a "safe" prime `p` so that `(p-1)/2` is also prime. + /// * `add`/`rem`: If `add` is set to `Some(add)`, `p % add == rem` will hold, where `p` is the + /// generated prime and `rem` is `1` if not specified (`None`). + /// + /// # Examples + /// + /// ``` + /// use openssl::bn::BigNum; + /// use openssl::error::ErrorStack; + /// + /// fn generate_weak_prime() -> Result< BigNum, ErrorStack > { + /// let mut big = BigNum::new()?; + /// + /// // Generates a 128-bit simple prime number + /// big.generate_prime(128, false, None, None); + /// Ok((big)) + /// } + /// ``` + #[corresponds(BN_generate_prime_ex)] + pub fn generate_prime( + &mut self, + bits: i32, + safe: bool, + add: Option<&BigNumRef>, + rem: Option<&BigNumRef>, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_generate_prime_ex( + self.as_ptr(), + bits as c_int, + safe as c_int, + add.map(|n| n.as_ptr()).unwrap_or(ptr::null_mut()), + rem.map(|n| n.as_ptr()).unwrap_or(ptr::null_mut()), + ptr::null_mut(), + )) + .map(|_| ()) + } + } + + /// Places the result of `a * b` in `self`. + /// [`core::ops::Mul`] is also implemented for `BigNumRef`. + /// + /// [`core::ops::Mul`]: struct.BigNumRef.html#method.mul + #[corresponds(BN_mul)] + pub fn checked_mul( + &mut self, + a: &BigNumRef, + b: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_mul( + self.as_ptr(), + a.as_ptr(), + b.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places the result of `a / b` in `self`. The remainder is discarded. + /// [`core::ops::Div`] is also implemented for `BigNumRef`. + /// + /// [`core::ops::Div`]: struct.BigNumRef.html#method.div + #[corresponds(BN_div)] + pub fn checked_div( + &mut self, + a: &BigNumRef, + b: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_div( + self.as_ptr(), + ptr::null_mut(), + a.as_ptr(), + b.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places the result of `a % b` in `self`. + #[corresponds(BN_div)] + pub fn checked_rem( + &mut self, + a: &BigNumRef, + b: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_div( + ptr::null_mut(), + self.as_ptr(), + a.as_ptr(), + b.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places the result of `a / b` in `self` and `a % b` in `rem`. + #[corresponds(BN_div)] + pub fn div_rem( + &mut self, + rem: &mut BigNumRef, + a: &BigNumRef, + b: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_div( + self.as_ptr(), + rem.as_ptr(), + a.as_ptr(), + b.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places the result of `a²` in `self`. + #[corresponds(BN_sqr)] + pub fn sqr(&mut self, a: &BigNumRef, ctx: &mut BigNumContextRef) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_sqr(self.as_ptr(), a.as_ptr(), ctx.as_ptr())).map(|_| ()) } + } + + /// Places the result of `a mod m` in `self`. As opposed to `div_rem` + /// the result is non-negative. + #[corresponds(BN_nnmod)] + pub fn nnmod( + &mut self, + a: &BigNumRef, + m: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_nnmod( + self.as_ptr(), + a.as_ptr(), + m.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places the result of `(a + b) mod m` in `self`. + #[corresponds(BN_mod_add)] + pub fn mod_add( + &mut self, + a: &BigNumRef, + b: &BigNumRef, + m: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_mod_add( + self.as_ptr(), + a.as_ptr(), + b.as_ptr(), + m.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places the result of `(a - b) mod m` in `self`. + #[corresponds(BN_mod_sub)] + pub fn mod_sub( + &mut self, + a: &BigNumRef, + b: &BigNumRef, + m: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_mod_sub( + self.as_ptr(), + a.as_ptr(), + b.as_ptr(), + m.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places the result of `(a * b) mod m` in `self`. + #[corresponds(BN_mod_mul)] + pub fn mod_mul( + &mut self, + a: &BigNumRef, + b: &BigNumRef, + m: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_mod_mul( + self.as_ptr(), + a.as_ptr(), + b.as_ptr(), + m.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places the result of `a² mod m` in `self`. + #[corresponds(BN_mod_sqr)] + pub fn mod_sqr( + &mut self, + a: &BigNumRef, + m: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_mod_sqr( + self.as_ptr(), + a.as_ptr(), + m.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places into `self` the modular square root of `a` such that `self^2 = a (mod p)` + #[corresponds(BN_mod_sqrt)] + #[cfg(ossl110)] + pub fn mod_sqrt( + &mut self, + a: &BigNumRef, + p: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt_p(ffi::BN_mod_sqrt( + self.as_ptr(), + a.as_ptr(), + p.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places the result of `a^p` in `self`. + #[corresponds(BN_exp)] + pub fn exp( + &mut self, + a: &BigNumRef, + p: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_exp( + self.as_ptr(), + a.as_ptr(), + p.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places the result of `a^p mod m` in `self`. + #[corresponds(BN_mod_exp)] + pub fn mod_exp( + &mut self, + a: &BigNumRef, + p: &BigNumRef, + m: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_mod_exp( + self.as_ptr(), + a.as_ptr(), + p.as_ptr(), + m.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places the inverse of `a` modulo `n` in `self`. + #[corresponds(BN_mod_inverse)] + pub fn mod_inverse( + &mut self, + a: &BigNumRef, + n: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt_p(ffi::BN_mod_inverse( + self.as_ptr(), + a.as_ptr(), + n.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places the greatest common denominator of `a` and `b` in `self`. + #[corresponds(BN_gcd)] + pub fn gcd( + &mut self, + a: &BigNumRef, + b: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_gcd( + self.as_ptr(), + a.as_ptr(), + b.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Checks whether `self` is prime. + /// + /// Performs a Miller-Rabin probabilistic primality test with `checks` iterations. + /// + /// # Return Value + /// + /// Returns `true` if `self` is prime with an error probability of less than `0.25 ^ checks`. + #[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] + #[corresponds(BN_is_prime_ex)] + #[allow(clippy::useless_conversion)] + pub fn is_prime(&self, checks: i32, ctx: &mut BigNumContextRef) -> Result<bool, ErrorStack> { + unsafe { + cvt_n(ffi::BN_is_prime_ex( + self.as_ptr(), + checks.into(), + ctx.as_ptr(), + ptr::null_mut(), + )) + .map(|r| r != 0) + } + } + + /// Checks whether `self` is prime with optional trial division. + /// + /// If `do_trial_division` is `true`, first performs trial division by a number of small primes. + /// Then, like `is_prime`, performs a Miller-Rabin probabilistic primality test with `checks` + /// iterations. + /// + /// # Return Value + /// + /// Returns `true` if `self` is prime with an error probability of less than `0.25 ^ checks`. + #[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] + #[corresponds(BN_is_prime_fasttest_ex)] + #[allow(clippy::useless_conversion)] + pub fn is_prime_fasttest( + &self, + checks: i32, + ctx: &mut BigNumContextRef, + do_trial_division: bool, + ) -> Result<bool, ErrorStack> { + unsafe { + cvt_n(ffi::BN_is_prime_fasttest_ex( + self.as_ptr(), + checks.into(), + ctx.as_ptr(), + do_trial_division as c_int, + ptr::null_mut(), + )) + .map(|r| r != 0) + } + } + + /// Returns a big-endian byte vector representation of the absolute value of `self`. + /// + /// `self` can be recreated by using `from_slice`. + /// + /// ``` + /// # use openssl::bn::BigNum; + /// let s = -BigNum::from_u32(4543).unwrap(); + /// let r = BigNum::from_u32(4543).unwrap(); + /// + /// let s_vec = s.to_vec(); + /// assert_eq!(BigNum::from_slice(&s_vec).unwrap(), r); + /// ``` + #[corresponds(BN_bn2bin)] + pub fn to_vec(&self) -> Vec<u8> { + let size = self.num_bytes() as usize; + let mut v = Vec::with_capacity(size); + unsafe { + ffi::BN_bn2bin(self.as_ptr(), v.as_mut_ptr()); + v.set_len(size); + } + v + } + + /// Returns a big-endian byte vector representation of the absolute value of `self` padded + /// to `pad_to` bytes. + /// + /// If `pad_to` is less than `self.num_bytes()` then an error is returned. + /// + /// `self` can be recreated by using `from_slice`. + /// + /// ``` + /// # use openssl::bn::BigNum; + /// let bn = BigNum::from_u32(0x4543).unwrap(); + /// + /// let bn_vec = bn.to_vec_padded(4).unwrap(); + /// assert_eq!(&bn_vec, &[0, 0, 0x45, 0x43]); + /// + /// let r = bn.to_vec_padded(1); + /// assert!(r.is_err()); + /// + /// let bn = -BigNum::from_u32(0x4543).unwrap(); + /// let bn_vec = bn.to_vec_padded(4).unwrap(); + /// assert_eq!(&bn_vec, &[0, 0, 0x45, 0x43]); + /// ``` + #[corresponds(BN_bn2binpad)] + #[cfg(any(ossl110, libressl340, boringssl))] + pub fn to_vec_padded(&self, pad_to: i32) -> Result<Vec<u8>, ErrorStack> { + let mut v = Vec::with_capacity(pad_to as usize); + unsafe { + cvt(ffi::BN_bn2binpad(self.as_ptr(), v.as_mut_ptr(), pad_to))?; + v.set_len(pad_to as usize); + } + Ok(v) + } + + /// Returns a decimal string representation of `self`. + /// + /// ``` + /// # use openssl::bn::BigNum; + /// let s = -BigNum::from_u32(12345).unwrap(); + /// + /// assert_eq!(&**s.to_dec_str().unwrap(), "-12345"); + /// ``` + #[corresponds(BN_bn2dec)] + pub fn to_dec_str(&self) -> Result<OpensslString, ErrorStack> { + unsafe { + let buf = cvt_p(ffi::BN_bn2dec(self.as_ptr()))?; + Ok(OpensslString::from_ptr(buf)) + } + } + + /// Returns a hexadecimal string representation of `self`. + /// + /// ``` + /// # use openssl::bn::BigNum; + /// let s = -BigNum::from_u32(0x99ff).unwrap(); + /// + /// assert_eq!(s.to_hex_str().unwrap().to_uppercase(), "-99FF"); + /// ``` + #[corresponds(BN_bn2hex)] + pub fn to_hex_str(&self) -> Result<OpensslString, ErrorStack> { + unsafe { + let buf = cvt_p(ffi::BN_bn2hex(self.as_ptr()))?; + Ok(OpensslString::from_ptr(buf)) + } + } + + /// Returns an `Asn1Integer` containing the value of `self`. + #[corresponds(BN_to_ASN1_INTEGER)] + pub fn to_asn1_integer(&self) -> Result<Asn1Integer, ErrorStack> { + unsafe { + cvt_p(ffi::BN_to_ASN1_INTEGER(self.as_ptr(), ptr::null_mut())) + .map(|p| Asn1Integer::from_ptr(p)) + } + } + + /// Force constant time computation on this value. + #[corresponds(BN_set_flags)] + #[cfg(ossl110)] + pub fn set_const_time(&mut self) { + unsafe { ffi::BN_set_flags(self.as_ptr(), ffi::BN_FLG_CONSTTIME) } + } + + /// Returns true if `self` is in const time mode. + #[corresponds(BN_get_flags)] + #[cfg(ossl110)] + pub fn is_const_time(&self) -> bool { + unsafe { + let ret = ffi::BN_get_flags(self.as_ptr(), ffi::BN_FLG_CONSTTIME); + ret == ffi::BN_FLG_CONSTTIME + } + } + + /// Returns true if `self` was created with [`BigNum::new_secure`]. + #[corresponds(BN_get_flags)] + #[cfg(ossl110)] + pub fn is_secure(&self) -> bool { + unsafe { + let ret = ffi::BN_get_flags(self.as_ptr(), ffi::BN_FLG_SECURE); + ret == ffi::BN_FLG_SECURE + } + } +} + +impl BigNum { + /// Creates a new `BigNum` with the value 0. + #[corresponds(BN_new)] + pub fn new() -> Result<BigNum, ErrorStack> { + unsafe { + ffi::init(); + let v = cvt_p(ffi::BN_new())?; + Ok(BigNum::from_ptr(v)) + } + } + + /// Returns a new secure `BigNum`. + #[corresponds(BN_secure_new)] + #[cfg(ossl110)] + pub fn new_secure() -> Result<BigNum, ErrorStack> { + unsafe { + ffi::init(); + let v = cvt_p(ffi::BN_secure_new())?; + Ok(BigNum::from_ptr(v)) + } + } + + /// Creates a new `BigNum` with the given value. + #[corresponds(BN_set_word)] + pub fn from_u32(n: u32) -> Result<BigNum, ErrorStack> { + BigNum::new().and_then(|v| unsafe { + cvt(ffi::BN_set_word(v.as_ptr(), n as ffi::BN_ULONG)).map(|_| v) + }) + } + + /// Creates a `BigNum` from a decimal string. + #[corresponds(BN_dec2bn)] + pub fn from_dec_str(s: &str) -> Result<BigNum, ErrorStack> { + unsafe { + ffi::init(); + let c_str = CString::new(s.as_bytes()).unwrap(); + let mut bn = ptr::null_mut(); + cvt(ffi::BN_dec2bn(&mut bn, c_str.as_ptr() as *const _))?; + Ok(BigNum::from_ptr(bn)) + } + } + + /// Creates a `BigNum` from a hexadecimal string. + #[corresponds(BN_hex2bn)] + pub fn from_hex_str(s: &str) -> Result<BigNum, ErrorStack> { + unsafe { + ffi::init(); + let c_str = CString::new(s.as_bytes()).unwrap(); + let mut bn = ptr::null_mut(); + cvt(ffi::BN_hex2bn(&mut bn, c_str.as_ptr() as *const _))?; + Ok(BigNum::from_ptr(bn)) + } + } + + /// Returns a constant used in IKE as defined in [`RFC 2409`]. This prime number is in + /// the order of magnitude of `2 ^ 768`. This number is used during calculated key + /// exchanges such as Diffie-Hellman. This number is labeled Oakley group id 1. + /// + /// [`RFC 2409`]: https://tools.ietf.org/html/rfc2409#page-21 + #[corresponds(BN_get_rfc2409_prime_768)] + #[cfg(not(boringssl))] + pub fn get_rfc2409_prime_768() -> Result<BigNum, ErrorStack> { + unsafe { + ffi::init(); + cvt_p(BN_get_rfc2409_prime_768(ptr::null_mut())).map(BigNum) + } + } + + /// Returns a constant used in IKE as defined in [`RFC 2409`]. This prime number is in + /// the order of magnitude of `2 ^ 1024`. This number is used during calculated key + /// exchanges such as Diffie-Hellman. This number is labeled Oakly group 2. + /// + /// [`RFC 2409`]: https://tools.ietf.org/html/rfc2409#page-21 + #[corresponds(BN_get_rfc2409_prime_1024)] + #[cfg(not(boringssl))] + pub fn get_rfc2409_prime_1024() -> Result<BigNum, ErrorStack> { + unsafe { + ffi::init(); + cvt_p(BN_get_rfc2409_prime_1024(ptr::null_mut())).map(BigNum) + } + } + + /// Returns a constant used in IKE as defined in [`RFC 3526`]. The prime is in the order + /// of magnitude of `2 ^ 1536`. This number is used during calculated key + /// exchanges such as Diffie-Hellman. This number is labeled MODP group 5. + /// + /// [`RFC 3526`]: https://tools.ietf.org/html/rfc3526#page-3 + #[corresponds(BN_get_rfc3526_prime_1536)] + #[cfg(not(boringssl))] + pub fn get_rfc3526_prime_1536() -> Result<BigNum, ErrorStack> { + unsafe { + ffi::init(); + cvt_p(BN_get_rfc3526_prime_1536(ptr::null_mut())).map(BigNum) + } + } + + /// Returns a constant used in IKE as defined in [`RFC 3526`]. The prime is in the order + /// of magnitude of `2 ^ 2048`. This number is used during calculated key + /// exchanges such as Diffie-Hellman. This number is labeled MODP group 14. + /// + /// [`RFC 3526`]: https://tools.ietf.org/html/rfc3526#page-3 + #[corresponds(BN_get_rfc3526_prime_2048)] + #[cfg(not(boringssl))] + pub fn get_rfc3526_prime_2048() -> Result<BigNum, ErrorStack> { + unsafe { + ffi::init(); + cvt_p(BN_get_rfc3526_prime_2048(ptr::null_mut())).map(BigNum) + } + } + + /// Returns a constant used in IKE as defined in [`RFC 3526`]. The prime is in the order + /// of magnitude of `2 ^ 3072`. This number is used during calculated key + /// exchanges such as Diffie-Hellman. This number is labeled MODP group 15. + /// + /// [`RFC 3526`]: https://tools.ietf.org/html/rfc3526#page-4 + #[corresponds(BN_get_rfc3526_prime_3072)] + #[cfg(not(boringssl))] + pub fn get_rfc3526_prime_3072() -> Result<BigNum, ErrorStack> { + unsafe { + ffi::init(); + cvt_p(BN_get_rfc3526_prime_3072(ptr::null_mut())).map(BigNum) + } + } + + /// Returns a constant used in IKE as defined in [`RFC 3526`]. The prime is in the order + /// of magnitude of `2 ^ 4096`. This number is used during calculated key + /// exchanges such as Diffie-Hellman. This number is labeled MODP group 16. + /// + /// [`RFC 3526`]: https://tools.ietf.org/html/rfc3526#page-4 + #[corresponds(BN_get_rfc3526_prime_4096)] + #[cfg(not(boringssl))] + pub fn get_rfc3526_prime_4096() -> Result<BigNum, ErrorStack> { + unsafe { + ffi::init(); + cvt_p(BN_get_rfc3526_prime_4096(ptr::null_mut())).map(BigNum) + } + } + + /// Returns a constant used in IKE as defined in [`RFC 3526`]. The prime is in the order + /// of magnitude of `2 ^ 6144`. This number is used during calculated key + /// exchanges such as Diffie-Hellman. This number is labeled MODP group 17. + /// + /// [`RFC 3526`]: https://tools.ietf.org/html/rfc3526#page-6 + #[corresponds(BN_get_rfc3526_prime_6114)] + #[cfg(not(boringssl))] + pub fn get_rfc3526_prime_6144() -> Result<BigNum, ErrorStack> { + unsafe { + ffi::init(); + cvt_p(BN_get_rfc3526_prime_6144(ptr::null_mut())).map(BigNum) + } + } + + /// Returns a constant used in IKE as defined in [`RFC 3526`]. The prime is in the order + /// of magnitude of `2 ^ 8192`. This number is used during calculated key + /// exchanges such as Diffie-Hellman. This number is labeled MODP group 18. + /// + /// [`RFC 3526`]: https://tools.ietf.org/html/rfc3526#page-6 + #[corresponds(BN_get_rfc3526_prime_8192)] + #[cfg(not(boringssl))] + pub fn get_rfc3526_prime_8192() -> Result<BigNum, ErrorStack> { + unsafe { + ffi::init(); + cvt_p(BN_get_rfc3526_prime_8192(ptr::null_mut())).map(BigNum) + } + } + + /// Creates a new `BigNum` from an unsigned, big-endian encoded number of arbitrary length. + /// + /// OpenSSL documentation at [`BN_bin2bn`] + /// + /// [`BN_bin2bn`]: https://www.openssl.org/docs/manmaster/crypto/BN_bin2bn.html + /// + /// ``` + /// # use openssl::bn::BigNum; + /// let bignum = BigNum::from_slice(&[0x12, 0x00, 0x34]).unwrap(); + /// + /// assert_eq!(bignum, BigNum::from_u32(0x120034).unwrap()); + /// ``` + #[corresponds(BN_bin2bn)] + pub fn from_slice(n: &[u8]) -> Result<BigNum, ErrorStack> { + unsafe { + ffi::init(); + assert!(n.len() <= LenType::max_value() as usize); + + cvt_p(ffi::BN_bin2bn( + n.as_ptr(), + n.len() as LenType, + ptr::null_mut(), + )) + .map(|p| BigNum::from_ptr(p)) + } + } + + /// Copies data from a slice overwriting what was in the BigNum. + /// + /// This function can be used to copy data from a slice to a + /// [secure BigNum][`BigNum::new_secure`]. + /// + /// # Examples + /// + /// ``` + /// # use openssl::bn::BigNum; + /// let mut bignum = BigNum::new().unwrap(); + /// bignum.copy_from_slice(&[0x12, 0x00, 0x34]).unwrap(); + /// + /// assert_eq!(bignum, BigNum::from_u32(0x120034).unwrap()); + /// ``` + #[corresponds(BN_bin2bn)] + pub fn copy_from_slice(&mut self, n: &[u8]) -> Result<(), ErrorStack> { + unsafe { + assert!(n.len() <= LenType::max_value() as usize); + + cvt_p(ffi::BN_bin2bn(n.as_ptr(), n.len() as LenType, self.0))?; + Ok(()) + } + } +} + +impl fmt::Debug for BigNumRef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.to_dec_str() { + Ok(s) => f.write_str(&s), + Err(e) => Err(e.into()), + } + } +} + +impl fmt::Debug for BigNum { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.to_dec_str() { + Ok(s) => f.write_str(&s), + Err(e) => Err(e.into()), + } + } +} + +impl fmt::Display for BigNumRef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.to_dec_str() { + Ok(s) => f.write_str(&s), + Err(e) => Err(e.into()), + } + } +} + +impl fmt::Display for BigNum { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.to_dec_str() { + Ok(s) => f.write_str(&s), + Err(e) => Err(e.into()), + } + } +} + +impl PartialEq<BigNumRef> for BigNumRef { + fn eq(&self, oth: &BigNumRef) -> bool { + self.cmp(oth) == Ordering::Equal + } +} + +impl PartialEq<BigNum> for BigNumRef { + fn eq(&self, oth: &BigNum) -> bool { + self.eq(oth.deref()) + } +} + +impl Eq for BigNumRef {} + +impl PartialEq for BigNum { + fn eq(&self, oth: &BigNum) -> bool { + self.deref().eq(oth) + } +} + +impl PartialEq<BigNumRef> for BigNum { + fn eq(&self, oth: &BigNumRef) -> bool { + self.deref().eq(oth) + } +} + +impl Eq for BigNum {} + +impl PartialOrd<BigNumRef> for BigNumRef { + fn partial_cmp(&self, oth: &BigNumRef) -> Option<Ordering> { + Some(self.cmp(oth)) + } +} + +impl PartialOrd<BigNum> for BigNumRef { + fn partial_cmp(&self, oth: &BigNum) -> Option<Ordering> { + Some(self.cmp(oth.deref())) + } +} + +impl Ord for BigNumRef { + fn cmp(&self, oth: &BigNumRef) -> Ordering { + unsafe { ffi::BN_cmp(self.as_ptr(), oth.as_ptr()).cmp(&0) } + } +} + +impl PartialOrd for BigNum { + fn partial_cmp(&self, oth: &BigNum) -> Option<Ordering> { + self.deref().partial_cmp(oth.deref()) + } +} + +impl PartialOrd<BigNumRef> for BigNum { + fn partial_cmp(&self, oth: &BigNumRef) -> Option<Ordering> { + self.deref().partial_cmp(oth) + } +} + +impl Ord for BigNum { + fn cmp(&self, oth: &BigNum) -> Ordering { + self.deref().cmp(oth.deref()) + } +} + +macro_rules! delegate { + ($t:ident, $m:ident) => { + impl<'a, 'b> $t<&'b BigNum> for &'a BigNumRef { + type Output = BigNum; + + fn $m(self, oth: &BigNum) -> BigNum { + $t::$m(self, oth.deref()) + } + } + + impl<'a, 'b> $t<&'b BigNumRef> for &'a BigNum { + type Output = BigNum; + + fn $m(self, oth: &BigNumRef) -> BigNum { + $t::$m(self.deref(), oth) + } + } + + impl<'a, 'b> $t<&'b BigNum> for &'a BigNum { + type Output = BigNum; + + fn $m(self, oth: &BigNum) -> BigNum { + $t::$m(self.deref(), oth.deref()) + } + } + }; +} + +impl<'a, 'b> Add<&'b BigNumRef> for &'a BigNumRef { + type Output = BigNum; + + fn add(self, oth: &BigNumRef) -> BigNum { + let mut r = BigNum::new().unwrap(); + r.checked_add(self, oth).unwrap(); + r + } +} + +delegate!(Add, add); + +impl<'a, 'b> Sub<&'b BigNumRef> for &'a BigNumRef { + type Output = BigNum; + + fn sub(self, oth: &BigNumRef) -> BigNum { + let mut r = BigNum::new().unwrap(); + r.checked_sub(self, oth).unwrap(); + r + } +} + +delegate!(Sub, sub); + +impl<'a, 'b> Mul<&'b BigNumRef> for &'a BigNumRef { + type Output = BigNum; + + fn mul(self, oth: &BigNumRef) -> BigNum { + let mut ctx = BigNumContext::new().unwrap(); + let mut r = BigNum::new().unwrap(); + r.checked_mul(self, oth, &mut ctx).unwrap(); + r + } +} + +delegate!(Mul, mul); + +impl<'a, 'b> Div<&'b BigNumRef> for &'a BigNumRef { + type Output = BigNum; + + fn div(self, oth: &'b BigNumRef) -> BigNum { + let mut ctx = BigNumContext::new().unwrap(); + let mut r = BigNum::new().unwrap(); + r.checked_div(self, oth, &mut ctx).unwrap(); + r + } +} + +delegate!(Div, div); + +impl<'a, 'b> Rem<&'b BigNumRef> for &'a BigNumRef { + type Output = BigNum; + + fn rem(self, oth: &'b BigNumRef) -> BigNum { + let mut ctx = BigNumContext::new().unwrap(); + let mut r = BigNum::new().unwrap(); + r.checked_rem(self, oth, &mut ctx).unwrap(); + r + } +} + +delegate!(Rem, rem); + +impl<'a> Shl<i32> for &'a BigNumRef { + type Output = BigNum; + + fn shl(self, n: i32) -> BigNum { + let mut r = BigNum::new().unwrap(); + r.lshift(self, n).unwrap(); + r + } +} + +impl<'a> Shl<i32> for &'a BigNum { + type Output = BigNum; + + fn shl(self, n: i32) -> BigNum { + self.deref().shl(n) + } +} + +impl<'a> Shr<i32> for &'a BigNumRef { + type Output = BigNum; + + fn shr(self, n: i32) -> BigNum { + let mut r = BigNum::new().unwrap(); + r.rshift(self, n).unwrap(); + r + } +} + +impl<'a> Shr<i32> for &'a BigNum { + type Output = BigNum; + + fn shr(self, n: i32) -> BigNum { + self.deref().shr(n) + } +} + +impl<'a> Neg for &'a BigNumRef { + type Output = BigNum; + + fn neg(self) -> BigNum { + self.to_owned().unwrap().neg() + } +} + +impl<'a> Neg for &'a BigNum { + type Output = BigNum; + + fn neg(self) -> BigNum { + self.deref().neg() + } +} + +impl Neg for BigNum { + type Output = BigNum; + + fn neg(mut self) -> BigNum { + let negative = self.is_negative(); + self.set_negative(!negative); + self + } +} + +#[cfg(test)] +mod tests { + use crate::bn::{BigNum, BigNumContext}; + + #[test] + fn test_to_from_slice() { + let v0 = BigNum::from_u32(10_203_004).unwrap(); + let vec = v0.to_vec(); + let v1 = BigNum::from_slice(&vec).unwrap(); + + assert_eq!(v0, v1); + } + + #[test] + fn test_negation() { + let a = BigNum::from_u32(909_829_283).unwrap(); + + assert!(!a.is_negative()); + assert!((-a).is_negative()); + } + + #[test] + fn test_shift() { + let a = BigNum::from_u32(909_829_283).unwrap(); + + assert_eq!(a, &(&a << 1) >> 1); + } + + #[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] + #[test] + fn test_rand_range() { + let range = BigNum::from_u32(909_829_283).unwrap(); + let mut result = BigNum::from_dec_str(&range.to_dec_str().unwrap()).unwrap(); + range.rand_range(&mut result).unwrap(); + assert!(result >= BigNum::from_u32(0).unwrap() && result < range); + } + + #[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] + #[test] + fn test_pseudo_rand_range() { + let range = BigNum::from_u32(909_829_283).unwrap(); + let mut result = BigNum::from_dec_str(&range.to_dec_str().unwrap()).unwrap(); + range.pseudo_rand_range(&mut result).unwrap(); + assert!(result >= BigNum::from_u32(0).unwrap() && result < range); + } + + #[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] + #[test] + fn test_prime_numbers() { + let a = BigNum::from_u32(19_029_017).unwrap(); + let mut p = BigNum::new().unwrap(); + p.generate_prime(128, true, None, Some(&a)).unwrap(); + + let mut ctx = BigNumContext::new().unwrap(); + assert!(p.is_prime(100, &mut ctx).unwrap()); + assert!(p.is_prime_fasttest(100, &mut ctx, true).unwrap()); + } + + #[cfg(ossl110)] + #[test] + fn test_secure_bn_ctx() { + let mut cxt = BigNumContext::new_secure().unwrap(); + let a = BigNum::from_u32(8).unwrap(); + let b = BigNum::from_u32(3).unwrap(); + + let mut remainder = BigNum::new().unwrap(); + remainder.nnmod(&a, &b, &mut cxt).unwrap(); + + assert!(remainder.eq(&BigNum::from_u32(2).unwrap())); + } + + #[cfg(ossl110)] + #[test] + fn test_secure_bn() { + let a = BigNum::new().unwrap(); + assert!(!a.is_secure()); + + let b = BigNum::new_secure().unwrap(); + assert!(b.is_secure()) + } + + #[cfg(ossl110)] + #[test] + fn test_const_time_bn() { + let a = BigNum::new().unwrap(); + assert!(!a.is_const_time()); + + let mut b = BigNum::new().unwrap(); + b.set_const_time(); + assert!(b.is_const_time()) + } + + #[cfg(ossl110)] + #[test] + fn test_mod_sqrt() { + let mut ctx = BigNumContext::new().unwrap(); + + let s = BigNum::from_hex_str("47A8DD7626B9908C80ACD7E0D3344D69").unwrap(); + let p = BigNum::from_hex_str("81EF47265B58BCE5").unwrap(); + let mut out = BigNum::new().unwrap(); + + out.mod_sqrt(&s, &p, &mut ctx).unwrap(); + assert_eq!(out, BigNum::from_hex_str("7C6D179E19B97BDD").unwrap()); + } + + #[test] + #[cfg(any(ossl110, boringssl, libressl350))] + fn test_odd_even() { + let a = BigNum::from_u32(17).unwrap(); + let b = BigNum::from_u32(18).unwrap(); + + assert!(a.is_odd()); + assert!(!b.is_odd()); + + assert!(!a.is_even()); + assert!(b.is_even()); + } +} diff --git a/vendor/openssl/src/cipher.rs b/vendor/openssl/src/cipher.rs new file mode 100644 index 0000000..2b89861 --- /dev/null +++ b/vendor/openssl/src/cipher.rs @@ -0,0 +1,536 @@ +//! Symmetric ciphers. + +#[cfg(ossl300)] +use crate::cvt_p; +#[cfg(ossl300)] +use crate::error::ErrorStack; +#[cfg(ossl300)] +use crate::lib_ctx::LibCtxRef; +use crate::nid::Nid; +use cfg_if::cfg_if; +use foreign_types::{ForeignTypeRef, Opaque}; +use openssl_macros::corresponds; +#[cfg(ossl300)] +use std::ffi::CString; +use std::ops::{Deref, DerefMut}; +#[cfg(ossl300)] +use std::ptr; + +cfg_if! { + if #[cfg(any(boringssl, ossl110, libressl273))] { + use ffi::{EVP_CIPHER_block_size, EVP_CIPHER_iv_length, EVP_CIPHER_key_length}; + } else { + use libc::c_int; + + #[allow(bad_style)] + pub unsafe fn EVP_CIPHER_iv_length(ptr: *const ffi::EVP_CIPHER) -> c_int { + (*ptr).iv_len + } + + #[allow(bad_style)] + pub unsafe fn EVP_CIPHER_block_size(ptr: *const ffi::EVP_CIPHER) -> c_int { + (*ptr).block_size + } + + #[allow(bad_style)] + pub unsafe fn EVP_CIPHER_key_length(ptr: *const ffi::EVP_CIPHER) -> c_int { + (*ptr).key_len + } + } +} + +cfg_if! { + if #[cfg(ossl300)] { + use foreign_types::ForeignType; + + type Inner = *mut ffi::EVP_CIPHER; + + impl Drop for Cipher { + #[inline] + fn drop(&mut self) { + unsafe { + ffi::EVP_CIPHER_free(self.as_ptr()); + } + } + } + + impl ForeignType for Cipher { + type CType = ffi::EVP_CIPHER; + type Ref = CipherRef; + + #[inline] + unsafe fn from_ptr(ptr: *mut Self::CType) -> Self { + Cipher(ptr) + } + + #[inline] + fn as_ptr(&self) -> *mut Self::CType { + self.0 + } + } + + impl Deref for Cipher { + type Target = CipherRef; + + #[inline] + fn deref(&self) -> &Self::Target { + unsafe { + CipherRef::from_ptr(self.as_ptr()) + } + } + } + + impl DerefMut for Cipher { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { + CipherRef::from_ptr_mut(self.as_ptr()) + } + } + } + } else { + enum Inner {} + + impl Deref for Cipher { + type Target = CipherRef; + + #[inline] + fn deref(&self) -> &Self::Target { + match self.0 {} + } + } + + impl DerefMut for Cipher { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + match self.0 {} + } + } + } +} + +/// A symmetric cipher. +pub struct Cipher(Inner); + +unsafe impl Sync for Cipher {} +unsafe impl Send for Cipher {} + +impl Cipher { + /// Looks up the cipher for a certain nid. + #[corresponds(EVP_get_cipherbynid)] + pub fn from_nid(nid: Nid) -> Option<&'static CipherRef> { + unsafe { + let ptr = ffi::EVP_get_cipherbyname(ffi::OBJ_nid2sn(nid.as_raw())); + if ptr.is_null() { + None + } else { + Some(CipherRef::from_ptr(ptr as *mut _)) + } + } + } + + /// Fetches a cipher object corresponding to the specified algorithm name and properties. + /// + /// Requires OpenSSL 3.0.0 or newer. + #[corresponds(EVP_CIPHER_fetch)] + #[cfg(ossl300)] + pub fn fetch( + ctx: Option<&LibCtxRef>, + algorithm: &str, + properties: Option<&str>, + ) -> Result<Self, ErrorStack> { + let algorithm = CString::new(algorithm).unwrap(); + let properties = properties.map(|s| CString::new(s).unwrap()); + + unsafe { + let ptr = cvt_p(ffi::EVP_CIPHER_fetch( + ctx.map_or(ptr::null_mut(), ForeignTypeRef::as_ptr), + algorithm.as_ptr(), + properties.map_or(ptr::null_mut(), |s| s.as_ptr()), + ))?; + + Ok(Cipher::from_ptr(ptr)) + } + } + + pub fn aes_128_ecb() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_128_ecb() as *mut _) } + } + + pub fn aes_128_cbc() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_128_cbc() as *mut _) } + } + + #[cfg(not(boringssl))] + pub fn aes_128_xts() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_128_xts() as *mut _) } + } + + #[cfg(not(boringssl))] + pub fn aes_128_ctr() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_128_ctr() as *mut _) } + } + + #[cfg(not(boringssl))] + pub fn aes_128_cfb1() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_128_cfb1() as *mut _) } + } + + #[cfg(not(boringssl))] + pub fn aes_128_cfb128() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_128_cfb128() as *mut _) } + } + + #[cfg(not(boringssl))] + pub fn aes_128_cfb8() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_128_cfb8() as *mut _) } + } + + pub fn aes_128_gcm() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_128_gcm() as *mut _) } + } + + #[cfg(not(boringssl))] + pub fn aes_128_ccm() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_128_ccm() as *mut _) } + } + + #[cfg(not(boringssl))] + pub fn aes_128_ofb() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_128_ofb() as *mut _) } + } + + /// Requires OpenSSL 1.1.0 or newer. + #[cfg(ossl110)] + pub fn aes_128_ocb() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_128_ocb() as *mut _) } + } + + /// Requires OpenSSL 1.0.2 or newer. + #[cfg(ossl102)] + pub fn aes_128_wrap() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_128_wrap() as *mut _) } + } + + /// Requires OpenSSL 1.1.0 or newer. + #[cfg(ossl110)] + pub fn aes_128_wrap_pad() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_128_wrap_pad() as *mut _) } + } + + pub fn aes_192_ecb() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_192_ecb() as *mut _) } + } + + pub fn aes_192_cbc() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_192_cbc() as *mut _) } + } + + pub fn aes_192_ctr() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_192_ctr() as *mut _) } + } + + #[cfg(not(boringssl))] + pub fn aes_192_cfb1() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_192_cfb1() as *mut _) } + } + + pub fn aes_192_cfb128() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_192_cfb128() as *mut _) } + } + + #[cfg(not(boringssl))] + pub fn aes_192_cfb8() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_192_cfb8() as *mut _) } + } + + pub fn aes_192_gcm() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_192_gcm() as *mut _) } + } + + #[cfg(not(boringssl))] + pub fn aes_192_ccm() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_192_ccm() as *mut _) } + } + + pub fn aes_192_ofb() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_192_ofb() as *mut _) } + } + + /// Requires OpenSSL 1.1.0 or newer. + #[cfg(ossl110)] + pub fn aes_192_ocb() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_192_ocb() as *mut _) } + } + + /// Requires OpenSSL 1.0.2 or newer. + #[cfg(ossl102)] + pub fn aes_192_wrap() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_192_wrap() as *mut _) } + } + + /// Requires OpenSSL 1.1.0 or newer. + #[cfg(ossl110)] + pub fn aes_192_wrap_pad() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_192_wrap_pad() as *mut _) } + } + + pub fn aes_256_ecb() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_256_ecb() as *mut _) } + } + + pub fn aes_256_cbc() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_256_cbc() as *mut _) } + } + + pub fn aes_256_ctr() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_256_ctr() as *mut _) } + } + + #[cfg(not(boringssl))] + pub fn aes_256_cfb1() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_256_cfb1() as *mut _) } + } + + pub fn aes_256_cfb128() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_256_cfb128() as *mut _) } + } + + #[cfg(not(boringssl))] + pub fn aes_256_cfb8() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_256_cfb8() as *mut _) } + } + + pub fn aes_256_gcm() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_256_gcm() as *mut _) } + } + + #[cfg(not(boringssl))] + pub fn aes_256_ccm() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_256_ccm() as *mut _) } + } + + pub fn aes_256_ofb() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_256_ofb() as *mut _) } + } + + /// Requires OpenSSL 1.1.0 or newer. + #[cfg(ossl110)] + pub fn aes_256_ocb() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_256_ocb() as *mut _) } + } + + /// Requires OpenSSL 1.0.2 or newer. + #[cfg(ossl102)] + pub fn aes_256_wrap() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_256_wrap() as *mut _) } + } + + /// Requires OpenSSL 1.1.0 or newer. + #[cfg(ossl110)] + pub fn aes_256_wrap_pad() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_aes_256_wrap_pad() as *mut _) } + } + + #[cfg(not(osslconf = "OPENSSL_NO_BF"))] + pub fn bf_cbc() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_bf_cbc() as *mut _) } + } + + #[cfg(not(osslconf = "OPENSSL_NO_BF"))] + pub fn bf_ecb() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_bf_ecb() as *mut _) } + } + + #[cfg(not(osslconf = "OPENSSL_NO_BF"))] + #[cfg(not(boringssl))] + pub fn bf_cfb64() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_bf_cfb64() as *mut _) } + } + + #[cfg(not(osslconf = "OPENSSL_NO_BF"))] + #[cfg(not(boringssl))] + pub fn bf_ofb() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_bf_ofb() as *mut _) } + } + + pub fn des_cbc() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_des_cbc() as *mut _) } + } + + pub fn des_ecb() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_des_ecb() as *mut _) } + } + + pub fn des_ede3() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_des_ede3() as *mut _) } + } + + pub fn des_ede3_cbc() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_des_ede3_cbc() as *mut _) } + } + + #[cfg(not(boringssl))] + pub fn des_ede3_cfb64() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_des_ede3_cfb64() as *mut _) } + } + + #[cfg(not(osslconf = "OPENSSL_NO_RC4"))] + pub fn rc4() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_rc4() as *mut _) } + } + + #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_CAMELLIA")))] + pub fn camellia128_cfb128() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_camellia_128_cfb128() as *mut _) } + } + + #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_CAMELLIA")))] + pub fn camellia128_ecb() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_camellia_128_ecb() as *mut _) } + } + + #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_CAMELLIA")))] + pub fn camellia192_cfb128() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_camellia_192_cfb128() as *mut _) } + } + + #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_CAMELLIA")))] + pub fn camellia192_ecb() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_camellia_192_ecb() as *mut _) } + } + + #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_CAMELLIA")))] + pub fn camellia256_cfb128() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_camellia_256_cfb128() as *mut _) } + } + + #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_CAMELLIA")))] + pub fn camellia256_ecb() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_camellia_256_ecb() as *mut _) } + } + + #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_CAST")))] + pub fn cast5_cfb64() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_cast5_cfb64() as *mut _) } + } + + #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_CAST")))] + pub fn cast5_ecb() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_cast5_ecb() as *mut _) } + } + + #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_IDEA")))] + pub fn idea_cfb64() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_idea_cfb64() as *mut _) } + } + + #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_IDEA")))] + pub fn idea_ecb() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_idea_ecb() as *mut _) } + } + + #[cfg(all(ossl110, not(osslconf = "OPENSSL_NO_CHACHA")))] + pub fn chacha20() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_chacha20() as *mut _) } + } + + #[cfg(all(any(ossl110, libressl360), not(osslconf = "OPENSSL_NO_CHACHA")))] + pub fn chacha20_poly1305() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_chacha20_poly1305() as *mut _) } + } + + #[cfg(not(osslconf = "OPENSSL_NO_SEED"))] + #[cfg(not(boringssl))] + pub fn seed_cbc() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_seed_cbc() as *mut _) } + } + + #[cfg(not(osslconf = "OPENSSL_NO_SEED"))] + #[cfg(not(boringssl))] + pub fn seed_cfb128() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_seed_cfb128() as *mut _) } + } + + #[cfg(not(osslconf = "OPENSSL_NO_SEED"))] + #[cfg(not(boringssl))] + pub fn seed_ecb() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_seed_ecb() as *mut _) } + } + + #[cfg(not(osslconf = "OPENSSL_NO_SEED"))] + #[cfg(not(boringssl))] + pub fn seed_ofb() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_seed_ofb() as *mut _) } + } + + #[cfg(all(any(ossl111, libressl291), not(osslconf = "OPENSSL_NO_SM4")))] + pub fn sm4_ecb() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_sm4_ecb() as *mut _) } + } + + #[cfg(all(any(ossl111, libressl291), not(osslconf = "OPENSSL_NO_SM4")))] + pub fn sm4_cbc() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_sm4_cbc() as *mut _) } + } + + #[cfg(all(any(ossl111, libressl291), not(osslconf = "OPENSSL_NO_SM4")))] + pub fn sm4_ctr() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_sm4_ctr() as *mut _) } + } + + #[cfg(all(any(ossl111, libressl291), not(osslconf = "OPENSSL_NO_SM4")))] + pub fn sm4_cfb128() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_sm4_cfb128() as *mut _) } + } + + #[cfg(all(any(ossl111, libressl291), not(osslconf = "OPENSSL_NO_SM4")))] + pub fn sm4_ofb() -> &'static CipherRef { + unsafe { CipherRef::from_ptr(ffi::EVP_sm4_ofb() as *mut _) } + } +} + +/// A reference to a [`Cipher`]. +pub struct CipherRef(Opaque); + +impl ForeignTypeRef for CipherRef { + type CType = ffi::EVP_CIPHER; +} + +unsafe impl Sync for CipherRef {} +unsafe impl Send for CipherRef {} + +impl CipherRef { + /// Returns the cipher's Nid. + #[corresponds(EVP_CIPHER_nid)] + pub fn nid(&self) -> Nid { + let nid = unsafe { ffi::EVP_CIPHER_nid(self.as_ptr()) }; + Nid::from_raw(nid) + } + + /// Returns the length of keys used with this cipher. + #[corresponds(EVP_CIPHER_key_length)] + pub fn key_length(&self) -> usize { + unsafe { EVP_CIPHER_key_length(self.as_ptr()) as usize } + } + + /// Returns the length of the IV used with this cipher. + /// + /// # Note + /// + /// Ciphers that do not use an IV have an IV length of 0. + #[corresponds(EVP_CIPHER_iv_length)] + pub fn iv_length(&self) -> usize { + unsafe { EVP_CIPHER_iv_length(self.as_ptr()) as usize } + } + + /// Returns the block size of the cipher. + /// + /// # Note + /// + /// Stream ciphers have a block size of 1. + #[corresponds(EVP_CIPHER_block_size)] + pub fn block_size(&self) -> usize { + unsafe { EVP_CIPHER_block_size(self.as_ptr()) as usize } + } +} diff --git a/vendor/openssl/src/cipher_ctx.rs b/vendor/openssl/src/cipher_ctx.rs new file mode 100644 index 0000000..f9031d2 --- /dev/null +++ b/vendor/openssl/src/cipher_ctx.rs @@ -0,0 +1,1106 @@ +//! The symmetric encryption context. +//! +//! # Examples +//! +//! Encrypt data with AES128 CBC +//! +//! ``` +//! use openssl::cipher::Cipher; +//! use openssl::cipher_ctx::CipherCtx; +//! +//! let cipher = Cipher::aes_128_cbc(); +//! let data = b"Some Crypto Text"; +//! let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"; +//! let iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07"; +//! +//! let mut ctx = CipherCtx::new().unwrap(); +//! ctx.encrypt_init(Some(cipher), Some(key), Some(iv)).unwrap(); +//! +//! let mut ciphertext = vec![]; +//! ctx.cipher_update_vec(data, &mut ciphertext).unwrap(); +//! ctx.cipher_final_vec(&mut ciphertext).unwrap(); +//! +//! assert_eq!( +//! b"\xB4\xB9\xE7\x30\xD6\xD6\xF7\xDE\x77\x3F\x1C\xFF\xB3\x3E\x44\x5A\x91\xD7\x27\x62\x87\x4D\ +//! \xFB\x3C\x5E\xC4\x59\x72\x4A\xF4\x7C\xA1", +//! &ciphertext[..], +//! ); +//! ``` +//! +//! Decrypt data with AES128 CBC +//! +//! ``` +//! use openssl::cipher::Cipher; +//! use openssl::cipher_ctx::CipherCtx; +//! +//! let cipher = Cipher::aes_128_cbc(); +//! let data = b"\xB4\xB9\xE7\x30\xD6\xD6\xF7\xDE\x77\x3F\x1C\xFF\xB3\x3E\x44\x5A\x91\xD7\x27\x62\ +//! \x87\x4D\xFB\x3C\x5E\xC4\x59\x72\x4A\xF4\x7C\xA1"; +//! let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"; +//! let iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07"; +//! +//! let mut ctx = CipherCtx::new().unwrap(); +//! ctx.decrypt_init(Some(cipher), Some(key), Some(iv)).unwrap(); +//! +//! let mut plaintext = vec![]; +//! ctx.cipher_update_vec(data, &mut plaintext).unwrap(); +//! ctx.cipher_final_vec(&mut plaintext).unwrap(); +//! +//! assert_eq!(b"Some Crypto Text", &plaintext[..]); +//! ``` +#![warn(missing_docs)] + +use crate::cipher::CipherRef; +use crate::error::ErrorStack; +#[cfg(not(boringssl))] +use crate::pkey::{HasPrivate, HasPublic, PKey, PKeyRef}; +use crate::{cvt, cvt_p}; +#[cfg(ossl102)] +use bitflags::bitflags; +use cfg_if::cfg_if; +use foreign_types::{ForeignType, ForeignTypeRef}; +use libc::{c_int, c_uchar}; +use openssl_macros::corresponds; +use std::convert::{TryFrom, TryInto}; +use std::ptr; + +cfg_if! { + if #[cfg(ossl300)] { + use ffi::EVP_CIPHER_CTX_get0_cipher; + } else { + use ffi::EVP_CIPHER_CTX_cipher as EVP_CIPHER_CTX_get0_cipher; + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::EVP_CIPHER_CTX; + fn drop = ffi::EVP_CIPHER_CTX_free; + + /// A context object used to perform symmetric encryption operations. + pub struct CipherCtx; + /// A reference to a [`CipherCtx`]. + pub struct CipherCtxRef; +} + +#[cfg(ossl102)] +bitflags! { + /// Flags for `EVP_CIPHER_CTX`. + pub struct CipherCtxFlags : c_int { + /// The flag used to opt into AES key wrap ciphers. + const FLAG_WRAP_ALLOW = ffi::EVP_CIPHER_CTX_FLAG_WRAP_ALLOW; + } +} + +impl CipherCtx { + /// Creates a new context. + #[corresponds(EVP_CIPHER_CTX_new)] + pub fn new() -> Result<Self, ErrorStack> { + ffi::init(); + + unsafe { + let ptr = cvt_p(ffi::EVP_CIPHER_CTX_new())?; + Ok(CipherCtx::from_ptr(ptr)) + } + } +} + +impl CipherCtxRef { + #[corresponds(EVP_CIPHER_CTX_copy)] + pub fn copy(&mut self, src: &CipherCtxRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_CIPHER_CTX_copy(self.as_ptr(), src.as_ptr()))?; + Ok(()) + } + } + + /// Initializes the context for encryption. + /// + /// Normally this is called once to set all of the cipher, key, and IV. However, this process can be split up + /// by first setting the cipher with no key or IV and then setting the key and IV with no cipher. This can be used + /// to, for example, use a nonstandard IV size. + /// + /// # Panics + /// + /// Panics if the key buffer is smaller than the key size of the cipher, the IV buffer is smaller than the IV size + /// of the cipher, or if a key or IV is provided before a cipher. + #[corresponds(EVP_EncryptInit_ex)] + pub fn encrypt_init( + &mut self, + type_: Option<&CipherRef>, + key: Option<&[u8]>, + iv: Option<&[u8]>, + ) -> Result<(), ErrorStack> { + self.cipher_init(type_, key, iv, ffi::EVP_EncryptInit_ex) + } + + /// Initializes the context for decryption. + /// + /// Normally this is called once to set all of the cipher, key, and IV. However, this process can be split up + /// by first setting the cipher with no key or IV and then setting the key and IV with no cipher. This can be used + /// to, for example, use a nonstandard IV size. + /// + /// # Panics + /// + /// Panics if the key buffer is smaller than the key size of the cipher, the IV buffer is smaller than the IV size + /// of the cipher, or if a key or IV is provided before a cipher. + #[corresponds(EVP_DecryptInit_ex)] + pub fn decrypt_init( + &mut self, + type_: Option<&CipherRef>, + key: Option<&[u8]>, + iv: Option<&[u8]>, + ) -> Result<(), ErrorStack> { + self.cipher_init(type_, key, iv, ffi::EVP_DecryptInit_ex) + } + + fn cipher_init( + &mut self, + type_: Option<&CipherRef>, + key: Option<&[u8]>, + iv: Option<&[u8]>, + f: unsafe extern "C" fn( + *mut ffi::EVP_CIPHER_CTX, + *const ffi::EVP_CIPHER, + *mut ffi::ENGINE, + *const c_uchar, + *const c_uchar, + ) -> c_int, + ) -> Result<(), ErrorStack> { + if let Some(key) = key { + let key_len = type_.map_or_else(|| self.key_length(), |c| c.key_length()); + assert!(key_len <= key.len()); + } + + if let Some(iv) = iv { + let iv_len = type_.map_or_else(|| self.iv_length(), |c| c.iv_length()); + assert!(iv_len <= iv.len()); + } + + unsafe { + cvt(f( + self.as_ptr(), + type_.map_or(ptr::null(), |p| p.as_ptr()), + ptr::null_mut(), + key.map_or(ptr::null(), |k| k.as_ptr()), + iv.map_or(ptr::null(), |iv| iv.as_ptr()), + ))?; + } + + Ok(()) + } + + /// Initializes the context to perform envelope encryption. + /// + /// Normally this is called once to set both the cipher and public keys. However, this process may be split up by + /// first providing the cipher with no public keys and then setting the public keys with no cipher. + /// + /// `encrypted_keys` will contain the generated symmetric key encrypted with each corresponding asymmetric private + /// key. The generated IV will be written to `iv`. + /// + /// # Panics + /// + /// Panics if `pub_keys` is not the same size as `encrypted_keys`, the IV buffer is smaller than the cipher's IV + /// size, or if an IV is provided before the cipher. + #[corresponds(EVP_SealInit)] + #[cfg(not(boringssl))] + pub fn seal_init<T>( + &mut self, + type_: Option<&CipherRef>, + pub_keys: &[PKey<T>], + encrypted_keys: &mut [Vec<u8>], + iv: Option<&mut [u8]>, + ) -> Result<(), ErrorStack> + where + T: HasPublic, + { + assert_eq!(pub_keys.len(), encrypted_keys.len()); + if !pub_keys.is_empty() { + let iv_len = type_.map_or_else(|| self.iv_length(), |c| c.iv_length()); + assert!(iv.as_ref().map_or(0, |b| b.len()) >= iv_len); + } + + for (pub_key, buf) in pub_keys.iter().zip(&mut *encrypted_keys) { + buf.resize(pub_key.size(), 0); + } + + let mut keys = encrypted_keys + .iter_mut() + .map(|b| b.as_mut_ptr()) + .collect::<Vec<_>>(); + let mut key_lengths = vec![0; pub_keys.len()]; + let pub_keys_len = i32::try_from(pub_keys.len()).unwrap(); + + unsafe { + cvt(ffi::EVP_SealInit( + self.as_ptr(), + type_.map_or(ptr::null(), |p| p.as_ptr()), + keys.as_mut_ptr(), + key_lengths.as_mut_ptr(), + iv.map_or(ptr::null_mut(), |b| b.as_mut_ptr()), + pub_keys.as_ptr() as *mut _, + pub_keys_len, + ))?; + } + + for (buf, len) in encrypted_keys.iter_mut().zip(key_lengths) { + buf.truncate(len as usize); + } + + Ok(()) + } + + /// Initializes the context to perform envelope decryption. + /// + /// Normally this is called once with all of the arguments present. However, this process may be split up by first + /// providing the cipher alone and then after providing the rest of the arguments in a second call. + /// + /// # Panics + /// + /// Panics if the IV buffer is smaller than the cipher's required IV size or if the IV is provided before the + /// cipher. + #[corresponds(EVP_OpenInit)] + #[cfg(not(boringssl))] + pub fn open_init<T>( + &mut self, + type_: Option<&CipherRef>, + encrypted_key: &[u8], + iv: Option<&[u8]>, + priv_key: Option<&PKeyRef<T>>, + ) -> Result<(), ErrorStack> + where + T: HasPrivate, + { + if priv_key.is_some() { + let iv_len = type_.map_or_else(|| self.iv_length(), |c| c.iv_length()); + assert!(iv.map_or(0, |b| b.len()) >= iv_len); + } + + let len = c_int::try_from(encrypted_key.len()).unwrap(); + unsafe { + cvt(ffi::EVP_OpenInit( + self.as_ptr(), + type_.map_or(ptr::null(), |p| p.as_ptr()), + encrypted_key.as_ptr(), + len, + iv.map_or(ptr::null(), |b| b.as_ptr()), + priv_key.map_or(ptr::null_mut(), ForeignTypeRef::as_ptr), + ))?; + } + + Ok(()) + } + + fn assert_cipher(&self) { + unsafe { + assert!(!EVP_CIPHER_CTX_get0_cipher(self.as_ptr()).is_null()); + } + } + + /// Returns the block size of the context's cipher. + /// + /// Stream ciphers will report a block size of 1. + /// + /// # Panics + /// + /// Panics if the context has not been initialized with a cipher. + #[corresponds(EVP_CIPHER_CTX_block_size)] + pub fn block_size(&self) -> usize { + self.assert_cipher(); + + unsafe { ffi::EVP_CIPHER_CTX_block_size(self.as_ptr()) as usize } + } + + /// Returns the key length of the context's cipher. + /// + /// # Panics + /// + /// Panics if the context has not been initialized with a cipher. + #[corresponds(EVP_CIPHER_CTX_key_length)] + pub fn key_length(&self) -> usize { + self.assert_cipher(); + + unsafe { ffi::EVP_CIPHER_CTX_key_length(self.as_ptr()) as usize } + } + + /// Generates a random key based on the configured cipher. + /// + /// # Panics + /// + /// Panics if the context has not been initialized with a cipher or if the buffer is smaller than the cipher's key + /// length. + /// + /// This corresponds to [`EVP_CIPHER_CTX_rand_key`]. + /// + /// [`EVP_CIPHER_CTX_rand_key`]: https://www.openssl.org/docs/manmaster/man3/EVP_CIPHER_CTX_rand_key.html + #[corresponds(EVP_CIPHER_CTX_rand_key)] + #[cfg(not(boringssl))] + pub fn rand_key(&self, buf: &mut [u8]) -> Result<(), ErrorStack> { + assert!(buf.len() >= self.key_length()); + + unsafe { + cvt(ffi::EVP_CIPHER_CTX_rand_key( + self.as_ptr(), + buf.as_mut_ptr(), + ))?; + } + + Ok(()) + } + + /// Sets the length of the key expected by the context. + /// + /// Only some ciphers support configurable key lengths. + /// + /// # Panics + /// + /// Panics if the context has not been initialized with a cipher. + #[corresponds(EVP_CIPHER_CTX_set_key_length)] + pub fn set_key_length(&mut self, len: usize) -> Result<(), ErrorStack> { + self.assert_cipher(); + + unsafe { + cvt(ffi::EVP_CIPHER_CTX_set_key_length( + self.as_ptr(), + len.try_into().unwrap(), + ))?; + } + + Ok(()) + } + + /// Returns the length of the IV expected by this context. + /// + /// Returns 0 if the cipher does not use an IV. + /// + /// # Panics + /// + /// Panics if the context has not been initialized with a cipher. + #[corresponds(EVP_CIPHER_CTX_iv_length)] + pub fn iv_length(&self) -> usize { + self.assert_cipher(); + + unsafe { ffi::EVP_CIPHER_CTX_iv_length(self.as_ptr()) as usize } + } + + /// Returns the `num` parameter of the cipher. + /// + /// Built-in ciphers typically use this to track how much of the + /// current underlying block has been "used" already. + /// + /// # Panics + /// + /// Panics if the context has not been initialized with a cipher. + #[corresponds(EVP_CIPHER_CTX_num)] + #[cfg(ossl110)] + pub fn num(&self) -> usize { + self.assert_cipher(); + + unsafe { ffi::EVP_CIPHER_CTX_num(self.as_ptr()) as usize } + } + + /// Sets the length of the IV expected by this context. + /// + /// Only some ciphers support configurable IV lengths. + /// + /// # Panics + /// + /// Panics if the context has not been initialized with a cipher. + #[corresponds(EVP_CIPHER_CTX_ctrl)] + pub fn set_iv_length(&mut self, len: usize) -> Result<(), ErrorStack> { + self.assert_cipher(); + + let len = c_int::try_from(len).unwrap(); + + unsafe { + cvt(ffi::EVP_CIPHER_CTX_ctrl( + self.as_ptr(), + ffi::EVP_CTRL_GCM_SET_IVLEN, + len, + ptr::null_mut(), + ))?; + } + + Ok(()) + } + + /// Returns the length of the authentication tag expected by this context. + /// + /// Returns 0 if the cipher is not authenticated. + /// + /// # Panics + /// + /// Panics if the context has not been initialized with a cipher. + /// + /// Requires OpenSSL 3.0.0 or newer. + #[corresponds(EVP_CIPHER_CTX_get_tag_length)] + #[cfg(ossl300)] + pub fn tag_length(&self) -> usize { + self.assert_cipher(); + + unsafe { ffi::EVP_CIPHER_CTX_get_tag_length(self.as_ptr()) as usize } + } + + /// Retrieves the calculated authentication tag from the context. + /// + /// This should be called after [`Self::cipher_final`], and is only supported by authenticated ciphers. + /// + /// The size of the buffer indicates the size of the tag. While some ciphers support a range of tag sizes, it is + /// recommended to pick the maximum size. + #[corresponds(EVP_CIPHER_CTX_ctrl)] + pub fn tag(&self, tag: &mut [u8]) -> Result<(), ErrorStack> { + let len = c_int::try_from(tag.len()).unwrap(); + + unsafe { + cvt(ffi::EVP_CIPHER_CTX_ctrl( + self.as_ptr(), + ffi::EVP_CTRL_GCM_GET_TAG, + len, + tag.as_mut_ptr() as *mut _, + ))?; + } + + Ok(()) + } + + /// Sets the length of the generated authentication tag. + /// + /// This must be called when encrypting with a cipher in CCM mode to use a tag size other than the default. + #[corresponds(EVP_CIPHER_CTX_ctrl)] + pub fn set_tag_length(&mut self, len: usize) -> Result<(), ErrorStack> { + let len = c_int::try_from(len).unwrap(); + + unsafe { + cvt(ffi::EVP_CIPHER_CTX_ctrl( + self.as_ptr(), + ffi::EVP_CTRL_GCM_SET_TAG, + len, + ptr::null_mut(), + ))?; + } + + Ok(()) + } + + /// Sets the authentication tag for verification during decryption. + #[corresponds(EVP_CIPHER_CTX_ctrl)] + pub fn set_tag(&mut self, tag: &[u8]) -> Result<(), ErrorStack> { + let len = c_int::try_from(tag.len()).unwrap(); + + unsafe { + cvt(ffi::EVP_CIPHER_CTX_ctrl( + self.as_ptr(), + ffi::EVP_CTRL_GCM_SET_TAG, + len, + tag.as_ptr() as *mut _, + ))?; + } + + Ok(()) + } + + /// Enables or disables padding. + /// + /// If padding is disabled, the plaintext must be an exact multiple of the cipher's block size. + #[corresponds(EVP_CIPHER_CTX_set_padding)] + pub fn set_padding(&mut self, padding: bool) { + unsafe { + ffi::EVP_CIPHER_CTX_set_padding(self.as_ptr(), padding as c_int); + } + } + + /// Sets the total length of plaintext data. + /// + /// This is required for ciphers operating in CCM mode. + #[corresponds(EVP_CipherUpdate)] + pub fn set_data_len(&mut self, len: usize) -> Result<(), ErrorStack> { + let len = c_int::try_from(len).unwrap(); + + unsafe { + cvt(ffi::EVP_CipherUpdate( + self.as_ptr(), + ptr::null_mut(), + &mut 0, + ptr::null(), + len, + ))?; + } + + Ok(()) + } + + /// Set ctx flags. + /// + /// This function is currently used to enable AES key wrap feature supported by OpenSSL 1.0.2 or newer. + #[corresponds(EVP_CIPHER_CTX_set_flags)] + #[cfg(ossl102)] + pub fn set_flags(&mut self, flags: CipherCtxFlags) { + unsafe { + ffi::EVP_CIPHER_CTX_set_flags(self.as_ptr(), flags.bits()); + } + } + + /// Writes data into the context. + /// + /// Providing no output buffer will cause the input to be considered additional authenticated data (AAD). + /// + /// Returns the number of bytes written to `output`. + /// + /// # Panics + /// + /// Panics if `output` doesn't contain enough space for data to be + /// written as specified by [`Self::minimal_output_size`]. + #[corresponds(EVP_CipherUpdate)] + pub fn cipher_update( + &mut self, + input: &[u8], + output: Option<&mut [u8]>, + ) -> Result<usize, ErrorStack> { + if let Some(output) = &output { + let mut block_size = self.block_size(); + if block_size == 1 { + block_size = 0; + } + let min_output_size = input.len() + block_size; + assert!( + output.len() >= min_output_size, + "Output buffer size should be at least {} bytes.", + min_output_size + ); + } + + unsafe { self.cipher_update_unchecked(input, output) } + } + + /// Writes data into the context. + /// + /// Providing no output buffer will cause the input to be considered additional authenticated data (AAD). + /// + /// Returns the number of bytes written to `output`. + /// + /// This function is the same as [`Self::cipher_update`] but with the + /// output size check removed. It can be used when the exact + /// buffer size control is maintained by the caller. + /// + /// SAFETY: The caller is expected to provide `output` buffer + /// large enough to contain correct number of bytes. For streaming + /// ciphers the output buffer size should be at least as big as + /// the input buffer. For block ciphers the size of the output + /// buffer depends on the state of partially updated blocks. + #[corresponds(EVP_CipherUpdate)] + pub unsafe fn cipher_update_unchecked( + &mut self, + input: &[u8], + output: Option<&mut [u8]>, + ) -> Result<usize, ErrorStack> { + let inlen = c_int::try_from(input.len()).unwrap(); + + let mut outlen = 0; + + cvt(ffi::EVP_CipherUpdate( + self.as_ptr(), + output.map_or(ptr::null_mut(), |b| b.as_mut_ptr()), + &mut outlen, + input.as_ptr(), + inlen, + ))?; + + Ok(outlen as usize) + } + + /// Like [`Self::cipher_update`] except that it appends output to a [`Vec`]. + pub fn cipher_update_vec( + &mut self, + input: &[u8], + output: &mut Vec<u8>, + ) -> Result<usize, ErrorStack> { + let base = output.len(); + output.resize(base + input.len() + self.block_size(), 0); + let len = self.cipher_update(input, Some(&mut output[base..]))?; + output.truncate(base + len); + + Ok(len) + } + + /// Like [`Self::cipher_update`] except that it writes output into the + /// `data` buffer. The `inlen` parameter specifies the number of bytes in + /// `data` that are considered the input. For streaming ciphers, the size of + /// `data` must be at least the input size. Otherwise, it must be at least + /// an additional block size larger. + /// + /// Note: Use [`Self::cipher_update`] with no output argument to write AAD. + /// + /// # Panics + /// + /// This function panics if the input size cannot be represented as `int` or + /// exceeds the buffer size, or if the output buffer does not contain enough + /// additional space. + #[corresponds(EVP_CipherUpdate)] + pub fn cipher_update_inplace( + &mut self, + data: &mut [u8], + inlen: usize, + ) -> Result<usize, ErrorStack> { + assert!(inlen <= data.len(), "Input size may not exceed buffer size"); + let block_size = self.block_size(); + if block_size != 1 { + assert!( + data.len() >= inlen + block_size, + "Output buffer size must be at least {} bytes.", + inlen + block_size + ); + } + + let inlen = c_int::try_from(inlen).unwrap(); + let mut outlen = 0; + unsafe { + cvt(ffi::EVP_CipherUpdate( + self.as_ptr(), + data.as_mut_ptr(), + &mut outlen, + data.as_ptr(), + inlen, + )) + }?; + + Ok(outlen as usize) + } + + /// Finalizes the encryption or decryption process. + /// + /// Any remaining data will be written to the output buffer. + /// + /// Returns the number of bytes written to `output`. + /// + /// # Panics + /// + /// Panics if `output` is smaller than the cipher's block size. + #[corresponds(EVP_CipherFinal)] + pub fn cipher_final(&mut self, output: &mut [u8]) -> Result<usize, ErrorStack> { + let block_size = self.block_size(); + if block_size > 1 { + assert!(output.len() >= block_size); + } + + unsafe { self.cipher_final_unchecked(output) } + } + + /// Finalizes the encryption or decryption process. + /// + /// Any remaining data will be written to the output buffer. + /// + /// Returns the number of bytes written to `output`. + /// + /// This function is the same as [`Self::cipher_final`] but with + /// the output buffer size check removed. + /// + /// SAFETY: The caller is expected to provide `output` buffer + /// large enough to contain correct number of bytes. For streaming + /// ciphers the output buffer can be empty, for block ciphers the + /// output buffer should be at least as big as the block. + #[corresponds(EVP_CipherFinal)] + pub unsafe fn cipher_final_unchecked( + &mut self, + output: &mut [u8], + ) -> Result<usize, ErrorStack> { + let mut outl = 0; + + cvt(ffi::EVP_CipherFinal( + self.as_ptr(), + output.as_mut_ptr(), + &mut outl, + ))?; + + Ok(outl as usize) + } + + /// Like [`Self::cipher_final`] except that it appends output to a [`Vec`]. + pub fn cipher_final_vec(&mut self, output: &mut Vec<u8>) -> Result<usize, ErrorStack> { + let base = output.len(); + output.resize(base + self.block_size(), 0); + let len = self.cipher_final(&mut output[base..])?; + output.truncate(base + len); + + Ok(len) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{cipher::Cipher, rand::rand_bytes}; + #[cfg(not(boringssl))] + use std::slice; + + #[test] + #[cfg(not(boringssl))] + fn seal_open() { + let private_pem = include_bytes!("../test/rsa.pem"); + let public_pem = include_bytes!("../test/rsa.pem.pub"); + let private_key = PKey::private_key_from_pem(private_pem).unwrap(); + let public_key = PKey::public_key_from_pem(public_pem).unwrap(); + let cipher = Cipher::aes_256_cbc(); + let secret = b"My secret message"; + + let mut ctx = CipherCtx::new().unwrap(); + let mut encrypted_key = vec![]; + let mut iv = vec![0; cipher.iv_length()]; + let mut encrypted = vec![]; + ctx.seal_init( + Some(cipher), + &[public_key], + slice::from_mut(&mut encrypted_key), + Some(&mut iv), + ) + .unwrap(); + ctx.cipher_update_vec(secret, &mut encrypted).unwrap(); + ctx.cipher_final_vec(&mut encrypted).unwrap(); + + let mut decrypted = vec![]; + ctx.open_init(Some(cipher), &encrypted_key, Some(&iv), Some(&private_key)) + .unwrap(); + ctx.cipher_update_vec(&encrypted, &mut decrypted).unwrap(); + ctx.cipher_final_vec(&mut decrypted).unwrap(); + + assert_eq!(secret, &decrypted[..]); + } + + fn aes_128_cbc(cipher: &CipherRef) { + // from https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf + let key = hex::decode("2b7e151628aed2a6abf7158809cf4f3c").unwrap(); + let iv = hex::decode("000102030405060708090a0b0c0d0e0f").unwrap(); + let pt = hex::decode("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51") + .unwrap(); + let ct = hex::decode("7649abac8119b246cee98e9b12e9197d5086cb9b507219ee95db113a917678b2") + .unwrap(); + + let mut ctx = CipherCtx::new().unwrap(); + + ctx.encrypt_init(Some(cipher), Some(&key), Some(&iv)) + .unwrap(); + ctx.set_padding(false); + + let mut buf = vec![]; + ctx.cipher_update_vec(&pt, &mut buf).unwrap(); + ctx.cipher_final_vec(&mut buf).unwrap(); + + assert_eq!(buf, ct); + + ctx.decrypt_init(Some(cipher), Some(&key), Some(&iv)) + .unwrap(); + ctx.set_padding(false); + + let mut buf = vec![]; + ctx.cipher_update_vec(&ct, &mut buf).unwrap(); + ctx.cipher_final_vec(&mut buf).unwrap(); + + assert_eq!(buf, pt); + } + + #[test] + #[cfg(ossl300)] + fn fetched_aes_128_cbc() { + let cipher = Cipher::fetch(None, "AES-128-CBC", None).unwrap(); + aes_128_cbc(&cipher); + } + + #[test] + fn default_aes_128_cbc() { + let cipher = Cipher::aes_128_cbc(); + aes_128_cbc(cipher); + } + + #[test] + fn test_stream_ciphers() { + test_stream_cipher(Cipher::aes_192_ctr()); + test_stream_cipher(Cipher::aes_256_ctr()); + } + + fn test_stream_cipher(cipher: &'static CipherRef) { + let mut key = vec![0; cipher.key_length()]; + rand_bytes(&mut key).unwrap(); + let mut iv = vec![0; cipher.iv_length()]; + rand_bytes(&mut iv).unwrap(); + + let mut ctx = CipherCtx::new().unwrap(); + + ctx.encrypt_init(Some(cipher), Some(&key), Some(&iv)) + .unwrap(); + ctx.set_padding(false); + + assert_eq!( + 1, + cipher.block_size(), + "Need a stream cipher, not a block cipher" + ); + + // update cipher with non-full block + // this is a streaming cipher so the number of output bytes + // will be the same as the number of input bytes + let mut output = vec![0; 32]; + let outlen = ctx + .cipher_update(&[1; 15], Some(&mut output[0..15])) + .unwrap(); + assert_eq!(15, outlen); + + // update cipher with missing bytes from the previous block + // as previously it will output the same number of bytes as + // the input + let outlen = ctx + .cipher_update(&[1; 17], Some(&mut output[15..])) + .unwrap(); + assert_eq!(17, outlen); + + ctx.cipher_final_vec(&mut vec![0; 0]).unwrap(); + + // encrypt again, but use in-place encryption this time + // First reset the IV + ctx.encrypt_init(None, None, Some(&iv)).unwrap(); + ctx.set_padding(false); + let mut data_inplace: [u8; 32] = [1; 32]; + let outlen = ctx + .cipher_update_inplace(&mut data_inplace[0..15], 15) + .unwrap(); + assert_eq!(15, outlen); + + let outlen = ctx + .cipher_update_inplace(&mut data_inplace[15..32], 17) + .unwrap(); + assert_eq!(17, outlen); + + ctx.cipher_final(&mut [0u8; 0]).unwrap(); + + // Check that the resulting data is encrypted in the same manner + assert_eq!(data_inplace.as_slice(), output.as_slice()); + + // try to decrypt + ctx.decrypt_init(Some(cipher), Some(&key), Some(&iv)) + .unwrap(); + ctx.set_padding(false); + + // update cipher with non-full block + // expect that the output for stream cipher will contain + // the same number of bytes as the input + let mut output_decrypted = vec![0; 32]; + let outlen = ctx + .cipher_update(&output[0..15], Some(&mut output_decrypted[0..15])) + .unwrap(); + assert_eq!(15, outlen); + + let outlen = ctx + .cipher_update(&output[15..], Some(&mut output_decrypted[15..])) + .unwrap(); + assert_eq!(17, outlen); + + ctx.cipher_final_vec(&mut vec![0; 0]).unwrap(); + // check if the decrypted blocks are the same as input (all ones) + assert_eq!(output_decrypted, vec![1; 32]); + + // decrypt again, but now the output in-place + ctx.decrypt_init(None, None, Some(&iv)).unwrap(); + ctx.set_padding(false); + + let outlen = ctx.cipher_update_inplace(&mut output[0..15], 15).unwrap(); + assert_eq!(15, outlen); + + let outlen = ctx.cipher_update_inplace(&mut output[15..], 17).unwrap(); + assert_eq!(17, outlen); + + ctx.cipher_final_vec(&mut vec![0; 0]).unwrap(); + assert_eq!(output_decrypted, output); + } + + #[test] + #[should_panic(expected = "Output buffer size should be at least 33 bytes.")] + fn full_block_updates_aes_128() { + output_buffer_too_small(Cipher::aes_128_cbc()); + } + + #[test] + #[should_panic(expected = "Output buffer size should be at least 33 bytes.")] + fn full_block_updates_aes_256() { + output_buffer_too_small(Cipher::aes_256_cbc()); + } + + #[test] + #[should_panic(expected = "Output buffer size should be at least 17 bytes.")] + fn full_block_updates_3des() { + output_buffer_too_small(Cipher::des_ede3_cbc()); + } + + fn output_buffer_too_small(cipher: &'static CipherRef) { + let mut key = vec![0; cipher.key_length()]; + rand_bytes(&mut key).unwrap(); + let mut iv = vec![0; cipher.iv_length()]; + rand_bytes(&mut iv).unwrap(); + + let mut ctx = CipherCtx::new().unwrap(); + + ctx.encrypt_init(Some(cipher), Some(&key), Some(&iv)) + .unwrap(); + ctx.set_padding(false); + + let block_size = cipher.block_size(); + assert!(block_size > 1, "Need a block cipher, not a stream cipher"); + + ctx.cipher_update(&vec![0; block_size + 1], Some(&mut vec![0; block_size - 1])) + .unwrap(); + } + + #[cfg(ossl102)] + fn cipher_wrap_test(cipher: &CipherRef, pt: &str, ct: &str, key: &str, iv: Option<&str>) { + let pt = hex::decode(pt).unwrap(); + let key = hex::decode(key).unwrap(); + let expected = hex::decode(ct).unwrap(); + let iv = iv.map(|v| hex::decode(v).unwrap()); + let padding = 8 - pt.len() % 8; + let mut computed = vec![0; pt.len() + padding + cipher.block_size() * 2]; + let mut ctx = CipherCtx::new().unwrap(); + + ctx.set_flags(CipherCtxFlags::FLAG_WRAP_ALLOW); + ctx.encrypt_init(Some(cipher), Some(&key), iv.as_deref()) + .unwrap(); + + let count = ctx.cipher_update(&pt, Some(&mut computed)).unwrap(); + let rest = ctx.cipher_final(&mut computed[count..]).unwrap(); + computed.truncate(count + rest); + + if computed != expected { + println!("Computed: {}", hex::encode(&computed)); + println!("Expected: {}", hex::encode(&expected)); + if computed.len() != expected.len() { + println!( + "Lengths differ: {} in computed vs {} expected", + computed.len(), + expected.len() + ); + } + panic!("test failure"); + } + } + + #[test] + #[cfg(ossl102)] + fn test_aes128_wrap() { + let pt = "00112233445566778899aabbccddeeff"; + let ct = "7940ff694448b5bb5139c959a4896832e55d69aa04daa27e"; + let key = "2b7e151628aed2a6abf7158809cf4f3c"; + let iv = "0001020304050607"; + + cipher_wrap_test(Cipher::aes_128_wrap(), pt, ct, key, Some(iv)); + } + + #[test] + #[cfg(ossl102)] + fn test_aes128_wrap_default_iv() { + let pt = "00112233445566778899aabbccddeeff"; + let ct = "38f1215f0212526f8a70b51955b9fbdc9fe3041d9832306e"; + let key = "2b7e151628aed2a6abf7158809cf4f3c"; + + cipher_wrap_test(Cipher::aes_128_wrap(), pt, ct, key, None); + } + + #[test] + #[cfg(ossl110)] + fn test_aes128_wrap_pad() { + let pt = "00112233445566778899aabbccddee"; + let ct = "f13998f5ab32ef82a1bdbcbe585e1d837385b529572a1e1b"; + let key = "2b7e151628aed2a6abf7158809cf4f3c"; + let iv = "00010203"; + + cipher_wrap_test(Cipher::aes_128_wrap_pad(), pt, ct, key, Some(iv)); + } + + #[test] + #[cfg(ossl110)] + fn test_aes128_wrap_pad_default_iv() { + let pt = "00112233445566778899aabbccddee"; + let ct = "3a501085fb8cf66f4186b7df851914d471ed823411598add"; + let key = "2b7e151628aed2a6abf7158809cf4f3c"; + + cipher_wrap_test(Cipher::aes_128_wrap_pad(), pt, ct, key, None); + } + + #[test] + #[cfg(ossl102)] + fn test_aes192_wrap() { + let pt = "9f6dee187d35302116aecbfd059657efd9f7589c4b5e7f5b"; + let ct = "83b89142dfeeb4871e078bfb81134d33e23fedc19b03a1cf689973d3831b6813"; + let key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b"; + let iv = "0001020304050607"; + + cipher_wrap_test(Cipher::aes_192_wrap(), pt, ct, key, Some(iv)); + } + + #[test] + #[cfg(ossl102)] + fn test_aes192_wrap_default_iv() { + let pt = "9f6dee187d35302116aecbfd059657efd9f7589c4b5e7f5b"; + let ct = "c02c2cf11505d3e4851030d5534cbf5a1d7eca7ba8839adbf239756daf1b43e6"; + let key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b"; + + cipher_wrap_test(Cipher::aes_192_wrap(), pt, ct, key, None); + } + + #[test] + #[cfg(ossl110)] + fn test_aes192_wrap_pad() { + let pt = "00112233445566778899aabbccddee"; + let ct = "b4f6bb167ef7caf061a74da82b36ad038ca057ab51e98d3a"; + let key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b"; + let iv = "00010203"; + + cipher_wrap_test(Cipher::aes_192_wrap_pad(), pt, ct, key, Some(iv)); + } + + #[test] + #[cfg(ossl110)] + fn test_aes192_wrap_pad_default_iv() { + let pt = "00112233445566778899aabbccddee"; + let ct = "b2c37a28cc602753a7c944a4c2555a2df9c98b2eded5312e"; + let key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b"; + + cipher_wrap_test(Cipher::aes_192_wrap_pad(), pt, ct, key, None); + } + + #[test] + #[cfg(ossl102)] + fn test_aes256_wrap() { + let pt = "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51"; + let ct = "cc05da2a7f56f7dd0c144231f90bce58648fa20a8278f5a6b7d13bba6aa57a33229d4333866b7fd6"; + let key = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"; + let iv = "0001020304050607"; + + cipher_wrap_test(Cipher::aes_256_wrap(), pt, ct, key, Some(iv)); + } + + #[test] + #[cfg(ossl102)] + fn test_aes256_wrap_default_iv() { + let pt = "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51"; + let ct = "0b24f068b50e52bc6987868411c36e1b03900866ed12af81eb87cef70a8d1911731c1d7abf789d88"; + let key = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"; + + cipher_wrap_test(Cipher::aes_256_wrap(), pt, ct, key, None); + } + + #[test] + #[cfg(ossl110)] + fn test_aes256_wrap_pad() { + let pt = "00112233445566778899aabbccddee"; + let ct = "91594e044ccc06130d60e6c84a996aa4f96a9faff8c5f6e7"; + let key = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"; + let iv = "00010203"; + + cipher_wrap_test(Cipher::aes_256_wrap_pad(), pt, ct, key, Some(iv)); + } + + #[test] + #[cfg(ossl110)] + fn test_aes256_wrap_pad_default_iv() { + let pt = "00112233445566778899aabbccddee"; + let ct = "dc3c166a854afd68aea624a4272693554bf2e4fcbae602cd"; + let key = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"; + + cipher_wrap_test(Cipher::aes_256_wrap_pad(), pt, ct, key, None); + } +} diff --git a/vendor/openssl/src/cms.rs b/vendor/openssl/src/cms.rs new file mode 100644 index 0000000..d11443b --- /dev/null +++ b/vendor/openssl/src/cms.rs @@ -0,0 +1,488 @@ +//! SMIME implementation using CMS +//! +//! CMS (PKCS#7) is an encryption standard. It allows signing and encrypting data using +//! X.509 certificates. The OpenSSL implementation of CMS is used in email encryption +//! generated from a `Vec` of bytes. This `Vec` follows the smime protocol standards. +//! Data accepted by this module will be smime type `enveloped-data`. + +use bitflags::bitflags; +use foreign_types::{ForeignType, ForeignTypeRef}; +use libc::c_uint; +use std::ptr; + +use crate::bio::{MemBio, MemBioSlice}; +use crate::error::ErrorStack; +use crate::pkey::{HasPrivate, PKeyRef}; +use crate::stack::StackRef; +use crate::symm::Cipher; +use crate::x509::{store::X509StoreRef, X509Ref, X509}; +use crate::{cvt, cvt_p}; +use openssl_macros::corresponds; + +bitflags! { + #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + #[repr(transparent)] + pub struct CMSOptions : c_uint { + const TEXT = ffi::CMS_TEXT; + const CMS_NOCERTS = ffi::CMS_NOCERTS; + const NO_CONTENT_VERIFY = ffi::CMS_NO_CONTENT_VERIFY; + const NO_ATTR_VERIFY = ffi::CMS_NO_ATTR_VERIFY; + const NOSIGS = ffi::CMS_NOSIGS; + const NOINTERN = ffi::CMS_NOINTERN; + const NO_SIGNER_CERT_VERIFY = ffi::CMS_NO_SIGNER_CERT_VERIFY; + const NOVERIFY = ffi::CMS_NOVERIFY; + const DETACHED = ffi::CMS_DETACHED; + const BINARY = ffi::CMS_BINARY; + const NOATTR = ffi::CMS_NOATTR; + const NOSMIMECAP = ffi::CMS_NOSMIMECAP; + const NOOLDMIMETYPE = ffi::CMS_NOOLDMIMETYPE; + const CRLFEOL = ffi::CMS_CRLFEOL; + const STREAM = ffi::CMS_STREAM; + const NOCRL = ffi::CMS_NOCRL; + const PARTIAL = ffi::CMS_PARTIAL; + const REUSE_DIGEST = ffi::CMS_REUSE_DIGEST; + const USE_KEYID = ffi::CMS_USE_KEYID; + const DEBUG_DECRYPT = ffi::CMS_DEBUG_DECRYPT; + #[cfg(all(not(libressl), not(ossl101)))] + const KEY_PARAM = ffi::CMS_KEY_PARAM; + #[cfg(all(not(libressl), not(ossl101), not(ossl102)))] + const ASCIICRLF = ffi::CMS_ASCIICRLF; + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::CMS_ContentInfo; + fn drop = ffi::CMS_ContentInfo_free; + + /// High level CMS wrapper + /// + /// CMS supports nesting various types of data, including signatures, certificates, + /// encrypted data, smime messages (encrypted email), and data digest. The ContentInfo + /// content type is the encapsulation of all those content types. [`RFC 5652`] describes + /// CMS and OpenSSL follows this RFC's implementation. + /// + /// [`RFC 5652`]: https://tools.ietf.org/html/rfc5652#page-6 + pub struct CmsContentInfo; + /// Reference to [`CMSContentInfo`] + /// + /// [`CMSContentInfo`]:struct.CmsContentInfo.html + pub struct CmsContentInfoRef; +} + +impl CmsContentInfoRef { + /// Given the sender's private key, `pkey` and the recipient's certificate, `cert`, + /// decrypt the data in `self`. + #[corresponds(CMS_decrypt)] + pub fn decrypt<T>(&self, pkey: &PKeyRef<T>, cert: &X509) -> Result<Vec<u8>, ErrorStack> + where + T: HasPrivate, + { + unsafe { + let pkey = pkey.as_ptr(); + let cert = cert.as_ptr(); + let out = MemBio::new()?; + + cvt(ffi::CMS_decrypt( + self.as_ptr(), + pkey, + cert, + ptr::null_mut(), + out.as_ptr(), + 0, + ))?; + + Ok(out.get_buf().to_owned()) + } + } + + /// Given the sender's private key, `pkey`, + /// decrypt the data in `self` without validating the recipient certificate. + /// + /// *Warning*: Not checking the recipient certificate may leave you vulnerable to Bleichenbacher's attack on PKCS#1 v1.5 RSA padding. + #[corresponds(CMS_decrypt)] + // FIXME merge into decrypt + pub fn decrypt_without_cert_check<T>(&self, pkey: &PKeyRef<T>) -> Result<Vec<u8>, ErrorStack> + where + T: HasPrivate, + { + unsafe { + let pkey = pkey.as_ptr(); + let out = MemBio::new()?; + + cvt(ffi::CMS_decrypt( + self.as_ptr(), + pkey, + ptr::null_mut(), + ptr::null_mut(), + out.as_ptr(), + 0, + ))?; + + Ok(out.get_buf().to_owned()) + } + } + + to_der! { + /// Serializes this CmsContentInfo using DER. + #[corresponds(i2d_CMS_ContentInfo)] + to_der, + ffi::i2d_CMS_ContentInfo + } + + to_pem! { + /// Serializes this CmsContentInfo using DER. + #[corresponds(PEM_write_bio_CMS)] + to_pem, + ffi::PEM_write_bio_CMS + } +} + +impl CmsContentInfo { + /// Parses a smime formatted `vec` of bytes into a `CmsContentInfo`. + #[corresponds(SMIME_read_CMS)] + pub fn smime_read_cms(smime: &[u8]) -> Result<CmsContentInfo, ErrorStack> { + unsafe { + let bio = MemBioSlice::new(smime)?; + + let cms = cvt_p(ffi::SMIME_read_CMS(bio.as_ptr(), ptr::null_mut()))?; + + Ok(CmsContentInfo::from_ptr(cms)) + } + } + + from_der! { + /// Deserializes a DER-encoded ContentInfo structure. + #[corresponds(d2i_CMS_ContentInfo)] + from_der, + CmsContentInfo, + ffi::d2i_CMS_ContentInfo + } + + from_pem! { + /// Deserializes a PEM-encoded ContentInfo structure. + #[corresponds(PEM_read_bio_CMS)] + from_pem, + CmsContentInfo, + ffi::PEM_read_bio_CMS + } + + /// Given a signing cert `signcert`, private key `pkey`, a certificate stack `certs`, + /// data `data` and flags `flags`, create a CmsContentInfo struct. + /// + /// All arguments are optional. + #[corresponds(CMS_sign)] + pub fn sign<T>( + signcert: Option<&X509Ref>, + pkey: Option<&PKeyRef<T>>, + certs: Option<&StackRef<X509>>, + data: Option<&[u8]>, + flags: CMSOptions, + ) -> Result<CmsContentInfo, ErrorStack> + where + T: HasPrivate, + { + unsafe { + let signcert = signcert.map_or(ptr::null_mut(), |p| p.as_ptr()); + let pkey = pkey.map_or(ptr::null_mut(), |p| p.as_ptr()); + let data_bio = match data { + Some(data) => Some(MemBioSlice::new(data)?), + None => None, + }; + let data_bio_ptr = data_bio.as_ref().map_or(ptr::null_mut(), |p| p.as_ptr()); + let certs = certs.map_or(ptr::null_mut(), |p| p.as_ptr()); + + let cms = cvt_p(ffi::CMS_sign( + signcert, + pkey, + certs, + data_bio_ptr, + flags.bits(), + ))?; + + Ok(CmsContentInfo::from_ptr(cms)) + } + } + + /// Given a certificate stack `certs`, data `data`, cipher `cipher` and flags `flags`, + /// create a CmsContentInfo struct. + /// + /// OpenSSL documentation at [`CMS_encrypt`] + /// + /// [`CMS_encrypt`]: https://www.openssl.org/docs/manmaster/man3/CMS_encrypt.html + #[corresponds(CMS_encrypt)] + pub fn encrypt( + certs: &StackRef<X509>, + data: &[u8], + cipher: Cipher, + flags: CMSOptions, + ) -> Result<CmsContentInfo, ErrorStack> { + unsafe { + let data_bio = MemBioSlice::new(data)?; + + let cms = cvt_p(ffi::CMS_encrypt( + certs.as_ptr(), + data_bio.as_ptr(), + cipher.as_ptr(), + flags.bits(), + ))?; + + Ok(CmsContentInfo::from_ptr(cms)) + } + } + + /// Verify this CmsContentInfo's signature, + /// This will search the 'certs' list for the signing certificate. + /// Additional certificates, needed for building the certificate chain, may be + /// given in 'store' as well as additional CRLs. + /// A detached signature may be passed in `detached_data`. The signed content + /// without signature, will be copied into output_data if it is present. + /// + #[corresponds(CMS_verify)] + pub fn verify( + &mut self, + certs: Option<&StackRef<X509>>, + store: Option<&X509StoreRef>, + detached_data: Option<&[u8]>, + output_data: Option<&mut Vec<u8>>, + flags: CMSOptions, + ) -> Result<(), ErrorStack> { + unsafe { + let certs_ptr = certs.map_or(ptr::null_mut(), |p| p.as_ptr()); + let store_ptr = store.map_or(ptr::null_mut(), |p| p.as_ptr()); + let detached_data_bio = match detached_data { + Some(data) => Some(MemBioSlice::new(data)?), + None => None, + }; + let detached_data_bio_ptr = detached_data_bio + .as_ref() + .map_or(ptr::null_mut(), |p| p.as_ptr()); + let out_bio = MemBio::new()?; + + cvt(ffi::CMS_verify( + self.as_ptr(), + certs_ptr, + store_ptr, + detached_data_bio_ptr, + out_bio.as_ptr(), + flags.bits(), + ))?; + + if let Some(data) = output_data { + data.clear(); + data.extend_from_slice(out_bio.get_buf()); + }; + + Ok(()) + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + use crate::pkcs12::Pkcs12; + use crate::pkey::PKey; + use crate::stack::Stack; + use crate::x509::{ + store::{X509Store, X509StoreBuilder}, + X509, + }; + + #[test] + fn cms_encrypt_decrypt() { + #[cfg(ossl300)] + let _provider = crate::provider::Provider::try_load(None, "legacy", true).unwrap(); + + // load cert with public key only + let pub_cert_bytes = include_bytes!("../test/cms_pubkey.der"); + let pub_cert = X509::from_der(pub_cert_bytes).expect("failed to load pub cert"); + + // load cert with private key + let priv_cert_bytes = include_bytes!("../test/cms.p12"); + let priv_cert = Pkcs12::from_der(priv_cert_bytes).expect("failed to load priv cert"); + let priv_cert = priv_cert + .parse2("mypass") + .expect("failed to parse priv cert"); + + // encrypt cms message using public key cert + let input = String::from("My Message"); + let mut cert_stack = Stack::new().expect("failed to create stack"); + cert_stack + .push(pub_cert) + .expect("failed to add pub cert to stack"); + + let encrypt = CmsContentInfo::encrypt( + &cert_stack, + input.as_bytes(), + Cipher::des_ede3_cbc(), + CMSOptions::empty(), + ) + .expect("failed create encrypted cms"); + + // decrypt cms message using private key cert (DER) + { + let encrypted_der = encrypt.to_der().expect("failed to create der from cms"); + let decrypt = + CmsContentInfo::from_der(&encrypted_der).expect("failed read cms from der"); + + let decrypt_with_cert_check = decrypt + .decrypt( + priv_cert.pkey.as_ref().unwrap(), + priv_cert.cert.as_ref().unwrap(), + ) + .expect("failed to decrypt cms"); + let decrypt_with_cert_check = String::from_utf8(decrypt_with_cert_check) + .expect("failed to create string from cms content"); + + let decrypt_without_cert_check = decrypt + .decrypt_without_cert_check(priv_cert.pkey.as_ref().unwrap()) + .expect("failed to decrypt cms"); + let decrypt_without_cert_check = String::from_utf8(decrypt_without_cert_check) + .expect("failed to create string from cms content"); + + assert_eq!(input, decrypt_with_cert_check); + assert_eq!(input, decrypt_without_cert_check); + } + + // decrypt cms message using private key cert (PEM) + { + let encrypted_pem = encrypt.to_pem().expect("failed to create pem from cms"); + let decrypt = + CmsContentInfo::from_pem(&encrypted_pem).expect("failed read cms from pem"); + + let decrypt_with_cert_check = decrypt + .decrypt( + priv_cert.pkey.as_ref().unwrap(), + priv_cert.cert.as_ref().unwrap(), + ) + .expect("failed to decrypt cms"); + let decrypt_with_cert_check = String::from_utf8(decrypt_with_cert_check) + .expect("failed to create string from cms content"); + + let decrypt_without_cert_check = decrypt + .decrypt_without_cert_check(priv_cert.pkey.as_ref().unwrap()) + .expect("failed to decrypt cms"); + let decrypt_without_cert_check = String::from_utf8(decrypt_without_cert_check) + .expect("failed to create string from cms content"); + + assert_eq!(input, decrypt_with_cert_check); + assert_eq!(input, decrypt_without_cert_check); + } + } + + fn cms_sign_verify_generic_helper(is_detached: bool) { + // load cert with private key + let cert_bytes = include_bytes!("../test/cert.pem"); + let cert = X509::from_pem(cert_bytes).expect("failed to load cert.pem"); + + let key_bytes = include_bytes!("../test/key.pem"); + let key = PKey::private_key_from_pem(key_bytes).expect("failed to load key.pem"); + + let root_bytes = include_bytes!("../test/root-ca.pem"); + let root = X509::from_pem(root_bytes).expect("failed to load root-ca.pem"); + + // sign cms message using public key cert + let data = b"Hello world!"; + + let (opt, ext_data): (CMSOptions, Option<&[u8]>) = if is_detached { + (CMSOptions::DETACHED | CMSOptions::BINARY, Some(data)) + } else { + (CMSOptions::empty(), None) + }; + + let mut cms = CmsContentInfo::sign(Some(&cert), Some(&key), None, Some(data), opt) + .expect("failed to CMS sign a message"); + + // check CMS signature length + let pem_cms = cms + .to_pem() + .expect("failed to pack CmsContentInfo into PEM"); + assert!(!pem_cms.is_empty()); + + // verify CMS signature + let mut builder = X509StoreBuilder::new().expect("failed to create X509StoreBuilder"); + builder + .add_cert(root) + .expect("failed to add root-ca into X509StoreBuilder"); + let store: X509Store = builder.build(); + let mut out_data: Vec<u8> = Vec::new(); + let res = cms.verify( + None, + Some(&store), + ext_data, + Some(&mut out_data), + CMSOptions::empty(), + ); + + // check verification result - valid signature + res.unwrap(); + assert_eq!(data.to_vec(), out_data); + } + + #[test] + fn cms_sign_verify_ok() { + cms_sign_verify_generic_helper(false); + } + + #[test] + fn cms_sign_verify_detached_ok() { + cms_sign_verify_generic_helper(true); + } + + #[test] + fn cms_sign_verify_error() { + #[cfg(ossl300)] + let _provider = crate::provider::Provider::try_load(None, "legacy", true).unwrap(); + + // load cert with private key + let priv_cert_bytes = include_bytes!("../test/cms.p12"); + let priv_cert = Pkcs12::from_der(priv_cert_bytes).expect("failed to load priv cert"); + let priv_cert = priv_cert + .parse2("mypass") + .expect("failed to parse priv cert"); + + // sign cms message using public key cert + let data = b"Hello world!"; + let mut cms = CmsContentInfo::sign( + Some(&priv_cert.cert.unwrap()), + Some(&priv_cert.pkey.unwrap()), + None, + Some(data), + CMSOptions::empty(), + ) + .expect("failed to CMS sign a message"); + + // check CMS signature length + let pem_cms = cms + .to_pem() + .expect("failed to pack CmsContentInfo into PEM"); + assert!(!pem_cms.is_empty()); + + let empty_store = X509StoreBuilder::new() + .expect("failed to create X509StoreBuilder") + .build(); + + // verify CMS signature + let res = cms.verify( + None, + Some(&empty_store), + Some(data), + None, + CMSOptions::empty(), + ); + + // check verification result - this is an invalid signature + // defined in openssl crypto/cms/cms.h + const CMS_R_CERTIFICATE_VERIFY_ERROR: i32 = 100; + match res { + Err(es) => { + let error_array = es.errors(); + assert_eq!(1, error_array.len()); + let code = error_array[0].code(); + assert_eq!(ffi::ERR_GET_REASON(code), CMS_R_CERTIFICATE_VERIFY_ERROR); + } + _ => panic!("expected CMS verification error, got Ok()"), + } + } +} diff --git a/vendor/openssl/src/conf.rs b/vendor/openssl/src/conf.rs new file mode 100644 index 0000000..715519c --- /dev/null +++ b/vendor/openssl/src/conf.rs @@ -0,0 +1,65 @@ +//! Interface for processing OpenSSL configuration files. + +foreign_type_and_impl_send_sync! { + type CType = ffi::CONF; + fn drop = ffi::NCONF_free; + + pub struct Conf; + pub struct ConfRef; +} + +#[cfg(not(boringssl))] +mod methods { + use super::Conf; + use crate::cvt_p; + use crate::error::ErrorStack; + use openssl_macros::corresponds; + + pub struct ConfMethod(*mut ffi::CONF_METHOD); + + impl ConfMethod { + /// Retrieve handle to the default OpenSSL configuration file processing function. + #[corresponds(NCONF_default)] + #[allow(clippy::should_implement_trait)] + pub fn default() -> ConfMethod { + unsafe { + ffi::init(); + // `NCONF` stands for "New Conf", as described in crypto/conf/conf_lib.c. This is + // a newer API than the "CONF classic" functions. + ConfMethod(ffi::NCONF_default()) + } + } + + /// Construct from raw pointer. + /// + /// # Safety + /// + /// The caller must ensure that the pointer is valid. + pub unsafe fn from_ptr(ptr: *mut ffi::CONF_METHOD) -> ConfMethod { + ConfMethod(ptr) + } + + /// Convert to raw pointer. + pub fn as_ptr(&self) -> *mut ffi::CONF_METHOD { + self.0 + } + } + + impl Conf { + /// Create a configuration parser. + /// + /// # Examples + /// + /// ``` + /// use openssl::conf::{Conf, ConfMethod}; + /// + /// let conf = Conf::new(ConfMethod::default()); + /// ``` + #[corresponds(NCONF_new)] + pub fn new(method: ConfMethod) -> Result<Conf, ErrorStack> { + unsafe { cvt_p(ffi::NCONF_new(method.as_ptr())).map(Conf) } + } + } +} +#[cfg(not(boringssl))] +pub use methods::*; diff --git a/vendor/openssl/src/derive.rs b/vendor/openssl/src/derive.rs new file mode 100644 index 0000000..424c5f9 --- /dev/null +++ b/vendor/openssl/src/derive.rs @@ -0,0 +1,217 @@ +//! Shared secret derivation. +//! +//! # Example +//! +//! The following example implements [ECDH] using `NIST P-384` keys: +//! +//! ``` +//! # fn main() -> Result<(), Box<dyn std::error::Error>> { +//! # use std::convert::TryInto; +//! use openssl::bn::BigNumContext; +//! use openssl::pkey::PKey; +//! use openssl::derive::Deriver; +//! use openssl::ec::{EcGroup, EcKey, EcPoint, PointConversionForm}; +//! use openssl::nid::Nid; +//! +//! let group = EcGroup::from_curve_name(Nid::SECP384R1)?; +//! +//! let first: PKey<_> = EcKey::generate(&group)?.try_into()?; +//! +//! // second party generates an ephemeral key and derives +//! // a shared secret using first party's public key +//! let shared_key = EcKey::generate(&group)?; +//! // shared_public is sent to first party +//! let mut ctx = BigNumContext::new()?; +//! let shared_public = shared_key.public_key().to_bytes( +//! &group, +//! PointConversionForm::COMPRESSED, +//! &mut ctx, +//! )?; +//! +//! let shared_key: PKey<_> = shared_key.try_into()?; +//! let mut deriver = Deriver::new(&shared_key)?; +//! deriver.set_peer(&first)?; +//! // secret can be used e.g. as a symmetric encryption key +//! let secret = deriver.derive_to_vec()?; +//! # drop(deriver); +//! +//! // first party derives the same shared secret using +//! // shared_public +//! let point = EcPoint::from_bytes(&group, &shared_public, &mut ctx)?; +//! let recipient_key: PKey<_> = EcKey::from_public_key(&group, &point)?.try_into()?; +//! let mut deriver = Deriver::new(&first)?; +//! deriver.set_peer(&recipient_key)?; +//! let first_secret = deriver.derive_to_vec()?; +//! +//! assert_eq!(secret, first_secret); +//! # Ok(()) } +//! ``` +//! +//! [ECDH]: https://wiki.openssl.org/index.php/Elliptic_Curve_Diffie_Hellman + +use foreign_types::ForeignTypeRef; +use std::marker::PhantomData; +use std::ptr; + +use crate::error::ErrorStack; +use crate::pkey::{HasPrivate, HasPublic, PKeyRef}; +use crate::{cvt, cvt_p}; +use openssl_macros::corresponds; + +/// A type used to derive a shared secret between two keys. +pub struct Deriver<'a>(*mut ffi::EVP_PKEY_CTX, PhantomData<&'a ()>); + +unsafe impl<'a> Sync for Deriver<'a> {} +unsafe impl<'a> Send for Deriver<'a> {} + +#[allow(clippy::len_without_is_empty)] +impl<'a> Deriver<'a> { + /// Creates a new `Deriver` using the provided private key. + /// + /// This corresponds to [`EVP_PKEY_derive_init`]. + /// + /// [`EVP_PKEY_derive_init`]: https://www.openssl.org/docs/manmaster/crypto/EVP_PKEY_derive_init.html + pub fn new<T>(key: &'a PKeyRef<T>) -> Result<Deriver<'a>, ErrorStack> + where + T: HasPrivate, + { + unsafe { + cvt_p(ffi::EVP_PKEY_CTX_new(key.as_ptr(), ptr::null_mut())) + .map(|p| Deriver(p, PhantomData)) + .and_then(|ctx| cvt(ffi::EVP_PKEY_derive_init(ctx.0)).map(|_| ctx)) + } + } + + /// Sets the peer key used for secret derivation. + #[corresponds(EVP_PKEY_derive_set_peer)] + pub fn set_peer<T>(&mut self, key: &'a PKeyRef<T>) -> Result<(), ErrorStack> + where + T: HasPublic, + { + unsafe { cvt(ffi::EVP_PKEY_derive_set_peer(self.0, key.as_ptr())).map(|_| ()) } + } + + /// Sets the peer key used for secret derivation along with optionally validating the peer public key. + /// + /// Requires OpenSSL 3.0.0 or newer. + #[corresponds(EVP_PKEY_derive_set_peer_ex)] + #[cfg(ossl300)] + pub fn set_peer_ex<T>( + &mut self, + key: &'a PKeyRef<T>, + validate_peer: bool, + ) -> Result<(), ErrorStack> + where + T: HasPublic, + { + unsafe { + cvt(ffi::EVP_PKEY_derive_set_peer_ex( + self.0, + key.as_ptr(), + validate_peer as i32, + )) + .map(|_| ()) + } + } + + /// Returns the size of the shared secret. + /// + /// It can be used to size the buffer passed to [`Deriver::derive`]. + /// + /// This corresponds to [`EVP_PKEY_derive`]. + /// + /// [`Deriver::derive`]: #method.derive + /// [`EVP_PKEY_derive`]: https://www.openssl.org/docs/manmaster/crypto/EVP_PKEY_derive_init.html + pub fn len(&mut self) -> Result<usize, ErrorStack> { + unsafe { + let mut len = 0; + cvt(ffi::EVP_PKEY_derive(self.0, ptr::null_mut(), &mut len)).map(|_| len) + } + } + + /// Derives a shared secret between the two keys, writing it into the buffer. + /// + /// Returns the number of bytes written. + /// + /// This corresponds to [`EVP_PKEY_derive`]. + /// + /// [`EVP_PKEY_derive`]: https://www.openssl.org/docs/manmaster/crypto/EVP_PKEY_derive_init.html + pub fn derive(&mut self, buf: &mut [u8]) -> Result<usize, ErrorStack> { + let mut len = buf.len(); + unsafe { + cvt(ffi::EVP_PKEY_derive( + self.0, + buf.as_mut_ptr() as *mut _, + &mut len, + )) + .map(|_| len) + } + } + + /// A convenience function which derives a shared secret and returns it in a new buffer. + /// + /// This simply wraps [`Deriver::len`] and [`Deriver::derive`]. + /// + /// [`Deriver::len`]: #method.len + /// [`Deriver::derive`]: #method.derive + pub fn derive_to_vec(&mut self) -> Result<Vec<u8>, ErrorStack> { + let len = self.len()?; + let mut buf = vec![0; len]; + let len = self.derive(&mut buf)?; + buf.truncate(len); + Ok(buf) + } +} + +impl<'a> Drop for Deriver<'a> { + fn drop(&mut self) { + unsafe { + ffi::EVP_PKEY_CTX_free(self.0); + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + use crate::ec::{EcGroup, EcKey}; + use crate::nid::Nid; + use crate::pkey::PKey; + + #[test] + fn derive_without_peer() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let ec_key = EcKey::generate(&group).unwrap(); + let pkey = PKey::from_ec_key(ec_key).unwrap(); + let mut deriver = Deriver::new(&pkey).unwrap(); + deriver.derive_to_vec().unwrap_err(); + } + + #[test] + fn test_ec_key_derive() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let ec_key = EcKey::generate(&group).unwrap(); + let ec_key2 = EcKey::generate(&group).unwrap(); + let pkey = PKey::from_ec_key(ec_key).unwrap(); + let pkey2 = PKey::from_ec_key(ec_key2).unwrap(); + let mut deriver = Deriver::new(&pkey).unwrap(); + deriver.set_peer(&pkey2).unwrap(); + let shared = deriver.derive_to_vec().unwrap(); + assert!(!shared.is_empty()); + } + + #[test] + #[cfg(ossl300)] + fn test_ec_key_derive_ex() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let ec_key = EcKey::generate(&group).unwrap(); + let ec_key2 = EcKey::generate(&group).unwrap(); + let pkey = PKey::from_ec_key(ec_key).unwrap(); + let pkey2 = PKey::from_ec_key(ec_key2).unwrap(); + let mut deriver = Deriver::new(&pkey).unwrap(); + deriver.set_peer_ex(&pkey2, true).unwrap(); + let shared = deriver.derive_to_vec().unwrap(); + assert!(!shared.is_empty()); + } +} diff --git a/vendor/openssl/src/dh.rs b/vendor/openssl/src/dh.rs new file mode 100644 index 0000000..7445e34 --- /dev/null +++ b/vendor/openssl/src/dh.rs @@ -0,0 +1,480 @@ +//! Diffie-Hellman key agreement. + +use cfg_if::cfg_if; +use foreign_types::{ForeignType, ForeignTypeRef}; +use std::mem; +use std::ptr; + +use crate::bn::{BigNum, BigNumRef}; +use crate::error::ErrorStack; +use crate::pkey::{HasParams, HasPrivate, HasPublic, Params, Private, Public}; +use crate::{cvt, cvt_p}; +use openssl_macros::corresponds; + +generic_foreign_type_and_impl_send_sync! { + type CType = ffi::DH; + fn drop = ffi::DH_free; + + pub struct Dh<T>; + + pub struct DhRef<T>; +} + +impl<T> DhRef<T> +where + T: HasParams, +{ + to_pem! { + /// Serializes the parameters into a PEM-encoded PKCS#3 DHparameter structure. + /// + /// The output will have a header of `-----BEGIN DH PARAMETERS-----`. + #[corresponds(PEM_write_bio_DHparams)] + params_to_pem, + ffi::PEM_write_bio_DHparams + } + + to_der! { + /// Serializes the parameters into a DER-encoded PKCS#3 DHparameter structure. + #[corresponds(i2d_DHparams)] + params_to_der, + ffi::i2d_DHparams + } + + /// Validates DH parameters for correctness + #[corresponds(DH_check_key)] + pub fn check_key(&self) -> Result<bool, ErrorStack> { + unsafe { + let mut codes = 0; + cvt(ffi::DH_check(self.as_ptr(), &mut codes))?; + Ok(codes == 0) + } + } +} + +impl Dh<Params> { + pub fn from_params(p: BigNum, g: BigNum, q: BigNum) -> Result<Dh<Params>, ErrorStack> { + Self::from_pqg(p, Some(q), g) + } + + /// Creates a DH instance based upon the given primes and generator params. + #[corresponds(DH_set0_pqg)] + pub fn from_pqg( + prime_p: BigNum, + prime_q: Option<BigNum>, + generator: BigNum, + ) -> Result<Dh<Params>, ErrorStack> { + unsafe { + let dh = Dh::from_ptr(cvt_p(ffi::DH_new())?); + cvt(DH_set0_pqg( + dh.0, + prime_p.as_ptr(), + prime_q.as_ref().map_or(ptr::null_mut(), |q| q.as_ptr()), + generator.as_ptr(), + ))?; + mem::forget((prime_p, prime_q, generator)); + Ok(dh) + } + } + + /// Sets the public key on the DH object. + pub fn set_public_key(self, pub_key: BigNum) -> Result<Dh<Public>, ErrorStack> { + unsafe { + let dh_ptr = self.0; + cvt(DH_set0_key(dh_ptr, pub_key.as_ptr(), ptr::null_mut()))?; + mem::forget((self, pub_key)); + Ok(Dh::from_ptr(dh_ptr)) + } + } + + /// Sets the private key on the DH object and recomputes the public key. + pub fn set_private_key(self, priv_key: BigNum) -> Result<Dh<Private>, ErrorStack> { + unsafe { + let dh_ptr = self.0; + cvt(DH_set0_key(dh_ptr, ptr::null_mut(), priv_key.as_ptr()))?; + mem::forget(priv_key); + + cvt(ffi::DH_generate_key(dh_ptr))?; + mem::forget(self); + Ok(Dh::from_ptr(dh_ptr)) + } + } + + /// Sets the public and private keys on the DH object. + pub fn set_key(self, pub_key: BigNum, priv_key: BigNum) -> Result<Dh<Private>, ErrorStack> { + unsafe { + let dh_ptr = self.0; + cvt(DH_set0_key(dh_ptr, pub_key.as_ptr(), priv_key.as_ptr()))?; + mem::forget((self, pub_key, priv_key)); + Ok(Dh::from_ptr(dh_ptr)) + } + } + + /// Generates DH params based on the given `prime_len` and a fixed `generator` value. + #[corresponds(DH_generate_parameters_ex)] + pub fn generate_params(prime_len: u32, generator: u32) -> Result<Dh<Params>, ErrorStack> { + unsafe { + let dh = Dh::from_ptr(cvt_p(ffi::DH_new())?); + cvt(ffi::DH_generate_parameters_ex( + dh.0, + prime_len as i32, + generator as i32, + ptr::null_mut(), + ))?; + Ok(dh) + } + } + + /// Generates a public and a private key based on the DH params. + #[corresponds(DH_generate_key)] + pub fn generate_key(self) -> Result<Dh<Private>, ErrorStack> { + unsafe { + let dh_ptr = self.0; + cvt(ffi::DH_generate_key(dh_ptr))?; + mem::forget(self); + Ok(Dh::from_ptr(dh_ptr)) + } + } + + from_pem! { + /// Deserializes a PEM-encoded PKCS#3 DHpararameters structure. + /// + /// The input should have a header of `-----BEGIN DH PARAMETERS-----`. + #[corresponds(PEM_read_bio_DHparams)] + params_from_pem, + Dh<Params>, + ffi::PEM_read_bio_DHparams + } + + from_der! { + /// Deserializes a DER-encoded PKCS#3 DHparameters structure. + #[corresponds(d2i_DHparams)] + params_from_der, + Dh<Params>, + ffi::d2i_DHparams + } + + /// Requires OpenSSL 1.0.2 or newer. + #[corresponds(DH_get_1024_160)] + #[cfg(any(ossl102, ossl110))] + pub fn get_1024_160() -> Result<Dh<Params>, ErrorStack> { + unsafe { + ffi::init(); + cvt_p(ffi::DH_get_1024_160()).map(|p| Dh::from_ptr(p)) + } + } + + /// Requires OpenSSL 1.0.2 or newer. + #[corresponds(DH_get_2048_224)] + #[cfg(any(ossl102, ossl110))] + pub fn get_2048_224() -> Result<Dh<Params>, ErrorStack> { + unsafe { + ffi::init(); + cvt_p(ffi::DH_get_2048_224()).map(|p| Dh::from_ptr(p)) + } + } + + /// Requires OpenSSL 1.0.2 or newer. + #[corresponds(DH_get_2048_256)] + #[cfg(any(ossl102, ossl110))] + pub fn get_2048_256() -> Result<Dh<Params>, ErrorStack> { + unsafe { + ffi::init(); + cvt_p(ffi::DH_get_2048_256()).map(|p| Dh::from_ptr(p)) + } + } +} + +impl<T> Dh<T> +where + T: HasParams, +{ + /// Returns the prime `p` from the DH instance. + #[corresponds(DH_get0_pqg)] + pub fn prime_p(&self) -> &BigNumRef { + let mut p = ptr::null(); + unsafe { + DH_get0_pqg(self.as_ptr(), &mut p, ptr::null_mut(), ptr::null_mut()); + BigNumRef::from_ptr(p as *mut _) + } + } + + /// Returns the prime `q` from the DH instance. + #[corresponds(DH_get0_pqg)] + pub fn prime_q(&self) -> Option<&BigNumRef> { + let mut q = ptr::null(); + unsafe { + DH_get0_pqg(self.as_ptr(), ptr::null_mut(), &mut q, ptr::null_mut()); + if q.is_null() { + None + } else { + Some(BigNumRef::from_ptr(q as *mut _)) + } + } + } + + /// Returns the generator from the DH instance. + #[corresponds(DH_get0_pqg)] + pub fn generator(&self) -> &BigNumRef { + let mut g = ptr::null(); + unsafe { + DH_get0_pqg(self.as_ptr(), ptr::null_mut(), ptr::null_mut(), &mut g); + BigNumRef::from_ptr(g as *mut _) + } + } +} + +impl<T> DhRef<T> +where + T: HasPublic, +{ + /// Returns the public key from the DH instance. + #[corresponds(DH_get0_key)] + pub fn public_key(&self) -> &BigNumRef { + let mut pub_key = ptr::null(); + unsafe { + DH_get0_key(self.as_ptr(), &mut pub_key, ptr::null_mut()); + BigNumRef::from_ptr(pub_key as *mut _) + } + } +} + +impl<T> DhRef<T> +where + T: HasPrivate, +{ + /// Computes a shared secret from the own private key and the given `public_key`. + #[corresponds(DH_compute_key)] + pub fn compute_key(&self, public_key: &BigNumRef) -> Result<Vec<u8>, ErrorStack> { + unsafe { + let key_len = ffi::DH_size(self.as_ptr()); + let mut key = vec![0u8; key_len as usize]; + cvt(ffi::DH_compute_key( + key.as_mut_ptr(), + public_key.as_ptr(), + self.as_ptr(), + ))?; + Ok(key) + } + } + + /// Returns the private key from the DH instance. + #[corresponds(DH_get0_key)] + pub fn private_key(&self) -> &BigNumRef { + let mut priv_key = ptr::null(); + unsafe { + DH_get0_key(self.as_ptr(), ptr::null_mut(), &mut priv_key); + BigNumRef::from_ptr(priv_key as *mut _) + } + } +} + +cfg_if! { + if #[cfg(any(ossl110, libressl270, boringssl))] { + use ffi::{DH_set0_pqg, DH_get0_pqg, DH_get0_key, DH_set0_key}; + } else { + #[allow(bad_style)] + unsafe fn DH_set0_pqg( + dh: *mut ffi::DH, + p: *mut ffi::BIGNUM, + q: *mut ffi::BIGNUM, + g: *mut ffi::BIGNUM, + ) -> ::libc::c_int { + (*dh).p = p; + (*dh).q = q; + (*dh).g = g; + 1 + } + + #[allow(bad_style)] + unsafe fn DH_get0_pqg( + dh: *mut ffi::DH, + p: *mut *const ffi::BIGNUM, + q: *mut *const ffi::BIGNUM, + g: *mut *const ffi::BIGNUM, + ) { + if !p.is_null() { + *p = (*dh).p; + } + if !q.is_null() { + *q = (*dh).q; + } + if !g.is_null() { + *g = (*dh).g; + } + } + + #[allow(bad_style)] + unsafe fn DH_set0_key( + dh: *mut ffi::DH, + pub_key: *mut ffi::BIGNUM, + priv_key: *mut ffi::BIGNUM, + ) -> ::libc::c_int { + (*dh).pub_key = pub_key; + (*dh).priv_key = priv_key; + 1 + } + + #[allow(bad_style)] + unsafe fn DH_get0_key( + dh: *mut ffi::DH, + pub_key: *mut *const ffi::BIGNUM, + priv_key: *mut *const ffi::BIGNUM, + ) { + if !pub_key.is_null() { + *pub_key = (*dh).pub_key; + } + if !priv_key.is_null() { + *priv_key = (*dh).priv_key; + } + } + } +} + +#[cfg(test)] +mod tests { + use crate::bn::BigNum; + use crate::dh::Dh; + use crate::ssl::{SslContext, SslMethod}; + + #[test] + #[cfg(ossl102)] + fn test_dh_rfc5114() { + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + let dh2 = Dh::get_2048_224().unwrap(); + ctx.set_tmp_dh(&dh2).unwrap(); + let dh3 = Dh::get_2048_256().unwrap(); + ctx.set_tmp_dh(&dh3).unwrap(); + } + + #[test] + fn test_dh_params() { + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + let prime_p = BigNum::from_hex_str( + "87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F25D2CEED4435E3B00E00DF8F1D61957D4FAF7DF\ + 4561B2AA3016C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD5A8A9D306BCF67ED91F9E6725B47\ + 58C022E0B1EF4275BF7B6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C4FDB70C581B23F76B6\ + 3ACAE1CAA6B7902D52526735488A0EF13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D967E144E5\ + 140564251CCACB83E6B486F6B3CA3F7971506026C0B857F689962856DED4010ABD0BE621C3A3960A54E710\ + C375F26375D7014103A4B54330C198AF126116D2276E11715F693877FAD7EF09CADB094AE91E1A1597", + ).unwrap(); + let prime_q = BigNum::from_hex_str( + "3FB32C9B73134D0B2E77506660EDBD484CA7B18F21EF205407F4793A1A0BA12510DBC15077BE463FFF4FED\ + 4AAC0BB555BE3A6C1B0C6B47B1BC3773BF7E8C6F62901228F8C28CBB18A55AE31341000A650196F931C77A\ + 57F2DDF463E5E9EC144B777DE62AAAB8A8628AC376D282D6ED3864E67982428EBC831D14348F6F2F9193B5\ + 045AF2767164E1DFC967C1FB3F2E55A4BD1BFFE83B9C80D052B985D182EA0ADB2A3B7313D3FE14C8484B1E\ + 052588B9B7D2BBD2DF016199ECD06E1557CD0915B3353BBB64E0EC377FD028370DF92B52C7891428CDC67E\ + B6184B523D1DB246C32F63078490F00EF8D647D148D47954515E2327CFEF98C582664B4C0F6CC41659", + ).unwrap(); + let generator = BigNum::from_hex_str( + "8CF83642A709A097B447997640129DA299B1A47D1EB3750BA308B0FE64F5FBD3", + ) + .unwrap(); + let dh = Dh::from_params( + prime_p.to_owned().unwrap(), + generator.to_owned().unwrap(), + prime_q.to_owned().unwrap(), + ) + .unwrap(); + ctx.set_tmp_dh(&dh).unwrap(); + + assert_eq!(dh.prime_p(), &prime_p); + assert_eq!(dh.prime_q().unwrap(), &prime_q); + assert_eq!(dh.generator(), &generator); + } + + #[test] + #[cfg(ossl102)] + fn test_dh_stored_restored() { + let dh1 = Dh::get_2048_256().unwrap(); + let key1 = dh1.generate_key().unwrap(); + + let dh2 = Dh::get_2048_256().unwrap(); + let key2 = dh2 + .set_private_key(key1.private_key().to_owned().unwrap()) + .unwrap(); + + assert_eq!(key1.public_key(), key2.public_key()); + assert_eq!(key1.private_key(), key2.private_key()); + } + + #[test] + #[cfg(ossl102)] + fn test_set_keys() { + let dh1 = Dh::get_2048_256().unwrap(); + let key1 = dh1.generate_key().unwrap(); + + let dh2 = Dh::get_2048_256().unwrap(); + let key2 = dh2 + .set_public_key(key1.public_key().to_owned().unwrap()) + .unwrap(); + + assert_eq!(key1.public_key(), key2.public_key()); + + let dh3 = Dh::get_2048_256().unwrap(); + let key3 = dh3 + .set_key( + key1.public_key().to_owned().unwrap(), + key1.private_key().to_owned().unwrap(), + ) + .unwrap(); + assert_eq!(key1.public_key(), key3.public_key()); + assert_eq!(key1.private_key(), key3.private_key()); + } + + #[test] + fn test_dh_from_pem() { + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + let params = include_bytes!("../test/dhparams.pem"); + let dh = Dh::params_from_pem(params).unwrap(); + ctx.set_tmp_dh(&dh).unwrap(); + } + + #[test] + fn test_dh_from_der() { + let params = include_bytes!("../test/dhparams.pem"); + let dh = Dh::params_from_pem(params).unwrap(); + let der = dh.params_to_der().unwrap(); + Dh::params_from_der(&der).unwrap(); + } + + #[test] + #[cfg(ossl102)] + fn test_dh_generate_key_compute_key() { + let dh1 = Dh::get_2048_224().unwrap().generate_key().unwrap(); + let dh2 = Dh::get_2048_224().unwrap().generate_key().unwrap(); + + let shared_a = dh1.compute_key(dh2.public_key()).unwrap(); + let shared_b = dh2.compute_key(dh1.public_key()).unwrap(); + + assert_eq!(shared_a, shared_b); + } + + #[test] + fn test_dh_generate_params_generate_key_compute_key() { + let dh_params1 = Dh::generate_params(512, 2).unwrap(); + let dh_params2 = Dh::from_pqg( + dh_params1.prime_p().to_owned().unwrap(), + None, + dh_params1.generator().to_owned().unwrap(), + ) + .unwrap(); + + let dh1 = dh_params1.generate_key().unwrap(); + let dh2 = dh_params2.generate_key().unwrap(); + + let shared_a = dh1.compute_key(dh2.public_key()).unwrap(); + let shared_b = dh2.compute_key(dh1.public_key()).unwrap(); + + assert_eq!(shared_a, shared_b); + } + + #[test] + fn test_dh_check_key() { + let dh1 = Dh::generate_params(512, 2).unwrap(); + let p = BigNum::from_hex_str("04").unwrap(); + let g = BigNum::from_hex_str("02").unwrap(); + let dh2 = Dh::from_pqg(p, None, g).unwrap(); + assert!(dh1.check_key().unwrap()); + assert!(!dh2.check_key().unwrap()); + } +} diff --git a/vendor/openssl/src/dsa.rs b/vendor/openssl/src/dsa.rs new file mode 100644 index 0000000..1a63e8a --- /dev/null +++ b/vendor/openssl/src/dsa.rs @@ -0,0 +1,697 @@ +//! Digital Signatures +//! +//! DSA ensures a message originated from a known sender, and was not modified. +//! DSA uses asymmetrical keys and an algorithm to output a signature of the message +//! using the private key that can be validated with the public key but not be generated +//! without the private key. + +use cfg_if::cfg_if; +use foreign_types::{ForeignType, ForeignTypeRef}; +#[cfg(not(boringssl))] +use libc::c_int; +use std::fmt; +use std::mem; +use std::ptr; + +use crate::bn::{BigNum, BigNumRef}; +use crate::error::ErrorStack; +use crate::pkey::{HasParams, HasPrivate, HasPublic, Params, Private, Public}; +use crate::util::ForeignTypeRefExt; +use crate::{cvt, cvt_p}; +use openssl_macros::corresponds; + +generic_foreign_type_and_impl_send_sync! { + type CType = ffi::DSA; + fn drop = ffi::DSA_free; + + /// Object representing DSA keys. + /// + /// A DSA object contains the parameters p, q, and g. There is a private + /// and public key. The values p, g, and q are: + /// + /// * `p`: DSA prime parameter + /// * `q`: DSA sub-prime parameter + /// * `g`: DSA base parameter + /// + /// These values are used to calculate a pair of asymmetrical keys used for + /// signing. + /// + /// OpenSSL documentation at [`DSA_new`] + /// + /// [`DSA_new`]: https://www.openssl.org/docs/manmaster/crypto/DSA_new.html + /// + /// # Examples + /// + /// ``` + /// use openssl::dsa::Dsa; + /// use openssl::error::ErrorStack; + /// use openssl::pkey::Private; + /// + /// fn create_dsa() -> Result<Dsa<Private>, ErrorStack> { + /// let sign = Dsa::generate(2048)?; + /// Ok(sign) + /// } + /// # fn main() { + /// # create_dsa(); + /// # } + /// ``` + pub struct Dsa<T>; + /// Reference to [`Dsa`]. + /// + /// [`Dsa`]: struct.Dsa.html + pub struct DsaRef<T>; +} + +impl<T> Clone for Dsa<T> { + fn clone(&self) -> Dsa<T> { + (**self).to_owned() + } +} + +impl<T> ToOwned for DsaRef<T> { + type Owned = Dsa<T>; + + fn to_owned(&self) -> Dsa<T> { + unsafe { + ffi::DSA_up_ref(self.as_ptr()); + Dsa::from_ptr(self.as_ptr()) + } + } +} + +impl<T> DsaRef<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_DSA_PUBKEY)] + public_key_to_pem, + ffi::PEM_write_bio_DSA_PUBKEY + } + + to_der! { + /// Serializes the public key into a DER-encoded SubjectPublicKeyInfo structure. + #[corresponds(i2d_DSA_PUBKEY)] + public_key_to_der, + ffi::i2d_DSA_PUBKEY + } + + /// Returns a reference to the public key component of `self`. + #[corresponds(DSA_get0_key)] + pub fn pub_key(&self) -> &BigNumRef { + unsafe { + let mut pub_key = ptr::null(); + DSA_get0_key(self.as_ptr(), &mut pub_key, ptr::null_mut()); + BigNumRef::from_const_ptr(pub_key) + } + } +} + +impl<T> DsaRef<T> +where + T: HasPrivate, +{ + private_key_to_pem! { + /// Serializes the private key to a PEM-encoded DSAPrivateKey structure. + /// + /// The output will have a header of `-----BEGIN DSA PRIVATE KEY-----`. + #[corresponds(PEM_write_bio_DSAPrivateKey)] + private_key_to_pem, + /// Serializes the private key to a PEM-encoded encrypted DSAPrivateKey structure. + /// + /// The output will have a header of `-----BEGIN DSA PRIVATE KEY-----`. + #[corresponds(PEM_write_bio_DSAPrivateKey)] + private_key_to_pem_passphrase, + ffi::PEM_write_bio_DSAPrivateKey + } + + to_der! { + /// Serializes the private_key to a DER-encoded `DSAPrivateKey` structure. + #[corresponds(i2d_DSAPrivateKey)] + private_key_to_der, + ffi::i2d_DSAPrivateKey + } + + /// Returns a reference to the private key component of `self`. + #[corresponds(DSA_get0_key)] + pub fn priv_key(&self) -> &BigNumRef { + unsafe { + let mut priv_key = ptr::null(); + DSA_get0_key(self.as_ptr(), ptr::null_mut(), &mut priv_key); + BigNumRef::from_const_ptr(priv_key) + } + } +} + +impl<T> DsaRef<T> +where + T: HasParams, +{ + /// Returns the maximum size of the signature output by `self` in bytes. + #[corresponds(DSA_size)] + pub fn size(&self) -> u32 { + unsafe { ffi::DSA_size(self.as_ptr()) as u32 } + } + + /// Returns the DSA prime parameter of `self`. + #[corresponds(DSA_get0_pqg)] + pub fn p(&self) -> &BigNumRef { + unsafe { + let mut p = ptr::null(); + DSA_get0_pqg(self.as_ptr(), &mut p, ptr::null_mut(), ptr::null_mut()); + BigNumRef::from_const_ptr(p) + } + } + + /// Returns the DSA sub-prime parameter of `self`. + #[corresponds(DSA_get0_pqg)] + pub fn q(&self) -> &BigNumRef { + unsafe { + let mut q = ptr::null(); + DSA_get0_pqg(self.as_ptr(), ptr::null_mut(), &mut q, ptr::null_mut()); + BigNumRef::from_const_ptr(q) + } + } + + /// Returns the DSA base parameter of `self`. + #[corresponds(DSA_get0_pqg)] + pub fn g(&self) -> &BigNumRef { + unsafe { + let mut g = ptr::null(); + DSA_get0_pqg(self.as_ptr(), ptr::null_mut(), ptr::null_mut(), &mut g); + BigNumRef::from_const_ptr(g) + } + } +} +#[cfg(boringssl)] +type BitType = libc::c_uint; +#[cfg(not(boringssl))] +type BitType = c_int; + +impl Dsa<Params> { + /// Creates a DSA params based upon the given parameters. + #[corresponds(DSA_set0_pqg)] + pub fn from_pqg(p: BigNum, q: BigNum, g: BigNum) -> Result<Dsa<Params>, ErrorStack> { + unsafe { + let dsa = Dsa::from_ptr(cvt_p(ffi::DSA_new())?); + cvt(DSA_set0_pqg(dsa.0, p.as_ptr(), q.as_ptr(), g.as_ptr()))?; + mem::forget((p, q, g)); + Ok(dsa) + } + } + + /// Generates DSA params based on the given number of bits. + #[corresponds(DSA_generate_parameters_ex)] + pub fn generate_params(bits: u32) -> Result<Dsa<Params>, ErrorStack> { + ffi::init(); + unsafe { + let dsa = Dsa::from_ptr(cvt_p(ffi::DSA_new())?); + cvt(ffi::DSA_generate_parameters_ex( + dsa.0, + bits as BitType, + ptr::null(), + 0, + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + ))?; + Ok(dsa) + } + } + + /// Generates a private key based on the DSA params. + #[corresponds(DSA_generate_key)] + pub fn generate_key(self) -> Result<Dsa<Private>, ErrorStack> { + unsafe { + let dsa_ptr = self.0; + cvt(ffi::DSA_generate_key(dsa_ptr))?; + mem::forget(self); + Ok(Dsa::from_ptr(dsa_ptr)) + } + } +} + +impl Dsa<Private> { + /// Generate a DSA key pair. + /// + /// The `bits` parameter corresponds to the length of the prime `p`. + pub fn generate(bits: u32) -> Result<Dsa<Private>, ErrorStack> { + let params = Dsa::generate_params(bits)?; + params.generate_key() + } + + /// Create a DSA key pair with the given parameters + /// + /// `p`, `q` and `g` are the common parameters. + /// `priv_key` is the private component of the key pair. + /// `pub_key` is the public component of the key. Can be computed via `g^(priv_key) mod p` + pub fn from_private_components( + p: BigNum, + q: BigNum, + g: BigNum, + priv_key: BigNum, + pub_key: BigNum, + ) -> Result<Dsa<Private>, ErrorStack> { + ffi::init(); + unsafe { + let dsa = Dsa::from_ptr(cvt_p(ffi::DSA_new())?); + cvt(DSA_set0_pqg(dsa.0, p.as_ptr(), q.as_ptr(), g.as_ptr()))?; + mem::forget((p, q, g)); + cvt(DSA_set0_key(dsa.0, pub_key.as_ptr(), priv_key.as_ptr()))?; + mem::forget((pub_key, priv_key)); + Ok(dsa) + } + } +} + +impl Dsa<Public> { + from_pem! { + /// Decodes a PEM-encoded SubjectPublicKeyInfo structure containing a DSA key. + /// + /// The input should have a header of `-----BEGIN PUBLIC KEY-----`. + #[corresponds(PEM_read_bio_DSA_PUBKEY)] + public_key_from_pem, + Dsa<Public>, + ffi::PEM_read_bio_DSA_PUBKEY + } + + from_der! { + /// Decodes a DER-encoded SubjectPublicKeyInfo structure containing a DSA key. + #[corresponds(d2i_DSA_PUBKEY)] + public_key_from_der, + Dsa<Public>, + ffi::d2i_DSA_PUBKEY + } + + /// Create a new DSA key with only public components. + /// + /// `p`, `q` and `g` are the common parameters. + /// `pub_key` is the public component of the key. + pub fn from_public_components( + p: BigNum, + q: BigNum, + g: BigNum, + pub_key: BigNum, + ) -> Result<Dsa<Public>, ErrorStack> { + ffi::init(); + unsafe { + let dsa = Dsa::from_ptr(cvt_p(ffi::DSA_new())?); + cvt(DSA_set0_pqg(dsa.0, p.as_ptr(), q.as_ptr(), g.as_ptr()))?; + mem::forget((p, q, g)); + cvt(DSA_set0_key(dsa.0, pub_key.as_ptr(), ptr::null_mut()))?; + mem::forget(pub_key); + Ok(dsa) + } + } +} + +impl<T> fmt::Debug for Dsa<T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "DSA") + } +} + +cfg_if! { + if #[cfg(any(ossl110, libressl273, boringssl))] { + use ffi::{DSA_get0_key, DSA_get0_pqg, DSA_set0_key, DSA_set0_pqg}; + } else { + #[allow(bad_style)] + unsafe fn DSA_get0_pqg( + d: *mut ffi::DSA, + p: *mut *const ffi::BIGNUM, + q: *mut *const ffi::BIGNUM, + g: *mut *const ffi::BIGNUM) + { + if !p.is_null() { + *p = (*d).p; + } + if !q.is_null() { + *q = (*d).q; + } + if !g.is_null() { + *g = (*d).g; + } + } + + #[allow(bad_style)] + unsafe fn DSA_get0_key( + d: *mut ffi::DSA, + pub_key: *mut *const ffi::BIGNUM, + priv_key: *mut *const ffi::BIGNUM) + { + if !pub_key.is_null() { + *pub_key = (*d).pub_key; + } + if !priv_key.is_null() { + *priv_key = (*d).priv_key; + } + } + + #[allow(bad_style)] + unsafe fn DSA_set0_key( + d: *mut ffi::DSA, + pub_key: *mut ffi::BIGNUM, + priv_key: *mut ffi::BIGNUM) -> c_int + { + (*d).pub_key = pub_key; + (*d).priv_key = priv_key; + 1 + } + + #[allow(bad_style)] + unsafe fn DSA_set0_pqg( + d: *mut ffi::DSA, + p: *mut ffi::BIGNUM, + q: *mut ffi::BIGNUM, + g: *mut ffi::BIGNUM) -> c_int + { + (*d).p = p; + (*d).q = q; + (*d).g = g; + 1 + } + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::DSA_SIG; + fn drop = ffi::DSA_SIG_free; + + /// Object representing DSA signature. + /// + /// DSA signatures consist of two components: `r` and `s`. + /// + /// # Examples + /// + /// ``` + /// use std::convert::TryInto; + /// + /// use openssl::bn::BigNum; + /// use openssl::dsa::{Dsa, DsaSig}; + /// use openssl::hash::MessageDigest; + /// use openssl::pkey::PKey; + /// use openssl::sign::{Signer, Verifier}; + /// + /// const TEST_DATA: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + /// let dsa_ref = Dsa::generate(1024).unwrap(); + /// + /// let pub_key: PKey<_> = dsa_ref.clone().try_into().unwrap(); + /// let priv_key: PKey<_> = dsa_ref.try_into().unwrap(); + /// + /// let mut signer = if let Ok(signer) = Signer::new(MessageDigest::sha256(), &priv_key) { + /// signer + /// } else { + /// // DSA signing is not supported (eg. BoringSSL) + /// return; + /// }; + /// + /// signer.update(TEST_DATA).unwrap(); + /// + /// let signature = signer.sign_to_vec().unwrap(); + /// // Parse DER-encoded DSA signature + /// let signature = DsaSig::from_der(&signature).unwrap(); + /// + /// // Extract components `r` and `s` + /// let r = BigNum::from_slice(&signature.r().to_vec()).unwrap(); + /// let s = BigNum::from_slice(&signature.s().to_vec()).unwrap(); + /// + /// // Construct new DSA signature from components + /// let signature = DsaSig::from_private_components(r, s).unwrap(); + /// + /// // Serialize DSA signature to DER + /// let signature = signature.to_der().unwrap(); + /// + /// let mut verifier = Verifier::new(MessageDigest::sha256(), &pub_key).unwrap(); + /// verifier.update(TEST_DATA).unwrap(); + /// assert!(verifier.verify(&signature[..]).unwrap()); + /// ``` + pub struct DsaSig; + + /// Reference to a [`DsaSig`]. + pub struct DsaSigRef; +} + +impl DsaSig { + /// Returns a new `DsaSig` by setting the `r` and `s` values associated with an DSA signature. + #[corresponds(DSA_SIG_set0)] + pub fn from_private_components(r: BigNum, s: BigNum) -> Result<Self, ErrorStack> { + unsafe { + let sig = cvt_p(ffi::DSA_SIG_new())?; + DSA_SIG_set0(sig, r.as_ptr(), s.as_ptr()); + mem::forget((r, s)); + Ok(DsaSig::from_ptr(sig)) + } + } + + from_der! { + /// Decodes a DER-encoded DSA signature. + #[corresponds(d2i_DSA_SIG)] + from_der, + DsaSig, + ffi::d2i_DSA_SIG + } +} + +impl fmt::Debug for DsaSig { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("DsaSig") + .field("r", self.r()) + .field("s", self.s()) + .finish() + } +} + +impl DsaSigRef { + to_der! { + /// Serializes the DSA signature into a DER-encoded `DSASignature` structure. + #[corresponds(i2d_DSA_SIG)] + to_der, + ffi::i2d_DSA_SIG + } + + /// Returns internal component `r` of an `DsaSig`. + #[corresponds(DSA_SIG_get0)] + pub fn r(&self) -> &BigNumRef { + unsafe { + let mut r = ptr::null(); + DSA_SIG_get0(self.as_ptr(), &mut r, ptr::null_mut()); + BigNumRef::from_const_ptr(r) + } + } + + /// Returns internal component `s` of an `DsaSig`. + #[corresponds(DSA_SIG_get0)] + pub fn s(&self) -> &BigNumRef { + unsafe { + let mut s = ptr::null(); + DSA_SIG_get0(self.as_ptr(), ptr::null_mut(), &mut s); + BigNumRef::from_const_ptr(s) + } + } +} + +cfg_if! { + if #[cfg(any(ossl110, libressl273, boringssl))] { + use ffi::{DSA_SIG_set0, DSA_SIG_get0}; + } else { + #[allow(bad_style)] + unsafe fn DSA_SIG_set0( + sig: *mut ffi::DSA_SIG, + r: *mut ffi::BIGNUM, + s: *mut ffi::BIGNUM, + ) -> c_int { + if r.is_null() || s.is_null() { + return 0; + } + ffi::BN_clear_free((*sig).r); + ffi::BN_clear_free((*sig).s); + (*sig).r = r; + (*sig).s = s; + 1 + } + + #[allow(bad_style)] + unsafe fn DSA_SIG_get0( + sig: *const ffi::DSA_SIG, + pr: *mut *const ffi::BIGNUM, + ps: *mut *const ffi::BIGNUM) + { + if !pr.is_null() { + (*pr) = (*sig).r; + } + if !ps.is_null() { + (*ps) = (*sig).s; + } + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::bn::BigNumContext; + #[cfg(not(boringssl))] + use crate::hash::MessageDigest; + #[cfg(not(boringssl))] + use crate::pkey::PKey; + #[cfg(not(boringssl))] + use crate::sign::{Signer, Verifier}; + + #[test] + pub fn test_generate() { + Dsa::generate(1024).unwrap(); + } + + #[test] + fn test_pubkey_generation() { + let dsa = Dsa::generate(1024).unwrap(); + let p = dsa.p(); + let g = dsa.g(); + let priv_key = dsa.priv_key(); + let pub_key = dsa.pub_key(); + let mut ctx = BigNumContext::new().unwrap(); + let mut calc = BigNum::new().unwrap(); + calc.mod_exp(g, priv_key, p, &mut ctx).unwrap(); + assert_eq!(&calc, pub_key) + } + + #[test] + fn test_priv_key_from_parts() { + let p = BigNum::from_u32(283).unwrap(); + let q = BigNum::from_u32(47).unwrap(); + let g = BigNum::from_u32(60).unwrap(); + let priv_key = BigNum::from_u32(15).unwrap(); + let pub_key = BigNum::from_u32(207).unwrap(); + + let dsa = Dsa::from_private_components(p, q, g, priv_key, pub_key).unwrap(); + assert_eq!(dsa.pub_key(), &BigNum::from_u32(207).unwrap()); + assert_eq!(dsa.priv_key(), &BigNum::from_u32(15).unwrap()); + assert_eq!(dsa.p(), &BigNum::from_u32(283).unwrap()); + assert_eq!(dsa.q(), &BigNum::from_u32(47).unwrap()); + assert_eq!(dsa.g(), &BigNum::from_u32(60).unwrap()); + } + + #[test] + fn test_pub_key_from_parts() { + let p = BigNum::from_u32(283).unwrap(); + let q = BigNum::from_u32(47).unwrap(); + let g = BigNum::from_u32(60).unwrap(); + let pub_key = BigNum::from_u32(207).unwrap(); + + let dsa = Dsa::from_public_components(p, q, g, pub_key).unwrap(); + assert_eq!(dsa.pub_key(), &BigNum::from_u32(207).unwrap()); + assert_eq!(dsa.p(), &BigNum::from_u32(283).unwrap()); + assert_eq!(dsa.q(), &BigNum::from_u32(47).unwrap()); + assert_eq!(dsa.g(), &BigNum::from_u32(60).unwrap()); + } + + #[test] + fn test_params() { + let params = Dsa::generate_params(1024).unwrap(); + let p = params.p().to_owned().unwrap(); + let q = params.q().to_owned().unwrap(); + let g = params.g().to_owned().unwrap(); + let key = params.generate_key().unwrap(); + let params2 = Dsa::from_pqg( + key.p().to_owned().unwrap(), + key.q().to_owned().unwrap(), + key.g().to_owned().unwrap(), + ) + .unwrap(); + assert_eq!(p, *params2.p()); + assert_eq!(q, *params2.q()); + assert_eq!(g, *params2.g()); + } + + #[test] + #[cfg(not(boringssl))] + fn test_signature() { + const TEST_DATA: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + let dsa_ref = Dsa::generate(1024).unwrap(); + + let p = dsa_ref.p(); + let q = dsa_ref.q(); + let g = dsa_ref.g(); + + let pub_key = dsa_ref.pub_key(); + let priv_key = dsa_ref.priv_key(); + + let priv_key = Dsa::from_private_components( + BigNumRef::to_owned(p).unwrap(), + BigNumRef::to_owned(q).unwrap(), + BigNumRef::to_owned(g).unwrap(), + BigNumRef::to_owned(priv_key).unwrap(), + BigNumRef::to_owned(pub_key).unwrap(), + ) + .unwrap(); + let priv_key = PKey::from_dsa(priv_key).unwrap(); + + let pub_key = Dsa::from_public_components( + BigNumRef::to_owned(p).unwrap(), + BigNumRef::to_owned(q).unwrap(), + BigNumRef::to_owned(g).unwrap(), + BigNumRef::to_owned(pub_key).unwrap(), + ) + .unwrap(); + let pub_key = PKey::from_dsa(pub_key).unwrap(); + + let mut signer = Signer::new(MessageDigest::sha256(), &priv_key).unwrap(); + signer.update(TEST_DATA).unwrap(); + + let signature = signer.sign_to_vec().unwrap(); + let mut verifier = Verifier::new(MessageDigest::sha256(), &pub_key).unwrap(); + verifier.update(TEST_DATA).unwrap(); + assert!(verifier.verify(&signature[..]).unwrap()); + } + + #[test] + #[cfg(not(boringssl))] + fn test_signature_der() { + use std::convert::TryInto; + + const TEST_DATA: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + let dsa_ref = Dsa::generate(1024).unwrap(); + + let pub_key: PKey<_> = dsa_ref.clone().try_into().unwrap(); + let priv_key: PKey<_> = dsa_ref.try_into().unwrap(); + + let mut signer = Signer::new(MessageDigest::sha256(), &priv_key).unwrap(); + signer.update(TEST_DATA).unwrap(); + + let signature = signer.sign_to_vec().unwrap(); + eprintln!("{:?}", signature); + let signature = DsaSig::from_der(&signature).unwrap(); + + let r = BigNum::from_slice(&signature.r().to_vec()).unwrap(); + let s = BigNum::from_slice(&signature.s().to_vec()).unwrap(); + + let signature = DsaSig::from_private_components(r, s).unwrap(); + let signature = signature.to_der().unwrap(); + + let mut verifier = Verifier::new(MessageDigest::sha256(), &pub_key).unwrap(); + verifier.update(TEST_DATA).unwrap(); + assert!(verifier.verify(&signature[..]).unwrap()); + } + + #[test] + #[allow(clippy::redundant_clone)] + fn clone() { + let key = Dsa::generate(2048).unwrap(); + drop(key.clone()); + } + + #[test] + fn dsa_sig_debug() { + let sig = DsaSig::from_der(&[ + 48, 46, 2, 21, 0, 135, 169, 24, 58, 153, 37, 175, 248, 200, 45, 251, 112, 238, 238, 89, + 172, 177, 182, 166, 237, 2, 21, 0, 159, 146, 151, 237, 187, 8, 82, 115, 14, 183, 103, + 12, 203, 46, 161, 208, 251, 167, 123, 131, + ]) + .unwrap(); + let s = format!("{:?}", sig); + assert_eq!(s, "DsaSig { r: 774484690634577222213819810519929266740561094381, s: 910998676210681457251421818099943952372231273347 }"); + } +} diff --git a/vendor/openssl/src/ec.rs b/vendor/openssl/src/ec.rs new file mode 100644 index 0000000..d541ddf --- /dev/null +++ b/vendor/openssl/src/ec.rs @@ -0,0 +1,1345 @@ +//! Elliptic Curve +//! +//! Cryptography relies on the difficulty of solving mathematical problems, such as the factor +//! of large integers composed of two large prime numbers and the discrete logarithm of a +//! random elliptic curve. This module provides low-level features of the latter. +//! Elliptic Curve protocols can provide the same security with smaller keys. +//! +//! There are 2 forms of elliptic curves, `Fp` and `F2^m`. These curves use irreducible +//! trinomial or pentanomial. Being a generic interface to a wide range of algorithms, +//! the curves are generally referenced by [`EcGroup`]. There are many built-in groups +//! found in [`Nid`]. +//! +//! OpenSSL Wiki explains the fields and curves in detail at [Elliptic Curve Cryptography]. +//! +//! [`EcGroup`]: struct.EcGroup.html +//! [`Nid`]: ../nid/struct.Nid.html +//! [Elliptic Curve Cryptography]: https://wiki.openssl.org/index.php/Elliptic_Curve_Cryptography +use cfg_if::cfg_if; +use foreign_types::{ForeignType, ForeignTypeRef}; +use libc::c_int; +use std::fmt; +use std::ptr; + +use crate::bn::{BigNum, BigNumContextRef, BigNumRef}; +use crate::error::ErrorStack; +use crate::nid::Nid; +use crate::pkey::{HasParams, HasPrivate, HasPublic, Params, Private, Public}; +use crate::util::ForeignTypeRefExt; +use crate::{cvt, cvt_n, cvt_p, init}; +use openssl_macros::corresponds; + +cfg_if! { + if #[cfg(not(boringssl))] { + use std::ffi::CString; + use crate::string::OpensslString; + } +} + +/// Compressed or Uncompressed conversion +/// +/// Conversion from the binary value of the point on the curve is performed in one of +/// compressed, uncompressed, or hybrid conversions. The default is compressed, except +/// for binary curves. +/// +/// Further documentation is available in the [X9.62] standard. +/// +/// [X9.62]: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.202.2977&rep=rep1&type=pdf +#[derive(Copy, Clone)] +pub struct PointConversionForm(ffi::point_conversion_form_t); + +impl PointConversionForm { + /// Compressed conversion from point value. + pub const COMPRESSED: PointConversionForm = + PointConversionForm(ffi::point_conversion_form_t::POINT_CONVERSION_COMPRESSED); + + /// Uncompressed conversion from point value. + pub const UNCOMPRESSED: PointConversionForm = + PointConversionForm(ffi::point_conversion_form_t::POINT_CONVERSION_UNCOMPRESSED); + + /// Performs both compressed and uncompressed conversions. + pub const HYBRID: PointConversionForm = + PointConversionForm(ffi::point_conversion_form_t::POINT_CONVERSION_HYBRID); +} + +/// Named Curve or Explicit +/// +/// This type acts as a boolean as to whether the `EcGroup` is named or explicit. +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct Asn1Flag(c_int); + +impl Asn1Flag { + /// Curve defined using polynomial parameters + /// + /// Most applications use a named EC_GROUP curve, however, support + /// is included to explicitly define the curve used to calculate keys + /// This information would need to be known by both endpoint to make communication + /// effective. + /// + /// OPENSSL_EC_EXPLICIT_CURVE, but that was only added in 1.1. + /// Man page documents that 0 can be used in older versions. + /// + /// OpenSSL documentation at [`EC_GROUP`] + /// + /// [`EC_GROUP`]: https://www.openssl.org/docs/manmaster/crypto/EC_GROUP_get_seed_len.html + pub const EXPLICIT_CURVE: Asn1Flag = Asn1Flag(0); + + /// Standard Curves + /// + /// Curves that make up the typical encryption use cases. The collection of curves + /// are well known but extensible. + /// + /// OpenSSL documentation at [`EC_GROUP`] + /// + /// [`EC_GROUP`]: https://www.openssl.org/docs/manmaster/man3/EC_GROUP_order_bits.html + pub const NAMED_CURVE: Asn1Flag = Asn1Flag(ffi::OPENSSL_EC_NAMED_CURVE); +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::EC_GROUP; + fn drop = ffi::EC_GROUP_free; + + /// Describes the curve + /// + /// A curve can be of the named curve type. These curves can be discovered + /// using openssl binary `openssl ecparam -list_curves`. Other operations + /// are available in the [wiki]. These named curves are available in the + /// [`Nid`] module. + /// + /// Curves can also be generated using prime field parameters or a binary field. + /// + /// Prime fields use the formula `y^2 mod p = x^3 + ax + b mod p`. Binary + /// fields use the formula `y^2 + xy = x^3 + ax^2 + b`. Named curves have + /// assured security. To prevent accidental vulnerabilities, they should + /// be preferred. + /// + /// [wiki]: https://wiki.openssl.org/index.php/Command_Line_Elliptic_Curve_Operations + /// [`Nid`]: ../nid/index.html + pub struct EcGroup; + /// Reference to [`EcGroup`] + /// + /// [`EcGroup`]: struct.EcGroup.html + pub struct EcGroupRef; +} + +impl EcGroup { + /// Returns the group of a standard named curve. + /// + /// # Examples + /// + /// ``` + /// # fn main() -> Result<(), Box<dyn std::error::Error>> { + /// use openssl::nid::Nid; + /// use openssl::ec::{EcGroup, EcKey}; + /// + /// let nid = Nid::X9_62_PRIME256V1; // NIST P-256 curve + /// let group = EcGroup::from_curve_name(nid)?; + /// let key = EcKey::generate(&group)?; + /// # Ok(()) } + /// ``` + #[corresponds(EC_GROUP_new_by_curve_name)] + pub fn from_curve_name(nid: Nid) -> Result<EcGroup, ErrorStack> { + unsafe { + init(); + cvt_p(ffi::EC_GROUP_new_by_curve_name(nid.as_raw())).map(EcGroup) + } + } + + /// Returns the group for given parameters + #[corresponds(EC_GROUP_new_curve_GFp)] + pub fn from_components( + p: BigNum, + a: BigNum, + b: BigNum, + ctx: &mut BigNumContextRef, + ) -> Result<EcGroup, ErrorStack> { + unsafe { + cvt_p(ffi::EC_GROUP_new_curve_GFp( + p.as_ptr(), + a.as_ptr(), + b.as_ptr(), + ctx.as_ptr(), + )) + .map(EcGroup) + } + } +} + +impl EcGroupRef { + /// Places the components of a curve over a prime field in the provided `BigNum`s. + /// The components make up the formula `y^2 mod p = x^3 + ax + b mod p`. + #[corresponds(EC_GROUP_get_curve_GFp)] + pub fn components_gfp( + &self, + p: &mut BigNumRef, + a: &mut BigNumRef, + b: &mut BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_GROUP_get_curve_GFp( + self.as_ptr(), + p.as_ptr(), + a.as_ptr(), + b.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places the components of a curve over a binary field in the provided `BigNum`s. + /// The components make up the formula `y^2 + xy = x^3 + ax^2 + b`. + /// + /// In this form `p` relates to the irreducible polynomial. Each bit represents + /// a term in the polynomial. It will be set to 3 `1`s or 5 `1`s depending on + /// using a trinomial or pentanomial. + #[corresponds(EC_GROUP_get_curve_GF2m)] + #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_EC2M")))] + pub fn components_gf2m( + &self, + p: &mut BigNumRef, + a: &mut BigNumRef, + b: &mut BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_GROUP_get_curve_GF2m( + self.as_ptr(), + p.as_ptr(), + a.as_ptr(), + b.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places the cofactor of the group in the provided `BigNum`. + #[corresponds(EC_GROUP_get_cofactor)] + pub fn cofactor( + &self, + cofactor: &mut BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_GROUP_get_cofactor( + self.as_ptr(), + cofactor.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Returns the degree of the curve. + #[corresponds(EC_GROUP_get_degree)] + pub fn degree(&self) -> u32 { + unsafe { ffi::EC_GROUP_get_degree(self.as_ptr()) as u32 } + } + + /// Returns the number of bits in the group order. + #[corresponds(EC_GROUP_order_bits)] + #[cfg(ossl110)] + pub fn order_bits(&self) -> u32 { + unsafe { ffi::EC_GROUP_order_bits(self.as_ptr()) as u32 } + } + + /// Returns the generator for the given curve as an [`EcPoint`]. + #[corresponds(EC_GROUP_get0_generator)] + pub fn generator(&self) -> &EcPointRef { + unsafe { + let ptr = ffi::EC_GROUP_get0_generator(self.as_ptr()); + EcPointRef::from_const_ptr(ptr) + } + } + + /// Sets the generator point for the given curve + #[corresponds(EC_GROUP_set_generator)] + pub fn set_generator( + &mut self, + generator: EcPoint, + order: BigNum, + cofactor: BigNum, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_GROUP_set_generator( + self.as_ptr(), + generator.as_ptr(), + order.as_ptr(), + cofactor.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places the order of the curve in the provided `BigNum`. + #[corresponds(EC_GROUP_get_order)] + pub fn order( + &self, + order: &mut BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_GROUP_get_order( + self.as_ptr(), + order.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Sets the flag determining if the group corresponds to a named curve or must be explicitly + /// parameterized. + /// + /// This defaults to `EXPLICIT_CURVE` in OpenSSL 1.0.1 and 1.0.2, but `NAMED_CURVE` in OpenSSL + /// 1.1.0. + #[corresponds(EC_GROUP_set_asn1_flag)] + pub fn set_asn1_flag(&mut self, flag: Asn1Flag) { + unsafe { + ffi::EC_GROUP_set_asn1_flag(self.as_ptr(), flag.0); + } + } + + /// Gets the flag determining if the group corresponds to a named curve. + #[corresponds(EC_GROUP_get_asn1_flag)] + pub fn asn1_flag(&self) -> Asn1Flag { + unsafe { Asn1Flag(ffi::EC_GROUP_get_asn1_flag(self.as_ptr())) } + } + + /// Returns the name of the curve, if a name is associated. + #[corresponds(EC_GROUP_get_curve_name)] + pub fn curve_name(&self) -> Option<Nid> { + let nid = unsafe { ffi::EC_GROUP_get_curve_name(self.as_ptr()) }; + if nid > 0 { + Some(Nid::from_raw(nid)) + } else { + None + } + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::EC_POINT; + fn drop = ffi::EC_POINT_free; + + /// Represents a point on the curve + pub struct EcPoint; + /// A reference a borrowed [`EcPoint`]. + pub struct EcPointRef; +} + +impl EcPointRef { + /// Computes `a + b`, storing the result in `self`. + #[corresponds(EC_POINT_add)] + pub fn add( + &mut self, + group: &EcGroupRef, + a: &EcPointRef, + b: &EcPointRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_POINT_add( + group.as_ptr(), + self.as_ptr(), + a.as_ptr(), + b.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Computes `q * m`, storing the result in `self`. + #[corresponds(EC_POINT_mul)] + pub fn mul( + &mut self, + group: &EcGroupRef, + q: &EcPointRef, + m: &BigNumRef, + // FIXME should be &mut + ctx: &BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_POINT_mul( + group.as_ptr(), + self.as_ptr(), + ptr::null(), + q.as_ptr(), + m.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Computes `generator * n`, storing the result in `self`. + #[corresponds(EC_POINT_mul)] + pub fn mul_generator( + &mut self, + group: &EcGroupRef, + n: &BigNumRef, + // FIXME should be &mut + ctx: &BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_POINT_mul( + group.as_ptr(), + self.as_ptr(), + n.as_ptr(), + ptr::null(), + ptr::null(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Computes `generator * n + q * m`, storing the result in `self`. + #[corresponds(EC_POINT_mul)] + pub fn mul_full( + &mut self, + group: &EcGroupRef, + n: &BigNumRef, + q: &EcPointRef, + m: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_POINT_mul( + group.as_ptr(), + self.as_ptr(), + n.as_ptr(), + q.as_ptr(), + m.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Inverts `self`. + #[corresponds(EC_POINT_invert)] + // FIXME should be mutable + pub fn invert(&mut self, group: &EcGroupRef, ctx: &BigNumContextRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_POINT_invert( + group.as_ptr(), + self.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Serializes the point to a binary representation. + #[corresponds(EC_POINT_point2oct)] + pub fn to_bytes( + &self, + group: &EcGroupRef, + form: PointConversionForm, + ctx: &mut BigNumContextRef, + ) -> Result<Vec<u8>, ErrorStack> { + unsafe { + let len = ffi::EC_POINT_point2oct( + group.as_ptr(), + self.as_ptr(), + form.0, + ptr::null_mut(), + 0, + ctx.as_ptr(), + ); + if len == 0 { + return Err(ErrorStack::get()); + } + let mut buf = vec![0; len]; + let len = ffi::EC_POINT_point2oct( + group.as_ptr(), + self.as_ptr(), + form.0, + buf.as_mut_ptr(), + len, + ctx.as_ptr(), + ); + if len == 0 { + Err(ErrorStack::get()) + } else { + Ok(buf) + } + } + } + + /// Serializes the point to a hexadecimal string representation. + #[corresponds(EC_POINT_point2hex)] + #[cfg(not(boringssl))] + pub fn to_hex_str( + &self, + group: &EcGroupRef, + form: PointConversionForm, + ctx: &mut BigNumContextRef, + ) -> Result<OpensslString, ErrorStack> { + unsafe { + let buf = cvt_p(ffi::EC_POINT_point2hex( + group.as_ptr(), + self.as_ptr(), + form.0, + ctx.as_ptr(), + ))?; + Ok(OpensslString::from_ptr(buf)) + } + } + + /// Creates a new point on the specified curve with the same value. + #[corresponds(EC_POINT_dup)] + pub fn to_owned(&self, group: &EcGroupRef) -> Result<EcPoint, ErrorStack> { + unsafe { cvt_p(ffi::EC_POINT_dup(self.as_ptr(), group.as_ptr())).map(EcPoint) } + } + + /// Determines if this point is equal to another. + #[corresponds(EC_POINT_cmp)] + pub fn eq( + &self, + group: &EcGroupRef, + other: &EcPointRef, + ctx: &mut BigNumContextRef, + ) -> Result<bool, ErrorStack> { + unsafe { + let res = cvt_n(ffi::EC_POINT_cmp( + group.as_ptr(), + self.as_ptr(), + other.as_ptr(), + ctx.as_ptr(), + ))?; + Ok(res == 0) + } + } + + /// Places affine coordinates of a curve over a prime field in the provided + /// `x` and `y` `BigNum`s. + #[corresponds(EC_POINT_get_affine_coordinates)] + #[cfg(any(ossl111, boringssl, libressl350))] + pub fn affine_coordinates( + &self, + group: &EcGroupRef, + x: &mut BigNumRef, + y: &mut BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_POINT_get_affine_coordinates( + group.as_ptr(), + self.as_ptr(), + x.as_ptr(), + y.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places affine coordinates of a curve over a prime field in the provided + /// `x` and `y` `BigNum`s + #[corresponds(EC_POINT_get_affine_coordinates_GFp)] + pub fn affine_coordinates_gfp( + &self, + group: &EcGroupRef, + x: &mut BigNumRef, + y: &mut BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_POINT_get_affine_coordinates_GFp( + group.as_ptr(), + self.as_ptr(), + x.as_ptr(), + y.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Sets affine coordinates of a curve over a prime field using the provided + /// `x` and `y` `BigNum`s + #[corresponds(EC_POINT_set_affine_coordinates_GFp)] + pub fn set_affine_coordinates_gfp( + &mut self, + group: &EcGroupRef, + x: &BigNumRef, + y: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_POINT_set_affine_coordinates_GFp( + group.as_ptr(), + self.as_ptr(), + x.as_ptr(), + y.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places affine coordinates of a curve over a binary field in the provided + /// `x` and `y` `BigNum`s + #[corresponds(EC_POINT_get_affine_coordinates_GF2m)] + #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_EC2M")))] + pub fn affine_coordinates_gf2m( + &self, + group: &EcGroupRef, + x: &mut BigNumRef, + y: &mut BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_POINT_get_affine_coordinates_GF2m( + group.as_ptr(), + self.as_ptr(), + x.as_ptr(), + y.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Checks if point is infinity + #[corresponds(EC_POINT_is_at_infinity)] + pub fn is_infinity(&self, group: &EcGroupRef) -> bool { + unsafe { + let res = ffi::EC_POINT_is_at_infinity(group.as_ptr(), self.as_ptr()); + res == 1 + } + } + + /// Checks if point is on a given curve + #[corresponds(EC_POINT_is_on_curve)] + pub fn is_on_curve( + &self, + group: &EcGroupRef, + ctx: &mut BigNumContextRef, + ) -> Result<bool, ErrorStack> { + unsafe { + let res = cvt_n(ffi::EC_POINT_is_on_curve( + group.as_ptr(), + self.as_ptr(), + ctx.as_ptr(), + ))?; + Ok(res == 1) + } + } +} + +impl EcPoint { + /// Creates a new point on the specified curve. + #[corresponds(EC_POINT_new)] + pub fn new(group: &EcGroupRef) -> Result<EcPoint, ErrorStack> { + unsafe { cvt_p(ffi::EC_POINT_new(group.as_ptr())).map(EcPoint) } + } + + /// Creates point from a binary representation + #[corresponds(EC_POINT_oct2point)] + pub fn from_bytes( + group: &EcGroupRef, + buf: &[u8], + ctx: &mut BigNumContextRef, + ) -> Result<EcPoint, ErrorStack> { + let point = EcPoint::new(group)?; + unsafe { + cvt(ffi::EC_POINT_oct2point( + group.as_ptr(), + point.as_ptr(), + buf.as_ptr(), + buf.len(), + ctx.as_ptr(), + ))?; + } + Ok(point) + } + + /// Creates point from a hexadecimal string representation + #[corresponds(EC_POINT_hex2point)] + #[cfg(not(boringssl))] + pub fn from_hex_str( + group: &EcGroupRef, + s: &str, + ctx: &mut BigNumContextRef, + ) -> Result<EcPoint, ErrorStack> { + let point = EcPoint::new(group)?; + unsafe { + let c_str = CString::new(s.as_bytes()).unwrap(); + cvt_p(ffi::EC_POINT_hex2point( + group.as_ptr(), + c_str.as_ptr() as *const _, + point.as_ptr(), + ctx.as_ptr(), + ))?; + } + Ok(point) + } +} + +generic_foreign_type_and_impl_send_sync! { + type CType = ffi::EC_KEY; + fn drop = ffi::EC_KEY_free; + + /// Public and optional private key on the given curve. + pub struct EcKey<T>; + /// A reference to an [`EcKey`]. + pub struct EcKeyRef<T>; +} + +impl<T> EcKeyRef<T> +where + T: HasPrivate, +{ + private_key_to_pem! { + /// Serializes the private key to a PEM-encoded ECPrivateKey structure. + /// + /// The output will have a header of `-----BEGIN EC PRIVATE KEY-----`. + #[corresponds(PEM_write_bio_ECPrivateKey)] + private_key_to_pem, + /// Serializes the private key to a PEM-encoded encrypted ECPrivateKey structure. + /// + /// The output will have a header of `-----BEGIN EC PRIVATE KEY-----`. + #[corresponds(PEM_write_bio_ECPrivateKey)] + private_key_to_pem_passphrase, + ffi::PEM_write_bio_ECPrivateKey + } + + to_der! { + /// Serializes the private key into a DER-encoded ECPrivateKey structure. + #[corresponds(i2d_ECPrivateKey)] + private_key_to_der, + ffi::i2d_ECPrivateKey + } + + /// Returns the private key value. + #[corresponds(EC_KEY_get0_private_key)] + pub fn private_key(&self) -> &BigNumRef { + unsafe { + let ptr = ffi::EC_KEY_get0_private_key(self.as_ptr()); + BigNumRef::from_const_ptr(ptr) + } + } +} + +impl<T> EcKeyRef<T> +where + T: HasPublic, +{ + /// Returns the public key. + #[corresponds(EC_KEY_get0_public_key)] + pub fn public_key(&self) -> &EcPointRef { + unsafe { + let ptr = ffi::EC_KEY_get0_public_key(self.as_ptr()); + EcPointRef::from_const_ptr(ptr) + } + } + + 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_EC_PUBKEY)] + public_key_to_pem, + ffi::PEM_write_bio_EC_PUBKEY + } + + to_der! { + /// Serializes the public key into a DER-encoded SubjectPublicKeyInfo structure. + #[corresponds(i2d_EC_PUBKEY)] + public_key_to_der, + ffi::i2d_EC_PUBKEY + } +} + +impl<T> EcKeyRef<T> +where + T: HasParams, +{ + /// Returns the key's group. + #[corresponds(EC_KEY_get0_group)] + pub fn group(&self) -> &EcGroupRef { + unsafe { + let ptr = ffi::EC_KEY_get0_group(self.as_ptr()); + EcGroupRef::from_const_ptr(ptr) + } + } + + /// Checks the key for validity. + #[corresponds(EC_KEY_check_key)] + pub fn check_key(&self) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::EC_KEY_check_key(self.as_ptr())).map(|_| ()) } + } +} + +impl<T> ToOwned for EcKeyRef<T> { + type Owned = EcKey<T>; + + fn to_owned(&self) -> EcKey<T> { + unsafe { + let r = ffi::EC_KEY_up_ref(self.as_ptr()); + assert!(r == 1); + EcKey::from_ptr(self.as_ptr()) + } + } +} + +impl EcKey<Params> { + /// Constructs an `EcKey` corresponding to a known curve. + /// + /// It will not have an associated public or private key. This kind of key is primarily useful + /// to be provided to the `set_tmp_ecdh` methods on `Ssl` and `SslContextBuilder`. + #[corresponds(EC_KEY_new_by_curve_name)] + pub fn from_curve_name(nid: Nid) -> Result<EcKey<Params>, ErrorStack> { + unsafe { + init(); + cvt_p(ffi::EC_KEY_new_by_curve_name(nid.as_raw())).map(|p| EcKey::from_ptr(p)) + } + } + + /// Constructs an `EcKey` corresponding to a curve. + #[corresponds(EC_KEY_set_group)] + pub fn from_group(group: &EcGroupRef) -> Result<EcKey<Params>, ErrorStack> { + unsafe { + cvt_p(ffi::EC_KEY_new()) + .map(|p| EcKey::from_ptr(p)) + .and_then(|key| { + cvt(ffi::EC_KEY_set_group(key.as_ptr(), group.as_ptr())).map(|_| key) + }) + } + } +} + +impl EcKey<Public> { + /// Constructs an `EcKey` from the specified group with the associated [`EcPoint`]: `public_key`. + /// + /// This will only have the associated `public_key`. + /// + /// # Example + /// + /// ``` + /// # fn main() -> Result<(), Box<dyn std::error::Error>> { + /// use openssl::bn::BigNumContext; + /// use openssl::ec::*; + /// use openssl::nid::Nid; + /// use openssl::pkey::PKey; + /// + /// let group = EcGroup::from_curve_name(Nid::SECP384R1)?; + /// let mut ctx = BigNumContext::new()?; + /// + /// // get bytes from somewhere + /// let public_key = // ... + /// # EcKey::generate(&group)?.public_key().to_bytes(&group, + /// # PointConversionForm::COMPRESSED, &mut ctx)?; + /// + /// // create an EcKey from the binary form of a EcPoint + /// let point = EcPoint::from_bytes(&group, &public_key, &mut ctx)?; + /// let key = EcKey::from_public_key(&group, &point)?; + /// key.check_key()?; + /// # Ok(()) } + /// ``` + #[corresponds(EC_KEY_set_public_key)] + pub fn from_public_key( + group: &EcGroupRef, + public_key: &EcPointRef, + ) -> Result<EcKey<Public>, ErrorStack> { + unsafe { + cvt_p(ffi::EC_KEY_new()) + .map(|p| EcKey::from_ptr(p)) + .and_then(|key| { + cvt(ffi::EC_KEY_set_group(key.as_ptr(), group.as_ptr())).map(|_| key) + }) + .and_then(|key| { + cvt(ffi::EC_KEY_set_public_key( + key.as_ptr(), + public_key.as_ptr(), + )) + .map(|_| key) + }) + } + } + + /// Constructs a public key from its affine coordinates. + #[corresponds(EC_KEY_set_public_key_affine_coordinates)] + pub fn from_public_key_affine_coordinates( + group: &EcGroupRef, + x: &BigNumRef, + y: &BigNumRef, + ) -> Result<EcKey<Public>, ErrorStack> { + unsafe { + cvt_p(ffi::EC_KEY_new()) + .map(|p| EcKey::from_ptr(p)) + .and_then(|key| { + cvt(ffi::EC_KEY_set_group(key.as_ptr(), group.as_ptr())).map(|_| key) + }) + .and_then(|key| { + cvt(ffi::EC_KEY_set_public_key_affine_coordinates( + key.as_ptr(), + x.as_ptr(), + y.as_ptr(), + )) + .map(|_| key) + }) + } + } + + from_pem! { + /// Decodes a PEM-encoded SubjectPublicKeyInfo structure containing a EC key. + /// + /// The input should have a header of `-----BEGIN PUBLIC KEY-----`. + #[corresponds(PEM_read_bio_EC_PUBKEY)] + public_key_from_pem, + EcKey<Public>, + ffi::PEM_read_bio_EC_PUBKEY + } + + from_der! { + /// Decodes a DER-encoded SubjectPublicKeyInfo structure containing a EC key. + #[corresponds(d2i_EC_PUBKEY)] + public_key_from_der, + EcKey<Public>, + ffi::d2i_EC_PUBKEY + } +} + +impl EcKey<Private> { + /// Generates a new public/private key pair on the specified curve. + /// + /// # Examples + /// + /// ``` + /// # fn main() -> Result<(), Box<dyn std::error::Error>> { + /// use openssl::bn::BigNumContext; + /// use openssl::nid::Nid; + /// use openssl::ec::{EcGroup, EcKey, PointConversionForm}; + /// + /// let nid = Nid::X9_62_PRIME256V1; // NIST P-256 curve + /// let group = EcGroup::from_curve_name(nid)?; + /// let key = EcKey::generate(&group)?; + /// + /// let mut ctx = BigNumContext::new()?; + /// + /// let public_key = &key.public_key().to_bytes( + /// &group, + /// PointConversionForm::COMPRESSED, + /// &mut ctx, + /// )?; + /// assert_eq!(public_key.len(), 33); + /// assert_ne!(public_key[0], 0x04); + /// + /// let private_key = key.private_key().to_vec(); + /// assert!(private_key.len() >= 31); + /// # Ok(()) } + /// ``` + #[corresponds(EC_KEY_generate_key)] + pub fn generate(group: &EcGroupRef) -> Result<EcKey<Private>, ErrorStack> { + unsafe { + cvt_p(ffi::EC_KEY_new()) + .map(|p| EcKey::from_ptr(p)) + .and_then(|key| { + cvt(ffi::EC_KEY_set_group(key.as_ptr(), group.as_ptr())).map(|_| key) + }) + .and_then(|key| cvt(ffi::EC_KEY_generate_key(key.as_ptr())).map(|_| key)) + } + } + + /// Constructs an public/private key pair given a curve, a private key and a public key point. + #[corresponds(EC_KEY_set_private_key)] + pub fn from_private_components( + group: &EcGroupRef, + private_number: &BigNumRef, + public_key: &EcPointRef, + ) -> Result<EcKey<Private>, ErrorStack> { + unsafe { + cvt_p(ffi::EC_KEY_new()) + .map(|p| EcKey::from_ptr(p)) + .and_then(|key| { + cvt(ffi::EC_KEY_set_group(key.as_ptr(), group.as_ptr())).map(|_| key) + }) + .and_then(|key| { + cvt(ffi::EC_KEY_set_private_key( + key.as_ptr(), + private_number.as_ptr(), + )) + .map(|_| key) + }) + .and_then(|key| { + cvt(ffi::EC_KEY_set_public_key( + key.as_ptr(), + public_key.as_ptr(), + )) + .map(|_| key) + }) + } + } + + private_key_from_pem! { + /// Deserializes a private key from a PEM-encoded ECPrivateKey structure. + /// + /// The input should have a header of `-----BEGIN EC PRIVATE KEY-----`. + #[corresponds(PEM_read_bio_ECPrivateKey)] + private_key_from_pem, + + /// Deserializes a private key from a PEM-encoded encrypted ECPrivateKey structure. + /// + /// The input should have a header of `-----BEGIN EC PRIVATE KEY-----`. + #[corresponds(PEM_read_bio_ECPrivateKey)] + private_key_from_pem_passphrase, + + /// Deserializes a private key from a PEM-encoded encrypted ECPrivateKey structure. + /// + /// The callback should fill the password into the provided buffer and return its length. + /// + /// The input should have a header of `-----BEGIN EC PRIVATE KEY-----`. + #[corresponds(PEM_read_bio_ECPrivateKey)] + private_key_from_pem_callback, + EcKey<Private>, + ffi::PEM_read_bio_ECPrivateKey + } + + from_der! { + /// Decodes a DER-encoded elliptic curve private key structure. + #[corresponds(d2i_ECPrivateKey)] + private_key_from_der, + EcKey<Private>, + ffi::d2i_ECPrivateKey + } +} + +impl<T> Clone for EcKey<T> { + fn clone(&self) -> EcKey<T> { + (**self).to_owned() + } +} + +impl<T> fmt::Debug for EcKey<T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "EcKey") + } +} + +#[cfg(test)] +mod test { + use hex::FromHex; + + use super::*; + use crate::bn::{BigNum, BigNumContext}; + use crate::nid::Nid; + + #[test] + fn key_new_by_curve_name() { + EcKey::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + } + + #[test] + fn generate() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + EcKey::generate(&group).unwrap(); + } + + #[test] + fn ec_group_from_components() { + // parameters are from secp256r1 + let p = BigNum::from_hex_str( + "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", + ) + .unwrap(); + let a = BigNum::from_hex_str( + "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", + ) + .unwrap(); + let b = BigNum::from_hex_str( + "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", + ) + .unwrap(); + let mut ctx = BigNumContext::new().unwrap(); + + let _curve = EcGroup::from_components(p, a, b, &mut ctx).unwrap(); + } + + #[test] + fn ec_point_set_affine() { + // parameters are from secp256r1 + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let mut ctx = BigNumContext::new().unwrap(); + let mut gen_point = EcPoint::new(&group).unwrap(); + let gen_x = BigNum::from_hex_str( + "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", + ) + .unwrap(); + let gen_y = BigNum::from_hex_str( + "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", + ) + .unwrap(); + gen_point + .set_affine_coordinates_gfp(&group, &gen_x, &gen_y, &mut ctx) + .unwrap(); + assert!(gen_point.is_on_curve(&group, &mut ctx).unwrap()); + } + + #[test] + fn ec_group_set_generator() { + // parameters are from secp256r1 + let mut ctx = BigNumContext::new().unwrap(); + let p = BigNum::from_hex_str( + "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", + ) + .unwrap(); + let a = BigNum::from_hex_str( + "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", + ) + .unwrap(); + let b = BigNum::from_hex_str( + "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", + ) + .unwrap(); + + let mut group = EcGroup::from_components(p, a, b, &mut ctx).unwrap(); + + let mut gen_point = EcPoint::new(&group).unwrap(); + let gen_x = BigNum::from_hex_str( + "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", + ) + .unwrap(); + let gen_y = BigNum::from_hex_str( + "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", + ) + .unwrap(); + gen_point + .set_affine_coordinates_gfp(&group, &gen_x, &gen_y, &mut ctx) + .unwrap(); + + let order = BigNum::from_hex_str( + "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", + ) + .unwrap(); + let cofactor = BigNum::from_hex_str("01").unwrap(); + group.set_generator(gen_point, order, cofactor).unwrap(); + let mut constructed_order = BigNum::new().unwrap(); + group.order(&mut constructed_order, &mut ctx).unwrap(); + + let named_group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let mut named_order = BigNum::new().unwrap(); + named_group.order(&mut named_order, &mut ctx).unwrap(); + + assert_eq!( + constructed_order.ucmp(&named_order), + std::cmp::Ordering::Equal + ); + } + + #[test] + fn cofactor() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let mut ctx = BigNumContext::new().unwrap(); + let mut cofactor = BigNum::new().unwrap(); + group.cofactor(&mut cofactor, &mut ctx).unwrap(); + let one = BigNum::from_u32(1).unwrap(); + assert_eq!(cofactor, one); + } + + #[test] + #[allow(clippy::redundant_clone)] + fn dup() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let key = EcKey::generate(&group).unwrap(); + drop(key.clone()); + } + + #[test] + fn point_new() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + EcPoint::new(&group).unwrap(); + } + + #[test] + fn point_bytes() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let key = EcKey::generate(&group).unwrap(); + let point = key.public_key(); + let mut ctx = BigNumContext::new().unwrap(); + let bytes = point + .to_bytes(&group, PointConversionForm::COMPRESSED, &mut ctx) + .unwrap(); + let point2 = EcPoint::from_bytes(&group, &bytes, &mut ctx).unwrap(); + assert!(point.eq(&group, &point2, &mut ctx).unwrap()); + } + + #[test] + #[cfg(not(boringssl))] + fn point_hex_str() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let key = EcKey::generate(&group).unwrap(); + let point = key.public_key(); + let mut ctx = BigNumContext::new().unwrap(); + let hex = point + .to_hex_str(&group, PointConversionForm::COMPRESSED, &mut ctx) + .unwrap(); + let point2 = EcPoint::from_hex_str(&group, &hex, &mut ctx).unwrap(); + assert!(point.eq(&group, &point2, &mut ctx).unwrap()); + } + + #[test] + fn point_owned() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let key = EcKey::generate(&group).unwrap(); + let point = key.public_key(); + let owned = point.to_owned(&group).unwrap(); + let mut ctx = BigNumContext::new().unwrap(); + assert!(owned.eq(&group, point, &mut ctx).unwrap()); + } + + #[test] + fn mul_generator() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let key = EcKey::generate(&group).unwrap(); + let mut ctx = BigNumContext::new().unwrap(); + let mut public_key = EcPoint::new(&group).unwrap(); + public_key + .mul_generator(&group, key.private_key(), &ctx) + .unwrap(); + assert!(public_key.eq(&group, key.public_key(), &mut ctx).unwrap()); + } + + #[test] + fn generator() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let gen = group.generator(); + let one = BigNum::from_u32(1).unwrap(); + let mut ctx = BigNumContext::new().unwrap(); + let mut ecp = EcPoint::new(&group).unwrap(); + ecp.mul_generator(&group, &one, &ctx).unwrap(); + assert!(ecp.eq(&group, gen, &mut ctx).unwrap()); + } + + #[test] + fn key_from_public_key() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let key = EcKey::generate(&group).unwrap(); + let mut ctx = BigNumContext::new().unwrap(); + let bytes = key + .public_key() + .to_bytes(&group, PointConversionForm::COMPRESSED, &mut ctx) + .unwrap(); + + drop(key); + let public_key = EcPoint::from_bytes(&group, &bytes, &mut ctx).unwrap(); + let ec_key = EcKey::from_public_key(&group, &public_key).unwrap(); + assert!(ec_key.check_key().is_ok()); + } + + #[test] + fn key_from_private_components() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let key = EcKey::generate(&group).unwrap(); + + let dup_key = + EcKey::from_private_components(&group, key.private_key(), key.public_key()).unwrap(); + dup_key.check_key().unwrap(); + + assert!(key.private_key() == dup_key.private_key()); + } + + #[test] + fn key_from_affine_coordinates() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let x = Vec::from_hex("30a0424cd21c2944838a2d75c92b37e76ea20d9f00893a3b4eee8a3c0aafec3e") + .unwrap(); + let y = Vec::from_hex("e04b65e92456d9888b52b379bdfbd51ee869ef1f0fc65b6659695b6cce081723") + .unwrap(); + + let xbn = BigNum::from_slice(&x).unwrap(); + let ybn = BigNum::from_slice(&y).unwrap(); + + let ec_key = EcKey::from_public_key_affine_coordinates(&group, &xbn, &ybn).unwrap(); + assert!(ec_key.check_key().is_ok()); + } + + #[cfg(any(ossl111, boringssl, libressl350))] + #[test] + fn get_affine_coordinates() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let x = Vec::from_hex("30a0424cd21c2944838a2d75c92b37e76ea20d9f00893a3b4eee8a3c0aafec3e") + .unwrap(); + let y = Vec::from_hex("e04b65e92456d9888b52b379bdfbd51ee869ef1f0fc65b6659695b6cce081723") + .unwrap(); + + let xbn = BigNum::from_slice(&x).unwrap(); + let ybn = BigNum::from_slice(&y).unwrap(); + + let ec_key = EcKey::from_public_key_affine_coordinates(&group, &xbn, &ybn).unwrap(); + + let mut xbn2 = BigNum::new().unwrap(); + let mut ybn2 = BigNum::new().unwrap(); + let mut ctx = BigNumContext::new().unwrap(); + let ec_key_pk = ec_key.public_key(); + ec_key_pk + .affine_coordinates(&group, &mut xbn2, &mut ybn2, &mut ctx) + .unwrap(); + assert_eq!(xbn2, xbn); + assert_eq!(ybn2, ybn); + } + + #[test] + fn get_affine_coordinates_gfp() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let x = Vec::from_hex("30a0424cd21c2944838a2d75c92b37e76ea20d9f00893a3b4eee8a3c0aafec3e") + .unwrap(); + let y = Vec::from_hex("e04b65e92456d9888b52b379bdfbd51ee869ef1f0fc65b6659695b6cce081723") + .unwrap(); + + let xbn = BigNum::from_slice(&x).unwrap(); + let ybn = BigNum::from_slice(&y).unwrap(); + + let ec_key = EcKey::from_public_key_affine_coordinates(&group, &xbn, &ybn).unwrap(); + + let mut xbn2 = BigNum::new().unwrap(); + let mut ybn2 = BigNum::new().unwrap(); + let mut ctx = BigNumContext::new().unwrap(); + let ec_key_pk = ec_key.public_key(); + ec_key_pk + .affine_coordinates_gfp(&group, &mut xbn2, &mut ybn2, &mut ctx) + .unwrap(); + assert_eq!(xbn2, xbn); + assert_eq!(ybn2, ybn); + } + + #[test] + fn is_infinity() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let mut ctx = BigNumContext::new().unwrap(); + let g = group.generator(); + assert!(!g.is_infinity(&group)); + + let mut order = BigNum::new().unwrap(); + group.order(&mut order, &mut ctx).unwrap(); + let mut inf = EcPoint::new(&group).unwrap(); + inf.mul_generator(&group, &order, &ctx).unwrap(); + assert!(inf.is_infinity(&group)); + } + + #[test] + #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_EC2M")))] + fn is_on_curve() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let mut ctx = BigNumContext::new().unwrap(); + let g = group.generator(); + assert!(g.is_on_curve(&group, &mut ctx).unwrap()); + + let group2 = EcGroup::from_curve_name(Nid::X9_62_PRIME239V3).unwrap(); + assert!(!g.is_on_curve(&group2, &mut ctx).unwrap()); + } + + #[test] + #[cfg(any(boringssl, ossl111, libressl350))] + fn asn1_flag() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let flag = group.asn1_flag(); + assert_eq!(flag, Asn1Flag::NAMED_CURVE); + } +} diff --git a/vendor/openssl/src/ecdsa.rs b/vendor/openssl/src/ecdsa.rs new file mode 100644 index 0000000..f3b27b3 --- /dev/null +++ b/vendor/openssl/src/ecdsa.rs @@ -0,0 +1,224 @@ +//! Low level Elliptic Curve Digital Signature Algorithm (ECDSA) functions. + +use cfg_if::cfg_if; +use foreign_types::{ForeignType, ForeignTypeRef}; +use libc::c_int; +use std::mem; +use std::ptr; + +use crate::bn::{BigNum, BigNumRef}; +use crate::ec::EcKeyRef; +use crate::error::ErrorStack; +use crate::pkey::{HasPrivate, HasPublic}; +use crate::util::ForeignTypeRefExt; +use crate::{cvt_n, cvt_p, LenType}; +use openssl_macros::corresponds; + +foreign_type_and_impl_send_sync! { + type CType = ffi::ECDSA_SIG; + fn drop = ffi::ECDSA_SIG_free; + + /// A low level interface to ECDSA. + pub struct EcdsaSig; + /// A reference to an [`EcdsaSig`]. + pub struct EcdsaSigRef; +} + +impl EcdsaSig { + /// Computes a digital signature of the hash value `data` using the private EC key eckey. + #[corresponds(ECDSA_do_sign)] + pub fn sign<T>(data: &[u8], eckey: &EcKeyRef<T>) -> Result<EcdsaSig, ErrorStack> + where + T: HasPrivate, + { + unsafe { + assert!(data.len() <= c_int::max_value() as usize); + let sig = cvt_p(ffi::ECDSA_do_sign( + data.as_ptr(), + data.len() as LenType, + eckey.as_ptr(), + ))?; + Ok(EcdsaSig::from_ptr(sig)) + } + } + + /// Returns a new `EcdsaSig` by setting the `r` and `s` values associated with an ECDSA signature. + #[corresponds(ECDSA_SIG_set0)] + pub fn from_private_components(r: BigNum, s: BigNum) -> Result<EcdsaSig, ErrorStack> { + unsafe { + let sig = cvt_p(ffi::ECDSA_SIG_new())?; + ECDSA_SIG_set0(sig, r.as_ptr(), s.as_ptr()); + mem::forget((r, s)); + Ok(EcdsaSig::from_ptr(sig)) + } + } + + from_der! { + /// Decodes a DER-encoded ECDSA signature. + #[corresponds(d2i_ECDSA_SIG)] + from_der, + EcdsaSig, + ffi::d2i_ECDSA_SIG + } +} + +impl EcdsaSigRef { + to_der! { + /// Serializes the ECDSA signature into a DER-encoded ECDSASignature structure. + #[corresponds(i2d_ECDSA_SIG)] + to_der, + ffi::i2d_ECDSA_SIG + } + + /// Verifies if the signature is a valid ECDSA signature using the given public key. + #[corresponds(ECDSA_do_verify)] + pub fn verify<T>(&self, data: &[u8], eckey: &EcKeyRef<T>) -> Result<bool, ErrorStack> + where + T: HasPublic, + { + unsafe { + assert!(data.len() <= c_int::max_value() as usize); + cvt_n(ffi::ECDSA_do_verify( + data.as_ptr(), + data.len() as LenType, + self.as_ptr(), + eckey.as_ptr(), + )) + .map(|x| x == 1) + } + } + + /// Returns internal component: `r` of an `EcdsaSig`. (See X9.62 or FIPS 186-2) + #[corresponds(ECDSA_SIG_get0)] + pub fn r(&self) -> &BigNumRef { + unsafe { + let mut r = ptr::null(); + ECDSA_SIG_get0(self.as_ptr(), &mut r, ptr::null_mut()); + BigNumRef::from_const_ptr(r) + } + } + + /// Returns internal components: `s` of an `EcdsaSig`. (See X9.62 or FIPS 186-2) + #[corresponds(ECDSA_SIG_get0)] + pub fn s(&self) -> &BigNumRef { + unsafe { + let mut s = ptr::null(); + ECDSA_SIG_get0(self.as_ptr(), ptr::null_mut(), &mut s); + BigNumRef::from_const_ptr(s) + } + } +} + +cfg_if! { + if #[cfg(any(ossl110, libressl273, boringssl))] { + use ffi::{ECDSA_SIG_set0, ECDSA_SIG_get0}; + } else { + #[allow(bad_style)] + unsafe fn ECDSA_SIG_set0( + sig: *mut ffi::ECDSA_SIG, + r: *mut ffi::BIGNUM, + s: *mut ffi::BIGNUM, + ) -> c_int { + if r.is_null() || s.is_null() { + return 0; + } + ffi::BN_clear_free((*sig).r); + ffi::BN_clear_free((*sig).s); + (*sig).r = r; + (*sig).s = s; + 1 + } + + #[allow(bad_style)] + unsafe fn ECDSA_SIG_get0( + sig: *const ffi::ECDSA_SIG, + pr: *mut *const ffi::BIGNUM, + ps: *mut *const ffi::BIGNUM) + { + if !pr.is_null() { + (*pr) = (*sig).r; + } + if !ps.is_null() { + (*ps) = (*sig).s; + } + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::ec::EcGroup; + use crate::ec::EcKey; + use crate::nid::Nid; + use crate::pkey::{Private, Public}; + + fn get_public_key(group: &EcGroup, x: &EcKey<Private>) -> Result<EcKey<Public>, ErrorStack> { + EcKey::from_public_key(group, x.public_key()) + } + + #[test] + #[cfg_attr(osslconf = "OPENSSL_NO_EC2M", ignore)] + fn sign_and_verify() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let private_key = EcKey::generate(&group).unwrap(); + let public_key = get_public_key(&group, &private_key).unwrap(); + + let private_key2 = EcKey::generate(&group).unwrap(); + let public_key2 = get_public_key(&group, &private_key2).unwrap(); + + let data = String::from("hello"); + let res = EcdsaSig::sign(data.as_bytes(), &private_key).unwrap(); + + // Signature can be verified using the correct data & correct public key + let verification = res.verify(data.as_bytes(), &public_key).unwrap(); + assert!(verification); + + // Signature will not be verified using the incorrect data but the correct public key + let verification2 = res + .verify(String::from("hello2").as_bytes(), &public_key) + .unwrap(); + assert!(!verification2); + + // Signature will not be verified using the correct data but the incorrect public key + let verification3 = res.verify(data.as_bytes(), &public_key2).unwrap(); + assert!(!verification3); + } + + #[test] + #[cfg_attr(osslconf = "OPENSSL_NO_EC2M", ignore)] + fn check_private_components() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let private_key = EcKey::generate(&group).unwrap(); + let public_key = get_public_key(&group, &private_key).unwrap(); + let data = String::from("hello"); + let res = EcdsaSig::sign(data.as_bytes(), &private_key).unwrap(); + + let verification = res.verify(data.as_bytes(), &public_key).unwrap(); + assert!(verification); + + let r = res.r().to_owned().unwrap(); + let s = res.s().to_owned().unwrap(); + + let res2 = EcdsaSig::from_private_components(r, s).unwrap(); + let verification2 = res2.verify(data.as_bytes(), &public_key).unwrap(); + assert!(verification2); + } + + #[test] + #[cfg_attr(osslconf = "OPENSSL_NO_EC2M", ignore)] + fn serialize_deserialize() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let private_key = EcKey::generate(&group).unwrap(); + let public_key = get_public_key(&group, &private_key).unwrap(); + + let data = String::from("hello"); + let res = EcdsaSig::sign(data.as_bytes(), &private_key).unwrap(); + + let der = res.to_der().unwrap(); + let sig = EcdsaSig::from_der(&der).unwrap(); + + let verification = sig.verify(data.as_bytes(), &public_key).unwrap(); + assert!(verification); + } +} diff --git a/vendor/openssl/src/encrypt.rs b/vendor/openssl/src/encrypt.rs new file mode 100644 index 0000000..4522146 --- /dev/null +++ b/vendor/openssl/src/encrypt.rs @@ -0,0 +1,578 @@ +//! Message encryption. +//! +//! The [`Encrypter`] allows for encryption of data given a public key. The [`Decrypter`] can be +//! used with the corresponding private key to decrypt the data. +//! +//! # Examples +//! +//! Encrypt and decrypt data given an RSA keypair: +//! +//! ```rust +//! use openssl::encrypt::{Encrypter, Decrypter}; +//! use openssl::rsa::{Rsa, Padding}; +//! use openssl::pkey::PKey; +//! +//! // Generate a keypair +//! let keypair = Rsa::generate(2048).unwrap(); +//! let keypair = PKey::from_rsa(keypair).unwrap(); +//! +//! let data = b"hello, world!"; +//! +//! // Encrypt the data with RSA PKCS1 +//! let mut encrypter = Encrypter::new(&keypair).unwrap(); +//! encrypter.set_rsa_padding(Padding::PKCS1).unwrap(); +//! // Create an output buffer +//! let buffer_len = encrypter.encrypt_len(data).unwrap(); +//! let mut encrypted = vec![0; buffer_len]; +//! // Encrypt and truncate the buffer +//! let encrypted_len = encrypter.encrypt(data, &mut encrypted).unwrap(); +//! encrypted.truncate(encrypted_len); +//! +//! // Decrypt the data +//! let mut decrypter = Decrypter::new(&keypair).unwrap(); +//! decrypter.set_rsa_padding(Padding::PKCS1).unwrap(); +//! // Create an output buffer +//! let buffer_len = decrypter.decrypt_len(&encrypted).unwrap(); +//! let mut decrypted = vec![0; buffer_len]; +//! // Encrypt and truncate the buffer +//! let decrypted_len = decrypter.decrypt(&encrypted, &mut decrypted).unwrap(); +//! decrypted.truncate(decrypted_len); +//! assert_eq!(&*decrypted, data); +//! ``` +#[cfg(any(ossl102, libressl310))] +use libc::c_int; +use std::{marker::PhantomData, ptr}; + +use crate::error::ErrorStack; +use crate::hash::MessageDigest; +use crate::pkey::{HasPrivate, HasPublic, PKeyRef}; +use crate::rsa::Padding; +use crate::{cvt, cvt_p}; +use foreign_types::ForeignTypeRef; + +/// A type which encrypts data. +pub struct Encrypter<'a> { + pctx: *mut ffi::EVP_PKEY_CTX, + _p: PhantomData<&'a ()>, +} + +unsafe impl<'a> Sync for Encrypter<'a> {} +unsafe impl<'a> Send for Encrypter<'a> {} + +impl<'a> Drop for Encrypter<'a> { + fn drop(&mut self) { + unsafe { + ffi::EVP_PKEY_CTX_free(self.pctx); + } + } +} + +impl<'a> Encrypter<'a> { + /// Creates a new `Encrypter`. + /// + /// OpenSSL documentation at [`EVP_PKEY_encrypt_init`]. + /// + /// [`EVP_PKEY_encrypt_init`]: https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_encrypt_init.html + pub fn new<T>(pkey: &'a PKeyRef<T>) -> Result<Encrypter<'a>, ErrorStack> + where + T: HasPublic, + { + unsafe { + ffi::init(); + + let pctx = cvt_p(ffi::EVP_PKEY_CTX_new(pkey.as_ptr(), ptr::null_mut()))?; + let r = ffi::EVP_PKEY_encrypt_init(pctx); + if r != 1 { + ffi::EVP_PKEY_CTX_free(pctx); + return Err(ErrorStack::get()); + } + + Ok(Encrypter { + pctx, + _p: PhantomData, + }) + } + } + + /// Returns the RSA padding mode in use. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to `EVP_PKEY_CTX_get_rsa_padding`. + pub fn rsa_padding(&self) -> Result<Padding, ErrorStack> { + unsafe { + let mut pad = 0; + cvt(ffi::EVP_PKEY_CTX_get_rsa_padding(self.pctx, &mut pad)) + .map(|_| Padding::from_raw(pad)) + } + } + + /// Sets the RSA padding mode. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to [`EVP_PKEY_CTX_set_rsa_padding`]. + /// + /// [`EVP_PKEY_CTX_set_rsa_padding`]: https://www.openssl.org/docs/manmaster/crypto/EVP_PKEY_CTX_set_rsa_padding.html + pub fn set_rsa_padding(&mut self, padding: Padding) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_rsa_padding( + self.pctx, + padding.as_raw(), + )) + .map(|_| ()) + } + } + + /// Sets the RSA MGF1 algorithm. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to [`EVP_PKEY_CTX_set_rsa_mgf1_md`]. + /// + /// [`EVP_PKEY_CTX_set_rsa_mgf1_md`]: https://www.openssl.org/docs/manmaster/man7/RSA-PSS.html + pub fn set_rsa_mgf1_md(&mut self, md: MessageDigest) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_rsa_mgf1_md( + self.pctx, + md.as_ptr() as *mut _, + )) + .map(|_| ()) + } + } + + /// Sets the RSA OAEP algorithm. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to [`EVP_PKEY_CTX_set_rsa_oaep_md`]. + /// + /// [`EVP_PKEY_CTX_set_rsa_oaep_md`]: https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_set_rsa_oaep_md.html + #[cfg(any(ossl102, libressl310))] + pub fn set_rsa_oaep_md(&mut self, md: MessageDigest) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_rsa_oaep_md( + self.pctx, + md.as_ptr() as *mut _, + )) + .map(|_| ()) + } + } + + /// Sets the RSA OAEP label. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to [`EVP_PKEY_CTX_set0_rsa_oaep_label`]. + /// + /// [`EVP_PKEY_CTX_set0_rsa_oaep_label`]: https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_set0_rsa_oaep_label.html + #[cfg(any(ossl102, libressl310))] + pub fn set_rsa_oaep_label(&mut self, label: &[u8]) -> Result<(), ErrorStack> { + unsafe { + let p = cvt_p(ffi::OPENSSL_malloc(label.len() as _))?; + ptr::copy_nonoverlapping(label.as_ptr(), p as *mut u8, label.len()); + + cvt(ffi::EVP_PKEY_CTX_set0_rsa_oaep_label( + self.pctx, + p, + label.len() as c_int, + )) + .map(|_| ()) + .map_err(|e| { + ffi::OPENSSL_free(p); + e + }) + } + } + + /// Performs public key encryption. + /// + /// In order to know the size needed for the output buffer, use [`encrypt_len`](Encrypter::encrypt_len). + /// Note that the length of the output buffer can be greater of the length of the encoded data. + /// ``` + /// # use openssl::{ + /// # encrypt::Encrypter, + /// # pkey::PKey, + /// # rsa::{Rsa, Padding}, + /// # }; + /// # + /// # let key = include_bytes!("../test/rsa.pem"); + /// # let private_key = Rsa::private_key_from_pem(key).unwrap(); + /// # let pkey = PKey::from_rsa(private_key).unwrap(); + /// # let input = b"hello world".to_vec(); + /// # + /// let mut encrypter = Encrypter::new(&pkey).unwrap(); + /// encrypter.set_rsa_padding(Padding::PKCS1).unwrap(); + /// + /// // Get the length of the output buffer + /// let buffer_len = encrypter.encrypt_len(&input).unwrap(); + /// let mut encoded = vec![0u8; buffer_len]; + /// + /// // Encode the data and get its length + /// let encoded_len = encrypter.encrypt(&input, &mut encoded).unwrap(); + /// + /// // Use only the part of the buffer with the encoded data + /// let encoded = &encoded[..encoded_len]; + /// ``` + /// + /// This corresponds to [`EVP_PKEY_encrypt`]. + /// + /// [`EVP_PKEY_encrypt`]: https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_encrypt.html + pub fn encrypt(&self, from: &[u8], to: &mut [u8]) -> Result<usize, ErrorStack> { + let mut written = to.len(); + unsafe { + cvt(ffi::EVP_PKEY_encrypt( + self.pctx, + to.as_mut_ptr(), + &mut written, + from.as_ptr(), + from.len(), + ))?; + } + + Ok(written) + } + + /// Gets the size of the buffer needed to encrypt the input data. + /// + /// This corresponds to [`EVP_PKEY_encrypt`] called with a null pointer as output argument. + /// + /// [`EVP_PKEY_encrypt`]: https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_encrypt.html + pub fn encrypt_len(&self, from: &[u8]) -> Result<usize, ErrorStack> { + let mut written = 0; + unsafe { + cvt(ffi::EVP_PKEY_encrypt( + self.pctx, + ptr::null_mut(), + &mut written, + from.as_ptr(), + from.len(), + ))?; + } + + Ok(written) + } +} + +/// A type which decrypts data. +pub struct Decrypter<'a> { + pctx: *mut ffi::EVP_PKEY_CTX, + _p: PhantomData<&'a ()>, +} + +unsafe impl<'a> Sync for Decrypter<'a> {} +unsafe impl<'a> Send for Decrypter<'a> {} + +impl<'a> Drop for Decrypter<'a> { + fn drop(&mut self) { + unsafe { + ffi::EVP_PKEY_CTX_free(self.pctx); + } + } +} + +impl<'a> Decrypter<'a> { + /// Creates a new `Decrypter`. + /// + /// OpenSSL documentation at [`EVP_PKEY_decrypt_init`]. + /// + /// [`EVP_PKEY_decrypt_init`]: https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_decrypt_init.html + pub fn new<T>(pkey: &'a PKeyRef<T>) -> Result<Decrypter<'a>, ErrorStack> + where + T: HasPrivate, + { + unsafe { + ffi::init(); + + let pctx = cvt_p(ffi::EVP_PKEY_CTX_new(pkey.as_ptr(), ptr::null_mut()))?; + let r = ffi::EVP_PKEY_decrypt_init(pctx); + if r != 1 { + ffi::EVP_PKEY_CTX_free(pctx); + return Err(ErrorStack::get()); + } + + Ok(Decrypter { + pctx, + _p: PhantomData, + }) + } + } + + /// Returns the RSA padding mode in use. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to `EVP_PKEY_CTX_get_rsa_padding`. + pub fn rsa_padding(&self) -> Result<Padding, ErrorStack> { + unsafe { + let mut pad = 0; + cvt(ffi::EVP_PKEY_CTX_get_rsa_padding(self.pctx, &mut pad)) + .map(|_| Padding::from_raw(pad)) + } + } + + /// Sets the RSA padding mode. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to [`EVP_PKEY_CTX_set_rsa_padding`]. + /// + /// [`EVP_PKEY_CTX_set_rsa_padding`]: https://www.openssl.org/docs/manmaster/crypto/EVP_PKEY_CTX_set_rsa_padding.html + pub fn set_rsa_padding(&mut self, padding: Padding) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_rsa_padding( + self.pctx, + padding.as_raw(), + )) + .map(|_| ()) + } + } + + /// Sets the RSA MGF1 algorithm. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to [`EVP_PKEY_CTX_set_rsa_mgf1_md`]. + /// + /// [`EVP_PKEY_CTX_set_rsa_mgf1_md`]: https://www.openssl.org/docs/manmaster/man7/RSA-PSS.html + pub fn set_rsa_mgf1_md(&mut self, md: MessageDigest) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_rsa_mgf1_md( + self.pctx, + md.as_ptr() as *mut _, + )) + .map(|_| ()) + } + } + + /// Sets the RSA OAEP algorithm. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to [`EVP_PKEY_CTX_set_rsa_oaep_md`]. + /// + /// [`EVP_PKEY_CTX_set_rsa_oaep_md`]: https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_set_rsa_oaep_md.html + #[cfg(any(ossl102, libressl310))] + pub fn set_rsa_oaep_md(&mut self, md: MessageDigest) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_rsa_oaep_md( + self.pctx, + md.as_ptr() as *mut _, + )) + .map(|_| ()) + } + } + + /// Sets the RSA OAEP label. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to [`EVP_PKEY_CTX_set0_rsa_oaep_label`]. + /// + /// [`EVP_PKEY_CTX_set0_rsa_oaep_label`]: https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_set0_rsa_oaep_label.html + #[cfg(any(ossl102, libressl310))] + pub fn set_rsa_oaep_label(&mut self, label: &[u8]) -> Result<(), ErrorStack> { + unsafe { + let p = cvt_p(ffi::OPENSSL_malloc(label.len() as _))?; + ptr::copy_nonoverlapping(label.as_ptr(), p as *mut u8, label.len()); + + cvt(ffi::EVP_PKEY_CTX_set0_rsa_oaep_label( + self.pctx, + p, + label.len() as c_int, + )) + .map(|_| ()) + .map_err(|e| { + ffi::OPENSSL_free(p); + e + }) + } + } + + /// Performs public key decryption. + /// + /// In order to know the size needed for the output buffer, use [`decrypt_len`](Decrypter::decrypt_len). + /// Note that the length of the output buffer can be greater of the length of the decoded data. + /// ``` + /// # use openssl::{ + /// # encrypt::Decrypter, + /// # pkey::PKey, + /// # rsa::{Rsa, Padding}, + /// # }; + /// # + /// # const INPUT: &[u8] = b"\ + /// # \x26\xa1\xc1\x13\xc5\x7f\xb4\x9f\xa0\xb4\xde\x61\x5e\x2e\xc6\xfb\x76\x5c\xd1\x2b\x5f\ + /// # \x1d\x36\x60\xfa\xf8\xe8\xb3\x21\xf4\x9c\x70\xbc\x03\xea\xea\xac\xce\x4b\xb3\xf6\x45\ + /// # \xcc\xb3\x80\x9e\xa8\xf7\xc3\x5d\x06\x12\x7a\xa3\x0c\x30\x67\xf1\xe7\x94\x6c\xf6\x26\ + /// # \xac\x28\x17\x59\x69\xe1\xdc\xed\x7e\xc0\xe9\x62\x57\x49\xce\xdd\x13\x07\xde\x18\x03\ + /// # \x0f\x9d\x61\x65\xb9\x23\x8c\x78\x4b\xad\x23\x49\x75\x47\x64\xa0\xa0\xa2\x90\xc1\x49\ + /// # \x1b\x05\x24\xc2\xe9\x2c\x0d\x49\x78\x72\x61\x72\xed\x8b\x6f\x8a\xe8\xca\x05\x5c\x58\ + /// # \xd6\x95\xd6\x7b\xe3\x2d\x0d\xaa\x3e\x6d\x3c\x9a\x1c\x1d\xb4\x6c\x42\x9d\x9a\x82\x55\ + /// # \xd9\xde\xc8\x08\x7b\x17\xac\xd7\xaf\x86\x7b\x69\x9e\x3c\xf4\x5e\x1c\x39\x52\x6d\x62\ + /// # \x50\x51\xbd\xa6\xc8\x4e\xe9\x34\xf0\x37\x0d\xa9\xa9\x77\xe6\xf5\xc2\x47\x2d\xa8\xee\ + /// # \x3f\x69\x78\xff\xa9\xdc\x70\x22\x20\x9a\x5c\x9b\x70\x15\x90\xd3\xb4\x0e\x54\x9e\x48\ + /// # \xed\xb6\x2c\x88\xfc\xb4\xa9\x37\x10\xfa\x71\xb2\xec\x75\xe7\xe7\x0e\xf4\x60\x2c\x7b\ + /// # \x58\xaf\xa0\x53\xbd\x24\xf1\x12\xe3\x2e\x99\x25\x0a\x54\x54\x9d\xa1\xdb\xca\x41\x85\ + /// # \xf4\x62\x78\x64"; + /// # + /// # let key = include_bytes!("../test/rsa.pem"); + /// # let private_key = Rsa::private_key_from_pem(key).unwrap(); + /// # let pkey = PKey::from_rsa(private_key).unwrap(); + /// # let input = INPUT.to_vec(); + /// # + /// let mut decrypter = Decrypter::new(&pkey).unwrap(); + /// decrypter.set_rsa_padding(Padding::PKCS1).unwrap(); + /// + /// // Get the length of the output buffer + /// let buffer_len = decrypter.decrypt_len(&input).unwrap(); + /// let mut decoded = vec![0u8; buffer_len]; + /// + /// // Decrypt the data and get its length + /// let decoded_len = decrypter.decrypt(&input, &mut decoded).unwrap(); + /// + /// // Use only the part of the buffer with the decrypted data + /// let decoded = &decoded[..decoded_len]; + /// ``` + /// + /// This corresponds to [`EVP_PKEY_decrypt`]. + /// + /// [`EVP_PKEY_decrypt`]: https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_decrypt.html + pub fn decrypt(&self, from: &[u8], to: &mut [u8]) -> Result<usize, ErrorStack> { + let mut written = to.len(); + unsafe { + cvt(ffi::EVP_PKEY_decrypt( + self.pctx, + to.as_mut_ptr(), + &mut written, + from.as_ptr(), + from.len(), + ))?; + } + + Ok(written) + } + + /// Gets the size of the buffer needed to decrypt the input data. + /// + /// This corresponds to [`EVP_PKEY_decrypt`] called with a null pointer as output argument. + /// + /// [`EVP_PKEY_decrypt`]: https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_decrypt.html + pub fn decrypt_len(&self, from: &[u8]) -> Result<usize, ErrorStack> { + let mut written = 0; + unsafe { + cvt(ffi::EVP_PKEY_decrypt( + self.pctx, + ptr::null_mut(), + &mut written, + from.as_ptr(), + from.len(), + ))?; + } + + Ok(written) + } +} + +#[cfg(test)] +mod test { + use hex::FromHex; + + use crate::encrypt::{Decrypter, Encrypter}; + #[cfg(any(ossl102, libressl310))] + use crate::hash::MessageDigest; + use crate::pkey::PKey; + use crate::rsa::{Padding, Rsa}; + + const INPUT: &str = + "65794a68624763694f694a53557a49314e694a392e65794a7063334d694f694a71623255694c41304b49434a6c\ + 654841694f6a457a4d4441344d546b7a4f44417344516f67496d6830644841364c79396c654746746347786c4c\ + 6d4e76625339706331397962323930496a7030636e566c6651"; + + #[test] + fn rsa_encrypt_decrypt() { + let key = include_bytes!("../test/rsa.pem"); + let private_key = Rsa::private_key_from_pem(key).unwrap(); + let pkey = PKey::from_rsa(private_key).unwrap(); + + let mut encrypter = Encrypter::new(&pkey).unwrap(); + encrypter.set_rsa_padding(Padding::PKCS1).unwrap(); + let input = Vec::from_hex(INPUT).unwrap(); + let buffer_len = encrypter.encrypt_len(&input).unwrap(); + let mut encoded = vec![0u8; buffer_len]; + let encoded_len = encrypter.encrypt(&input, &mut encoded).unwrap(); + let encoded = &encoded[..encoded_len]; + + let mut decrypter = Decrypter::new(&pkey).unwrap(); + decrypter.set_rsa_padding(Padding::PKCS1).unwrap(); + let buffer_len = decrypter.decrypt_len(encoded).unwrap(); + let mut decoded = vec![0u8; buffer_len]; + let decoded_len = decrypter.decrypt(encoded, &mut decoded).unwrap(); + let decoded = &decoded[..decoded_len]; + + assert_eq!(decoded, &*input); + } + + #[test] + #[cfg(any(ossl102, libressl310))] + fn rsa_encrypt_decrypt_with_sha256() { + let key = include_bytes!("../test/rsa.pem"); + let private_key = Rsa::private_key_from_pem(key).unwrap(); + let pkey = PKey::from_rsa(private_key).unwrap(); + + let md = MessageDigest::sha256(); + + let mut encrypter = Encrypter::new(&pkey).unwrap(); + encrypter.set_rsa_padding(Padding::PKCS1_OAEP).unwrap(); + encrypter.set_rsa_oaep_md(md).unwrap(); + encrypter.set_rsa_mgf1_md(md).unwrap(); + let input = Vec::from_hex(INPUT).unwrap(); + let buffer_len = encrypter.encrypt_len(&input).unwrap(); + let mut encoded = vec![0u8; buffer_len]; + let encoded_len = encrypter.encrypt(&input, &mut encoded).unwrap(); + let encoded = &encoded[..encoded_len]; + + let mut decrypter = Decrypter::new(&pkey).unwrap(); + decrypter.set_rsa_padding(Padding::PKCS1_OAEP).unwrap(); + decrypter.set_rsa_oaep_md(md).unwrap(); + decrypter.set_rsa_mgf1_md(md).unwrap(); + let buffer_len = decrypter.decrypt_len(encoded).unwrap(); + let mut decoded = vec![0u8; buffer_len]; + let decoded_len = decrypter.decrypt(encoded, &mut decoded).unwrap(); + let decoded = &decoded[..decoded_len]; + + assert_eq!(decoded, &*input); + } + + #[test] + #[cfg(any(ossl102, libressl310))] + fn rsa_encrypt_decrypt_oaep_label() { + let key = include_bytes!("../test/rsa.pem"); + let private_key = Rsa::private_key_from_pem(key).unwrap(); + let pkey = PKey::from_rsa(private_key).unwrap(); + + let mut encrypter = Encrypter::new(&pkey).unwrap(); + encrypter.set_rsa_padding(Padding::PKCS1_OAEP).unwrap(); + encrypter.set_rsa_oaep_label(b"test_oaep_label").unwrap(); + let input = Vec::from_hex(INPUT).unwrap(); + let buffer_len = encrypter.encrypt_len(&input).unwrap(); + let mut encoded = vec![0u8; buffer_len]; + let encoded_len = encrypter.encrypt(&input, &mut encoded).unwrap(); + let encoded = &encoded[..encoded_len]; + + let mut decrypter = Decrypter::new(&pkey).unwrap(); + decrypter.set_rsa_padding(Padding::PKCS1_OAEP).unwrap(); + decrypter.set_rsa_oaep_label(b"test_oaep_label").unwrap(); + let buffer_len = decrypter.decrypt_len(encoded).unwrap(); + let mut decoded = vec![0u8; buffer_len]; + let decoded_len = decrypter.decrypt(encoded, &mut decoded).unwrap(); + let decoded = &decoded[..decoded_len]; + + assert_eq!(decoded, &*input); + + decrypter.set_rsa_oaep_label(b"wrong_oaep_label").unwrap(); + let buffer_len = decrypter.decrypt_len(encoded).unwrap(); + let mut decoded = vec![0u8; buffer_len]; + + assert!(decrypter.decrypt(encoded, &mut decoded).is_err()); + } +} diff --git a/vendor/openssl/src/envelope.rs b/vendor/openssl/src/envelope.rs new file mode 100644 index 0000000..25055ac --- /dev/null +++ b/vendor/openssl/src/envelope.rs @@ -0,0 +1,181 @@ +//! Envelope encryption. +//! +//! # Example +//! +//! ```rust +//! use openssl::rsa::Rsa; +//! use openssl::envelope::Seal; +//! use openssl::pkey::PKey; +//! use openssl::symm::Cipher; +//! +//! let rsa = Rsa::generate(2048).unwrap(); +//! let key = PKey::from_rsa(rsa).unwrap(); +//! +//! let cipher = Cipher::aes_256_cbc(); +//! let mut seal = Seal::new(cipher, &[key]).unwrap(); +//! +//! let secret = b"My secret message"; +//! let mut encrypted = vec![0; secret.len() + cipher.block_size()]; +//! +//! let mut enc_len = seal.update(secret, &mut encrypted).unwrap(); +//! enc_len += seal.finalize(&mut encrypted[enc_len..]).unwrap(); +//! encrypted.truncate(enc_len); +//! ``` +use crate::cipher::CipherRef; +use crate::cipher_ctx::CipherCtx; +use crate::error::ErrorStack; +use crate::pkey::{HasPrivate, HasPublic, PKey, PKeyRef}; +use crate::symm::Cipher; +use foreign_types::ForeignTypeRef; + +/// Represents an EVP_Seal context. +pub struct Seal { + ctx: CipherCtx, + iv: Option<Vec<u8>>, + enc_keys: Vec<Vec<u8>>, +} + +impl Seal { + /// Creates a new `Seal`. + pub fn new<T>(cipher: Cipher, pub_keys: &[PKey<T>]) -> Result<Seal, ErrorStack> + where + T: HasPublic, + { + let mut iv = cipher.iv_len().map(|len| vec![0; len]); + let mut enc_keys = vec![vec![]; pub_keys.len()]; + + let mut ctx = CipherCtx::new()?; + ctx.seal_init( + Some(unsafe { CipherRef::from_ptr(cipher.as_ptr() as *mut _) }), + pub_keys, + &mut enc_keys, + iv.as_deref_mut(), + )?; + + Ok(Seal { ctx, iv, enc_keys }) + } + + /// Returns the initialization vector, if the cipher uses one. + #[allow(clippy::option_as_ref_deref)] + pub fn iv(&self) -> Option<&[u8]> { + self.iv.as_ref().map(|v| &**v) + } + + /// Returns the encrypted keys. + pub fn encrypted_keys(&self) -> &[Vec<u8>] { + &self.enc_keys + } + + /// Feeds data from `input` through the cipher, writing encrypted bytes into `output`. + /// + /// The number of bytes written to `output` is returned. Note that this may + /// not be equal to the length of `input`. + /// + /// # Panics + /// + /// Panics if `output.len() < input.len() + block_size` where `block_size` is + /// the block size of the cipher (see `Cipher::block_size`), or if + /// `output.len() > c_int::max_value()`. + pub fn update(&mut self, input: &[u8], output: &mut [u8]) -> Result<usize, ErrorStack> { + self.ctx.cipher_update(input, Some(output)) + } + + /// Finishes the encryption process, writing any remaining data to `output`. + /// + /// The number of bytes written to `output` is returned. + /// + /// `update` should not be called after this method. + /// + /// # Panics + /// + /// Panics if `output` is less than the cipher's block size. + pub fn finalize(&mut self, output: &mut [u8]) -> Result<usize, ErrorStack> { + self.ctx.cipher_final(output) + } +} + +/// Represents an EVP_Open context. +pub struct Open { + ctx: CipherCtx, +} + +impl Open { + /// Creates a new `Open`. + pub fn new<T>( + cipher: Cipher, + priv_key: &PKeyRef<T>, + iv: Option<&[u8]>, + encrypted_key: &[u8], + ) -> Result<Open, ErrorStack> + where + T: HasPrivate, + { + let mut ctx = CipherCtx::new()?; + ctx.open_init( + Some(unsafe { CipherRef::from_ptr(cipher.as_ptr() as *mut _) }), + encrypted_key, + iv, + Some(priv_key), + )?; + + Ok(Open { ctx }) + } + + /// Feeds data from `input` through the cipher, writing decrypted bytes into `output`. + /// + /// The number of bytes written to `output` is returned. Note that this may + /// not be equal to the length of `input`. + /// + /// # Panics + /// + /// Panics if `output.len() < input.len() + block_size` where + /// `block_size` is the block size of the cipher (see `Cipher::block_size`), + /// or if `output.len() > c_int::max_value()`. + pub fn update(&mut self, input: &[u8], output: &mut [u8]) -> Result<usize, ErrorStack> { + self.ctx.cipher_update(input, Some(output)) + } + + /// Finishes the decryption process, writing any remaining data to `output`. + /// + /// The number of bytes written to `output` is returned. + /// + /// `update` should not be called after this method. + /// + /// # Panics + /// + /// Panics if `output` is less than the cipher's block size. + pub fn finalize(&mut self, output: &mut [u8]) -> Result<usize, ErrorStack> { + self.ctx.cipher_final(output) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::pkey::PKey; + use crate::symm::Cipher; + + #[test] + fn public_encrypt_private_decrypt() { + let private_pem = include_bytes!("../test/rsa.pem"); + let public_pem = include_bytes!("../test/rsa.pem.pub"); + let private_key = PKey::private_key_from_pem(private_pem).unwrap(); + let public_key = PKey::public_key_from_pem(public_pem).unwrap(); + let cipher = Cipher::aes_256_cbc(); + let secret = b"My secret message"; + + let mut seal = Seal::new(cipher, &[public_key]).unwrap(); + let mut encrypted = vec![0; secret.len() + cipher.block_size()]; + let mut enc_len = seal.update(secret, &mut encrypted).unwrap(); + enc_len += seal.finalize(&mut encrypted[enc_len..]).unwrap(); + let iv = seal.iv(); + let encrypted_key = &seal.encrypted_keys()[0]; + + let mut open = Open::new(cipher, &private_key, iv, encrypted_key).unwrap(); + let mut decrypted = vec![0; enc_len + cipher.block_size()]; + let mut dec_len = open.update(&encrypted[..enc_len], &mut decrypted).unwrap(); + dec_len += open.finalize(&mut decrypted[dec_len..]).unwrap(); + + assert_eq!(&secret[..], &decrypted[..dec_len]); + } +} diff --git a/vendor/openssl/src/error.rs b/vendor/openssl/src/error.rs new file mode 100644 index 0000000..e097ce6 --- /dev/null +++ b/vendor/openssl/src/error.rs @@ -0,0 +1,418 @@ +//! Errors returned by OpenSSL library. +//! +//! OpenSSL errors are stored in an `ErrorStack`. Most methods in the crate +//! returns a `Result<T, ErrorStack>` type. +//! +//! # Examples +//! +//! ``` +//! use openssl::error::ErrorStack; +//! use openssl::bn::BigNum; +//! +//! let an_error = BigNum::from_dec_str("Cannot parse letters"); +//! match an_error { +//! Ok(_) => (), +//! Err(e) => println!("Parsing Error: {:?}", e), +//! } +//! ``` +use cfg_if::cfg_if; +use libc::{c_char, c_int}; +use std::borrow::Cow; +#[cfg(boringssl)] +use std::convert::TryInto; +use std::error; +use std::ffi::CStr; +use std::fmt; +use std::io; +use std::ptr; +use std::str; + +#[cfg(not(boringssl))] +type ErrType = libc::c_ulong; +#[cfg(boringssl)] +type ErrType = libc::c_uint; + +/// Collection of [`Error`]s from OpenSSL. +/// +/// [`Error`]: struct.Error.html +#[derive(Debug, Clone)] +pub struct ErrorStack(Vec<Error>); + +impl ErrorStack { + /// Returns the contents of the OpenSSL error stack. + pub fn get() -> ErrorStack { + let mut vec = vec![]; + while let Some(err) = Error::get() { + vec.push(err); + } + ErrorStack(vec) + } + + /// Pushes the errors back onto the OpenSSL error stack. + pub fn put(&self) { + for error in self.errors() { + error.put(); + } + } +} + +impl ErrorStack { + /// Returns the errors in the stack. + pub fn errors(&self) -> &[Error] { + &self.0 + } +} + +impl fmt::Display for ErrorStack { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.0.is_empty() { + return fmt.write_str("OpenSSL error"); + } + + let mut first = true; + for err in &self.0 { + if !first { + fmt.write_str(", ")?; + } + write!(fmt, "{}", err)?; + first = false; + } + Ok(()) + } +} + +impl error::Error for ErrorStack {} + +impl From<ErrorStack> for io::Error { + fn from(e: ErrorStack) -> io::Error { + io::Error::new(io::ErrorKind::Other, e) + } +} + +impl From<ErrorStack> for fmt::Error { + fn from(_: ErrorStack) -> fmt::Error { + fmt::Error + } +} + +/// An error reported from OpenSSL. +#[derive(Clone)] +pub struct Error { + code: ErrType, + file: ShimStr, + line: c_int, + func: Option<ShimStr>, + data: Option<Cow<'static, str>>, +} + +unsafe impl Sync for Error {} +unsafe impl Send for Error {} + +impl Error { + /// Returns the first error on the OpenSSL error stack. + pub fn get() -> Option<Error> { + unsafe { + ffi::init(); + + let mut file = ptr::null(); + let mut line = 0; + let mut func = ptr::null(); + let mut data = ptr::null(); + let mut flags = 0; + match ERR_get_error_all(&mut file, &mut line, &mut func, &mut data, &mut flags) { + 0 => None, + code => { + // The memory referenced by data is only valid until that slot is overwritten + // in the error stack, so we'll need to copy it off if it's dynamic + let data = if flags & ffi::ERR_TXT_STRING != 0 { + let bytes = CStr::from_ptr(data as *const _).to_bytes(); + let data = str::from_utf8(bytes).unwrap(); + #[cfg(not(boringssl))] + let data = if flags & ffi::ERR_TXT_MALLOCED != 0 { + Cow::Owned(data.to_string()) + } else { + Cow::Borrowed(data) + }; + #[cfg(boringssl)] + let data = Cow::Borrowed(data); + Some(data) + } else { + None + }; + + let file = ShimStr::new(file); + + let func = if func.is_null() { + None + } else { + Some(ShimStr::new(func)) + }; + + Some(Error { + code, + file, + line, + func, + data, + }) + } + } + } + } + + /// Pushes the error back onto the OpenSSL error stack. + pub fn put(&self) { + self.put_error(); + + unsafe { + let data = match self.data { + Some(Cow::Borrowed(data)) => Some((data.as_ptr() as *mut c_char, 0)), + Some(Cow::Owned(ref data)) => { + let ptr = ffi::CRYPTO_malloc( + (data.len() + 1) as _, + concat!(file!(), "\0").as_ptr() as _, + line!() as _, + ) as *mut c_char; + if ptr.is_null() { + None + } else { + ptr::copy_nonoverlapping(data.as_ptr(), ptr as *mut u8, data.len()); + *ptr.add(data.len()) = 0; + Some((ptr, ffi::ERR_TXT_MALLOCED)) + } + } + None => None, + }; + if let Some((ptr, flags)) = data { + ffi::ERR_set_error_data(ptr, flags | ffi::ERR_TXT_STRING); + } + } + } + + #[cfg(ossl300)] + fn put_error(&self) { + unsafe { + ffi::ERR_new(); + ffi::ERR_set_debug( + self.file.as_ptr(), + self.line, + self.func.as_ref().map_or(ptr::null(), |s| s.as_ptr()), + ); + ffi::ERR_set_error(self.library_code(), self.reason_code(), ptr::null()); + } + } + + #[cfg(not(ossl300))] + fn put_error(&self) { + #[cfg(not(boringssl))] + let line = self.line; + #[cfg(boringssl)] + let line = self.line.try_into().unwrap(); + unsafe { + ffi::ERR_put_error( + self.library_code(), + ffi::ERR_GET_FUNC(self.code), + self.reason_code(), + self.file.as_ptr(), + line, + ); + } + } + + /// Returns the raw OpenSSL error code for this error. + pub fn code(&self) -> ErrType { + self.code + } + + /// Returns the name of the library reporting the error, if available. + pub fn library(&self) -> Option<&'static str> { + unsafe { + let cstr = ffi::ERR_lib_error_string(self.code); + if cstr.is_null() { + return None; + } + let bytes = CStr::from_ptr(cstr as *const _).to_bytes(); + Some(str::from_utf8(bytes).unwrap()) + } + } + + /// Returns the raw OpenSSL error constant for the library reporting the + /// error. + // On BoringSSL ERR_GET_{LIB,FUNC,REASON} are `unsafe`, but on + // OpenSSL/LibreSSL they're safe. + #[allow(unused_unsafe)] + pub fn library_code(&self) -> libc::c_int { + unsafe { ffi::ERR_GET_LIB(self.code) } + } + + /// Returns the name of the function reporting the error. + pub fn function(&self) -> Option<RetStr<'_>> { + self.func.as_ref().map(|s| s.as_str()) + } + + /// Returns the reason for the error. + pub fn reason(&self) -> Option<&'static str> { + unsafe { + let cstr = ffi::ERR_reason_error_string(self.code); + if cstr.is_null() { + return None; + } + let bytes = CStr::from_ptr(cstr as *const _).to_bytes(); + Some(str::from_utf8(bytes).unwrap()) + } + } + + /// Returns the raw OpenSSL error constant for the reason for the error. + // On BoringSSL ERR_GET_{LIB,FUNC,REASON} are `unsafe`, but on + // OpenSSL/LibreSSL they're safe. + #[allow(unused_unsafe)] + pub fn reason_code(&self) -> libc::c_int { + unsafe { ffi::ERR_GET_REASON(self.code) } + } + + /// Returns the name of the source file which encountered the error. + pub fn file(&self) -> RetStr<'_> { + self.file.as_str() + } + + /// Returns the line in the source file which encountered the error. + pub fn line(&self) -> u32 { + self.line as u32 + } + + /// Returns additional data describing the error. + #[allow(clippy::option_as_ref_deref)] + pub fn data(&self) -> Option<&str> { + self.data.as_ref().map(|s| &**s) + } +} + +impl fmt::Debug for Error { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut builder = fmt.debug_struct("Error"); + builder.field("code", &self.code()); + if let Some(library) = self.library() { + builder.field("library", &library); + } + if let Some(function) = self.function() { + builder.field("function", &function); + } + if let Some(reason) = self.reason() { + builder.field("reason", &reason); + } + builder.field("file", &self.file()); + builder.field("line", &self.line()); + if let Some(data) = self.data() { + builder.field("data", &data); + } + builder.finish() + } +} + +impl fmt::Display for Error { + // On BoringSSL ERR_GET_{LIB,FUNC,REASON} are `unsafe`, but on + // OpenSSL/LibreSSL they're safe. + #[allow(unused_unsafe)] + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "error:{:08X}", self.code())?; + match self.library() { + Some(l) => write!(fmt, ":{}", l)?, + None => write!(fmt, ":lib({})", self.library_code())?, + } + match self.function() { + Some(f) => write!(fmt, ":{}", f)?, + None => write!(fmt, ":func({})", unsafe { ffi::ERR_GET_FUNC(self.code()) })?, + } + match self.reason() { + Some(r) => write!(fmt, ":{}", r)?, + None => write!(fmt, ":reason({})", self.reason_code())?, + } + write!( + fmt, + ":{}:{}:{}", + self.file(), + self.line(), + self.data().unwrap_or("") + ) + } +} + +impl error::Error for Error {} + +cfg_if! { + if #[cfg(ossl300)] { + use std::ffi::{CString}; + use ffi::ERR_get_error_all; + + type RetStr<'a> = &'a str; + + #[derive(Clone)] + struct ShimStr(CString); + + impl ShimStr { + unsafe fn new(s: *const c_char) -> Self { + ShimStr(CStr::from_ptr(s).to_owned()) + } + + fn as_ptr(&self) -> *const c_char { + self.0.as_ptr() + } + + fn as_str(&self) -> &str { + self.0.to_str().unwrap() + } + } + } else { + #[allow(bad_style)] + unsafe extern "C" fn ERR_get_error_all( + file: *mut *const c_char, + line: *mut c_int, + func: *mut *const c_char, + data: *mut *const c_char, + flags: *mut c_int, + ) -> ErrType { + let code = ffi::ERR_get_error_line_data(file, line, data, flags); + *func = ffi::ERR_func_error_string(code); + code + } + + type RetStr<'a> = &'static str; + + #[derive(Clone)] + struct ShimStr(*const c_char); + + impl ShimStr { + unsafe fn new(s: *const c_char) -> Self { + ShimStr(s) + } + + fn as_ptr(&self) -> *const c_char { + self.0 + } + + fn as_str(&self) -> &'static str { + unsafe { + CStr::from_ptr(self.0).to_str().unwrap() + } + } + } + } +} + +#[cfg(test)] +mod tests { + #[cfg(not(ossl310))] + use crate::nid::Nid; + + #[test] + // Due to a bug in OpenSSL 3.1.0, this test can hang there. Skip for now. + #[cfg(not(ossl310))] + fn test_error_library_code() { + let stack = Nid::create("not-an-oid", "invalid", "invalid").unwrap_err(); + let errors = stack.errors(); + #[cfg(not(boringssl))] + assert_eq!(errors[0].library_code(), ffi::ERR_LIB_ASN1); + #[cfg(boringssl)] + assert_eq!(errors[0].library_code(), ffi::ERR_LIB_OBJ as libc::c_int); + } +} diff --git a/vendor/openssl/src/ex_data.rs b/vendor/openssl/src/ex_data.rs new file mode 100644 index 0000000..d4f0021 --- /dev/null +++ b/vendor/openssl/src/ex_data.rs @@ -0,0 +1,32 @@ +use libc::c_int; +use std::marker::PhantomData; + +/// A slot in a type's "extra data" structure. +/// +/// It is parameterized over the type containing the extra data as well as the +/// type of the data in the slot. +pub struct Index<T, U>(c_int, PhantomData<(T, U)>); + +impl<T, U> Copy for Index<T, U> {} + +impl<T, U> Clone for Index<T, U> { + fn clone(&self) -> Index<T, U> { + *self + } +} + +impl<T, U> Index<T, U> { + /// Creates an `Index` from a raw integer index. + /// + /// # Safety + /// + /// The caller must ensure that the index correctly maps to a `U` value stored in a `T`. + pub unsafe fn from_raw(idx: c_int) -> Index<T, U> { + Index(idx, PhantomData) + } + + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn as_raw(&self) -> c_int { + self.0 + } +} diff --git a/vendor/openssl/src/fips.rs b/vendor/openssl/src/fips.rs new file mode 100644 index 0000000..2a8a2fe --- /dev/null +++ b/vendor/openssl/src/fips.rs @@ -0,0 +1,21 @@ +//! FIPS 140-2 support. +//! +//! See [OpenSSL's documentation] for details. +//! +//! [OpenSSL's documentation]: https://www.openssl.org/docs/fips/UserGuide-2.0.pdf +use crate::cvt; +use crate::error::ErrorStack; +use openssl_macros::corresponds; + +/// Moves the library into or out of the FIPS 140-2 mode of operation. +#[corresponds(FIPS_mode_set)] +pub fn enable(enabled: bool) -> Result<(), ErrorStack> { + ffi::init(); + unsafe { cvt(ffi::FIPS_mode_set(enabled as _)).map(|_| ()) } +} + +/// Determines if the library is running in the FIPS 140-2 mode of operation. +#[corresponds(FIPS_mode)] +pub fn enabled() -> bool { + unsafe { ffi::FIPS_mode() != 0 } +} diff --git a/vendor/openssl/src/hash.rs b/vendor/openssl/src/hash.rs new file mode 100644 index 0000000..52d73de --- /dev/null +++ b/vendor/openssl/src/hash.rs @@ -0,0 +1,800 @@ +//! Message digest (hash) computation support. +//! +//! # Examples +//! +//! Calculate a hash in one go: +//! +//! ``` +//! # fn main() -> Result<(), Box<dyn std::error::Error>> { +//! use openssl::hash::{hash, MessageDigest}; +//! +//! let data = b"\x42\xF4\x97\xE0"; +//! let spec = b"\x7c\x43\x0f\x17\x8a\xef\xdf\x14\x87\xfe\xe7\x14\x4e\x96\x41\xe2"; +//! let res = hash(MessageDigest::md5(), data)?; +//! assert_eq!(&*res, spec); +//! # Ok(()) } +//! ``` +//! +//! Supply the input in chunks: +//! +//! ``` +//! use openssl::hash::{Hasher, MessageDigest}; +//! +//! # fn main() -> Result<(), Box<dyn std::error::Error>> { +//! let mut hasher = Hasher::new(MessageDigest::sha256())?; +//! hasher.update(b"test")?; +//! hasher.update(b"this")?; +//! let digest: &[u8] = &hasher.finish()?; +//! +//! let expected = hex::decode("9740e652ab5b4acd997a7cca13d6696702ccb2d441cca59fc6e285127f28cfe6")?; +//! assert_eq!(digest, expected); +//! # Ok(()) } +//! ``` +use cfg_if::cfg_if; +use std::ffi::CString; +use std::fmt; +use std::io; +use std::io::prelude::*; +use std::ops::{Deref, DerefMut}; +use std::ptr; + +use crate::error::ErrorStack; +use crate::nid::Nid; +use crate::{cvt, cvt_p}; + +cfg_if! { + if #[cfg(any(ossl110, boringssl))] { + use ffi::{EVP_MD_CTX_free, EVP_MD_CTX_new}; + } else { + use ffi::{EVP_MD_CTX_create as EVP_MD_CTX_new, EVP_MD_CTX_destroy as EVP_MD_CTX_free}; + } +} + +/// A message digest algorithm. +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct MessageDigest(*const ffi::EVP_MD); + +impl MessageDigest { + /// Creates a `MessageDigest` from a raw OpenSSL pointer. + /// + /// # Safety + /// + /// The caller must ensure the pointer is valid. + pub unsafe fn from_ptr(x: *const ffi::EVP_MD) -> Self { + MessageDigest(x) + } + + /// Returns the `MessageDigest` corresponding to an `Nid`. + /// + /// This corresponds to [`EVP_get_digestbynid`]. + /// + /// [`EVP_get_digestbynid`]: https://www.openssl.org/docs/manmaster/crypto/EVP_DigestInit.html + pub fn from_nid(type_: Nid) -> Option<MessageDigest> { + unsafe { + let ptr = ffi::EVP_get_digestbynid(type_.as_raw()); + if ptr.is_null() { + None + } else { + Some(MessageDigest(ptr)) + } + } + } + + /// Returns the `MessageDigest` corresponding to an algorithm name. + /// + /// This corresponds to [`EVP_get_digestbyname`]. + /// + /// [`EVP_get_digestbyname`]: https://www.openssl.org/docs/manmaster/crypto/EVP_DigestInit.html + pub fn from_name(name: &str) -> Option<MessageDigest> { + ffi::init(); + let name = CString::new(name).ok()?; + unsafe { + let ptr = ffi::EVP_get_digestbyname(name.as_ptr()); + if ptr.is_null() { + None + } else { + Some(MessageDigest(ptr)) + } + } + } + + #[cfg(not(boringssl))] + pub fn null() -> MessageDigest { + unsafe { MessageDigest(ffi::EVP_md_null()) } + } + + pub fn md5() -> MessageDigest { + unsafe { MessageDigest(ffi::EVP_md5()) } + } + + pub fn sha1() -> MessageDigest { + unsafe { MessageDigest(ffi::EVP_sha1()) } + } + + pub fn sha224() -> MessageDigest { + unsafe { MessageDigest(ffi::EVP_sha224()) } + } + + pub fn sha256() -> MessageDigest { + unsafe { MessageDigest(ffi::EVP_sha256()) } + } + + pub fn sha384() -> MessageDigest { + unsafe { MessageDigest(ffi::EVP_sha384()) } + } + + pub fn sha512() -> MessageDigest { + unsafe { MessageDigest(ffi::EVP_sha512()) } + } + + #[cfg(ossl111)] + pub fn sha3_224() -> MessageDigest { + unsafe { MessageDigest(ffi::EVP_sha3_224()) } + } + + #[cfg(ossl111)] + pub fn sha3_256() -> MessageDigest { + unsafe { MessageDigest(ffi::EVP_sha3_256()) } + } + + #[cfg(ossl111)] + pub fn sha3_384() -> MessageDigest { + unsafe { MessageDigest(ffi::EVP_sha3_384()) } + } + + #[cfg(ossl111)] + pub fn sha3_512() -> MessageDigest { + unsafe { MessageDigest(ffi::EVP_sha3_512()) } + } + + #[cfg(ossl111)] + pub fn shake_128() -> MessageDigest { + unsafe { MessageDigest(ffi::EVP_shake128()) } + } + + #[cfg(ossl111)] + pub fn shake_256() -> MessageDigest { + unsafe { MessageDigest(ffi::EVP_shake256()) } + } + + #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_RMD160")))] + pub fn ripemd160() -> MessageDigest { + unsafe { MessageDigest(ffi::EVP_ripemd160()) } + } + + #[cfg(all(any(ossl111, libressl291), not(osslconf = "OPENSSL_NO_SM3")))] + pub fn sm3() -> MessageDigest { + unsafe { MessageDigest(ffi::EVP_sm3()) } + } + + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn as_ptr(&self) -> *const ffi::EVP_MD { + self.0 + } + + /// The block size of the digest in bytes. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn block_size(&self) -> usize { + unsafe { ffi::EVP_MD_block_size(self.0) as usize } + } + + /// The size of the digest in bytes. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn size(&self) -> usize { + unsafe { ffi::EVP_MD_size(self.0) as usize } + } + + /// The name of the digest. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn type_(&self) -> Nid { + Nid::from_raw(unsafe { ffi::EVP_MD_type(self.0) }) + } +} + +unsafe impl Sync for MessageDigest {} +unsafe impl Send for MessageDigest {} + +#[derive(PartialEq, Copy, Clone)] +enum State { + Reset, + Updated, + Finalized, +} + +use self::State::*; + +/// Provides message digest (hash) computation. +/// +/// # Examples +/// +/// ``` +/// use openssl::hash::{Hasher, MessageDigest}; +/// +/// # fn main() -> Result<(), Box<dyn std::error::Error>> { +/// let data = [b"\x42\xF4", b"\x97\xE0"]; +/// let spec = b"\x7c\x43\x0f\x17\x8a\xef\xdf\x14\x87\xfe\xe7\x14\x4e\x96\x41\xe2"; +/// let mut h = Hasher::new(MessageDigest::md5())?; +/// h.update(data[0])?; +/// h.update(data[1])?; +/// let res = h.finish()?; +/// assert_eq!(&*res, spec); +/// # Ok(()) } +/// ``` +/// +/// # Warning +/// +/// Don't actually use MD5 and SHA-1 hashes, they're not secure anymore. +/// +/// Don't ever hash passwords, use the functions in the `pkcs5` module or bcrypt/scrypt instead. +/// +/// For extendable output functions (XOFs, i.e. SHAKE128/SHAKE256), +/// you must use [`Hasher::finish_xof`] instead of [`Hasher::finish`] +/// and provide a `buf` to store the hash. The hash will be as long as +/// the `buf`. +pub struct Hasher { + ctx: *mut ffi::EVP_MD_CTX, + md: *const ffi::EVP_MD, + type_: MessageDigest, + state: State, +} + +unsafe impl Sync for Hasher {} +unsafe impl Send for Hasher {} + +impl Hasher { + /// Creates a new `Hasher` with the specified hash type. + pub fn new(ty: MessageDigest) -> Result<Hasher, ErrorStack> { + ffi::init(); + + let ctx = unsafe { cvt_p(EVP_MD_CTX_new())? }; + + let mut h = Hasher { + ctx, + md: ty.as_ptr(), + type_: ty, + state: Finalized, + }; + h.init()?; + Ok(h) + } + + fn init(&mut self) -> Result<(), ErrorStack> { + match self.state { + Reset => return Ok(()), + Updated => { + self.finish()?; + } + Finalized => (), + } + unsafe { + cvt(ffi::EVP_DigestInit_ex(self.ctx, self.md, ptr::null_mut()))?; + } + self.state = Reset; + Ok(()) + } + + /// Feeds data into the hasher. + pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> { + if self.state == Finalized { + self.init()?; + } + unsafe { + cvt(ffi::EVP_DigestUpdate( + self.ctx, + data.as_ptr() as *mut _, + data.len(), + ))?; + } + self.state = Updated; + Ok(()) + } + + /// Returns the hash of the data written and resets the non-XOF hasher. + pub fn finish(&mut self) -> Result<DigestBytes, ErrorStack> { + if self.state == Finalized { + self.init()?; + } + unsafe { + #[cfg(not(boringssl))] + let mut len = ffi::EVP_MAX_MD_SIZE; + #[cfg(boringssl)] + let mut len = ffi::EVP_MAX_MD_SIZE as u32; + let mut buf = [0; ffi::EVP_MAX_MD_SIZE as usize]; + cvt(ffi::EVP_DigestFinal_ex( + self.ctx, + buf.as_mut_ptr(), + &mut len, + ))?; + self.state = Finalized; + Ok(DigestBytes { + buf, + len: len as usize, + }) + } + } + + /// Writes the hash of the data into the supplied buf and resets the XOF hasher. + /// The hash will be as long as the buf. + #[cfg(ossl111)] + pub fn finish_xof(&mut self, buf: &mut [u8]) -> Result<(), ErrorStack> { + if self.state == Finalized { + self.init()?; + } + unsafe { + cvt(ffi::EVP_DigestFinalXOF( + self.ctx, + buf.as_mut_ptr(), + buf.len(), + ))?; + self.state = Finalized; + Ok(()) + } + } +} + +impl Write for Hasher { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.update(buf)?; + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl Clone for Hasher { + fn clone(&self) -> Hasher { + let ctx = unsafe { + let ctx = EVP_MD_CTX_new(); + assert!(!ctx.is_null()); + let r = ffi::EVP_MD_CTX_copy_ex(ctx, self.ctx); + assert_eq!(r, 1); + ctx + }; + Hasher { + ctx, + md: self.md, + type_: self.type_, + state: self.state, + } + } +} + +impl Drop for Hasher { + fn drop(&mut self) { + unsafe { + if self.state != Finalized { + drop(self.finish()); + } + EVP_MD_CTX_free(self.ctx); + } + } +} + +/// The resulting bytes of a digest. +/// +/// This type derefs to a byte slice - it exists to avoid allocating memory to +/// store the digest data. +#[derive(Copy)] +pub struct DigestBytes { + pub(crate) buf: [u8; ffi::EVP_MAX_MD_SIZE as usize], + pub(crate) len: usize, +} + +impl Clone for DigestBytes { + #[inline] + fn clone(&self) -> DigestBytes { + *self + } +} + +impl Deref for DigestBytes { + type Target = [u8]; + + #[inline] + fn deref(&self) -> &[u8] { + &self.buf[..self.len] + } +} + +impl DerefMut for DigestBytes { + #[inline] + fn deref_mut(&mut self) -> &mut [u8] { + &mut self.buf[..self.len] + } +} + +impl AsRef<[u8]> for DigestBytes { + #[inline] + fn as_ref(&self) -> &[u8] { + self.deref() + } +} + +impl fmt::Debug for DigestBytes { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, fmt) + } +} + +/// Computes the hash of the `data` with the non-XOF hasher `t`. +/// +/// # Examples +/// +/// ``` +/// # fn main() -> Result<(), Box<dyn std::error::Error>> { +/// use openssl::hash::{hash, MessageDigest}; +/// +/// let data = b"\x42\xF4\x97\xE0"; +/// let spec = b"\x7c\x43\x0f\x17\x8a\xef\xdf\x14\x87\xfe\xe7\x14\x4e\x96\x41\xe2"; +/// let res = hash(MessageDigest::md5(), data)?; +/// assert_eq!(&*res, spec); +/// # Ok(()) } +/// ``` +pub fn hash(t: MessageDigest, data: &[u8]) -> Result<DigestBytes, ErrorStack> { + let mut h = Hasher::new(t)?; + h.update(data)?; + h.finish() +} + +/// Computes the hash of the `data` with the XOF hasher `t` and stores it in `buf`. +/// +/// # Examples +/// +/// ``` +/// use openssl::hash::{hash_xof, MessageDigest}; +/// +/// let data = b"\x41\x6c\x6c\x20\x79\x6f\x75\x72\x20\x62\x61\x73\x65\x20\x61\x72\x65\x20\x62\x65\x6c\x6f\x6e\x67\x20\x74\x6f\x20\x75\x73"; +/// let spec = b"\x49\xd0\x69\x7f\xf5\x08\x11\x1d\x8b\x84\xf1\x5e\x46\xda\xf1\x35"; +/// let mut buf = vec![0; 16]; +/// hash_xof(MessageDigest::shake_128(), data, buf.as_mut_slice()).unwrap(); +/// assert_eq!(buf, spec); +/// ``` +/// +#[cfg(ossl111)] +pub fn hash_xof(t: MessageDigest, data: &[u8], buf: &mut [u8]) -> Result<(), ErrorStack> { + let mut h = Hasher::new(t)?; + h.update(data)?; + h.finish_xof(buf) +} + +#[cfg(test)] +mod tests { + use hex::{self, FromHex}; + use std::io::prelude::*; + + use super::*; + + fn hash_test(hashtype: MessageDigest, hashtest: &(&str, &str)) { + let res = hash(hashtype, &Vec::from_hex(hashtest.0).unwrap()).unwrap(); + assert_eq!(hex::encode(res), hashtest.1); + } + + #[cfg(ossl111)] + fn hash_xof_test(hashtype: MessageDigest, hashtest: &(&str, &str)) { + let expected = Vec::from_hex(hashtest.1).unwrap(); + let mut buf = vec![0; expected.len()]; + hash_xof( + hashtype, + &Vec::from_hex(hashtest.0).unwrap(), + buf.as_mut_slice(), + ) + .unwrap(); + assert_eq!(buf, expected); + } + + fn hash_recycle_test(h: &mut Hasher, hashtest: &(&str, &str)) { + h.write_all(&Vec::from_hex(hashtest.0).unwrap()).unwrap(); + let res = h.finish().unwrap(); + assert_eq!(hex::encode(res), hashtest.1); + } + + // Test vectors from http://www.nsrl.nist.gov/testdata/ + const MD5_TESTS: [(&str, &str); 13] = [ + ("", "d41d8cd98f00b204e9800998ecf8427e"), + ("7F", "83acb6e67e50e31db6ed341dd2de1595"), + ("EC9C", "0b07f0d4ca797d8ac58874f887cb0b68"), + ("FEE57A", "e0d583171eb06d56198fc0ef22173907"), + ("42F497E0", "7c430f178aefdf1487fee7144e9641e2"), + ("C53B777F1C", "75ef141d64cb37ec423da2d9d440c925"), + ("89D5B576327B", "ebbaf15eb0ed784c6faa9dc32831bf33"), + ("5D4CCE781EB190", "ce175c4b08172019f05e6b5279889f2c"), + ("81901FE94932D7B9", "cd4d2f62b8cdb3a0cf968a735a239281"), + ("C9FFDEE7788EFB4EC9", "e0841a231ab698db30c6c0f3f246c014"), + ("66AC4B7EBA95E53DC10B", "a3b3cea71910d9af56742aa0bb2fe329"), + ("A510CD18F7A56852EB0319", "577e216843dd11573574d3fb209b97d8"), + ( + "AAED18DBE8938C19ED734A8D", + "6f80fb775f27e0a4ce5c2f42fc72c5f1", + ), + ]; + + #[test] + fn test_md5() { + for test in MD5_TESTS.iter() { + hash_test(MessageDigest::md5(), test); + } + + assert_eq!(MessageDigest::md5().block_size(), 64); + assert_eq!(MessageDigest::md5().size(), 16); + assert_eq!(MessageDigest::md5().type_().as_raw(), Nid::MD5.as_raw()); + } + + #[test] + fn test_md5_recycle() { + let mut h = Hasher::new(MessageDigest::md5()).unwrap(); + for test in MD5_TESTS.iter() { + hash_recycle_test(&mut h, test); + } + } + + #[test] + fn test_finish_twice() { + let mut h = Hasher::new(MessageDigest::md5()).unwrap(); + h.write_all(&Vec::from_hex(MD5_TESTS[6].0).unwrap()) + .unwrap(); + h.finish().unwrap(); + let res = h.finish().unwrap(); + let null = hash(MessageDigest::md5(), &[]).unwrap(); + assert_eq!(&*res, &*null); + } + + #[test] + #[allow(clippy::redundant_clone)] + fn test_clone() { + let i = 7; + let inp = Vec::from_hex(MD5_TESTS[i].0).unwrap(); + assert!(inp.len() > 2); + let p = inp.len() / 2; + let h0 = Hasher::new(MessageDigest::md5()).unwrap(); + + println!("Clone a new hasher"); + let mut h1 = h0.clone(); + h1.write_all(&inp[..p]).unwrap(); + { + println!("Clone an updated hasher"); + let mut h2 = h1.clone(); + h2.write_all(&inp[p..]).unwrap(); + let res = h2.finish().unwrap(); + assert_eq!(hex::encode(res), MD5_TESTS[i].1); + } + h1.write_all(&inp[p..]).unwrap(); + let res = h1.finish().unwrap(); + assert_eq!(hex::encode(res), MD5_TESTS[i].1); + + println!("Clone a finished hasher"); + let mut h3 = h1.clone(); + h3.write_all(&Vec::from_hex(MD5_TESTS[i + 1].0).unwrap()) + .unwrap(); + let res = h3.finish().unwrap(); + assert_eq!(hex::encode(res), MD5_TESTS[i + 1].1); + } + + #[test] + fn test_sha1() { + let tests = [("616263", "a9993e364706816aba3e25717850c26c9cd0d89d")]; + + for test in tests.iter() { + hash_test(MessageDigest::sha1(), test); + } + + assert_eq!(MessageDigest::sha1().block_size(), 64); + assert_eq!(MessageDigest::sha1().size(), 20); + assert_eq!(MessageDigest::sha1().type_().as_raw(), Nid::SHA1.as_raw()); + } + + #[test] + fn test_sha256() { + let tests = [( + "616263", + "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", + )]; + + for test in tests.iter() { + hash_test(MessageDigest::sha256(), test); + } + + assert_eq!(MessageDigest::sha256().block_size(), 64); + assert_eq!(MessageDigest::sha256().size(), 32); + assert_eq!( + MessageDigest::sha256().type_().as_raw(), + Nid::SHA256.as_raw() + ); + } + + #[test] + fn test_sha512() { + let tests = [( + "737465766566696e647365766572797468696e67", + "ba61d1f1af0f2dd80729f6cc900f19c0966bd38ba5c75e4471ef11b771dfe7551afab7fcbd300fdc4418f2\ + b07a028fcd99e7b6446a566f2d9bcd7c604a1ea801", + )]; + + for test in tests.iter() { + hash_test(MessageDigest::sha512(), test); + } + + assert_eq!(MessageDigest::sha512().block_size(), 128); + assert_eq!(MessageDigest::sha512().size(), 64); + assert_eq!( + MessageDigest::sha512().type_().as_raw(), + Nid::SHA512.as_raw() + ); + } + + #[cfg(ossl111)] + #[test] + fn test_sha3_224() { + let tests = [( + "416c6c20796f75722062617365206172652062656c6f6e6720746f207573", + "1de092dd9fbcbbf450f26264f4778abd48af851f2832924554c56913", + )]; + + for test in tests.iter() { + hash_test(MessageDigest::sha3_224(), test); + } + + assert_eq!(MessageDigest::sha3_224().block_size(), 144); + assert_eq!(MessageDigest::sha3_224().size(), 28); + assert_eq!( + MessageDigest::sha3_224().type_().as_raw(), + Nid::SHA3_224.as_raw() + ); + } + + #[cfg(ossl111)] + #[test] + fn test_sha3_256() { + let tests = [( + "416c6c20796f75722062617365206172652062656c6f6e6720746f207573", + "b38e38f08bc1c0091ed4b5f060fe13e86aa4179578513ad11a6e3abba0062f61", + )]; + + for test in tests.iter() { + hash_test(MessageDigest::sha3_256(), test); + } + + assert_eq!(MessageDigest::sha3_256().block_size(), 136); + assert_eq!(MessageDigest::sha3_256().size(), 32); + assert_eq!( + MessageDigest::sha3_256().type_().as_raw(), + Nid::SHA3_256.as_raw() + ); + } + + #[cfg(ossl111)] + #[test] + fn test_sha3_384() { + let tests = [("416c6c20796f75722062617365206172652062656c6f6e6720746f207573", + "966ee786ab3482dd811bf7c8fa8db79aa1f52f6c3c369942ef14240ebd857c6ff626ec35d9e131ff64d328\ + ef2008ff16" + )]; + + for test in tests.iter() { + hash_test(MessageDigest::sha3_384(), test); + } + + assert_eq!(MessageDigest::sha3_384().block_size(), 104); + assert_eq!(MessageDigest::sha3_384().size(), 48); + assert_eq!( + MessageDigest::sha3_384().type_().as_raw(), + Nid::SHA3_384.as_raw() + ); + } + + #[cfg(ossl111)] + #[test] + fn test_sha3_512() { + let tests = [("416c6c20796f75722062617365206172652062656c6f6e6720746f207573", + "c072288ef728cd53a029c47687960b9225893532f42b923156e37020bdc1eda753aafbf30af859d4f4c3a1\ + 807caee3a79f8eb02dcd61589fbbdf5f40c8787a72" + )]; + + for test in tests.iter() { + hash_test(MessageDigest::sha3_512(), test); + } + + assert_eq!(MessageDigest::sha3_512().block_size(), 72); + assert_eq!(MessageDigest::sha3_512().size(), 64); + assert_eq!( + MessageDigest::sha3_512().type_().as_raw(), + Nid::SHA3_512.as_raw() + ); + } + + #[cfg(ossl111)] + #[test] + fn test_shake_128() { + let tests = [( + "416c6c20796f75722062617365206172652062656c6f6e6720746f207573", + "49d0697ff508111d8b84f15e46daf135", + )]; + + for test in tests.iter() { + hash_xof_test(MessageDigest::shake_128(), test); + } + + assert_eq!(MessageDigest::shake_128().block_size(), 168); + assert_eq!(MessageDigest::shake_128().size(), 16); + assert_eq!( + MessageDigest::shake_128().type_().as_raw(), + Nid::SHAKE128.as_raw() + ); + } + + #[cfg(ossl111)] + #[test] + fn test_shake_256() { + let tests = [( + "416c6c20796f75722062617365206172652062656c6f6e6720746f207573", + "4e2dfdaa75d1e049d0eaeffe28e76b17cea47b650fb8826fe48b94664326a697", + )]; + + for test in tests.iter() { + hash_xof_test(MessageDigest::shake_256(), test); + } + + assert_eq!(MessageDigest::shake_256().block_size(), 136); + assert_eq!(MessageDigest::shake_256().size(), 32); + assert_eq!( + MessageDigest::shake_256().type_().as_raw(), + Nid::SHAKE256.as_raw() + ); + } + + #[test] + #[cfg(not(boringssl))] + #[cfg_attr(ossl300, ignore)] + fn test_ripemd160() { + #[cfg(ossl300)] + let _provider = crate::provider::Provider::try_load(None, "legacy", true).unwrap(); + + let tests = [("616263", "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc")]; + + for test in tests.iter() { + hash_test(MessageDigest::ripemd160(), test); + } + + assert_eq!(MessageDigest::ripemd160().block_size(), 64); + assert_eq!(MessageDigest::ripemd160().size(), 20); + assert_eq!( + MessageDigest::ripemd160().type_().as_raw(), + Nid::RIPEMD160.as_raw() + ); + } + + #[cfg(all(any(ossl111, libressl291), not(osslconf = "OPENSSL_NO_SM3")))] + #[test] + fn test_sm3() { + let tests = [( + "616263", + "66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0", + )]; + + for test in tests.iter() { + hash_test(MessageDigest::sm3(), test); + } + + assert_eq!(MessageDigest::sm3().block_size(), 64); + assert_eq!(MessageDigest::sm3().size(), 32); + assert_eq!(MessageDigest::sm3().type_().as_raw(), Nid::SM3.as_raw()); + } + + #[test] + fn from_nid() { + assert_eq!( + MessageDigest::from_nid(Nid::SHA256).unwrap().as_ptr(), + MessageDigest::sha256().as_ptr() + ); + } + + #[test] + fn from_name() { + assert_eq!( + MessageDigest::from_name("SHA256").unwrap().as_ptr(), + MessageDigest::sha256().as_ptr() + ) + } +} diff --git a/vendor/openssl/src/lib.rs b/vendor/openssl/src/lib.rs new file mode 100644 index 0000000..fe29d02 --- /dev/null +++ b/vendor/openssl/src/lib.rs @@ -0,0 +1,222 @@ +//! Bindings to OpenSSL +//! +//! This crate provides a safe interface to the popular OpenSSL cryptography library. OpenSSL versions 1.0.1 through +//! 3.x.x and LibreSSL versions 2.5 through 3.7.x are supported. +//! +//! # Building +//! +//! Both OpenSSL libraries and headers are required to build this crate. There are multiple options available to locate +//! OpenSSL. +//! +//! ## Vendored +//! +//! If the `vendored` Cargo feature is enabled, the `openssl-src` crate will be used to compile and statically link to +//! a copy of OpenSSL. The build process requires a C compiler, perl (and perl-core), and make. The OpenSSL version will generally track +//! the newest OpenSSL release, and changes to the version are *not* considered breaking changes. +//! +//! ```toml +//! [dependencies] +//! openssl = { version = "0.10", features = ["vendored"] } +//! ``` +//! +//! The vendored copy will not be configured to automatically find the system's root certificates, but the +//! `openssl-probe` crate can be used to do that instead. +//! +//! ## Automatic +//! +//! The `openssl-sys` crate will automatically detect OpenSSL installations via Homebrew on macOS and vcpkg on Windows. +//! Additionally, it will use `pkg-config` on Unix-like systems to find the system installation. +//! +//! ```not_rust +//! # macOS (Homebrew) +//! $ brew install openssl@3 +//! +//! # macOS (MacPorts) +//! $ sudo port install openssl +//! +//! # macOS (pkgsrc) +//! $ sudo pkgin install openssl +//! +//! # Arch Linux +//! $ sudo pacman -S pkg-config openssl +//! +//! # Debian and Ubuntu +//! $ sudo apt-get install pkg-config libssl-dev +//! +//! # Fedora +//! $ sudo dnf install pkg-config perl-FindBin openssl-devel +//! +//! # Alpine Linux +//! $ apk add pkgconfig openssl-dev +//! ``` +//! +//! ## Manual +//! +//! A set of environment variables can be used to point `openssl-sys` towards an OpenSSL installation. They will +//! override the automatic detection logic. +//! +//! * `OPENSSL_DIR` - If specified, the directory of an OpenSSL installation. The directory should contain `lib` and +//! `include` subdirectories containing the libraries and headers respectively. +//! * `OPENSSL_LIB_DIR` and `OPENSSL_INCLUDE_DIR` - If specified, the directories containing the OpenSSL libraries and +//! headers respectively. This can be used if the OpenSSL installation is split in a nonstandard directory layout. +//! * `OPENSSL_STATIC` - If set, the crate will statically link to OpenSSL rather than dynamically link. +//! * `OPENSSL_LIBS` - If set, a `:`-separated list of library names to link to (e.g. `ssl:crypto`). This can be used +//! if nonstandard library names were used for whatever reason. +//! * `OPENSSL_NO_VENDOR` - If set, always find OpenSSL in the system, even if the `vendored` feature is enabled. +//! +//! Additionally, these variables can be prefixed with the upper-cased target architecture (e.g. +//! `X86_64_UNKNOWN_LINUX_GNU_OPENSSL_DIR`), which can be useful when cross compiling. +//! +//! # Feature Detection +//! +//! APIs have been added to and removed from the various supported OpenSSL versions, and this library exposes the +//! functionality available in the version being linked against. This means that methods, constants, and even modules +//! will be present when building against one version of OpenSSL but not when building against another! APIs will +//! document any version-specific availability restrictions. +//! +//! A build script can be used to detect the OpenSSL or LibreSSL version at compile time if needed. The `openssl-sys` +//! crate propagates the version via the `DEP_OPENSSL_VERSION_NUMBER` and `DEP_OPENSSL_LIBRESSL_VERSION_NUMBER` +//! environment variables to build scripts. The version format is a hex-encoding of the OpenSSL release version: +//! `0xMNNFFPPS`. For example, version 1.0.2g's encoding is `0x1_00_02_07_0`. +//! +//! For example, let's say we want to adjust the TLSv1.3 cipher suites used by a client, but also want to compile +//! against OpenSSL versions that don't support TLSv1.3: +//! +//! Cargo.toml: +//! +//! ```toml +//! [dependencies] +//! openssl-sys = "0.9" +//! openssl = "0.10" +//! ``` +//! +//! build.rs: +//! +//! ``` +//! use std::env; +//! +//! fn main() { +//! if let Ok(v) = env::var("DEP_OPENSSL_VERSION_NUMBER") { +//! let version = u64::from_str_radix(&v, 16).unwrap(); +//! +//! if version >= 0x1_01_01_00_0 { +//! println!("cargo:rustc-cfg=openssl111"); +//! } +//! } +//! } +//! ``` +//! +//! lib.rs: +//! +//! ``` +//! use openssl::ssl::{SslConnector, SslMethod}; +//! +//! let mut ctx = SslConnector::builder(SslMethod::tls()).unwrap(); +//! +//! // set_ciphersuites was added in OpenSSL 1.1.1, so we can only call it when linking against that version +//! #[cfg(openssl111)] +//! ctx.set_ciphersuites("TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256").unwrap(); +//! ``` +#![doc(html_root_url = "https://docs.rs/openssl/0.10")] +#![warn(rust_2018_idioms)] +#![allow(clippy::uninlined_format_args)] + +#[doc(inline)] +pub use ffi::init; + +use libc::c_int; + +use crate::error::ErrorStack; + +#[macro_use] +mod macros; + +mod bio; +#[macro_use] +mod util; +pub mod aes; +pub mod asn1; +pub mod base64; +pub mod bn; +pub mod cipher; +pub mod cipher_ctx; +#[cfg(all(not(boringssl), not(libressl), not(osslconf = "OPENSSL_NO_CMS")))] +pub mod cms; +pub mod conf; +pub mod derive; +pub mod dh; +pub mod dsa; +pub mod ec; +pub mod ecdsa; +pub mod encrypt; +#[cfg(not(boringssl))] +pub mod envelope; +pub mod error; +pub mod ex_data; +#[cfg(not(any(libressl, ossl300)))] +pub mod fips; +pub mod hash; +#[cfg(ossl300)] +pub mod lib_ctx; +pub mod md; +pub mod md_ctx; +pub mod memcmp; +pub mod nid; +#[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_OCSP")))] +pub mod ocsp; +pub mod pkcs12; +pub mod pkcs5; +#[cfg(not(boringssl))] +pub mod pkcs7; +pub mod pkey; +pub mod pkey_ctx; +#[cfg(ossl300)] +pub mod provider; +pub mod rand; +pub mod rsa; +pub mod sha; +pub mod sign; +pub mod srtp; +pub mod ssl; +pub mod stack; +pub mod string; +pub mod symm; +pub mod version; +pub mod x509; + +#[cfg(boringssl)] +type LenType = libc::size_t; +#[cfg(not(boringssl))] +type LenType = libc::c_int; + +#[cfg(boringssl)] +type SLenType = libc::ssize_t; +#[cfg(not(boringssl))] +type SLenType = libc::c_int; + +#[inline] +fn cvt_p<T>(r: *mut T) -> Result<*mut T, ErrorStack> { + if r.is_null() { + Err(ErrorStack::get()) + } else { + Ok(r) + } +} + +#[inline] +fn cvt(r: c_int) -> Result<c_int, ErrorStack> { + if r <= 0 { + Err(ErrorStack::get()) + } else { + Ok(r) + } +} + +#[inline] +fn cvt_n(r: c_int) -> Result<c_int, ErrorStack> { + if r < 0 { + Err(ErrorStack::get()) + } else { + Ok(r) + } +} diff --git a/vendor/openssl/src/lib_ctx.rs b/vendor/openssl/src/lib_ctx.rs new file mode 100644 index 0000000..1fcdc65 --- /dev/null +++ b/vendor/openssl/src/lib_ctx.rs @@ -0,0 +1,22 @@ +use crate::cvt_p; +use crate::error::ErrorStack; +use foreign_types::ForeignType; +use openssl_macros::corresponds; + +foreign_type_and_impl_send_sync! { + type CType = ffi::OSSL_LIB_CTX; + fn drop = ffi::OSSL_LIB_CTX_free; + + pub struct LibCtx; + pub struct LibCtxRef; +} + +impl LibCtx { + #[corresponds(OSSL_LIB_CTX_new)] + pub fn new() -> Result<Self, ErrorStack> { + unsafe { + let ptr = cvt_p(ffi::OSSL_LIB_CTX_new())?; + Ok(LibCtx::from_ptr(ptr)) + } + } +} diff --git a/vendor/openssl/src/macros.rs b/vendor/openssl/src/macros.rs new file mode 100644 index 0000000..671a11b --- /dev/null +++ b/vendor/openssl/src/macros.rs @@ -0,0 +1,270 @@ +macro_rules! private_key_from_pem { + ($(#[$m:meta])* $n:ident, $(#[$m2:meta])* $n2:ident, $(#[$m3:meta])* $n3:ident, $t:ty, $f:path) => { + from_pem!($(#[$m])* $n, $t, $f); + + $(#[$m2])* + pub fn $n2(pem: &[u8], passphrase: &[u8]) -> Result<$t, crate::error::ErrorStack> { + unsafe { + ffi::init(); + let bio = crate::bio::MemBioSlice::new(pem)?; + let passphrase = ::std::ffi::CString::new(passphrase).unwrap(); + cvt_p($f(bio.as_ptr(), + ptr::null_mut(), + None, + passphrase.as_ptr() as *const _ as *mut _)) + .map(|p| ::foreign_types::ForeignType::from_ptr(p)) + } + } + + $(#[$m3])* + pub fn $n3<F>(pem: &[u8], callback: F) -> Result<$t, crate::error::ErrorStack> + where F: FnOnce(&mut [u8]) -> Result<usize, crate::error::ErrorStack> + { + unsafe { + ffi::init(); + let mut cb = crate::util::CallbackState::new(callback); + let bio = crate::bio::MemBioSlice::new(pem)?; + cvt_p($f(bio.as_ptr(), + ptr::null_mut(), + Some(crate::util::invoke_passwd_cb::<F>), + &mut cb as *mut _ as *mut _)) + .map(|p| ::foreign_types::ForeignType::from_ptr(p)) + } + } + } +} + +macro_rules! private_key_to_pem { + ($(#[$m:meta])* $n:ident, $(#[$m2:meta])* $n2:ident, $f:path) => { + $(#[$m])* + pub fn $n(&self) -> Result<Vec<u8>, crate::error::ErrorStack> { + unsafe { + let bio = crate::bio::MemBio::new()?; + cvt($f(bio.as_ptr(), + self.as_ptr(), + ptr::null(), + ptr::null_mut(), + -1, + None, + ptr::null_mut()))?; + Ok(bio.get_buf().to_owned()) + } + } + + $(#[$m2])* + pub fn $n2( + &self, + cipher: crate::symm::Cipher, + passphrase: &[u8] + ) -> Result<Vec<u8>, crate::error::ErrorStack> { + unsafe { + let bio = crate::bio::MemBio::new()?; + assert!(passphrase.len() <= ::libc::c_int::max_value() as usize); + cvt($f(bio.as_ptr(), + self.as_ptr(), + cipher.as_ptr(), + passphrase.as_ptr() as *const _ as *mut _, + passphrase.len() as ::libc::c_int, + None, + ptr::null_mut()))?; + Ok(bio.get_buf().to_owned()) + } + } + } +} + +macro_rules! to_pem { + ($(#[$m:meta])* $n:ident, $f:path) => { + $(#[$m])* + pub fn $n(&self) -> Result<Vec<u8>, crate::error::ErrorStack> { + unsafe { + let bio = crate::bio::MemBio::new()?; + cvt($f(bio.as_ptr(), self.as_ptr()))?; + Ok(bio.get_buf().to_owned()) + } + } + } +} + +macro_rules! to_der { + ($(#[$m:meta])* $n:ident, $f:path) => { + $(#[$m])* + pub fn $n(&self) -> Result<Vec<u8>, crate::error::ErrorStack> { + unsafe { + let len = crate::cvt($f(::foreign_types::ForeignTypeRef::as_ptr(self), + ptr::null_mut()))?; + let mut buf = vec![0; len as usize]; + crate::cvt($f(::foreign_types::ForeignTypeRef::as_ptr(self), + &mut buf.as_mut_ptr()))?; + Ok(buf) + } + } + }; +} + +macro_rules! from_der { + ($(#[$m:meta])* $n:ident, $t:ty, $f:path) => { + $(#[$m])* + pub fn $n(der: &[u8]) -> Result<$t, crate::error::ErrorStack> { + use std::convert::TryInto; + unsafe { + ffi::init(); + let len = ::std::cmp::min(der.len(), ::libc::c_long::max_value() as usize) as ::libc::c_long; + crate::cvt_p($f(::std::ptr::null_mut(), &mut der.as_ptr(), len.try_into().unwrap())) + .map(|p| ::foreign_types::ForeignType::from_ptr(p)) + } + } + } +} + +macro_rules! from_pem { + ($(#[$m:meta])* $n:ident, $t:ty, $f:path) => { + $(#[$m])* + pub fn $n(pem: &[u8]) -> Result<$t, crate::error::ErrorStack> { + unsafe { + crate::init(); + let bio = crate::bio::MemBioSlice::new(pem)?; + cvt_p($f(bio.as_ptr(), ::std::ptr::null_mut(), None, ::std::ptr::null_mut())) + .map(|p| ::foreign_types::ForeignType::from_ptr(p)) + } + } + } +} + +macro_rules! foreign_type_and_impl_send_sync { + ( + $(#[$impl_attr:meta])* + type CType = $ctype:ty; + fn drop = $drop:expr; + $(fn clone = $clone:expr;)* + + $(#[$owned_attr:meta])* + pub struct $owned:ident; + $(#[$borrowed_attr:meta])* + pub struct $borrowed:ident; + ) + => { + ::foreign_types::foreign_type! { + $(#[$impl_attr])* + type CType = $ctype; + fn drop = $drop; + $(fn clone = $clone;)* + $(#[$owned_attr])* + pub struct $owned; + $(#[$borrowed_attr])* + pub struct $borrowed; + } + + unsafe impl Send for $owned{} + unsafe impl Send for $borrowed{} + unsafe impl Sync for $owned{} + unsafe impl Sync for $borrowed{} + }; +} + +macro_rules! generic_foreign_type_and_impl_send_sync { + ( + $(#[$impl_attr:meta])* + type CType = $ctype:ty; + fn drop = $drop:expr; + $(fn clone = $clone:expr;)* + + $(#[$owned_attr:meta])* + pub struct $owned:ident<T>; + $(#[$borrowed_attr:meta])* + pub struct $borrowed:ident<T>; + ) => { + $(#[$owned_attr])* + pub struct $owned<T>(*mut $ctype, ::std::marker::PhantomData<T>); + + $(#[$impl_attr])* + impl<T> ::foreign_types::ForeignType for $owned<T> { + type CType = $ctype; + type Ref = $borrowed<T>; + + #[inline] + unsafe fn from_ptr(ptr: *mut $ctype) -> $owned<T> { + $owned(ptr, ::std::marker::PhantomData) + } + + #[inline] + fn as_ptr(&self) -> *mut $ctype { + self.0 + } + } + + impl<T> Drop for $owned<T> { + #[inline] + fn drop(&mut self) { + unsafe { $drop(self.0) } + } + } + + $( + impl<T> Clone for $owned<T> { + #[inline] + fn clone(&self) -> $owned<T> { + unsafe { + let handle: *mut $ctype = $clone(self.0); + ::foreign_types::ForeignType::from_ptr(handle) + } + } + } + + impl<T> ::std::borrow::ToOwned for $borrowed<T> { + type Owned = $owned<T>; + #[inline] + fn to_owned(&self) -> $owned<T> { + unsafe { + let handle: *mut $ctype = + $clone(::foreign_types::ForeignTypeRef::as_ptr(self)); + $crate::ForeignType::from_ptr(handle) + } + } + } + )* + + impl<T> ::std::ops::Deref for $owned<T> { + type Target = $borrowed<T>; + + #[inline] + fn deref(&self) -> &$borrowed<T> { + unsafe { ::foreign_types::ForeignTypeRef::from_ptr(self.0) } + } + } + + impl<T> ::std::ops::DerefMut for $owned<T> { + #[inline] + fn deref_mut(&mut self) -> &mut $borrowed<T> { + unsafe { ::foreign_types::ForeignTypeRef::from_ptr_mut(self.0) } + } + } + + impl<T> ::std::borrow::Borrow<$borrowed<T>> for $owned<T> { + #[inline] + fn borrow(&self) -> &$borrowed<T> { + &**self + } + } + + impl<T> ::std::convert::AsRef<$borrowed<T>> for $owned<T> { + #[inline] + fn as_ref(&self) -> &$borrowed<T> { + &**self + } + } + + $(#[$borrowed_attr])* + pub struct $borrowed<T>(::foreign_types::Opaque, ::std::marker::PhantomData<T>); + + $(#[$impl_attr])* + impl<T> ::foreign_types::ForeignTypeRef for $borrowed<T> { + type CType = $ctype; + } + + unsafe impl<T> Send for $owned<T>{} + unsafe impl<T> Send for $borrowed<T>{} + unsafe impl<T> Sync for $owned<T>{} + unsafe impl<T> Sync for $borrowed<T>{} + }; +} diff --git a/vendor/openssl/src/md.rs b/vendor/openssl/src/md.rs new file mode 100644 index 0000000..4ade8e8 --- /dev/null +++ b/vendor/openssl/src/md.rs @@ -0,0 +1,235 @@ +//! Message digest algorithms. + +#[cfg(ossl300)] +use crate::cvt_p; +#[cfg(ossl300)] +use crate::error::ErrorStack; +#[cfg(ossl300)] +use crate::lib_ctx::LibCtxRef; +use crate::nid::Nid; +use cfg_if::cfg_if; +use foreign_types::{ForeignTypeRef, Opaque}; +use openssl_macros::corresponds; +#[cfg(ossl300)] +use std::ffi::CString; +#[cfg(ossl300)] +use std::ptr; + +cfg_if! { + if #[cfg(ossl300)] { + use foreign_types::ForeignType; + use std::ops::{Deref, DerefMut}; + + type Inner = *mut ffi::EVP_MD; + + impl Drop for Md { + #[inline] + fn drop(&mut self) { + unsafe { + ffi::EVP_MD_free(self.as_ptr()); + } + } + } + + impl ForeignType for Md { + type CType = ffi::EVP_MD; + type Ref = MdRef; + + #[inline] + unsafe fn from_ptr(ptr: *mut Self::CType) -> Self { + Md(ptr) + } + + #[inline] + fn as_ptr(&self) -> *mut Self::CType { + self.0 + } + } + + impl Deref for Md { + type Target = MdRef; + + #[inline] + fn deref(&self) -> &Self::Target { + unsafe { + MdRef::from_ptr(self.as_ptr()) + } + } + } + + impl DerefMut for Md { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { + MdRef::from_ptr_mut(self.as_ptr()) + } + } + } + } else { + enum Inner {} + } +} + +/// A message digest algorithm. +pub struct Md(Inner); + +unsafe impl Sync for Md {} +unsafe impl Send for Md {} + +impl Md { + /// Returns the `Md` corresponding to an [`Nid`]. + #[corresponds(EVP_get_digestbynid)] + pub fn from_nid(type_: Nid) -> Option<&'static MdRef> { + unsafe { + let ptr = ffi::EVP_get_digestbynid(type_.as_raw()); + if ptr.is_null() { + None + } else { + Some(MdRef::from_ptr(ptr as *mut _)) + } + } + } + + /// Fetches an `Md` object corresponding to the specified algorithm name and properties. + /// + /// Requires OpenSSL 3.0.0 or newer. + #[corresponds(EVP_MD_fetch)] + #[cfg(ossl300)] + pub fn fetch( + ctx: Option<&LibCtxRef>, + algorithm: &str, + properties: Option<&str>, + ) -> Result<Self, ErrorStack> { + let algorithm = CString::new(algorithm).unwrap(); + let properties = properties.map(|s| CString::new(s).unwrap()); + + unsafe { + let ptr = cvt_p(ffi::EVP_MD_fetch( + ctx.map_or(ptr::null_mut(), ForeignTypeRef::as_ptr), + algorithm.as_ptr(), + properties.map_or(ptr::null_mut(), |s| s.as_ptr()), + ))?; + + Ok(Md::from_ptr(ptr)) + } + } + + #[inline] + #[cfg(not(boringssl))] + pub fn null() -> &'static MdRef { + unsafe { MdRef::from_ptr(ffi::EVP_md_null() as *mut _) } + } + + #[inline] + pub fn md5() -> &'static MdRef { + unsafe { MdRef::from_ptr(ffi::EVP_md5() as *mut _) } + } + + #[inline] + pub fn sha1() -> &'static MdRef { + unsafe { MdRef::from_ptr(ffi::EVP_sha1() as *mut _) } + } + + #[inline] + pub fn sha224() -> &'static MdRef { + unsafe { MdRef::from_ptr(ffi::EVP_sha224() as *mut _) } + } + + #[inline] + pub fn sha256() -> &'static MdRef { + unsafe { MdRef::from_ptr(ffi::EVP_sha256() as *mut _) } + } + + #[inline] + pub fn sha384() -> &'static MdRef { + unsafe { MdRef::from_ptr(ffi::EVP_sha384() as *mut _) } + } + + #[inline] + pub fn sha512() -> &'static MdRef { + unsafe { MdRef::from_ptr(ffi::EVP_sha512() as *mut _) } + } + + #[cfg(ossl111)] + #[inline] + pub fn sha3_224() -> &'static MdRef { + unsafe { MdRef::from_ptr(ffi::EVP_sha3_224() as *mut _) } + } + + #[cfg(ossl111)] + #[inline] + pub fn sha3_256() -> &'static MdRef { + unsafe { MdRef::from_ptr(ffi::EVP_sha3_256() as *mut _) } + } + + #[cfg(ossl111)] + #[inline] + pub fn sha3_384() -> &'static MdRef { + unsafe { MdRef::from_ptr(ffi::EVP_sha3_384() as *mut _) } + } + + #[cfg(ossl111)] + #[inline] + pub fn sha3_512() -> &'static MdRef { + unsafe { MdRef::from_ptr(ffi::EVP_sha3_512() as *mut _) } + } + + #[cfg(ossl111)] + #[inline] + pub fn shake128() -> &'static MdRef { + unsafe { MdRef::from_ptr(ffi::EVP_shake128() as *mut _) } + } + + #[cfg(ossl111)] + #[inline] + pub fn shake256() -> &'static MdRef { + unsafe { MdRef::from_ptr(ffi::EVP_shake256() as *mut _) } + } + + #[cfg(not(osslconf = "OPENSSL_NO_RMD160"))] + #[inline] + #[cfg(not(boringssl))] + pub fn ripemd160() -> &'static MdRef { + unsafe { MdRef::from_ptr(ffi::EVP_ripemd160() as *mut _) } + } + + #[cfg(all(any(ossl111, libressl291), not(osslconf = "OPENSSL_NO_SM3")))] + #[inline] + #[cfg(not(boringssl))] + pub fn sm3() -> &'static MdRef { + unsafe { MdRef::from_ptr(ffi::EVP_sm3() as *mut _) } + } +} + +/// A reference to an [`Md`]. +pub struct MdRef(Opaque); + +impl ForeignTypeRef for MdRef { + type CType = ffi::EVP_MD; +} + +unsafe impl Sync for MdRef {} +unsafe impl Send for MdRef {} + +impl MdRef { + /// Returns the block size of the digest in bytes. + #[corresponds(EVP_MD_block_size)] + #[inline] + pub fn block_size(&self) -> usize { + unsafe { ffi::EVP_MD_block_size(self.as_ptr()) as usize } + } + + /// Returns the size of the digest in bytes. + #[corresponds(EVP_MD_size)] + #[inline] + pub fn size(&self) -> usize { + unsafe { ffi::EVP_MD_size(self.as_ptr()) as usize } + } + + /// Returns the [`Nid`] of the digest. + #[corresponds(EVP_MD_type)] + #[inline] + pub fn type_(&self) -> Nid { + unsafe { Nid::from_raw(ffi::EVP_MD_type(self.as_ptr())) } + } +} diff --git a/vendor/openssl/src/md_ctx.rs b/vendor/openssl/src/md_ctx.rs new file mode 100644 index 0000000..156f3c2 --- /dev/null +++ b/vendor/openssl/src/md_ctx.rs @@ -0,0 +1,540 @@ +//! The message digest context. +//! +//! # Examples +//! +//! Compute the SHA256 checksum of data +//! +//! ``` +//! use openssl::md::Md; +//! use openssl::md_ctx::MdCtx; +//! +//! let mut ctx = MdCtx::new().unwrap(); +//! ctx.digest_init(Md::sha256()).unwrap(); +//! ctx.digest_update(b"Some Crypto Text").unwrap(); +//! let mut digest = [0; 32]; +//! ctx.digest_final(&mut digest).unwrap(); +//! +//! assert_eq!( +//! digest, +//! *b"\x60\x78\x56\x38\x8a\xca\x5c\x51\x83\xc4\xd1\x4d\xc8\xf9\xcc\xf2\ +//! \xa5\x21\xb3\x10\x93\x72\xfa\xd6\x7c\x55\xf5\xc9\xe3\xd1\x83\x19", +//! ); +//! ``` +//! +//! Sign and verify data with RSA and SHA256 +//! +//! ``` +//! use openssl::md::Md; +//! use openssl::md_ctx::MdCtx; +//! use openssl::pkey::PKey; +//! use openssl::rsa::Rsa; +//! +//! // Generate a random RSA key. +//! let key = Rsa::generate(4096).unwrap(); +//! let key = PKey::from_rsa(key).unwrap(); +//! +//! let text = b"Some Crypto Text"; +//! +//! // Create the signature. +//! let mut ctx = MdCtx::new().unwrap(); +//! ctx.digest_sign_init(Some(Md::sha256()), &key).unwrap(); +//! ctx.digest_sign_update(text).unwrap(); +//! let mut signature = vec![]; +//! ctx.digest_sign_final_to_vec(&mut signature).unwrap(); +//! +//! // Verify the signature. +//! let mut ctx = MdCtx::new().unwrap(); +//! ctx.digest_verify_init(Some(Md::sha256()), &key).unwrap(); +//! ctx.digest_verify_update(text).unwrap(); +//! let valid = ctx.digest_verify_final(&signature).unwrap(); +//! assert!(valid); +//! ``` +//! + +#![cfg_attr( + not(boringssl), + doc = r#"\ +Compute and verify an HMAC-SHA256 + +``` +use openssl::md::Md; +use openssl::md_ctx::MdCtx; +use openssl::memcmp; +use openssl::pkey::PKey; + +// Create a key with the HMAC secret. +let key = PKey::hmac(b"my secret").unwrap(); + +let text = b"Some Crypto Text"; + +// Compute the HMAC. +let mut ctx = MdCtx::new().unwrap(); +ctx.digest_sign_init(Some(Md::sha256()), &key).unwrap(); +ctx.digest_sign_update(text).unwrap(); +let mut hmac = vec![]; +ctx.digest_sign_final_to_vec(&mut hmac).unwrap(); + +// Verify the HMAC. You can't use MdCtx to do this; instead use a constant time equality check. +# let target = hmac.clone(); +let valid = memcmp::eq(&hmac, &target); +assert!(valid); +```"# +)] + +use crate::error::ErrorStack; +use crate::md::MdRef; +use crate::pkey::{HasPrivate, HasPublic, PKeyRef}; +use crate::pkey_ctx::PkeyCtxRef; +use crate::{cvt, cvt_n, cvt_p}; +use cfg_if::cfg_if; +use foreign_types::{ForeignType, ForeignTypeRef}; +use openssl_macros::corresponds; +use std::convert::TryFrom; +use std::ptr; + +cfg_if! { + if #[cfg(any(ossl110, boringssl))] { + use ffi::{EVP_MD_CTX_free, EVP_MD_CTX_new}; + } else { + use ffi::{EVP_MD_CTX_create as EVP_MD_CTX_new, EVP_MD_CTX_destroy as EVP_MD_CTX_free}; + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::EVP_MD_CTX; + fn drop = EVP_MD_CTX_free; + + pub struct MdCtx; + /// A reference to an [`MdCtx`]. + pub struct MdCtxRef; +} + +impl MdCtx { + /// Creates a new context. + #[corresponds(EVP_MD_CTX_new)] + #[inline] + pub fn new() -> Result<Self, ErrorStack> { + ffi::init(); + + unsafe { + let ptr = cvt_p(EVP_MD_CTX_new())?; + Ok(MdCtx::from_ptr(ptr)) + } + } +} + +impl MdCtxRef { + /// Initializes the context to compute the digest of data. + #[corresponds(EVP_DigestInit_ex)] + #[inline] + pub fn digest_init(&mut self, digest: &MdRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_DigestInit_ex( + self.as_ptr(), + digest.as_ptr(), + ptr::null_mut(), + ))?; + } + + Ok(()) + } + + /// Initializes the context to compute the signature of data. + /// + /// A reference to the context's inner `PkeyCtx` is returned, allowing signature settings to be configured. + #[corresponds(EVP_DigestSignInit)] + #[inline] + pub fn digest_sign_init<'a, T>( + &'a mut self, + digest: Option<&MdRef>, + pkey: &PKeyRef<T>, + ) -> Result<&'a mut PkeyCtxRef<T>, ErrorStack> + where + T: HasPrivate, + { + unsafe { + let mut p = ptr::null_mut(); + cvt(ffi::EVP_DigestSignInit( + self.as_ptr(), + &mut p, + digest.map_or(ptr::null(), |p| p.as_ptr()), + ptr::null_mut(), + pkey.as_ptr(), + ))?; + Ok(PkeyCtxRef::from_ptr_mut(p)) + } + } + + /// Initializes the context to verify the signature of data. + /// + /// A reference to the context's inner `PkeyCtx` is returned, allowing signature settings to be configured. + #[corresponds(EVP_DigestVerifyInit)] + #[inline] + pub fn digest_verify_init<'a, T>( + &'a mut self, + digest: Option<&MdRef>, + pkey: &PKeyRef<T>, + ) -> Result<&'a mut PkeyCtxRef<T>, ErrorStack> + where + T: HasPublic, + { + unsafe { + let mut p = ptr::null_mut(); + cvt(ffi::EVP_DigestVerifyInit( + self.as_ptr(), + &mut p, + digest.map_or(ptr::null(), |p| p.as_ptr()), + ptr::null_mut(), + pkey.as_ptr(), + ))?; + Ok(PkeyCtxRef::from_ptr_mut(p)) + } + } + + /// Updates the context with more data. + #[corresponds(EVP_DigestUpdate)] + #[inline] + pub fn digest_update(&mut self, data: &[u8]) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_DigestUpdate( + self.as_ptr(), + data.as_ptr() as *const _, + data.len(), + ))?; + } + + Ok(()) + } + + /// Updates the context with more data. + #[corresponds(EVP_DigestSignUpdate)] + #[inline] + pub fn digest_sign_update(&mut self, data: &[u8]) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_DigestSignUpdate( + self.as_ptr(), + data.as_ptr() as *const _, + data.len(), + ))?; + } + + Ok(()) + } + + /// Updates the context with more data. + #[corresponds(EVP_DigestVerifyUpdate)] + #[inline] + pub fn digest_verify_update(&mut self, data: &[u8]) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_DigestVerifyUpdate( + self.as_ptr(), + data.as_ptr() as *const _, + data.len(), + ))?; + } + + Ok(()) + } + + /// Copies the computed digest into the buffer, returning the number of bytes written. + #[corresponds(EVP_DigestFinal)] + #[inline] + pub fn digest_final(&mut self, out: &mut [u8]) -> Result<usize, ErrorStack> { + let mut len = u32::try_from(out.len()).unwrap_or(u32::MAX); + + unsafe { + cvt(ffi::EVP_DigestFinal( + self.as_ptr(), + out.as_mut_ptr(), + &mut len, + ))?; + } + + Ok(len as usize) + } + + /// Copies the computed digest into the buffer. + /// + /// Requires OpenSSL 1.1.1 or newer. + #[corresponds(EVP_DigestFinalXOF)] + #[inline] + #[cfg(ossl111)] + pub fn digest_final_xof(&mut self, out: &mut [u8]) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_DigestFinalXOF( + self.as_ptr(), + out.as_mut_ptr(), + out.len(), + ))?; + } + + Ok(()) + } + + /// Signs the computed digest. + /// + /// If `out` is set to `None`, an upper bound on the number of bytes required for the output buffer will be + /// returned. + #[corresponds(EVP_DigestSignFinal)] + #[inline] + pub fn digest_sign_final(&mut self, out: Option<&mut [u8]>) -> Result<usize, ErrorStack> { + let mut len = out.as_ref().map_or(0, |b| b.len()); + + unsafe { + cvt(ffi::EVP_DigestSignFinal( + self.as_ptr(), + out.map_or(ptr::null_mut(), |b| b.as_mut_ptr()), + &mut len, + ))?; + } + + Ok(len) + } + + /// Like [`Self::digest_sign_final`] but appends the signature to a [`Vec`]. + pub fn digest_sign_final_to_vec(&mut self, out: &mut Vec<u8>) -> Result<usize, ErrorStack> { + let base = out.len(); + let len = self.digest_sign_final(None)?; + out.resize(base + len, 0); + let len = self.digest_sign_final(Some(&mut out[base..]))?; + out.truncate(base + len); + Ok(len) + } + + /// Verifies the provided signature. + /// + /// Returns `Ok(true)` if the signature is valid, `Ok(false)` if the signature is invalid, and `Err` if an error + /// occurred. + #[corresponds(EVP_DigestVerifyFinal)] + #[inline] + pub fn digest_verify_final(&mut self, signature: &[u8]) -> Result<bool, ErrorStack> { + unsafe { + let r = cvt_n(ffi::EVP_DigestVerifyFinal( + self.as_ptr(), + signature.as_ptr() as *mut _, + signature.len(), + ))?; + Ok(r == 1) + } + } + + /// Computes the signature of the data in `from`. + /// + /// If `to` is set to `None`, an upper bound on the number of bytes required for the output buffer will be + /// returned. + /// + /// Requires OpenSSL 1.1.1 or newer. + #[corresponds(EVP_DigestSign)] + #[cfg(ossl111)] + #[inline] + pub fn digest_sign(&mut self, from: &[u8], to: Option<&mut [u8]>) -> Result<usize, ErrorStack> { + let mut len = to.as_ref().map_or(0, |b| b.len()); + + unsafe { + cvt(ffi::EVP_DigestSign( + self.as_ptr(), + to.map_or(ptr::null_mut(), |b| b.as_mut_ptr()), + &mut len, + from.as_ptr(), + from.len(), + ))?; + } + + Ok(len) + } + + /// Like [`Self::digest_sign`] but appends the signature to a [`Vec`]. + #[cfg(ossl111)] + pub fn digest_sign_to_vec( + &mut self, + from: &[u8], + to: &mut Vec<u8>, + ) -> Result<usize, ErrorStack> { + let base = to.len(); + let len = self.digest_sign(from, None)?; + to.resize(base + len, 0); + let len = self.digest_sign(from, Some(&mut to[base..]))?; + to.truncate(base + len); + Ok(len) + } + + /// Verifies the signature of the data in `data`. + /// + /// Returns `Ok(true)` if the signature is valid, `Ok(false)` if the signature is invalid, and `Err` if an error + /// occurred. + /// + /// Requires OpenSSL 1.1.1 or newer. + #[corresponds(EVP_DigestVerify)] + #[cfg(ossl111)] + #[inline] + pub fn digest_verify(&mut self, data: &[u8], signature: &[u8]) -> Result<bool, ErrorStack> { + unsafe { + let r = cvt(ffi::EVP_DigestVerify( + self.as_ptr(), + signature.as_ptr(), + signature.len(), + data.as_ptr(), + data.len(), + ))?; + Ok(r == 1) + } + } + + /// Returns the size of the message digest, i.e. the size of the hash + #[corresponds(EVP_MD_CTX_size)] + #[inline] + pub fn size(&self) -> usize { + unsafe { ffi::EVP_MD_CTX_size(self.as_ptr()) as usize } + } + + /// Resets the underlying EVP_MD_CTX instance + #[corresponds(EVP_MD_CTX_reset)] + #[cfg(ossl111)] + #[inline] + pub fn reset(&mut self) -> Result<(), ErrorStack> { + unsafe { + let _ = cvt(ffi::EVP_MD_CTX_reset(self.as_ptr()))?; + Ok(()) + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::md::Md; + use crate::pkey::PKey; + use crate::rsa::Rsa; + + #[test] + fn verify_fail() { + let key1 = Rsa::generate(4096).unwrap(); + let key1 = PKey::from_rsa(key1).unwrap(); + + let md = Md::sha256(); + let data = b"Some Crypto Text"; + + let mut ctx = MdCtx::new().unwrap(); + ctx.digest_sign_init(Some(md), &key1).unwrap(); + ctx.digest_sign_update(data).unwrap(); + let mut signature = vec![]; + ctx.digest_sign_final_to_vec(&mut signature).unwrap(); + + let bad_data = b"Some Crypto text"; + + ctx.digest_verify_init(Some(md), &key1).unwrap(); + ctx.digest_verify_update(bad_data).unwrap(); + let valid = ctx.digest_verify_final(&signature).unwrap(); + assert!(!valid); + } + + #[test] + fn verify_success() { + let key1 = Rsa::generate(2048).unwrap(); + let key1 = PKey::from_rsa(key1).unwrap(); + + let md = Md::sha256(); + let data = b"Some Crypto Text"; + + let mut ctx = MdCtx::new().unwrap(); + ctx.digest_sign_init(Some(md), &key1).unwrap(); + ctx.digest_sign_update(data).unwrap(); + let mut signature = vec![]; + ctx.digest_sign_final_to_vec(&mut signature).unwrap(); + + let good_data = b"Some Crypto Text"; + + ctx.digest_verify_init(Some(md), &key1).unwrap(); + ctx.digest_verify_update(good_data).unwrap(); + let valid = ctx.digest_verify_final(&signature).unwrap(); + assert!(valid); + } + + #[test] + fn verify_with_public_success() { + let rsa = Rsa::generate(2048).unwrap(); + let key1 = PKey::from_rsa(rsa.clone()).unwrap(); + + let md = Md::sha256(); + let data = b"Some Crypto Text"; + + let mut ctx = MdCtx::new().unwrap(); + ctx.digest_sign_init(Some(md), &key1).unwrap(); + ctx.digest_sign_update(data).unwrap(); + let mut signature = vec![]; + ctx.digest_sign_final_to_vec(&mut signature).unwrap(); + + let good_data = b"Some Crypto Text"; + + // try to verify using only public components of the key + let n = rsa.n().to_owned().unwrap(); + let e = rsa.e().to_owned().unwrap(); + + let rsa = Rsa::from_public_components(n, e).unwrap(); + let key1 = PKey::from_rsa(rsa).unwrap(); + + ctx.digest_verify_init(Some(md), &key1).unwrap(); + ctx.digest_verify_update(good_data).unwrap(); + let valid = ctx.digest_verify_final(&signature).unwrap(); + assert!(valid); + } + + #[test] + fn verify_md_ctx_size() { + let mut ctx = MdCtx::new().unwrap(); + ctx.digest_init(Md::sha224()).unwrap(); + assert_eq!(Md::sha224().size(), ctx.size()); + assert_eq!(Md::sha224().size(), 28); + + let mut ctx = MdCtx::new().unwrap(); + ctx.digest_init(Md::sha256()).unwrap(); + assert_eq!(Md::sha256().size(), ctx.size()); + assert_eq!(Md::sha256().size(), 32); + + let mut ctx = MdCtx::new().unwrap(); + ctx.digest_init(Md::sha384()).unwrap(); + assert_eq!(Md::sha384().size(), ctx.size()); + assert_eq!(Md::sha384().size(), 48); + + let mut ctx = MdCtx::new().unwrap(); + ctx.digest_init(Md::sha512()).unwrap(); + assert_eq!(Md::sha512().size(), ctx.size()); + assert_eq!(Md::sha512().size(), 64); + } + + #[test] + #[cfg(ossl111)] + fn verify_md_ctx_reset() { + let hello_expected = + hex::decode("185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969") + .unwrap(); + let world_expected = + hex::decode("78ae647dc5544d227130a0682a51e30bc7777fbb6d8a8f17007463a3ecd1d524") + .unwrap(); + // Calculate SHA-256 digest of "Hello" + let mut ctx = MdCtx::new().unwrap(); + ctx.digest_init(Md::sha256()).unwrap(); + ctx.digest_update(b"Hello").unwrap(); + let mut result = vec![0; 32]; + let result_len = ctx.digest_final(result.as_mut_slice()).unwrap(); + assert_eq!(result_len, result.len()); + // Validate result of "Hello" + assert_eq!(result, hello_expected); + + // Create new context + let mut ctx = MdCtx::new().unwrap(); + // Initialize and update to "Hello" + ctx.digest_init(Md::sha256()).unwrap(); + ctx.digest_update(b"Hello").unwrap(); + // Now reset, init to SHA-256 and use "World" + ctx.reset().unwrap(); + ctx.digest_init(Md::sha256()).unwrap(); + ctx.digest_update(b"World").unwrap(); + + let mut reset_result = vec![0; 32]; + let result_len = ctx.digest_final(reset_result.as_mut_slice()).unwrap(); + assert_eq!(result_len, reset_result.len()); + // Validate result of digest of "World" + assert_eq!(reset_result, world_expected); + } +} diff --git a/vendor/openssl/src/memcmp.rs b/vendor/openssl/src/memcmp.rs new file mode 100644 index 0000000..91281b9 --- /dev/null +++ b/vendor/openssl/src/memcmp.rs @@ -0,0 +1,93 @@ +//! Utilities to safely compare cryptographic values. +//! +//! Extra care must be taken when comparing values in +//! cryptographic code. If done incorrectly, it can lead +//! to a [timing attack](https://en.wikipedia.org/wiki/Timing_attack). +//! By analyzing the time taken to execute parts of a cryptographic +//! algorithm, and attacker can attempt to compromise the +//! cryptosystem. +//! +//! The utilities in this module are designed to be resistant +//! to this type of attack. +//! +//! # Examples +//! +//! To perform a constant-time comparison of two arrays of the same length but different +//! values: +//! +//! ``` +//! use openssl::memcmp::eq; +//! +//! // We want to compare `a` to `b` and `c`, without giving +//! // away through timing analysis that `c` is more similar to `a` +//! // than `b`. +//! let a = [0, 0, 0]; +//! let b = [1, 1, 1]; +//! let c = [0, 0, 1]; +//! +//! // These statements will execute in the same amount of time. +//! assert!(!eq(&a, &b)); +//! assert!(!eq(&a, &c)); +//! ``` +use libc::size_t; +use openssl_macros::corresponds; + +/// Returns `true` iff `a` and `b` contain the same bytes. +/// +/// This operation takes an amount of time dependent on the length of the two +/// arrays given, but is independent of the contents of a and b. +/// +/// # Panics +/// +/// This function will panic the current task if `a` and `b` do not have the same +/// length. +/// +/// # Examples +/// +/// To perform a constant-time comparison of two arrays of the same length but different +/// values: +/// +/// ``` +/// use openssl::memcmp::eq; +/// +/// // We want to compare `a` to `b` and `c`, without giving +/// // away through timing analysis that `c` is more similar to `a` +/// // than `b`. +/// let a = [0, 0, 0]; +/// let b = [1, 1, 1]; +/// let c = [0, 0, 1]; +/// +/// // These statements will execute in the same amount of time. +/// assert!(!eq(&a, &b)); +/// assert!(!eq(&a, &c)); +/// ``` +#[corresponds(CRYPTO_memcmp)] +pub fn eq(a: &[u8], b: &[u8]) -> bool { + assert!(a.len() == b.len()); + let ret = unsafe { + ffi::CRYPTO_memcmp( + a.as_ptr() as *const _, + b.as_ptr() as *const _, + a.len() as size_t, + ) + }; + ret == 0 +} + +#[cfg(test)] +mod tests { + use super::eq; + + #[test] + fn test_eq() { + assert!(eq(&[], &[])); + assert!(eq(&[1], &[1])); + assert!(!eq(&[1, 2, 3], &[1, 2, 4])); + } + + #[test] + #[should_panic] + fn test_diff_lens() { + eq(&[], &[1]); + } +} diff --git a/vendor/openssl/src/nid.rs b/vendor/openssl/src/nid.rs new file mode 100644 index 0000000..91fcdec --- /dev/null +++ b/vendor/openssl/src/nid.rs @@ -0,0 +1,1179 @@ +//! A collection of numerical identifiers for OpenSSL objects. +use libc::{c_char, c_int}; + +use std::ffi::CStr; +use std::ffi::CString; +use std::str; + +use crate::cvt_p; +use crate::error::ErrorStack; +use openssl_macros::corresponds; + +/// The digest and public-key algorithms associated with a signature. +pub struct SignatureAlgorithms { + /// The signature's digest. + /// + /// If the signature does not specify a digest, this will be `NID::UNDEF`. + pub digest: Nid, + + /// The signature's public-key. + pub pkey: Nid, +} + +/// A numerical identifier for an OpenSSL object. +/// +/// Objects in OpenSSL can have a short name, a long name, and +/// a numerical identifier (NID). For convenience, objects +/// are usually represented in source code using these numeric +/// identifiers. +/// +/// Users should generally not need to create new `Nid`s. +/// +/// # Examples +/// +/// To view the integer representation of a `Nid`: +/// +/// ``` +/// use openssl::nid::Nid; +/// +/// assert!(Nid::AES_256_GCM.as_raw() == 901); +/// ``` +/// +/// # External Documentation +/// +/// The following documentation provides context about `Nid`s and their usage +/// in OpenSSL. +/// +/// - [Obj_nid2obj](https://www.openssl.org/docs/manmaster/crypto/OBJ_create.html) +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct Nid(c_int); + +#[allow(non_snake_case)] +impl Nid { + /// Create a `Nid` from an integer representation. + pub const fn from_raw(raw: c_int) -> Nid { + Nid(raw) + } + + /// Return the integer representation of a `Nid`. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub const fn as_raw(&self) -> c_int { + self.0 + } + + /// Creates a new `Nid` for the `oid` with short name `sn` and long name `ln`. + #[corresponds(OBJ_create)] + pub fn create(oid: &str, sn: &str, ln: &str) -> Result<Nid, ErrorStack> { + unsafe { + ffi::init(); + let oid = CString::new(oid).unwrap(); + let sn = CString::new(sn).unwrap(); + let ln = CString::new(ln).unwrap(); + let raw = ffi::OBJ_create(oid.as_ptr(), sn.as_ptr(), ln.as_ptr()); + if raw == ffi::NID_undef { + Err(ErrorStack::get()) + } else { + Ok(Nid(raw)) + } + } + } + + /// Returns the `Nid`s of the digest and public key algorithms associated with a signature ID. + /// + /// This corresponds to `OBJ_find_sigid_algs`. + #[corresponds(OBJ_find_sigid_algs)] + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn signature_algorithms(&self) -> Option<SignatureAlgorithms> { + unsafe { + let mut digest = 0; + let mut pkey = 0; + if ffi::OBJ_find_sigid_algs(self.0, &mut digest, &mut pkey) == 1 { + Some(SignatureAlgorithms { + digest: Nid(digest), + pkey: Nid(pkey), + }) + } else { + None + } + } + } + + /// Returns the string representation of a `Nid` (long). + #[corresponds(OBJ_nid2ln)] + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn long_name(&self) -> Result<&'static str, ErrorStack> { + unsafe { + cvt_p(ffi::OBJ_nid2ln(self.0) as *mut c_char) + .map(|nameptr| str::from_utf8(CStr::from_ptr(nameptr).to_bytes()).unwrap()) + } + } + + /// Returns the string representation of a `Nid` (short). + #[corresponds(OBJ_nid2sn)] + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn short_name(&self) -> Result<&'static str, ErrorStack> { + unsafe { + cvt_p(ffi::OBJ_nid2sn(self.0) as *mut c_char) + .map(|nameptr| str::from_utf8(CStr::from_ptr(nameptr).to_bytes()).unwrap()) + } + } + + pub const UNDEF: Nid = Nid(ffi::NID_undef); + pub const ITU_T: Nid = Nid(ffi::NID_itu_t); + #[cfg(not(boringssl))] + pub const CCITT: Nid = Nid(ffi::NID_ccitt); + pub const ISO: Nid = Nid(ffi::NID_iso); + pub const JOINT_ISO_ITU_T: Nid = Nid(ffi::NID_joint_iso_itu_t); + #[cfg(not(boringssl))] + pub const JOINT_ISO_CCITT: Nid = Nid(ffi::NID_joint_iso_ccitt); + pub const MEMBER_BODY: Nid = Nid(ffi::NID_member_body); + pub const IDENTIFIED_ORGANIZATION: Nid = Nid(ffi::NID_identified_organization); + pub const HMAC_MD5: Nid = Nid(ffi::NID_hmac_md5); + pub const HMAC_SHA1: Nid = Nid(ffi::NID_hmac_sha1); + pub const CERTICOM_ARC: Nid = Nid(ffi::NID_certicom_arc); + pub const INTERNATIONAL_ORGANIZATIONS: Nid = Nid(ffi::NID_international_organizations); + pub const WAP: Nid = Nid(ffi::NID_wap); + pub const WAP_WSG: Nid = Nid(ffi::NID_wap_wsg); + pub const SELECTED_ATTRIBUTE_TYPES: Nid = Nid(ffi::NID_selected_attribute_types); + pub const CLEARANCE: Nid = Nid(ffi::NID_clearance); + pub const ISO_US: Nid = Nid(ffi::NID_ISO_US); + pub const X9_57: Nid = Nid(ffi::NID_X9_57); + pub const X9CM: Nid = Nid(ffi::NID_X9cm); + pub const DSA: Nid = Nid(ffi::NID_dsa); + pub const DSAWITHSHA1: Nid = Nid(ffi::NID_dsaWithSHA1); + pub const ANSI_X9_62: Nid = Nid(ffi::NID_ansi_X9_62); + pub const X9_62_PRIME_FIELD: Nid = Nid(ffi::NID_X9_62_prime_field); + pub const X9_62_CHARACTERISTIC_TWO_FIELD: Nid = Nid(ffi::NID_X9_62_characteristic_two_field); + pub const X9_62_ID_CHARACTERISTIC_TWO_BASIS: Nid = + Nid(ffi::NID_X9_62_id_characteristic_two_basis); + pub const X9_62_ONBASIS: Nid = Nid(ffi::NID_X9_62_onBasis); + pub const X9_62_TPBASIS: Nid = Nid(ffi::NID_X9_62_tpBasis); + pub const X9_62_PPBASIS: Nid = Nid(ffi::NID_X9_62_ppBasis); + pub const X9_62_ID_ECPUBLICKEY: Nid = Nid(ffi::NID_X9_62_id_ecPublicKey); + pub const X9_62_C2PNB163V1: Nid = Nid(ffi::NID_X9_62_c2pnb163v1); + pub const X9_62_C2PNB163V2: Nid = Nid(ffi::NID_X9_62_c2pnb163v2); + pub const X9_62_C2PNB163V3: Nid = Nid(ffi::NID_X9_62_c2pnb163v3); + pub const X9_62_C2PNB176V1: Nid = Nid(ffi::NID_X9_62_c2pnb176v1); + pub const X9_62_C2TNB191V1: Nid = Nid(ffi::NID_X9_62_c2tnb191v1); + pub const X9_62_C2TNB191V2: Nid = Nid(ffi::NID_X9_62_c2tnb191v2); + pub const X9_62_C2TNB191V3: Nid = Nid(ffi::NID_X9_62_c2tnb191v3); + pub const X9_62_C2ONB191V4: Nid = Nid(ffi::NID_X9_62_c2onb191v4); + pub const X9_62_C2ONB191V5: Nid = Nid(ffi::NID_X9_62_c2onb191v5); + pub const X9_62_C2PNB208W1: Nid = Nid(ffi::NID_X9_62_c2pnb208w1); + pub const X9_62_C2TNB239V1: Nid = Nid(ffi::NID_X9_62_c2tnb239v1); + pub const X9_62_C2TNB239V2: Nid = Nid(ffi::NID_X9_62_c2tnb239v2); + pub const X9_62_C2TNB239V3: Nid = Nid(ffi::NID_X9_62_c2tnb239v3); + pub const X9_62_C2ONB239V4: Nid = Nid(ffi::NID_X9_62_c2onb239v4); + pub const X9_62_C2ONB239V5: Nid = Nid(ffi::NID_X9_62_c2onb239v5); + pub const X9_62_C2PNB272W1: Nid = Nid(ffi::NID_X9_62_c2pnb272w1); + pub const X9_62_C2PNB304W1: Nid = Nid(ffi::NID_X9_62_c2pnb304w1); + pub const X9_62_C2TNB359V1: Nid = Nid(ffi::NID_X9_62_c2tnb359v1); + pub const X9_62_C2PNB368W1: Nid = Nid(ffi::NID_X9_62_c2pnb368w1); + pub const X9_62_C2TNB431R1: Nid = Nid(ffi::NID_X9_62_c2tnb431r1); + pub const X9_62_PRIME192V1: Nid = Nid(ffi::NID_X9_62_prime192v1); + pub const X9_62_PRIME192V2: Nid = Nid(ffi::NID_X9_62_prime192v2); + pub const X9_62_PRIME192V3: Nid = Nid(ffi::NID_X9_62_prime192v3); + pub const X9_62_PRIME239V1: Nid = Nid(ffi::NID_X9_62_prime239v1); + pub const X9_62_PRIME239V2: Nid = Nid(ffi::NID_X9_62_prime239v2); + pub const X9_62_PRIME239V3: Nid = Nid(ffi::NID_X9_62_prime239v3); + pub const X9_62_PRIME256V1: Nid = Nid(ffi::NID_X9_62_prime256v1); + pub const ECDSA_WITH_SHA1: Nid = Nid(ffi::NID_ecdsa_with_SHA1); + pub const ECDSA_WITH_RECOMMENDED: Nid = Nid(ffi::NID_ecdsa_with_Recommended); + pub const ECDSA_WITH_SPECIFIED: Nid = Nid(ffi::NID_ecdsa_with_Specified); + pub const ECDSA_WITH_SHA224: Nid = Nid(ffi::NID_ecdsa_with_SHA224); + pub const ECDSA_WITH_SHA256: Nid = Nid(ffi::NID_ecdsa_with_SHA256); + pub const ECDSA_WITH_SHA384: Nid = Nid(ffi::NID_ecdsa_with_SHA384); + pub const ECDSA_WITH_SHA512: Nid = Nid(ffi::NID_ecdsa_with_SHA512); + pub const SECP112R1: Nid = Nid(ffi::NID_secp112r1); + pub const SECP112R2: Nid = Nid(ffi::NID_secp112r2); + pub const SECP128R1: Nid = Nid(ffi::NID_secp128r1); + pub const SECP128R2: Nid = Nid(ffi::NID_secp128r2); + pub const SECP160K1: Nid = Nid(ffi::NID_secp160k1); + pub const SECP160R1: Nid = Nid(ffi::NID_secp160r1); + pub const SECP160R2: Nid = Nid(ffi::NID_secp160r2); + pub const SECP192K1: Nid = Nid(ffi::NID_secp192k1); + pub const SECP224K1: Nid = Nid(ffi::NID_secp224k1); + pub const SECP224R1: Nid = Nid(ffi::NID_secp224r1); + pub const SECP256K1: Nid = Nid(ffi::NID_secp256k1); + pub const SECP384R1: Nid = Nid(ffi::NID_secp384r1); + pub const SECP521R1: Nid = Nid(ffi::NID_secp521r1); + pub const SECT113R1: Nid = Nid(ffi::NID_sect113r1); + pub const SECT113R2: Nid = Nid(ffi::NID_sect113r2); + pub const SECT131R1: Nid = Nid(ffi::NID_sect131r1); + pub const SECT131R2: Nid = Nid(ffi::NID_sect131r2); + pub const SECT163K1: Nid = Nid(ffi::NID_sect163k1); + pub const SECT163R1: Nid = Nid(ffi::NID_sect163r1); + pub const SECT163R2: Nid = Nid(ffi::NID_sect163r2); + pub const SECT193R1: Nid = Nid(ffi::NID_sect193r1); + pub const SECT193R2: Nid = Nid(ffi::NID_sect193r2); + pub const SECT233K1: Nid = Nid(ffi::NID_sect233k1); + pub const SECT233R1: Nid = Nid(ffi::NID_sect233r1); + pub const SECT239K1: Nid = Nid(ffi::NID_sect239k1); + pub const SECT283K1: Nid = Nid(ffi::NID_sect283k1); + pub const SECT283R1: Nid = Nid(ffi::NID_sect283r1); + pub const SECT409K1: Nid = Nid(ffi::NID_sect409k1); + pub const SECT409R1: Nid = Nid(ffi::NID_sect409r1); + pub const SECT571K1: Nid = Nid(ffi::NID_sect571k1); + pub const SECT571R1: Nid = Nid(ffi::NID_sect571r1); + #[cfg(ossl110)] + pub const BRAINPOOL_P256R1: Nid = Nid(ffi::NID_brainpoolP256r1); + #[cfg(ossl110)] + pub const BRAINPOOL_P384R1: Nid = Nid(ffi::NID_brainpoolP384r1); + #[cfg(ossl110)] + pub const BRAINPOOL_P512R1: Nid = Nid(ffi::NID_brainpoolP512r1); + pub const WAP_WSG_IDM_ECID_WTLS1: Nid = Nid(ffi::NID_wap_wsg_idm_ecid_wtls1); + pub const WAP_WSG_IDM_ECID_WTLS3: Nid = Nid(ffi::NID_wap_wsg_idm_ecid_wtls3); + pub const WAP_WSG_IDM_ECID_WTLS4: Nid = Nid(ffi::NID_wap_wsg_idm_ecid_wtls4); + pub const WAP_WSG_IDM_ECID_WTLS5: Nid = Nid(ffi::NID_wap_wsg_idm_ecid_wtls5); + pub const WAP_WSG_IDM_ECID_WTLS6: Nid = Nid(ffi::NID_wap_wsg_idm_ecid_wtls6); + pub const WAP_WSG_IDM_ECID_WTLS7: Nid = Nid(ffi::NID_wap_wsg_idm_ecid_wtls7); + pub const WAP_WSG_IDM_ECID_WTLS8: Nid = Nid(ffi::NID_wap_wsg_idm_ecid_wtls8); + pub const WAP_WSG_IDM_ECID_WTLS9: Nid = Nid(ffi::NID_wap_wsg_idm_ecid_wtls9); + pub const WAP_WSG_IDM_ECID_WTLS10: Nid = Nid(ffi::NID_wap_wsg_idm_ecid_wtls10); + pub const WAP_WSG_IDM_ECID_WTLS11: Nid = Nid(ffi::NID_wap_wsg_idm_ecid_wtls11); + pub const WAP_WSG_IDM_ECID_WTLS12: Nid = Nid(ffi::NID_wap_wsg_idm_ecid_wtls12); + pub const CAST5_CBC: Nid = Nid(ffi::NID_cast5_cbc); + pub const CAST5_ECB: Nid = Nid(ffi::NID_cast5_ecb); + pub const CAST5_CFB64: Nid = Nid(ffi::NID_cast5_cfb64); + pub const CAST5_OFB64: Nid = Nid(ffi::NID_cast5_ofb64); + pub const PBEWITHMD5ANDCAST5_CBC: Nid = Nid(ffi::NID_pbeWithMD5AndCast5_CBC); + pub const ID_PASSWORDBASEDMAC: Nid = Nid(ffi::NID_id_PasswordBasedMAC); + pub const ID_DHBASEDMAC: Nid = Nid(ffi::NID_id_DHBasedMac); + pub const RSADSI: Nid = Nid(ffi::NID_rsadsi); + pub const PKCS: Nid = Nid(ffi::NID_pkcs); + pub const PKCS1: Nid = Nid(ffi::NID_pkcs1); + pub const RSAENCRYPTION: Nid = Nid(ffi::NID_rsaEncryption); + pub const MD2WITHRSAENCRYPTION: Nid = Nid(ffi::NID_md2WithRSAEncryption); + pub const MD4WITHRSAENCRYPTION: Nid = Nid(ffi::NID_md4WithRSAEncryption); + pub const MD5WITHRSAENCRYPTION: Nid = Nid(ffi::NID_md5WithRSAEncryption); + pub const SHA1WITHRSAENCRYPTION: Nid = Nid(ffi::NID_sha1WithRSAEncryption); + pub const RSAESOAEP: Nid = Nid(ffi::NID_rsaesOaep); + pub const MGF1: Nid = Nid(ffi::NID_mgf1); + pub const RSASSAPSS: Nid = Nid(ffi::NID_rsassaPss); + pub const SHA256WITHRSAENCRYPTION: Nid = Nid(ffi::NID_sha256WithRSAEncryption); + pub const SHA384WITHRSAENCRYPTION: Nid = Nid(ffi::NID_sha384WithRSAEncryption); + pub const SHA512WITHRSAENCRYPTION: Nid = Nid(ffi::NID_sha512WithRSAEncryption); + pub const SHA224WITHRSAENCRYPTION: Nid = Nid(ffi::NID_sha224WithRSAEncryption); + pub const PKCS3: Nid = Nid(ffi::NID_pkcs3); + pub const DHKEYAGREEMENT: Nid = Nid(ffi::NID_dhKeyAgreement); + pub const PKCS5: Nid = Nid(ffi::NID_pkcs5); + pub const PBEWITHMD2ANDDES_CBC: Nid = Nid(ffi::NID_pbeWithMD2AndDES_CBC); + pub const PBEWITHMD5ANDDES_CBC: Nid = Nid(ffi::NID_pbeWithMD5AndDES_CBC); + pub const PBEWITHMD2ANDRC2_CBC: Nid = Nid(ffi::NID_pbeWithMD2AndRC2_CBC); + pub const PBEWITHMD5ANDRC2_CBC: Nid = Nid(ffi::NID_pbeWithMD5AndRC2_CBC); + pub const PBEWITHSHA1ANDDES_CBC: Nid = Nid(ffi::NID_pbeWithSHA1AndDES_CBC); + pub const PBEWITHSHA1ANDRC2_CBC: Nid = Nid(ffi::NID_pbeWithSHA1AndRC2_CBC); + pub const ID_PBKDF2: Nid = Nid(ffi::NID_id_pbkdf2); + pub const PBES2: Nid = Nid(ffi::NID_pbes2); + pub const PBMAC1: Nid = Nid(ffi::NID_pbmac1); + pub const PKCS7: Nid = Nid(ffi::NID_pkcs7); + pub const PKCS7_DATA: Nid = Nid(ffi::NID_pkcs7_data); + pub const PKCS7_SIGNED: Nid = Nid(ffi::NID_pkcs7_signed); + pub const PKCS7_ENVELOPED: Nid = Nid(ffi::NID_pkcs7_enveloped); + pub const PKCS7_SIGNEDANDENVELOPED: Nid = Nid(ffi::NID_pkcs7_signedAndEnveloped); + pub const PKCS7_DIGEST: Nid = Nid(ffi::NID_pkcs7_digest); + pub const PKCS7_ENCRYPTED: Nid = Nid(ffi::NID_pkcs7_encrypted); + pub const PKCS9: Nid = Nid(ffi::NID_pkcs9); + pub const PKCS9_EMAILADDRESS: Nid = Nid(ffi::NID_pkcs9_emailAddress); + pub const PKCS9_UNSTRUCTUREDNAME: Nid = Nid(ffi::NID_pkcs9_unstructuredName); + pub const PKCS9_CONTENTTYPE: Nid = Nid(ffi::NID_pkcs9_contentType); + pub const PKCS9_MESSAGEDIGEST: Nid = Nid(ffi::NID_pkcs9_messageDigest); + pub const PKCS9_SIGNINGTIME: Nid = Nid(ffi::NID_pkcs9_signingTime); + pub const PKCS9_COUNTERSIGNATURE: Nid = Nid(ffi::NID_pkcs9_countersignature); + pub const PKCS9_CHALLENGEPASSWORD: Nid = Nid(ffi::NID_pkcs9_challengePassword); + pub const PKCS9_UNSTRUCTUREDADDRESS: Nid = Nid(ffi::NID_pkcs9_unstructuredAddress); + pub const PKCS9_EXTCERTATTRIBUTES: Nid = Nid(ffi::NID_pkcs9_extCertAttributes); + pub const EXT_REQ: Nid = Nid(ffi::NID_ext_req); + pub const SMIMECAPABILITIES: Nid = Nid(ffi::NID_SMIMECapabilities); + pub const SMIME: Nid = Nid(ffi::NID_SMIME); + pub const ID_SMIME_MOD: Nid = Nid(ffi::NID_id_smime_mod); + pub const ID_SMIME_CT: Nid = Nid(ffi::NID_id_smime_ct); + pub const ID_SMIME_AA: Nid = Nid(ffi::NID_id_smime_aa); + pub const ID_SMIME_ALG: Nid = Nid(ffi::NID_id_smime_alg); + pub const ID_SMIME_CD: Nid = Nid(ffi::NID_id_smime_cd); + pub const ID_SMIME_SPQ: Nid = Nid(ffi::NID_id_smime_spq); + pub const ID_SMIME_CTI: Nid = Nid(ffi::NID_id_smime_cti); + pub const ID_SMIME_MOD_CMS: Nid = Nid(ffi::NID_id_smime_mod_cms); + pub const ID_SMIME_MOD_ESS: Nid = Nid(ffi::NID_id_smime_mod_ess); + pub const ID_SMIME_MOD_OID: Nid = Nid(ffi::NID_id_smime_mod_oid); + pub const ID_SMIME_MOD_MSG_V3: Nid = Nid(ffi::NID_id_smime_mod_msg_v3); + pub const ID_SMIME_MOD_ETS_ESIGNATURE_88: Nid = Nid(ffi::NID_id_smime_mod_ets_eSignature_88); + pub const ID_SMIME_MOD_ETS_ESIGNATURE_97: Nid = Nid(ffi::NID_id_smime_mod_ets_eSignature_97); + pub const ID_SMIME_MOD_ETS_ESIGPOLICY_88: Nid = Nid(ffi::NID_id_smime_mod_ets_eSigPolicy_88); + pub const ID_SMIME_MOD_ETS_ESIGPOLICY_97: Nid = Nid(ffi::NID_id_smime_mod_ets_eSigPolicy_97); + pub const ID_SMIME_CT_RECEIPT: Nid = Nid(ffi::NID_id_smime_ct_receipt); + pub const ID_SMIME_CT_AUTHDATA: Nid = Nid(ffi::NID_id_smime_ct_authData); + pub const ID_SMIME_CT_PUBLISHCERT: Nid = Nid(ffi::NID_id_smime_ct_publishCert); + pub const ID_SMIME_CT_TSTINFO: Nid = Nid(ffi::NID_id_smime_ct_TSTInfo); + pub const ID_SMIME_CT_TDTINFO: Nid = Nid(ffi::NID_id_smime_ct_TDTInfo); + pub const ID_SMIME_CT_CONTENTINFO: Nid = Nid(ffi::NID_id_smime_ct_contentInfo); + pub const ID_SMIME_CT_DVCSREQUESTDATA: Nid = Nid(ffi::NID_id_smime_ct_DVCSRequestData); + pub const ID_SMIME_CT_DVCSRESPONSEDATA: Nid = Nid(ffi::NID_id_smime_ct_DVCSResponseData); + pub const ID_SMIME_CT_COMPRESSEDDATA: Nid = Nid(ffi::NID_id_smime_ct_compressedData); + pub const ID_CT_ASCIITEXTWITHCRLF: Nid = Nid(ffi::NID_id_ct_asciiTextWithCRLF); + pub const ID_SMIME_AA_RECEIPTREQUEST: Nid = Nid(ffi::NID_id_smime_aa_receiptRequest); + pub const ID_SMIME_AA_SECURITYLABEL: Nid = Nid(ffi::NID_id_smime_aa_securityLabel); + pub const ID_SMIME_AA_MLEXPANDHISTORY: Nid = Nid(ffi::NID_id_smime_aa_mlExpandHistory); + pub const ID_SMIME_AA_CONTENTHINT: Nid = Nid(ffi::NID_id_smime_aa_contentHint); + pub const ID_SMIME_AA_MSGSIGDIGEST: Nid = Nid(ffi::NID_id_smime_aa_msgSigDigest); + pub const ID_SMIME_AA_ENCAPCONTENTTYPE: Nid = Nid(ffi::NID_id_smime_aa_encapContentType); + pub const ID_SMIME_AA_CONTENTIDENTIFIER: Nid = Nid(ffi::NID_id_smime_aa_contentIdentifier); + pub const ID_SMIME_AA_MACVALUE: Nid = Nid(ffi::NID_id_smime_aa_macValue); + pub const ID_SMIME_AA_EQUIVALENTLABELS: Nid = Nid(ffi::NID_id_smime_aa_equivalentLabels); + pub const ID_SMIME_AA_CONTENTREFERENCE: Nid = Nid(ffi::NID_id_smime_aa_contentReference); + pub const ID_SMIME_AA_ENCRYPKEYPREF: Nid = Nid(ffi::NID_id_smime_aa_encrypKeyPref); + pub const ID_SMIME_AA_SIGNINGCERTIFICATE: Nid = Nid(ffi::NID_id_smime_aa_signingCertificate); + pub const ID_SMIME_AA_SMIMEENCRYPTCERTS: Nid = Nid(ffi::NID_id_smime_aa_smimeEncryptCerts); + pub const ID_SMIME_AA_TIMESTAMPTOKEN: Nid = Nid(ffi::NID_id_smime_aa_timeStampToken); + pub const ID_SMIME_AA_ETS_SIGPOLICYID: Nid = Nid(ffi::NID_id_smime_aa_ets_sigPolicyId); + pub const ID_SMIME_AA_ETS_COMMITMENTTYPE: Nid = Nid(ffi::NID_id_smime_aa_ets_commitmentType); + pub const ID_SMIME_AA_ETS_SIGNERLOCATION: Nid = Nid(ffi::NID_id_smime_aa_ets_signerLocation); + pub const ID_SMIME_AA_ETS_SIGNERATTR: Nid = Nid(ffi::NID_id_smime_aa_ets_signerAttr); + pub const ID_SMIME_AA_ETS_OTHERSIGCERT: Nid = Nid(ffi::NID_id_smime_aa_ets_otherSigCert); + pub const ID_SMIME_AA_ETS_CONTENTTIMESTAMP: Nid = + Nid(ffi::NID_id_smime_aa_ets_contentTimestamp); + pub const ID_SMIME_AA_ETS_CERTIFICATEREFS: Nid = Nid(ffi::NID_id_smime_aa_ets_CertificateRefs); + pub const ID_SMIME_AA_ETS_REVOCATIONREFS: Nid = Nid(ffi::NID_id_smime_aa_ets_RevocationRefs); + pub const ID_SMIME_AA_ETS_CERTVALUES: Nid = Nid(ffi::NID_id_smime_aa_ets_certValues); + pub const ID_SMIME_AA_ETS_REVOCATIONVALUES: Nid = + Nid(ffi::NID_id_smime_aa_ets_revocationValues); + pub const ID_SMIME_AA_ETS_ESCTIMESTAMP: Nid = Nid(ffi::NID_id_smime_aa_ets_escTimeStamp); + pub const ID_SMIME_AA_ETS_CERTCRLTIMESTAMP: Nid = + Nid(ffi::NID_id_smime_aa_ets_certCRLTimestamp); + pub const ID_SMIME_AA_ETS_ARCHIVETIMESTAMP: Nid = + Nid(ffi::NID_id_smime_aa_ets_archiveTimeStamp); + pub const ID_SMIME_AA_SIGNATURETYPE: Nid = Nid(ffi::NID_id_smime_aa_signatureType); + pub const ID_SMIME_AA_DVCS_DVC: Nid = Nid(ffi::NID_id_smime_aa_dvcs_dvc); + pub const ID_SMIME_ALG_ESDHWITH3DES: Nid = Nid(ffi::NID_id_smime_alg_ESDHwith3DES); + pub const ID_SMIME_ALG_ESDHWITHRC2: Nid = Nid(ffi::NID_id_smime_alg_ESDHwithRC2); + pub const ID_SMIME_ALG_3DESWRAP: Nid = Nid(ffi::NID_id_smime_alg_3DESwrap); + pub const ID_SMIME_ALG_RC2WRAP: Nid = Nid(ffi::NID_id_smime_alg_RC2wrap); + pub const ID_SMIME_ALG_ESDH: Nid = Nid(ffi::NID_id_smime_alg_ESDH); + pub const ID_SMIME_ALG_CMS3DESWRAP: Nid = Nid(ffi::NID_id_smime_alg_CMS3DESwrap); + pub const ID_SMIME_ALG_CMSRC2WRAP: Nid = Nid(ffi::NID_id_smime_alg_CMSRC2wrap); + pub const ID_ALG_PWRI_KEK: Nid = Nid(ffi::NID_id_alg_PWRI_KEK); + pub const ID_SMIME_CD_LDAP: Nid = Nid(ffi::NID_id_smime_cd_ldap); + pub const ID_SMIME_SPQ_ETS_SQT_URI: Nid = Nid(ffi::NID_id_smime_spq_ets_sqt_uri); + pub const ID_SMIME_SPQ_ETS_SQT_UNOTICE: Nid = Nid(ffi::NID_id_smime_spq_ets_sqt_unotice); + pub const ID_SMIME_CTI_ETS_PROOFOFORIGIN: Nid = Nid(ffi::NID_id_smime_cti_ets_proofOfOrigin); + pub const ID_SMIME_CTI_ETS_PROOFOFRECEIPT: Nid = Nid(ffi::NID_id_smime_cti_ets_proofOfReceipt); + pub const ID_SMIME_CTI_ETS_PROOFOFDELIVERY: Nid = + Nid(ffi::NID_id_smime_cti_ets_proofOfDelivery); + pub const ID_SMIME_CTI_ETS_PROOFOFSENDER: Nid = Nid(ffi::NID_id_smime_cti_ets_proofOfSender); + pub const ID_SMIME_CTI_ETS_PROOFOFAPPROVAL: Nid = + Nid(ffi::NID_id_smime_cti_ets_proofOfApproval); + pub const ID_SMIME_CTI_ETS_PROOFOFCREATION: Nid = + Nid(ffi::NID_id_smime_cti_ets_proofOfCreation); + pub const FRIENDLYNAME: Nid = Nid(ffi::NID_friendlyName); + pub const LOCALKEYID: Nid = Nid(ffi::NID_localKeyID); + pub const MS_CSP_NAME: Nid = Nid(ffi::NID_ms_csp_name); + pub const LOCALKEYSET: Nid = Nid(ffi::NID_LocalKeySet); + pub const X509CERTIFICATE: Nid = Nid(ffi::NID_x509Certificate); + pub const SDSICERTIFICATE: Nid = Nid(ffi::NID_sdsiCertificate); + pub const X509CRL: Nid = Nid(ffi::NID_x509Crl); + pub const PBE_WITHSHA1AND128BITRC4: Nid = Nid(ffi::NID_pbe_WithSHA1And128BitRC4); + pub const PBE_WITHSHA1AND40BITRC4: Nid = Nid(ffi::NID_pbe_WithSHA1And40BitRC4); + pub const PBE_WITHSHA1AND3_KEY_TRIPLEDES_CBC: Nid = + Nid(ffi::NID_pbe_WithSHA1And3_Key_TripleDES_CBC); + pub const PBE_WITHSHA1AND2_KEY_TRIPLEDES_CBC: Nid = + Nid(ffi::NID_pbe_WithSHA1And2_Key_TripleDES_CBC); + pub const PBE_WITHSHA1AND128BITRC2_CBC: Nid = Nid(ffi::NID_pbe_WithSHA1And128BitRC2_CBC); + pub const PBE_WITHSHA1AND40BITRC2_CBC: Nid = Nid(ffi::NID_pbe_WithSHA1And40BitRC2_CBC); + pub const KEYBAG: Nid = Nid(ffi::NID_keyBag); + pub const PKCS8SHROUDEDKEYBAG: Nid = Nid(ffi::NID_pkcs8ShroudedKeyBag); + pub const CERTBAG: Nid = Nid(ffi::NID_certBag); + pub const CRLBAG: Nid = Nid(ffi::NID_crlBag); + pub const SECRETBAG: Nid = Nid(ffi::NID_secretBag); + pub const SAFECONTENTSBAG: Nid = Nid(ffi::NID_safeContentsBag); + pub const MD2: Nid = Nid(ffi::NID_md2); + pub const MD4: Nid = Nid(ffi::NID_md4); + pub const MD5: Nid = Nid(ffi::NID_md5); + pub const MD5_SHA1: Nid = Nid(ffi::NID_md5_sha1); + pub const HMACWITHMD5: Nid = Nid(ffi::NID_hmacWithMD5); + pub const HMACWITHSHA1: Nid = Nid(ffi::NID_hmacWithSHA1); + pub const HMACWITHSHA224: Nid = Nid(ffi::NID_hmacWithSHA224); + pub const HMACWITHSHA256: Nid = Nid(ffi::NID_hmacWithSHA256); + pub const HMACWITHSHA384: Nid = Nid(ffi::NID_hmacWithSHA384); + pub const HMACWITHSHA512: Nid = Nid(ffi::NID_hmacWithSHA512); + pub const RC2_CBC: Nid = Nid(ffi::NID_rc2_cbc); + pub const RC2_ECB: Nid = Nid(ffi::NID_rc2_ecb); + pub const RC2_CFB64: Nid = Nid(ffi::NID_rc2_cfb64); + pub const RC2_OFB64: Nid = Nid(ffi::NID_rc2_ofb64); + pub const RC2_40_CBC: Nid = Nid(ffi::NID_rc2_40_cbc); + pub const RC2_64_CBC: Nid = Nid(ffi::NID_rc2_64_cbc); + pub const RC4: Nid = Nid(ffi::NID_rc4); + pub const RC4_40: Nid = Nid(ffi::NID_rc4_40); + pub const DES_EDE3_CBC: Nid = Nid(ffi::NID_des_ede3_cbc); + pub const RC5_CBC: Nid = Nid(ffi::NID_rc5_cbc); + pub const RC5_ECB: Nid = Nid(ffi::NID_rc5_ecb); + pub const RC5_CFB64: Nid = Nid(ffi::NID_rc5_cfb64); + pub const RC5_OFB64: Nid = Nid(ffi::NID_rc5_ofb64); + pub const MS_EXT_REQ: Nid = Nid(ffi::NID_ms_ext_req); + pub const MS_CODE_IND: Nid = Nid(ffi::NID_ms_code_ind); + pub const MS_CODE_COM: Nid = Nid(ffi::NID_ms_code_com); + pub const MS_CTL_SIGN: Nid = Nid(ffi::NID_ms_ctl_sign); + pub const MS_SGC: Nid = Nid(ffi::NID_ms_sgc); + pub const MS_EFS: Nid = Nid(ffi::NID_ms_efs); + pub const MS_SMARTCARD_LOGIN: Nid = Nid(ffi::NID_ms_smartcard_login); + pub const MS_UPN: Nid = Nid(ffi::NID_ms_upn); + pub const IDEA_CBC: Nid = Nid(ffi::NID_idea_cbc); + pub const IDEA_ECB: Nid = Nid(ffi::NID_idea_ecb); + pub const IDEA_CFB64: Nid = Nid(ffi::NID_idea_cfb64); + pub const IDEA_OFB64: Nid = Nid(ffi::NID_idea_ofb64); + pub const BF_CBC: Nid = Nid(ffi::NID_bf_cbc); + pub const BF_ECB: Nid = Nid(ffi::NID_bf_ecb); + pub const BF_CFB64: Nid = Nid(ffi::NID_bf_cfb64); + pub const BF_OFB64: Nid = Nid(ffi::NID_bf_ofb64); + pub const ID_PKIX: Nid = Nid(ffi::NID_id_pkix); + pub const ID_PKIX_MOD: Nid = Nid(ffi::NID_id_pkix_mod); + pub const ID_PE: Nid = Nid(ffi::NID_id_pe); + pub const ID_QT: Nid = Nid(ffi::NID_id_qt); + pub const ID_KP: Nid = Nid(ffi::NID_id_kp); + pub const ID_IT: Nid = Nid(ffi::NID_id_it); + pub const ID_PKIP: Nid = Nid(ffi::NID_id_pkip); + pub const ID_ALG: Nid = Nid(ffi::NID_id_alg); + pub const ID_CMC: Nid = Nid(ffi::NID_id_cmc); + pub const ID_ON: Nid = Nid(ffi::NID_id_on); + pub const ID_PDA: Nid = Nid(ffi::NID_id_pda); + pub const ID_ACA: Nid = Nid(ffi::NID_id_aca); + pub const ID_QCS: Nid = Nid(ffi::NID_id_qcs); + pub const ID_CCT: Nid = Nid(ffi::NID_id_cct); + pub const ID_PPL: Nid = Nid(ffi::NID_id_ppl); + pub const ID_AD: Nid = Nid(ffi::NID_id_ad); + pub const ID_PKIX1_EXPLICIT_88: Nid = Nid(ffi::NID_id_pkix1_explicit_88); + pub const ID_PKIX1_IMPLICIT_88: Nid = Nid(ffi::NID_id_pkix1_implicit_88); + pub const ID_PKIX1_EXPLICIT_93: Nid = Nid(ffi::NID_id_pkix1_explicit_93); + pub const ID_PKIX1_IMPLICIT_93: Nid = Nid(ffi::NID_id_pkix1_implicit_93); + pub const ID_MOD_CRMF: Nid = Nid(ffi::NID_id_mod_crmf); + pub const ID_MOD_CMC: Nid = Nid(ffi::NID_id_mod_cmc); + pub const ID_MOD_KEA_PROFILE_88: Nid = Nid(ffi::NID_id_mod_kea_profile_88); + pub const ID_MOD_KEA_PROFILE_93: Nid = Nid(ffi::NID_id_mod_kea_profile_93); + pub const ID_MOD_CMP: Nid = Nid(ffi::NID_id_mod_cmp); + pub const ID_MOD_QUALIFIED_CERT_88: Nid = Nid(ffi::NID_id_mod_qualified_cert_88); + pub const ID_MOD_QUALIFIED_CERT_93: Nid = Nid(ffi::NID_id_mod_qualified_cert_93); + pub const ID_MOD_ATTRIBUTE_CERT: Nid = Nid(ffi::NID_id_mod_attribute_cert); + pub const ID_MOD_TIMESTAMP_PROTOCOL: Nid = Nid(ffi::NID_id_mod_timestamp_protocol); + pub const ID_MOD_OCSP: Nid = Nid(ffi::NID_id_mod_ocsp); + pub const ID_MOD_DVCS: Nid = Nid(ffi::NID_id_mod_dvcs); + pub const ID_MOD_CMP2000: Nid = Nid(ffi::NID_id_mod_cmp2000); + pub const INFO_ACCESS: Nid = Nid(ffi::NID_info_access); + pub const BIOMETRICINFO: Nid = Nid(ffi::NID_biometricInfo); + pub const QCSTATEMENTS: Nid = Nid(ffi::NID_qcStatements); + pub const AC_AUDITENTITY: Nid = Nid(ffi::NID_ac_auditEntity); + pub const AC_TARGETING: Nid = Nid(ffi::NID_ac_targeting); + pub const AACONTROLS: Nid = Nid(ffi::NID_aaControls); + pub const SBGP_IPADDRBLOCK: Nid = Nid(ffi::NID_sbgp_ipAddrBlock); + pub const SBGP_AUTONOMOUSSYSNUM: Nid = Nid(ffi::NID_sbgp_autonomousSysNum); + pub const SBGP_ROUTERIDENTIFIER: Nid = Nid(ffi::NID_sbgp_routerIdentifier); + pub const AC_PROXYING: Nid = Nid(ffi::NID_ac_proxying); + pub const SINFO_ACCESS: Nid = Nid(ffi::NID_sinfo_access); + pub const PROXYCERTINFO: Nid = Nid(ffi::NID_proxyCertInfo); + pub const ID_QT_CPS: Nid = Nid(ffi::NID_id_qt_cps); + pub const ID_QT_UNOTICE: Nid = Nid(ffi::NID_id_qt_unotice); + pub const TEXTNOTICE: Nid = Nid(ffi::NID_textNotice); + pub const SERVER_AUTH: Nid = Nid(ffi::NID_server_auth); + pub const CLIENT_AUTH: Nid = Nid(ffi::NID_client_auth); + pub const CODE_SIGN: Nid = Nid(ffi::NID_code_sign); + pub const EMAIL_PROTECT: Nid = Nid(ffi::NID_email_protect); + pub const IPSECENDSYSTEM: Nid = Nid(ffi::NID_ipsecEndSystem); + pub const IPSECTUNNEL: Nid = Nid(ffi::NID_ipsecTunnel); + pub const IPSECUSER: Nid = Nid(ffi::NID_ipsecUser); + pub const TIME_STAMP: Nid = Nid(ffi::NID_time_stamp); + pub const OCSP_SIGN: Nid = Nid(ffi::NID_OCSP_sign); + pub const DVCS: Nid = Nid(ffi::NID_dvcs); + pub const ID_IT_CAPROTENCCERT: Nid = Nid(ffi::NID_id_it_caProtEncCert); + pub const ID_IT_SIGNKEYPAIRTYPES: Nid = Nid(ffi::NID_id_it_signKeyPairTypes); + pub const ID_IT_ENCKEYPAIRTYPES: Nid = Nid(ffi::NID_id_it_encKeyPairTypes); + pub const ID_IT_PREFERREDSYMMALG: Nid = Nid(ffi::NID_id_it_preferredSymmAlg); + pub const ID_IT_CAKEYUPDATEINFO: Nid = Nid(ffi::NID_id_it_caKeyUpdateInfo); + pub const ID_IT_CURRENTCRL: Nid = Nid(ffi::NID_id_it_currentCRL); + pub const ID_IT_UNSUPPORTEDOIDS: Nid = Nid(ffi::NID_id_it_unsupportedOIDs); + pub const ID_IT_SUBSCRIPTIONREQUEST: Nid = Nid(ffi::NID_id_it_subscriptionRequest); + pub const ID_IT_SUBSCRIPTIONRESPONSE: Nid = Nid(ffi::NID_id_it_subscriptionResponse); + pub const ID_IT_KEYPAIRPARAMREQ: Nid = Nid(ffi::NID_id_it_keyPairParamReq); + pub const ID_IT_KEYPAIRPARAMREP: Nid = Nid(ffi::NID_id_it_keyPairParamRep); + pub const ID_IT_REVPASSPHRASE: Nid = Nid(ffi::NID_id_it_revPassphrase); + pub const ID_IT_IMPLICITCONFIRM: Nid = Nid(ffi::NID_id_it_implicitConfirm); + pub const ID_IT_CONFIRMWAITTIME: Nid = Nid(ffi::NID_id_it_confirmWaitTime); + pub const ID_IT_ORIGPKIMESSAGE: Nid = Nid(ffi::NID_id_it_origPKIMessage); + pub const ID_IT_SUPPLANGTAGS: Nid = Nid(ffi::NID_id_it_suppLangTags); + pub const ID_REGCTRL: Nid = Nid(ffi::NID_id_regCtrl); + pub const ID_REGINFO: Nid = Nid(ffi::NID_id_regInfo); + pub const ID_REGCTRL_REGTOKEN: Nid = Nid(ffi::NID_id_regCtrl_regToken); + pub const ID_REGCTRL_AUTHENTICATOR: Nid = Nid(ffi::NID_id_regCtrl_authenticator); + pub const ID_REGCTRL_PKIPUBLICATIONINFO: Nid = Nid(ffi::NID_id_regCtrl_pkiPublicationInfo); + pub const ID_REGCTRL_PKIARCHIVEOPTIONS: Nid = Nid(ffi::NID_id_regCtrl_pkiArchiveOptions); + pub const ID_REGCTRL_OLDCERTID: Nid = Nid(ffi::NID_id_regCtrl_oldCertID); + pub const ID_REGCTRL_PROTOCOLENCRKEY: Nid = Nid(ffi::NID_id_regCtrl_protocolEncrKey); + pub const ID_REGINFO_UTF8PAIRS: Nid = Nid(ffi::NID_id_regInfo_utf8Pairs); + pub const ID_REGINFO_CERTREQ: Nid = Nid(ffi::NID_id_regInfo_certReq); + pub const ID_ALG_DES40: Nid = Nid(ffi::NID_id_alg_des40); + pub const ID_ALG_NOSIGNATURE: Nid = Nid(ffi::NID_id_alg_noSignature); + pub const ID_ALG_DH_SIG_HMAC_SHA1: Nid = Nid(ffi::NID_id_alg_dh_sig_hmac_sha1); + pub const ID_ALG_DH_POP: Nid = Nid(ffi::NID_id_alg_dh_pop); + pub const ID_CMC_STATUSINFO: Nid = Nid(ffi::NID_id_cmc_statusInfo); + pub const ID_CMC_IDENTIFICATION: Nid = Nid(ffi::NID_id_cmc_identification); + pub const ID_CMC_IDENTITYPROOF: Nid = Nid(ffi::NID_id_cmc_identityProof); + pub const ID_CMC_DATARETURN: Nid = Nid(ffi::NID_id_cmc_dataReturn); + pub const ID_CMC_TRANSACTIONID: Nid = Nid(ffi::NID_id_cmc_transactionId); + pub const ID_CMC_SENDERNONCE: Nid = Nid(ffi::NID_id_cmc_senderNonce); + pub const ID_CMC_RECIPIENTNONCE: Nid = Nid(ffi::NID_id_cmc_recipientNonce); + pub const ID_CMC_ADDEXTENSIONS: Nid = Nid(ffi::NID_id_cmc_addExtensions); + pub const ID_CMC_ENCRYPTEDPOP: Nid = Nid(ffi::NID_id_cmc_encryptedPOP); + pub const ID_CMC_DECRYPTEDPOP: Nid = Nid(ffi::NID_id_cmc_decryptedPOP); + pub const ID_CMC_LRAPOPWITNESS: Nid = Nid(ffi::NID_id_cmc_lraPOPWitness); + pub const ID_CMC_GETCERT: Nid = Nid(ffi::NID_id_cmc_getCert); + pub const ID_CMC_GETCRL: Nid = Nid(ffi::NID_id_cmc_getCRL); + pub const ID_CMC_REVOKEREQUEST: Nid = Nid(ffi::NID_id_cmc_revokeRequest); + pub const ID_CMC_REGINFO: Nid = Nid(ffi::NID_id_cmc_regInfo); + pub const ID_CMC_RESPONSEINFO: Nid = Nid(ffi::NID_id_cmc_responseInfo); + pub const ID_CMC_QUERYPENDING: Nid = Nid(ffi::NID_id_cmc_queryPending); + pub const ID_CMC_POPLINKRANDOM: Nid = Nid(ffi::NID_id_cmc_popLinkRandom); + pub const ID_CMC_POPLINKWITNESS: Nid = Nid(ffi::NID_id_cmc_popLinkWitness); + pub const ID_CMC_CONFIRMCERTACCEPTANCE: Nid = Nid(ffi::NID_id_cmc_confirmCertAcceptance); + pub const ID_ON_PERSONALDATA: Nid = Nid(ffi::NID_id_on_personalData); + pub const ID_ON_PERMANENTIDENTIFIER: Nid = Nid(ffi::NID_id_on_permanentIdentifier); + pub const ID_PDA_DATEOFBIRTH: Nid = Nid(ffi::NID_id_pda_dateOfBirth); + pub const ID_PDA_PLACEOFBIRTH: Nid = Nid(ffi::NID_id_pda_placeOfBirth); + pub const ID_PDA_GENDER: Nid = Nid(ffi::NID_id_pda_gender); + pub const ID_PDA_COUNTRYOFCITIZENSHIP: Nid = Nid(ffi::NID_id_pda_countryOfCitizenship); + pub const ID_PDA_COUNTRYOFRESIDENCE: Nid = Nid(ffi::NID_id_pda_countryOfResidence); + pub const ID_ACA_AUTHENTICATIONINFO: Nid = Nid(ffi::NID_id_aca_authenticationInfo); + pub const ID_ACA_ACCESSIDENTITY: Nid = Nid(ffi::NID_id_aca_accessIdentity); + pub const ID_ACA_CHARGINGIDENTITY: Nid = Nid(ffi::NID_id_aca_chargingIdentity); + pub const ID_ACA_GROUP: Nid = Nid(ffi::NID_id_aca_group); + pub const ID_ACA_ROLE: Nid = Nid(ffi::NID_id_aca_role); + pub const ID_ACA_ENCATTRS: Nid = Nid(ffi::NID_id_aca_encAttrs); + pub const ID_QCS_PKIXQCSYNTAX_V1: Nid = Nid(ffi::NID_id_qcs_pkixQCSyntax_v1); + pub const ID_CCT_CRS: Nid = Nid(ffi::NID_id_cct_crs); + pub const ID_CCT_PKIDATA: Nid = Nid(ffi::NID_id_cct_PKIData); + pub const ID_CCT_PKIRESPONSE: Nid = Nid(ffi::NID_id_cct_PKIResponse); + pub const ID_PPL_ANYLANGUAGE: Nid = Nid(ffi::NID_id_ppl_anyLanguage); + pub const ID_PPL_INHERITALL: Nid = Nid(ffi::NID_id_ppl_inheritAll); + pub const INDEPENDENT: Nid = Nid(ffi::NID_Independent); + pub const AD_OCSP: Nid = Nid(ffi::NID_ad_OCSP); + pub const AD_CA_ISSUERS: Nid = Nid(ffi::NID_ad_ca_issuers); + pub const AD_TIMESTAMPING: Nid = Nid(ffi::NID_ad_timeStamping); + pub const AD_DVCS: Nid = Nid(ffi::NID_ad_dvcs); + pub const CAREPOSITORY: Nid = Nid(ffi::NID_caRepository); + pub const ID_PKIX_OCSP_BASIC: Nid = Nid(ffi::NID_id_pkix_OCSP_basic); + pub const ID_PKIX_OCSP_NONCE: Nid = Nid(ffi::NID_id_pkix_OCSP_Nonce); + pub const ID_PKIX_OCSP_CRLID: Nid = Nid(ffi::NID_id_pkix_OCSP_CrlID); + pub const ID_PKIX_OCSP_ACCEPTABLERESPONSES: Nid = + Nid(ffi::NID_id_pkix_OCSP_acceptableResponses); + pub const ID_PKIX_OCSP_NOCHECK: Nid = Nid(ffi::NID_id_pkix_OCSP_noCheck); + pub const ID_PKIX_OCSP_ARCHIVECUTOFF: Nid = Nid(ffi::NID_id_pkix_OCSP_archiveCutoff); + pub const ID_PKIX_OCSP_SERVICELOCATOR: Nid = Nid(ffi::NID_id_pkix_OCSP_serviceLocator); + pub const ID_PKIX_OCSP_EXTENDEDSTATUS: Nid = Nid(ffi::NID_id_pkix_OCSP_extendedStatus); + pub const ID_PKIX_OCSP_VALID: Nid = Nid(ffi::NID_id_pkix_OCSP_valid); + pub const ID_PKIX_OCSP_PATH: Nid = Nid(ffi::NID_id_pkix_OCSP_path); + pub const ID_PKIX_OCSP_TRUSTROOT: Nid = Nid(ffi::NID_id_pkix_OCSP_trustRoot); + pub const ALGORITHM: Nid = Nid(ffi::NID_algorithm); + pub const MD5WITHRSA: Nid = Nid(ffi::NID_md5WithRSA); + pub const DES_ECB: Nid = Nid(ffi::NID_des_ecb); + pub const DES_CBC: Nid = Nid(ffi::NID_des_cbc); + pub const DES_OFB64: Nid = Nid(ffi::NID_des_ofb64); + pub const DES_CFB64: Nid = Nid(ffi::NID_des_cfb64); + pub const RSASIGNATURE: Nid = Nid(ffi::NID_rsaSignature); + pub const DSA_2: Nid = Nid(ffi::NID_dsa_2); + pub const DSAWITHSHA: Nid = Nid(ffi::NID_dsaWithSHA); + pub const SHAWITHRSAENCRYPTION: Nid = Nid(ffi::NID_shaWithRSAEncryption); + pub const DES_EDE_ECB: Nid = Nid(ffi::NID_des_ede_ecb); + pub const DES_EDE3_ECB: Nid = Nid(ffi::NID_des_ede3_ecb); + pub const DES_EDE_CBC: Nid = Nid(ffi::NID_des_ede_cbc); + pub const DES_EDE_CFB64: Nid = Nid(ffi::NID_des_ede_cfb64); + pub const DES_EDE3_CFB64: Nid = Nid(ffi::NID_des_ede3_cfb64); + pub const DES_EDE_OFB64: Nid = Nid(ffi::NID_des_ede_ofb64); + pub const DES_EDE3_OFB64: Nid = Nid(ffi::NID_des_ede3_ofb64); + pub const DESX_CBC: Nid = Nid(ffi::NID_desx_cbc); + pub const SHA: Nid = Nid(ffi::NID_sha); + pub const SHA1: Nid = Nid(ffi::NID_sha1); + pub const DSAWITHSHA1_2: Nid = Nid(ffi::NID_dsaWithSHA1_2); + pub const SHA1WITHRSA: Nid = Nid(ffi::NID_sha1WithRSA); + pub const RIPEMD160: Nid = Nid(ffi::NID_ripemd160); + pub const RIPEMD160WITHRSA: Nid = Nid(ffi::NID_ripemd160WithRSA); + pub const SXNET: Nid = Nid(ffi::NID_sxnet); + pub const X500: Nid = Nid(ffi::NID_X500); + pub const X509: Nid = Nid(ffi::NID_X509); + pub const COMMONNAME: Nid = Nid(ffi::NID_commonName); + pub const SURNAME: Nid = Nid(ffi::NID_surname); + pub const SERIALNUMBER: Nid = Nid(ffi::NID_serialNumber); + pub const COUNTRYNAME: Nid = Nid(ffi::NID_countryName); + pub const LOCALITYNAME: Nid = Nid(ffi::NID_localityName); + pub const STATEORPROVINCENAME: Nid = Nid(ffi::NID_stateOrProvinceName); + pub const STREETADDRESS: Nid = Nid(ffi::NID_streetAddress); + pub const ORGANIZATIONNAME: Nid = Nid(ffi::NID_organizationName); + pub const ORGANIZATIONALUNITNAME: Nid = Nid(ffi::NID_organizationalUnitName); + pub const TITLE: Nid = Nid(ffi::NID_title); + pub const DESCRIPTION: Nid = Nid(ffi::NID_description); + pub const SEARCHGUIDE: Nid = Nid(ffi::NID_searchGuide); + pub const BUSINESSCATEGORY: Nid = Nid(ffi::NID_businessCategory); + pub const POSTALADDRESS: Nid = Nid(ffi::NID_postalAddress); + pub const POSTALCODE: Nid = Nid(ffi::NID_postalCode); + pub const POSTOFFICEBOX: Nid = Nid(ffi::NID_postOfficeBox); + pub const PHYSICALDELIVERYOFFICENAME: Nid = Nid(ffi::NID_physicalDeliveryOfficeName); + pub const TELEPHONENUMBER: Nid = Nid(ffi::NID_telephoneNumber); + pub const TELEXNUMBER: Nid = Nid(ffi::NID_telexNumber); + pub const TELETEXTERMINALIDENTIFIER: Nid = Nid(ffi::NID_teletexTerminalIdentifier); + pub const FACSIMILETELEPHONENUMBER: Nid = Nid(ffi::NID_facsimileTelephoneNumber); + pub const X121ADDRESS: Nid = Nid(ffi::NID_x121Address); + pub const INTERNATIONALISDNNUMBER: Nid = Nid(ffi::NID_internationaliSDNNumber); + pub const REGISTEREDADDRESS: Nid = Nid(ffi::NID_registeredAddress); + pub const DESTINATIONINDICATOR: Nid = Nid(ffi::NID_destinationIndicator); + pub const PREFERREDDELIVERYMETHOD: Nid = Nid(ffi::NID_preferredDeliveryMethod); + pub const PRESENTATIONADDRESS: Nid = Nid(ffi::NID_presentationAddress); + pub const SUPPORTEDAPPLICATIONCONTEXT: Nid = Nid(ffi::NID_supportedApplicationContext); + pub const MEMBER: Nid = Nid(ffi::NID_member); + pub const OWNER: Nid = Nid(ffi::NID_owner); + pub const ROLEOCCUPANT: Nid = Nid(ffi::NID_roleOccupant); + pub const SEEALSO: Nid = Nid(ffi::NID_seeAlso); + pub const USERPASSWORD: Nid = Nid(ffi::NID_userPassword); + pub const USERCERTIFICATE: Nid = Nid(ffi::NID_userCertificate); + pub const CACERTIFICATE: Nid = Nid(ffi::NID_cACertificate); + pub const AUTHORITYREVOCATIONLIST: Nid = Nid(ffi::NID_authorityRevocationList); + pub const CERTIFICATEREVOCATIONLIST: Nid = Nid(ffi::NID_certificateRevocationList); + pub const CROSSCERTIFICATEPAIR: Nid = Nid(ffi::NID_crossCertificatePair); + pub const NAME: Nid = Nid(ffi::NID_name); + pub const GIVENNAME: Nid = Nid(ffi::NID_givenName); + pub const INITIALS: Nid = Nid(ffi::NID_initials); + pub const GENERATIONQUALIFIER: Nid = Nid(ffi::NID_generationQualifier); + pub const X500UNIQUEIDENTIFIER: Nid = Nid(ffi::NID_x500UniqueIdentifier); + pub const DNQUALIFIER: Nid = Nid(ffi::NID_dnQualifier); + pub const ENHANCEDSEARCHGUIDE: Nid = Nid(ffi::NID_enhancedSearchGuide); + pub const PROTOCOLINFORMATION: Nid = Nid(ffi::NID_protocolInformation); + pub const DISTINGUISHEDNAME: Nid = Nid(ffi::NID_distinguishedName); + pub const UNIQUEMEMBER: Nid = Nid(ffi::NID_uniqueMember); + pub const HOUSEIDENTIFIER: Nid = Nid(ffi::NID_houseIdentifier); + pub const SUPPORTEDALGORITHMS: Nid = Nid(ffi::NID_supportedAlgorithms); + pub const DELTAREVOCATIONLIST: Nid = Nid(ffi::NID_deltaRevocationList); + pub const DMDNAME: Nid = Nid(ffi::NID_dmdName); + pub const PSEUDONYM: Nid = Nid(ffi::NID_pseudonym); + pub const ROLE: Nid = Nid(ffi::NID_role); + pub const X500ALGORITHMS: Nid = Nid(ffi::NID_X500algorithms); + pub const RSA: Nid = Nid(ffi::NID_rsa); + pub const MDC2WITHRSA: Nid = Nid(ffi::NID_mdc2WithRSA); + pub const MDC2: Nid = Nid(ffi::NID_mdc2); + pub const ID_CE: Nid = Nid(ffi::NID_id_ce); + pub const SUBJECT_DIRECTORY_ATTRIBUTES: Nid = Nid(ffi::NID_subject_directory_attributes); + pub const SUBJECT_KEY_IDENTIFIER: Nid = Nid(ffi::NID_subject_key_identifier); + pub const KEY_USAGE: Nid = Nid(ffi::NID_key_usage); + pub const PRIVATE_KEY_USAGE_PERIOD: Nid = Nid(ffi::NID_private_key_usage_period); + pub const SUBJECT_ALT_NAME: Nid = Nid(ffi::NID_subject_alt_name); + pub const ISSUER_ALT_NAME: Nid = Nid(ffi::NID_issuer_alt_name); + pub const BASIC_CONSTRAINTS: Nid = Nid(ffi::NID_basic_constraints); + pub const CRL_NUMBER: Nid = Nid(ffi::NID_crl_number); + pub const CRL_REASON: Nid = Nid(ffi::NID_crl_reason); + pub const INVALIDITY_DATE: Nid = Nid(ffi::NID_invalidity_date); + pub const DELTA_CRL: Nid = Nid(ffi::NID_delta_crl); + pub const ISSUING_DISTRIBUTION_POINT: Nid = Nid(ffi::NID_issuing_distribution_point); + pub const CERTIFICATE_ISSUER: Nid = Nid(ffi::NID_certificate_issuer); + pub const NAME_CONSTRAINTS: Nid = Nid(ffi::NID_name_constraints); + pub const CRL_DISTRIBUTION_POINTS: Nid = Nid(ffi::NID_crl_distribution_points); + pub const CERTIFICATE_POLICIES: Nid = Nid(ffi::NID_certificate_policies); + pub const ANY_POLICY: Nid = Nid(ffi::NID_any_policy); + pub const POLICY_MAPPINGS: Nid = Nid(ffi::NID_policy_mappings); + pub const AUTHORITY_KEY_IDENTIFIER: Nid = Nid(ffi::NID_authority_key_identifier); + pub const POLICY_CONSTRAINTS: Nid = Nid(ffi::NID_policy_constraints); + pub const EXT_KEY_USAGE: Nid = Nid(ffi::NID_ext_key_usage); + pub const FRESHEST_CRL: Nid = Nid(ffi::NID_freshest_crl); + pub const INHIBIT_ANY_POLICY: Nid = Nid(ffi::NID_inhibit_any_policy); + pub const TARGET_INFORMATION: Nid = Nid(ffi::NID_target_information); + pub const NO_REV_AVAIL: Nid = Nid(ffi::NID_no_rev_avail); + pub const ANYEXTENDEDKEYUSAGE: Nid = Nid(ffi::NID_anyExtendedKeyUsage); + pub const NETSCAPE: Nid = Nid(ffi::NID_netscape); + pub const NETSCAPE_CERT_EXTENSION: Nid = Nid(ffi::NID_netscape_cert_extension); + pub const NETSCAPE_DATA_TYPE: Nid = Nid(ffi::NID_netscape_data_type); + pub const NETSCAPE_CERT_TYPE: Nid = Nid(ffi::NID_netscape_cert_type); + pub const NETSCAPE_BASE_URL: Nid = Nid(ffi::NID_netscape_base_url); + pub const NETSCAPE_REVOCATION_URL: Nid = Nid(ffi::NID_netscape_revocation_url); + pub const NETSCAPE_CA_REVOCATION_URL: Nid = Nid(ffi::NID_netscape_ca_revocation_url); + pub const NETSCAPE_RENEWAL_URL: Nid = Nid(ffi::NID_netscape_renewal_url); + pub const NETSCAPE_CA_POLICY_URL: Nid = Nid(ffi::NID_netscape_ca_policy_url); + pub const NETSCAPE_SSL_SERVER_NAME: Nid = Nid(ffi::NID_netscape_ssl_server_name); + pub const NETSCAPE_COMMENT: Nid = Nid(ffi::NID_netscape_comment); + pub const NETSCAPE_CERT_SEQUENCE: Nid = Nid(ffi::NID_netscape_cert_sequence); + pub const NS_SGC: Nid = Nid(ffi::NID_ns_sgc); + pub const ORG: Nid = Nid(ffi::NID_org); + pub const DOD: Nid = Nid(ffi::NID_dod); + pub const IANA: Nid = Nid(ffi::NID_iana); + pub const DIRECTORY: Nid = Nid(ffi::NID_Directory); + pub const MANAGEMENT: Nid = Nid(ffi::NID_Management); + pub const EXPERIMENTAL: Nid = Nid(ffi::NID_Experimental); + pub const PRIVATE: Nid = Nid(ffi::NID_Private); + pub const SECURITY: Nid = Nid(ffi::NID_Security); + pub const SNMPV2: Nid = Nid(ffi::NID_SNMPv2); + pub const MAIL: Nid = Nid(ffi::NID_Mail); + pub const ENTERPRISES: Nid = Nid(ffi::NID_Enterprises); + pub const DCOBJECT: Nid = Nid(ffi::NID_dcObject); + pub const MIME_MHS: Nid = Nid(ffi::NID_mime_mhs); + pub const MIME_MHS_HEADINGS: Nid = Nid(ffi::NID_mime_mhs_headings); + pub const MIME_MHS_BODIES: Nid = Nid(ffi::NID_mime_mhs_bodies); + pub const ID_HEX_PARTIAL_MESSAGE: Nid = Nid(ffi::NID_id_hex_partial_message); + pub const ID_HEX_MULTIPART_MESSAGE: Nid = Nid(ffi::NID_id_hex_multipart_message); + pub const ZLIB_COMPRESSION: Nid = Nid(ffi::NID_zlib_compression); + pub const AES_128_ECB: Nid = Nid(ffi::NID_aes_128_ecb); + pub const AES_128_CBC: Nid = Nid(ffi::NID_aes_128_cbc); + pub const AES_128_OFB128: Nid = Nid(ffi::NID_aes_128_ofb128); + pub const AES_128_CFB128: Nid = Nid(ffi::NID_aes_128_cfb128); + pub const ID_AES128_WRAP: Nid = Nid(ffi::NID_id_aes128_wrap); + pub const AES_128_GCM: Nid = Nid(ffi::NID_aes_128_gcm); + pub const AES_128_CCM: Nid = Nid(ffi::NID_aes_128_ccm); + pub const ID_AES128_WRAP_PAD: Nid = Nid(ffi::NID_id_aes128_wrap_pad); + pub const AES_192_ECB: Nid = Nid(ffi::NID_aes_192_ecb); + pub const AES_192_CBC: Nid = Nid(ffi::NID_aes_192_cbc); + pub const AES_192_OFB128: Nid = Nid(ffi::NID_aes_192_ofb128); + pub const AES_192_CFB128: Nid = Nid(ffi::NID_aes_192_cfb128); + pub const ID_AES192_WRAP: Nid = Nid(ffi::NID_id_aes192_wrap); + pub const AES_192_GCM: Nid = Nid(ffi::NID_aes_192_gcm); + pub const AES_192_CCM: Nid = Nid(ffi::NID_aes_192_ccm); + pub const ID_AES192_WRAP_PAD: Nid = Nid(ffi::NID_id_aes192_wrap_pad); + pub const AES_256_ECB: Nid = Nid(ffi::NID_aes_256_ecb); + pub const AES_256_CBC: Nid = Nid(ffi::NID_aes_256_cbc); + pub const AES_256_OFB128: Nid = Nid(ffi::NID_aes_256_ofb128); + pub const AES_256_CFB128: Nid = Nid(ffi::NID_aes_256_cfb128); + pub const ID_AES256_WRAP: Nid = Nid(ffi::NID_id_aes256_wrap); + pub const AES_256_GCM: Nid = Nid(ffi::NID_aes_256_gcm); + pub const AES_256_CCM: Nid = Nid(ffi::NID_aes_256_ccm); + pub const ID_AES256_WRAP_PAD: Nid = Nid(ffi::NID_id_aes256_wrap_pad); + pub const AES_128_CFB1: Nid = Nid(ffi::NID_aes_128_cfb1); + pub const AES_192_CFB1: Nid = Nid(ffi::NID_aes_192_cfb1); + pub const AES_256_CFB1: Nid = Nid(ffi::NID_aes_256_cfb1); + pub const AES_128_CFB8: Nid = Nid(ffi::NID_aes_128_cfb8); + pub const AES_192_CFB8: Nid = Nid(ffi::NID_aes_192_cfb8); + pub const AES_256_CFB8: Nid = Nid(ffi::NID_aes_256_cfb8); + pub const AES_128_CTR: Nid = Nid(ffi::NID_aes_128_ctr); + pub const AES_192_CTR: Nid = Nid(ffi::NID_aes_192_ctr); + pub const AES_256_CTR: Nid = Nid(ffi::NID_aes_256_ctr); + pub const AES_128_XTS: Nid = Nid(ffi::NID_aes_128_xts); + pub const AES_256_XTS: Nid = Nid(ffi::NID_aes_256_xts); + pub const DES_CFB1: Nid = Nid(ffi::NID_des_cfb1); + pub const DES_CFB8: Nid = Nid(ffi::NID_des_cfb8); + pub const DES_EDE3_CFB1: Nid = Nid(ffi::NID_des_ede3_cfb1); + pub const DES_EDE3_CFB8: Nid = Nid(ffi::NID_des_ede3_cfb8); + pub const SHA256: Nid = Nid(ffi::NID_sha256); + pub const SHA384: Nid = Nid(ffi::NID_sha384); + pub const SHA512: Nid = Nid(ffi::NID_sha512); + pub const SHA224: Nid = Nid(ffi::NID_sha224); + pub const DSA_WITH_SHA224: Nid = Nid(ffi::NID_dsa_with_SHA224); + pub const DSA_WITH_SHA256: Nid = Nid(ffi::NID_dsa_with_SHA256); + pub const HOLD_INSTRUCTION_CODE: Nid = Nid(ffi::NID_hold_instruction_code); + pub const HOLD_INSTRUCTION_NONE: Nid = Nid(ffi::NID_hold_instruction_none); + pub const HOLD_INSTRUCTION_CALL_ISSUER: Nid = Nid(ffi::NID_hold_instruction_call_issuer); + pub const HOLD_INSTRUCTION_REJECT: Nid = Nid(ffi::NID_hold_instruction_reject); + pub const DATA: Nid = Nid(ffi::NID_data); + pub const PSS: Nid = Nid(ffi::NID_pss); + pub const UCL: Nid = Nid(ffi::NID_ucl); + pub const PILOT: Nid = Nid(ffi::NID_pilot); + pub const PILOTATTRIBUTETYPE: Nid = Nid(ffi::NID_pilotAttributeType); + pub const PILOTATTRIBUTESYNTAX: Nid = Nid(ffi::NID_pilotAttributeSyntax); + pub const PILOTOBJECTCLASS: Nid = Nid(ffi::NID_pilotObjectClass); + pub const PILOTGROUPS: Nid = Nid(ffi::NID_pilotGroups); + pub const IA5STRINGSYNTAX: Nid = Nid(ffi::NID_iA5StringSyntax); + pub const CASEIGNOREIA5STRINGSYNTAX: Nid = Nid(ffi::NID_caseIgnoreIA5StringSyntax); + pub const PILOTOBJECT: Nid = Nid(ffi::NID_pilotObject); + pub const PILOTPERSON: Nid = Nid(ffi::NID_pilotPerson); + pub const ACCOUNT: Nid = Nid(ffi::NID_account); + pub const DOCUMENT: Nid = Nid(ffi::NID_document); + pub const ROOM: Nid = Nid(ffi::NID_room); + pub const DOCUMENTSERIES: Nid = Nid(ffi::NID_documentSeries); + pub const DOMAIN: Nid = Nid(ffi::NID_Domain); + pub const RFC822LOCALPART: Nid = Nid(ffi::NID_rFC822localPart); + pub const DNSDOMAIN: Nid = Nid(ffi::NID_dNSDomain); + pub const DOMAINRELATEDOBJECT: Nid = Nid(ffi::NID_domainRelatedObject); + pub const FRIENDLYCOUNTRY: Nid = Nid(ffi::NID_friendlyCountry); + pub const SIMPLESECURITYOBJECT: Nid = Nid(ffi::NID_simpleSecurityObject); + pub const PILOTORGANIZATION: Nid = Nid(ffi::NID_pilotOrganization); + pub const PILOTDSA: Nid = Nid(ffi::NID_pilotDSA); + pub const QUALITYLABELLEDDATA: Nid = Nid(ffi::NID_qualityLabelledData); + pub const USERID: Nid = Nid(ffi::NID_userId); + pub const TEXTENCODEDORADDRESS: Nid = Nid(ffi::NID_textEncodedORAddress); + pub const RFC822MAILBOX: Nid = Nid(ffi::NID_rfc822Mailbox); + pub const INFO: Nid = Nid(ffi::NID_info); + pub const FAVOURITEDRINK: Nid = Nid(ffi::NID_favouriteDrink); + pub const ROOMNUMBER: Nid = Nid(ffi::NID_roomNumber); + pub const PHOTO: Nid = Nid(ffi::NID_photo); + pub const USERCLASS: Nid = Nid(ffi::NID_userClass); + pub const HOST: Nid = Nid(ffi::NID_host); + pub const MANAGER: Nid = Nid(ffi::NID_manager); + pub const DOCUMENTIDENTIFIER: Nid = Nid(ffi::NID_documentIdentifier); + pub const DOCUMENTTITLE: Nid = Nid(ffi::NID_documentTitle); + pub const DOCUMENTVERSION: Nid = Nid(ffi::NID_documentVersion); + pub const DOCUMENTAUTHOR: Nid = Nid(ffi::NID_documentAuthor); + pub const DOCUMENTLOCATION: Nid = Nid(ffi::NID_documentLocation); + pub const HOMETELEPHONENUMBER: Nid = Nid(ffi::NID_homeTelephoneNumber); + pub const SECRETARY: Nid = Nid(ffi::NID_secretary); + pub const OTHERMAILBOX: Nid = Nid(ffi::NID_otherMailbox); + pub const LASTMODIFIEDTIME: Nid = Nid(ffi::NID_lastModifiedTime); + pub const LASTMODIFIEDBY: Nid = Nid(ffi::NID_lastModifiedBy); + pub const DOMAINCOMPONENT: Nid = Nid(ffi::NID_domainComponent); + pub const ARECORD: Nid = Nid(ffi::NID_aRecord); + pub const PILOTATTRIBUTETYPE27: Nid = Nid(ffi::NID_pilotAttributeType27); + pub const MXRECORD: Nid = Nid(ffi::NID_mXRecord); + pub const NSRECORD: Nid = Nid(ffi::NID_nSRecord); + pub const SOARECORD: Nid = Nid(ffi::NID_sOARecord); + pub const CNAMERECORD: Nid = Nid(ffi::NID_cNAMERecord); + pub const ASSOCIATEDDOMAIN: Nid = Nid(ffi::NID_associatedDomain); + pub const ASSOCIATEDNAME: Nid = Nid(ffi::NID_associatedName); + pub const HOMEPOSTALADDRESS: Nid = Nid(ffi::NID_homePostalAddress); + pub const PERSONALTITLE: Nid = Nid(ffi::NID_personalTitle); + pub const MOBILETELEPHONENUMBER: Nid = Nid(ffi::NID_mobileTelephoneNumber); + pub const PAGERTELEPHONENUMBER: Nid = Nid(ffi::NID_pagerTelephoneNumber); + pub const FRIENDLYCOUNTRYNAME: Nid = Nid(ffi::NID_friendlyCountryName); + pub const ORGANIZATIONALSTATUS: Nid = Nid(ffi::NID_organizationalStatus); + pub const JANETMAILBOX: Nid = Nid(ffi::NID_janetMailbox); + pub const MAILPREFERENCEOPTION: Nid = Nid(ffi::NID_mailPreferenceOption); + pub const BUILDINGNAME: Nid = Nid(ffi::NID_buildingName); + pub const DSAQUALITY: Nid = Nid(ffi::NID_dSAQuality); + pub const SINGLELEVELQUALITY: Nid = Nid(ffi::NID_singleLevelQuality); + pub const SUBTREEMINIMUMQUALITY: Nid = Nid(ffi::NID_subtreeMinimumQuality); + pub const SUBTREEMAXIMUMQUALITY: Nid = Nid(ffi::NID_subtreeMaximumQuality); + pub const PERSONALSIGNATURE: Nid = Nid(ffi::NID_personalSignature); + pub const DITREDIRECT: Nid = Nid(ffi::NID_dITRedirect); + pub const AUDIO: Nid = Nid(ffi::NID_audio); + pub const DOCUMENTPUBLISHER: Nid = Nid(ffi::NID_documentPublisher); + pub const ID_SET: Nid = Nid(ffi::NID_id_set); + pub const SET_CTYPE: Nid = Nid(ffi::NID_set_ctype); + pub const SET_MSGEXT: Nid = Nid(ffi::NID_set_msgExt); + pub const SET_ATTR: Nid = Nid(ffi::NID_set_attr); + pub const SET_POLICY: Nid = Nid(ffi::NID_set_policy); + pub const SET_CERTEXT: Nid = Nid(ffi::NID_set_certExt); + pub const SET_BRAND: Nid = Nid(ffi::NID_set_brand); + pub const SETCT_PANDATA: Nid = Nid(ffi::NID_setct_PANData); + pub const SETCT_PANTOKEN: Nid = Nid(ffi::NID_setct_PANToken); + pub const SETCT_PANONLY: Nid = Nid(ffi::NID_setct_PANOnly); + pub const SETCT_OIDATA: Nid = Nid(ffi::NID_setct_OIData); + pub const SETCT_PI: Nid = Nid(ffi::NID_setct_PI); + pub const SETCT_PIDATA: Nid = Nid(ffi::NID_setct_PIData); + pub const SETCT_PIDATAUNSIGNED: Nid = Nid(ffi::NID_setct_PIDataUnsigned); + pub const SETCT_HODINPUT: Nid = Nid(ffi::NID_setct_HODInput); + pub const SETCT_AUTHRESBAGGAGE: Nid = Nid(ffi::NID_setct_AuthResBaggage); + pub const SETCT_AUTHREVREQBAGGAGE: Nid = Nid(ffi::NID_setct_AuthRevReqBaggage); + pub const SETCT_AUTHREVRESBAGGAGE: Nid = Nid(ffi::NID_setct_AuthRevResBaggage); + pub const SETCT_CAPTOKENSEQ: Nid = Nid(ffi::NID_setct_CapTokenSeq); + pub const SETCT_PINITRESDATA: Nid = Nid(ffi::NID_setct_PInitResData); + pub const SETCT_PI_TBS: Nid = Nid(ffi::NID_setct_PI_TBS); + pub const SETCT_PRESDATA: Nid = Nid(ffi::NID_setct_PResData); + pub const SETCT_AUTHREQTBS: Nid = Nid(ffi::NID_setct_AuthReqTBS); + pub const SETCT_AUTHRESTBS: Nid = Nid(ffi::NID_setct_AuthResTBS); + pub const SETCT_AUTHRESTBSX: Nid = Nid(ffi::NID_setct_AuthResTBSX); + pub const SETCT_AUTHTOKENTBS: Nid = Nid(ffi::NID_setct_AuthTokenTBS); + pub const SETCT_CAPTOKENDATA: Nid = Nid(ffi::NID_setct_CapTokenData); + pub const SETCT_CAPTOKENTBS: Nid = Nid(ffi::NID_setct_CapTokenTBS); + pub const SETCT_ACQCARDCODEMSG: Nid = Nid(ffi::NID_setct_AcqCardCodeMsg); + pub const SETCT_AUTHREVREQTBS: Nid = Nid(ffi::NID_setct_AuthRevReqTBS); + pub const SETCT_AUTHREVRESDATA: Nid = Nid(ffi::NID_setct_AuthRevResData); + pub const SETCT_AUTHREVRESTBS: Nid = Nid(ffi::NID_setct_AuthRevResTBS); + pub const SETCT_CAPREQTBS: Nid = Nid(ffi::NID_setct_CapReqTBS); + pub const SETCT_CAPREQTBSX: Nid = Nid(ffi::NID_setct_CapReqTBSX); + pub const SETCT_CAPRESDATA: Nid = Nid(ffi::NID_setct_CapResData); + pub const SETCT_CAPREVREQTBS: Nid = Nid(ffi::NID_setct_CapRevReqTBS); + pub const SETCT_CAPREVREQTBSX: Nid = Nid(ffi::NID_setct_CapRevReqTBSX); + pub const SETCT_CAPREVRESDATA: Nid = Nid(ffi::NID_setct_CapRevResData); + pub const SETCT_CREDREQTBS: Nid = Nid(ffi::NID_setct_CredReqTBS); + pub const SETCT_CREDREQTBSX: Nid = Nid(ffi::NID_setct_CredReqTBSX); + pub const SETCT_CREDRESDATA: Nid = Nid(ffi::NID_setct_CredResData); + pub const SETCT_CREDREVREQTBS: Nid = Nid(ffi::NID_setct_CredRevReqTBS); + pub const SETCT_CREDREVREQTBSX: Nid = Nid(ffi::NID_setct_CredRevReqTBSX); + pub const SETCT_CREDREVRESDATA: Nid = Nid(ffi::NID_setct_CredRevResData); + pub const SETCT_PCERTREQDATA: Nid = Nid(ffi::NID_setct_PCertReqData); + pub const SETCT_PCERTRESTBS: Nid = Nid(ffi::NID_setct_PCertResTBS); + pub const SETCT_BATCHADMINREQDATA: Nid = Nid(ffi::NID_setct_BatchAdminReqData); + pub const SETCT_BATCHADMINRESDATA: Nid = Nid(ffi::NID_setct_BatchAdminResData); + pub const SETCT_CARDCINITRESTBS: Nid = Nid(ffi::NID_setct_CardCInitResTBS); + pub const SETCT_MEAQCINITRESTBS: Nid = Nid(ffi::NID_setct_MeAqCInitResTBS); + pub const SETCT_REGFORMRESTBS: Nid = Nid(ffi::NID_setct_RegFormResTBS); + pub const SETCT_CERTREQDATA: Nid = Nid(ffi::NID_setct_CertReqData); + pub const SETCT_CERTREQTBS: Nid = Nid(ffi::NID_setct_CertReqTBS); + pub const SETCT_CERTRESDATA: Nid = Nid(ffi::NID_setct_CertResData); + pub const SETCT_CERTINQREQTBS: Nid = Nid(ffi::NID_setct_CertInqReqTBS); + pub const SETCT_ERRORTBS: Nid = Nid(ffi::NID_setct_ErrorTBS); + pub const SETCT_PIDUALSIGNEDTBE: Nid = Nid(ffi::NID_setct_PIDualSignedTBE); + pub const SETCT_PIUNSIGNEDTBE: Nid = Nid(ffi::NID_setct_PIUnsignedTBE); + pub const SETCT_AUTHREQTBE: Nid = Nid(ffi::NID_setct_AuthReqTBE); + pub const SETCT_AUTHRESTBE: Nid = Nid(ffi::NID_setct_AuthResTBE); + pub const SETCT_AUTHRESTBEX: Nid = Nid(ffi::NID_setct_AuthResTBEX); + pub const SETCT_AUTHTOKENTBE: Nid = Nid(ffi::NID_setct_AuthTokenTBE); + pub const SETCT_CAPTOKENTBE: Nid = Nid(ffi::NID_setct_CapTokenTBE); + pub const SETCT_CAPTOKENTBEX: Nid = Nid(ffi::NID_setct_CapTokenTBEX); + pub const SETCT_ACQCARDCODEMSGTBE: Nid = Nid(ffi::NID_setct_AcqCardCodeMsgTBE); + pub const SETCT_AUTHREVREQTBE: Nid = Nid(ffi::NID_setct_AuthRevReqTBE); + pub const SETCT_AUTHREVRESTBE: Nid = Nid(ffi::NID_setct_AuthRevResTBE); + pub const SETCT_AUTHREVRESTBEB: Nid = Nid(ffi::NID_setct_AuthRevResTBEB); + pub const SETCT_CAPREQTBE: Nid = Nid(ffi::NID_setct_CapReqTBE); + pub const SETCT_CAPREQTBEX: Nid = Nid(ffi::NID_setct_CapReqTBEX); + pub const SETCT_CAPRESTBE: Nid = Nid(ffi::NID_setct_CapResTBE); + pub const SETCT_CAPREVREQTBE: Nid = Nid(ffi::NID_setct_CapRevReqTBE); + pub const SETCT_CAPREVREQTBEX: Nid = Nid(ffi::NID_setct_CapRevReqTBEX); + pub const SETCT_CAPREVRESTBE: Nid = Nid(ffi::NID_setct_CapRevResTBE); + pub const SETCT_CREDREQTBE: Nid = Nid(ffi::NID_setct_CredReqTBE); + pub const SETCT_CREDREQTBEX: Nid = Nid(ffi::NID_setct_CredReqTBEX); + pub const SETCT_CREDRESTBE: Nid = Nid(ffi::NID_setct_CredResTBE); + pub const SETCT_CREDREVREQTBE: Nid = Nid(ffi::NID_setct_CredRevReqTBE); + pub const SETCT_CREDREVREQTBEX: Nid = Nid(ffi::NID_setct_CredRevReqTBEX); + pub const SETCT_CREDREVRESTBE: Nid = Nid(ffi::NID_setct_CredRevResTBE); + pub const SETCT_BATCHADMINREQTBE: Nid = Nid(ffi::NID_setct_BatchAdminReqTBE); + pub const SETCT_BATCHADMINRESTBE: Nid = Nid(ffi::NID_setct_BatchAdminResTBE); + pub const SETCT_REGFORMREQTBE: Nid = Nid(ffi::NID_setct_RegFormReqTBE); + pub const SETCT_CERTREQTBE: Nid = Nid(ffi::NID_setct_CertReqTBE); + pub const SETCT_CERTREQTBEX: Nid = Nid(ffi::NID_setct_CertReqTBEX); + pub const SETCT_CERTRESTBE: Nid = Nid(ffi::NID_setct_CertResTBE); + pub const SETCT_CRLNOTIFICATIONTBS: Nid = Nid(ffi::NID_setct_CRLNotificationTBS); + pub const SETCT_CRLNOTIFICATIONRESTBS: Nid = Nid(ffi::NID_setct_CRLNotificationResTBS); + pub const SETCT_BCIDISTRIBUTIONTBS: Nid = Nid(ffi::NID_setct_BCIDistributionTBS); + pub const SETEXT_GENCRYPT: Nid = Nid(ffi::NID_setext_genCrypt); + pub const SETEXT_MIAUTH: Nid = Nid(ffi::NID_setext_miAuth); + pub const SETEXT_PINSECURE: Nid = Nid(ffi::NID_setext_pinSecure); + pub const SETEXT_PINANY: Nid = Nid(ffi::NID_setext_pinAny); + pub const SETEXT_TRACK2: Nid = Nid(ffi::NID_setext_track2); + pub const SETEXT_CV: Nid = Nid(ffi::NID_setext_cv); + pub const SET_POLICY_ROOT: Nid = Nid(ffi::NID_set_policy_root); + pub const SETCEXT_HASHEDROOT: Nid = Nid(ffi::NID_setCext_hashedRoot); + pub const SETCEXT_CERTTYPE: Nid = Nid(ffi::NID_setCext_certType); + pub const SETCEXT_MERCHDATA: Nid = Nid(ffi::NID_setCext_merchData); + pub const SETCEXT_CCERTREQUIRED: Nid = Nid(ffi::NID_setCext_cCertRequired); + pub const SETCEXT_TUNNELING: Nid = Nid(ffi::NID_setCext_tunneling); + pub const SETCEXT_SETEXT: Nid = Nid(ffi::NID_setCext_setExt); + pub const SETCEXT_SETQUALF: Nid = Nid(ffi::NID_setCext_setQualf); + pub const SETCEXT_PGWYCAPABILITIES: Nid = Nid(ffi::NID_setCext_PGWYcapabilities); + pub const SETCEXT_TOKENIDENTIFIER: Nid = Nid(ffi::NID_setCext_TokenIdentifier); + pub const SETCEXT_TRACK2DATA: Nid = Nid(ffi::NID_setCext_Track2Data); + pub const SETCEXT_TOKENTYPE: Nid = Nid(ffi::NID_setCext_TokenType); + pub const SETCEXT_ISSUERCAPABILITIES: Nid = Nid(ffi::NID_setCext_IssuerCapabilities); + pub const SETATTR_CERT: Nid = Nid(ffi::NID_setAttr_Cert); + pub const SETATTR_PGWYCAP: Nid = Nid(ffi::NID_setAttr_PGWYcap); + pub const SETATTR_TOKENTYPE: Nid = Nid(ffi::NID_setAttr_TokenType); + pub const SETATTR_ISSCAP: Nid = Nid(ffi::NID_setAttr_IssCap); + pub const SET_ROOTKEYTHUMB: Nid = Nid(ffi::NID_set_rootKeyThumb); + pub const SET_ADDPOLICY: Nid = Nid(ffi::NID_set_addPolicy); + pub const SETATTR_TOKEN_EMV: Nid = Nid(ffi::NID_setAttr_Token_EMV); + pub const SETATTR_TOKEN_B0PRIME: Nid = Nid(ffi::NID_setAttr_Token_B0Prime); + pub const SETATTR_ISSCAP_CVM: Nid = Nid(ffi::NID_setAttr_IssCap_CVM); + pub const SETATTR_ISSCAP_T2: Nid = Nid(ffi::NID_setAttr_IssCap_T2); + pub const SETATTR_ISSCAP_SIG: Nid = Nid(ffi::NID_setAttr_IssCap_Sig); + pub const SETATTR_GENCRYPTGRM: Nid = Nid(ffi::NID_setAttr_GenCryptgrm); + pub const SETATTR_T2ENC: Nid = Nid(ffi::NID_setAttr_T2Enc); + pub const SETATTR_T2CLEARTXT: Nid = Nid(ffi::NID_setAttr_T2cleartxt); + pub const SETATTR_TOKICCSIG: Nid = Nid(ffi::NID_setAttr_TokICCsig); + pub const SETATTR_SECDEVSIG: Nid = Nid(ffi::NID_setAttr_SecDevSig); + pub const SET_BRAND_IATA_ATA: Nid = Nid(ffi::NID_set_brand_IATA_ATA); + pub const SET_BRAND_DINERS: Nid = Nid(ffi::NID_set_brand_Diners); + pub const SET_BRAND_AMERICANEXPRESS: Nid = Nid(ffi::NID_set_brand_AmericanExpress); + pub const SET_BRAND_JCB: Nid = Nid(ffi::NID_set_brand_JCB); + pub const SET_BRAND_VISA: Nid = Nid(ffi::NID_set_brand_Visa); + pub const SET_BRAND_MASTERCARD: Nid = Nid(ffi::NID_set_brand_MasterCard); + pub const SET_BRAND_NOVUS: Nid = Nid(ffi::NID_set_brand_Novus); + pub const DES_CDMF: Nid = Nid(ffi::NID_des_cdmf); + pub const RSAOAEPENCRYPTIONSET: Nid = Nid(ffi::NID_rsaOAEPEncryptionSET); + pub const IPSEC3: Nid = Nid(ffi::NID_ipsec3); + pub const IPSEC4: Nid = Nid(ffi::NID_ipsec4); + pub const WHIRLPOOL: Nid = Nid(ffi::NID_whirlpool); + pub const CRYPTOPRO: Nid = Nid(ffi::NID_cryptopro); + pub const CRYPTOCOM: Nid = Nid(ffi::NID_cryptocom); + pub const ID_GOSTR3411_94_WITH_GOSTR3410_2001: Nid = + Nid(ffi::NID_id_GostR3411_94_with_GostR3410_2001); + pub const ID_GOSTR3411_94_WITH_GOSTR3410_94: Nid = + Nid(ffi::NID_id_GostR3411_94_with_GostR3410_94); + pub const ID_GOSTR3411_94: Nid = Nid(ffi::NID_id_GostR3411_94); + pub const ID_HMACGOSTR3411_94: Nid = Nid(ffi::NID_id_HMACGostR3411_94); + pub const ID_GOSTR3410_2001: Nid = Nid(ffi::NID_id_GostR3410_2001); + pub const ID_GOSTR3410_94: Nid = Nid(ffi::NID_id_GostR3410_94); + pub const ID_GOST28147_89: Nid = Nid(ffi::NID_id_Gost28147_89); + pub const GOST89_CNT: Nid = Nid(ffi::NID_gost89_cnt); + pub const ID_GOST28147_89_MAC: Nid = Nid(ffi::NID_id_Gost28147_89_MAC); + pub const ID_GOSTR3411_94_PRF: Nid = Nid(ffi::NID_id_GostR3411_94_prf); + pub const ID_GOSTR3410_2001DH: Nid = Nid(ffi::NID_id_GostR3410_2001DH); + pub const ID_GOSTR3410_94DH: Nid = Nid(ffi::NID_id_GostR3410_94DH); + pub const ID_GOST28147_89_CRYPTOPRO_KEYMESHING: Nid = + Nid(ffi::NID_id_Gost28147_89_CryptoPro_KeyMeshing); + pub const ID_GOST28147_89_NONE_KEYMESHING: Nid = Nid(ffi::NID_id_Gost28147_89_None_KeyMeshing); + pub const ID_GOSTR3411_94_TESTPARAMSET: Nid = Nid(ffi::NID_id_GostR3411_94_TestParamSet); + pub const ID_GOSTR3411_94_CRYPTOPROPARAMSET: Nid = + Nid(ffi::NID_id_GostR3411_94_CryptoProParamSet); + pub const ID_GOST28147_89_TESTPARAMSET: Nid = Nid(ffi::NID_id_Gost28147_89_TestParamSet); + pub const ID_GOST28147_89_CRYPTOPRO_A_PARAMSET: Nid = + Nid(ffi::NID_id_Gost28147_89_CryptoPro_A_ParamSet); + pub const ID_GOST28147_89_CRYPTOPRO_B_PARAMSET: Nid = + Nid(ffi::NID_id_Gost28147_89_CryptoPro_B_ParamSet); + pub const ID_GOST28147_89_CRYPTOPRO_C_PARAMSET: Nid = + Nid(ffi::NID_id_Gost28147_89_CryptoPro_C_ParamSet); + pub const ID_GOST28147_89_CRYPTOPRO_D_PARAMSET: Nid = + Nid(ffi::NID_id_Gost28147_89_CryptoPro_D_ParamSet); + pub const ID_GOST28147_89_CRYPTOPRO_OSCAR_1_1_PARAMSET: Nid = + Nid(ffi::NID_id_Gost28147_89_CryptoPro_Oscar_1_1_ParamSet); + pub const ID_GOST28147_89_CRYPTOPRO_OSCAR_1_0_PARAMSET: Nid = + Nid(ffi::NID_id_Gost28147_89_CryptoPro_Oscar_1_0_ParamSet); + pub const ID_GOST28147_89_CRYPTOPRO_RIC_1_PARAMSET: Nid = + Nid(ffi::NID_id_Gost28147_89_CryptoPro_RIC_1_ParamSet); + pub const ID_GOSTR3410_94_TESTPARAMSET: Nid = Nid(ffi::NID_id_GostR3410_94_TestParamSet); + pub const ID_GOSTR3410_94_CRYPTOPRO_A_PARAMSET: Nid = + Nid(ffi::NID_id_GostR3410_94_CryptoPro_A_ParamSet); + pub const ID_GOSTR3410_94_CRYPTOPRO_B_PARAMSET: Nid = + Nid(ffi::NID_id_GostR3410_94_CryptoPro_B_ParamSet); + pub const ID_GOSTR3410_94_CRYPTOPRO_C_PARAMSET: Nid = + Nid(ffi::NID_id_GostR3410_94_CryptoPro_C_ParamSet); + pub const ID_GOSTR3410_94_CRYPTOPRO_D_PARAMSET: Nid = + Nid(ffi::NID_id_GostR3410_94_CryptoPro_D_ParamSet); + pub const ID_GOSTR3410_94_CRYPTOPRO_XCHA_PARAMSET: Nid = + Nid(ffi::NID_id_GostR3410_94_CryptoPro_XchA_ParamSet); + pub const ID_GOSTR3410_94_CRYPTOPRO_XCHB_PARAMSET: Nid = + Nid(ffi::NID_id_GostR3410_94_CryptoPro_XchB_ParamSet); + pub const ID_GOSTR3410_94_CRYPTOPRO_XCHC_PARAMSET: Nid = + Nid(ffi::NID_id_GostR3410_94_CryptoPro_XchC_ParamSet); + pub const ID_GOSTR3410_2001_TESTPARAMSET: Nid = Nid(ffi::NID_id_GostR3410_2001_TestParamSet); + pub const ID_GOSTR3410_2001_CRYPTOPRO_A_PARAMSET: Nid = + Nid(ffi::NID_id_GostR3410_2001_CryptoPro_A_ParamSet); + pub const ID_GOSTR3410_2001_CRYPTOPRO_B_PARAMSET: Nid = + Nid(ffi::NID_id_GostR3410_2001_CryptoPro_B_ParamSet); + pub const ID_GOSTR3410_2001_CRYPTOPRO_C_PARAMSET: Nid = + Nid(ffi::NID_id_GostR3410_2001_CryptoPro_C_ParamSet); + pub const ID_GOSTR3410_2001_CRYPTOPRO_XCHA_PARAMSET: Nid = + Nid(ffi::NID_id_GostR3410_2001_CryptoPro_XchA_ParamSet); + pub const ID_GOSTR3410_2001_CRYPTOPRO_XCHB_PARAMSET: Nid = + Nid(ffi::NID_id_GostR3410_2001_CryptoPro_XchB_ParamSet); + pub const ID_GOSTR3410_94_A: Nid = Nid(ffi::NID_id_GostR3410_94_a); + pub const ID_GOSTR3410_94_ABIS: Nid = Nid(ffi::NID_id_GostR3410_94_aBis); + pub const ID_GOSTR3410_94_B: Nid = Nid(ffi::NID_id_GostR3410_94_b); + pub const ID_GOSTR3410_94_BBIS: Nid = Nid(ffi::NID_id_GostR3410_94_bBis); + pub const ID_GOST28147_89_CC: Nid = Nid(ffi::NID_id_Gost28147_89_cc); + pub const ID_GOSTR3410_94_CC: Nid = Nid(ffi::NID_id_GostR3410_94_cc); + pub const ID_GOSTR3410_2001_CC: Nid = Nid(ffi::NID_id_GostR3410_2001_cc); + pub const ID_GOSTR3411_94_WITH_GOSTR3410_94_CC: Nid = + Nid(ffi::NID_id_GostR3411_94_with_GostR3410_94_cc); + pub const ID_GOSTR3411_94_WITH_GOSTR3410_2001_CC: Nid = + Nid(ffi::NID_id_GostR3411_94_with_GostR3410_2001_cc); + pub const ID_GOSTR3410_2001_PARAMSET_CC: Nid = Nid(ffi::NID_id_GostR3410_2001_ParamSet_cc); + pub const CAMELLIA_128_CBC: Nid = Nid(ffi::NID_camellia_128_cbc); + pub const CAMELLIA_192_CBC: Nid = Nid(ffi::NID_camellia_192_cbc); + pub const CAMELLIA_256_CBC: Nid = Nid(ffi::NID_camellia_256_cbc); + pub const ID_CAMELLIA128_WRAP: Nid = Nid(ffi::NID_id_camellia128_wrap); + pub const ID_CAMELLIA192_WRAP: Nid = Nid(ffi::NID_id_camellia192_wrap); + pub const ID_CAMELLIA256_WRAP: Nid = Nid(ffi::NID_id_camellia256_wrap); + pub const CAMELLIA_128_ECB: Nid = Nid(ffi::NID_camellia_128_ecb); + pub const CAMELLIA_128_OFB128: Nid = Nid(ffi::NID_camellia_128_ofb128); + pub const CAMELLIA_128_CFB128: Nid = Nid(ffi::NID_camellia_128_cfb128); + pub const CAMELLIA_192_ECB: Nid = Nid(ffi::NID_camellia_192_ecb); + pub const CAMELLIA_192_OFB128: Nid = Nid(ffi::NID_camellia_192_ofb128); + pub const CAMELLIA_192_CFB128: Nid = Nid(ffi::NID_camellia_192_cfb128); + pub const CAMELLIA_256_ECB: Nid = Nid(ffi::NID_camellia_256_ecb); + pub const CAMELLIA_256_OFB128: Nid = Nid(ffi::NID_camellia_256_ofb128); + pub const CAMELLIA_256_CFB128: Nid = Nid(ffi::NID_camellia_256_cfb128); + pub const CAMELLIA_128_CFB1: Nid = Nid(ffi::NID_camellia_128_cfb1); + pub const CAMELLIA_192_CFB1: Nid = Nid(ffi::NID_camellia_192_cfb1); + pub const CAMELLIA_256_CFB1: Nid = Nid(ffi::NID_camellia_256_cfb1); + pub const CAMELLIA_128_CFB8: Nid = Nid(ffi::NID_camellia_128_cfb8); + pub const CAMELLIA_192_CFB8: Nid = Nid(ffi::NID_camellia_192_cfb8); + pub const CAMELLIA_256_CFB8: Nid = Nid(ffi::NID_camellia_256_cfb8); + pub const KISA: Nid = Nid(ffi::NID_kisa); + pub const SEED_ECB: Nid = Nid(ffi::NID_seed_ecb); + pub const SEED_CBC: Nid = Nid(ffi::NID_seed_cbc); + pub const SEED_CFB128: Nid = Nid(ffi::NID_seed_cfb128); + pub const SEED_OFB128: Nid = Nid(ffi::NID_seed_ofb128); + pub const HMAC: Nid = Nid(ffi::NID_hmac); + pub const CMAC: Nid = Nid(ffi::NID_cmac); + pub const RC4_HMAC_MD5: Nid = Nid(ffi::NID_rc4_hmac_md5); + pub const AES_128_CBC_HMAC_SHA1: Nid = Nid(ffi::NID_aes_128_cbc_hmac_sha1); + pub const AES_192_CBC_HMAC_SHA1: Nid = Nid(ffi::NID_aes_192_cbc_hmac_sha1); + pub const AES_256_CBC_HMAC_SHA1: Nid = Nid(ffi::NID_aes_256_cbc_hmac_sha1); + #[cfg(ossl111)] + pub const SM2: Nid = Nid(ffi::NID_sm2); + #[cfg(any(ossl111, libressl291))] + pub const SM3: Nid = Nid(ffi::NID_sm3); + #[cfg(ossl111)] + pub const SHA3_224: Nid = Nid(ffi::NID_sha3_224); + #[cfg(ossl111)] + pub const SHA3_256: Nid = Nid(ffi::NID_sha3_256); + #[cfg(ossl111)] + pub const SHA3_384: Nid = Nid(ffi::NID_sha3_384); + #[cfg(ossl111)] + pub const SHA3_512: Nid = Nid(ffi::NID_sha3_512); + #[cfg(ossl111)] + pub const SHAKE128: Nid = Nid(ffi::NID_shake128); + #[cfg(ossl111)] + pub const SHAKE256: Nid = Nid(ffi::NID_shake256); +} + +#[cfg(test)] +mod test { + use super::Nid; + + #[test] + fn signature_digest() { + let algs = Nid::SHA256WITHRSAENCRYPTION.signature_algorithms().unwrap(); + assert_eq!(algs.digest, Nid::SHA256); + assert_eq!(algs.pkey, Nid::RSAENCRYPTION); + } + + #[test] + fn test_long_name_conversion() { + let common_name = Nid::COMMONNAME; + let organizational_unit_name = Nid::ORGANIZATIONALUNITNAME; + let aes256_cbc_hmac_sha1 = Nid::AES_256_CBC_HMAC_SHA1; + let id_cmc_lrapopwitness = Nid::ID_CMC_LRAPOPWITNESS; + let ms_ctl_sign = Nid::MS_CTL_SIGN; + let undefined_nid = Nid::from_raw(118); + + assert_eq!(common_name.long_name().unwrap(), "commonName"); + assert_eq!( + organizational_unit_name.long_name().unwrap(), + "organizationalUnitName" + ); + assert_eq!( + aes256_cbc_hmac_sha1.long_name().unwrap(), + "aes-256-cbc-hmac-sha1" + ); + assert_eq!( + id_cmc_lrapopwitness.long_name().unwrap(), + "id-cmc-lraPOPWitness" + ); + assert_eq!( + ms_ctl_sign.long_name().unwrap(), + "Microsoft Trust List Signing" + ); + assert!( + undefined_nid.long_name().is_err(), + "undefined_nid should not return a valid value" + ); + } + + #[test] + fn test_short_name_conversion() { + let common_name = Nid::COMMONNAME; + let organizational_unit_name = Nid::ORGANIZATIONALUNITNAME; + let aes256_cbc_hmac_sha1 = Nid::AES_256_CBC_HMAC_SHA1; + let id_cmc_lrapopwitness = Nid::ID_CMC_LRAPOPWITNESS; + let ms_ctl_sign = Nid::MS_CTL_SIGN; + let undefined_nid = Nid::from_raw(118); + + assert_eq!(common_name.short_name().unwrap(), "CN"); + assert_eq!(organizational_unit_name.short_name().unwrap(), "OU"); + assert_eq!( + aes256_cbc_hmac_sha1.short_name().unwrap(), + "AES-256-CBC-HMAC-SHA1" + ); + assert_eq!( + id_cmc_lrapopwitness.short_name().unwrap(), + "id-cmc-lraPOPWitness" + ); + assert_eq!(ms_ctl_sign.short_name().unwrap(), "msCTLSign"); + assert!( + undefined_nid.short_name().is_err(), + "undefined_nid should not return a valid value" + ); + } + + #[test] + fn test_create() { + let nid = Nid::create("1.2.3.4", "foo", "foobar").unwrap(); + assert_eq!(nid.short_name().unwrap(), "foo"); + assert_eq!(nid.long_name().unwrap(), "foobar"); + + // Due to a bug in OpenSSL 3.1.0, this test crashes on Windows + if !cfg!(ossl310) { + let invalid_oid = Nid::create("invalid_oid", "invalid", "invalid"); + assert!( + invalid_oid.is_err(), + "invalid_oid should not return a valid value" + ); + } + } +} diff --git a/vendor/openssl/src/ocsp.rs b/vendor/openssl/src/ocsp.rs new file mode 100644 index 0000000..93a5d36 --- /dev/null +++ b/vendor/openssl/src/ocsp.rs @@ -0,0 +1,352 @@ +use bitflags::bitflags; +use foreign_types::ForeignTypeRef; +use libc::{c_int, c_long, c_ulong}; +use std::mem; +use std::ptr; + +use crate::asn1::Asn1GeneralizedTimeRef; +use crate::error::ErrorStack; +use crate::hash::MessageDigest; +use crate::stack::StackRef; +use crate::util::ForeignTypeRefExt; +use crate::x509::store::X509StoreRef; +use crate::x509::{X509Ref, X509}; +use crate::{cvt, cvt_p}; +use openssl_macros::corresponds; + +bitflags! { + #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + #[repr(transparent)] + pub struct OcspFlag: c_ulong { + const NO_CERTS = ffi::OCSP_NOCERTS; + const NO_INTERN = ffi::OCSP_NOINTERN; + const NO_CHAIN = ffi::OCSP_NOCHAIN; + const NO_VERIFY = ffi::OCSP_NOVERIFY; + const NO_EXPLICIT = ffi::OCSP_NOEXPLICIT; + const NO_CA_SIGN = ffi::OCSP_NOCASIGN; + const NO_DELEGATED = ffi::OCSP_NODELEGATED; + const NO_CHECKS = ffi::OCSP_NOCHECKS; + const TRUST_OTHER = ffi::OCSP_TRUSTOTHER; + const RESPID_KEY = ffi::OCSP_RESPID_KEY; + const NO_TIME = ffi::OCSP_NOTIME; + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct OcspResponseStatus(c_int); + +impl OcspResponseStatus { + pub const SUCCESSFUL: OcspResponseStatus = + OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_SUCCESSFUL); + pub const MALFORMED_REQUEST: OcspResponseStatus = + OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_MALFORMEDREQUEST); + pub const INTERNAL_ERROR: OcspResponseStatus = + OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_INTERNALERROR); + pub const TRY_LATER: OcspResponseStatus = + OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_TRYLATER); + pub const SIG_REQUIRED: OcspResponseStatus = + OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_SIGREQUIRED); + pub const UNAUTHORIZED: OcspResponseStatus = + OcspResponseStatus(ffi::OCSP_RESPONSE_STATUS_UNAUTHORIZED); + + pub fn from_raw(raw: c_int) -> OcspResponseStatus { + OcspResponseStatus(raw) + } + + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn as_raw(&self) -> c_int { + self.0 + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct OcspCertStatus(c_int); + +impl OcspCertStatus { + pub const GOOD: OcspCertStatus = OcspCertStatus(ffi::V_OCSP_CERTSTATUS_GOOD); + pub const REVOKED: OcspCertStatus = OcspCertStatus(ffi::V_OCSP_CERTSTATUS_REVOKED); + pub const UNKNOWN: OcspCertStatus = OcspCertStatus(ffi::V_OCSP_CERTSTATUS_UNKNOWN); + + pub fn from_raw(raw: c_int) -> OcspCertStatus { + OcspCertStatus(raw) + } + + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn as_raw(&self) -> c_int { + self.0 + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct OcspRevokedStatus(c_int); + +impl OcspRevokedStatus { + pub const NO_STATUS: OcspRevokedStatus = OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_NOSTATUS); + pub const UNSPECIFIED: OcspRevokedStatus = + OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_UNSPECIFIED); + pub const KEY_COMPROMISE: OcspRevokedStatus = + OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_KEYCOMPROMISE); + pub const CA_COMPROMISE: OcspRevokedStatus = + OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_CACOMPROMISE); + pub const AFFILIATION_CHANGED: OcspRevokedStatus = + OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_AFFILIATIONCHANGED); + pub const STATUS_SUPERSEDED: OcspRevokedStatus = + OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_SUPERSEDED); + pub const STATUS_CESSATION_OF_OPERATION: OcspRevokedStatus = + OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_CESSATIONOFOPERATION); + pub const STATUS_CERTIFICATE_HOLD: OcspRevokedStatus = + OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_CERTIFICATEHOLD); + pub const REMOVE_FROM_CRL: OcspRevokedStatus = + OcspRevokedStatus(ffi::OCSP_REVOKED_STATUS_REMOVEFROMCRL); + + pub fn from_raw(raw: c_int) -> OcspRevokedStatus { + OcspRevokedStatus(raw) + } + + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn as_raw(&self) -> c_int { + self.0 + } +} + +pub struct OcspStatus<'a> { + /// The overall status of the response. + pub status: OcspCertStatus, + /// If `status` is `CERT_STATUS_REVOKED`, the reason for the revocation. + pub reason: OcspRevokedStatus, + /// If `status` is `CERT_STATUS_REVOKED`, the time at which the certificate was revoked. + pub revocation_time: Option<&'a Asn1GeneralizedTimeRef>, + /// The time that this revocation check was performed. + pub this_update: &'a Asn1GeneralizedTimeRef, + /// The time at which this revocation check expires. + pub next_update: &'a Asn1GeneralizedTimeRef, +} + +impl<'a> OcspStatus<'a> { + /// Checks validity of the `this_update` and `next_update` fields. + /// + /// The `nsec` parameter specifies an amount of slack time that will be used when comparing + /// those times with the current time to account for delays and clock skew. + /// + /// The `maxsec` parameter limits the maximum age of the `this_update` parameter to prohibit + /// very old responses. + #[corresponds(OCSP_check_validity)] + pub fn check_validity(&self, nsec: u32, maxsec: Option<u32>) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::OCSP_check_validity( + self.this_update.as_ptr(), + self.next_update.as_ptr(), + nsec as c_long, + maxsec.map(|n| n as c_long).unwrap_or(-1), + )) + .map(|_| ()) + } + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::OCSP_BASICRESP; + fn drop = ffi::OCSP_BASICRESP_free; + + pub struct OcspBasicResponse; + pub struct OcspBasicResponseRef; +} + +impl OcspBasicResponseRef { + /// Verifies the validity of the response. + /// + /// The `certs` parameter contains a set of certificates that will be searched when locating the + /// OCSP response signing certificate. Some responders do not include this in the response. + #[corresponds(OCSP_basic_verify)] + pub fn verify( + &self, + certs: &StackRef<X509>, + store: &X509StoreRef, + flags: OcspFlag, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::OCSP_basic_verify( + self.as_ptr(), + certs.as_ptr(), + store.as_ptr(), + flags.bits(), + )) + .map(|_| ()) + } + } + + /// Looks up the status for the specified certificate ID. + #[corresponds(OCSP_resp_find_status)] + pub fn find_status<'a>(&'a self, id: &OcspCertIdRef) -> Option<OcspStatus<'a>> { + unsafe { + let mut status = ffi::V_OCSP_CERTSTATUS_UNKNOWN; + let mut reason = ffi::OCSP_REVOKED_STATUS_NOSTATUS; + let mut revocation_time = ptr::null_mut(); + let mut this_update = ptr::null_mut(); + let mut next_update = ptr::null_mut(); + + let r = ffi::OCSP_resp_find_status( + self.as_ptr(), + id.as_ptr(), + &mut status, + &mut reason, + &mut revocation_time, + &mut this_update, + &mut next_update, + ); + if r == 1 { + let revocation_time = Asn1GeneralizedTimeRef::from_const_ptr_opt(revocation_time); + + Some(OcspStatus { + status: OcspCertStatus(status), + reason: OcspRevokedStatus(status), + revocation_time, + this_update: Asn1GeneralizedTimeRef::from_ptr(this_update), + next_update: Asn1GeneralizedTimeRef::from_ptr(next_update), + }) + } else { + None + } + } + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::OCSP_CERTID; + fn drop = ffi::OCSP_CERTID_free; + + pub struct OcspCertId; + pub struct OcspCertIdRef; +} + +impl OcspCertId { + /// Constructs a certificate ID for certificate `subject`. + #[corresponds(OCSP_cert_to_id)] + pub fn from_cert( + digest: MessageDigest, + subject: &X509Ref, + issuer: &X509Ref, + ) -> Result<OcspCertId, ErrorStack> { + unsafe { + cvt_p(ffi::OCSP_cert_to_id( + digest.as_ptr(), + subject.as_ptr(), + issuer.as_ptr(), + )) + .map(OcspCertId) + } + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::OCSP_RESPONSE; + fn drop = ffi::OCSP_RESPONSE_free; + + pub struct OcspResponse; + pub struct OcspResponseRef; +} + +impl OcspResponse { + /// Creates an OCSP response from the status and optional body. + /// + /// A body should only be provided if `status` is `RESPONSE_STATUS_SUCCESSFUL`. + #[corresponds(OCSP_response_create)] + pub fn create( + status: OcspResponseStatus, + body: Option<&OcspBasicResponseRef>, + ) -> Result<OcspResponse, ErrorStack> { + unsafe { + ffi::init(); + + cvt_p(ffi::OCSP_response_create( + status.as_raw(), + body.map(|r| r.as_ptr()).unwrap_or(ptr::null_mut()), + )) + .map(OcspResponse) + } + } + + from_der! { + /// Deserializes a DER-encoded OCSP response. + #[corresponds(d2i_OCSP_RESPONSE)] + from_der, + OcspResponse, + ffi::d2i_OCSP_RESPONSE + } +} + +impl OcspResponseRef { + to_der! { + /// Serializes the response to its standard DER encoding. + #[corresponds(i2d_OCSP_RESPONSE)] + to_der, + ffi::i2d_OCSP_RESPONSE + } + + /// Returns the status of the response. + #[corresponds(OCSP_response_status)] + pub fn status(&self) -> OcspResponseStatus { + unsafe { OcspResponseStatus(ffi::OCSP_response_status(self.as_ptr())) } + } + + /// Returns the basic response. + /// + /// This will only succeed if `status()` returns `RESPONSE_STATUS_SUCCESSFUL`. + #[corresponds(OCSP_response_get1_basic)] + pub fn basic(&self) -> Result<OcspBasicResponse, ErrorStack> { + unsafe { cvt_p(ffi::OCSP_response_get1_basic(self.as_ptr())).map(OcspBasicResponse) } + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::OCSP_REQUEST; + fn drop = ffi::OCSP_REQUEST_free; + + pub struct OcspRequest; + pub struct OcspRequestRef; +} + +impl OcspRequest { + #[corresponds(OCSP_REQUEST_new)] + pub fn new() -> Result<OcspRequest, ErrorStack> { + unsafe { + ffi::init(); + + cvt_p(ffi::OCSP_REQUEST_new()).map(OcspRequest) + } + } + + from_der! { + /// Deserializes a DER-encoded OCSP request. + #[corresponds(d2i_OCSP_REQUEST)] + from_der, + OcspRequest, + ffi::d2i_OCSP_REQUEST + } +} + +impl OcspRequestRef { + to_der! { + /// Serializes the request to its standard DER encoding. + #[corresponds(i2d_OCSP_REQUEST)] + to_der, + ffi::i2d_OCSP_REQUEST + } + + #[corresponds(OCSP_request_add0_id)] + pub fn add_id(&mut self, id: OcspCertId) -> Result<&mut OcspOneReqRef, ErrorStack> { + unsafe { + let ptr = cvt_p(ffi::OCSP_request_add0_id(self.as_ptr(), id.as_ptr()))?; + mem::forget(id); + Ok(OcspOneReqRef::from_ptr_mut(ptr)) + } + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::OCSP_ONEREQ; + fn drop = ffi::OCSP_ONEREQ_free; + + pub struct OcspOneReq; + pub struct OcspOneReqRef; +} 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); + } +} diff --git a/vendor/openssl/src/pkcs5.rs b/vendor/openssl/src/pkcs5.rs new file mode 100644 index 0000000..cd704e8 --- /dev/null +++ b/vendor/openssl/src/pkcs5.rs @@ -0,0 +1,310 @@ +#[cfg(not(boringssl))] +use libc::c_int; +use std::convert::TryInto; +#[cfg(not(boringssl))] +use std::ptr; + +use crate::cvt; +use crate::error::ErrorStack; +use crate::hash::MessageDigest; +#[cfg(not(boringssl))] +use crate::symm::Cipher; +use openssl_macros::corresponds; + +#[derive(Clone, Eq, PartialEq, Hash, Debug)] +pub struct KeyIvPair { + pub key: Vec<u8>, + pub iv: Option<Vec<u8>>, +} + +/// Derives a key and an IV from various parameters. +/// +/// If specified, `salt` must be 8 bytes in length. +/// +/// If the total key and IV length is less than 16 bytes and MD5 is used then +/// the algorithm is compatible with the key derivation algorithm from PKCS#5 +/// v1.5 or PBKDF1 from PKCS#5 v2.0. +/// +/// New applications should not use this and instead use +/// `pbkdf2_hmac` or another more modern key derivation algorithm. +#[corresponds(EVP_BytesToKey)] +#[allow(clippy::useless_conversion)] +#[cfg(not(boringssl))] +pub fn bytes_to_key( + cipher: Cipher, + digest: MessageDigest, + data: &[u8], + salt: Option<&[u8]>, + count: i32, +) -> Result<KeyIvPair, ErrorStack> { + unsafe { + assert!(data.len() <= c_int::max_value() as usize); + let salt_ptr = match salt { + Some(salt) => { + assert_eq!(salt.len(), ffi::PKCS5_SALT_LEN as usize); + salt.as_ptr() + } + None => ptr::null(), + }; + + ffi::init(); + + let mut iv = cipher.iv_len().map(|l| vec![0; l]); + + let cipher = cipher.as_ptr(); + let digest = digest.as_ptr(); + + let len = cvt(ffi::EVP_BytesToKey( + cipher, + digest, + salt_ptr, + ptr::null(), + data.len() as c_int, + count.into(), + ptr::null_mut(), + ptr::null_mut(), + ))?; + + let mut key = vec![0; len as usize]; + let iv_ptr = iv + .as_mut() + .map(|v| v.as_mut_ptr()) + .unwrap_or(ptr::null_mut()); + + cvt(ffi::EVP_BytesToKey( + cipher, + digest, + salt_ptr, + data.as_ptr(), + data.len() as c_int, + count as c_int, + key.as_mut_ptr(), + iv_ptr, + ))?; + + Ok(KeyIvPair { key, iv }) + } +} + +/// Derives a key from a password and salt using the PBKDF2-HMAC algorithm with a digest function. +#[corresponds(PKCS5_PBKDF2_HMAC)] +pub fn pbkdf2_hmac( + pass: &[u8], + salt: &[u8], + iter: usize, + hash: MessageDigest, + key: &mut [u8], +) -> Result<(), ErrorStack> { + unsafe { + ffi::init(); + cvt(ffi::PKCS5_PBKDF2_HMAC( + pass.as_ptr() as *const _, + pass.len().try_into().unwrap(), + salt.as_ptr(), + salt.len().try_into().unwrap(), + iter.try_into().unwrap(), + hash.as_ptr(), + key.len().try_into().unwrap(), + key.as_mut_ptr(), + )) + .map(|_| ()) + } +} + +/// Derives a key from a password and salt using the scrypt algorithm. +/// +/// Requires OpenSSL 1.1.0 or newer. +#[corresponds(EVP_PBE_scrypt)] +#[cfg(any(ossl110, boringssl))] +#[allow(clippy::useless_conversion)] +pub fn scrypt( + pass: &[u8], + salt: &[u8], + n: u64, + r: u64, + p: u64, + maxmem: u64, + key: &mut [u8], +) -> Result<(), ErrorStack> { + unsafe { + ffi::init(); + cvt(ffi::EVP_PBE_scrypt( + pass.as_ptr() as *const _, + pass.len(), + salt.as_ptr() as *const _, + salt.len(), + n, + r, + p, + maxmem.try_into().unwrap(), + key.as_mut_ptr() as *mut _, + key.len(), + )) + .map(|_| ()) + } +} + +#[cfg(test)] +mod tests { + use crate::hash::MessageDigest; + #[cfg(not(boringssl))] + use crate::symm::Cipher; + + // Test vectors from + // https://git.lysator.liu.se/nettle/nettle/blob/nettle_3.1.1_release_20150424/testsuite/pbkdf2-test.c + #[test] + fn pbkdf2_hmac_sha256() { + let mut buf = [0; 16]; + + super::pbkdf2_hmac(b"passwd", b"salt", 1, MessageDigest::sha256(), &mut buf).unwrap(); + assert_eq!( + buf, + &[ + 0x55_u8, 0xac_u8, 0x04_u8, 0x6e_u8, 0x56_u8, 0xe3_u8, 0x08_u8, 0x9f_u8, 0xec_u8, + 0x16_u8, 0x91_u8, 0xc2_u8, 0x25_u8, 0x44_u8, 0xb6_u8, 0x05_u8, + ][..] + ); + + super::pbkdf2_hmac( + b"Password", + b"NaCl", + 80000, + MessageDigest::sha256(), + &mut buf, + ) + .unwrap(); + assert_eq!( + buf, + &[ + 0x4d_u8, 0xdc_u8, 0xd8_u8, 0xf6_u8, 0x0b_u8, 0x98_u8, 0xbe_u8, 0x21_u8, 0x83_u8, + 0x0c_u8, 0xee_u8, 0x5e_u8, 0xf2_u8, 0x27_u8, 0x01_u8, 0xf9_u8, + ][..] + ); + } + + // Test vectors from + // https://git.lysator.liu.se/nettle/nettle/blob/nettle_3.1.1_release_20150424/testsuite/pbkdf2-test.c + #[test] + fn pbkdf2_hmac_sha512() { + let mut buf = [0; 64]; + + super::pbkdf2_hmac(b"password", b"NaCL", 1, MessageDigest::sha512(), &mut buf).unwrap(); + assert_eq!( + &buf[..], + &[ + 0x73_u8, 0xde_u8, 0xcf_u8, 0xa5_u8, 0x8a_u8, 0xa2_u8, 0xe8_u8, 0x4f_u8, 0x94_u8, + 0x77_u8, 0x1a_u8, 0x75_u8, 0x73_u8, 0x6b_u8, 0xb8_u8, 0x8b_u8, 0xd3_u8, 0xc7_u8, + 0xb3_u8, 0x82_u8, 0x70_u8, 0xcf_u8, 0xb5_u8, 0x0c_u8, 0xb3_u8, 0x90_u8, 0xed_u8, + 0x78_u8, 0xb3_u8, 0x05_u8, 0x65_u8, 0x6a_u8, 0xf8_u8, 0x14_u8, 0x8e_u8, 0x52_u8, + 0x45_u8, 0x2b_u8, 0x22_u8, 0x16_u8, 0xb2_u8, 0xb8_u8, 0x09_u8, 0x8b_u8, 0x76_u8, + 0x1f_u8, 0xc6_u8, 0x33_u8, 0x60_u8, 0x60_u8, 0xa0_u8, 0x9f_u8, 0x76_u8, 0x41_u8, + 0x5e_u8, 0x9f_u8, 0x71_u8, 0xea_u8, 0x47_u8, 0xf9_u8, 0xe9_u8, 0x06_u8, 0x43_u8, + 0x06_u8, + ][..] + ); + + super::pbkdf2_hmac( + b"pass\0word", + b"sa\0lt", + 1, + MessageDigest::sha512(), + &mut buf, + ) + .unwrap(); + assert_eq!( + &buf[..], + &[ + 0x71_u8, 0xa0_u8, 0xec_u8, 0x84_u8, 0x2a_u8, 0xbd_u8, 0x5c_u8, 0x67_u8, 0x8b_u8, + 0xcf_u8, 0xd1_u8, 0x45_u8, 0xf0_u8, 0x9d_u8, 0x83_u8, 0x52_u8, 0x2f_u8, 0x93_u8, + 0x36_u8, 0x15_u8, 0x60_u8, 0x56_u8, 0x3c_u8, 0x4d_u8, 0x0d_u8, 0x63_u8, 0xb8_u8, + 0x83_u8, 0x29_u8, 0x87_u8, 0x10_u8, 0x90_u8, 0xe7_u8, 0x66_u8, 0x04_u8, 0xa4_u8, + 0x9a_u8, 0xf0_u8, 0x8f_u8, 0xe7_u8, 0xc9_u8, 0xf5_u8, 0x71_u8, 0x56_u8, 0xc8_u8, + 0x79_u8, 0x09_u8, 0x96_u8, 0xb2_u8, 0x0f_u8, 0x06_u8, 0xbc_u8, 0x53_u8, 0x5e_u8, + 0x5a_u8, 0xb5_u8, 0x44_u8, 0x0d_u8, 0xf7_u8, 0xe8_u8, 0x78_u8, 0x29_u8, 0x6f_u8, + 0xa7_u8, + ][..] + ); + + super::pbkdf2_hmac( + b"passwordPASSWORDpassword", + b"salt\0\0\0", + 50, + MessageDigest::sha512(), + &mut buf, + ) + .unwrap(); + assert_eq!( + &buf[..], + &[ + 0x01_u8, 0x68_u8, 0x71_u8, 0xa4_u8, 0xc4_u8, 0xb7_u8, 0x5f_u8, 0x96_u8, 0x85_u8, + 0x7f_u8, 0xd2_u8, 0xb9_u8, 0xf8_u8, 0xca_u8, 0x28_u8, 0x02_u8, 0x3b_u8, 0x30_u8, + 0xee_u8, 0x2a_u8, 0x39_u8, 0xf5_u8, 0xad_u8, 0xca_u8, 0xc8_u8, 0xc9_u8, 0x37_u8, + 0x5f_u8, 0x9b_u8, 0xda_u8, 0x1c_u8, 0xcd_u8, 0x1b_u8, 0x6f_u8, 0x0b_u8, 0x2f_u8, + 0xc3_u8, 0xad_u8, 0xda_u8, 0x50_u8, 0x54_u8, 0x12_u8, 0xe7_u8, 0x9d_u8, 0x89_u8, + 0x00_u8, 0x56_u8, 0xc6_u8, 0x2e_u8, 0x52_u8, 0x4c_u8, 0x7d_u8, 0x51_u8, 0x15_u8, + 0x4b_u8, 0x1a_u8, 0x85_u8, 0x34_u8, 0x57_u8, 0x5b_u8, 0xd0_u8, 0x2d_u8, 0xee_u8, + 0x39_u8, + ][..] + ); + } + + #[test] + #[cfg(not(boringssl))] + fn bytes_to_key() { + let salt = [16_u8, 34_u8, 19_u8, 23_u8, 141_u8, 4_u8, 207_u8, 221_u8]; + + let data = [ + 143_u8, 210_u8, 75_u8, 63_u8, 214_u8, 179_u8, 155_u8, 241_u8, 242_u8, 31_u8, 154_u8, + 56_u8, 198_u8, 145_u8, 192_u8, 64_u8, 2_u8, 245_u8, 167_u8, 220_u8, 55_u8, 119_u8, + 233_u8, 136_u8, 139_u8, 27_u8, 71_u8, 242_u8, 119_u8, 175_u8, 65_u8, 207_u8, + ]; + + let expected_key = vec![ + 249_u8, 115_u8, 114_u8, 97_u8, 32_u8, 213_u8, 165_u8, 146_u8, 58_u8, 87_u8, 234_u8, + 3_u8, 43_u8, 250_u8, 97_u8, 114_u8, 26_u8, 98_u8, 245_u8, 246_u8, 238_u8, 177_u8, + 229_u8, 161_u8, 183_u8, 224_u8, 174_u8, 3_u8, 6_u8, 244_u8, 236_u8, 255_u8, + ]; + let expected_iv = vec![ + 4_u8, 223_u8, 153_u8, 219_u8, 28_u8, 142_u8, 234_u8, 68_u8, 227_u8, 69_u8, 98_u8, + 107_u8, 208_u8, 14_u8, 236_u8, 60_u8, + ]; + + assert_eq!( + super::bytes_to_key( + Cipher::aes_256_cbc(), + MessageDigest::sha1(), + &data, + Some(&salt), + 1, + ) + .unwrap(), + super::KeyIvPair { + key: expected_key, + iv: Some(expected_iv), + } + ); + } + + #[test] + #[cfg(any(ossl110, boringssl))] + fn scrypt() { + let pass = "pleaseletmein"; + let salt = "SodiumChloride"; + let expected = + "7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2d5432955613\ + f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887"; + + let mut actual = [0; 64]; + super::scrypt( + pass.as_bytes(), + salt.as_bytes(), + 16384, + 8, + 1, + 0, + &mut actual, + ) + .unwrap(); + assert_eq!(hex::encode(&actual[..]), expected); + } +} diff --git a/vendor/openssl/src/pkcs7.rs b/vendor/openssl/src/pkcs7.rs new file mode 100644 index 0000000..a272c59 --- /dev/null +++ b/vendor/openssl/src/pkcs7.rs @@ -0,0 +1,448 @@ +use bitflags::bitflags; +use foreign_types::{ForeignType, ForeignTypeRef}; +use libc::c_int; +use std::mem; +use std::ptr; + +use crate::bio::{MemBio, MemBioSlice}; +use crate::error::ErrorStack; +use crate::pkey::{HasPrivate, PKeyRef}; +use crate::stack::{Stack, StackRef}; +use crate::symm::Cipher; +use crate::x509::store::X509StoreRef; +use crate::x509::{X509Ref, X509}; +use crate::{cvt, cvt_p}; +use openssl_macros::corresponds; + +foreign_type_and_impl_send_sync! { + type CType = ffi::PKCS7; + fn drop = ffi::PKCS7_free; + + /// A PKCS#7 structure. + /// + /// Contains signed and/or encrypted data. + pub struct Pkcs7; + + /// Reference to `Pkcs7` + pub struct Pkcs7Ref; +} + +bitflags! { + #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + #[repr(transparent)] + pub struct Pkcs7Flags: c_int { + const TEXT = ffi::PKCS7_TEXT; + const NOCERTS = ffi::PKCS7_NOCERTS; + const NOSIGS = ffi::PKCS7_NOSIGS; + const NOCHAIN = ffi::PKCS7_NOCHAIN; + const NOINTERN = ffi::PKCS7_NOINTERN; + const NOVERIFY = ffi::PKCS7_NOVERIFY; + const DETACHED = ffi::PKCS7_DETACHED; + const BINARY = ffi::PKCS7_BINARY; + const NOATTR = ffi::PKCS7_NOATTR; + const NOSMIMECAP = ffi::PKCS7_NOSMIMECAP; + const NOOLDMIMETYPE = ffi::PKCS7_NOOLDMIMETYPE; + const CRLFEOL = ffi::PKCS7_CRLFEOL; + const STREAM = ffi::PKCS7_STREAM; + const NOCRL = ffi::PKCS7_NOCRL; + const PARTIAL = ffi::PKCS7_PARTIAL; + const REUSE_DIGEST = ffi::PKCS7_REUSE_DIGEST; + #[cfg(not(any(ossl101, ossl102, libressl)))] + const NO_DUAL_CONTENT = ffi::PKCS7_NO_DUAL_CONTENT; + } +} + +impl Pkcs7 { + from_pem! { + /// Deserializes a PEM-encoded PKCS#7 signature + /// + /// The input should have a header of `-----BEGIN PKCS7-----`. + #[corresponds(PEM_read_bio_PKCS7)] + from_pem, + Pkcs7, + ffi::PEM_read_bio_PKCS7 + } + + from_der! { + /// Deserializes a DER-encoded PKCS#7 signature + #[corresponds(d2i_PKCS7)] + from_der, + Pkcs7, + ffi::d2i_PKCS7 + } + + /// Parses a message in S/MIME format. + /// + /// Returns the loaded signature, along with the cleartext message (if + /// available). + #[corresponds(SMIME_read_PKCS7)] + pub fn from_smime(input: &[u8]) -> Result<(Pkcs7, Option<Vec<u8>>), ErrorStack> { + ffi::init(); + + let input_bio = MemBioSlice::new(input)?; + let mut bcont_bio = ptr::null_mut(); + unsafe { + let pkcs7 = + cvt_p(ffi::SMIME_read_PKCS7(input_bio.as_ptr(), &mut bcont_bio)).map(Pkcs7)?; + let out = if !bcont_bio.is_null() { + let bcont_bio = MemBio::from_ptr(bcont_bio); + Some(bcont_bio.get_buf().to_vec()) + } else { + None + }; + Ok((pkcs7, out)) + } + } + + /// Creates and returns a PKCS#7 `envelopedData` structure. + /// + /// `certs` is a list of recipient certificates. `input` is the content to be + /// encrypted. `cipher` is the symmetric cipher to use. `flags` is an optional + /// set of flags. + #[corresponds(PKCS7_encrypt)] + pub fn encrypt( + certs: &StackRef<X509>, + input: &[u8], + cipher: Cipher, + flags: Pkcs7Flags, + ) -> Result<Pkcs7, ErrorStack> { + let input_bio = MemBioSlice::new(input)?; + + unsafe { + cvt_p(ffi::PKCS7_encrypt( + certs.as_ptr(), + input_bio.as_ptr(), + cipher.as_ptr(), + flags.bits(), + )) + .map(Pkcs7) + } + } + + /// Creates and returns a PKCS#7 `signedData` structure. + /// + /// `signcert` is the certificate to sign with, `pkey` is the corresponding + /// private key. `certs` is an optional additional set of certificates to + /// include in the PKCS#7 structure (for example any intermediate CAs in the + /// chain). + #[corresponds(PKCS7_sign)] + pub fn sign<PT>( + signcert: &X509Ref, + pkey: &PKeyRef<PT>, + certs: &StackRef<X509>, + input: &[u8], + flags: Pkcs7Flags, + ) -> Result<Pkcs7, ErrorStack> + where + PT: HasPrivate, + { + let input_bio = MemBioSlice::new(input)?; + unsafe { + cvt_p(ffi::PKCS7_sign( + signcert.as_ptr(), + pkey.as_ptr(), + certs.as_ptr(), + input_bio.as_ptr(), + flags.bits(), + )) + .map(Pkcs7) + } + } +} + +impl Pkcs7Ref { + /// Converts PKCS#7 structure to S/MIME format + #[corresponds(SMIME_write_PKCS7)] + pub fn to_smime(&self, input: &[u8], flags: Pkcs7Flags) -> Result<Vec<u8>, ErrorStack> { + let input_bio = MemBioSlice::new(input)?; + let output = MemBio::new()?; + unsafe { + cvt(ffi::SMIME_write_PKCS7( + output.as_ptr(), + self.as_ptr(), + input_bio.as_ptr(), + flags.bits(), + )) + .map(|_| output.get_buf().to_owned()) + } + } + + to_pem! { + /// Serializes the data into a PEM-encoded PKCS#7 structure. + /// + /// The output will have a header of `-----BEGIN PKCS7-----`. + #[corresponds(PEM_write_bio_PKCS7)] + to_pem, + ffi::PEM_write_bio_PKCS7 + } + + to_der! { + /// Serializes the data into a DER-encoded PKCS#7 structure. + #[corresponds(i2d_PKCS7)] + to_der, + ffi::i2d_PKCS7 + } + + /// Decrypts data using the provided private key. + /// + /// `pkey` is the recipient's private key, and `cert` is the recipient's + /// certificate. + /// + /// Returns the decrypted message. + #[corresponds(PKCS7_decrypt)] + pub fn decrypt<PT>( + &self, + pkey: &PKeyRef<PT>, + cert: &X509Ref, + flags: Pkcs7Flags, + ) -> Result<Vec<u8>, ErrorStack> + where + PT: HasPrivate, + { + let output = MemBio::new()?; + + unsafe { + cvt(ffi::PKCS7_decrypt( + self.as_ptr(), + pkey.as_ptr(), + cert.as_ptr(), + output.as_ptr(), + flags.bits(), + )) + .map(|_| output.get_buf().to_owned()) + } + } + + /// Verifies the PKCS#7 `signedData` structure contained by `&self`. + /// + /// `certs` is a set of certificates in which to search for the signer's + /// certificate. `store` is a trusted certificate store (used for chain + /// verification). `indata` is the signed data if the content is not present + /// in `&self`. The content is written to `out` if it is not `None`. + #[corresponds(PKCS7_verify)] + pub fn verify( + &self, + certs: &StackRef<X509>, + store: &X509StoreRef, + indata: Option<&[u8]>, + out: Option<&mut Vec<u8>>, + flags: Pkcs7Flags, + ) -> Result<(), ErrorStack> { + let out_bio = MemBio::new()?; + + let indata_bio = match indata { + Some(data) => Some(MemBioSlice::new(data)?), + None => None, + }; + let indata_bio_ptr = indata_bio.as_ref().map_or(ptr::null_mut(), |p| p.as_ptr()); + + unsafe { + cvt(ffi::PKCS7_verify( + self.as_ptr(), + certs.as_ptr(), + store.as_ptr(), + indata_bio_ptr, + out_bio.as_ptr(), + flags.bits(), + )) + .map(|_| ())? + } + + if let Some(data) = out { + data.clear(); + data.extend_from_slice(out_bio.get_buf()); + } + + Ok(()) + } + + /// Retrieve the signer's certificates from the PKCS#7 structure without verifying them. + #[corresponds(PKCS7_get0_signers)] + pub fn signers( + &self, + certs: &StackRef<X509>, + flags: Pkcs7Flags, + ) -> Result<Stack<X509>, ErrorStack> { + unsafe { + let ptr = cvt_p(ffi::PKCS7_get0_signers( + self.as_ptr(), + certs.as_ptr(), + flags.bits(), + ))?; + + // The returned stack is owned by the caller, but the certs inside are not! Our stack interface can't deal + // with that, so instead we just manually bump the refcount of the certs so that the whole stack is properly + // owned. + let stack = Stack::<X509>::from_ptr(ptr); + for cert in &stack { + mem::forget(cert.to_owned()); + } + + Ok(stack) + } + } +} + +#[cfg(test)] +mod tests { + use crate::hash::MessageDigest; + use crate::pkcs7::{Pkcs7, Pkcs7Flags}; + use crate::pkey::PKey; + use crate::stack::Stack; + use crate::symm::Cipher; + use crate::x509::store::X509StoreBuilder; + use crate::x509::X509; + + #[test] + fn encrypt_decrypt_test() { + let cert = include_bytes!("../test/certs.pem"); + let cert = X509::from_pem(cert).unwrap(); + let mut certs = Stack::new().unwrap(); + certs.push(cert.clone()).unwrap(); + let message: String = String::from("foo"); + let cipher = Cipher::des_ede3_cbc(); + let flags = Pkcs7Flags::STREAM; + let pkey = include_bytes!("../test/key.pem"); + let pkey = PKey::private_key_from_pem(pkey).unwrap(); + + let pkcs7 = + Pkcs7::encrypt(&certs, message.as_bytes(), cipher, flags).expect("should succeed"); + + let encrypted = pkcs7 + .to_smime(message.as_bytes(), flags) + .expect("should succeed"); + + let (pkcs7_decoded, _) = Pkcs7::from_smime(encrypted.as_slice()).expect("should succeed"); + + let decoded = pkcs7_decoded + .decrypt(&pkey, &cert, Pkcs7Flags::empty()) + .expect("should succeed"); + + assert_eq!(decoded, message.into_bytes()); + } + + #[test] + fn sign_verify_test_detached() { + let cert = include_bytes!("../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let certs = Stack::new().unwrap(); + let message = "foo"; + let flags = Pkcs7Flags::STREAM | Pkcs7Flags::DETACHED; + let pkey = include_bytes!("../test/key.pem"); + let pkey = PKey::private_key_from_pem(pkey).unwrap(); + let mut store_builder = X509StoreBuilder::new().expect("should succeed"); + + let root_ca = include_bytes!("../test/root-ca.pem"); + let root_ca = X509::from_pem(root_ca).unwrap(); + store_builder.add_cert(root_ca).expect("should succeed"); + + let store = store_builder.build(); + + let pkcs7 = + Pkcs7::sign(&cert, &pkey, &certs, message.as_bytes(), flags).expect("should succeed"); + + let signed = pkcs7 + .to_smime(message.as_bytes(), flags) + .expect("should succeed"); + println!("{:?}", String::from_utf8(signed.clone()).unwrap()); + let (pkcs7_decoded, content) = + Pkcs7::from_smime(signed.as_slice()).expect("should succeed"); + + let mut output = Vec::new(); + pkcs7_decoded + .verify( + &certs, + &store, + Some(message.as_bytes()), + Some(&mut output), + flags, + ) + .expect("should succeed"); + + assert_eq!(output, message.as_bytes()); + assert_eq!(content.expect("should be non-empty"), message.as_bytes()); + } + + /// https://marc.info/?l=openbsd-cvs&m=166602943014106&w=2 + #[test] + #[cfg_attr(all(libressl360, not(libressl361)), ignore)] + fn sign_verify_test_normal() { + let cert = include_bytes!("../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let certs = Stack::new().unwrap(); + let message = "foo"; + let flags = Pkcs7Flags::STREAM; + let pkey = include_bytes!("../test/key.pem"); + let pkey = PKey::private_key_from_pem(pkey).unwrap(); + let mut store_builder = X509StoreBuilder::new().expect("should succeed"); + + let root_ca = include_bytes!("../test/root-ca.pem"); + let root_ca = X509::from_pem(root_ca).unwrap(); + store_builder.add_cert(root_ca).expect("should succeed"); + + let store = store_builder.build(); + + let pkcs7 = + Pkcs7::sign(&cert, &pkey, &certs, message.as_bytes(), flags).expect("should succeed"); + + let signed = pkcs7 + .to_smime(message.as_bytes(), flags) + .expect("should succeed"); + + let (pkcs7_decoded, content) = + Pkcs7::from_smime(signed.as_slice()).expect("should succeed"); + + let mut output = Vec::new(); + pkcs7_decoded + .verify(&certs, &store, None, Some(&mut output), flags) + .expect("should succeed"); + + assert_eq!(output, message.as_bytes()); + assert!(content.is_none()); + } + + /// https://marc.info/?l=openbsd-cvs&m=166602943014106&w=2 + #[test] + #[cfg_attr(all(libressl360, not(libressl361)), ignore)] + fn signers() { + let cert = include_bytes!("../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let cert_digest = cert.digest(MessageDigest::sha256()).unwrap(); + let certs = Stack::new().unwrap(); + let message = "foo"; + let flags = Pkcs7Flags::STREAM; + let pkey = include_bytes!("../test/key.pem"); + let pkey = PKey::private_key_from_pem(pkey).unwrap(); + let mut store_builder = X509StoreBuilder::new().expect("should succeed"); + + let root_ca = include_bytes!("../test/root-ca.pem"); + let root_ca = X509::from_pem(root_ca).unwrap(); + store_builder.add_cert(root_ca).expect("should succeed"); + + let pkcs7 = + Pkcs7::sign(&cert, &pkey, &certs, message.as_bytes(), flags).expect("should succeed"); + + let signed = pkcs7 + .to_smime(message.as_bytes(), flags) + .expect("should succeed"); + + let (pkcs7_decoded, _) = Pkcs7::from_smime(signed.as_slice()).expect("should succeed"); + + let empty_certs = Stack::new().unwrap(); + let signer_certs = pkcs7_decoded + .signers(&empty_certs, flags) + .expect("should succeed"); + assert_eq!(empty_certs.len(), 0); + assert_eq!(signer_certs.len(), 1); + let signer_digest = signer_certs[0].digest(MessageDigest::sha256()).unwrap(); + assert_eq!(*cert_digest, *signer_digest); + } + + #[test] + fn invalid_from_smime() { + let input = String::from("Invalid SMIME Message"); + let result = Pkcs7::from_smime(input.as_bytes()); + + assert!(result.is_err()); + } +} diff --git a/vendor/openssl/src/pkey.rs b/vendor/openssl/src/pkey.rs new file mode 100644 index 0000000..453aeed --- /dev/null +++ b/vendor/openssl/src/pkey.rs @@ -0,0 +1,1173 @@ +//! 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(any(ossl110, boringssl, libressl370))] +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, TryInto}; +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); + #[cfg(not(boringssl))] + pub const HMAC: Id = Id(ffi::EVP_PKEY_HMAC); + #[cfg(not(boringssl))] + 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(ossl111)] + pub const SM2: Id = Id(ffi::EVP_PKEY_SM2); + + #[cfg(any(ossl110, boringssl))] + pub const HKDF: Id = Id(ffi::EVP_PKEY_HKDF); + + #[cfg(any(ossl111, boringssl, libressl370))] + pub const ED25519: Id = Id(ffi::EVP_PKEY_ED25519); + #[cfg(ossl111)] + pub const ED448: Id = Id(ffi::EVP_PKEY_ED448); + #[cfg(any(ossl111, boringssl, libressl370))] + pub const X25519: Id = Id(ffi::EVP_PKEY_X25519); + #[cfg(ossl111)] + pub const X448: Id = Id(ffi::EVP_PKEY_X448); + #[cfg(ossl111)] + pub const POLY1305: Id = Id(ffi::EVP_PKEY_POLY1305); + + /// 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 } + } + + ///Returns the number of security bits. + /// + ///Bits of security is defined in NIST SP800-57. + #[corresponds(EVP_PKEY_security_bits)] + #[cfg(any(ossl110, libressl360))] + pub fn security_bits(&self) -> u32 { + unsafe { ffi::EVP_PKEY_security_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, + { + let res = unsafe { ffi::EVP_PKEY_cmp(self.as_ptr(), other.as_ptr()) == 1 }; + // Clear the stack. OpenSSL will put an error on the stack when the + // keys are different types in some situations. + let _ = ErrorStack::get(); + res + } + + /// Raw byte representation of a public key. + /// + /// This function only works for algorithms that support raw public keys. + /// Currently this is: [`Id::X25519`], [`Id::ED25519`], [`Id::X448`] or [`Id::ED448`]. + #[corresponds(EVP_PKEY_get_raw_public_key)] + #[cfg(any(ossl111, boringssl, libressl370))] + 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: [`Id::HMAC`], [`Id::X25519`], [`Id::ED25519`], [`Id::X448`] or [`Id::ED448`]. + #[corresponds(EVP_PKEY_get_raw_private_key)] + #[cfg(any(ossl111, boringssl, libressl370))] + 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 an unencrypted DER-formatted PKCS#8 + #[corresponds(i2d_PKCS8PrivateKey_bio)] + pub fn private_key_to_pkcs8(&self) -> Result<Vec<u8>, ErrorStack> { + unsafe { + let bio = MemBio::new()?; + cvt(ffi::i2d_PKCS8PrivateKey_bio( + bio.as_ptr(), + self.as_ptr(), + ptr::null(), + ptr::null_mut(), + 0, + None, + ptr::null_mut(), + ))?; + + Ok(bio.get_buf().to_owned()) + } + } + + /// Serializes a private key into a DER-formatted PKCS#8, using the supplied password to + /// encrypt the key. + #[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()?; + cvt(ffi::i2d_PKCS8PrivateKey_bio( + bio.as_ptr(), + self.as_ptr(), + cipher.as_ptr(), + passphrase.as_ptr() as *const _ as *mut _, + passphrase.len().try_into().unwrap(), + 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", + #[cfg(not(boringssl))] + 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_RSA(pkey.0, rsa.as_ptr()))?; + 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_DSA(pkey.0, dsa.as_ptr()))?; + mem::forget(dsa); + Ok(pkey) + } + } + + /// Creates a new `PKey` containing a Diffie-Hellman key. + #[corresponds(EVP_PKEY_assign_DH)] + #[cfg(not(boringssl))] + 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_DH(pkey.0, dh.as_ptr()))?; + 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_EC_KEY(pkey.0, ec_key.as_ptr()))?; + 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)] + #[cfg(not(boringssl))] + 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(all(not(boringssl), 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(any(ossl111, boringssl, libressl370))] + 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 X25519 key. + /// + /// To import a private key from raw bytes see [`PKey::private_key_from_raw_bytes`]. + /// + /// # Examples + /// + /// ``` + /// # fn main() -> Result<(), Box<dyn std::error::Error>> { + /// use openssl::pkey::{PKey, Id}; + /// use openssl::derive::Deriver; + /// + /// let public = // ... + /// # &PKey::generate_x25519()?.raw_public_key()?; + /// let public_key = PKey::public_key_from_raw_bytes(public, Id::X25519)?; + /// + /// let key = PKey::generate_x25519()?; + /// let mut deriver = Deriver::new(&key)?; + /// deriver.set_peer(&public_key)?; + /// + /// let secret = deriver.derive_to_vec()?; + /// assert_eq!(secret.len(), 32); + /// # Ok(()) } + /// ``` + #[cfg(any(ossl111, boringssl, libressl370))] + pub fn generate_x25519() -> Result<PKey<Private>, ErrorStack> { + PKey::generate_eddsa(Id::X25519) + } + + /// Generates a new private X448 key. + /// + /// To import a private key from raw bytes see [`PKey::private_key_from_raw_bytes`]. + /// + /// # Examples + /// + /// ``` + /// # fn main() -> Result<(), Box<dyn std::error::Error>> { + /// use openssl::pkey::{PKey, Id}; + /// use openssl::derive::Deriver; + /// + /// let public = // ... + /// # &PKey::generate_x448()?.raw_public_key()?; + /// let public_key = PKey::public_key_from_raw_bytes(public, Id::X448)?; + /// + /// let key = PKey::generate_x448()?; + /// let mut deriver = Deriver::new(&key)?; + /// deriver.set_peer(&public_key)?; + /// + /// let secret = deriver.derive_to_vec()?; + /// assert_eq!(secret.len(), 56); + /// # Ok(()) } + /// ``` + #[cfg(ossl111)] + pub fn generate_x448() -> Result<PKey<Private>, ErrorStack> { + PKey::generate_eddsa(Id::X448) + } + + /// Generates a new private Ed25519 key. + /// + /// To import a private key from raw bytes see [`PKey::private_key_from_raw_bytes`]. + /// + /// # Examples + /// + /// ``` + /// # fn main() -> Result<(), Box<dyn std::error::Error>> { + /// use openssl::pkey::{PKey, Id}; + /// use openssl::sign::Signer; + /// + /// let key = PKey::generate_ed25519()?; + /// let public_key = key.raw_public_key()?; + /// + /// let mut signer = Signer::new_without_digest(&key)?; + /// let digest = // ... + /// # &vec![0; 32]; + /// let signature = signer.sign_oneshot_to_vec(digest)?; + /// assert_eq!(signature.len(), 64); + /// # Ok(()) } + /// ``` + #[cfg(any(ossl111, boringssl, libressl370))] + pub fn generate_ed25519() -> Result<PKey<Private>, ErrorStack> { + PKey::generate_eddsa(Id::ED25519) + } + + /// Generates a new private Ed448 key. + /// + /// To import a private key from raw bytes see [`PKey::private_key_from_raw_bytes`]. + /// + /// # Examples + /// + /// ``` + /// # fn main() -> Result<(), Box<dyn std::error::Error>> { + /// use openssl::pkey::{PKey, Id}; + /// use openssl::sign::Signer; + /// + /// let key = PKey::generate_ed448()?; + /// let public_key = key.raw_public_key()?; + /// + /// let mut signer = Signer::new_without_digest(&key)?; + /// let digest = // ... + /// # &vec![0; 32]; + /// let signature = signer.sign_oneshot_to_vec(digest)?; + /// assert_eq!(signature.len(), 114); + /// # Ok(()) } + /// ``` + #[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> { + ffi::init(); + + 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(any(ossl111, boringssl, libressl370))] + 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(any(ossl111, boringssl, libressl370))] + 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(boringssl, 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() + } +} + +#[cfg(not(boringssl))] +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; + + #[cfg(not(boringssl))] + use crate::dh::Dh; + use crate::dsa::Dsa; + use crate::ec::EcKey; + use crate::error::Error; + 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"); + let pkey = PKey::private_key_from_pkcs8(key).unwrap(); + let serialized = pkey.private_key_to_pkcs8().unwrap(); + let pkey2 = PKey::private_key_from_pkcs8(&serialized).unwrap(); + + assert_eq!( + pkey2.private_key_to_der().unwrap(), + pkey.private_key_to_der().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] + #[cfg(not(boringssl))] + 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] + #[cfg(any(ossl110, libressl360))] + fn test_security_bits() { + let group = crate::ec::EcGroup::from_curve_name(crate::nid::Nid::SECP521R1).unwrap(); + let ec_key = EcKey::generate(&group).unwrap(); + let pkey: PKey<Private> = ec_key.try_into().unwrap(); + + assert_eq!(pkey.security_bits(), 256); + } + + #[test] + #[cfg(not(boringssl))] + 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(any(ossl111, boringssl, libressl370))] + 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(any(ossl111, boringssl, libressl370))] + 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_pkcs8().unwrap(), + from_raw.private_key_to_pkcs8().unwrap() + ); + } + + #[cfg(any(ossl111, boringssl, libressl370))] + #[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); + #[cfg(all(not(boringssl), not(libressl370)))] + test_raw_public_key(PKey::generate_x448, Id::X448); + #[cfg(all(not(boringssl), not(libressl370)))] + test_raw_public_key(PKey::generate_ed448, Id::ED448); + } + + #[cfg(any(ossl111, boringssl, libressl370))] + #[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); + #[cfg(all(not(boringssl), not(libressl370)))] + test_raw_private_key(PKey::generate_x448, Id::X448); + #[cfg(all(not(boringssl), not(libressl370)))] + 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()); + } + + #[test] + fn test_public_eq() { + let rsa = Rsa::generate(2048).unwrap(); + let pkey1 = PKey::from_rsa(rsa).unwrap(); + + let group = crate::ec::EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let ec_key = EcKey::generate(&group).unwrap(); + let pkey2 = PKey::from_ec_key(ec_key).unwrap(); + + assert!(!pkey1.public_eq(&pkey2)); + assert!(Error::get().is_none()); + } +} diff --git a/vendor/openssl/src/pkey_ctx.rs b/vendor/openssl/src/pkey_ctx.rs new file mode 100644 index 0000000..4ac32a8 --- /dev/null +++ b/vendor/openssl/src/pkey_ctx.rs @@ -0,0 +1,1002 @@ +//! The asymmetric encryption context. +//! +//! # Examples +//! +//! Encrypt data with RSA +//! +//! ``` +//! use openssl::rsa::Rsa; +//! use openssl::pkey::PKey; +//! use openssl::pkey_ctx::PkeyCtx; +//! +//! let key = Rsa::generate(4096).unwrap(); +//! let key = PKey::from_rsa(key).unwrap(); +//! +//! let mut ctx = PkeyCtx::new(&key).unwrap(); +//! ctx.encrypt_init().unwrap(); +//! +//! let data = b"Some Crypto Text"; +//! let mut ciphertext = vec![]; +//! ctx.encrypt_to_vec(data, &mut ciphertext).unwrap(); +//! ``` + +#![cfg_attr( + not(boringssl), + doc = r#"\ +Generate a CMAC key + +``` +use openssl::pkey_ctx::PkeyCtx; +use openssl::pkey::Id; +use openssl::cipher::Cipher; + +let mut ctx = PkeyCtx::new_id(Id::CMAC).unwrap(); +ctx.keygen_init().unwrap(); +ctx.set_keygen_cipher(Cipher::aes_128_cbc()).unwrap(); +ctx.set_keygen_mac_key(b"0123456789abcdef").unwrap(); +let cmac_key = ctx.keygen().unwrap(); +```"# +)] + +//! +//! Sign and verify data with RSA +//! +//! ``` +//! use openssl::pkey_ctx::PkeyCtx; +//! use openssl::pkey::PKey; +//! use openssl::rsa::Rsa; +//! +//! // Generate a random RSA key. +//! let key = Rsa::generate(4096).unwrap(); +//! let key = PKey::from_rsa(key).unwrap(); +//! +//! let text = b"Some Crypto Text"; +//! +//! // Create the signature. +//! let mut ctx = PkeyCtx::new(&key).unwrap(); +//! ctx.sign_init().unwrap(); +//! let mut signature = vec![]; +//! ctx.sign_to_vec(text, &mut signature).unwrap(); +//! +//! // Verify the signature. +//! let mut ctx = PkeyCtx::new(&key).unwrap(); +//! ctx.verify_init().unwrap(); +//! let valid = ctx.verify(text, &signature).unwrap(); +//! assert!(valid); +//! ``` +#[cfg(not(boringssl))] +use crate::cipher::CipherRef; +use crate::error::ErrorStack; +use crate::md::MdRef; +use crate::pkey::{HasPrivate, HasPublic, Id, PKey, PKeyRef, Private}; +use crate::rsa::Padding; +use crate::sign::RsaPssSaltlen; +use crate::{cvt, cvt_p}; +use foreign_types::{ForeignType, ForeignTypeRef}; +#[cfg(not(boringssl))] +use libc::c_int; +use openssl_macros::corresponds; +use std::convert::TryFrom; +use std::ptr; + +/// HKDF modes of operation. +#[cfg(ossl111)] +pub struct HkdfMode(c_int); + +#[cfg(ossl111)] +impl HkdfMode { + /// This is the default mode. Calling [`derive`][PkeyCtxRef::derive] on a [`PkeyCtxRef`] set up + /// for HKDF will perform an extract followed by an expand operation in one go. The derived key + /// returned will be the result after the expand operation. The intermediate fixed-length + /// pseudorandom key K is not returned. + pub const EXTRACT_THEN_EXPAND: Self = HkdfMode(ffi::EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND); + + /// In this mode calling [`derive`][PkeyCtxRef::derive] will just perform the extract operation. + /// The value returned will be the intermediate fixed-length pseudorandom key K. + /// + /// The digest, key and salt values must be set before a key is derived or an error occurs. + pub const EXTRACT_ONLY: Self = HkdfMode(ffi::EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY); + + /// In this mode calling [`derive`][PkeyCtxRef::derive] will just perform the expand operation. + /// The input key should be set to the intermediate fixed-length pseudorandom key K returned + /// from a previous extract operation. + /// + /// The digest, key and info values must be set before a key is derived or an error occurs. + pub const EXPAND_ONLY: Self = HkdfMode(ffi::EVP_PKEY_HKDEF_MODE_EXPAND_ONLY); +} + +generic_foreign_type_and_impl_send_sync! { + type CType = ffi::EVP_PKEY_CTX; + fn drop = ffi::EVP_PKEY_CTX_free; + + /// A context object which can perform asymmetric cryptography operations. + pub struct PkeyCtx<T>; + /// A reference to a [`PkeyCtx`]. + pub struct PkeyCtxRef<T>; +} + +impl<T> PkeyCtx<T> { + /// Creates a new pkey context using the provided key. + #[corresponds(EVP_PKEY_CTX_new)] + #[inline] + pub fn new(pkey: &PKeyRef<T>) -> Result<Self, ErrorStack> { + unsafe { + let ptr = cvt_p(ffi::EVP_PKEY_CTX_new(pkey.as_ptr(), ptr::null_mut()))?; + Ok(PkeyCtx::from_ptr(ptr)) + } + } +} + +impl PkeyCtx<()> { + /// Creates a new pkey context for the specified algorithm ID. + #[corresponds(EVP_PKEY_new_id)] + #[inline] + pub fn new_id(id: Id) -> Result<Self, ErrorStack> { + unsafe { + let ptr = cvt_p(ffi::EVP_PKEY_CTX_new_id(id.as_raw(), ptr::null_mut()))?; + Ok(PkeyCtx::from_ptr(ptr)) + } + } +} + +impl<T> PkeyCtxRef<T> +where + T: HasPublic, +{ + /// Prepares the context for encryption using the public key. + #[corresponds(EVP_PKEY_encrypt_init)] + #[inline] + pub fn encrypt_init(&mut self) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_encrypt_init(self.as_ptr()))?; + } + + Ok(()) + } + + /// Prepares the context for signature verification using the public key. + #[corresponds(EVP_PKEY_verify_init)] + #[inline] + pub fn verify_init(&mut self) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_verify_init(self.as_ptr()))?; + } + + Ok(()) + } + + /// Prepares the context for signature recovery using the public key. + #[corresponds(EVP_PKEY_verify_recover_init)] + #[inline] + pub fn verify_recover_init(&mut self) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_verify_recover_init(self.as_ptr()))?; + } + + Ok(()) + } + + /// Encrypts data using the public key. + /// + /// If `to` is set to `None`, an upper bound on the number of bytes required for the output buffer will be + /// returned. + #[corresponds(EVP_PKEY_encrypt)] + #[inline] + pub fn encrypt(&mut self, from: &[u8], to: Option<&mut [u8]>) -> Result<usize, ErrorStack> { + let mut written = to.as_ref().map_or(0, |b| b.len()); + unsafe { + cvt(ffi::EVP_PKEY_encrypt( + self.as_ptr(), + to.map_or(ptr::null_mut(), |b| b.as_mut_ptr()), + &mut written, + from.as_ptr(), + from.len(), + ))?; + } + + Ok(written) + } + + /// Like [`Self::encrypt`] but appends ciphertext to a [`Vec`]. + pub fn encrypt_to_vec(&mut self, from: &[u8], out: &mut Vec<u8>) -> Result<usize, ErrorStack> { + let base = out.len(); + let len = self.encrypt(from, None)?; + out.resize(base + len, 0); + let len = self.encrypt(from, Some(&mut out[base..]))?; + out.truncate(base + len); + Ok(len) + } + + /// Verifies the signature of data using the public key. + /// + /// Returns `Ok(true)` if the signature is valid, `Ok(false)` if the signature is invalid, and `Err` if an error + /// occurred. + /// + /// # Note + /// + /// This verifies the signature of the *raw* data. It is more common to compute and verify the signature of the + /// cryptographic hash of an arbitrary amount of data. The [`MdCtx`](crate::md_ctx::MdCtx) type can be used to do + /// that. + #[corresponds(EVP_PKEY_verify)] + #[inline] + pub fn verify(&mut self, data: &[u8], sig: &[u8]) -> Result<bool, ErrorStack> { + unsafe { + let r = ffi::EVP_PKEY_verify( + self.as_ptr(), + sig.as_ptr(), + sig.len(), + data.as_ptr(), + data.len(), + ); + // `EVP_PKEY_verify` is not terribly consistent about how it, + // reports errors. It does not clearly distinguish between 0 and + // -1, and may put errors on the stack in both cases. If there's + // errors on the stack, we return `Err()`, else we return + // `Ok(false)`. + if r <= 0 { + let errors = ErrorStack::get(); + if !errors.errors().is_empty() { + return Err(errors); + } + } + + Ok(r == 1) + } + } + + /// Recovers the original data signed by the private key. You almost + /// always want `verify` instead. + /// + /// Returns the number of bytes written to `to`, or the number of bytes + /// that would be written, if `to` is `None. + #[corresponds(EVP_PKEY_verify_recover)] + #[inline] + pub fn verify_recover( + &mut self, + sig: &[u8], + to: Option<&mut [u8]>, + ) -> Result<usize, ErrorStack> { + let mut written = to.as_ref().map_or(0, |b| b.len()); + unsafe { + cvt(ffi::EVP_PKEY_verify_recover( + self.as_ptr(), + to.map_or(ptr::null_mut(), |b| b.as_mut_ptr()), + &mut written, + sig.as_ptr(), + sig.len(), + ))?; + } + + Ok(written) + } +} + +impl<T> PkeyCtxRef<T> +where + T: HasPrivate, +{ + /// Prepares the context for decryption using the private key. + #[corresponds(EVP_PKEY_decrypt_init)] + #[inline] + pub fn decrypt_init(&mut self) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_decrypt_init(self.as_ptr()))?; + } + + Ok(()) + } + + /// Prepares the context for signing using the private key. + #[corresponds(EVP_PKEY_sign_init)] + #[inline] + pub fn sign_init(&mut self) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_sign_init(self.as_ptr()))?; + } + + Ok(()) + } + + /// Sets the peer key used for secret derivation. + #[corresponds(EVP_PKEY_derive_set_peer)] + pub fn derive_set_peer<U>(&mut self, key: &PKeyRef<U>) -> Result<(), ErrorStack> + where + U: HasPublic, + { + unsafe { + cvt(ffi::EVP_PKEY_derive_set_peer(self.as_ptr(), key.as_ptr()))?; + } + + Ok(()) + } + + /// Decrypts data using the private key. + /// + /// If `to` is set to `None`, an upper bound on the number of bytes required for the output buffer will be + /// returned. + #[corresponds(EVP_PKEY_decrypt)] + #[inline] + pub fn decrypt(&mut self, from: &[u8], to: Option<&mut [u8]>) -> Result<usize, ErrorStack> { + let mut written = to.as_ref().map_or(0, |b| b.len()); + unsafe { + cvt(ffi::EVP_PKEY_decrypt( + self.as_ptr(), + to.map_or(ptr::null_mut(), |b| b.as_mut_ptr()), + &mut written, + from.as_ptr(), + from.len(), + ))?; + } + + Ok(written) + } + + /// Like [`Self::decrypt`] but appends plaintext to a [`Vec`]. + pub fn decrypt_to_vec(&mut self, from: &[u8], out: &mut Vec<u8>) -> Result<usize, ErrorStack> { + let base = out.len(); + let len = self.decrypt(from, None)?; + out.resize(base + len, 0); + let len = self.decrypt(from, Some(&mut out[base..]))?; + out.truncate(base + len); + Ok(len) + } + + /// Signs the contents of `data`. + /// + /// If `sig` is set to `None`, an upper bound on the number of bytes required for the output buffer will be + /// returned. + /// + /// # Note + /// + /// This computes the signature of the *raw* bytes of `data`. It is more common to sign the cryptographic hash of + /// an arbitrary amount of data. The [`MdCtx`](crate::md_ctx::MdCtx) type can be used to do that. + #[corresponds(EVP_PKEY_sign)] + #[inline] + pub fn sign(&mut self, data: &[u8], sig: Option<&mut [u8]>) -> Result<usize, ErrorStack> { + let mut written = sig.as_ref().map_or(0, |b| b.len()); + unsafe { + cvt(ffi::EVP_PKEY_sign( + self.as_ptr(), + sig.map_or(ptr::null_mut(), |b| b.as_mut_ptr()), + &mut written, + data.as_ptr(), + data.len(), + ))?; + } + + Ok(written) + } + + /// Like [`Self::sign`] but appends the signature to a [`Vec`]. + pub fn sign_to_vec(&mut self, data: &[u8], sig: &mut Vec<u8>) -> Result<usize, ErrorStack> { + let base = sig.len(); + let len = self.sign(data, None)?; + sig.resize(base + len, 0); + let len = self.sign(data, Some(&mut sig[base..]))?; + sig.truncate(base + len); + Ok(len) + } +} + +impl<T> PkeyCtxRef<T> { + /// Prepares the context for shared secret derivation. + #[corresponds(EVP_PKEY_derive_init)] + #[inline] + pub fn derive_init(&mut self) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_derive_init(self.as_ptr()))?; + } + + Ok(()) + } + + /// Prepares the context for key generation. + #[corresponds(EVP_PKEY_keygen_init)] + #[inline] + pub fn keygen_init(&mut self) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_keygen_init(self.as_ptr()))?; + } + + Ok(()) + } + + /// Sets which algorithm was used to compute the digest used in a + /// signature. With RSA signatures this causes the signature to be wrapped + /// in a `DigestInfo` structure. This is almost always what you want with + /// RSA signatures. + #[corresponds(EVP_PKEY_CTX_set_signature_md)] + #[inline] + pub fn set_signature_md(&self, md: &MdRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_signature_md( + self.as_ptr(), + md.as_ptr(), + ))?; + } + Ok(()) + } + + /// Returns the RSA padding mode in use. + /// + /// This is only useful for RSA keys. + #[corresponds(EVP_PKEY_CTX_get_rsa_padding)] + #[inline] + pub fn rsa_padding(&self) -> Result<Padding, ErrorStack> { + let mut pad = 0; + unsafe { + cvt(ffi::EVP_PKEY_CTX_get_rsa_padding(self.as_ptr(), &mut pad))?; + } + + Ok(Padding::from_raw(pad)) + } + + /// Sets the RSA padding mode. + /// + /// This is only useful for RSA keys. + #[corresponds(EVP_PKEY_CTX_set_rsa_padding)] + #[inline] + pub fn set_rsa_padding(&mut self, padding: Padding) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_rsa_padding( + self.as_ptr(), + padding.as_raw(), + ))?; + } + + Ok(()) + } + + /// Sets the RSA PSS salt length. + /// + /// This is only useful for RSA keys. + #[corresponds(EVP_PKEY_CTX_set_rsa_pss_saltlen)] + #[inline] + pub fn set_rsa_pss_saltlen(&mut self, len: RsaPssSaltlen) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_rsa_pss_saltlen( + self.as_ptr(), + len.as_raw(), + )) + .map(|_| ()) + } + } + + /// Sets the RSA MGF1 algorithm. + /// + /// This is only useful for RSA keys. + #[corresponds(EVP_PKEY_CTX_set_rsa_mgf1_md)] + #[inline] + pub fn set_rsa_mgf1_md(&mut self, md: &MdRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_rsa_mgf1_md( + self.as_ptr(), + md.as_ptr(), + ))?; + } + + Ok(()) + } + + /// Sets the RSA OAEP algorithm. + /// + /// This is only useful for RSA keys. + #[corresponds(EVP_PKEY_CTX_set_rsa_oaep_md)] + #[cfg(any(ossl102, libressl310, boringssl))] + #[inline] + pub fn set_rsa_oaep_md(&mut self, md: &MdRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_rsa_oaep_md( + self.as_ptr(), + md.as_ptr() as *mut _, + ))?; + } + + Ok(()) + } + + /// Sets the RSA OAEP label. + /// + /// This is only useful for RSA keys. + #[corresponds(EVP_PKEY_CTX_set0_rsa_oaep_label)] + #[cfg(any(ossl102, libressl310, boringssl))] + pub fn set_rsa_oaep_label(&mut self, label: &[u8]) -> Result<(), ErrorStack> { + use crate::LenType; + let len = LenType::try_from(label.len()).unwrap(); + + unsafe { + let p = ffi::OPENSSL_malloc(label.len() as _); + ptr::copy_nonoverlapping(label.as_ptr(), p as *mut _, label.len()); + + let r = cvt(ffi::EVP_PKEY_CTX_set0_rsa_oaep_label( + self.as_ptr(), + p as *mut _, + len, + )); + if r.is_err() { + ffi::OPENSSL_free(p); + } + r?; + } + + Ok(()) + } + + /// Sets the cipher used during key generation. + #[cfg(not(boringssl))] + #[corresponds(EVP_PKEY_CTX_ctrl)] + #[inline] + pub fn set_keygen_cipher(&mut self, cipher: &CipherRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_ctrl( + self.as_ptr(), + -1, + ffi::EVP_PKEY_OP_KEYGEN, + ffi::EVP_PKEY_CTRL_CIPHER, + 0, + cipher.as_ptr() as *mut _, + ))?; + } + + Ok(()) + } + + /// Sets the key MAC key used during key generation. + #[cfg(not(boringssl))] + #[corresponds(EVP_PKEY_CTX_ctrl)] + #[inline] + pub fn set_keygen_mac_key(&mut self, key: &[u8]) -> Result<(), ErrorStack> { + let len = c_int::try_from(key.len()).unwrap(); + + unsafe { + cvt(ffi::EVP_PKEY_CTX_ctrl( + self.as_ptr(), + -1, + ffi::EVP_PKEY_OP_KEYGEN, + ffi::EVP_PKEY_CTRL_SET_MAC_KEY, + len, + key.as_ptr() as *mut _, + ))?; + } + + Ok(()) + } + + /// Sets the digest used for HKDF derivation. + /// + /// Requires OpenSSL 1.1.0 or newer. + #[corresponds(EVP_PKEY_CTX_set_hkdf_md)] + #[cfg(any(ossl110, boringssl))] + #[inline] + pub fn set_hkdf_md(&mut self, digest: &MdRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_hkdf_md( + self.as_ptr(), + digest.as_ptr(), + ))?; + } + + Ok(()) + } + + /// Sets the HKDF mode of operation. + /// + /// Defaults to [`HkdfMode::EXTRACT_THEN_EXPAND`]. + /// + /// WARNING: Although this API calls it a "mode", HKDF-Extract and HKDF-Expand are distinct + /// operations with distinct inputs and distinct kinds of keys. Callers should not pass input + /// secrets for one operation into the other. + /// + /// Requires OpenSSL 1.1.1 or newer. + #[corresponds(EVP_PKEY_CTX_set_hkdf_mode)] + #[cfg(ossl111)] + #[inline] + pub fn set_hkdf_mode(&mut self, mode: HkdfMode) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_hkdf_mode(self.as_ptr(), mode.0))?; + } + + Ok(()) + } + + /// Sets the input material for HKDF generation as the "key". + /// + /// Which input is the key depends on the "mode" (see [`set_hkdf_mode`][Self::set_hkdf_mode]). + /// If [`HkdfMode::EXTRACT_THEN_EXPAND`] or [`HkdfMode::EXTRACT_ONLY`], this function specifies + /// the input keying material (IKM) for HKDF-Extract. If [`HkdfMode::EXPAND_ONLY`], it instead + /// specifies the pseudorandom key (PRK) for HKDF-Expand. + /// + /// Requires OpenSSL 1.1.0 or newer. + #[corresponds(EVP_PKEY_CTX_set1_hkdf_key)] + #[cfg(any(ossl110, boringssl))] + #[inline] + pub fn set_hkdf_key(&mut self, key: &[u8]) -> Result<(), ErrorStack> { + #[cfg(not(boringssl))] + let len = c_int::try_from(key.len()).unwrap(); + #[cfg(boringssl)] + let len = key.len(); + + unsafe { + cvt(ffi::EVP_PKEY_CTX_set1_hkdf_key( + self.as_ptr(), + key.as_ptr(), + len, + ))?; + } + + Ok(()) + } + + /// Sets the salt value for HKDF generation. + /// + /// If performing HKDF-Expand only, this parameter is ignored. + /// + /// Requires OpenSSL 1.1.0 or newer. + #[corresponds(EVP_PKEY_CTX_set1_hkdf_salt)] + #[cfg(any(ossl110, boringssl))] + #[inline] + pub fn set_hkdf_salt(&mut self, salt: &[u8]) -> Result<(), ErrorStack> { + #[cfg(not(boringssl))] + let len = c_int::try_from(salt.len()).unwrap(); + #[cfg(boringssl)] + let len = salt.len(); + + unsafe { + cvt(ffi::EVP_PKEY_CTX_set1_hkdf_salt( + self.as_ptr(), + salt.as_ptr(), + len, + ))?; + } + + Ok(()) + } + + /// Appends info bytes for HKDF generation. + /// + /// If performing HKDF-Extract only, this parameter is ignored. + /// + /// Requires OpenSSL 1.1.0 or newer. + #[corresponds(EVP_PKEY_CTX_add1_hkdf_info)] + #[cfg(any(ossl110, boringssl))] + #[inline] + pub fn add_hkdf_info(&mut self, info: &[u8]) -> Result<(), ErrorStack> { + #[cfg(not(boringssl))] + let len = c_int::try_from(info.len()).unwrap(); + #[cfg(boringssl)] + let len = info.len(); + + unsafe { + cvt(ffi::EVP_PKEY_CTX_add1_hkdf_info( + self.as_ptr(), + info.as_ptr(), + len, + ))?; + } + + Ok(()) + } + + /// Derives a shared secret between two keys. + /// + /// If `buf` is set to `None`, an upper bound on the number of bytes required for the buffer will be returned. + #[corresponds(EVP_PKEY_derive)] + pub fn derive(&mut self, buf: Option<&mut [u8]>) -> Result<usize, ErrorStack> { + let mut len = buf.as_ref().map_or(0, |b| b.len()); + unsafe { + cvt(ffi::EVP_PKEY_derive( + self.as_ptr(), + buf.map_or(ptr::null_mut(), |b| b.as_mut_ptr()), + &mut len, + ))?; + } + + Ok(len) + } + + /// Like [`Self::derive`] but appends the secret to a [`Vec`]. + pub fn derive_to_vec(&mut self, buf: &mut Vec<u8>) -> Result<usize, ErrorStack> { + let base = buf.len(); + let len = self.derive(None)?; + buf.resize(base + len, 0); + let len = self.derive(Some(&mut buf[base..]))?; + buf.truncate(base + len); + Ok(len) + } + + /// Generates a new public/private keypair. + #[corresponds(EVP_PKEY_keygen)] + #[inline] + pub fn keygen(&mut self) -> Result<PKey<Private>, ErrorStack> { + unsafe { + let mut key = ptr::null_mut(); + cvt(ffi::EVP_PKEY_keygen(self.as_ptr(), &mut key))?; + Ok(PKey::from_ptr(key)) + } + } +} + +#[cfg(test)] +mod test { + use super::*; + #[cfg(not(boringssl))] + use crate::cipher::Cipher; + use crate::ec::{EcGroup, EcKey}; + use crate::hash::{hash, MessageDigest}; + use crate::md::Md; + use crate::nid::Nid; + use crate::pkey::PKey; + use crate::rsa::Rsa; + use crate::sign::Verifier; + + #[test] + fn rsa() { + let key = include_bytes!("../test/rsa.pem"); + let rsa = Rsa::private_key_from_pem(key).unwrap(); + let pkey = PKey::from_rsa(rsa).unwrap(); + + let mut ctx = PkeyCtx::new(&pkey).unwrap(); + ctx.encrypt_init().unwrap(); + ctx.set_rsa_padding(Padding::PKCS1).unwrap(); + + let pt = "hello world".as_bytes(); + let mut ct = vec![]; + ctx.encrypt_to_vec(pt, &mut ct).unwrap(); + + ctx.decrypt_init().unwrap(); + ctx.set_rsa_padding(Padding::PKCS1).unwrap(); + + let mut out = vec![]; + ctx.decrypt_to_vec(&ct, &mut out).unwrap(); + + assert_eq!(pt, out); + } + + #[test] + #[cfg(any(ossl102, libressl310, boringssl))] + fn rsa_oaep() { + let key = include_bytes!("../test/rsa.pem"); + let rsa = Rsa::private_key_from_pem(key).unwrap(); + let pkey = PKey::from_rsa(rsa).unwrap(); + + let mut ctx = PkeyCtx::new(&pkey).unwrap(); + ctx.encrypt_init().unwrap(); + ctx.set_rsa_padding(Padding::PKCS1_OAEP).unwrap(); + ctx.set_rsa_oaep_md(Md::sha256()).unwrap(); + ctx.set_rsa_mgf1_md(Md::sha256()).unwrap(); + + let pt = "hello world".as_bytes(); + let mut ct = vec![]; + ctx.encrypt_to_vec(pt, &mut ct).unwrap(); + + ctx.decrypt_init().unwrap(); + ctx.set_rsa_padding(Padding::PKCS1_OAEP).unwrap(); + ctx.set_rsa_oaep_md(Md::sha256()).unwrap(); + ctx.set_rsa_mgf1_md(Md::sha256()).unwrap(); + + let mut out = vec![]; + ctx.decrypt_to_vec(&ct, &mut out).unwrap(); + + assert_eq!(pt, out); + } + + #[test] + fn rsa_sign() { + let key = include_bytes!("../test/rsa.pem"); + let rsa = Rsa::private_key_from_pem(key).unwrap(); + let pkey = PKey::from_rsa(rsa).unwrap(); + + let mut ctx = PkeyCtx::new(&pkey).unwrap(); + ctx.sign_init().unwrap(); + ctx.set_rsa_padding(Padding::PKCS1).unwrap(); + ctx.set_signature_md(Md::sha384()).unwrap(); + + let msg = b"hello world"; + let digest = hash(MessageDigest::sha384(), msg).unwrap(); + let mut signature = vec![]; + ctx.sign_to_vec(&digest, &mut signature).unwrap(); + + let mut verifier = Verifier::new(MessageDigest::sha384(), &pkey).unwrap(); + verifier.update(msg).unwrap(); + assert!(matches!(verifier.verify(&signature), Ok(true))); + } + + #[test] + fn rsa_sign_pss() { + let key = include_bytes!("../test/rsa.pem"); + let rsa = Rsa::private_key_from_pem(key).unwrap(); + let pkey = PKey::from_rsa(rsa).unwrap(); + + let mut ctx = PkeyCtx::new(&pkey).unwrap(); + ctx.sign_init().unwrap(); + ctx.set_rsa_padding(Padding::PKCS1_PSS).unwrap(); + ctx.set_signature_md(Md::sha384()).unwrap(); + ctx.set_rsa_pss_saltlen(RsaPssSaltlen::custom(14)).unwrap(); + + let msg = b"hello world"; + let digest = hash(MessageDigest::sha384(), msg).unwrap(); + let mut signature = vec![]; + ctx.sign_to_vec(&digest, &mut signature).unwrap(); + + let mut verifier = Verifier::new(MessageDigest::sha384(), &pkey).unwrap(); + verifier.set_rsa_padding(Padding::PKCS1_PSS).unwrap(); + verifier + .set_rsa_pss_saltlen(RsaPssSaltlen::custom(14)) + .unwrap(); + verifier.update(msg).unwrap(); + assert!(matches!(verifier.verify(&signature), Ok(true))); + } + + #[test] + fn derive() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let key1 = EcKey::generate(&group).unwrap(); + let key1 = PKey::from_ec_key(key1).unwrap(); + let key2 = EcKey::generate(&group).unwrap(); + let key2 = PKey::from_ec_key(key2).unwrap(); + + let mut ctx = PkeyCtx::new(&key1).unwrap(); + ctx.derive_init().unwrap(); + ctx.derive_set_peer(&key2).unwrap(); + + let mut buf = vec![]; + ctx.derive_to_vec(&mut buf).unwrap(); + } + + #[test] + #[cfg(not(boringssl))] + fn cmac_keygen() { + let mut ctx = PkeyCtx::new_id(Id::CMAC).unwrap(); + ctx.keygen_init().unwrap(); + ctx.set_keygen_cipher(Cipher::aes_128_cbc()).unwrap(); + ctx.set_keygen_mac_key(&hex::decode("9294727a3638bb1c13f48ef8158bfc9d").unwrap()) + .unwrap(); + ctx.keygen().unwrap(); + } + + #[test] + #[cfg(any(ossl110, boringssl))] + fn hkdf() { + let mut ctx = PkeyCtx::new_id(Id::HKDF).unwrap(); + ctx.derive_init().unwrap(); + ctx.set_hkdf_md(Md::sha256()).unwrap(); + ctx.set_hkdf_key(&hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap()) + .unwrap(); + ctx.set_hkdf_salt(&hex::decode("000102030405060708090a0b0c").unwrap()) + .unwrap(); + ctx.add_hkdf_info(&hex::decode("f0f1f2f3f4f5f6f7f8f9").unwrap()) + .unwrap(); + let mut out = [0; 42]; + ctx.derive(Some(&mut out)).unwrap(); + + assert_eq!( + &out[..], + hex::decode("3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865") + .unwrap() + ); + } + + #[test] + #[cfg(ossl111)] + fn hkdf_expand() { + let mut ctx = PkeyCtx::new_id(Id::HKDF).unwrap(); + ctx.derive_init().unwrap(); + ctx.set_hkdf_mode(HkdfMode::EXPAND_ONLY).unwrap(); + ctx.set_hkdf_md(Md::sha256()).unwrap(); + ctx.set_hkdf_key( + &hex::decode("077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5") + .unwrap(), + ) + .unwrap(); + ctx.add_hkdf_info(&hex::decode("f0f1f2f3f4f5f6f7f8f9").unwrap()) + .unwrap(); + let mut out = [0; 42]; + ctx.derive(Some(&mut out)).unwrap(); + + assert_eq!( + &out[..], + hex::decode("3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865") + .unwrap() + ); + } + + #[test] + #[cfg(ossl111)] + fn hkdf_extract() { + let mut ctx = PkeyCtx::new_id(Id::HKDF).unwrap(); + ctx.derive_init().unwrap(); + ctx.set_hkdf_mode(HkdfMode::EXTRACT_ONLY).unwrap(); + ctx.set_hkdf_md(Md::sha256()).unwrap(); + ctx.set_hkdf_key(&hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap()) + .unwrap(); + ctx.set_hkdf_salt(&hex::decode("000102030405060708090a0b0c").unwrap()) + .unwrap(); + let mut out = vec![]; + ctx.derive_to_vec(&mut out).unwrap(); + + assert_eq!( + &out[..], + hex::decode("077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5") + .unwrap() + ); + } + + #[test] + fn verify_fail() { + let key1 = Rsa::generate(4096).unwrap(); + let key1 = PKey::from_rsa(key1).unwrap(); + + let data = b"Some Crypto Text"; + + let mut ctx = PkeyCtx::new(&key1).unwrap(); + ctx.sign_init().unwrap(); + let mut signature = vec![]; + ctx.sign_to_vec(data, &mut signature).unwrap(); + + let bad_data = b"Some Crypto text"; + + ctx.verify_init().unwrap(); + let valid = ctx.verify(bad_data, &signature); + assert!(matches!(valid, Ok(false) | Err(_))); + assert!(ErrorStack::get().errors().is_empty()); + } + + #[test] + fn verify_fail_ec() { + let key1 = + EcKey::generate(&EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap()).unwrap(); + let key1 = PKey::from_ec_key(key1).unwrap(); + + let data = b"Some Crypto Text"; + let mut ctx = PkeyCtx::new(&key1).unwrap(); + ctx.verify_init().unwrap(); + assert!(matches!(ctx.verify(data, &[0; 64]), Ok(false) | Err(_))); + assert!(ErrorStack::get().errors().is_empty()); + } + + #[test] + fn test_verify_recover() { + let key = Rsa::generate(2048).unwrap(); + let key = PKey::from_rsa(key).unwrap(); + + let digest = [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + ]; + + let mut ctx = PkeyCtx::new(&key).unwrap(); + ctx.sign_init().unwrap(); + ctx.set_rsa_padding(Padding::PKCS1).unwrap(); + ctx.set_signature_md(Md::sha256()).unwrap(); + let mut signature = vec![]; + ctx.sign_to_vec(&digest, &mut signature).unwrap(); + + // Attempt recovery of just the digest. + let mut ctx = PkeyCtx::new(&key).unwrap(); + ctx.verify_recover_init().unwrap(); + ctx.set_rsa_padding(Padding::PKCS1).unwrap(); + ctx.set_signature_md(Md::sha256()).unwrap(); + let length = ctx.verify_recover(&signature, None).unwrap(); + let mut result_buf = vec![0; length]; + let length = ctx + .verify_recover(&signature, Some(&mut result_buf)) + .unwrap(); + assert_eq!(length, digest.len()); + // result_buf contains the digest + assert_eq!(result_buf[..length], digest); + + // Attempt recovery of teh entire DigestInfo + let mut ctx = PkeyCtx::new(&key).unwrap(); + ctx.verify_recover_init().unwrap(); + ctx.set_rsa_padding(Padding::PKCS1).unwrap(); + let length = ctx.verify_recover(&signature, None).unwrap(); + let mut result_buf = vec![0; length]; + let length = ctx + .verify_recover(&signature, Some(&mut result_buf)) + .unwrap(); + // 32-bytes of SHA256 digest + the ASN.1 DigestInfo structure == 51 bytes + assert_eq!(length, 51); + // The digest is the end of the DigestInfo structure. + assert_eq!(result_buf[length - digest.len()..length], digest); + } +} diff --git a/vendor/openssl/src/provider.rs b/vendor/openssl/src/provider.rs new file mode 100644 index 0000000..147fadf --- /dev/null +++ b/vendor/openssl/src/provider.rs @@ -0,0 +1,77 @@ +use crate::error::ErrorStack; +use crate::lib_ctx::LibCtxRef; +use crate::{cvt, cvt_p}; +use foreign_types::{ForeignType, ForeignTypeRef}; +use openssl_macros::corresponds; +use std::ffi::CString; +use std::ptr; + +foreign_type_and_impl_send_sync! { + type CType = ffi::OSSL_PROVIDER; + fn drop = ossl_provider_free; + + pub struct Provider; + /// A reference to a [`Provider`]. + pub struct ProviderRef; +} + +#[inline] +unsafe fn ossl_provider_free(p: *mut ffi::OSSL_PROVIDER) { + ffi::OSSL_PROVIDER_unload(p); +} + +impl Provider { + /// Loads a new provider into the specified library context, disabling the fallback providers. + /// + /// If `ctx` is `None`, the provider will be loaded in to the default library context. + #[corresponds(OSSL_provider_load)] + pub fn load(ctx: Option<&LibCtxRef>, name: &str) -> Result<Self, ErrorStack> { + let name = CString::new(name).unwrap(); + unsafe { + let p = cvt_p(ffi::OSSL_PROVIDER_load( + ctx.map_or(ptr::null_mut(), ForeignTypeRef::as_ptr), + name.as_ptr(), + ))?; + + Ok(Provider::from_ptr(p)) + } + } + + /// Loads a new provider into the specified library context, disabling the fallback providers if `retain_fallbacks` + /// is `false` and the load succeeds. + /// + /// If `ctx` is `None`, the provider will be loaded into the default library context. + #[corresponds(OSSL_provider_try_load)] + pub fn try_load( + ctx: Option<&LibCtxRef>, + name: &str, + retain_fallbacks: bool, + ) -> Result<Self, ErrorStack> { + let name = CString::new(name).unwrap(); + unsafe { + let p = cvt_p(ffi::OSSL_PROVIDER_try_load( + ctx.map_or(ptr::null_mut(), ForeignTypeRef::as_ptr), + name.as_ptr(), + retain_fallbacks as _, + ))?; + + Ok(Provider::from_ptr(p)) + } + } + + /// Specifies the default search path that is to be used for looking for providers in the specified library context. + /// If left unspecified, an environment variable and a fall back default value will be used instead + /// + /// If `ctx` is `None`, the provider will be loaded into the default library context. + #[corresponds(OSSL_PROVIDER_set_default_search_path)] + pub fn set_default_search_path(ctx: Option<&LibCtxRef>, path: &str) -> Result<(), ErrorStack> { + let path = CString::new(path).unwrap(); + unsafe { + cvt(ffi::OSSL_PROVIDER_set_default_search_path( + ctx.map_or(ptr::null_mut(), ForeignTypeRef::as_ptr), + path.as_ptr(), + )) + .map(|_| ()) + } + } +} diff --git a/vendor/openssl/src/rand.rs b/vendor/openssl/src/rand.rs new file mode 100644 index 0000000..8317951 --- /dev/null +++ b/vendor/openssl/src/rand.rs @@ -0,0 +1,60 @@ +//! Utilities for secure random number generation. +//! +//! # Examples +//! +//! To generate a buffer with cryptographically strong bytes: +//! +//! ``` +//! use openssl::rand::rand_bytes; +//! +//! let mut buf = [0; 256]; +//! rand_bytes(&mut buf).unwrap(); +//! ``` +use libc::c_int; + +use crate::error::ErrorStack; +use crate::{cvt, LenType}; +use openssl_macros::corresponds; + +/// Fill buffer with cryptographically strong pseudo-random bytes. +/// +/// # Examples +/// +/// To generate a buffer with cryptographically strong random bytes: +/// +/// ``` +/// use openssl::rand::rand_bytes; +/// +/// let mut buf = [0; 256]; +/// rand_bytes(&mut buf).unwrap(); +/// ``` +#[corresponds(RAND_bytes)] +pub fn rand_bytes(buf: &mut [u8]) -> Result<(), ErrorStack> { + unsafe { + ffi::init(); + assert!(buf.len() <= c_int::max_value() as usize); + cvt(ffi::RAND_bytes(buf.as_mut_ptr(), buf.len() as LenType)).map(|_| ()) + } +} + +/// Controls random device file descriptor behavior. +/// +/// Requires OpenSSL 1.1.1 or newer. +#[corresponds(RAND_keep_random_devices_open)] +#[cfg(ossl111)] +pub fn keep_random_devices_open(keep: bool) { + unsafe { + ffi::RAND_keep_random_devices_open(keep as LenType); + } +} + +#[cfg(test)] +mod tests { + use super::rand_bytes; + + #[test] + fn test_rand_bytes() { + let mut buf = [0; 32]; + rand_bytes(&mut buf).unwrap(); + } +} diff --git a/vendor/openssl/src/rsa.rs b/vendor/openssl/src/rsa.rs new file mode 100644 index 0000000..f155b12 --- /dev/null +++ b/vendor/openssl/src/rsa.rs @@ -0,0 +1,852 @@ +//! Rivest–Shamir–Adleman cryptosystem +//! +//! RSA is one of the earliest asymmetric public key encryption schemes. +//! Like many other cryptosystems, RSA relies on the presumed difficulty of a hard +//! mathematical problem, namely factorization of the product of two large prime +//! numbers. At the moment there does not exist an algorithm that can factor such +//! large numbers in reasonable time. RSA is used in a wide variety of +//! applications including digital signatures and key exchanges such as +//! establishing a TLS/SSL connection. +//! +//! The RSA acronym is derived from the first letters of the surnames of the +//! algorithm's founding trio. +//! +//! # Example +//! +//! Generate a 2048-bit RSA key pair and use the public key to encrypt some data. +//! +//! ```rust +//! use openssl::rsa::{Rsa, Padding}; +//! +//! let rsa = Rsa::generate(2048).unwrap(); +//! let data = b"foobar"; +//! let mut buf = vec![0; rsa.size() as usize]; +//! let encrypted_len = rsa.public_encrypt(data, &mut buf, Padding::PKCS1).unwrap(); +//! ``` +use cfg_if::cfg_if; +use foreign_types::{ForeignType, ForeignTypeRef}; +use libc::c_int; +use std::fmt; +use std::mem; +use std::ptr; + +use crate::bn::{BigNum, BigNumRef}; +use crate::error::ErrorStack; +use crate::pkey::{HasPrivate, HasPublic, Private, Public}; +use crate::util::ForeignTypeRefExt; +use crate::{cvt, cvt_n, cvt_p, LenType}; +use openssl_macros::corresponds; + +/// Type of encryption padding to use. +/// +/// Random length padding is primarily used to prevent attackers from +/// predicting or knowing the exact length of a plaintext message that +/// can possibly lead to breaking encryption. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct Padding(c_int); + +impl Padding { + pub const NONE: Padding = Padding(ffi::RSA_NO_PADDING); + pub const PKCS1: Padding = Padding(ffi::RSA_PKCS1_PADDING); + pub const PKCS1_OAEP: Padding = Padding(ffi::RSA_PKCS1_OAEP_PADDING); + pub const PKCS1_PSS: Padding = Padding(ffi::RSA_PKCS1_PSS_PADDING); + + /// Creates a `Padding` from an integer representation. + pub fn from_raw(value: c_int) -> Padding { + Padding(value) + } + + /// Returns the integer representation of `Padding`. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn as_raw(&self) -> c_int { + self.0 + } +} + +generic_foreign_type_and_impl_send_sync! { + type CType = ffi::RSA; + fn drop = ffi::RSA_free; + + /// An RSA key. + pub struct Rsa<T>; + + /// Reference to `RSA` + pub struct RsaRef<T>; +} + +impl<T> Clone for Rsa<T> { + fn clone(&self) -> Rsa<T> { + (**self).to_owned() + } +} + +impl<T> ToOwned for RsaRef<T> { + type Owned = Rsa<T>; + + fn to_owned(&self) -> Rsa<T> { + unsafe { + ffi::RSA_up_ref(self.as_ptr()); + Rsa::from_ptr(self.as_ptr()) + } + } +} + +impl<T> RsaRef<T> +where + T: HasPrivate, +{ + private_key_to_pem! { + /// Serializes the private key to a PEM-encoded PKCS#1 RSAPrivateKey structure. + /// + /// The output will have a header of `-----BEGIN RSA PRIVATE KEY-----`. + #[corresponds(PEM_write_bio_RSAPrivateKey)] + private_key_to_pem, + /// Serializes the private key to a PEM-encoded encrypted PKCS#1 RSAPrivateKey structure. + /// + /// The output will have a header of `-----BEGIN RSA PRIVATE KEY-----`. + #[corresponds(PEM_write_bio_RSAPrivateKey)] + private_key_to_pem_passphrase, + ffi::PEM_write_bio_RSAPrivateKey + } + + to_der! { + /// Serializes the private key to a DER-encoded PKCS#1 RSAPrivateKey structure. + #[corresponds(i2d_RSAPrivateKey)] + private_key_to_der, + ffi::i2d_RSAPrivateKey + } + + /// Decrypts data using the private key, returning the number of decrypted bytes. + /// + /// # Panics + /// + /// Panics if `self` has no private components, or if `to` is smaller + /// than `self.size()`. + #[corresponds(RSA_private_decrypt)] + pub fn private_decrypt( + &self, + from: &[u8], + to: &mut [u8], + padding: Padding, + ) -> Result<usize, ErrorStack> { + assert!(from.len() <= i32::max_value() as usize); + assert!(to.len() >= self.size() as usize); + + unsafe { + let len = cvt_n(ffi::RSA_private_decrypt( + from.len() as LenType, + from.as_ptr(), + to.as_mut_ptr(), + self.as_ptr(), + padding.0, + ))?; + Ok(len as usize) + } + } + + /// Encrypts data using the private key, returning the number of encrypted bytes. + /// + /// # Panics + /// + /// Panics if `self` has no private components, or if `to` is smaller + /// than `self.size()`. + #[corresponds(RSA_private_encrypt)] + pub fn private_encrypt( + &self, + from: &[u8], + to: &mut [u8], + padding: Padding, + ) -> Result<usize, ErrorStack> { + assert!(from.len() <= i32::max_value() as usize); + assert!(to.len() >= self.size() as usize); + + unsafe { + let len = cvt_n(ffi::RSA_private_encrypt( + from.len() as LenType, + from.as_ptr(), + to.as_mut_ptr(), + self.as_ptr(), + padding.0, + ))?; + Ok(len as usize) + } + } + + /// Returns a reference to the private exponent of the key. + #[corresponds(RSA_get0_key)] + pub fn d(&self) -> &BigNumRef { + unsafe { + let mut d = ptr::null(); + RSA_get0_key(self.as_ptr(), ptr::null_mut(), ptr::null_mut(), &mut d); + BigNumRef::from_const_ptr(d) + } + } + + /// Returns a reference to the first factor of the exponent of the key. + #[corresponds(RSA_get0_factors)] + pub fn p(&self) -> Option<&BigNumRef> { + unsafe { + let mut p = ptr::null(); + RSA_get0_factors(self.as_ptr(), &mut p, ptr::null_mut()); + BigNumRef::from_const_ptr_opt(p) + } + } + + /// Returns a reference to the second factor of the exponent of the key. + #[corresponds(RSA_get0_factors)] + pub fn q(&self) -> Option<&BigNumRef> { + unsafe { + let mut q = ptr::null(); + RSA_get0_factors(self.as_ptr(), ptr::null_mut(), &mut q); + BigNumRef::from_const_ptr_opt(q) + } + } + + /// Returns a reference to the first exponent used for CRT calculations. + #[corresponds(RSA_get0_crt_params)] + pub fn dmp1(&self) -> Option<&BigNumRef> { + unsafe { + let mut dp = ptr::null(); + RSA_get0_crt_params(self.as_ptr(), &mut dp, ptr::null_mut(), ptr::null_mut()); + BigNumRef::from_const_ptr_opt(dp) + } + } + + /// Returns a reference to the second exponent used for CRT calculations. + #[corresponds(RSA_get0_crt_params)] + pub fn dmq1(&self) -> Option<&BigNumRef> { + unsafe { + let mut dq = ptr::null(); + RSA_get0_crt_params(self.as_ptr(), ptr::null_mut(), &mut dq, ptr::null_mut()); + BigNumRef::from_const_ptr_opt(dq) + } + } + + /// Returns a reference to the coefficient used for CRT calculations. + #[corresponds(RSA_get0_crt_params)] + pub fn iqmp(&self) -> Option<&BigNumRef> { + unsafe { + let mut qi = ptr::null(); + RSA_get0_crt_params(self.as_ptr(), ptr::null_mut(), ptr::null_mut(), &mut qi); + BigNumRef::from_const_ptr_opt(qi) + } + } + + /// Validates RSA parameters for correctness + #[corresponds(RSA_check_key)] + #[allow(clippy::unnecessary_cast)] + pub fn check_key(&self) -> Result<bool, ErrorStack> { + unsafe { + let result = ffi::RSA_check_key(self.as_ptr()) as i32; + if result == -1 { + Err(ErrorStack::get()) + } else { + Ok(result == 1) + } + } + } +} + +impl<T> RsaRef<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_RSA_PUBKEY)] + public_key_to_pem, + ffi::PEM_write_bio_RSA_PUBKEY + } + + to_der! { + /// Serializes the public key into a DER-encoded SubjectPublicKeyInfo structure. + #[corresponds(i2d_RSA_PUBKEY)] + public_key_to_der, + ffi::i2d_RSA_PUBKEY + } + + to_pem! { + /// Serializes the public key into a PEM-encoded PKCS#1 RSAPublicKey structure. + /// + /// The output will have a header of `-----BEGIN RSA PUBLIC KEY-----`. + #[corresponds(PEM_write_bio_RSAPublicKey)] + public_key_to_pem_pkcs1, + ffi::PEM_write_bio_RSAPublicKey + } + + to_der! { + /// Serializes the public key into a DER-encoded PKCS#1 RSAPublicKey structure. + #[corresponds(i2d_RSAPublicKey)] + public_key_to_der_pkcs1, + ffi::i2d_RSAPublicKey + } + + /// Returns the size of the modulus in bytes. + #[corresponds(RSA_size)] + pub fn size(&self) -> u32 { + unsafe { ffi::RSA_size(self.as_ptr()) as u32 } + } + + /// Decrypts data using the public key, returning the number of decrypted bytes. + /// + /// # Panics + /// + /// Panics if `to` is smaller than `self.size()`. + #[corresponds(RSA_public_decrypt)] + pub fn public_decrypt( + &self, + from: &[u8], + to: &mut [u8], + padding: Padding, + ) -> Result<usize, ErrorStack> { + assert!(from.len() <= i32::max_value() as usize); + assert!(to.len() >= self.size() as usize); + + unsafe { + let len = cvt_n(ffi::RSA_public_decrypt( + from.len() as LenType, + from.as_ptr(), + to.as_mut_ptr(), + self.as_ptr(), + padding.0, + ))?; + Ok(len as usize) + } + } + + /// Encrypts data using the public key, returning the number of encrypted bytes. + /// + /// # Panics + /// + /// Panics if `to` is smaller than `self.size()`. + #[corresponds(RSA_public_encrypt)] + pub fn public_encrypt( + &self, + from: &[u8], + to: &mut [u8], + padding: Padding, + ) -> Result<usize, ErrorStack> { + assert!(from.len() <= i32::max_value() as usize); + assert!(to.len() >= self.size() as usize); + + unsafe { + let len = cvt_n(ffi::RSA_public_encrypt( + from.len() as LenType, + from.as_ptr(), + to.as_mut_ptr(), + self.as_ptr(), + padding.0, + ))?; + Ok(len as usize) + } + } + + /// Returns a reference to the modulus of the key. + #[corresponds(RSA_get0_key)] + pub fn n(&self) -> &BigNumRef { + unsafe { + let mut n = ptr::null(); + RSA_get0_key(self.as_ptr(), &mut n, ptr::null_mut(), ptr::null_mut()); + BigNumRef::from_const_ptr(n) + } + } + + /// Returns a reference to the public exponent of the key. + #[corresponds(RSA_get0_key)] + pub fn e(&self) -> &BigNumRef { + unsafe { + let mut e = ptr::null(); + RSA_get0_key(self.as_ptr(), ptr::null_mut(), &mut e, ptr::null_mut()); + BigNumRef::from_const_ptr(e) + } + } +} + +impl Rsa<Public> { + /// Creates a new RSA key with only public components. + /// + /// `n` is the modulus common to both public and private key. + /// `e` is the public exponent. + /// + /// This corresponds to [`RSA_new`] and uses [`RSA_set0_key`]. + /// + /// [`RSA_new`]: https://www.openssl.org/docs/manmaster/crypto/RSA_new.html + /// [`RSA_set0_key`]: https://www.openssl.org/docs/manmaster/crypto/RSA_set0_key.html + pub fn from_public_components(n: BigNum, e: BigNum) -> Result<Rsa<Public>, ErrorStack> { + unsafe { + let rsa = cvt_p(ffi::RSA_new())?; + RSA_set0_key(rsa, n.as_ptr(), e.as_ptr(), ptr::null_mut()); + mem::forget((n, e)); + Ok(Rsa::from_ptr(rsa)) + } + } + + from_pem! { + /// Decodes a PEM-encoded SubjectPublicKeyInfo structure containing an RSA key. + /// + /// The input should have a header of `-----BEGIN PUBLIC KEY-----`. + #[corresponds(PEM_read_bio_RSA_PUBKEY)] + public_key_from_pem, + Rsa<Public>, + ffi::PEM_read_bio_RSA_PUBKEY + } + + from_pem! { + /// Decodes a PEM-encoded PKCS#1 RSAPublicKey structure. + /// + /// The input should have a header of `-----BEGIN RSA PUBLIC KEY-----`. + #[corresponds(PEM_read_bio_RSAPublicKey)] + public_key_from_pem_pkcs1, + Rsa<Public>, + ffi::PEM_read_bio_RSAPublicKey + } + + from_der! { + /// Decodes a DER-encoded SubjectPublicKeyInfo structure containing an RSA key. + #[corresponds(d2i_RSA_PUBKEY)] + public_key_from_der, + Rsa<Public>, + ffi::d2i_RSA_PUBKEY + } + + from_der! { + /// Decodes a DER-encoded PKCS#1 RSAPublicKey structure. + #[corresponds(d2i_RSAPublicKey)] + public_key_from_der_pkcs1, + Rsa<Public>, + ffi::d2i_RSAPublicKey + } +} + +pub struct RsaPrivateKeyBuilder { + rsa: Rsa<Private>, +} + +impl RsaPrivateKeyBuilder { + /// Creates a new `RsaPrivateKeyBuilder`. + /// + /// `n` is the modulus common to both public and private key. + /// `e` is the public exponent and `d` is the private exponent. + /// + /// This corresponds to [`RSA_new`] and uses [`RSA_set0_key`]. + /// + /// [`RSA_new`]: https://www.openssl.org/docs/manmaster/crypto/RSA_new.html + /// [`RSA_set0_key`]: https://www.openssl.org/docs/manmaster/crypto/RSA_set0_key.html + pub fn new(n: BigNum, e: BigNum, d: BigNum) -> Result<RsaPrivateKeyBuilder, ErrorStack> { + unsafe { + let rsa = cvt_p(ffi::RSA_new())?; + RSA_set0_key(rsa, n.as_ptr(), e.as_ptr(), d.as_ptr()); + mem::forget((n, e, d)); + Ok(RsaPrivateKeyBuilder { + rsa: Rsa::from_ptr(rsa), + }) + } + } + + /// Sets the factors of the Rsa key. + /// + /// `p` and `q` are the first and second factors of `n`. + #[corresponds(RSA_set0_factors)] + // FIXME should be infallible + pub fn set_factors(self, p: BigNum, q: BigNum) -> Result<RsaPrivateKeyBuilder, ErrorStack> { + unsafe { + RSA_set0_factors(self.rsa.as_ptr(), p.as_ptr(), q.as_ptr()); + mem::forget((p, q)); + } + Ok(self) + } + + /// Sets the Chinese Remainder Theorem params of the Rsa key. + /// + /// `dmp1`, `dmq1`, and `iqmp` are the exponents and coefficient for + /// CRT calculations which is used to speed up RSA operations. + #[corresponds(RSA_set0_crt_params)] + // FIXME should be infallible + pub fn set_crt_params( + self, + dmp1: BigNum, + dmq1: BigNum, + iqmp: BigNum, + ) -> Result<RsaPrivateKeyBuilder, ErrorStack> { + unsafe { + RSA_set0_crt_params( + self.rsa.as_ptr(), + dmp1.as_ptr(), + dmq1.as_ptr(), + iqmp.as_ptr(), + ); + mem::forget((dmp1, dmq1, iqmp)); + } + Ok(self) + } + + /// Returns the Rsa key. + pub fn build(self) -> Rsa<Private> { + self.rsa + } +} + +impl Rsa<Private> { + /// Creates a new RSA key with private components (public components are assumed). + /// + /// This a convenience method over: + /// ``` + /// # use openssl::rsa::RsaPrivateKeyBuilder; + /// # fn main() -> Result<(), Box<dyn std::error::Error>> { + /// # let bn = || openssl::bn::BigNum::new().unwrap(); + /// # let (n, e, d, p, q, dmp1, dmq1, iqmp) = (bn(), bn(), bn(), bn(), bn(), bn(), bn(), bn()); + /// RsaPrivateKeyBuilder::new(n, e, d)? + /// .set_factors(p, q)? + /// .set_crt_params(dmp1, dmq1, iqmp)? + /// .build(); + /// # Ok(()) } + /// ``` + #[allow(clippy::too_many_arguments, clippy::many_single_char_names)] + pub fn from_private_components( + n: BigNum, + e: BigNum, + d: BigNum, + p: BigNum, + q: BigNum, + dmp1: BigNum, + dmq1: BigNum, + iqmp: BigNum, + ) -> Result<Rsa<Private>, ErrorStack> { + Ok(RsaPrivateKeyBuilder::new(n, e, d)? + .set_factors(p, q)? + .set_crt_params(dmp1, dmq1, iqmp)? + .build()) + } + + /// Generates a public/private key pair with the specified size. + /// + /// The public exponent will be 65537. + #[corresponds(RSA_generate_key_ex)] + pub fn generate(bits: u32) -> Result<Rsa<Private>, ErrorStack> { + let e = BigNum::from_u32(ffi::RSA_F4 as u32)?; + Rsa::generate_with_e(bits, &e) + } + + /// Generates a public/private key pair with the specified size and a custom exponent. + /// + /// Unless you have specific needs and know what you're doing, use `Rsa::generate` instead. + #[corresponds(RSA_generate_key_ex)] + pub fn generate_with_e(bits: u32, e: &BigNumRef) -> Result<Rsa<Private>, ErrorStack> { + unsafe { + let rsa = Rsa::from_ptr(cvt_p(ffi::RSA_new())?); + cvt(ffi::RSA_generate_key_ex( + rsa.0, + bits as c_int, + e.as_ptr(), + ptr::null_mut(), + ))?; + Ok(rsa) + } + } + + // FIXME these need to identify input formats + private_key_from_pem! { + /// Deserializes a private key from a PEM-encoded PKCS#1 RSAPrivateKey structure. + #[corresponds(PEM_read_bio_RSAPrivateKey)] + private_key_from_pem, + + /// Deserializes a private key from a PEM-encoded encrypted PKCS#1 RSAPrivateKey structure. + #[corresponds(PEM_read_bio_RSAPrivateKey)] + private_key_from_pem_passphrase, + + /// Deserializes a private key from a PEM-encoded encrypted PKCS#1 RSAPrivateKey structure. + /// + /// The callback should fill the password into the provided buffer and return its length. + #[corresponds(PEM_read_bio_RSAPrivateKey)] + private_key_from_pem_callback, + Rsa<Private>, + ffi::PEM_read_bio_RSAPrivateKey + } + + from_der! { + /// Decodes a DER-encoded PKCS#1 RSAPrivateKey structure. + #[corresponds(d2i_RSAPrivateKey)] + private_key_from_der, + Rsa<Private>, + ffi::d2i_RSAPrivateKey + } +} + +impl<T> fmt::Debug for Rsa<T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Rsa") + } +} + +cfg_if! { + if #[cfg(any(ossl110, libressl273, boringssl))] { + use ffi::{ + RSA_get0_key, RSA_get0_factors, RSA_get0_crt_params, RSA_set0_key, RSA_set0_factors, + RSA_set0_crt_params, + }; + } else { + #[allow(bad_style)] + unsafe fn RSA_get0_key( + r: *const ffi::RSA, + n: *mut *const ffi::BIGNUM, + e: *mut *const ffi::BIGNUM, + d: *mut *const ffi::BIGNUM, + ) { + if !n.is_null() { + *n = (*r).n; + } + if !e.is_null() { + *e = (*r).e; + } + if !d.is_null() { + *d = (*r).d; + } + } + + #[allow(bad_style)] + unsafe fn RSA_get0_factors( + r: *const ffi::RSA, + p: *mut *const ffi::BIGNUM, + q: *mut *const ffi::BIGNUM, + ) { + if !p.is_null() { + *p = (*r).p; + } + if !q.is_null() { + *q = (*r).q; + } + } + + #[allow(bad_style)] + unsafe fn RSA_get0_crt_params( + r: *const ffi::RSA, + dmp1: *mut *const ffi::BIGNUM, + dmq1: *mut *const ffi::BIGNUM, + iqmp: *mut *const ffi::BIGNUM, + ) { + if !dmp1.is_null() { + *dmp1 = (*r).dmp1; + } + if !dmq1.is_null() { + *dmq1 = (*r).dmq1; + } + if !iqmp.is_null() { + *iqmp = (*r).iqmp; + } + } + + #[allow(bad_style)] + unsafe fn RSA_set0_key( + r: *mut ffi::RSA, + n: *mut ffi::BIGNUM, + e: *mut ffi::BIGNUM, + d: *mut ffi::BIGNUM, + ) -> c_int { + (*r).n = n; + (*r).e = e; + (*r).d = d; + 1 + } + + #[allow(bad_style)] + unsafe fn RSA_set0_factors( + r: *mut ffi::RSA, + p: *mut ffi::BIGNUM, + q: *mut ffi::BIGNUM, + ) -> c_int { + (*r).p = p; + (*r).q = q; + 1 + } + + #[allow(bad_style)] + unsafe fn RSA_set0_crt_params( + r: *mut ffi::RSA, + dmp1: *mut ffi::BIGNUM, + dmq1: *mut ffi::BIGNUM, + iqmp: *mut ffi::BIGNUM, + ) -> c_int { + (*r).dmp1 = dmp1; + (*r).dmq1 = dmq1; + (*r).iqmp = iqmp; + 1 + } + } +} + +#[cfg(test)] +mod test { + use crate::symm::Cipher; + + use super::*; + + #[test] + fn test_from_password() { + let key = include_bytes!("../test/rsa-encrypted.pem"); + Rsa::private_key_from_pem_passphrase(key, b"mypass").unwrap(); + } + + #[test] + fn test_from_password_callback() { + let mut password_queried = false; + let key = include_bytes!("../test/rsa-encrypted.pem"); + Rsa::private_key_from_pem_callback(key, |password| { + password_queried = true; + password[..6].copy_from_slice(b"mypass"); + Ok(6) + }) + .unwrap(); + + assert!(password_queried); + } + + #[test] + fn test_to_password() { + let key = Rsa::generate(2048).unwrap(); + let pem = key + .private_key_to_pem_passphrase(Cipher::aes_128_cbc(), b"foobar") + .unwrap(); + Rsa::private_key_from_pem_passphrase(&pem, b"foobar").unwrap(); + assert!(Rsa::private_key_from_pem_passphrase(&pem, b"fizzbuzz").is_err()); + } + + #[test] + fn test_public_encrypt_private_decrypt_with_padding() { + let key = include_bytes!("../test/rsa.pem.pub"); + let public_key = Rsa::public_key_from_pem(key).unwrap(); + + let mut result = vec![0; public_key.size() as usize]; + let original_data = b"This is test"; + let len = public_key + .public_encrypt(original_data, &mut result, Padding::PKCS1) + .unwrap(); + assert_eq!(len, 256); + + let pkey = include_bytes!("../test/rsa.pem"); + let private_key = Rsa::private_key_from_pem(pkey).unwrap(); + let mut dec_result = vec![0; private_key.size() as usize]; + let len = private_key + .private_decrypt(&result, &mut dec_result, Padding::PKCS1) + .unwrap(); + + assert_eq!(&dec_result[..len], original_data); + } + + #[test] + fn test_private_encrypt() { + let k0 = super::Rsa::generate(512).unwrap(); + let k0pkey = k0.public_key_to_pem().unwrap(); + let k1 = super::Rsa::public_key_from_pem(&k0pkey).unwrap(); + + let msg = vec![0xdeu8, 0xadu8, 0xd0u8, 0x0du8]; + + let mut emesg = vec![0; k0.size() as usize]; + k0.private_encrypt(&msg, &mut emesg, Padding::PKCS1) + .unwrap(); + let mut dmesg = vec![0; k1.size() as usize]; + let len = k1 + .public_decrypt(&emesg, &mut dmesg, Padding::PKCS1) + .unwrap(); + assert_eq!(msg, &dmesg[..len]); + } + + #[test] + fn test_public_encrypt() { + let k0 = super::Rsa::generate(512).unwrap(); + let k0pkey = k0.private_key_to_pem().unwrap(); + let k1 = super::Rsa::private_key_from_pem(&k0pkey).unwrap(); + + let msg = vec![0xdeu8, 0xadu8, 0xd0u8, 0x0du8]; + + let mut emesg = vec![0; k0.size() as usize]; + k0.public_encrypt(&msg, &mut emesg, Padding::PKCS1).unwrap(); + let mut dmesg = vec![0; k1.size() as usize]; + let len = k1 + .private_decrypt(&emesg, &mut dmesg, Padding::PKCS1) + .unwrap(); + assert_eq!(msg, &dmesg[..len]); + } + + #[test] + fn test_public_key_from_pem_pkcs1() { + let key = include_bytes!("../test/pkcs1.pem.pub"); + Rsa::public_key_from_pem_pkcs1(key).unwrap(); + } + + #[test] + #[should_panic] + fn test_public_key_from_pem_pkcs1_file_panic() { + let key = include_bytes!("../test/key.pem.pub"); + Rsa::public_key_from_pem_pkcs1(key).unwrap(); + } + + #[test] + fn test_public_key_to_pem_pkcs1() { + let keypair = super::Rsa::generate(512).unwrap(); + let pubkey_pem = keypair.public_key_to_pem_pkcs1().unwrap(); + super::Rsa::public_key_from_pem_pkcs1(&pubkey_pem).unwrap(); + } + + #[test] + #[should_panic] + fn test_public_key_from_pem_pkcs1_generate_panic() { + let keypair = super::Rsa::generate(512).unwrap(); + let pubkey_pem = keypair.public_key_to_pem().unwrap(); + super::Rsa::public_key_from_pem_pkcs1(&pubkey_pem).unwrap(); + } + + #[test] + fn test_pem_pkcs1_encrypt() { + let keypair = super::Rsa::generate(2048).unwrap(); + let pubkey_pem = keypair.public_key_to_pem_pkcs1().unwrap(); + let pubkey = super::Rsa::public_key_from_pem_pkcs1(&pubkey_pem).unwrap(); + let msg = b"Hello, world!"; + + let mut encrypted = vec![0; pubkey.size() as usize]; + let len = pubkey + .public_encrypt(msg, &mut encrypted, Padding::PKCS1) + .unwrap(); + assert!(len > msg.len()); + let mut decrypted = vec![0; keypair.size() as usize]; + let len = keypair + .private_decrypt(&encrypted, &mut decrypted, Padding::PKCS1) + .unwrap(); + assert_eq!(len, msg.len()); + assert_eq!(&decrypted[..len], msg); + } + + #[test] + fn test_pem_pkcs1_padding() { + let keypair = super::Rsa::generate(2048).unwrap(); + let pubkey_pem = keypair.public_key_to_pem_pkcs1().unwrap(); + let pubkey = super::Rsa::public_key_from_pem_pkcs1(&pubkey_pem).unwrap(); + let msg = b"foo"; + + let mut encrypted1 = vec![0; pubkey.size() as usize]; + let mut encrypted2 = vec![0; pubkey.size() as usize]; + let len1 = pubkey + .public_encrypt(msg, &mut encrypted1, Padding::PKCS1) + .unwrap(); + let len2 = pubkey + .public_encrypt(msg, &mut encrypted2, Padding::PKCS1) + .unwrap(); + assert!(len1 > (msg.len() + 1)); + assert_eq!(len1, len2); + assert_ne!(encrypted1, encrypted2); + } + + #[test] + #[allow(clippy::redundant_clone)] + fn clone() { + let key = Rsa::generate(2048).unwrap(); + drop(key.clone()); + } + + #[test] + fn generate_with_e() { + let e = BigNum::from_u32(0x10001).unwrap(); + Rsa::generate_with_e(2048, &e).unwrap(); + } +} diff --git a/vendor/openssl/src/sha.rs b/vendor/openssl/src/sha.rs new file mode 100644 index 0000000..2412890 --- /dev/null +++ b/vendor/openssl/src/sha.rs @@ -0,0 +1,463 @@ +//! The SHA family of hashes. +//! +//! SHA, or Secure Hash Algorithms, are a family of cryptographic hashing algorithms published by +//! the National Institute of Standards and Technology (NIST). Hash algorithms such as those in +//! the SHA family are used to map data of an arbitrary size to a fixed-size string of bytes. +//! As cryptographic hashing algorithms, these mappings have the property of being irreversible. +//! This property makes hash algorithms like these excellent for uses such as verifying the +//! contents of a file- if you know the hash you expect beforehand, then you can verify that the +//! data you have is correct if it hashes to the same value. +//! +//! # Examples +//! +//! When dealing with data that becomes available in chunks, such as while buffering data from IO, +//! you can create a hasher that you can repeatedly update to add bytes to. +//! +//! ```rust +//! use openssl::sha; +//! +//! let mut hasher = sha::Sha256::new(); +//! +//! hasher.update(b"Hello, "); +//! hasher.update(b"world"); +//! +//! let hash = hasher.finish(); +//! println!("Hashed \"Hello, world\" to {}", hex::encode(hash)); +//! ``` +//! +//! On the other hand, if you already have access to all of the data you would like to hash, you +//! may prefer to use the slightly simpler method of simply calling the hash function corresponding +//! to the algorithm you want to use. +//! +//! ```rust +//! use openssl::sha::sha256; +//! +//! let hash = sha256(b"your data or message"); +//! println!("Hash = {}", hex::encode(hash)); +//! ``` +use cfg_if::cfg_if; +use libc::c_void; +use openssl_macros::corresponds; +use std::mem::MaybeUninit; + +/// Computes the SHA1 hash of some data. +/// +/// # Warning +/// +/// SHA1 is known to be insecure - it should not be used unless required for +/// compatibility with existing systems. +#[corresponds(SHA1)] +#[inline] +pub fn sha1(data: &[u8]) -> [u8; 20] { + unsafe { + let mut hash = MaybeUninit::<[u8; 20]>::uninit(); + ffi::SHA1(data.as_ptr(), data.len(), hash.as_mut_ptr() as *mut _); + hash.assume_init() + } +} + +/// Computes the SHA224 hash of some data. +#[corresponds(SHA224)] +#[inline] +pub fn sha224(data: &[u8]) -> [u8; 28] { + unsafe { + let mut hash = MaybeUninit::<[u8; 28]>::uninit(); + ffi::SHA224(data.as_ptr(), data.len(), hash.as_mut_ptr() as *mut _); + hash.assume_init() + } +} + +/// Computes the SHA256 hash of some data. +#[corresponds(SHA256)] +#[inline] +pub fn sha256(data: &[u8]) -> [u8; 32] { + unsafe { + let mut hash = MaybeUninit::<[u8; 32]>::uninit(); + ffi::SHA256(data.as_ptr(), data.len(), hash.as_mut_ptr() as *mut _); + hash.assume_init() + } +} + +/// Computes the SHA384 hash of some data. +#[corresponds(SHA384)] +#[inline] +pub fn sha384(data: &[u8]) -> [u8; 48] { + unsafe { + let mut hash = MaybeUninit::<[u8; 48]>::uninit(); + ffi::SHA384(data.as_ptr(), data.len(), hash.as_mut_ptr() as *mut _); + hash.assume_init() + } +} + +/// Computes the SHA512 hash of some data. +#[corresponds(SHA512)] +#[inline] +pub fn sha512(data: &[u8]) -> [u8; 64] { + unsafe { + let mut hash = MaybeUninit::<[u8; 64]>::uninit(); + ffi::SHA512(data.as_ptr(), data.len(), hash.as_mut_ptr() as *mut _); + hash.assume_init() + } +} + +cfg_if! { + if #[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] { + /// An object which calculates a SHA1 hash of some data. + /// + /// # Warning + /// + /// SHA1 is known to be insecure - it should not be used unless required for + /// compatibility with existing systems. + #[derive(Clone)] + pub struct Sha1(ffi::SHA_CTX); + + impl Default for Sha1 { + #[inline] + fn default() -> Sha1 { + Sha1::new() + } + } + + impl Sha1 { + /// Creates a new hasher. + #[corresponds(SHA1_Init)] + #[inline] + pub fn new() -> Sha1 { + unsafe { + let mut ctx = MaybeUninit::uninit(); + ffi::SHA1_Init( ctx.as_mut_ptr()); + Sha1(ctx.assume_init()) + } + } + + /// Feeds some data into the hasher. + /// + /// This can be called multiple times. + #[corresponds(SHA1_Update)] + #[inline] + pub fn update(&mut self, buf: &[u8]) { + unsafe { + ffi::SHA1_Update(&mut self.0, buf.as_ptr() as *const c_void, buf.len()); + } + } + + /// Returns the hash of the data. + #[corresponds(SHA1_Final)] + #[inline] + pub fn finish(mut self) -> [u8; 20] { + unsafe { + let mut hash = MaybeUninit::<[u8; 20]>::uninit(); + ffi::SHA1_Final(hash.as_mut_ptr() as *mut _, &mut self.0); + hash.assume_init() + } + } + } + + /// An object which calculates a SHA224 hash of some data. + #[derive(Clone)] + pub struct Sha224(ffi::SHA256_CTX); + + impl Default for Sha224 { + #[inline] + fn default() -> Sha224 { + Sha224::new() + } + } + + impl Sha224 { + /// Creates a new hasher. + #[corresponds(SHA224_Init)] + #[inline] + pub fn new() -> Sha224 { + unsafe { + let mut ctx = MaybeUninit::uninit(); + ffi::SHA224_Init(ctx.as_mut_ptr()); + Sha224(ctx.assume_init()) + } + } + + /// Feeds some data into the hasher. + /// + /// This can be called multiple times. + #[corresponds(SHA224_Update)] + #[inline] + pub fn update(&mut self, buf: &[u8]) { + unsafe { + ffi::SHA224_Update(&mut self.0, buf.as_ptr() as *const c_void, buf.len()); + } + } + + /// Returns the hash of the data. + #[corresponds(SHA224_Final)] + #[inline] + pub fn finish(mut self) -> [u8; 28] { + unsafe { + let mut hash = MaybeUninit::<[u8; 28]>::uninit(); + ffi::SHA224_Final(hash.as_mut_ptr() as *mut _, &mut self.0); + hash.assume_init() + } + } + } + + /// An object which calculates a SHA256 hash of some data. + #[derive(Clone)] + pub struct Sha256(ffi::SHA256_CTX); + + impl Default for Sha256 { + #[inline] + fn default() -> Sha256 { + Sha256::new() + } + } + + impl Sha256 { + /// Creates a new hasher. + #[corresponds(SHA256_Init)] + #[inline] + pub fn new() -> Sha256 { + unsafe { + let mut ctx = MaybeUninit::uninit(); + ffi::SHA256_Init(ctx.as_mut_ptr()); + Sha256(ctx.assume_init()) + } + } + + /// Feeds some data into the hasher. + /// + /// This can be called multiple times. + #[corresponds(SHA256_Update)] + #[inline] + pub fn update(&mut self, buf: &[u8]) { + unsafe { + ffi::SHA256_Update(&mut self.0, buf.as_ptr() as *const c_void, buf.len()); + } + } + + /// Returns the hash of the data. + #[corresponds(SHA256_Final)] + #[inline] + pub fn finish(mut self) -> [u8; 32] { + unsafe { + let mut hash = MaybeUninit::<[u8; 32]>::uninit(); + ffi::SHA256_Final(hash.as_mut_ptr() as *mut _, &mut self.0); + hash.assume_init() + } + } + } + + /// An object which calculates a SHA384 hash of some data. + #[derive(Clone)] + pub struct Sha384(ffi::SHA512_CTX); + + impl Default for Sha384 { + #[inline] + fn default() -> Sha384 { + Sha384::new() + } + } + + impl Sha384 { + /// Creates a new hasher. + #[corresponds(SHA384_Init)] + #[inline] + pub fn new() -> Sha384 { + unsafe { + let mut ctx = MaybeUninit::uninit(); + ffi::SHA384_Init(ctx.as_mut_ptr()); + Sha384(ctx.assume_init()) + } + } + + /// Feeds some data into the hasher. + /// + /// This can be called multiple times. + #[corresponds(SHA384_Update)] + #[inline] + pub fn update(&mut self, buf: &[u8]) { + unsafe { + ffi::SHA384_Update(&mut self.0, buf.as_ptr() as *const c_void, buf.len()); + } + } + + /// Returns the hash of the data. + #[corresponds(SHA384_Final)] + #[inline] + pub fn finish(mut self) -> [u8; 48] { + unsafe { + let mut hash = MaybeUninit::<[u8; 48]>::uninit(); + ffi::SHA384_Final(hash.as_mut_ptr() as *mut _, &mut self.0); + hash.assume_init() + } + } + } + + /// An object which calculates a SHA512 hash of some data. + #[derive(Clone)] + pub struct Sha512(ffi::SHA512_CTX); + + impl Default for Sha512 { + #[inline] + fn default() -> Sha512 { + Sha512::new() + } + } + + impl Sha512 { + /// Creates a new hasher. + #[corresponds(SHA512_Init)] + #[inline] + pub fn new() -> Sha512 { + unsafe { + let mut ctx = MaybeUninit::uninit(); + ffi::SHA512_Init(ctx.as_mut_ptr()); + Sha512(ctx.assume_init()) + } + } + + /// Feeds some data into the hasher. + /// + /// This can be called multiple times. + #[corresponds(SHA512_Update)] + #[inline] + pub fn update(&mut self, buf: &[u8]) { + unsafe { + ffi::SHA512_Update(&mut self.0, buf.as_ptr() as *const c_void, buf.len()); + } + } + + /// Returns the hash of the data. + #[corresponds(SHA512_Final)] + #[inline] + pub fn finish(mut self) -> [u8; 64] { + unsafe { + let mut hash= MaybeUninit::<[u8; 64]>::uninit(); + ffi::SHA512_Final(hash.as_mut_ptr() as *mut _, &mut self.0); + hash.assume_init() + } + } + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn standalone_1() { + let data = b"abc"; + let expected = "a9993e364706816aba3e25717850c26c9cd0d89d"; + + assert_eq!(hex::encode(sha1(data)), expected); + } + + #[test] + #[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] + fn struct_1() { + let expected = "a9993e364706816aba3e25717850c26c9cd0d89d"; + + let mut hasher = Sha1::new(); + hasher.update(b"a"); + hasher.update(b"bc"); + assert_eq!(hex::encode(hasher.finish()), expected); + } + + #[test] + #[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] + fn cloning_allows_incremental_hashing() { + let expected = "a9993e364706816aba3e25717850c26c9cd0d89d"; + + let mut hasher = Sha1::new(); + hasher.update(b"a"); + + let mut incr_hasher = hasher.clone(); + incr_hasher.update(b"bc"); + + assert_eq!(hex::encode(incr_hasher.finish()), expected); + assert_ne!(hex::encode(hasher.finish()), expected); + } + + #[test] + fn standalone_224() { + let data = b"abc"; + let expected = "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7"; + + assert_eq!(hex::encode(sha224(data)), expected); + } + + #[test] + #[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] + fn struct_224() { + let expected = "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7"; + + let mut hasher = Sha224::new(); + hasher.update(b"a"); + hasher.update(b"bc"); + assert_eq!(hex::encode(hasher.finish()), expected); + } + + #[test] + fn standalone_256() { + let data = b"abc"; + let expected = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"; + + assert_eq!(hex::encode(sha256(data)), expected); + } + + #[test] + #[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] + fn struct_256() { + let expected = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"; + + let mut hasher = Sha256::new(); + hasher.update(b"a"); + hasher.update(b"bc"); + assert_eq!(hex::encode(hasher.finish()), expected); + } + + #[test] + fn standalone_384() { + let data = b"abc"; + let expected = + "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e\ + 7cc2358baeca134c825a7"; + + assert_eq!(hex::encode(&sha384(data)[..]), expected); + } + + #[test] + #[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] + fn struct_384() { + let expected = + "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e\ + 7cc2358baeca134c825a7"; + + let mut hasher = Sha384::new(); + hasher.update(b"a"); + hasher.update(b"bc"); + assert_eq!(hex::encode(&hasher.finish()[..]), expected); + } + + #[test] + fn standalone_512() { + let data = b"abc"; + let expected = + "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274\ + fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"; + + assert_eq!(hex::encode(&sha512(data)[..]), expected); + } + + #[test] + #[cfg(not(osslconf = "OPENSSL_NO_DEPRECATED_3_0"))] + fn struct_512() { + let expected = + "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274\ + fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"; + + let mut hasher = Sha512::new(); + hasher.update(b"a"); + hasher.update(b"bc"); + assert_eq!(hex::encode(&hasher.finish()[..]), expected); + } +} diff --git a/vendor/openssl/src/sign.rs b/vendor/openssl/src/sign.rs new file mode 100644 index 0000000..1c770d1 --- /dev/null +++ b/vendor/openssl/src/sign.rs @@ -0,0 +1,886 @@ +//! Message signatures. +//! +//! The `Signer` allows for the computation of cryptographic signatures of +//! data given a private key. The `Verifier` can then be used with the +//! corresponding public key to verify the integrity and authenticity of that +//! data given the signature. +//! +//! # Examples +//! +//! Sign and verify data given an RSA keypair: +//! +//! ```rust +//! use openssl::sign::{Signer, Verifier}; +//! use openssl::rsa::Rsa; +//! use openssl::pkey::PKey; +//! use openssl::hash::MessageDigest; +//! +//! // Generate a keypair +//! let keypair = Rsa::generate(2048).unwrap(); +//! let keypair = PKey::from_rsa(keypair).unwrap(); +//! +//! let data = b"hello, world!"; +//! let data2 = b"hola, mundo!"; +//! +//! // Sign the data +//! let mut signer = Signer::new(MessageDigest::sha256(), &keypair).unwrap(); +//! signer.update(data).unwrap(); +//! signer.update(data2).unwrap(); +//! let signature = signer.sign_to_vec().unwrap(); +//! +//! // Verify the data +//! let mut verifier = Verifier::new(MessageDigest::sha256(), &keypair).unwrap(); +//! verifier.update(data).unwrap(); +//! verifier.update(data2).unwrap(); +//! assert!(verifier.verify(&signature).unwrap()); +//! ``` + +#![cfg_attr( + not(boringssl), + doc = r#"\ + +Compute an HMAC: + +```rust +use openssl::hash::MessageDigest; +use openssl::memcmp; +use openssl::pkey::PKey; +use openssl::sign::Signer; + +// Create a PKey +let key = PKey::hmac(b"my secret").unwrap(); + +let data = b"hello, world!"; +let data2 = b"hola, mundo!"; + +// Compute the HMAC +let mut signer = Signer::new(MessageDigest::sha256(), &key).unwrap(); +signer.update(data).unwrap(); +signer.update(data2).unwrap(); +let hmac = signer.sign_to_vec().unwrap(); + +// `Verifier` cannot be used with HMACs; use the `memcmp::eq` function instead +// +// Do not simply check for equality with `==`! +# let target = hmac.clone(); +assert!(memcmp::eq(&hmac, &target)); +```"# +)] + +use cfg_if::cfg_if; +use foreign_types::ForeignTypeRef; +use libc::c_int; +use std::io::{self, Write}; +use std::marker::PhantomData; +use std::ptr; + +use crate::error::ErrorStack; +use crate::hash::MessageDigest; +use crate::pkey::{HasPrivate, HasPublic, PKeyRef}; +use crate::rsa::Padding; +use crate::{cvt, cvt_p}; + +cfg_if! { + if #[cfg(ossl110)] { + use ffi::{EVP_MD_CTX_free, EVP_MD_CTX_new}; + } else { + use ffi::{EVP_MD_CTX_create as EVP_MD_CTX_new, EVP_MD_CTX_destroy as EVP_MD_CTX_free}; + } +} + +/// Salt lengths that must be used with `set_rsa_pss_saltlen`. +pub struct RsaPssSaltlen(c_int); + +impl RsaPssSaltlen { + /// Returns the integer representation of `RsaPssSaltlen`. + pub(crate) fn as_raw(&self) -> c_int { + self.0 + } + + /// Sets the salt length to the given value. + pub fn custom(val: c_int) -> RsaPssSaltlen { + RsaPssSaltlen(val) + } + + /// The salt length is set to the digest length. + /// Corresponds to the special value `-1`. + pub const DIGEST_LENGTH: RsaPssSaltlen = RsaPssSaltlen(-1); + /// The salt length is set to the maximum permissible value. + /// Corresponds to the special value `-2`. + pub const MAXIMUM_LENGTH: RsaPssSaltlen = RsaPssSaltlen(-2); +} + +/// A type which computes cryptographic signatures of data. +pub struct Signer<'a> { + md_ctx: *mut ffi::EVP_MD_CTX, + pctx: *mut ffi::EVP_PKEY_CTX, + _p: PhantomData<&'a ()>, +} + +unsafe impl Sync for Signer<'_> {} +unsafe impl Send for Signer<'_> {} + +impl Drop for Signer<'_> { + fn drop(&mut self) { + // pkey_ctx is owned by the md_ctx, so no need to explicitly free it. + unsafe { + EVP_MD_CTX_free(self.md_ctx); + } + } +} + +#[allow(clippy::len_without_is_empty)] +impl Signer<'_> { + /// Creates a new `Signer`. + /// + /// This cannot be used with Ed25519 or Ed448 keys. Please refer to + /// `new_without_digest`. + /// + /// OpenSSL documentation at [`EVP_DigestSignInit`]. + /// + /// [`EVP_DigestSignInit`]: https://www.openssl.org/docs/manmaster/man3/EVP_DigestSignInit.html + pub fn new<'a, T>(type_: MessageDigest, pkey: &PKeyRef<T>) -> Result<Signer<'a>, ErrorStack> + where + T: HasPrivate, + { + Self::new_intern(Some(type_), pkey) + } + + /// Creates a new `Signer` without a digest. + /// + /// This is the only way to create a `Verifier` for Ed25519 or Ed448 keys. + /// It can also be used to create a CMAC. + /// + /// OpenSSL documentation at [`EVP_DigestSignInit`]. + /// + /// [`EVP_DigestSignInit`]: https://www.openssl.org/docs/manmaster/man3/EVP_DigestSignInit.html + pub fn new_without_digest<'a, T>(pkey: &PKeyRef<T>) -> Result<Signer<'a>, ErrorStack> + where + T: HasPrivate, + { + Self::new_intern(None, pkey) + } + + fn new_intern<'a, T>( + type_: Option<MessageDigest>, + pkey: &PKeyRef<T>, + ) -> Result<Signer<'a>, ErrorStack> + where + T: HasPrivate, + { + unsafe { + ffi::init(); + + let ctx = cvt_p(EVP_MD_CTX_new())?; + let mut pctx: *mut ffi::EVP_PKEY_CTX = ptr::null_mut(); + let r = ffi::EVP_DigestSignInit( + ctx, + &mut pctx, + type_.map(|t| t.as_ptr()).unwrap_or(ptr::null()), + ptr::null_mut(), + pkey.as_ptr(), + ); + if r != 1 { + EVP_MD_CTX_free(ctx); + return Err(ErrorStack::get()); + } + + assert!(!pctx.is_null()); + + Ok(Signer { + md_ctx: ctx, + pctx, + _p: PhantomData, + }) + } + } + + /// Returns the RSA padding mode in use. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to `EVP_PKEY_CTX_get_rsa_padding`. + pub fn rsa_padding(&self) -> Result<Padding, ErrorStack> { + unsafe { + let mut pad = 0; + cvt(ffi::EVP_PKEY_CTX_get_rsa_padding(self.pctx, &mut pad)) + .map(|_| Padding::from_raw(pad)) + } + } + + /// Sets the RSA padding mode. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to [`EVP_PKEY_CTX_set_rsa_padding`]. + /// + /// [`EVP_PKEY_CTX_set_rsa_padding`]: https://www.openssl.org/docs/manmaster/crypto/EVP_PKEY_CTX_set_rsa_padding.html + pub fn set_rsa_padding(&mut self, padding: Padding) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_rsa_padding( + self.pctx, + padding.as_raw(), + )) + .map(|_| ()) + } + } + + /// Sets the RSA PSS salt length. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to [`EVP_PKEY_CTX_set_rsa_pss_saltlen`]. + /// + /// [`EVP_PKEY_CTX_set_rsa_pss_saltlen`]: https://www.openssl.org/docs/manmaster/crypto/EVP_PKEY_CTX_set_rsa_pss_saltlen.html + pub fn set_rsa_pss_saltlen(&mut self, len: RsaPssSaltlen) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_rsa_pss_saltlen( + self.pctx, + len.as_raw(), + )) + .map(|_| ()) + } + } + + /// Sets the RSA MGF1 algorithm. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to [`EVP_PKEY_CTX_set_rsa_mgf1_md`]. + /// + /// [`EVP_PKEY_CTX_set_rsa_mgf1_md`]: https://www.openssl.org/docs/manmaster/man7/RSA-PSS.html + pub fn set_rsa_mgf1_md(&mut self, md: MessageDigest) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_rsa_mgf1_md( + self.pctx, + md.as_ptr() as *mut _, + )) + .map(|_| ()) + } + } + + /// Feeds more data into the `Signer`. + /// + /// Please note that PureEdDSA (Ed25519 and Ed448 keys) do not support streaming. + /// Use `sign_oneshot` instead. + /// + /// OpenSSL documentation at [`EVP_DigestUpdate`]. + /// + /// [`EVP_DigestUpdate`]: https://www.openssl.org/docs/manmaster/man3/EVP_DigestInit.html + pub fn update(&mut self, buf: &[u8]) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_DigestUpdate( + self.md_ctx, + buf.as_ptr() as *const _, + buf.len(), + )) + .map(|_| ()) + } + } + + /// Computes an upper bound on the signature length. + /// + /// The actual signature may be shorter than this value. Check the return value of + /// `sign` to get the exact length. + /// + /// OpenSSL documentation at [`EVP_DigestSignFinal`]. + /// + /// [`EVP_DigestSignFinal`]: https://www.openssl.org/docs/manmaster/crypto/EVP_DigestSignFinal.html + pub fn len(&self) -> Result<usize, ErrorStack> { + self.len_intern() + } + + #[cfg(all(not(ossl111), not(boringssl), not(libressl370)))] + fn len_intern(&self) -> Result<usize, ErrorStack> { + unsafe { + let mut len = 0; + cvt(ffi::EVP_DigestSignFinal( + self.md_ctx, + ptr::null_mut(), + &mut len, + ))?; + Ok(len) + } + } + + #[cfg(any(ossl111, boringssl, libressl370))] + fn len_intern(&self) -> Result<usize, ErrorStack> { + unsafe { + let mut len = 0; + cvt(ffi::EVP_DigestSign( + self.md_ctx, + ptr::null_mut(), + &mut len, + ptr::null(), + 0, + ))?; + Ok(len) + } + } + + /// Writes the signature into the provided buffer, returning the number of bytes written. + /// + /// This method will fail if the buffer is not large enough for the signature. Use the `len` + /// method to get an upper bound on the required size. + /// + /// OpenSSL documentation at [`EVP_DigestSignFinal`]. + /// + /// [`EVP_DigestSignFinal`]: https://www.openssl.org/docs/manmaster/crypto/EVP_DigestSignFinal.html + pub fn sign(&self, buf: &mut [u8]) -> Result<usize, ErrorStack> { + unsafe { + let mut len = buf.len(); + cvt(ffi::EVP_DigestSignFinal( + self.md_ctx, + buf.as_mut_ptr() as *mut _, + &mut len, + ))?; + Ok(len) + } + } + + /// Returns the signature. + /// + /// This is a simple convenience wrapper over `len` and `sign`. + pub fn sign_to_vec(&self) -> Result<Vec<u8>, ErrorStack> { + let mut buf = vec![0; self.len()?]; + let len = self.sign(&mut buf)?; + // The advertised length is not always equal to the real length for things like DSA + buf.truncate(len); + Ok(buf) + } + + /// Signs the data in `data_buf` and writes the signature into the buffer `sig_buf`, returning the + /// number of bytes written. + /// + /// For PureEdDSA (Ed25519 and Ed448 keys), this is the only way to sign data. + /// + /// This method will fail if the buffer is not large enough for the signature. Use the `len` + /// method to get an upper bound on the required size. + /// + /// OpenSSL documentation at [`EVP_DigestSign`]. + /// + /// [`EVP_DigestSign`]: https://www.openssl.org/docs/man1.1.1/man3/EVP_DigestSign.html + #[cfg(any(ossl111, boringssl, libressl370))] + pub fn sign_oneshot( + &mut self, + sig_buf: &mut [u8], + data_buf: &[u8], + ) -> Result<usize, ErrorStack> { + unsafe { + let mut sig_len = sig_buf.len(); + cvt(ffi::EVP_DigestSign( + self.md_ctx, + sig_buf.as_mut_ptr() as *mut _, + &mut sig_len, + data_buf.as_ptr() as *const _, + data_buf.len(), + ))?; + Ok(sig_len) + } + } + + /// Returns the signature. + /// + /// This is a simple convenience wrapper over `len` and `sign_oneshot`. + #[cfg(any(ossl111, boringssl, libressl370))] + pub fn sign_oneshot_to_vec(&mut self, data_buf: &[u8]) -> Result<Vec<u8>, ErrorStack> { + let mut sig_buf = vec![0; self.len()?]; + let len = self.sign_oneshot(&mut sig_buf, data_buf)?; + // The advertised length is not always equal to the real length for things like DSA + sig_buf.truncate(len); + Ok(sig_buf) + } +} + +impl<'a> Write for Signer<'a> { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.update(buf)?; + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +/// A type which can be used to verify the integrity and authenticity +/// of data given the signature. +pub struct Verifier<'a> { + md_ctx: *mut ffi::EVP_MD_CTX, + pctx: *mut ffi::EVP_PKEY_CTX, + pkey_pd: PhantomData<&'a ()>, +} + +unsafe impl<'a> Sync for Verifier<'a> {} +unsafe impl<'a> Send for Verifier<'a> {} + +impl<'a> Drop for Verifier<'a> { + fn drop(&mut self) { + // pkey_ctx is owned by the md_ctx, so no need to explicitly free it. + unsafe { + EVP_MD_CTX_free(self.md_ctx); + } + } +} + +/// A type which verifies cryptographic signatures of data. +impl<'a> Verifier<'a> { + /// Creates a new `Verifier`. + /// + /// This cannot be used with Ed25519 or Ed448 keys. Please refer to + /// [`Verifier::new_without_digest`]. + /// + /// OpenSSL documentation at [`EVP_DigestVerifyInit`]. + /// + /// [`EVP_DigestVerifyInit`]: https://www.openssl.org/docs/manmaster/man3/EVP_DigestVerifyInit.html + pub fn new<T>(type_: MessageDigest, pkey: &'a PKeyRef<T>) -> Result<Verifier<'a>, ErrorStack> + where + T: HasPublic, + { + Verifier::new_intern(Some(type_), pkey) + } + + /// Creates a new `Verifier` without a digest. + /// + /// This is the only way to create a `Verifier` for Ed25519 or Ed448 keys. + /// + /// OpenSSL documentation at [`EVP_DigestVerifyInit`]. + /// + /// [`EVP_DigestVerifyInit`]: https://www.openssl.org/docs/manmaster/man3/EVP_DigestVerifyInit.html + pub fn new_without_digest<T>(pkey: &'a PKeyRef<T>) -> Result<Verifier<'a>, ErrorStack> + where + T: HasPublic, + { + Verifier::new_intern(None, pkey) + } + + fn new_intern<T>( + type_: Option<MessageDigest>, + pkey: &'a PKeyRef<T>, + ) -> Result<Verifier<'a>, ErrorStack> + where + T: HasPublic, + { + unsafe { + ffi::init(); + + let ctx = cvt_p(EVP_MD_CTX_new())?; + let mut pctx: *mut ffi::EVP_PKEY_CTX = ptr::null_mut(); + let r = ffi::EVP_DigestVerifyInit( + ctx, + &mut pctx, + type_.map(|t| t.as_ptr()).unwrap_or(ptr::null()), + ptr::null_mut(), + pkey.as_ptr(), + ); + if r != 1 { + EVP_MD_CTX_free(ctx); + return Err(ErrorStack::get()); + } + + assert!(!pctx.is_null()); + + Ok(Verifier { + md_ctx: ctx, + pctx, + pkey_pd: PhantomData, + }) + } + } + + /// Returns the RSA padding mode in use. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to `EVP_PKEY_CTX_get_rsa_padding`. + pub fn rsa_padding(&self) -> Result<Padding, ErrorStack> { + unsafe { + let mut pad = 0; + cvt(ffi::EVP_PKEY_CTX_get_rsa_padding(self.pctx, &mut pad)) + .map(|_| Padding::from_raw(pad)) + } + } + + /// Sets the RSA padding mode. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to [`EVP_PKEY_CTX_set_rsa_padding`]. + /// + /// [`EVP_PKEY_CTX_set_rsa_padding`]: https://www.openssl.org/docs/manmaster/crypto/EVP_PKEY_CTX_set_rsa_padding.html + pub fn set_rsa_padding(&mut self, padding: Padding) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_rsa_padding( + self.pctx, + padding.as_raw(), + )) + .map(|_| ()) + } + } + + /// Sets the RSA PSS salt length. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to [`EVP_PKEY_CTX_set_rsa_pss_saltlen`]. + /// + /// [`EVP_PKEY_CTX_set_rsa_pss_saltlen`]: https://www.openssl.org/docs/manmaster/crypto/EVP_PKEY_CTX_set_rsa_pss_saltlen.html + pub fn set_rsa_pss_saltlen(&mut self, len: RsaPssSaltlen) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_rsa_pss_saltlen( + self.pctx, + len.as_raw(), + )) + .map(|_| ()) + } + } + + /// Sets the RSA MGF1 algorithm. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to [`EVP_PKEY_CTX_set_rsa_mgf1_md`]. + /// + /// [`EVP_PKEY_CTX_set_rsa_mgf1_md`]: https://www.openssl.org/docs/manmaster/man7/RSA-PSS.html + pub fn set_rsa_mgf1_md(&mut self, md: MessageDigest) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_rsa_mgf1_md( + self.pctx, + md.as_ptr() as *mut _, + )) + .map(|_| ()) + } + } + + /// Feeds more data into the `Verifier`. + /// + /// Please note that PureEdDSA (Ed25519 and Ed448 keys) do not support streaming. + /// Use [`Verifier::verify_oneshot`] instead. + /// + /// OpenSSL documentation at [`EVP_DigestUpdate`]. + /// + /// [`EVP_DigestUpdate`]: https://www.openssl.org/docs/manmaster/man3/EVP_DigestInit.html + pub fn update(&mut self, buf: &[u8]) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_DigestUpdate( + self.md_ctx, + buf.as_ptr() as *const _, + buf.len(), + )) + .map(|_| ()) + } + } + + /// Determines if the data fed into the `Verifier` matches the provided signature. + /// + /// OpenSSL documentation at [`EVP_DigestVerifyFinal`]. + /// + /// [`EVP_DigestVerifyFinal`]: https://www.openssl.org/docs/manmaster/man3/EVP_DigestVerifyFinal.html + pub fn verify(&self, signature: &[u8]) -> Result<bool, ErrorStack> { + unsafe { + let r = + EVP_DigestVerifyFinal(self.md_ctx, signature.as_ptr() as *mut _, signature.len()); + match r { + 1 => Ok(true), + 0 => { + ErrorStack::get(); // discard error stack + Ok(false) + } + _ => Err(ErrorStack::get()), + } + } + } + + /// Determines if the data given in `buf` matches the provided signature. + /// + /// OpenSSL documentation at [`EVP_DigestVerify`]. + /// + /// [`EVP_DigestVerify`]: https://www.openssl.org/docs/man1.1.1/man3/EVP_DigestVerify.html + #[cfg(any(ossl111, boringssl, libressl370))] + pub fn verify_oneshot(&mut self, signature: &[u8], buf: &[u8]) -> Result<bool, ErrorStack> { + unsafe { + let r = ffi::EVP_DigestVerify( + self.md_ctx, + signature.as_ptr() as *const _, + signature.len(), + buf.as_ptr() as *const _, + buf.len(), + ); + match r { + 1 => Ok(true), + 0 => { + ErrorStack::get(); + Ok(false) + } + _ => Err(ErrorStack::get()), + } + } + } +} + +impl<'a> Write for Verifier<'a> { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.update(buf)?; + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[cfg(not(ossl101))] +use ffi::EVP_DigestVerifyFinal; + +#[cfg(ossl101)] +#[allow(bad_style)] +unsafe fn EVP_DigestVerifyFinal( + ctx: *mut ffi::EVP_MD_CTX, + sigret: *const ::libc::c_uchar, + siglen: ::libc::size_t, +) -> ::libc::c_int { + ffi::EVP_DigestVerifyFinal(ctx, sigret as *mut _, siglen) +} + +#[cfg(test)] +mod test { + use hex::{self, FromHex}; + #[cfg(not(boringssl))] + use std::iter; + + use crate::ec::{EcGroup, EcKey}; + use crate::hash::MessageDigest; + use crate::nid::Nid; + use crate::pkey::PKey; + use crate::rsa::{Padding, Rsa}; + #[cfg(ossl111)] + use crate::sign::RsaPssSaltlen; + use crate::sign::{Signer, Verifier}; + + const INPUT: &str = + "65794a68624763694f694a53557a49314e694a392e65794a7063334d694f694a71623255694c41304b49434a6c\ + 654841694f6a457a4d4441344d546b7a4f44417344516f67496d6830644841364c79396c654746746347786c4c\ + 6d4e76625339706331397962323930496a7030636e566c6651"; + + const SIGNATURE: &str = + "702e218943e88fd11eb5d82dbf7845f34106ae1b81fff7731116add1717d83656d420afd3c96eedd73a2663e51\ + 66687b000b87226e0187ed1073f945e582adfcef16d85a798ee8c66ddb3db8975b17d09402beedd5d9d9700710\ + 8db28160d5f8040ca7445762b81fbe7ff9d92e0ae76f24f25b33bbe6f44ae61eb1040acb20044d3ef9128ed401\ + 30795bd4bd3b41eecad066ab651981fde48df77f372dc38b9fafdd3befb18b5da3cc3c2eb02f9e3a41d612caad\ + 15911273a05f23b9e838faaf849d698429ef5a1e88798236c3d40e604522a544c8f27a7a2db80663d16cf7caea\ + 56de405cb2215a45b2c25566b55ac1a748a070dfc8a32a469543d019eefb47"; + + #[test] + fn rsa_sign() { + let key = include_bytes!("../test/rsa.pem"); + let private_key = Rsa::private_key_from_pem(key).unwrap(); + let pkey = PKey::from_rsa(private_key).unwrap(); + + let mut signer = Signer::new(MessageDigest::sha256(), &pkey).unwrap(); + assert_eq!(signer.rsa_padding().unwrap(), Padding::PKCS1); + signer.set_rsa_padding(Padding::PKCS1).unwrap(); + signer.update(&Vec::from_hex(INPUT).unwrap()).unwrap(); + let result = signer.sign_to_vec().unwrap(); + + assert_eq!(hex::encode(result), SIGNATURE); + } + + #[test] + fn rsa_verify_ok() { + let key = include_bytes!("../test/rsa.pem"); + let private_key = Rsa::private_key_from_pem(key).unwrap(); + let pkey = PKey::from_rsa(private_key).unwrap(); + + let mut verifier = Verifier::new(MessageDigest::sha256(), &pkey).unwrap(); + assert_eq!(verifier.rsa_padding().unwrap(), Padding::PKCS1); + verifier.update(&Vec::from_hex(INPUT).unwrap()).unwrap(); + assert!(verifier.verify(&Vec::from_hex(SIGNATURE).unwrap()).unwrap()); + } + + #[test] + fn rsa_verify_invalid() { + let key = include_bytes!("../test/rsa.pem"); + let private_key = Rsa::private_key_from_pem(key).unwrap(); + let pkey = PKey::from_rsa(private_key).unwrap(); + + let mut verifier = Verifier::new(MessageDigest::sha256(), &pkey).unwrap(); + verifier.update(&Vec::from_hex(INPUT).unwrap()).unwrap(); + verifier.update(b"foobar").unwrap(); + assert!(!verifier.verify(&Vec::from_hex(SIGNATURE).unwrap()).unwrap()); + } + + #[cfg(not(boringssl))] + fn test_hmac(ty: MessageDigest, tests: &[(Vec<u8>, Vec<u8>, Vec<u8>)]) { + for (key, data, res) in tests.iter() { + let pkey = PKey::hmac(key).unwrap(); + let mut signer = Signer::new(ty, &pkey).unwrap(); + signer.update(data).unwrap(); + assert_eq!(signer.sign_to_vec().unwrap(), *res); + } + } + + #[test] + #[cfg(not(boringssl))] + fn hmac_md5() { + // test vectors from RFC 2202 + let tests: [(Vec<u8>, Vec<u8>, Vec<u8>); 7] = [ + ( + iter::repeat(0x0b_u8).take(16).collect(), + b"Hi There".to_vec(), + Vec::from_hex("9294727a3638bb1c13f48ef8158bfc9d").unwrap(), + ), + ( + b"Jefe".to_vec(), + b"what do ya want for nothing?".to_vec(), + Vec::from_hex("750c783e6ab0b503eaa86e310a5db738").unwrap(), + ), + ( + iter::repeat(0xaa_u8).take(16).collect(), + iter::repeat(0xdd_u8).take(50).collect(), + Vec::from_hex("56be34521d144c88dbb8c733f0e8b3f6").unwrap(), + ), + ( + Vec::from_hex("0102030405060708090a0b0c0d0e0f10111213141516171819").unwrap(), + iter::repeat(0xcd_u8).take(50).collect(), + Vec::from_hex("697eaf0aca3a3aea3a75164746ffaa79").unwrap(), + ), + ( + iter::repeat(0x0c_u8).take(16).collect(), + b"Test With Truncation".to_vec(), + Vec::from_hex("56461ef2342edc00f9bab995690efd4c").unwrap(), + ), + ( + iter::repeat(0xaa_u8).take(80).collect(), + b"Test Using Larger Than Block-Size Key - Hash Key First".to_vec(), + Vec::from_hex("6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd").unwrap(), + ), + ( + iter::repeat(0xaa_u8).take(80).collect(), + b"Test Using Larger Than Block-Size Key \ + and Larger Than One Block-Size Data" + .to_vec(), + Vec::from_hex("6f630fad67cda0ee1fb1f562db3aa53e").unwrap(), + ), + ]; + + test_hmac(MessageDigest::md5(), &tests); + } + + #[test] + #[cfg(not(boringssl))] + fn hmac_sha1() { + // test vectors from RFC 2202 + let tests: [(Vec<u8>, Vec<u8>, Vec<u8>); 7] = [ + ( + iter::repeat(0x0b_u8).take(20).collect(), + b"Hi There".to_vec(), + Vec::from_hex("b617318655057264e28bc0b6fb378c8ef146be00").unwrap(), + ), + ( + b"Jefe".to_vec(), + b"what do ya want for nothing?".to_vec(), + Vec::from_hex("effcdf6ae5eb2fa2d27416d5f184df9c259a7c79").unwrap(), + ), + ( + iter::repeat(0xaa_u8).take(20).collect(), + iter::repeat(0xdd_u8).take(50).collect(), + Vec::from_hex("125d7342b9ac11cd91a39af48aa17b4f63f175d3").unwrap(), + ), + ( + Vec::from_hex("0102030405060708090a0b0c0d0e0f10111213141516171819").unwrap(), + iter::repeat(0xcd_u8).take(50).collect(), + Vec::from_hex("4c9007f4026250c6bc8414f9bf50c86c2d7235da").unwrap(), + ), + ( + iter::repeat(0x0c_u8).take(20).collect(), + b"Test With Truncation".to_vec(), + Vec::from_hex("4c1a03424b55e07fe7f27be1d58bb9324a9a5a04").unwrap(), + ), + ( + iter::repeat(0xaa_u8).take(80).collect(), + b"Test Using Larger Than Block-Size Key - Hash Key First".to_vec(), + Vec::from_hex("aa4ae5e15272d00e95705637ce8a3b55ed402112").unwrap(), + ), + ( + iter::repeat(0xaa_u8).take(80).collect(), + b"Test Using Larger Than Block-Size Key \ + and Larger Than One Block-Size Data" + .to_vec(), + Vec::from_hex("e8e99d0f45237d786d6bbaa7965c7808bbff1a91").unwrap(), + ), + ]; + + test_hmac(MessageDigest::sha1(), &tests); + } + + #[test] + #[cfg(ossl110)] + fn test_cmac() { + let cipher = crate::symm::Cipher::aes_128_cbc(); + let key = Vec::from_hex("9294727a3638bb1c13f48ef8158bfc9d").unwrap(); + let pkey = PKey::cmac(&cipher, &key).unwrap(); + let mut signer = Signer::new_without_digest(&pkey).unwrap(); + + let data = b"Hi There"; + signer.update(data as &[u8]).unwrap(); + + let expected = vec![ + 136, 101, 61, 167, 61, 30, 248, 234, 124, 166, 196, 157, 203, 52, 171, 19, + ]; + assert_eq!(signer.sign_to_vec().unwrap(), expected); + } + + #[test] + fn ec() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let key = EcKey::generate(&group).unwrap(); + let key = PKey::from_ec_key(key).unwrap(); + + let mut signer = Signer::new(MessageDigest::sha256(), &key).unwrap(); + signer.update(b"hello world").unwrap(); + let signature = signer.sign_to_vec().unwrap(); + + let mut verifier = Verifier::new(MessageDigest::sha256(), &key).unwrap(); + verifier.update(b"hello world").unwrap(); + assert!(verifier.verify(&signature).unwrap()); + } + + #[test] + #[cfg(any(ossl111, boringssl, libressl370))] + fn eddsa() { + let key = PKey::generate_ed25519().unwrap(); + + let mut signer = Signer::new_without_digest(&key).unwrap(); + let signature = signer.sign_oneshot_to_vec(b"hello world").unwrap(); + + let mut verifier = Verifier::new_without_digest(&key).unwrap(); + assert!(verifier.verify_oneshot(&signature, b"hello world").unwrap()); + } + + #[test] + #[cfg(ossl111)] + fn rsa_sign_verify() { + let key = include_bytes!("../test/rsa.pem"); + let private_key = Rsa::private_key_from_pem(key).unwrap(); + let pkey = PKey::from_rsa(private_key).unwrap(); + + let mut signer = Signer::new(MessageDigest::sha256(), &pkey).unwrap(); + signer.set_rsa_padding(Padding::PKCS1_PSS).unwrap(); + assert_eq!(signer.rsa_padding().unwrap(), Padding::PKCS1_PSS); + signer + .set_rsa_pss_saltlen(RsaPssSaltlen::DIGEST_LENGTH) + .unwrap(); + signer.set_rsa_mgf1_md(MessageDigest::sha256()).unwrap(); + signer.update(&Vec::from_hex(INPUT).unwrap()).unwrap(); + let signature = signer.sign_to_vec().unwrap(); + + let mut verifier = Verifier::new(MessageDigest::sha256(), &pkey).unwrap(); + verifier.set_rsa_padding(Padding::PKCS1_PSS).unwrap(); + verifier + .set_rsa_pss_saltlen(RsaPssSaltlen::DIGEST_LENGTH) + .unwrap(); + verifier.set_rsa_mgf1_md(MessageDigest::sha256()).unwrap(); + verifier.update(&Vec::from_hex(INPUT).unwrap()).unwrap(); + assert!(verifier.verify(&signature).unwrap()); + } +} diff --git a/vendor/openssl/src/srtp.rs b/vendor/openssl/src/srtp.rs new file mode 100644 index 0000000..595757d --- /dev/null +++ b/vendor/openssl/src/srtp.rs @@ -0,0 +1,66 @@ +use crate::stack::Stackable; +use foreign_types::ForeignTypeRef; +use libc::c_ulong; +use std::ffi::CStr; +use std::str; + +/// fake free method, since SRTP_PROTECTION_PROFILE is static +unsafe fn free(_profile: *mut ffi::SRTP_PROTECTION_PROFILE) {} + +foreign_type_and_impl_send_sync! { + type CType = ffi::SRTP_PROTECTION_PROFILE; + fn drop = free; + + pub struct SrtpProtectionProfile; + /// Reference to `SrtpProtectionProfile`. + pub struct SrtpProtectionProfileRef; +} + +impl Stackable for SrtpProtectionProfile { + type StackType = ffi::stack_st_SRTP_PROTECTION_PROFILE; +} + +impl SrtpProtectionProfileRef { + pub fn id(&self) -> SrtpProfileId { + SrtpProfileId::from_raw(unsafe { (*self.as_ptr()).id }) + } + pub fn name(&self) -> &'static str { + unsafe { CStr::from_ptr((*self.as_ptr()).name as *const _) } + .to_str() + .expect("should be UTF-8") + } +} + +/// An identifier of an SRTP protection profile. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct SrtpProfileId(c_ulong); + +impl SrtpProfileId { + pub const SRTP_AES128_CM_SHA1_80: SrtpProfileId = + SrtpProfileId(ffi::SRTP_AES128_CM_SHA1_80 as c_ulong); + pub const SRTP_AES128_CM_SHA1_32: SrtpProfileId = + SrtpProfileId(ffi::SRTP_AES128_CM_SHA1_32 as c_ulong); + pub const SRTP_AES128_F8_SHA1_80: SrtpProfileId = + SrtpProfileId(ffi::SRTP_AES128_F8_SHA1_80 as c_ulong); + pub const SRTP_AES128_F8_SHA1_32: SrtpProfileId = + SrtpProfileId(ffi::SRTP_AES128_F8_SHA1_32 as c_ulong); + pub const SRTP_NULL_SHA1_80: SrtpProfileId = SrtpProfileId(ffi::SRTP_NULL_SHA1_80 as c_ulong); + pub const SRTP_NULL_SHA1_32: SrtpProfileId = SrtpProfileId(ffi::SRTP_NULL_SHA1_32 as c_ulong); + #[cfg(any(boringssl, ossl110))] + pub const SRTP_AEAD_AES_128_GCM: SrtpProfileId = + SrtpProfileId(ffi::SRTP_AEAD_AES_128_GCM as c_ulong); + #[cfg(any(boringssl, ossl110))] + pub const SRTP_AEAD_AES_256_GCM: SrtpProfileId = + SrtpProfileId(ffi::SRTP_AEAD_AES_256_GCM as c_ulong); + + /// Creates a `SrtpProfileId` from an integer representation. + pub fn from_raw(value: c_ulong) -> SrtpProfileId { + SrtpProfileId(value) + } + + /// Returns the integer representation of `SrtpProfileId`. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn as_raw(&self) -> c_ulong { + self.0 + } +} diff --git a/vendor/openssl/src/ssl/bio.rs b/vendor/openssl/src/ssl/bio.rs new file mode 100644 index 0000000..36cea23 --- /dev/null +++ b/vendor/openssl/src/ssl/bio.rs @@ -0,0 +1,290 @@ +use cfg_if::cfg_if; +use ffi::{ + self, BIO_clear_retry_flags, BIO_new, BIO_set_retry_read, BIO_set_retry_write, BIO, + BIO_CTRL_DGRAM_QUERY_MTU, BIO_CTRL_FLUSH, +}; +use libc::{c_char, c_int, c_long, c_void, strlen}; +use std::any::Any; +use std::io; +use std::io::prelude::*; +use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::ptr; +use std::slice; + +use crate::cvt_p; +use crate::error::ErrorStack; + +pub struct StreamState<S> { + pub stream: S, + pub error: Option<io::Error>, + pub panic: Option<Box<dyn Any + Send>>, + pub dtls_mtu_size: c_long, +} + +/// Safe wrapper for `BIO_METHOD` +pub struct BioMethod(BIO_METHOD); + +impl BioMethod { + fn new<S: Read + Write>() -> Result<BioMethod, ErrorStack> { + BIO_METHOD::new::<S>().map(BioMethod) + } +} + +unsafe impl Sync for BioMethod {} +unsafe impl Send for BioMethod {} + +pub fn new<S: Read + Write>(stream: S) -> Result<(*mut BIO, BioMethod), ErrorStack> { + let method = BioMethod::new::<S>()?; + + let state = Box::new(StreamState { + stream, + error: None, + panic: None, + dtls_mtu_size: 0, + }); + + unsafe { + let bio = cvt_p(BIO_new(method.0.get()))?; + BIO_set_data(bio, Box::into_raw(state) as *mut _); + BIO_set_init(bio, 1); + + Ok((bio, method)) + } +} + +pub unsafe fn take_error<S>(bio: *mut BIO) -> Option<io::Error> { + let state = state::<S>(bio); + state.error.take() +} + +pub unsafe fn take_panic<S>(bio: *mut BIO) -> Option<Box<dyn Any + Send>> { + let state = state::<S>(bio); + state.panic.take() +} + +pub unsafe fn get_ref<'a, S: 'a>(bio: *mut BIO) -> &'a S { + let state = &*(BIO_get_data(bio) as *const StreamState<S>); + &state.stream +} + +pub unsafe fn get_mut<'a, S: 'a>(bio: *mut BIO) -> &'a mut S { + &mut state(bio).stream +} + +pub unsafe fn set_dtls_mtu_size<S>(bio: *mut BIO, mtu_size: usize) { + if mtu_size as u64 > c_long::max_value() as u64 { + panic!( + "Given MTU size {} can't be represented in a positive `c_long` range", + mtu_size + ) + } + state::<S>(bio).dtls_mtu_size = mtu_size as c_long; +} + +unsafe fn state<'a, S: 'a>(bio: *mut BIO) -> &'a mut StreamState<S> { + &mut *(BIO_get_data(bio) as *mut _) +} + +unsafe extern "C" fn bwrite<S: Write>(bio: *mut BIO, buf: *const c_char, len: c_int) -> c_int { + BIO_clear_retry_flags(bio); + + let state = state::<S>(bio); + let buf = slice::from_raw_parts(buf as *const _, len as usize); + + match catch_unwind(AssertUnwindSafe(|| state.stream.write(buf))) { + Ok(Ok(len)) => len as c_int, + Ok(Err(err)) => { + if retriable_error(&err) { + BIO_set_retry_write(bio); + } + state.error = Some(err); + -1 + } + Err(err) => { + state.panic = Some(err); + -1 + } + } +} + +unsafe extern "C" fn bread<S: Read>(bio: *mut BIO, buf: *mut c_char, len: c_int) -> c_int { + BIO_clear_retry_flags(bio); + + let state = state::<S>(bio); + let buf = slice::from_raw_parts_mut(buf as *mut _, len as usize); + + match catch_unwind(AssertUnwindSafe(|| state.stream.read(buf))) { + Ok(Ok(len)) => len as c_int, + Ok(Err(err)) => { + if retriable_error(&err) { + BIO_set_retry_read(bio); + } + state.error = Some(err); + -1 + } + Err(err) => { + state.panic = Some(err); + -1 + } + } +} + +#[allow(clippy::match_like_matches_macro)] // matches macro requires rust 1.42.0 +fn retriable_error(err: &io::Error) -> bool { + match err.kind() { + io::ErrorKind::WouldBlock | io::ErrorKind::NotConnected => true, + _ => false, + } +} + +unsafe extern "C" fn bputs<S: Write>(bio: *mut BIO, s: *const c_char) -> c_int { + bwrite::<S>(bio, s, strlen(s) as c_int) +} + +unsafe extern "C" fn ctrl<S: Write>( + bio: *mut BIO, + cmd: c_int, + _num: c_long, + _ptr: *mut c_void, +) -> c_long { + let state = state::<S>(bio); + + if cmd == BIO_CTRL_FLUSH { + match catch_unwind(AssertUnwindSafe(|| state.stream.flush())) { + Ok(Ok(())) => 1, + Ok(Err(err)) => { + state.error = Some(err); + 0 + } + Err(err) => { + state.panic = Some(err); + 0 + } + } + } else if cmd == BIO_CTRL_DGRAM_QUERY_MTU { + state.dtls_mtu_size + } else { + 0 + } +} + +unsafe extern "C" fn create(bio: *mut BIO) -> c_int { + BIO_set_init(bio, 0); + BIO_set_num(bio, 0); + BIO_set_data(bio, ptr::null_mut()); + BIO_set_flags(bio, 0); + 1 +} + +unsafe extern "C" fn destroy<S>(bio: *mut BIO) -> c_int { + if bio.is_null() { + return 0; + } + + let data = BIO_get_data(bio); + assert!(!data.is_null()); + let _ = Box::<StreamState<S>>::from_raw(data as *mut _); + BIO_set_data(bio, ptr::null_mut()); + BIO_set_init(bio, 0); + 1 +} + +cfg_if! { + if #[cfg(any(ossl110, libressl273))] { + use ffi::{BIO_get_data, BIO_set_data, BIO_set_flags, BIO_set_init}; + use crate::cvt; + + #[allow(bad_style)] + unsafe fn BIO_set_num(_bio: *mut ffi::BIO, _num: c_int) {} + + #[allow(bad_style, clippy::upper_case_acronyms)] + struct BIO_METHOD(*mut ffi::BIO_METHOD); + + impl BIO_METHOD { + fn new<S: Read + Write>() -> Result<BIO_METHOD, ErrorStack> { + unsafe { + let ptr = cvt_p(ffi::BIO_meth_new(ffi::BIO_TYPE_NONE, b"rust\0".as_ptr() as *const _))?; + let method = BIO_METHOD(ptr); + cvt(ffi::BIO_meth_set_write__fixed_rust(method.0, Some(bwrite::<S>)))?; + cvt(ffi::BIO_meth_set_read__fixed_rust(method.0, Some(bread::<S>)))?; + cvt(ffi::BIO_meth_set_puts__fixed_rust(method.0, Some(bputs::<S>)))?; + cvt(ffi::BIO_meth_set_ctrl__fixed_rust(method.0, Some(ctrl::<S>)))?; + cvt(ffi::BIO_meth_set_create__fixed_rust(method.0, Some(create)))?; + cvt(ffi::BIO_meth_set_destroy__fixed_rust(method.0, Some(destroy::<S>)))?; + Ok(method) + } + } + + fn get(&self) -> *mut ffi::BIO_METHOD { + self.0 + } + } + + impl Drop for BIO_METHOD { + fn drop(&mut self) { + unsafe { + ffi::BIO_meth_free(self.0); + } + } + } + } else { + #[allow(bad_style, clippy::upper_case_acronyms)] + struct BIO_METHOD(*mut ffi::BIO_METHOD); + + impl BIO_METHOD { + fn new<S: Read + Write>() -> Result<BIO_METHOD, ErrorStack> { + let ptr = Box::new(ffi::BIO_METHOD { + type_: ffi::BIO_TYPE_NONE, + name: b"rust\0".as_ptr() as *const _, + bwrite: Some(bwrite::<S>), + bread: Some(bread::<S>), + bputs: Some(bputs::<S>), + bgets: None, + ctrl: Some(ctrl::<S>), + create: Some(create), + destroy: Some(destroy::<S>), + callback_ctrl: None, + }); + + Ok(BIO_METHOD(Box::into_raw(ptr))) + } + + fn get(&self) -> *mut ffi::BIO_METHOD { + self.0 + } + } + + impl Drop for BIO_METHOD { + fn drop(&mut self) { + unsafe { + let _ = Box::<ffi::BIO_METHOD>::from_raw(self.0); + } + } + } + + #[allow(bad_style)] + unsafe fn BIO_set_init(bio: *mut ffi::BIO, init: c_int) { + (*bio).init = init; + } + + #[allow(bad_style)] + unsafe fn BIO_set_flags(bio: *mut ffi::BIO, flags: c_int) { + (*bio).flags = flags; + } + + #[allow(bad_style)] + unsafe fn BIO_get_data(bio: *mut ffi::BIO) -> *mut c_void { + (*bio).ptr + } + + #[allow(bad_style)] + unsafe fn BIO_set_data(bio: *mut ffi::BIO, data: *mut c_void) { + (*bio).ptr = data; + } + + #[allow(bad_style)] + unsafe fn BIO_set_num(bio: *mut ffi::BIO, num: c_int) { + (*bio).num = num; + } + } +} diff --git a/vendor/openssl/src/ssl/callbacks.rs b/vendor/openssl/src/ssl/callbacks.rs new file mode 100644 index 0000000..c6414fb --- /dev/null +++ b/vendor/openssl/src/ssl/callbacks.rs @@ -0,0 +1,707 @@ +use cfg_if::cfg_if; +use foreign_types::ForeignType; +use foreign_types::ForeignTypeRef; +#[cfg(any(ossl111, not(osslconf = "OPENSSL_NO_PSK")))] +use libc::c_char; +#[cfg(ossl111)] +use libc::size_t; +use libc::{c_int, c_uchar, c_uint, c_void}; +#[cfg(any(ossl111, not(osslconf = "OPENSSL_NO_PSK")))] +use std::ffi::CStr; +use std::mem; +use std::ptr; +use std::slice; +#[cfg(ossl111)] +use std::str; +use std::sync::Arc; + +use crate::dh::Dh; +#[cfg(all(ossl101, not(ossl110)))] +use crate::ec::EcKey; +use crate::error::ErrorStack; +use crate::pkey::Params; +#[cfg(any(ossl102, libressl261))] +use crate::ssl::AlpnError; +use crate::ssl::{ + try_get_session_ctx_index, SniError, Ssl, SslAlert, SslContext, SslContextRef, SslRef, + SslSession, SslSessionRef, +}; +#[cfg(ossl111)] +use crate::ssl::{ClientHelloResponse, ExtensionContext}; +#[cfg(ossl111)] +use crate::util::ForeignTypeRefExt; +#[cfg(ossl111)] +use crate::x509::X509Ref; +use crate::x509::{X509StoreContext, X509StoreContextRef}; + +pub extern "C" fn raw_verify<F>(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_CTX) -> c_int +where + F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send, +{ + unsafe { + let ctx = X509StoreContextRef::from_ptr_mut(x509_ctx); + let ssl_idx = X509StoreContext::ssl_idx().expect("BUG: store context ssl index missing"); + let verify_idx = SslContext::cached_ex_index::<F>(); + + // raw pointer shenanigans to break the borrow of ctx + // the callback can't mess with its own ex_data slot so this is safe + let verify = ctx + .ex_data(ssl_idx) + .expect("BUG: store context missing ssl") + .ssl_context() + .ex_data(verify_idx) + .expect("BUG: verify callback missing") as *const F; + + (*verify)(preverify_ok != 0, ctx) as c_int + } +} + +#[cfg(not(osslconf = "OPENSSL_NO_PSK"))] +pub extern "C" fn raw_client_psk<F>( + ssl: *mut ffi::SSL, + hint: *const c_char, + identity: *mut c_char, + max_identity_len: c_uint, + psk: *mut c_uchar, + max_psk_len: c_uint, +) -> c_uint +where + F: Fn(&mut SslRef, Option<&[u8]>, &mut [u8], &mut [u8]) -> Result<usize, ErrorStack> + + 'static + + Sync + + Send, +{ + unsafe { + let ssl = SslRef::from_ptr_mut(ssl); + let callback_idx = SslContext::cached_ex_index::<F>(); + + let callback = ssl + .ssl_context() + .ex_data(callback_idx) + .expect("BUG: psk callback missing") as *const F; + let hint = if !hint.is_null() { + Some(CStr::from_ptr(hint).to_bytes()) + } else { + None + }; + // Give the callback mutable slices into which it can write the identity and psk. + let identity_sl = slice::from_raw_parts_mut(identity as *mut u8, max_identity_len as usize); + #[allow(clippy::unnecessary_cast)] + let psk_sl = slice::from_raw_parts_mut(psk as *mut u8, max_psk_len as usize); + match (*callback)(ssl, hint, identity_sl, psk_sl) { + Ok(psk_len) => psk_len as u32, + Err(e) => { + e.put(); + 0 + } + } + } +} + +#[cfg(not(osslconf = "OPENSSL_NO_PSK"))] +pub extern "C" fn raw_server_psk<F>( + ssl: *mut ffi::SSL, + identity: *const c_char, + psk: *mut c_uchar, + max_psk_len: c_uint, +) -> c_uint +where + F: Fn(&mut SslRef, Option<&[u8]>, &mut [u8]) -> Result<usize, ErrorStack> + + 'static + + Sync + + Send, +{ + unsafe { + let ssl = SslRef::from_ptr_mut(ssl); + let callback_idx = SslContext::cached_ex_index::<F>(); + + let callback = ssl + .ssl_context() + .ex_data(callback_idx) + .expect("BUG: psk callback missing") as *const F; + let identity = if identity.is_null() { + None + } else { + Some(CStr::from_ptr(identity).to_bytes()) + }; + // Give the callback mutable slices into which it can write the psk. + #[allow(clippy::unnecessary_cast)] + let psk_sl = slice::from_raw_parts_mut(psk as *mut u8, max_psk_len as usize); + match (*callback)(ssl, identity, psk_sl) { + Ok(psk_len) => psk_len as u32, + Err(e) => { + e.put(); + 0 + } + } + } +} + +pub extern "C" fn ssl_raw_verify<F>( + preverify_ok: c_int, + x509_ctx: *mut ffi::X509_STORE_CTX, +) -> c_int +where + F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send, +{ + unsafe { + let ctx = X509StoreContextRef::from_ptr_mut(x509_ctx); + let ssl_idx = X509StoreContext::ssl_idx().expect("BUG: store context ssl index missing"); + let callback_idx = Ssl::cached_ex_index::<Arc<F>>(); + + let callback = ctx + .ex_data(ssl_idx) + .expect("BUG: store context missing ssl") + .ex_data(callback_idx) + .expect("BUG: ssl verify callback missing") + .clone(); + + callback(preverify_ok != 0, ctx) as c_int + } +} + +pub extern "C" fn raw_sni<F>(ssl: *mut ffi::SSL, al: *mut c_int, arg: *mut c_void) -> c_int +where + F: Fn(&mut SslRef, &mut SslAlert) -> Result<(), SniError> + 'static + Sync + Send, +{ + unsafe { + let ssl = SslRef::from_ptr_mut(ssl); + let callback = arg as *const F; + let mut alert = SslAlert(*al); + + let r = (*callback)(ssl, &mut alert); + *al = alert.0; + match r { + Ok(()) => ffi::SSL_TLSEXT_ERR_OK, + Err(e) => e.0, + } + } +} + +#[cfg(any(ossl102, libressl261))] +pub extern "C" fn raw_alpn_select<F>( + ssl: *mut ffi::SSL, + out: *mut *const c_uchar, + outlen: *mut c_uchar, + inbuf: *const c_uchar, + inlen: c_uint, + _arg: *mut c_void, +) -> c_int +where + F: for<'a> Fn(&mut SslRef, &'a [u8]) -> Result<&'a [u8], AlpnError> + 'static + Sync + Send, +{ + unsafe { + let ssl = SslRef::from_ptr_mut(ssl); + let callback = ssl + .ssl_context() + .ex_data(SslContext::cached_ex_index::<F>()) + .expect("BUG: alpn callback missing") as *const F; + #[allow(clippy::unnecessary_cast)] + let protos = slice::from_raw_parts(inbuf as *const u8, inlen as usize); + + match (*callback)(ssl, protos) { + Ok(proto) => { + *out = proto.as_ptr() as *const c_uchar; + *outlen = proto.len() as c_uchar; + ffi::SSL_TLSEXT_ERR_OK + } + Err(e) => e.0, + } + } +} + +pub unsafe extern "C" fn raw_tmp_dh<F>( + ssl: *mut ffi::SSL, + is_export: c_int, + keylength: c_int, +) -> *mut ffi::DH +where + F: Fn(&mut SslRef, bool, u32) -> Result<Dh<Params>, ErrorStack> + 'static + Sync + Send, +{ + let ssl = SslRef::from_ptr_mut(ssl); + let callback = ssl + .ssl_context() + .ex_data(SslContext::cached_ex_index::<F>()) + .expect("BUG: tmp dh callback missing") as *const F; + + match (*callback)(ssl, is_export != 0, keylength as u32) { + Ok(dh) => { + let ptr = dh.as_ptr(); + mem::forget(dh); + ptr + } + Err(e) => { + e.put(); + ptr::null_mut() + } + } +} + +#[cfg(all(ossl101, not(ossl110)))] +pub unsafe extern "C" fn raw_tmp_ecdh<F>( + ssl: *mut ffi::SSL, + is_export: c_int, + keylength: c_int, +) -> *mut ffi::EC_KEY +where + F: Fn(&mut SslRef, bool, u32) -> Result<EcKey<Params>, ErrorStack> + 'static + Sync + Send, +{ + let ssl = SslRef::from_ptr_mut(ssl); + let callback = ssl + .ssl_context() + .ex_data(SslContext::cached_ex_index::<F>()) + .expect("BUG: tmp ecdh callback missing") as *const F; + + match (*callback)(ssl, is_export != 0, keylength as u32) { + Ok(ec_key) => { + let ptr = ec_key.as_ptr(); + mem::forget(ec_key); + ptr + } + Err(e) => { + e.put(); + ptr::null_mut() + } + } +} + +pub unsafe extern "C" fn raw_tmp_dh_ssl<F>( + ssl: *mut ffi::SSL, + is_export: c_int, + keylength: c_int, +) -> *mut ffi::DH +where + F: Fn(&mut SslRef, bool, u32) -> Result<Dh<Params>, ErrorStack> + 'static + Sync + Send, +{ + let ssl = SslRef::from_ptr_mut(ssl); + let callback = ssl + .ex_data(Ssl::cached_ex_index::<Arc<F>>()) + .expect("BUG: ssl tmp dh callback missing") + .clone(); + + match callback(ssl, is_export != 0, keylength as u32) { + Ok(dh) => { + let ptr = dh.as_ptr(); + mem::forget(dh); + ptr + } + Err(e) => { + e.put(); + ptr::null_mut() + } + } +} + +#[cfg(all(ossl101, not(ossl110)))] +pub unsafe extern "C" fn raw_tmp_ecdh_ssl<F>( + ssl: *mut ffi::SSL, + is_export: c_int, + keylength: c_int, +) -> *mut ffi::EC_KEY +where + F: Fn(&mut SslRef, bool, u32) -> Result<EcKey<Params>, ErrorStack> + 'static + Sync + Send, +{ + let ssl = SslRef::from_ptr_mut(ssl); + let callback = ssl + .ex_data(Ssl::cached_ex_index::<Arc<F>>()) + .expect("BUG: ssl tmp ecdh callback missing") + .clone(); + + match callback(ssl, is_export != 0, keylength as u32) { + Ok(ec_key) => { + let ptr = ec_key.as_ptr(); + mem::forget(ec_key); + ptr + } + Err(e) => { + e.put(); + ptr::null_mut() + } + } +} + +pub unsafe extern "C" fn raw_tlsext_status<F>(ssl: *mut ffi::SSL, _: *mut c_void) -> c_int +where + F: Fn(&mut SslRef) -> Result<bool, ErrorStack> + 'static + Sync + Send, +{ + let ssl = SslRef::from_ptr_mut(ssl); + let callback = ssl + .ssl_context() + .ex_data(SslContext::cached_ex_index::<F>()) + .expect("BUG: ocsp callback missing") as *const F; + let ret = (*callback)(ssl); + + if ssl.is_server() { + match ret { + Ok(true) => ffi::SSL_TLSEXT_ERR_OK, + Ok(false) => ffi::SSL_TLSEXT_ERR_NOACK, + Err(e) => { + e.put(); + ffi::SSL_TLSEXT_ERR_ALERT_FATAL + } + } + } else { + match ret { + Ok(true) => 1, + Ok(false) => 0, + Err(e) => { + e.put(); + -1 + } + } + } +} + +pub unsafe extern "C" fn raw_new_session<F>( + ssl: *mut ffi::SSL, + session: *mut ffi::SSL_SESSION, +) -> c_int +where + F: Fn(&mut SslRef, SslSession) + 'static + Sync + Send, +{ + let session_ctx_index = + try_get_session_ctx_index().expect("BUG: session context index initialization failed"); + let ssl = SslRef::from_ptr_mut(ssl); + let callback = ssl + .ex_data(*session_ctx_index) + .expect("BUG: session context missing") + .ex_data(SslContext::cached_ex_index::<F>()) + .expect("BUG: new session callback missing") as *const F; + let session = SslSession::from_ptr(session); + + (*callback)(ssl, session); + + // the return code doesn't indicate error vs success, but whether or not we consumed the session + 1 +} + +pub unsafe extern "C" fn raw_remove_session<F>( + ctx: *mut ffi::SSL_CTX, + session: *mut ffi::SSL_SESSION, +) where + F: Fn(&SslContextRef, &SslSessionRef) + 'static + Sync + Send, +{ + let ctx = SslContextRef::from_ptr(ctx); + let callback = ctx + .ex_data(SslContext::cached_ex_index::<F>()) + .expect("BUG: remove session callback missing"); + let session = SslSessionRef::from_ptr(session); + + callback(ctx, session) +} + +cfg_if! { + if #[cfg(any(ossl110, libressl280, boringssl))] { + type DataPtr = *const c_uchar; + } else { + type DataPtr = *mut c_uchar; + } +} + +pub unsafe extern "C" fn raw_get_session<F>( + ssl: *mut ffi::SSL, + data: DataPtr, + len: c_int, + copy: *mut c_int, +) -> *mut ffi::SSL_SESSION +where + F: Fn(&mut SslRef, &[u8]) -> Option<SslSession> + 'static + Sync + Send, +{ + let session_ctx_index = + try_get_session_ctx_index().expect("BUG: session context index initialization failed"); + let ssl = SslRef::from_ptr_mut(ssl); + let callback = ssl + .ex_data(*session_ctx_index) + .expect("BUG: session context missing") + .ex_data(SslContext::cached_ex_index::<F>()) + .expect("BUG: get session callback missing") as *const F; + #[allow(clippy::unnecessary_cast)] + let data = slice::from_raw_parts(data as *const u8, len as usize); + + match (*callback)(ssl, data) { + Some(session) => { + let p = session.as_ptr(); + mem::forget(session); + *copy = 0; + p + } + None => ptr::null_mut(), + } +} + +#[cfg(ossl111)] +pub unsafe extern "C" fn raw_keylog<F>(ssl: *const ffi::SSL, line: *const c_char) +where + F: Fn(&SslRef, &str) + 'static + Sync + Send, +{ + let ssl = SslRef::from_const_ptr(ssl); + let callback = ssl + .ssl_context() + .ex_data(SslContext::cached_ex_index::<F>()) + .expect("BUG: get session callback missing"); + let line = CStr::from_ptr(line).to_bytes(); + let line = str::from_utf8_unchecked(line); + + callback(ssl, line); +} + +#[cfg(ossl111)] +pub unsafe extern "C" fn raw_stateless_cookie_generate<F>( + ssl: *mut ffi::SSL, + cookie: *mut c_uchar, + cookie_len: *mut size_t, +) -> c_int +where + F: Fn(&mut SslRef, &mut [u8]) -> Result<usize, ErrorStack> + 'static + Sync + Send, +{ + let ssl = SslRef::from_ptr_mut(ssl); + let callback = ssl + .ssl_context() + .ex_data(SslContext::cached_ex_index::<F>()) + .expect("BUG: stateless cookie generate callback missing") as *const F; + #[allow(clippy::unnecessary_cast)] + let slice = slice::from_raw_parts_mut(cookie as *mut u8, ffi::SSL_COOKIE_LENGTH as usize); + match (*callback)(ssl, slice) { + Ok(len) => { + *cookie_len = len as size_t; + 1 + } + Err(e) => { + e.put(); + 0 + } + } +} + +#[cfg(ossl111)] +pub unsafe extern "C" fn raw_stateless_cookie_verify<F>( + ssl: *mut ffi::SSL, + cookie: *const c_uchar, + cookie_len: size_t, +) -> c_int +where + F: Fn(&mut SslRef, &[u8]) -> bool + 'static + Sync + Send, +{ + let ssl = SslRef::from_ptr_mut(ssl); + let callback = ssl + .ssl_context() + .ex_data(SslContext::cached_ex_index::<F>()) + .expect("BUG: stateless cookie verify callback missing") as *const F; + #[allow(clippy::unnecessary_cast)] + let slice = slice::from_raw_parts(cookie as *const c_uchar as *const u8, cookie_len); + (*callback)(ssl, slice) as c_int +} + +#[cfg(not(boringssl))] +pub extern "C" fn raw_cookie_generate<F>( + ssl: *mut ffi::SSL, + cookie: *mut c_uchar, + cookie_len: *mut c_uint, +) -> c_int +where + F: Fn(&mut SslRef, &mut [u8]) -> Result<usize, ErrorStack> + 'static + Sync + Send, +{ + unsafe { + let ssl = SslRef::from_ptr_mut(ssl); + let callback = ssl + .ssl_context() + .ex_data(SslContext::cached_ex_index::<F>()) + .expect("BUG: cookie generate callback missing") as *const F; + // We subtract 1 from DTLS1_COOKIE_LENGTH as the ostensible value, 256, is erroneous but retained for + // compatibility. See comments in dtls1.h. + #[allow(clippy::unnecessary_cast)] + let slice = + slice::from_raw_parts_mut(cookie as *mut u8, ffi::DTLS1_COOKIE_LENGTH as usize - 1); + match (*callback)(ssl, slice) { + Ok(len) => { + *cookie_len = len as c_uint; + 1 + } + Err(e) => { + e.put(); + 0 + } + } + } +} + +#[cfg(not(boringssl))] +cfg_if! { + if #[cfg(any(ossl110, libressl280))] { + type CookiePtr = *const c_uchar; + } else { + type CookiePtr = *mut c_uchar; + } +} + +#[cfg(not(boringssl))] +pub extern "C" fn raw_cookie_verify<F>( + ssl: *mut ffi::SSL, + cookie: CookiePtr, + cookie_len: c_uint, +) -> c_int +where + F: Fn(&mut SslRef, &[u8]) -> bool + 'static + Sync + Send, +{ + unsafe { + let ssl = SslRef::from_ptr_mut(ssl); + let callback = ssl + .ssl_context() + .ex_data(SslContext::cached_ex_index::<F>()) + .expect("BUG: cookie verify callback missing") as *const F; + #[allow(clippy::unnecessary_cast)] + let slice = + slice::from_raw_parts(cookie as *const c_uchar as *const u8, cookie_len as usize); + (*callback)(ssl, slice) as c_int + } +} + +#[cfg(ossl111)] +pub struct CustomExtAddState<T>(Option<T>); + +#[cfg(ossl111)] +pub extern "C" fn raw_custom_ext_add<F, T>( + ssl: *mut ffi::SSL, + _: c_uint, + context: c_uint, + out: *mut *const c_uchar, + outlen: *mut size_t, + x: *mut ffi::X509, + chainidx: size_t, + al: *mut c_int, + _: *mut c_void, +) -> c_int +where + F: Fn(&mut SslRef, ExtensionContext, Option<(usize, &X509Ref)>) -> Result<Option<T>, SslAlert> + + 'static + + Sync + + Send, + T: AsRef<[u8]> + 'static + Sync + Send, +{ + unsafe { + let ssl = SslRef::from_ptr_mut(ssl); + let callback = ssl + .ssl_context() + .ex_data(SslContext::cached_ex_index::<F>()) + .expect("BUG: custom ext add callback missing") as *const F; + let ectx = ExtensionContext::from_bits_truncate(context); + let cert = if ectx.contains(ExtensionContext::TLS1_3_CERTIFICATE) { + Some((chainidx, X509Ref::from_ptr(x))) + } else { + None + }; + match (*callback)(ssl, ectx, cert) { + Ok(None) => 0, + Ok(Some(buf)) => { + *outlen = buf.as_ref().len(); + *out = buf.as_ref().as_ptr(); + + let idx = Ssl::cached_ex_index::<CustomExtAddState<T>>(); + let mut buf = Some(buf); + let new = match ssl.ex_data_mut(idx) { + Some(state) => { + state.0 = buf.take(); + false + } + None => true, + }; + if new { + ssl.set_ex_data(idx, CustomExtAddState(buf)); + } + 1 + } + Err(alert) => { + *al = alert.0; + -1 + } + } + } +} + +#[cfg(ossl111)] +pub extern "C" fn raw_custom_ext_free<T>( + ssl: *mut ffi::SSL, + _: c_uint, + _: c_uint, + _: *const c_uchar, + _: *mut c_void, +) where + T: 'static + Sync + Send, +{ + unsafe { + let ssl = SslRef::from_ptr_mut(ssl); + let idx = Ssl::cached_ex_index::<CustomExtAddState<T>>(); + if let Some(state) = ssl.ex_data_mut(idx) { + state.0 = None; + } + } +} + +#[cfg(ossl111)] +pub extern "C" fn raw_custom_ext_parse<F>( + ssl: *mut ffi::SSL, + _: c_uint, + context: c_uint, + input: *const c_uchar, + inlen: size_t, + x: *mut ffi::X509, + chainidx: size_t, + al: *mut c_int, + _: *mut c_void, +) -> c_int +where + F: Fn(&mut SslRef, ExtensionContext, &[u8], Option<(usize, &X509Ref)>) -> Result<(), SslAlert> + + 'static + + Sync + + Send, +{ + unsafe { + let ssl = SslRef::from_ptr_mut(ssl); + let callback = ssl + .ssl_context() + .ex_data(SslContext::cached_ex_index::<F>()) + .expect("BUG: custom ext parse callback missing") as *const F; + let ectx = ExtensionContext::from_bits_truncate(context); + #[allow(clippy::unnecessary_cast)] + let slice = slice::from_raw_parts(input as *const u8, inlen); + let cert = if ectx.contains(ExtensionContext::TLS1_3_CERTIFICATE) { + Some((chainidx, X509Ref::from_ptr(x))) + } else { + None + }; + match (*callback)(ssl, ectx, slice, cert) { + Ok(()) => 1, + Err(alert) => { + *al = alert.0; + 0 + } + } + } +} + +#[cfg(ossl111)] +pub unsafe extern "C" fn raw_client_hello<F>( + ssl: *mut ffi::SSL, + al: *mut c_int, + arg: *mut c_void, +) -> c_int +where + F: Fn(&mut SslRef, &mut SslAlert) -> Result<ClientHelloResponse, ErrorStack> + + 'static + + Sync + + Send, +{ + let ssl = SslRef::from_ptr_mut(ssl); + let callback = arg as *const F; + let mut alert = SslAlert(*al); + + let r = (*callback)(ssl, &mut alert); + *al = alert.0; + match r { + Ok(c) => c.0, + Err(e) => { + e.put(); + ffi::SSL_CLIENT_HELLO_ERROR + } + } +} diff --git a/vendor/openssl/src/ssl/connector.rs b/vendor/openssl/src/ssl/connector.rs new file mode 100644 index 0000000..66d1bd8 --- /dev/null +++ b/vendor/openssl/src/ssl/connector.rs @@ -0,0 +1,605 @@ +use cfg_if::cfg_if; +use std::io::{Read, Write}; +use std::ops::{Deref, DerefMut}; + +use crate::dh::Dh; +use crate::error::ErrorStack; +#[cfg(any(ossl111, libressl340))] +use crate::ssl::SslVersion; +use crate::ssl::{ + HandshakeError, Ssl, SslContext, SslContextBuilder, SslContextRef, SslMethod, SslMode, + SslOptions, SslRef, SslStream, SslVerifyMode, +}; +use crate::version; +use std::net::IpAddr; + +const FFDHE_2048: &str = " +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz ++8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a +87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7 +YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi +7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD +ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg== +-----END DH PARAMETERS----- +"; + +#[allow(clippy::inconsistent_digit_grouping, clippy::unusual_byte_groupings)] +fn ctx(method: SslMethod) -> Result<SslContextBuilder, ErrorStack> { + let mut ctx = SslContextBuilder::new(method)?; + + cfg_if! { + if #[cfg(not(boringssl))] { + let mut opts = SslOptions::ALL + | SslOptions::NO_COMPRESSION + | SslOptions::NO_SSLV2 + | SslOptions::NO_SSLV3 + | SslOptions::SINGLE_DH_USE + | SslOptions::SINGLE_ECDH_USE; + opts &= !SslOptions::DONT_INSERT_EMPTY_FRAGMENTS; + + ctx.set_options(opts); + } + } + + let mut mode = + SslMode::AUTO_RETRY | SslMode::ACCEPT_MOVING_WRITE_BUFFER | SslMode::ENABLE_PARTIAL_WRITE; + + // This is quite a useful optimization for saving memory, but historically + // caused CVEs in OpenSSL pre-1.0.1h, according to + // https://bugs.python.org/issue25672 + if version::number() >= 0x1_00_01_08_0 { + mode |= SslMode::RELEASE_BUFFERS; + } + + ctx.set_mode(mode); + + Ok(ctx) +} + +/// A type which wraps client-side streams in a TLS session. +/// +/// OpenSSL's default configuration is highly insecure. This connector manages the OpenSSL +/// structures, configuring cipher suites, session options, hostname verification, and more. +/// +/// OpenSSL's built-in hostname verification is used when linking against OpenSSL 1.0.2 or 1.1.0, +/// and a custom implementation is used when linking against OpenSSL 1.0.1. +#[derive(Clone, Debug)] +pub struct SslConnector(SslContext); + +impl SslConnector { + /// Creates a new builder for TLS connections. + /// + /// The default configuration is subject to change, and is currently derived from Python. + pub fn builder(method: SslMethod) -> Result<SslConnectorBuilder, ErrorStack> { + let mut ctx = ctx(method)?; + ctx.set_default_verify_paths()?; + ctx.set_cipher_list( + "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK", + )?; + setup_verify(&mut ctx); + + Ok(SslConnectorBuilder(ctx)) + } + + /// Initiates a client-side TLS session on a stream. + /// + /// The domain is used for SNI and hostname verification. + pub fn connect<S>(&self, domain: &str, stream: S) -> Result<SslStream<S>, HandshakeError<S>> + where + S: Read + Write, + { + self.configure()?.connect(domain, stream) + } + + /// Returns a structure allowing for configuration of a single TLS session before connection. + pub fn configure(&self) -> Result<ConnectConfiguration, ErrorStack> { + Ssl::new(&self.0).map(|ssl| ConnectConfiguration { + ssl, + sni: true, + verify_hostname: true, + }) + } + + /// Consumes the `SslConnector`, returning the inner raw `SslContext`. + pub fn into_context(self) -> SslContext { + self.0 + } + + /// Returns a shared reference to the inner raw `SslContext`. + pub fn context(&self) -> &SslContextRef { + &self.0 + } +} + +/// A builder for `SslConnector`s. +pub struct SslConnectorBuilder(SslContextBuilder); + +impl SslConnectorBuilder { + /// Consumes the builder, returning an `SslConnector`. + pub fn build(self) -> SslConnector { + SslConnector(self.0.build()) + } +} + +impl Deref for SslConnectorBuilder { + type Target = SslContextBuilder; + + fn deref(&self) -> &SslContextBuilder { + &self.0 + } +} + +impl DerefMut for SslConnectorBuilder { + fn deref_mut(&mut self) -> &mut SslContextBuilder { + &mut self.0 + } +} + +/// A type which allows for configuration of a client-side TLS session before connection. +pub struct ConnectConfiguration { + ssl: Ssl, + sni: bool, + verify_hostname: bool, +} + +impl ConnectConfiguration { + /// A builder-style version of `set_use_server_name_indication`. + pub fn use_server_name_indication(mut self, use_sni: bool) -> ConnectConfiguration { + self.set_use_server_name_indication(use_sni); + self + } + + /// Configures the use of Server Name Indication (SNI) when connecting. + /// + /// Defaults to `true`. + pub fn set_use_server_name_indication(&mut self, use_sni: bool) { + self.sni = use_sni; + } + + /// A builder-style version of `set_verify_hostname`. + pub fn verify_hostname(mut self, verify_hostname: bool) -> ConnectConfiguration { + self.set_verify_hostname(verify_hostname); + self + } + + /// Configures the use of hostname verification when connecting. + /// + /// Defaults to `true`. + /// + /// # Warning + /// + /// You should think very carefully before you use this method. If hostname verification is not + /// used, *any* valid certificate for *any* site will be trusted for use from any other. This + /// introduces a significant vulnerability to man-in-the-middle attacks. + pub fn set_verify_hostname(&mut self, verify_hostname: bool) { + self.verify_hostname = verify_hostname; + } + + /// Returns an `Ssl` configured to connect to the provided domain. + /// + /// The domain is used for SNI (if it is not an IP address) and hostname verification if enabled. + pub fn into_ssl(mut self, domain: &str) -> Result<Ssl, ErrorStack> { + if self.sni && domain.parse::<IpAddr>().is_err() { + self.ssl.set_hostname(domain)?; + } + + if self.verify_hostname { + setup_verify_hostname(&mut self.ssl, domain)?; + } + + Ok(self.ssl) + } + + /// Initiates a client-side TLS session on a stream. + /// + /// The domain is used for SNI and hostname verification if enabled. + pub fn connect<S>(self, domain: &str, stream: S) -> Result<SslStream<S>, HandshakeError<S>> + where + S: Read + Write, + { + self.into_ssl(domain)?.connect(stream) + } +} + +impl Deref for ConnectConfiguration { + type Target = SslRef; + + fn deref(&self) -> &SslRef { + &self.ssl + } +} + +impl DerefMut for ConnectConfiguration { + fn deref_mut(&mut self) -> &mut SslRef { + &mut self.ssl + } +} + +/// A type which wraps server-side streams in a TLS session. +/// +/// OpenSSL's default configuration is highly insecure. This connector manages the OpenSSL +/// structures, configuring cipher suites, session options, and more. +#[derive(Clone)] +pub struct SslAcceptor(SslContext); + +impl SslAcceptor { + /// Creates a new builder configured to connect to non-legacy clients. This should generally be + /// considered a reasonable default choice. + /// + /// This corresponds to the intermediate configuration of version 5 of Mozilla's server side TLS + /// recommendations. See its [documentation][docs] for more details on specifics. + /// + /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS + pub fn mozilla_intermediate_v5(method: SslMethod) -> Result<SslAcceptorBuilder, ErrorStack> { + let mut ctx = ctx(method)?; + ctx.set_options(SslOptions::NO_TLSV1 | SslOptions::NO_TLSV1_1); + let dh = Dh::params_from_pem(FFDHE_2048.as_bytes())?; + ctx.set_tmp_dh(&dh)?; + setup_curves(&mut ctx)?; + ctx.set_cipher_list( + "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:\ + ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:\ + DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384" + )?; + #[cfg(any(ossl111, libressl340))] + ctx.set_ciphersuites( + "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256", + )?; + Ok(SslAcceptorBuilder(ctx)) + } + + /// Creates a new builder configured to connect to modern clients. + /// + /// This corresponds to the modern configuration of version 5 of Mozilla's server side TLS recommendations. + /// See its [documentation][docs] for more details on specifics. + /// + /// Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer. + /// + /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS + #[cfg(any(ossl111, libressl340))] + pub fn mozilla_modern_v5(method: SslMethod) -> Result<SslAcceptorBuilder, ErrorStack> { + let mut ctx = ctx(method)?; + ctx.set_min_proto_version(Some(SslVersion::TLS1_3))?; + ctx.set_ciphersuites( + "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256", + )?; + Ok(SslAcceptorBuilder(ctx)) + } + + /// Creates a new builder configured to connect to non-legacy clients. This should generally be + /// considered a reasonable default choice. + /// + /// This corresponds to the intermediate configuration of version 4 of Mozilla's server side TLS + /// recommendations. See its [documentation][docs] for more details on specifics. + /// + /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS + // FIXME remove in next major version + pub fn mozilla_intermediate(method: SslMethod) -> Result<SslAcceptorBuilder, ErrorStack> { + let mut ctx = ctx(method)?; + ctx.set_options(SslOptions::CIPHER_SERVER_PREFERENCE); + #[cfg(any(ossl111, libressl340))] + ctx.set_options(SslOptions::NO_TLSV1_3); + let dh = Dh::params_from_pem(FFDHE_2048.as_bytes())?; + ctx.set_tmp_dh(&dh)?; + setup_curves(&mut ctx)?; + ctx.set_cipher_list( + "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:\ + ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:\ + DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:\ + ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:\ + ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:\ + DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:\ + EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:\ + AES256-SHA:DES-CBC3-SHA:!DSS", + )?; + Ok(SslAcceptorBuilder(ctx)) + } + + /// Creates a new builder configured to connect to modern clients. + /// + /// This corresponds to the modern configuration of version 4 of Mozilla's server side TLS recommendations. + /// See its [documentation][docs] for more details on specifics. + /// + /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS + // FIXME remove in next major version + pub fn mozilla_modern(method: SslMethod) -> Result<SslAcceptorBuilder, ErrorStack> { + let mut ctx = ctx(method)?; + ctx.set_options( + SslOptions::CIPHER_SERVER_PREFERENCE | SslOptions::NO_TLSV1 | SslOptions::NO_TLSV1_1, + ); + #[cfg(any(ossl111, libressl340))] + ctx.set_options(SslOptions::NO_TLSV1_3); + setup_curves(&mut ctx)?; + ctx.set_cipher_list( + "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:\ + ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:\ + ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256", + )?; + Ok(SslAcceptorBuilder(ctx)) + } + + /// Initiates a server-side TLS session on a stream. + pub fn accept<S>(&self, stream: S) -> Result<SslStream<S>, HandshakeError<S>> + where + S: Read + Write, + { + let ssl = Ssl::new(&self.0)?; + ssl.accept(stream) + } + + /// Consumes the `SslAcceptor`, returning the inner raw `SslContext`. + pub fn into_context(self) -> SslContext { + self.0 + } + + /// Returns a shared reference to the inner raw `SslContext`. + pub fn context(&self) -> &SslContextRef { + &self.0 + } +} + +/// A builder for `SslAcceptor`s. +pub struct SslAcceptorBuilder(SslContextBuilder); + +impl SslAcceptorBuilder { + /// Consumes the builder, returning a `SslAcceptor`. + pub fn build(self) -> SslAcceptor { + SslAcceptor(self.0.build()) + } +} + +impl Deref for SslAcceptorBuilder { + type Target = SslContextBuilder; + + fn deref(&self) -> &SslContextBuilder { + &self.0 + } +} + +impl DerefMut for SslAcceptorBuilder { + fn deref_mut(&mut self) -> &mut SslContextBuilder { + &mut self.0 + } +} + +cfg_if! { + if #[cfg(ossl110)] { + #[allow(clippy::unnecessary_wraps)] + fn setup_curves(_: &mut SslContextBuilder) -> Result<(), ErrorStack> { + Ok(()) + } + } else if #[cfg(any(ossl102, libressl))] { + fn setup_curves(ctx: &mut SslContextBuilder) -> Result<(), ErrorStack> { + ctx.set_ecdh_auto(true) + } + } else { + fn setup_curves(ctx: &mut SslContextBuilder) -> Result<(), ErrorStack> { + use crate::ec::EcKey; + use crate::nid::Nid; + + let curve = EcKey::from_curve_name(Nid::X9_62_PRIME256V1)?; + ctx.set_tmp_ecdh(&curve) + } + } +} + +cfg_if! { + if #[cfg(any(ossl102, libressl261))] { + fn setup_verify(ctx: &mut SslContextBuilder) { + ctx.set_verify(SslVerifyMode::PEER); + } + + fn setup_verify_hostname(ssl: &mut SslRef, domain: &str) -> Result<(), ErrorStack> { + use crate::x509::verify::X509CheckFlags; + + let param = ssl.param_mut(); + param.set_hostflags(X509CheckFlags::NO_PARTIAL_WILDCARDS); + match domain.parse() { + Ok(ip) => param.set_ip(ip), + Err(_) => param.set_host(domain), + } + } + } else { + fn setup_verify(ctx: &mut SslContextBuilder) { + ctx.set_verify_callback(SslVerifyMode::PEER, verify::verify_callback); + } + + fn setup_verify_hostname(ssl: &mut Ssl, domain: &str) -> Result<(), ErrorStack> { + let domain = domain.to_string(); + let hostname_idx = verify::try_get_hostname_idx()?; + ssl.set_ex_data(*hostname_idx, domain); + Ok(()) + } + + mod verify { + use std::net::IpAddr; + use std::str; + use once_cell::sync::OnceCell; + + use crate::error::ErrorStack; + use crate::ex_data::Index; + use crate::nid::Nid; + use crate::ssl::Ssl; + use crate::stack::Stack; + use crate::x509::{ + GeneralName, X509NameRef, X509Ref, X509StoreContext, X509StoreContextRef, + X509VerifyResult, + }; + + static HOSTNAME_IDX: OnceCell<Index<Ssl, String>> = OnceCell::new(); + + pub fn try_get_hostname_idx() -> Result<&'static Index<Ssl, String>, ErrorStack> { + HOSTNAME_IDX.get_or_try_init(Ssl::new_ex_index) + } + + pub fn verify_callback(preverify_ok: bool, x509_ctx: &mut X509StoreContextRef) -> bool { + if !preverify_ok || x509_ctx.error_depth() != 0 { + return preverify_ok; + } + + let hostname_idx = + try_get_hostname_idx().expect("failed to initialize hostname index"); + let ok = match ( + x509_ctx.current_cert(), + X509StoreContext::ssl_idx() + .ok() + .and_then(|idx| x509_ctx.ex_data(idx)) + .and_then(|ssl| ssl.ex_data(*hostname_idx)), + ) { + (Some(x509), Some(domain)) => verify_hostname(domain, &x509), + _ => true, + }; + + if !ok { + x509_ctx.set_error(X509VerifyResult::APPLICATION_VERIFICATION); + } + + ok + } + + fn verify_hostname(domain: &str, cert: &X509Ref) -> bool { + match cert.subject_alt_names() { + Some(names) => verify_subject_alt_names(domain, names), + None => verify_subject_name(domain, &cert.subject_name()), + } + } + + fn verify_subject_alt_names(domain: &str, names: Stack<GeneralName>) -> bool { + let ip = domain.parse(); + + for name in &names { + match ip { + Ok(ip) => { + if let Some(actual) = name.ipaddress() { + if matches_ip(&ip, actual) { + return true; + } + } + } + Err(_) => { + if let Some(pattern) = name.dnsname() { + if matches_dns(pattern, domain) { + return true; + } + } + } + } + } + + false + } + + fn verify_subject_name(domain: &str, subject_name: &X509NameRef) -> bool { + match subject_name.entries_by_nid(Nid::COMMONNAME).next() { + Some(pattern) => { + let pattern = match str::from_utf8(pattern.data().as_slice()) { + Ok(pattern) => pattern, + Err(_) => return false, + }; + + // Unlike SANs, IP addresses in the subject name don't have a + // different encoding. + match domain.parse::<IpAddr>() { + Ok(ip) => pattern + .parse::<IpAddr>() + .ok() + .map_or(false, |pattern| pattern == ip), + Err(_) => matches_dns(pattern, domain), + } + } + None => false, + } + } + + fn matches_dns(mut pattern: &str, mut hostname: &str) -> bool { + // first strip trailing . off of pattern and hostname to normalize + if pattern.ends_with('.') { + pattern = &pattern[..pattern.len() - 1]; + } + if hostname.ends_with('.') { + hostname = &hostname[..hostname.len() - 1]; + } + + matches_wildcard(pattern, hostname).unwrap_or_else(|| pattern.eq_ignore_ascii_case(hostname)) + } + + fn matches_wildcard(pattern: &str, hostname: &str) -> Option<bool> { + let wildcard_location = match pattern.find('*') { + Some(l) => l, + None => return None, + }; + + let mut dot_idxs = pattern.match_indices('.').map(|(l, _)| l); + let wildcard_end = match dot_idxs.next() { + Some(l) => l, + None => return None, + }; + + // Never match wildcards if the pattern has less than 2 '.'s (no *.com) + // + // This is a bit dubious, as it doesn't disallow other TLDs like *.co.uk. + // Chrome has a black- and white-list for this, but Firefox (via NSS) does + // the same thing we do here. + // + // The Public Suffix (https://www.publicsuffix.org/) list could + // potentially be used here, but it's both huge and updated frequently + // enough that management would be a PITA. + if dot_idxs.next().is_none() { + return None; + } + + // Wildcards can only be in the first component, and must be the entire first label + if wildcard_location != 0 || wildcard_end != wildcard_location + 1 { + return None; + } + + let hostname_label_end = match hostname.find('.') { + Some(l) => l, + None => return None, + }; + + let pattern_after_wildcard = &pattern[wildcard_end..]; + let hostname_after_wildcard = &hostname[hostname_label_end..]; + + Some(pattern_after_wildcard.eq_ignore_ascii_case(hostname_after_wildcard)) + } + + fn matches_ip(expected: &IpAddr, actual: &[u8]) -> bool { + match *expected { + IpAddr::V4(ref addr) => actual == addr.octets(), + IpAddr::V6(ref addr) => actual == addr.octets(), + } + } + + #[test] + fn test_dns_match() { + use crate::ssl::connector::verify::matches_dns; + assert!(matches_dns("website.tld", "website.tld")); // A name should match itself. + assert!(matches_dns("website.tld", "wEbSiTe.tLd")); // DNS name matching ignores case of hostname. + assert!(matches_dns("wEbSiTe.TlD", "website.tld")); // DNS name matching ignores case of subject. + + assert!(matches_dns("xn--bcher-kva.tld", "xn--bcher-kva.tld")); // Likewise, nothing special to punycode names. + assert!(matches_dns("xn--bcher-kva.tld", "xn--BcHer-Kva.tLd")); // And punycode must be compared similarly case-insensitively. + + assert!(matches_dns("*.example.com", "subdomain.example.com")); // Wildcard matching works. + assert!(matches_dns("*.eXaMpLe.cOm", "subdomain.example.com")); // Wildcard matching ignores case of subject. + assert!(matches_dns("*.example.com", "sUbDoMaIn.eXaMpLe.cOm")); // Wildcard matching ignores case of hostname. + + assert!(!matches_dns("prefix*.example.com", "p.example.com")); // Prefix longer than the label works and does not match. + assert!(!matches_dns("*suffix.example.com", "s.example.com")); // Suffix longer than the label works and does not match. + + assert!(!matches_dns("prefix*.example.com", "prefix.example.com")); // Partial wildcards do not work. + assert!(!matches_dns("*suffix.example.com", "suffix.example.com")); // Partial wildcards do not work. + + assert!(!matches_dns("prefix*.example.com", "prefixdomain.example.com")); // Partial wildcards do not work. + assert!(!matches_dns("*suffix.example.com", "domainsuffix.example.com")); // Partial wildcards do not work. + + assert!(!matches_dns("xn--*.example.com", "subdomain.example.com")); // Punycode domains with wildcard parts do not match. + assert!(!matches_dns("xN--*.example.com", "subdomain.example.com")); // And we can't bypass a punycode test with weird casing. + assert!(!matches_dns("Xn--*.example.com", "subdomain.example.com")); // And we can't bypass a punycode test with weird casing. + assert!(!matches_dns("XN--*.example.com", "subdomain.example.com")); // And we can't bypass a punycode test with weird casing. + } + } + } +} diff --git a/vendor/openssl/src/ssl/error.rs b/vendor/openssl/src/ssl/error.rs new file mode 100644 index 0000000..5565833 --- /dev/null +++ b/vendor/openssl/src/ssl/error.rs @@ -0,0 +1,185 @@ +use libc::c_int; +use std::error; +use std::error::Error as StdError; +use std::fmt; +use std::io; + +use crate::error::ErrorStack; +use crate::ssl::MidHandshakeSslStream; +use crate::x509::X509VerifyResult; + +/// An error code returned from SSL functions. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct ErrorCode(c_int); + +impl ErrorCode { + /// The SSL session has been closed. + pub const ZERO_RETURN: ErrorCode = ErrorCode(ffi::SSL_ERROR_ZERO_RETURN); + + /// An attempt to read data from the underlying socket returned `WouldBlock`. + /// + /// Wait for read readiness and retry the operation. + pub const WANT_READ: ErrorCode = ErrorCode(ffi::SSL_ERROR_WANT_READ); + + /// An attempt to write data to the underlying socket returned `WouldBlock`. + /// + /// Wait for write readiness and retry the operation. + pub const WANT_WRITE: ErrorCode = ErrorCode(ffi::SSL_ERROR_WANT_WRITE); + + /// A non-recoverable IO error occurred. + pub const SYSCALL: ErrorCode = ErrorCode(ffi::SSL_ERROR_SYSCALL); + + /// An error occurred in the SSL library. + pub const SSL: ErrorCode = ErrorCode(ffi::SSL_ERROR_SSL); + + /// The client hello callback indicated that it needed to be retried. + /// + /// Requires OpenSSL 1.1.1 or newer. + #[cfg(ossl111)] + pub const WANT_CLIENT_HELLO_CB: ErrorCode = ErrorCode(ffi::SSL_ERROR_WANT_CLIENT_HELLO_CB); + + pub fn from_raw(raw: c_int) -> ErrorCode { + ErrorCode(raw) + } + + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn as_raw(&self) -> c_int { + self.0 + } +} + +#[derive(Debug)] +pub(crate) enum InnerError { + Io(io::Error), + Ssl(ErrorStack), +} + +/// An SSL error. +#[derive(Debug)] +pub struct Error { + pub(crate) code: ErrorCode, + pub(crate) cause: Option<InnerError>, +} + +impl Error { + pub fn code(&self) -> ErrorCode { + self.code + } + + pub fn io_error(&self) -> Option<&io::Error> { + match self.cause { + Some(InnerError::Io(ref e)) => Some(e), + _ => None, + } + } + + pub fn into_io_error(self) -> Result<io::Error, Error> { + match self.cause { + Some(InnerError::Io(e)) => Ok(e), + _ => Err(self), + } + } + + pub fn ssl_error(&self) -> Option<&ErrorStack> { + match self.cause { + Some(InnerError::Ssl(ref e)) => Some(e), + _ => None, + } + } +} + +impl From<ErrorStack> for Error { + fn from(e: ErrorStack) -> Error { + Error { + code: ErrorCode::SSL, + cause: Some(InnerError::Ssl(e)), + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.code { + ErrorCode::ZERO_RETURN => fmt.write_str("the SSL session has been shut down"), + ErrorCode::WANT_READ => match self.io_error() { + Some(_) => fmt.write_str("a nonblocking read call would have blocked"), + None => fmt.write_str("the operation should be retried"), + }, + ErrorCode::WANT_WRITE => match self.io_error() { + Some(_) => fmt.write_str("a nonblocking write call would have blocked"), + None => fmt.write_str("the operation should be retried"), + }, + ErrorCode::SYSCALL => match self.io_error() { + Some(err) => write!(fmt, "{}", err), + None => fmt.write_str("unexpected EOF"), + }, + ErrorCode::SSL => match self.ssl_error() { + Some(e) => write!(fmt, "{}", e), + None => fmt.write_str("OpenSSL error"), + }, + ErrorCode(code) => write!(fmt, "unknown error code {}", code), + } + } +} + +impl error::Error for Error { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match self.cause { + Some(InnerError::Io(ref e)) => Some(e), + Some(InnerError::Ssl(ref e)) => Some(e), + None => None, + } + } +} + +/// An error or intermediate state after a TLS handshake attempt. +// FIXME overhaul +#[derive(Debug)] +pub enum HandshakeError<S> { + /// Setup failed. + SetupFailure(ErrorStack), + /// The handshake failed. + Failure(MidHandshakeSslStream<S>), + /// The handshake encountered a `WouldBlock` error midway through. + /// + /// This error will never be returned for blocking streams. + WouldBlock(MidHandshakeSslStream<S>), +} + +impl<S: fmt::Debug> StdError for HandshakeError<S> { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + match *self { + HandshakeError::SetupFailure(ref e) => Some(e), + HandshakeError::Failure(ref s) | HandshakeError::WouldBlock(ref s) => Some(s.error()), + } + } +} + +impl<S: fmt::Debug> fmt::Display for HandshakeError<S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + HandshakeError::SetupFailure(ref e) => write!(f, "stream setup failed: {}", e)?, + HandshakeError::Failure(ref s) => { + write!(f, "the handshake failed: {}", s.error())?; + let verify = s.ssl().verify_result(); + if verify != X509VerifyResult::OK { + write!(f, ": {}", verify)?; + } + } + HandshakeError::WouldBlock(ref s) => { + write!(f, "the handshake was interrupted: {}", s.error())?; + let verify = s.ssl().verify_result(); + if verify != X509VerifyResult::OK { + write!(f, ": {}", verify)?; + } + } + } + Ok(()) + } +} + +impl<S> From<ErrorStack> for HandshakeError<S> { + fn from(e: ErrorStack) -> HandshakeError<S> { + HandshakeError::SetupFailure(e) + } +} diff --git a/vendor/openssl/src/ssl/mod.rs b/vendor/openssl/src/ssl/mod.rs new file mode 100644 index 0000000..bdfbfc1 --- /dev/null +++ b/vendor/openssl/src/ssl/mod.rs @@ -0,0 +1,4211 @@ +//! SSL/TLS support. +//! +//! `SslConnector` and `SslAcceptor` should be used in most cases - they handle +//! configuration of the OpenSSL primitives for you. +//! +//! # Examples +//! +//! To connect as a client to a remote server: +//! +//! ```no_run +//! use openssl::ssl::{SslMethod, SslConnector}; +//! use std::io::{Read, Write}; +//! use std::net::TcpStream; +//! +//! let connector = SslConnector::builder(SslMethod::tls()).unwrap().build(); +//! +//! let stream = TcpStream::connect("google.com:443").unwrap(); +//! let mut stream = connector.connect("google.com", stream).unwrap(); +//! +//! stream.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap(); +//! let mut res = vec![]; +//! stream.read_to_end(&mut res).unwrap(); +//! println!("{}", String::from_utf8_lossy(&res)); +//! ``` +//! +//! To accept connections as a server from remote clients: +//! +//! ```no_run +//! use openssl::ssl::{SslMethod, SslAcceptor, SslStream, SslFiletype}; +//! use std::net::{TcpListener, TcpStream}; +//! use std::sync::Arc; +//! use std::thread; +//! +//! +//! let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); +//! acceptor.set_private_key_file("key.pem", SslFiletype::PEM).unwrap(); +//! acceptor.set_certificate_chain_file("certs.pem").unwrap(); +//! acceptor.check_private_key().unwrap(); +//! let acceptor = Arc::new(acceptor.build()); +//! +//! let listener = TcpListener::bind("0.0.0.0:8443").unwrap(); +//! +//! fn handle_client(stream: SslStream<TcpStream>) { +//! // ... +//! } +//! +//! for stream in listener.incoming() { +//! match stream { +//! Ok(stream) => { +//! let acceptor = acceptor.clone(); +//! thread::spawn(move || { +//! let stream = acceptor.accept(stream).unwrap(); +//! handle_client(stream); +//! }); +//! } +//! Err(e) => { /* connection failed */ } +//! } +//! } +//! ``` +use crate::dh::{Dh, DhRef}; +#[cfg(all(ossl101, not(ossl110)))] +use crate::ec::EcKey; +use crate::ec::EcKeyRef; +use crate::error::ErrorStack; +use crate::ex_data::Index; +#[cfg(ossl111)] +use crate::hash::MessageDigest; +#[cfg(any(ossl110, libressl270))] +use crate::nid::Nid; +use crate::pkey::{HasPrivate, PKeyRef, Params, Private}; +use crate::srtp::{SrtpProtectionProfile, SrtpProtectionProfileRef}; +use crate::ssl::bio::BioMethod; +use crate::ssl::callbacks::*; +use crate::ssl::error::InnerError; +use crate::stack::{Stack, StackRef, Stackable}; +use crate::util::{ForeignTypeExt, ForeignTypeRefExt}; +use crate::x509::store::{X509Store, X509StoreBuilderRef, X509StoreRef}; +#[cfg(any(ossl102, libressl261))] +use crate::x509::verify::X509VerifyParamRef; +use crate::x509::{X509Name, X509Ref, X509StoreContextRef, X509VerifyResult, X509}; +use crate::{cvt, cvt_n, cvt_p, init}; +use bitflags::bitflags; +use cfg_if::cfg_if; +use foreign_types::{ForeignType, ForeignTypeRef, Opaque}; +use libc::{c_char, c_int, c_long, c_uchar, c_uint, c_void}; +use once_cell::sync::{Lazy, OnceCell}; +use openssl_macros::corresponds; +use std::any::TypeId; +use std::cmp; +use std::collections::HashMap; +use std::ffi::{CStr, CString}; +use std::fmt; +use std::io; +use std::io::prelude::*; +use std::marker::PhantomData; +use std::mem::{self, ManuallyDrop}; +use std::ops::{Deref, DerefMut}; +use std::panic::resume_unwind; +use std::path::Path; +use std::ptr; +use std::slice; +use std::str; +use std::sync::{Arc, Mutex}; + +pub use crate::ssl::connector::{ + ConnectConfiguration, SslAcceptor, SslAcceptorBuilder, SslConnector, SslConnectorBuilder, +}; +pub use crate::ssl::error::{Error, ErrorCode, HandshakeError}; + +mod bio; +mod callbacks; +mod connector; +mod error; +#[cfg(test)] +mod test; + +/// Returns the OpenSSL name of a cipher corresponding to an RFC-standard cipher name. +/// +/// If the cipher has no corresponding OpenSSL name, the string `(NONE)` is returned. +/// +/// Requires OpenSSL 1.1.1 or newer. +#[corresponds(OPENSSL_cipher_name)] +#[cfg(ossl111)] +pub fn cipher_name(std_name: &str) -> &'static str { + unsafe { + ffi::init(); + + let s = CString::new(std_name).unwrap(); + let ptr = ffi::OPENSSL_cipher_name(s.as_ptr()); + CStr::from_ptr(ptr).to_str().unwrap() + } +} + +cfg_if! { + if #[cfg(ossl300)] { + type SslOptionsRepr = u64; + } else if #[cfg(boringssl)] { + type SslOptionsRepr = u32; + } else { + type SslOptionsRepr = libc::c_ulong; + } +} + +bitflags! { + /// Options controlling the behavior of an `SslContext`. + #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + #[repr(transparent)] + pub struct SslOptions: SslOptionsRepr { + /// Disables a countermeasure against an SSLv3/TLSv1.0 vulnerability affecting CBC ciphers. + const DONT_INSERT_EMPTY_FRAGMENTS = ffi::SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS as SslOptionsRepr; + + /// A "reasonable default" set of options which enables compatibility flags. + #[cfg(not(boringssl))] + const ALL = ffi::SSL_OP_ALL as SslOptionsRepr; + + /// Do not query the MTU. + /// + /// Only affects DTLS connections. + const NO_QUERY_MTU = ffi::SSL_OP_NO_QUERY_MTU as SslOptionsRepr; + + /// Enables Cookie Exchange as described in [RFC 4347 Section 4.2.1]. + /// + /// Only affects DTLS connections. + /// + /// [RFC 4347 Section 4.2.1]: https://tools.ietf.org/html/rfc4347#section-4.2.1 + #[cfg(not(boringssl))] + const COOKIE_EXCHANGE = ffi::SSL_OP_COOKIE_EXCHANGE as SslOptionsRepr; + + /// Disables the use of session tickets for session resumption. + const NO_TICKET = ffi::SSL_OP_NO_TICKET as SslOptionsRepr; + + /// Always start a new session when performing a renegotiation on the server side. + #[cfg(not(boringssl))] + const NO_SESSION_RESUMPTION_ON_RENEGOTIATION = + ffi::SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION as SslOptionsRepr; + + /// Disables the use of TLS compression. + #[cfg(not(boringssl))] + const NO_COMPRESSION = ffi::SSL_OP_NO_COMPRESSION as SslOptionsRepr; + + /// Allow legacy insecure renegotiation with servers or clients that do not support secure + /// renegotiation. + const ALLOW_UNSAFE_LEGACY_RENEGOTIATION = + ffi::SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION as SslOptionsRepr; + + /// Creates a new key for each session when using ECDHE. + /// + /// This is always enabled in OpenSSL 1.1.0. + const SINGLE_ECDH_USE = ffi::SSL_OP_SINGLE_ECDH_USE as SslOptionsRepr; + + /// Creates a new key for each session when using DHE. + /// + /// This is always enabled in OpenSSL 1.1.0. + const SINGLE_DH_USE = ffi::SSL_OP_SINGLE_DH_USE as SslOptionsRepr; + + /// Use the server's preferences rather than the client's when selecting a cipher. + /// + /// This has no effect on the client side. + const CIPHER_SERVER_PREFERENCE = ffi::SSL_OP_CIPHER_SERVER_PREFERENCE as SslOptionsRepr; + + /// Disables version rollback attach detection. + const TLS_ROLLBACK_BUG = ffi::SSL_OP_TLS_ROLLBACK_BUG as SslOptionsRepr; + + /// Disables the use of SSLv2. + const NO_SSLV2 = ffi::SSL_OP_NO_SSLv2 as SslOptionsRepr; + + /// Disables the use of SSLv3. + const NO_SSLV3 = ffi::SSL_OP_NO_SSLv3 as SslOptionsRepr; + + /// Disables the use of TLSv1.0. + const NO_TLSV1 = ffi::SSL_OP_NO_TLSv1 as SslOptionsRepr; + + /// Disables the use of TLSv1.1. + const NO_TLSV1_1 = ffi::SSL_OP_NO_TLSv1_1 as SslOptionsRepr; + + /// Disables the use of TLSv1.2. + const NO_TLSV1_2 = ffi::SSL_OP_NO_TLSv1_2 as SslOptionsRepr; + + /// Disables the use of TLSv1.3. + /// + /// Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer. + #[cfg(any(boringssl, ossl111, libressl340))] + const NO_TLSV1_3 = ffi::SSL_OP_NO_TLSv1_3 as SslOptionsRepr; + + /// Disables the use of DTLSv1.0 + /// + /// Requires OpenSSL 1.0.2 or LibreSSL 3.3.2 or newer. + #[cfg(any(boringssl, ossl102, ossl110, libressl332))] + const NO_DTLSV1 = ffi::SSL_OP_NO_DTLSv1 as SslOptionsRepr; + + /// Disables the use of DTLSv1.2. + /// + /// Requires OpenSSL 1.0.2 or LibreSSL 3.3.2 or newer. + #[cfg(any(boringssl, ossl102, ossl110, libressl332))] + const NO_DTLSV1_2 = ffi::SSL_OP_NO_DTLSv1_2 as SslOptionsRepr; + + /// Disables the use of all (D)TLS protocol versions. + /// + /// This can be used as a mask when whitelisting protocol versions. + /// + /// Requires OpenSSL 1.0.2 or newer. + /// + /// # Examples + /// + /// Only support TLSv1.2: + /// + /// ```rust + /// use openssl::ssl::SslOptions; + /// + /// let options = SslOptions::NO_SSL_MASK & !SslOptions::NO_TLSV1_2; + /// ``` + #[cfg(any(ossl102, ossl110))] + const NO_SSL_MASK = ffi::SSL_OP_NO_SSL_MASK as SslOptionsRepr; + + /// Disallow all renegotiation in TLSv1.2 and earlier. + /// + /// Requires OpenSSL 1.1.0h or newer. + #[cfg(any(boringssl, ossl110h))] + const NO_RENEGOTIATION = ffi::SSL_OP_NO_RENEGOTIATION as SslOptionsRepr; + + /// Enable TLSv1.3 Compatibility mode. + /// + /// Requires OpenSSL 1.1.1 or newer. This is on by default in 1.1.1, but a future version + /// may have this disabled by default. + #[cfg(ossl111)] + const ENABLE_MIDDLEBOX_COMPAT = ffi::SSL_OP_ENABLE_MIDDLEBOX_COMPAT as SslOptionsRepr; + + /// Prioritize ChaCha ciphers when preferred by clients. + /// + /// Temporarily reprioritize ChaCha20-Poly1305 ciphers to the top of the server cipher list + /// if a ChaCha20-Poly1305 cipher is at the top of the client cipher list. This helps those + /// clients (e.g. mobile) use ChaCha20-Poly1305 if that cipher is anywhere in the server + /// cipher list; but still allows other clients to use AES and other ciphers. + /// + /// Requires enable [`SslOptions::CIPHER_SERVER_PREFERENCE`]. + /// Requires OpenSSL 1.1.1 or newer. + /// + /// [`SslOptions::CIPHER_SERVER_PREFERENCE`]: struct.SslOptions.html#associatedconstant.CIPHER_SERVER_PREFERENCE + #[cfg(ossl111)] + const PRIORITIZE_CHACHA = ffi::SSL_OP_PRIORITIZE_CHACHA as SslOptionsRepr; + } +} + +bitflags! { + /// Options controlling the behavior of an `SslContext`. + #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + #[repr(transparent)] + pub struct SslMode: SslBitType { + /// Enables "short writes". + /// + /// Normally, a write in OpenSSL will always write out all of the requested data, even if it + /// requires more than one TLS record or write to the underlying stream. This option will + /// cause a write to return after writing a single TLS record instead. + const ENABLE_PARTIAL_WRITE = ffi::SSL_MODE_ENABLE_PARTIAL_WRITE; + + /// Disables a check that the data buffer has not moved between calls when operating in a + /// non-blocking context. + const ACCEPT_MOVING_WRITE_BUFFER = ffi::SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER; + + /// Enables automatic retries after TLS session events such as renegotiations or heartbeats. + /// + /// By default, OpenSSL will return a `WantRead` error after a renegotiation or heartbeat. + /// This option will cause OpenSSL to automatically continue processing the requested + /// operation instead. + /// + /// Note that `SslStream::read` and `SslStream::write` will automatically retry regardless + /// of the state of this option. It only affects `SslStream::ssl_read` and + /// `SslStream::ssl_write`. + const AUTO_RETRY = ffi::SSL_MODE_AUTO_RETRY; + + /// Disables automatic chain building when verifying a peer's certificate. + /// + /// TLS peers are responsible for sending the entire certificate chain from the leaf to a + /// trusted root, but some will incorrectly not do so. OpenSSL will try to build the chain + /// out of certificates it knows of, and this option will disable that behavior. + const NO_AUTO_CHAIN = ffi::SSL_MODE_NO_AUTO_CHAIN; + + /// Release memory buffers when the session does not need them. + /// + /// This saves ~34 KiB of memory for idle streams. + const RELEASE_BUFFERS = ffi::SSL_MODE_RELEASE_BUFFERS; + + /// Sends the fake `TLS_FALLBACK_SCSV` cipher suite in the ClientHello message of a + /// handshake. + /// + /// This should only be enabled if a client has failed to connect to a server which + /// attempted to downgrade the protocol version of the session. + /// + /// Do not use this unless you know what you're doing! + #[cfg(not(libressl))] + const SEND_FALLBACK_SCSV = ffi::SSL_MODE_SEND_FALLBACK_SCSV; + } +} + +/// A type specifying the kind of protocol an `SslContext` will speak. +#[derive(Copy, Clone)] +pub struct SslMethod(*const ffi::SSL_METHOD); + +impl SslMethod { + /// Support all versions of the TLS protocol. + #[corresponds(TLS_method)] + pub fn tls() -> SslMethod { + unsafe { SslMethod(TLS_method()) } + } + + /// Support all versions of the DTLS protocol. + #[corresponds(DTLS_method)] + pub fn dtls() -> SslMethod { + unsafe { SslMethod(DTLS_method()) } + } + + /// Support all versions of the TLS protocol, explicitly as a client. + #[corresponds(TLS_client_method)] + pub fn tls_client() -> SslMethod { + unsafe { SslMethod(TLS_client_method()) } + } + + /// Support all versions of the TLS protocol, explicitly as a server. + #[corresponds(TLS_server_method)] + pub fn tls_server() -> SslMethod { + unsafe { SslMethod(TLS_server_method()) } + } + + /// Constructs an `SslMethod` from a pointer to the underlying OpenSSL value. + /// + /// # Safety + /// + /// The caller must ensure the pointer is valid. + pub unsafe fn from_ptr(ptr: *const ffi::SSL_METHOD) -> SslMethod { + SslMethod(ptr) + } + + /// Returns a pointer to the underlying OpenSSL value. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn as_ptr(&self) -> *const ffi::SSL_METHOD { + self.0 + } +} + +unsafe impl Sync for SslMethod {} +unsafe impl Send for SslMethod {} + +bitflags! { + /// Options controlling the behavior of certificate verification. + #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + #[repr(transparent)] + pub struct SslVerifyMode: i32 { + /// Verifies that the peer's certificate is trusted. + /// + /// On the server side, this will cause OpenSSL to request a certificate from the client. + const PEER = ffi::SSL_VERIFY_PEER; + + /// Disables verification of the peer's certificate. + /// + /// On the server side, this will cause OpenSSL to not request a certificate from the + /// client. On the client side, the certificate will be checked for validity, but the + /// negotiation will continue regardless of the result of that check. + const NONE = ffi::SSL_VERIFY_NONE; + + /// On the server side, abort the handshake if the client did not send a certificate. + /// + /// This should be paired with `SSL_VERIFY_PEER`. It has no effect on the client side. + const FAIL_IF_NO_PEER_CERT = ffi::SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + } +} + +#[cfg(boringssl)] +type SslBitType = c_int; +#[cfg(not(boringssl))] +type SslBitType = c_long; + +#[cfg(boringssl)] +type SslTimeTy = u64; +#[cfg(not(boringssl))] +type SslTimeTy = c_long; + +bitflags! { + /// Options controlling the behavior of session caching. + #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + #[repr(transparent)] + pub struct SslSessionCacheMode: SslBitType { + /// No session caching for the client or server takes place. + const OFF = ffi::SSL_SESS_CACHE_OFF; + + /// Enable session caching on the client side. + /// + /// OpenSSL has no way of identifying the proper session to reuse automatically, so the + /// application is responsible for setting it explicitly via [`SslRef::set_session`]. + /// + /// [`SslRef::set_session`]: struct.SslRef.html#method.set_session + const CLIENT = ffi::SSL_SESS_CACHE_CLIENT; + + /// Enable session caching on the server side. + /// + /// This is the default mode. + const SERVER = ffi::SSL_SESS_CACHE_SERVER; + + /// Enable session caching on both the client and server side. + const BOTH = ffi::SSL_SESS_CACHE_BOTH; + + /// Disable automatic removal of expired sessions from the session cache. + const NO_AUTO_CLEAR = ffi::SSL_SESS_CACHE_NO_AUTO_CLEAR; + + /// Disable use of the internal session cache for session lookups. + const NO_INTERNAL_LOOKUP = ffi::SSL_SESS_CACHE_NO_INTERNAL_LOOKUP; + + /// Disable use of the internal session cache for session storage. + const NO_INTERNAL_STORE = ffi::SSL_SESS_CACHE_NO_INTERNAL_STORE; + + /// Disable use of the internal session cache for storage and lookup. + const NO_INTERNAL = ffi::SSL_SESS_CACHE_NO_INTERNAL; + } +} + +#[cfg(ossl111)] +bitflags! { + /// Which messages and under which conditions an extension should be added or expected. + #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + #[repr(transparent)] + pub struct ExtensionContext: c_uint { + /// This extension is only allowed in TLS + const TLS_ONLY = ffi::SSL_EXT_TLS_ONLY; + /// This extension is only allowed in DTLS + const DTLS_ONLY = ffi::SSL_EXT_DTLS_ONLY; + /// Some extensions may be allowed in DTLS but we don't implement them for it + const TLS_IMPLEMENTATION_ONLY = ffi::SSL_EXT_TLS_IMPLEMENTATION_ONLY; + /// Most extensions are not defined for SSLv3 but EXT_TYPE_renegotiate is + const SSL3_ALLOWED = ffi::SSL_EXT_SSL3_ALLOWED; + /// Extension is only defined for TLS1.2 and below + const TLS1_2_AND_BELOW_ONLY = ffi::SSL_EXT_TLS1_2_AND_BELOW_ONLY; + /// Extension is only defined for TLS1.3 and above + const TLS1_3_ONLY = ffi::SSL_EXT_TLS1_3_ONLY; + /// Ignore this extension during parsing if we are resuming + const IGNORE_ON_RESUMPTION = ffi::SSL_EXT_IGNORE_ON_RESUMPTION; + const CLIENT_HELLO = ffi::SSL_EXT_CLIENT_HELLO; + /// Really means TLS1.2 or below + const TLS1_2_SERVER_HELLO = ffi::SSL_EXT_TLS1_2_SERVER_HELLO; + const TLS1_3_SERVER_HELLO = ffi::SSL_EXT_TLS1_3_SERVER_HELLO; + const TLS1_3_ENCRYPTED_EXTENSIONS = ffi::SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS; + const TLS1_3_HELLO_RETRY_REQUEST = ffi::SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST; + const TLS1_3_CERTIFICATE = ffi::SSL_EXT_TLS1_3_CERTIFICATE; + const TLS1_3_NEW_SESSION_TICKET = ffi::SSL_EXT_TLS1_3_NEW_SESSION_TICKET; + const TLS1_3_CERTIFICATE_REQUEST = ffi::SSL_EXT_TLS1_3_CERTIFICATE_REQUEST; + } +} + +/// An identifier of the format of a certificate or key file. +#[derive(Copy, Clone)] +pub struct SslFiletype(c_int); + +impl SslFiletype { + /// The PEM format. + /// + /// This corresponds to `SSL_FILETYPE_PEM`. + pub const PEM: SslFiletype = SslFiletype(ffi::SSL_FILETYPE_PEM); + + /// The ASN1 format. + /// + /// This corresponds to `SSL_FILETYPE_ASN1`. + pub const ASN1: SslFiletype = SslFiletype(ffi::SSL_FILETYPE_ASN1); + + /// Constructs an `SslFiletype` from a raw OpenSSL value. + pub fn from_raw(raw: c_int) -> SslFiletype { + SslFiletype(raw) + } + + /// Returns the raw OpenSSL value represented by this type. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn as_raw(&self) -> c_int { + self.0 + } +} + +/// An identifier of a certificate status type. +#[derive(Copy, Clone)] +pub struct StatusType(c_int); + +impl StatusType { + /// An OSCP status. + pub const OCSP: StatusType = StatusType(ffi::TLSEXT_STATUSTYPE_ocsp); + + /// Constructs a `StatusType` from a raw OpenSSL value. + pub fn from_raw(raw: c_int) -> StatusType { + StatusType(raw) + } + + /// Returns the raw OpenSSL value represented by this type. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn as_raw(&self) -> c_int { + self.0 + } +} + +/// An identifier of a session name type. +#[derive(Copy, Clone)] +pub struct NameType(c_int); + +impl NameType { + /// A host name. + pub const HOST_NAME: NameType = NameType(ffi::TLSEXT_NAMETYPE_host_name); + + /// Constructs a `StatusType` from a raw OpenSSL value. + pub fn from_raw(raw: c_int) -> StatusType { + StatusType(raw) + } + + /// Returns the raw OpenSSL value represented by this type. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn as_raw(&self) -> c_int { + self.0 + } +} + +static INDEXES: Lazy<Mutex<HashMap<TypeId, c_int>>> = Lazy::new(|| Mutex::new(HashMap::new())); +static SSL_INDEXES: Lazy<Mutex<HashMap<TypeId, c_int>>> = Lazy::new(|| Mutex::new(HashMap::new())); +static SESSION_CTX_INDEX: OnceCell<Index<Ssl, SslContext>> = OnceCell::new(); + +fn try_get_session_ctx_index() -> Result<&'static Index<Ssl, SslContext>, ErrorStack> { + SESSION_CTX_INDEX.get_or_try_init(Ssl::new_ex_index) +} + +unsafe extern "C" fn free_data_box<T>( + _parent: *mut c_void, + ptr: *mut c_void, + _ad: *mut ffi::CRYPTO_EX_DATA, + _idx: c_int, + _argl: c_long, + _argp: *mut c_void, +) { + if !ptr.is_null() { + let _ = Box::<T>::from_raw(ptr as *mut T); + } +} + +/// An error returned from the SNI callback. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct SniError(c_int); + +impl SniError { + /// Abort the handshake with a fatal alert. + pub const ALERT_FATAL: SniError = SniError(ffi::SSL_TLSEXT_ERR_ALERT_FATAL); + + /// Send a warning alert to the client and continue the handshake. + pub const ALERT_WARNING: SniError = SniError(ffi::SSL_TLSEXT_ERR_ALERT_WARNING); + + pub const NOACK: SniError = SniError(ffi::SSL_TLSEXT_ERR_NOACK); +} + +/// An SSL/TLS alert. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct SslAlert(c_int); + +impl SslAlert { + /// Alert 112 - `unrecognized_name`. + pub const UNRECOGNIZED_NAME: SslAlert = SslAlert(ffi::SSL_AD_UNRECOGNIZED_NAME); + pub const ILLEGAL_PARAMETER: SslAlert = SslAlert(ffi::SSL_AD_ILLEGAL_PARAMETER); + pub const DECODE_ERROR: SslAlert = SslAlert(ffi::SSL_AD_DECODE_ERROR); +} + +/// An error returned from an ALPN selection callback. +/// +/// Requires OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer. +#[cfg(any(ossl102, libressl261))] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct AlpnError(c_int); + +#[cfg(any(ossl102, libressl261))] +impl AlpnError { + /// Terminate the handshake with a fatal alert. + /// + /// Requires OpenSSL 1.1.0 or newer. + #[cfg(ossl110)] + pub const ALERT_FATAL: AlpnError = AlpnError(ffi::SSL_TLSEXT_ERR_ALERT_FATAL); + + /// Do not select a protocol, but continue the handshake. + pub const NOACK: AlpnError = AlpnError(ffi::SSL_TLSEXT_ERR_NOACK); +} + +/// The result of a client hello callback. +/// +/// Requires OpenSSL 1.1.1 or newer. +#[cfg(ossl111)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct ClientHelloResponse(c_int); + +#[cfg(ossl111)] +impl ClientHelloResponse { + /// Continue the handshake. + pub const SUCCESS: ClientHelloResponse = ClientHelloResponse(ffi::SSL_CLIENT_HELLO_SUCCESS); + + /// Return from the handshake with an `ErrorCode::WANT_CLIENT_HELLO_CB` error. + pub const RETRY: ClientHelloResponse = ClientHelloResponse(ffi::SSL_CLIENT_HELLO_RETRY); +} + +/// An SSL/TLS protocol version. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct SslVersion(c_int); + +impl SslVersion { + /// SSLv3 + pub const SSL3: SslVersion = SslVersion(ffi::SSL3_VERSION); + + /// TLSv1.0 + pub const TLS1: SslVersion = SslVersion(ffi::TLS1_VERSION); + + /// TLSv1.1 + pub const TLS1_1: SslVersion = SslVersion(ffi::TLS1_1_VERSION); + + /// TLSv1.2 + pub const TLS1_2: SslVersion = SslVersion(ffi::TLS1_2_VERSION); + + /// TLSv1.3 + /// + /// Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer. + #[cfg(any(ossl111, libressl340))] + pub const TLS1_3: SslVersion = SslVersion(ffi::TLS1_3_VERSION); + + /// DTLSv1.0 + /// + /// DTLS 1.0 corresponds to TLS 1.1. + pub const DTLS1: SslVersion = SslVersion(ffi::DTLS1_VERSION); + + /// DTLSv1.2 + /// + /// DTLS 1.2 corresponds to TLS 1.2 to harmonize versions. There was never a DTLS 1.1. + #[cfg(any(ossl102, libressl332))] + pub const DTLS1_2: SslVersion = SslVersion(ffi::DTLS1_2_VERSION); +} + +cfg_if! { + if #[cfg(boringssl)] { + type SslCacheTy = i64; + type SslCacheSize = libc::c_ulong; + type MtuTy = u32; + type SizeTy = usize; + } else { + type SslCacheTy = i64; + type SslCacheSize = c_long; + type MtuTy = c_long; + type SizeTy = u32; + } +} + +/// A standard implementation of protocol selection for Application Layer Protocol Negotiation +/// (ALPN). +/// +/// `server` should contain the server's list of supported protocols and `client` the client's. They +/// must both be in the ALPN wire format. See the documentation for +/// [`SslContextBuilder::set_alpn_protos`] for details. +/// +/// It will select the first protocol supported by the server which is also supported by the client. +/// +/// [`SslContextBuilder::set_alpn_protos`]: struct.SslContextBuilder.html#method.set_alpn_protos +#[corresponds(SSL_select_next_proto)] +pub fn select_next_proto<'a>(server: &[u8], client: &'a [u8]) -> Option<&'a [u8]> { + unsafe { + let mut out = ptr::null_mut(); + let mut outlen = 0; + let r = ffi::SSL_select_next_proto( + &mut out, + &mut outlen, + server.as_ptr(), + server.len() as c_uint, + client.as_ptr(), + client.len() as c_uint, + ); + if r == ffi::OPENSSL_NPN_NEGOTIATED { + Some(slice::from_raw_parts(out as *const u8, outlen as usize)) + } else { + None + } + } +} + +/// A builder for `SslContext`s. +pub struct SslContextBuilder(SslContext); + +impl SslContextBuilder { + /// Creates a new `SslContextBuilder`. + #[corresponds(SSL_CTX_new)] + pub fn new(method: SslMethod) -> Result<SslContextBuilder, ErrorStack> { + unsafe { + init(); + let ctx = cvt_p(ffi::SSL_CTX_new(method.as_ptr()))?; + + Ok(SslContextBuilder::from_ptr(ctx)) + } + } + + /// Creates an `SslContextBuilder` from a pointer to a raw OpenSSL value. + /// + /// # Safety + /// + /// The caller must ensure that the pointer is valid and uniquely owned by the builder. + pub unsafe fn from_ptr(ctx: *mut ffi::SSL_CTX) -> SslContextBuilder { + SslContextBuilder(SslContext::from_ptr(ctx)) + } + + /// Returns a pointer to the raw OpenSSL value. + pub fn as_ptr(&self) -> *mut ffi::SSL_CTX { + self.0.as_ptr() + } + + /// Configures the certificate verification method for new connections. + #[corresponds(SSL_CTX_set_verify)] + pub fn set_verify(&mut self, mode: SslVerifyMode) { + unsafe { + ffi::SSL_CTX_set_verify(self.as_ptr(), mode.bits() as c_int, None); + } + } + + /// Configures the certificate verification method for new connections and + /// registers a verification callback. + /// + /// The callback is passed a boolean indicating if OpenSSL's internal verification succeeded as + /// well as a reference to the `X509StoreContext` which can be used to examine the certificate + /// chain. It should return a boolean indicating if verification succeeded. + #[corresponds(SSL_CTX_set_verify)] + pub fn set_verify_callback<F>(&mut self, mode: SslVerifyMode, verify: F) + where + F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send, + { + unsafe { + self.set_ex_data(SslContext::cached_ex_index::<F>(), verify); + ffi::SSL_CTX_set_verify(self.as_ptr(), mode.bits() as c_int, Some(raw_verify::<F>)); + } + } + + /// Configures the server name indication (SNI) callback for new connections. + /// + /// SNI is used to allow a single server to handle requests for multiple domains, each of which + /// has its own certificate chain and configuration. + /// + /// Obtain the server name with the `servername` method and then set the corresponding context + /// with `set_ssl_context` + #[corresponds(SSL_CTX_set_tlsext_servername_callback)] + // FIXME tlsext prefix? + pub fn set_servername_callback<F>(&mut self, callback: F) + where + F: Fn(&mut SslRef, &mut SslAlert) -> Result<(), SniError> + 'static + Sync + Send, + { + unsafe { + // The SNI callback is somewhat unique in that the callback associated with the original + // context associated with an SSL can be used even if the SSL's context has been swapped + // out. When that happens, we wouldn't be able to look up the callback's state in the + // context's ex data. Instead, pass the pointer directly as the servername arg. It's + // still stored in ex data to manage the lifetime. + let arg = self.set_ex_data_inner(SslContext::cached_ex_index::<F>(), callback); + ffi::SSL_CTX_set_tlsext_servername_arg(self.as_ptr(), arg); + #[cfg(boringssl)] + ffi::SSL_CTX_set_tlsext_servername_callback(self.as_ptr(), Some(raw_sni::<F>)); + #[cfg(not(boringssl))] + ffi::SSL_CTX_set_tlsext_servername_callback__fixed_rust( + self.as_ptr(), + Some(raw_sni::<F>), + ); + } + } + + /// Sets the certificate verification depth. + /// + /// If the peer's certificate chain is longer than this value, verification will fail. + #[corresponds(SSL_CTX_set_verify_depth)] + pub fn set_verify_depth(&mut self, depth: u32) { + unsafe { + ffi::SSL_CTX_set_verify_depth(self.as_ptr(), depth as c_int); + } + } + + /// Sets a custom certificate store for verifying peer certificates. + /// + /// Requires OpenSSL 1.0.2 or newer. + #[corresponds(SSL_CTX_set0_verify_cert_store)] + #[cfg(ossl102)] + pub fn set_verify_cert_store(&mut self, cert_store: X509Store) -> Result<(), ErrorStack> { + unsafe { + let ptr = cert_store.as_ptr(); + cvt(ffi::SSL_CTX_set0_verify_cert_store(self.as_ptr(), ptr) as c_int)?; + mem::forget(cert_store); + + Ok(()) + } + } + + /// Replaces the context's certificate store. + #[corresponds(SSL_CTX_set_cert_store)] + pub fn set_cert_store(&mut self, cert_store: X509Store) { + unsafe { + ffi::SSL_CTX_set_cert_store(self.as_ptr(), cert_store.as_ptr()); + mem::forget(cert_store); + } + } + + /// Controls read ahead behavior. + /// + /// If enabled, OpenSSL will read as much data as is available from the underlying stream, + /// instead of a single record at a time. + /// + /// It has no effect when used with DTLS. + #[corresponds(SSL_CTX_set_read_ahead)] + pub fn set_read_ahead(&mut self, read_ahead: bool) { + unsafe { + ffi::SSL_CTX_set_read_ahead(self.as_ptr(), read_ahead as SslBitType); + } + } + + /// Sets the mode used by the context, returning the previous mode. + #[corresponds(SSL_CTX_set_mode)] + pub fn set_mode(&mut self, mode: SslMode) -> SslMode { + unsafe { + let bits = ffi::SSL_CTX_set_mode(self.as_ptr(), mode.bits() as MtuTy) as SslBitType; + SslMode::from_bits_retain(bits) + } + } + + /// Sets the parameters to be used during ephemeral Diffie-Hellman key exchange. + #[corresponds(SSL_CTX_set_tmp_dh)] + pub fn set_tmp_dh(&mut self, dh: &DhRef<Params>) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::SSL_CTX_set_tmp_dh(self.as_ptr(), dh.as_ptr()) as c_int).map(|_| ()) } + } + + /// Sets the callback which will generate parameters to be used during ephemeral Diffie-Hellman + /// key exchange. + /// + /// The callback is provided with a reference to the `Ssl` for the session, as well as a boolean + /// indicating if the selected cipher is export-grade, and the key length. The export and key + /// length options are archaic and should be ignored in almost all cases. + #[corresponds(SSL_CTX_set_tmp_dh_callback)] + pub fn set_tmp_dh_callback<F>(&mut self, callback: F) + where + F: Fn(&mut SslRef, bool, u32) -> Result<Dh<Params>, ErrorStack> + 'static + Sync + Send, + { + unsafe { + self.set_ex_data(SslContext::cached_ex_index::<F>(), callback); + + #[cfg(not(boringssl))] + ffi::SSL_CTX_set_tmp_dh_callback__fixed_rust(self.as_ptr(), Some(raw_tmp_dh::<F>)); + #[cfg(boringssl)] + ffi::SSL_CTX_set_tmp_dh_callback(self.as_ptr(), Some(raw_tmp_dh::<F>)); + } + } + + /// Sets the parameters to be used during ephemeral elliptic curve Diffie-Hellman key exchange. + #[corresponds(SSL_CTX_set_tmp_ecdh)] + pub fn set_tmp_ecdh(&mut self, key: &EcKeyRef<Params>) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::SSL_CTX_set_tmp_ecdh(self.as_ptr(), key.as_ptr()) as c_int).map(|_| ()) } + } + + /// Sets the callback which will generate parameters to be used during ephemeral elliptic curve + /// Diffie-Hellman key exchange. + /// + /// The callback is provided with a reference to the `Ssl` for the session, as well as a boolean + /// indicating if the selected cipher is export-grade, and the key length. The export and key + /// length options are archaic and should be ignored in almost all cases. + /// + /// Requires OpenSSL 1.0.1 or 1.0.2. + #[corresponds(SSL_CTX_set_tmp_ecdh_callback)] + #[cfg(all(ossl101, not(ossl110)))] + #[deprecated(note = "this function leaks memory and does not exist on newer OpenSSL versions")] + pub fn set_tmp_ecdh_callback<F>(&mut self, callback: F) + where + F: Fn(&mut SslRef, bool, u32) -> Result<EcKey<Params>, ErrorStack> + 'static + Sync + Send, + { + unsafe { + self.set_ex_data(SslContext::cached_ex_index::<F>(), callback); + ffi::SSL_CTX_set_tmp_ecdh_callback__fixed_rust(self.as_ptr(), Some(raw_tmp_ecdh::<F>)); + } + } + + /// Use the default locations of trusted certificates for verification. + /// + /// These locations are read from the `SSL_CERT_FILE` and `SSL_CERT_DIR` environment variables + /// if present, or defaults specified at OpenSSL build time otherwise. + #[corresponds(SSL_CTX_set_default_verify_paths)] + pub fn set_default_verify_paths(&mut self) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::SSL_CTX_set_default_verify_paths(self.as_ptr())).map(|_| ()) } + } + + /// Loads trusted root certificates from a file. + /// + /// The file should contain a sequence of PEM-formatted CA certificates. + #[corresponds(SSL_CTX_load_verify_locations)] + pub fn set_ca_file<P: AsRef<Path>>(&mut self, file: P) -> Result<(), ErrorStack> { + let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap(); + unsafe { + cvt(ffi::SSL_CTX_load_verify_locations( + self.as_ptr(), + file.as_ptr() as *const _, + ptr::null(), + )) + .map(|_| ()) + } + } + + /// Sets the list of CA names sent to the client. + /// + /// The CA certificates must still be added to the trust root - they are not automatically set + /// as trusted by this method. + #[corresponds(SSL_CTX_set_client_CA_list)] + pub fn set_client_ca_list(&mut self, list: Stack<X509Name>) { + unsafe { + ffi::SSL_CTX_set_client_CA_list(self.as_ptr(), list.as_ptr()); + mem::forget(list); + } + } + + /// Add the provided CA certificate to the list sent by the server to the client when + /// requesting client-side TLS authentication. + #[corresponds(SSL_CTX_add_client_CA)] + pub fn add_client_ca(&mut self, cacert: &X509Ref) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::SSL_CTX_add_client_CA(self.as_ptr(), cacert.as_ptr())).map(|_| ()) } + } + + /// Set the context identifier for sessions. + /// + /// This value identifies the server's session cache to clients, telling them when they're + /// able to reuse sessions. It should be set to a unique value per server, unless multiple + /// servers share a session cache. + /// + /// This value should be set when using client certificates, or each request will fail its + /// handshake and need to be restarted. + #[corresponds(SSL_CTX_set_session_id_context)] + pub fn set_session_id_context(&mut self, sid_ctx: &[u8]) -> Result<(), ErrorStack> { + unsafe { + assert!(sid_ctx.len() <= c_uint::max_value() as usize); + cvt(ffi::SSL_CTX_set_session_id_context( + self.as_ptr(), + sid_ctx.as_ptr(), + sid_ctx.len() as SizeTy, + )) + .map(|_| ()) + } + } + + /// Loads a leaf certificate from a file. + /// + /// Only a single certificate will be loaded - use `add_extra_chain_cert` to add the remainder + /// of the certificate chain, or `set_certificate_chain_file` to load the entire chain from a + /// single file. + #[corresponds(SSL_CTX_use_certificate_file)] + pub fn set_certificate_file<P: AsRef<Path>>( + &mut self, + file: P, + file_type: SslFiletype, + ) -> Result<(), ErrorStack> { + let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap(); + unsafe { + cvt(ffi::SSL_CTX_use_certificate_file( + self.as_ptr(), + file.as_ptr() as *const _, + file_type.as_raw(), + )) + .map(|_| ()) + } + } + + /// Loads a certificate chain from a file. + /// + /// The file should contain a sequence of PEM-formatted certificates, the first being the leaf + /// certificate, and the remainder forming the chain of certificates up to and including the + /// trusted root certificate. + #[corresponds(SSL_CTX_use_certificate_chain_file)] + pub fn set_certificate_chain_file<P: AsRef<Path>>( + &mut self, + file: P, + ) -> Result<(), ErrorStack> { + let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap(); + unsafe { + cvt(ffi::SSL_CTX_use_certificate_chain_file( + self.as_ptr(), + file.as_ptr() as *const _, + )) + .map(|_| ()) + } + } + + /// Sets the leaf certificate. + /// + /// Use `add_extra_chain_cert` to add the remainder of the certificate chain. + #[corresponds(SSL_CTX_use_certificate)] + pub fn set_certificate(&mut self, cert: &X509Ref) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::SSL_CTX_use_certificate(self.as_ptr(), cert.as_ptr())).map(|_| ()) } + } + + /// Appends a certificate to the certificate chain. + /// + /// This chain should contain all certificates necessary to go from the certificate specified by + /// `set_certificate` to a trusted root. + #[corresponds(SSL_CTX_add_extra_chain_cert)] + pub fn add_extra_chain_cert(&mut self, cert: X509) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::SSL_CTX_add_extra_chain_cert(self.as_ptr(), cert.as_ptr()) as c_int)?; + mem::forget(cert); + Ok(()) + } + } + + /// Loads the private key from a file. + #[corresponds(SSL_CTX_use_PrivateKey_file)] + pub fn set_private_key_file<P: AsRef<Path>>( + &mut self, + file: P, + file_type: SslFiletype, + ) -> Result<(), ErrorStack> { + let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap(); + unsafe { + cvt(ffi::SSL_CTX_use_PrivateKey_file( + self.as_ptr(), + file.as_ptr() as *const _, + file_type.as_raw(), + )) + .map(|_| ()) + } + } + + /// Sets the private key. + #[corresponds(SSL_CTX_use_PrivateKey)] + pub fn set_private_key<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack> + where + T: HasPrivate, + { + unsafe { cvt(ffi::SSL_CTX_use_PrivateKey(self.as_ptr(), key.as_ptr())).map(|_| ()) } + } + + /// Sets the list of supported ciphers for protocols before TLSv1.3. + /// + /// The `set_ciphersuites` method controls the cipher suites for TLSv1.3. + /// + /// See [`ciphers`] for details on the format. + /// + /// [`ciphers`]: https://www.openssl.org/docs/manmaster/apps/ciphers.html + #[corresponds(SSL_CTX_set_cipher_list)] + pub fn set_cipher_list(&mut self, cipher_list: &str) -> Result<(), ErrorStack> { + let cipher_list = CString::new(cipher_list).unwrap(); + unsafe { + cvt(ffi::SSL_CTX_set_cipher_list( + self.as_ptr(), + cipher_list.as_ptr() as *const _, + )) + .map(|_| ()) + } + } + + /// Sets the list of supported ciphers for the TLSv1.3 protocol. + /// + /// The `set_cipher_list` method controls the cipher suites for protocols before TLSv1.3. + /// + /// The format consists of TLSv1.3 cipher suite names separated by `:` characters in order of + /// preference. + /// + /// Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer. + #[corresponds(SSL_CTX_set_ciphersuites)] + #[cfg(any(ossl111, libressl340))] + pub fn set_ciphersuites(&mut self, cipher_list: &str) -> Result<(), ErrorStack> { + let cipher_list = CString::new(cipher_list).unwrap(); + unsafe { + cvt(ffi::SSL_CTX_set_ciphersuites( + self.as_ptr(), + cipher_list.as_ptr() as *const _, + )) + .map(|_| ()) + } + } + + /// Enables ECDHE key exchange with an automatically chosen curve list. + /// + /// Requires OpenSSL 1.0.2. + #[corresponds(SSL_CTX_set_ecdh_auto)] + #[cfg(any(libressl, all(ossl102, not(ossl110))))] + pub fn set_ecdh_auto(&mut self, onoff: bool) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::SSL_CTX_set_ecdh_auto(self.as_ptr(), onoff as c_int)).map(|_| ()) } + } + + /// Sets the options used by the context, returning the old set. + /// + /// # Note + /// + /// This *enables* the specified options, but does not disable unspecified options. Use + /// `clear_options` for that. + #[corresponds(SSL_CTX_set_options)] + pub fn set_options(&mut self, option: SslOptions) -> SslOptions { + let bits = + unsafe { ffi::SSL_CTX_set_options(self.as_ptr(), option.bits()) } as SslOptionsRepr; + SslOptions::from_bits_retain(bits) + } + + /// Returns the options used by the context. + #[corresponds(SSL_CTX_get_options)] + pub fn options(&self) -> SslOptions { + let bits = unsafe { ffi::SSL_CTX_get_options(self.as_ptr()) } as SslOptionsRepr; + SslOptions::from_bits_retain(bits) + } + + /// Clears the options used by the context, returning the old set. + #[corresponds(SSL_CTX_clear_options)] + pub fn clear_options(&mut self, option: SslOptions) -> SslOptions { + let bits = + unsafe { ffi::SSL_CTX_clear_options(self.as_ptr(), option.bits()) } as SslOptionsRepr; + SslOptions::from_bits_retain(bits) + } + + /// Sets the minimum supported protocol version. + /// + /// A value of `None` will enable protocol versions down to the lowest version supported by + /// OpenSSL. + /// + /// Requires OpenSSL 1.1.0 or LibreSSL 2.6.1 or newer. + #[corresponds(SSL_CTX_set_min_proto_version)] + #[cfg(any(ossl110, libressl261))] + pub fn set_min_proto_version(&mut self, version: Option<SslVersion>) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::SSL_CTX_set_min_proto_version( + self.as_ptr(), + version.map_or(0, |v| v.0 as _), + )) + .map(|_| ()) + } + } + + /// Sets the maximum supported protocol version. + /// + /// A value of `None` will enable protocol versions up to the highest version supported by + /// OpenSSL. + /// + /// Requires OpenSSL 1.1.0 or or LibreSSL 2.6.1 or newer. + #[corresponds(SSL_CTX_set_max_proto_version)] + #[cfg(any(ossl110, libressl261))] + pub fn set_max_proto_version(&mut self, version: Option<SslVersion>) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::SSL_CTX_set_max_proto_version( + self.as_ptr(), + version.map_or(0, |v| v.0 as _), + )) + .map(|_| ()) + } + } + + /// Gets the minimum supported protocol version. + /// + /// A value of `None` indicates that all versions down to the lowest version supported by + /// OpenSSL are enabled. + /// + /// Requires OpenSSL 1.1.0g or LibreSSL 2.7.0 or newer. + #[corresponds(SSL_CTX_get_min_proto_version)] + #[cfg(any(ossl110g, libressl270))] + pub fn min_proto_version(&mut self) -> Option<SslVersion> { + unsafe { + let r = ffi::SSL_CTX_get_min_proto_version(self.as_ptr()); + if r == 0 { + None + } else { + Some(SslVersion(r)) + } + } + } + + /// Gets the maximum supported protocol version. + /// + /// A value of `None` indicates that all versions up to the highest version supported by + /// OpenSSL are enabled. + /// + /// Requires OpenSSL 1.1.0g or LibreSSL 2.7.0 or newer. + #[corresponds(SSL_CTX_get_max_proto_version)] + #[cfg(any(ossl110g, libressl270))] + pub fn max_proto_version(&mut self) -> Option<SslVersion> { + unsafe { + let r = ffi::SSL_CTX_get_max_proto_version(self.as_ptr()); + if r == 0 { + None + } else { + Some(SslVersion(r)) + } + } + } + + /// Sets the protocols to sent to the server for Application Layer Protocol Negotiation (ALPN). + /// + /// The input must be in ALPN "wire format". It consists of a sequence of supported protocol + /// names prefixed by their byte length. For example, the protocol list consisting of `spdy/1` + /// and `http/1.1` is encoded as `b"\x06spdy/1\x08http/1.1"`. The protocols are ordered by + /// preference. + /// + /// Requires OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer. + #[corresponds(SSL_CTX_set_alpn_protos)] + #[cfg(any(ossl102, libressl261))] + pub fn set_alpn_protos(&mut self, protocols: &[u8]) -> Result<(), ErrorStack> { + unsafe { + assert!(protocols.len() <= c_uint::max_value() as usize); + let r = ffi::SSL_CTX_set_alpn_protos( + self.as_ptr(), + protocols.as_ptr(), + protocols.len() as c_uint, + ); + // fun fact, SSL_CTX_set_alpn_protos has a reversed return code D: + if r == 0 { + Ok(()) + } else { + Err(ErrorStack::get()) + } + } + } + + /// Enables the DTLS extension "use_srtp" as defined in RFC5764. + #[corresponds(SSL_CTX_set_tlsext_use_srtp)] + pub fn set_tlsext_use_srtp(&mut self, protocols: &str) -> Result<(), ErrorStack> { + unsafe { + let cstr = CString::new(protocols).unwrap(); + + let r = ffi::SSL_CTX_set_tlsext_use_srtp(self.as_ptr(), cstr.as_ptr()); + // fun fact, set_tlsext_use_srtp has a reversed return code D: + if r == 0 { + Ok(()) + } else { + Err(ErrorStack::get()) + } + } + } + + /// Sets the callback used by a server to select a protocol for Application Layer Protocol + /// Negotiation (ALPN). + /// + /// The callback is provided with the client's protocol list in ALPN wire format. See the + /// documentation for [`SslContextBuilder::set_alpn_protos`] for details. It should return one + /// of those protocols on success. The [`select_next_proto`] function implements the standard + /// protocol selection algorithm. + /// + /// Requires OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer. + /// + /// [`SslContextBuilder::set_alpn_protos`]: struct.SslContextBuilder.html#method.set_alpn_protos + /// [`select_next_proto`]: fn.select_next_proto.html + #[corresponds(SSL_CTX_set_alpn_select_cb)] + #[cfg(any(ossl102, libressl261))] + pub fn set_alpn_select_callback<F>(&mut self, callback: F) + where + F: for<'a> Fn(&mut SslRef, &'a [u8]) -> Result<&'a [u8], AlpnError> + 'static + Sync + Send, + { + unsafe { + self.set_ex_data(SslContext::cached_ex_index::<F>(), callback); + ffi::SSL_CTX_set_alpn_select_cb__fixed_rust( + self.as_ptr(), + Some(callbacks::raw_alpn_select::<F>), + ptr::null_mut(), + ); + } + } + + /// Checks for consistency between the private key and certificate. + #[corresponds(SSL_CTX_check_private_key)] + pub fn check_private_key(&self) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::SSL_CTX_check_private_key(self.as_ptr())).map(|_| ()) } + } + + /// Returns a shared reference to the context's certificate store. + #[corresponds(SSL_CTX_get_cert_store)] + pub fn cert_store(&self) -> &X509StoreBuilderRef { + unsafe { X509StoreBuilderRef::from_ptr(ffi::SSL_CTX_get_cert_store(self.as_ptr())) } + } + + /// Returns a mutable reference to the context's certificate store. + #[corresponds(SSL_CTX_get_cert_store)] + pub fn cert_store_mut(&mut self) -> &mut X509StoreBuilderRef { + unsafe { X509StoreBuilderRef::from_ptr_mut(ffi::SSL_CTX_get_cert_store(self.as_ptr())) } + } + + /// Returns a reference to the X509 verification configuration. + /// + /// Requires OpenSSL 1.0.2 or newer. + #[corresponds(SSL_CTX_get0_param)] + #[cfg(any(ossl102, libressl261))] + pub fn verify_param(&self) -> &X509VerifyParamRef { + unsafe { X509VerifyParamRef::from_ptr(ffi::SSL_CTX_get0_param(self.as_ptr())) } + } + + /// Returns a mutable reference to the X509 verification configuration. + /// + /// Requires OpenSSL 1.0.2 or newer. + #[corresponds(SSL_CTX_get0_param)] + #[cfg(any(ossl102, libressl261))] + pub fn verify_param_mut(&mut self) -> &mut X509VerifyParamRef { + unsafe { X509VerifyParamRef::from_ptr_mut(ffi::SSL_CTX_get0_param(self.as_ptr())) } + } + + /// Sets the callback dealing with OCSP stapling. + /// + /// On the client side, this callback is responsible for validating the OCSP status response + /// returned by the server. The status may be retrieved with the `SslRef::ocsp_status` method. + /// A response of `Ok(true)` indicates that the OCSP status is valid, and a response of + /// `Ok(false)` indicates that the OCSP status is invalid and the handshake should be + /// terminated. + /// + /// On the server side, this callback is responsible for setting the OCSP status response to be + /// returned to clients. The status may be set with the `SslRef::set_ocsp_status` method. A + /// response of `Ok(true)` indicates that the OCSP status should be returned to the client, and + /// `Ok(false)` indicates that the status should not be returned to the client. + #[corresponds(SSL_CTX_set_tlsext_status_cb)] + pub fn set_status_callback<F>(&mut self, callback: F) -> Result<(), ErrorStack> + where + F: Fn(&mut SslRef) -> Result<bool, ErrorStack> + 'static + Sync + Send, + { + unsafe { + self.set_ex_data(SslContext::cached_ex_index::<F>(), callback); + cvt( + ffi::SSL_CTX_set_tlsext_status_cb(self.as_ptr(), Some(raw_tlsext_status::<F>)) + as c_int, + ) + .map(|_| ()) + } + } + + /// Sets the callback for providing an identity and pre-shared key for a TLS-PSK client. + /// + /// The callback will be called with the SSL context, an identity hint if one was provided + /// by the server, a mutable slice for each of the identity and pre-shared key bytes. The + /// identity must be written as a null-terminated C string. + #[corresponds(SSL_CTX_set_psk_client_callback)] + #[cfg(not(osslconf = "OPENSSL_NO_PSK"))] + pub fn set_psk_client_callback<F>(&mut self, callback: F) + where + F: Fn(&mut SslRef, Option<&[u8]>, &mut [u8], &mut [u8]) -> Result<usize, ErrorStack> + + 'static + + Sync + + Send, + { + unsafe { + self.set_ex_data(SslContext::cached_ex_index::<F>(), callback); + ffi::SSL_CTX_set_psk_client_callback(self.as_ptr(), Some(raw_client_psk::<F>)); + } + } + + #[deprecated(since = "0.10.10", note = "renamed to `set_psk_client_callback`")] + #[cfg(not(osslconf = "OPENSSL_NO_PSK"))] + pub fn set_psk_callback<F>(&mut self, callback: F) + where + F: Fn(&mut SslRef, Option<&[u8]>, &mut [u8], &mut [u8]) -> Result<usize, ErrorStack> + + 'static + + Sync + + Send, + { + self.set_psk_client_callback(callback) + } + + /// Sets the callback for providing an identity and pre-shared key for a TLS-PSK server. + /// + /// The callback will be called with the SSL context, an identity provided by the client, + /// and, a mutable slice for the pre-shared key bytes. The callback returns the number of + /// bytes in the pre-shared key. + #[corresponds(SSL_CTX_set_psk_server_callback)] + #[cfg(not(osslconf = "OPENSSL_NO_PSK"))] + pub fn set_psk_server_callback<F>(&mut self, callback: F) + where + F: Fn(&mut SslRef, Option<&[u8]>, &mut [u8]) -> Result<usize, ErrorStack> + + 'static + + Sync + + Send, + { + unsafe { + self.set_ex_data(SslContext::cached_ex_index::<F>(), callback); + ffi::SSL_CTX_set_psk_server_callback(self.as_ptr(), Some(raw_server_psk::<F>)); + } + } + + /// Sets the callback which is called when new sessions are negotiated. + /// + /// This can be used by clients to implement session caching. While in TLSv1.2 the session is + /// available to access via [`SslRef::session`] immediately after the handshake completes, this + /// is not the case for TLSv1.3. There, a session is not generally available immediately, and + /// the server may provide multiple session tokens to the client over a single session. The new + /// session callback is a portable way to deal with both cases. + /// + /// Note that session caching must be enabled for the callback to be invoked, and it defaults + /// off for clients. [`set_session_cache_mode`] controls that behavior. + /// + /// [`SslRef::session`]: struct.SslRef.html#method.session + /// [`set_session_cache_mode`]: #method.set_session_cache_mode + #[corresponds(SSL_CTX_sess_set_new_cb)] + pub fn set_new_session_callback<F>(&mut self, callback: F) + where + F: Fn(&mut SslRef, SslSession) + 'static + Sync + Send, + { + unsafe { + self.set_ex_data(SslContext::cached_ex_index::<F>(), callback); + ffi::SSL_CTX_sess_set_new_cb(self.as_ptr(), Some(callbacks::raw_new_session::<F>)); + } + } + + /// Sets the callback which is called when sessions are removed from the context. + /// + /// Sessions can be removed because they have timed out or because they are considered faulty. + #[corresponds(SSL_CTX_sess_set_remove_cb)] + pub fn set_remove_session_callback<F>(&mut self, callback: F) + where + F: Fn(&SslContextRef, &SslSessionRef) + 'static + Sync + Send, + { + unsafe { + self.set_ex_data(SslContext::cached_ex_index::<F>(), callback); + ffi::SSL_CTX_sess_set_remove_cb( + self.as_ptr(), + Some(callbacks::raw_remove_session::<F>), + ); + } + } + + /// Sets the callback which is called when a client proposed to resume a session but it was not + /// found in the internal cache. + /// + /// The callback is passed a reference to the session ID provided by the client. It should + /// return the session corresponding to that ID if available. This is only used for servers, not + /// clients. + /// + /// # Safety + /// + /// The returned `SslSession` must not be associated with a different `SslContext`. + #[corresponds(SSL_CTX_sess_set_get_cb)] + pub unsafe fn set_get_session_callback<F>(&mut self, callback: F) + where + F: Fn(&mut SslRef, &[u8]) -> Option<SslSession> + 'static + Sync + Send, + { + self.set_ex_data(SslContext::cached_ex_index::<F>(), callback); + ffi::SSL_CTX_sess_set_get_cb(self.as_ptr(), Some(callbacks::raw_get_session::<F>)); + } + + /// Sets the TLS key logging callback. + /// + /// The callback is invoked whenever TLS key material is generated, and is passed a line of NSS + /// SSLKEYLOGFILE-formatted text. This can be used by tools like Wireshark to decrypt message + /// traffic. The line does not contain a trailing newline. + /// + /// Requires OpenSSL 1.1.1 or newer. + #[corresponds(SSL_CTX_set_keylog_callback)] + #[cfg(ossl111)] + pub fn set_keylog_callback<F>(&mut self, callback: F) + where + F: Fn(&SslRef, &str) + 'static + Sync + Send, + { + unsafe { + self.set_ex_data(SslContext::cached_ex_index::<F>(), callback); + ffi::SSL_CTX_set_keylog_callback(self.as_ptr(), Some(callbacks::raw_keylog::<F>)); + } + } + + /// Sets the session caching mode use for connections made with the context. + /// + /// Returns the previous session caching mode. + #[corresponds(SSL_CTX_set_session_cache_mode)] + pub fn set_session_cache_mode(&mut self, mode: SslSessionCacheMode) -> SslSessionCacheMode { + unsafe { + let bits = ffi::SSL_CTX_set_session_cache_mode(self.as_ptr(), mode.bits()); + SslSessionCacheMode::from_bits_retain(bits) + } + } + + /// Sets the callback for generating an application cookie for TLS1.3 + /// stateless handshakes. + /// + /// The callback will be called with the SSL context and a slice into which the cookie + /// should be written. The callback should return the number of bytes written. + #[corresponds(SSL_CTX_set_stateless_cookie_generate_cb)] + #[cfg(ossl111)] + pub fn set_stateless_cookie_generate_cb<F>(&mut self, callback: F) + where + F: Fn(&mut SslRef, &mut [u8]) -> Result<usize, ErrorStack> + 'static + Sync + Send, + { + unsafe { + self.set_ex_data(SslContext::cached_ex_index::<F>(), callback); + ffi::SSL_CTX_set_stateless_cookie_generate_cb( + self.as_ptr(), + Some(raw_stateless_cookie_generate::<F>), + ); + } + } + + /// Sets the callback for verifying an application cookie for TLS1.3 + /// stateless handshakes. + /// + /// The callback will be called with the SSL context and the cookie supplied by the + /// client. It should return true if and only if the cookie is valid. + /// + /// Note that the OpenSSL implementation independently verifies the integrity of + /// application cookies using an HMAC before invoking the supplied callback. + #[corresponds(SSL_CTX_set_stateless_cookie_verify_cb)] + #[cfg(ossl111)] + pub fn set_stateless_cookie_verify_cb<F>(&mut self, callback: F) + where + F: Fn(&mut SslRef, &[u8]) -> bool + 'static + Sync + Send, + { + unsafe { + self.set_ex_data(SslContext::cached_ex_index::<F>(), callback); + ffi::SSL_CTX_set_stateless_cookie_verify_cb( + self.as_ptr(), + Some(raw_stateless_cookie_verify::<F>), + ) + } + } + + /// Sets the callback for generating a DTLSv1 cookie + /// + /// The callback will be called with the SSL context and a slice into which the cookie + /// should be written. The callback should return the number of bytes written. + #[corresponds(SSL_CTX_set_cookie_generate_cb)] + #[cfg(not(boringssl))] + pub fn set_cookie_generate_cb<F>(&mut self, callback: F) + where + F: Fn(&mut SslRef, &mut [u8]) -> Result<usize, ErrorStack> + 'static + Sync + Send, + { + unsafe { + self.set_ex_data(SslContext::cached_ex_index::<F>(), callback); + ffi::SSL_CTX_set_cookie_generate_cb(self.as_ptr(), Some(raw_cookie_generate::<F>)); + } + } + + /// Sets the callback for verifying a DTLSv1 cookie + /// + /// The callback will be called with the SSL context and the cookie supplied by the + /// client. It should return true if and only if the cookie is valid. + #[corresponds(SSL_CTX_set_cookie_verify_cb)] + #[cfg(not(boringssl))] + pub fn set_cookie_verify_cb<F>(&mut self, callback: F) + where + F: Fn(&mut SslRef, &[u8]) -> bool + 'static + Sync + Send, + { + unsafe { + self.set_ex_data(SslContext::cached_ex_index::<F>(), callback); + ffi::SSL_CTX_set_cookie_verify_cb(self.as_ptr(), Some(raw_cookie_verify::<F>)); + } + } + + /// Sets the extra data at the specified index. + /// + /// This can be used to provide data to callbacks registered with the context. Use the + /// `SslContext::new_ex_index` method to create an `Index`. + #[corresponds(SSL_CTX_set_ex_data)] + pub fn set_ex_data<T>(&mut self, index: Index<SslContext, T>, data: T) { + self.set_ex_data_inner(index, data); + } + + fn set_ex_data_inner<T>(&mut self, index: Index<SslContext, T>, data: T) -> *mut c_void { + unsafe { + let data = Box::into_raw(Box::new(data)) as *mut c_void; + ffi::SSL_CTX_set_ex_data(self.as_ptr(), index.as_raw(), data); + data + } + } + + /// Adds a custom extension for a TLS/DTLS client or server for all supported protocol versions. + /// + /// Requires OpenSSL 1.1.1 or newer. + #[corresponds(SSL_CTX_add_custom_ext)] + #[cfg(ossl111)] + pub fn add_custom_ext<AddFn, ParseFn, T>( + &mut self, + ext_type: u16, + context: ExtensionContext, + add_cb: AddFn, + parse_cb: ParseFn, + ) -> Result<(), ErrorStack> + where + AddFn: Fn( + &mut SslRef, + ExtensionContext, + Option<(usize, &X509Ref)>, + ) -> Result<Option<T>, SslAlert> + + 'static + + Sync + + Send, + T: AsRef<[u8]> + 'static + Sync + Send, + ParseFn: Fn( + &mut SslRef, + ExtensionContext, + &[u8], + Option<(usize, &X509Ref)>, + ) -> Result<(), SslAlert> + + 'static + + Sync + + Send, + { + let ret = unsafe { + self.set_ex_data(SslContext::cached_ex_index::<AddFn>(), add_cb); + self.set_ex_data(SslContext::cached_ex_index::<ParseFn>(), parse_cb); + + ffi::SSL_CTX_add_custom_ext( + self.as_ptr(), + ext_type as c_uint, + context.bits(), + Some(raw_custom_ext_add::<AddFn, T>), + Some(raw_custom_ext_free::<T>), + ptr::null_mut(), + Some(raw_custom_ext_parse::<ParseFn>), + ptr::null_mut(), + ) + }; + if ret == 1 { + Ok(()) + } else { + Err(ErrorStack::get()) + } + } + + /// Sets the maximum amount of early data that will be accepted on incoming connections. + /// + /// Defaults to 0. + /// + /// Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer. + #[corresponds(SSL_CTX_set_max_early_data)] + #[cfg(any(ossl111, libressl340))] + pub fn set_max_early_data(&mut self, bytes: u32) -> Result<(), ErrorStack> { + if unsafe { ffi::SSL_CTX_set_max_early_data(self.as_ptr(), bytes) } == 1 { + Ok(()) + } else { + Err(ErrorStack::get()) + } + } + + /// Sets a callback which will be invoked just after the client's hello message is received. + /// + /// Requires OpenSSL 1.1.1 or newer. + #[corresponds(SSL_CTX_set_client_hello_cb)] + #[cfg(ossl111)] + pub fn set_client_hello_callback<F>(&mut self, callback: F) + where + F: Fn(&mut SslRef, &mut SslAlert) -> Result<ClientHelloResponse, ErrorStack> + + 'static + + Sync + + Send, + { + unsafe { + let ptr = self.set_ex_data_inner(SslContext::cached_ex_index::<F>(), callback); + ffi::SSL_CTX_set_client_hello_cb( + self.as_ptr(), + Some(callbacks::raw_client_hello::<F>), + ptr, + ); + } + } + + /// Sets the context's session cache size limit, returning the previous limit. + /// + /// A value of 0 means that the cache size is unbounded. + #[corresponds(SSL_CTX_sess_set_cache_size)] + #[allow(clippy::useless_conversion)] + pub fn set_session_cache_size(&mut self, size: i32) -> i64 { + unsafe { + ffi::SSL_CTX_sess_set_cache_size(self.as_ptr(), size as SslCacheSize) as SslCacheTy + } + } + + /// Sets the context's supported signature algorithms. + /// + /// Requires OpenSSL 1.0.2 or newer. + #[corresponds(SSL_CTX_set1_sigalgs_list)] + #[cfg(ossl102)] + pub fn set_sigalgs_list(&mut self, sigalgs: &str) -> Result<(), ErrorStack> { + let sigalgs = CString::new(sigalgs).unwrap(); + unsafe { + cvt(ffi::SSL_CTX_set1_sigalgs_list(self.as_ptr(), sigalgs.as_ptr()) as c_int) + .map(|_| ()) + } + } + + /// Sets the context's supported elliptic curve groups. + /// + /// Requires OpenSSL 1.1.1 or LibreSSL 2.5.1 or newer. + #[corresponds(SSL_CTX_set1_groups_list)] + #[cfg(any(ossl111, libressl251))] + pub fn set_groups_list(&mut self, groups: &str) -> Result<(), ErrorStack> { + let groups = CString::new(groups).unwrap(); + unsafe { + cvt(ffi::SSL_CTX_set1_groups_list(self.as_ptr(), groups.as_ptr()) as c_int).map(|_| ()) + } + } + + /// Sets the number of TLS 1.3 session tickets that will be sent to a client after a full + /// handshake. + /// + /// Requires OpenSSL 1.1.1 or newer. + #[corresponds(SSL_CTX_set_num_tickets)] + #[cfg(ossl111)] + pub fn set_num_tickets(&mut self, num_tickets: usize) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::SSL_CTX_set_num_tickets(self.as_ptr(), num_tickets)).map(|_| ()) } + } + + /// Consumes the builder, returning a new `SslContext`. + pub fn build(self) -> SslContext { + self.0 + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::SSL_CTX; + fn drop = ffi::SSL_CTX_free; + + /// A context object for TLS streams. + /// + /// Applications commonly configure a single `SslContext` that is shared by all of its + /// `SslStreams`. + pub struct SslContext; + + /// Reference to [`SslContext`] + /// + /// [`SslContext`]: struct.SslContext.html + pub struct SslContextRef; +} + +impl Clone for SslContext { + fn clone(&self) -> Self { + (**self).to_owned() + } +} + +impl ToOwned for SslContextRef { + type Owned = SslContext; + + fn to_owned(&self) -> Self::Owned { + unsafe { + SSL_CTX_up_ref(self.as_ptr()); + SslContext::from_ptr(self.as_ptr()) + } + } +} + +// TODO: add useful info here +impl fmt::Debug for SslContext { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "SslContext") + } +} + +impl SslContext { + /// Creates a new builder object for an `SslContext`. + pub fn builder(method: SslMethod) -> Result<SslContextBuilder, ErrorStack> { + SslContextBuilder::new(method) + } + + /// Returns a new extra data index. + /// + /// Each invocation of this function is guaranteed to return a distinct index. These can be used + /// to store data in the context that can be retrieved later by callbacks, for example. + #[corresponds(SSL_CTX_get_ex_new_index)] + pub fn new_ex_index<T>() -> Result<Index<SslContext, T>, ErrorStack> + where + T: 'static + Sync + Send, + { + unsafe { + ffi::init(); + #[cfg(boringssl)] + let idx = cvt_n(get_new_idx(Some(free_data_box::<T>)))?; + #[cfg(not(boringssl))] + let idx = cvt_n(get_new_idx(free_data_box::<T>))?; + Ok(Index::from_raw(idx)) + } + } + + // FIXME should return a result? + fn cached_ex_index<T>() -> Index<SslContext, T> + where + T: 'static + Sync + Send, + { + unsafe { + let idx = *INDEXES + .lock() + .unwrap_or_else(|e| e.into_inner()) + .entry(TypeId::of::<T>()) + .or_insert_with(|| SslContext::new_ex_index::<T>().unwrap().as_raw()); + Index::from_raw(idx) + } + } +} + +impl SslContextRef { + /// Returns the certificate associated with this `SslContext`, if present. + /// + /// Requires OpenSSL 1.0.2 or LibreSSL 2.7.0 or newer. + #[corresponds(SSL_CTX_get0_certificate)] + #[cfg(any(ossl102, libressl270))] + pub fn certificate(&self) -> Option<&X509Ref> { + unsafe { + let ptr = ffi::SSL_CTX_get0_certificate(self.as_ptr()); + X509Ref::from_const_ptr_opt(ptr) + } + } + + /// Returns the private key associated with this `SslContext`, if present. + /// + /// Requires OpenSSL 1.0.2 or LibreSSL 3.4.0 or newer. + #[corresponds(SSL_CTX_get0_privatekey)] + #[cfg(any(ossl102, libressl340))] + pub fn private_key(&self) -> Option<&PKeyRef<Private>> { + unsafe { + let ptr = ffi::SSL_CTX_get0_privatekey(self.as_ptr()); + PKeyRef::from_const_ptr_opt(ptr) + } + } + + /// Returns a shared reference to the certificate store used for verification. + #[corresponds(SSL_CTX_get_cert_store)] + pub fn cert_store(&self) -> &X509StoreRef { + unsafe { X509StoreRef::from_ptr(ffi::SSL_CTX_get_cert_store(self.as_ptr())) } + } + + /// Returns a shared reference to the stack of certificates making up the chain from the leaf. + #[corresponds(SSL_CTX_get_extra_chain_certs)] + pub fn extra_chain_certs(&self) -> &StackRef<X509> { + unsafe { + let mut chain = ptr::null_mut(); + ffi::SSL_CTX_get_extra_chain_certs(self.as_ptr(), &mut chain); + StackRef::from_const_ptr_opt(chain).expect("extra chain certs must not be null") + } + } + + /// Returns a reference to the extra data at the specified index. + #[corresponds(SSL_CTX_get_ex_data)] + pub fn ex_data<T>(&self, index: Index<SslContext, T>) -> Option<&T> { + unsafe { + let data = ffi::SSL_CTX_get_ex_data(self.as_ptr(), index.as_raw()); + if data.is_null() { + None + } else { + Some(&*(data as *const T)) + } + } + } + + /// Gets the maximum amount of early data that will be accepted on incoming connections. + /// + /// Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer. + #[corresponds(SSL_CTX_get_max_early_data)] + #[cfg(any(ossl111, libressl340))] + pub fn max_early_data(&self) -> u32 { + unsafe { ffi::SSL_CTX_get_max_early_data(self.as_ptr()) } + } + + /// Adds a session to the context's cache. + /// + /// Returns `true` if the session was successfully added to the cache, and `false` if it was already present. + /// + /// # Safety + /// + /// The caller of this method is responsible for ensuring that the session has never been used with another + /// `SslContext` than this one. + #[corresponds(SSL_CTX_add_session)] + pub unsafe fn add_session(&self, session: &SslSessionRef) -> bool { + ffi::SSL_CTX_add_session(self.as_ptr(), session.as_ptr()) != 0 + } + + /// Removes a session from the context's cache and marks it as non-resumable. + /// + /// Returns `true` if the session was successfully found and removed, and `false` otherwise. + /// + /// # Safety + /// + /// The caller of this method is responsible for ensuring that the session has never been used with another + /// `SslContext` than this one. + #[corresponds(SSL_CTX_remove_session)] + pub unsafe fn remove_session(&self, session: &SslSessionRef) -> bool { + ffi::SSL_CTX_remove_session(self.as_ptr(), session.as_ptr()) != 0 + } + + /// Returns the context's session cache size limit. + /// + /// A value of 0 means that the cache size is unbounded. + #[corresponds(SSL_CTX_sess_get_cache_size)] + #[allow(clippy::unnecessary_cast)] + pub fn session_cache_size(&self) -> i64 { + unsafe { ffi::SSL_CTX_sess_get_cache_size(self.as_ptr()) as i64 } + } + + /// Returns the verify mode that was set on this context from [`SslContextBuilder::set_verify`]. + /// + /// [`SslContextBuilder::set_verify`]: struct.SslContextBuilder.html#method.set_verify + #[corresponds(SSL_CTX_get_verify_mode)] + pub fn verify_mode(&self) -> SslVerifyMode { + let mode = unsafe { ffi::SSL_CTX_get_verify_mode(self.as_ptr()) }; + SslVerifyMode::from_bits(mode).expect("SSL_CTX_get_verify_mode returned invalid mode") + } + + /// Gets the number of TLS 1.3 session tickets that will be sent to a client after a full + /// handshake. + /// + /// Requires OpenSSL 1.1.1 or newer. + #[corresponds(SSL_CTX_get_num_tickets)] + #[cfg(ossl111)] + pub fn num_tickets(&self) -> usize { + unsafe { ffi::SSL_CTX_get_num_tickets(self.as_ptr()) } + } +} + +/// Information about the state of a cipher. +pub struct CipherBits { + /// The number of secret bits used for the cipher. + pub secret: i32, + + /// The number of bits processed by the chosen algorithm. + pub algorithm: i32, +} + +/// Information about a cipher. +pub struct SslCipher(*mut ffi::SSL_CIPHER); + +impl ForeignType for SslCipher { + type CType = ffi::SSL_CIPHER; + type Ref = SslCipherRef; + + #[inline] + unsafe fn from_ptr(ptr: *mut ffi::SSL_CIPHER) -> SslCipher { + SslCipher(ptr) + } + + #[inline] + fn as_ptr(&self) -> *mut ffi::SSL_CIPHER { + self.0 + } +} + +impl Stackable for SslCipher { + type StackType = ffi::stack_st_SSL_CIPHER; +} + +impl Deref for SslCipher { + type Target = SslCipherRef; + + fn deref(&self) -> &SslCipherRef { + unsafe { SslCipherRef::from_ptr(self.0) } + } +} + +impl DerefMut for SslCipher { + fn deref_mut(&mut self) -> &mut SslCipherRef { + unsafe { SslCipherRef::from_ptr_mut(self.0) } + } +} + +/// Reference to an [`SslCipher`]. +/// +/// [`SslCipher`]: struct.SslCipher.html +pub struct SslCipherRef(Opaque); + +impl ForeignTypeRef for SslCipherRef { + type CType = ffi::SSL_CIPHER; +} + +impl SslCipherRef { + /// Returns the name of the cipher. + #[corresponds(SSL_CIPHER_get_name)] + pub fn name(&self) -> &'static str { + unsafe { + let ptr = ffi::SSL_CIPHER_get_name(self.as_ptr()); + CStr::from_ptr(ptr).to_str().unwrap() + } + } + + /// Returns the RFC-standard name of the cipher, if one exists. + /// + /// Requires OpenSSL 1.1.1 or newer. + #[corresponds(SSL_CIPHER_standard_name)] + #[cfg(ossl111)] + pub fn standard_name(&self) -> Option<&'static str> { + unsafe { + let ptr = ffi::SSL_CIPHER_standard_name(self.as_ptr()); + if ptr.is_null() { + None + } else { + Some(CStr::from_ptr(ptr).to_str().unwrap()) + } + } + } + + /// Returns the SSL/TLS protocol version that first defined the cipher. + #[corresponds(SSL_CIPHER_get_version)] + pub fn version(&self) -> &'static str { + let version = unsafe { + let ptr = ffi::SSL_CIPHER_get_version(self.as_ptr()); + CStr::from_ptr(ptr as *const _) + }; + + str::from_utf8(version.to_bytes()).unwrap() + } + + /// Returns the number of bits used for the cipher. + #[corresponds(SSL_CIPHER_get_bits)] + #[allow(clippy::useless_conversion)] + pub fn bits(&self) -> CipherBits { + unsafe { + let mut algo_bits = 0; + let secret_bits = ffi::SSL_CIPHER_get_bits(self.as_ptr(), &mut algo_bits); + CipherBits { + secret: secret_bits.into(), + algorithm: algo_bits.into(), + } + } + } + + /// Returns a textual description of the cipher. + #[corresponds(SSL_CIPHER_description)] + pub fn description(&self) -> String { + unsafe { + // SSL_CIPHER_description requires a buffer of at least 128 bytes. + let mut buf = [0; 128]; + let ptr = ffi::SSL_CIPHER_description(self.as_ptr(), buf.as_mut_ptr(), 128); + String::from_utf8(CStr::from_ptr(ptr as *const _).to_bytes().to_vec()).unwrap() + } + } + + /// Returns the handshake digest of the cipher. + /// + /// Requires OpenSSL 1.1.1 or newer. + #[corresponds(SSL_CIPHER_get_handshake_digest)] + #[cfg(ossl111)] + pub fn handshake_digest(&self) -> Option<MessageDigest> { + unsafe { + let ptr = ffi::SSL_CIPHER_get_handshake_digest(self.as_ptr()); + if ptr.is_null() { + None + } else { + Some(MessageDigest::from_ptr(ptr)) + } + } + } + + /// Returns the NID corresponding to the cipher. + /// + /// Requires OpenSSL 1.1.0 or LibreSSL 2.7.0 or newer. + #[corresponds(SSL_CIPHER_get_cipher_nid)] + #[cfg(any(ossl110, libressl270))] + pub fn cipher_nid(&self) -> Option<Nid> { + let n = unsafe { ffi::SSL_CIPHER_get_cipher_nid(self.as_ptr()) }; + if n == 0 { + None + } else { + Some(Nid::from_raw(n)) + } + } +} + +impl fmt::Debug for SslCipherRef { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "{}", self.name()) + } +} + +/// A stack of selected ciphers, and a stack of selected signalling cipher suites +#[derive(Debug)] +pub struct CipherLists { + pub suites: Stack<SslCipher>, + pub signalling_suites: Stack<SslCipher>, +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::SSL_SESSION; + fn drop = ffi::SSL_SESSION_free; + + /// An encoded SSL session. + /// + /// These can be cached to share sessions across connections. + pub struct SslSession; + + /// Reference to [`SslSession`]. + /// + /// [`SslSession`]: struct.SslSession.html + pub struct SslSessionRef; +} + +impl Clone for SslSession { + fn clone(&self) -> SslSession { + SslSessionRef::to_owned(self) + } +} + +impl SslSession { + from_der! { + /// Deserializes a DER-encoded session structure. + #[corresponds(d2i_SSL_SESSION)] + from_der, + SslSession, + ffi::d2i_SSL_SESSION + } +} + +impl ToOwned for SslSessionRef { + type Owned = SslSession; + + fn to_owned(&self) -> SslSession { + unsafe { + SSL_SESSION_up_ref(self.as_ptr()); + SslSession(self.as_ptr()) + } + } +} + +impl SslSessionRef { + /// Returns the SSL session ID. + #[corresponds(SSL_SESSION_get_id)] + pub fn id(&self) -> &[u8] { + unsafe { + let mut len = 0; + let p = ffi::SSL_SESSION_get_id(self.as_ptr(), &mut len); + #[allow(clippy::unnecessary_cast)] + slice::from_raw_parts(p as *const u8, len as usize) + } + } + + /// Returns the length of the master key. + #[corresponds(SSL_SESSION_get_master_key)] + pub fn master_key_len(&self) -> usize { + unsafe { SSL_SESSION_get_master_key(self.as_ptr(), ptr::null_mut(), 0) } + } + + /// Copies the master key into the provided buffer. + /// + /// Returns the number of bytes written, or the size of the master key if the buffer is empty. + #[corresponds(SSL_SESSION_get_master_key)] + pub fn master_key(&self, buf: &mut [u8]) -> usize { + unsafe { SSL_SESSION_get_master_key(self.as_ptr(), buf.as_mut_ptr(), buf.len()) } + } + + /// Gets the maximum amount of early data that can be sent on this session. + /// + /// Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer. + #[corresponds(SSL_SESSION_get_max_early_data)] + #[cfg(any(ossl111, libressl340))] + pub fn max_early_data(&self) -> u32 { + unsafe { ffi::SSL_SESSION_get_max_early_data(self.as_ptr()) } + } + + /// Returns the time at which the session was established, in seconds since the Unix epoch. + #[corresponds(SSL_SESSION_get_time)] + #[allow(clippy::useless_conversion)] + pub fn time(&self) -> SslTimeTy { + unsafe { ffi::SSL_SESSION_get_time(self.as_ptr()) } + } + + /// Returns the sessions timeout, in seconds. + /// + /// A session older than this time should not be used for session resumption. + #[corresponds(SSL_SESSION_get_timeout)] + #[allow(clippy::useless_conversion)] + pub fn timeout(&self) -> i64 { + unsafe { ffi::SSL_SESSION_get_timeout(self.as_ptr()).into() } + } + + /// Returns the session's TLS protocol version. + /// + /// Requires OpenSSL 1.1.0 or LibreSSL 2.7.0 or newer. + #[corresponds(SSL_SESSION_get_protocol_version)] + #[cfg(any(ossl110, libressl270))] + pub fn protocol_version(&self) -> SslVersion { + unsafe { + let version = ffi::SSL_SESSION_get_protocol_version(self.as_ptr()); + SslVersion(version) + } + } + + to_der! { + /// Serializes the session into a DER-encoded structure. + #[corresponds(i2d_SSL_SESSION)] + to_der, + ffi::i2d_SSL_SESSION + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::SSL; + fn drop = ffi::SSL_free; + + /// The state of an SSL/TLS session. + /// + /// `Ssl` objects are created from an [`SslContext`], which provides configuration defaults. + /// These defaults can be overridden on a per-`Ssl` basis, however. + /// + /// [`SslContext`]: struct.SslContext.html + pub struct Ssl; + + /// Reference to an [`Ssl`]. + /// + /// [`Ssl`]: struct.Ssl.html + pub struct SslRef; +} + +impl fmt::Debug for Ssl { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, fmt) + } +} + +impl Ssl { + /// Returns a new extra data index. + /// + /// Each invocation of this function is guaranteed to return a distinct index. These can be used + /// to store data in the context that can be retrieved later by callbacks, for example. + #[corresponds(SSL_get_ex_new_index)] + pub fn new_ex_index<T>() -> Result<Index<Ssl, T>, ErrorStack> + where + T: 'static + Sync + Send, + { + unsafe { + ffi::init(); + #[cfg(boringssl)] + let idx = cvt_n(get_new_ssl_idx(Some(free_data_box::<T>)))?; + #[cfg(not(boringssl))] + let idx = cvt_n(get_new_ssl_idx(free_data_box::<T>))?; + Ok(Index::from_raw(idx)) + } + } + + // FIXME should return a result? + fn cached_ex_index<T>() -> Index<Ssl, T> + where + T: 'static + Sync + Send, + { + unsafe { + let idx = *SSL_INDEXES + .lock() + .unwrap_or_else(|e| e.into_inner()) + .entry(TypeId::of::<T>()) + .or_insert_with(|| Ssl::new_ex_index::<T>().unwrap().as_raw()); + Index::from_raw(idx) + } + } + + /// Creates a new `Ssl`. + /// + /// This corresponds to [`SSL_new`]. + /// + /// [`SSL_new`]: https://www.openssl.org/docs/manmaster/ssl/SSL_new.html + #[corresponds(SSL_new)] + pub fn new(ctx: &SslContextRef) -> Result<Ssl, ErrorStack> { + let session_ctx_index = try_get_session_ctx_index()?; + unsafe { + let ptr = cvt_p(ffi::SSL_new(ctx.as_ptr()))?; + let mut ssl = Ssl::from_ptr(ptr); + ssl.set_ex_data(*session_ctx_index, ctx.to_owned()); + + Ok(ssl) + } + } + + /// Initiates a client-side TLS handshake. + /// + /// This corresponds to [`SSL_connect`]. + /// + /// # Warning + /// + /// OpenSSL's default configuration is insecure. It is highly recommended to use + /// `SslConnector` rather than `Ssl` directly, as it manages that configuration. + /// + /// [`SSL_connect`]: https://www.openssl.org/docs/manmaster/man3/SSL_connect.html + #[corresponds(SSL_connect)] + #[allow(deprecated)] + pub fn connect<S>(self, stream: S) -> Result<SslStream<S>, HandshakeError<S>> + where + S: Read + Write, + { + SslStreamBuilder::new(self, stream).connect() + } + + /// Initiates a server-side TLS handshake. + /// + /// This corresponds to [`SSL_accept`]. + /// + /// # Warning + /// + /// OpenSSL's default configuration is insecure. It is highly recommended to use + /// `SslAcceptor` rather than `Ssl` directly, as it manages that configuration. + /// + /// [`SSL_accept`]: https://www.openssl.org/docs/manmaster/man3/SSL_accept.html + #[corresponds(SSL_accept)] + #[allow(deprecated)] + pub fn accept<S>(self, stream: S) -> Result<SslStream<S>, HandshakeError<S>> + where + S: Read + Write, + { + SslStreamBuilder::new(self, stream).accept() + } +} + +impl fmt::Debug for SslRef { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("Ssl") + .field("state", &self.state_string_long()) + .field("verify_result", &self.verify_result()) + .finish() + } +} + +impl SslRef { + fn get_raw_rbio(&self) -> *mut ffi::BIO { + unsafe { ffi::SSL_get_rbio(self.as_ptr()) } + } + + fn read(&mut self, buf: &mut [u8]) -> c_int { + let len = cmp::min(c_int::max_value() as usize, buf.len()) as c_int; + unsafe { ffi::SSL_read(self.as_ptr(), buf.as_ptr() as *mut c_void, len) } + } + + fn peek(&mut self, buf: &mut [u8]) -> c_int { + let len = cmp::min(c_int::max_value() as usize, buf.len()) as c_int; + unsafe { ffi::SSL_peek(self.as_ptr(), buf.as_ptr() as *mut c_void, len) } + } + + fn write(&mut self, buf: &[u8]) -> c_int { + let len = cmp::min(c_int::max_value() as usize, buf.len()) as c_int; + unsafe { ffi::SSL_write(self.as_ptr(), buf.as_ptr() as *const c_void, len) } + } + + fn get_error(&self, ret: c_int) -> ErrorCode { + unsafe { ErrorCode::from_raw(ffi::SSL_get_error(self.as_ptr(), ret)) } + } + + /// Configure as an outgoing stream from a client. + #[corresponds(SSL_set_connect_state)] + pub fn set_connect_state(&mut self) { + unsafe { ffi::SSL_set_connect_state(self.as_ptr()) } + } + + /// Configure as an incoming stream to a server. + #[corresponds(SSL_set_accept_state)] + pub fn set_accept_state(&mut self) { + unsafe { ffi::SSL_set_accept_state(self.as_ptr()) } + } + + /// Like [`SslContextBuilder::set_verify`]. + /// + /// [`SslContextBuilder::set_verify`]: struct.SslContextBuilder.html#method.set_verify + #[corresponds(SSL_set_verify)] + pub fn set_verify(&mut self, mode: SslVerifyMode) { + unsafe { ffi::SSL_set_verify(self.as_ptr(), mode.bits() as c_int, None) } + } + + /// Returns the verify mode that was set using `set_verify`. + #[corresponds(SSL_set_verify_mode)] + pub fn verify_mode(&self) -> SslVerifyMode { + let mode = unsafe { ffi::SSL_get_verify_mode(self.as_ptr()) }; + SslVerifyMode::from_bits(mode).expect("SSL_get_verify_mode returned invalid mode") + } + + /// Like [`SslContextBuilder::set_verify_callback`]. + /// + /// [`SslContextBuilder::set_verify_callback`]: struct.SslContextBuilder.html#method.set_verify_callback + #[corresponds(SSL_set_verify)] + pub fn set_verify_callback<F>(&mut self, mode: SslVerifyMode, verify: F) + where + F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send, + { + unsafe { + // this needs to be in an Arc since the callback can register a new callback! + self.set_ex_data(Ssl::cached_ex_index(), Arc::new(verify)); + ffi::SSL_set_verify( + self.as_ptr(), + mode.bits() as c_int, + Some(ssl_raw_verify::<F>), + ); + } + } + + /// Like [`SslContextBuilder::set_tmp_dh`]. + /// + /// [`SslContextBuilder::set_tmp_dh`]: struct.SslContextBuilder.html#method.set_tmp_dh + #[corresponds(SSL_set_tmp_dh)] + pub fn set_tmp_dh(&mut self, dh: &DhRef<Params>) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::SSL_set_tmp_dh(self.as_ptr(), dh.as_ptr()) as c_int).map(|_| ()) } + } + + /// Like [`SslContextBuilder::set_tmp_dh_callback`]. + /// + /// [`SslContextBuilder::set_tmp_dh_callback`]: struct.SslContextBuilder.html#method.set_tmp_dh_callback + #[corresponds(SSL_set_tmp_dh_callback)] + pub fn set_tmp_dh_callback<F>(&mut self, callback: F) + where + F: Fn(&mut SslRef, bool, u32) -> Result<Dh<Params>, ErrorStack> + 'static + Sync + Send, + { + unsafe { + // this needs to be in an Arc since the callback can register a new callback! + self.set_ex_data(Ssl::cached_ex_index(), Arc::new(callback)); + #[cfg(boringssl)] + ffi::SSL_set_tmp_dh_callback(self.as_ptr(), Some(raw_tmp_dh_ssl::<F>)); + #[cfg(not(boringssl))] + ffi::SSL_set_tmp_dh_callback__fixed_rust(self.as_ptr(), Some(raw_tmp_dh_ssl::<F>)); + } + } + + /// Like [`SslContextBuilder::set_tmp_ecdh`]. + /// + /// [`SslContextBuilder::set_tmp_ecdh`]: struct.SslContextBuilder.html#method.set_tmp_ecdh + #[corresponds(SSL_set_tmp_ecdh)] + pub fn set_tmp_ecdh(&mut self, key: &EcKeyRef<Params>) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::SSL_set_tmp_ecdh(self.as_ptr(), key.as_ptr()) as c_int).map(|_| ()) } + } + + /// Like [`SslContextBuilder::set_tmp_ecdh_callback`]. + /// + /// Requires OpenSSL 1.0.1 or 1.0.2. + #[corresponds(SSL_set_tmp_ecdh_callback)] + #[cfg(all(ossl101, not(ossl110)))] + #[deprecated(note = "this function leaks memory and does not exist on newer OpenSSL versions")] + pub fn set_tmp_ecdh_callback<F>(&mut self, callback: F) + where + F: Fn(&mut SslRef, bool, u32) -> Result<EcKey<Params>, ErrorStack> + 'static + Sync + Send, + { + unsafe { + // this needs to be in an Arc since the callback can register a new callback! + self.set_ex_data(Ssl::cached_ex_index(), Arc::new(callback)); + ffi::SSL_set_tmp_ecdh_callback__fixed_rust(self.as_ptr(), Some(raw_tmp_ecdh_ssl::<F>)); + } + } + + /// Like [`SslContextBuilder::set_ecdh_auto`]. + /// + /// Requires OpenSSL 1.0.2 or LibreSSL. + /// + /// [`SslContextBuilder::set_tmp_ecdh`]: struct.SslContextBuilder.html#method.set_tmp_ecdh + #[corresponds(SSL_set_ecdh_auto)] + #[cfg(any(all(ossl102, not(ossl110)), libressl))] + pub fn set_ecdh_auto(&mut self, onoff: bool) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::SSL_set_ecdh_auto(self.as_ptr(), onoff as c_int)).map(|_| ()) } + } + + /// Like [`SslContextBuilder::set_alpn_protos`]. + /// + /// Requires OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer. + /// + /// [`SslContextBuilder::set_alpn_protos`]: struct.SslContextBuilder.html#method.set_alpn_protos + #[corresponds(SSL_set_alpn_protos)] + #[cfg(any(ossl102, libressl261))] + pub fn set_alpn_protos(&mut self, protocols: &[u8]) -> Result<(), ErrorStack> { + unsafe { + assert!(protocols.len() <= c_uint::max_value() as usize); + let r = ffi::SSL_set_alpn_protos( + self.as_ptr(), + protocols.as_ptr(), + protocols.len() as c_uint, + ); + // fun fact, SSL_set_alpn_protos has a reversed return code D: + if r == 0 { + Ok(()) + } else { + Err(ErrorStack::get()) + } + } + } + + /// Returns the current cipher if the session is active. + #[corresponds(SSL_get_current_cipher)] + pub fn current_cipher(&self) -> Option<&SslCipherRef> { + unsafe { + let ptr = ffi::SSL_get_current_cipher(self.as_ptr()); + + SslCipherRef::from_const_ptr_opt(ptr) + } + } + + /// Returns a short string describing the state of the session. + #[corresponds(SSL_state_string)] + pub fn state_string(&self) -> &'static str { + let state = unsafe { + let ptr = ffi::SSL_state_string(self.as_ptr()); + CStr::from_ptr(ptr as *const _) + }; + + str::from_utf8(state.to_bytes()).unwrap() + } + + /// Returns a longer string describing the state of the session. + #[corresponds(SSL_state_string_long)] + pub fn state_string_long(&self) -> &'static str { + let state = unsafe { + let ptr = ffi::SSL_state_string_long(self.as_ptr()); + CStr::from_ptr(ptr as *const _) + }; + + str::from_utf8(state.to_bytes()).unwrap() + } + + /// Sets the host name to be sent to the server for Server Name Indication (SNI). + /// + /// It has no effect for a server-side connection. + #[corresponds(SSL_set_tlsext_host_name)] + pub fn set_hostname(&mut self, hostname: &str) -> Result<(), ErrorStack> { + let cstr = CString::new(hostname).unwrap(); + unsafe { + cvt(ffi::SSL_set_tlsext_host_name(self.as_ptr(), cstr.as_ptr() as *mut _) as c_int) + .map(|_| ()) + } + } + + /// Returns the peer's certificate, if present. + #[corresponds(SSL_get_peer_certificate)] + pub fn peer_certificate(&self) -> Option<X509> { + unsafe { + let ptr = SSL_get1_peer_certificate(self.as_ptr()); + X509::from_ptr_opt(ptr) + } + } + + /// Returns the certificate chain of the peer, if present. + /// + /// On the client side, the chain includes the leaf certificate, but on the server side it does + /// not. Fun! + #[corresponds(SSL_get_peer_cert_chain)] + pub fn peer_cert_chain(&self) -> Option<&StackRef<X509>> { + unsafe { + let ptr = ffi::SSL_get_peer_cert_chain(self.as_ptr()); + StackRef::from_const_ptr_opt(ptr) + } + } + + /// Returns the verified certificate chain of the peer, including the leaf certificate. + /// + /// If verification was not successful (i.e. [`verify_result`] does not return + /// [`X509VerifyResult::OK`]), this chain may be incomplete or invalid. + /// + /// Requires OpenSSL 1.1.0 or newer. + /// + /// [`verify_result`]: #method.verify_result + /// [`X509VerifyResult::OK`]: ../x509/struct.X509VerifyResult.html#associatedconstant.OK + #[corresponds(SSL_get0_verified_chain)] + #[cfg(ossl110)] + pub fn verified_chain(&self) -> Option<&StackRef<X509>> { + unsafe { + let ptr = ffi::SSL_get0_verified_chain(self.as_ptr()); + StackRef::from_const_ptr_opt(ptr) + } + } + + /// Like [`SslContext::certificate`]. + #[corresponds(SSL_get_certificate)] + pub fn certificate(&self) -> Option<&X509Ref> { + unsafe { + let ptr = ffi::SSL_get_certificate(self.as_ptr()); + X509Ref::from_const_ptr_opt(ptr) + } + } + + /// Like [`SslContext::private_key`]. + /// + /// [`SslContext::private_key`]: struct.SslContext.html#method.private_key + #[corresponds(SSL_get_privatekey)] + pub fn private_key(&self) -> Option<&PKeyRef<Private>> { + unsafe { + let ptr = ffi::SSL_get_privatekey(self.as_ptr()); + PKeyRef::from_const_ptr_opt(ptr) + } + } + + #[deprecated(since = "0.10.5", note = "renamed to `version_str`")] + pub fn version(&self) -> &str { + self.version_str() + } + + /// Returns the protocol version of the session. + #[corresponds(SSL_version)] + pub fn version2(&self) -> Option<SslVersion> { + unsafe { + let r = ffi::SSL_version(self.as_ptr()); + if r == 0 { + None + } else { + Some(SslVersion(r)) + } + } + } + + /// Returns a string describing the protocol version of the session. + #[corresponds(SSL_get_version)] + pub fn version_str(&self) -> &'static str { + let version = unsafe { + let ptr = ffi::SSL_get_version(self.as_ptr()); + CStr::from_ptr(ptr as *const _) + }; + + str::from_utf8(version.to_bytes()).unwrap() + } + + /// Returns the protocol selected via Application Layer Protocol Negotiation (ALPN). + /// + /// The protocol's name is returned is an opaque sequence of bytes. It is up to the client + /// to interpret it. + /// + /// Requires OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer. + #[corresponds(SSL_get0_alpn_selected)] + #[cfg(any(ossl102, libressl261))] + pub fn selected_alpn_protocol(&self) -> Option<&[u8]> { + unsafe { + let mut data: *const c_uchar = ptr::null(); + let mut len: c_uint = 0; + // Get the negotiated protocol from the SSL instance. + // `data` will point at a `c_uchar` array; `len` will contain the length of this array. + ffi::SSL_get0_alpn_selected(self.as_ptr(), &mut data, &mut len); + + if data.is_null() { + None + } else { + Some(slice::from_raw_parts(data, len as usize)) + } + } + } + + /// Enables the DTLS extension "use_srtp" as defined in RFC5764. + /// + /// This corresponds to [`SSL_set_tlsext_use_srtp`]. + /// + /// [`SSL_set_tlsext_use_srtp`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_tlsext_use_srtp.html + #[corresponds(SSL_set_tlsext_use_srtp)] + pub fn set_tlsext_use_srtp(&mut self, protocols: &str) -> Result<(), ErrorStack> { + unsafe { + let cstr = CString::new(protocols).unwrap(); + + let r = ffi::SSL_set_tlsext_use_srtp(self.as_ptr(), cstr.as_ptr()); + // fun fact, set_tlsext_use_srtp has a reversed return code D: + if r == 0 { + Ok(()) + } else { + Err(ErrorStack::get()) + } + } + } + + /// Gets all SRTP profiles that are enabled for handshake via set_tlsext_use_srtp + /// + /// DTLS extension "use_srtp" as defined in RFC5764 has to be enabled. + /// + /// This corresponds to [`SSL_get_srtp_profiles`]. + /// + /// [`SSL_get_srtp_profiles`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_tlsext_use_srtp.html + #[corresponds(SSL_get_srtp_profiles)] + pub fn srtp_profiles(&self) -> Option<&StackRef<SrtpProtectionProfile>> { + unsafe { + let chain = ffi::SSL_get_srtp_profiles(self.as_ptr()); + + StackRef::from_const_ptr_opt(chain) + } + } + + /// Gets the SRTP profile selected by handshake. + /// + /// DTLS extension "use_srtp" as defined in RFC5764 has to be enabled. + #[corresponds(SSL_get_selected_srtp_profile)] + pub fn selected_srtp_profile(&self) -> Option<&SrtpProtectionProfileRef> { + unsafe { + let profile = ffi::SSL_get_selected_srtp_profile(self.as_ptr()); + + SrtpProtectionProfileRef::from_const_ptr_opt(profile) + } + } + + /// Returns the number of bytes remaining in the currently processed TLS record. + /// + /// If this is greater than 0, the next call to `read` will not call down to the underlying + /// stream. + #[corresponds(SSL_pending)] + pub fn pending(&self) -> usize { + unsafe { ffi::SSL_pending(self.as_ptr()) as usize } + } + + /// Returns the servername sent by the client via Server Name Indication (SNI). + /// + /// It is only useful on the server side. + /// + /// # Note + /// + /// While the SNI specification requires that servernames be valid domain names (and therefore + /// ASCII), OpenSSL does not enforce this restriction. If the servername provided by the client + /// is not valid UTF-8, this function will return `None`. The `servername_raw` method returns + /// the raw bytes and does not have this restriction. + /// + /// [`SSL_get_servername`]: https://www.openssl.org/docs/manmaster/man3/SSL_get_servername.html + #[corresponds(SSL_get_servername)] + // FIXME maybe rethink in 0.11? + pub fn servername(&self, type_: NameType) -> Option<&str> { + self.servername_raw(type_) + .and_then(|b| str::from_utf8(b).ok()) + } + + /// Returns the servername sent by the client via Server Name Indication (SNI). + /// + /// It is only useful on the server side. + /// + /// # Note + /// + /// Unlike `servername`, this method does not require the name be valid UTF-8. + #[corresponds(SSL_get_servername)] + pub fn servername_raw(&self, type_: NameType) -> Option<&[u8]> { + unsafe { + let name = ffi::SSL_get_servername(self.as_ptr(), type_.0); + if name.is_null() { + None + } else { + Some(CStr::from_ptr(name as *const _).to_bytes()) + } + } + } + + /// Changes the context corresponding to the current connection. + /// + /// It is most commonly used in the Server Name Indication (SNI) callback. + #[corresponds(SSL_set_SSL_CTX)] + pub fn set_ssl_context(&mut self, ctx: &SslContextRef) -> Result<(), ErrorStack> { + unsafe { cvt_p(ffi::SSL_set_SSL_CTX(self.as_ptr(), ctx.as_ptr())).map(|_| ()) } + } + + /// Returns the context corresponding to the current connection. + #[corresponds(SSL_get_SSL_CTX)] + pub fn ssl_context(&self) -> &SslContextRef { + unsafe { + let ssl_ctx = ffi::SSL_get_SSL_CTX(self.as_ptr()); + SslContextRef::from_ptr(ssl_ctx) + } + } + + /// Returns a mutable reference to the X509 verification configuration. + /// + /// Requires OpenSSL 1.0.2 or newer. + #[corresponds(SSL_get0_param)] + #[cfg(any(ossl102, libressl261))] + pub fn param_mut(&mut self) -> &mut X509VerifyParamRef { + unsafe { X509VerifyParamRef::from_ptr_mut(ffi::SSL_get0_param(self.as_ptr())) } + } + + /// Returns the certificate verification result. + #[corresponds(SSL_get_verify_result)] + pub fn verify_result(&self) -> X509VerifyResult { + unsafe { X509VerifyResult::from_raw(ffi::SSL_get_verify_result(self.as_ptr()) as c_int) } + } + + /// Returns a shared reference to the SSL session. + #[corresponds(SSL_get_session)] + pub fn session(&self) -> Option<&SslSessionRef> { + unsafe { + let p = ffi::SSL_get_session(self.as_ptr()); + SslSessionRef::from_const_ptr_opt(p) + } + } + + /// Copies the `client_random` value sent by the client in the TLS handshake into a buffer. + /// + /// Returns the number of bytes copied, or if the buffer is empty, the size of the `client_random` + /// value. + /// + /// Requires OpenSSL 1.1.0 or LibreSSL 2.7.0 or newer. + #[corresponds(SSL_get_client_random)] + #[cfg(any(ossl110, libressl270))] + pub fn client_random(&self, buf: &mut [u8]) -> usize { + unsafe { + ffi::SSL_get_client_random(self.as_ptr(), buf.as_mut_ptr() as *mut c_uchar, buf.len()) + } + } + + /// Copies the `server_random` value sent by the server in the TLS handshake into a buffer. + /// + /// Returns the number of bytes copied, or if the buffer is empty, the size of the `server_random` + /// value. + /// + /// Requires OpenSSL 1.1.0 or LibreSSL 2.7.0 or newer. + #[corresponds(SSL_get_server_random)] + #[cfg(any(ossl110, libressl270))] + pub fn server_random(&self, buf: &mut [u8]) -> usize { + unsafe { + ffi::SSL_get_server_random(self.as_ptr(), buf.as_mut_ptr() as *mut c_uchar, buf.len()) + } + } + + /// Derives keying material for application use in accordance to RFC 5705. + #[corresponds(SSL_export_keying_material)] + pub fn export_keying_material( + &self, + out: &mut [u8], + label: &str, + context: Option<&[u8]>, + ) -> Result<(), ErrorStack> { + unsafe { + let (context, contextlen, use_context) = match context { + Some(context) => (context.as_ptr() as *const c_uchar, context.len(), 1), + None => (ptr::null(), 0, 0), + }; + cvt(ffi::SSL_export_keying_material( + self.as_ptr(), + out.as_mut_ptr() as *mut c_uchar, + out.len(), + label.as_ptr() as *const c_char, + label.len(), + context, + contextlen, + use_context, + )) + .map(|_| ()) + } + } + + /// Derives keying material for application use in accordance to RFC 5705. + /// + /// This function is only usable with TLSv1.3, wherein there is no distinction between an empty context and no + /// context. Therefore, unlike `export_keying_material`, `context` must always be supplied. + /// + /// Requires OpenSSL 1.1.1 or newer. + #[corresponds(SSL_export_keying_material_early)] + #[cfg(ossl111)] + pub fn export_keying_material_early( + &self, + out: &mut [u8], + label: &str, + context: &[u8], + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::SSL_export_keying_material_early( + self.as_ptr(), + out.as_mut_ptr() as *mut c_uchar, + out.len(), + label.as_ptr() as *const c_char, + label.len(), + context.as_ptr() as *const c_uchar, + context.len(), + )) + .map(|_| ()) + } + } + + /// Sets the session to be used. + /// + /// This should be called before the handshake to attempt to reuse a previously established + /// session. If the server is not willing to reuse the session, a new one will be transparently + /// negotiated. + /// + /// # Safety + /// + /// The caller of this method is responsible for ensuring that the session is associated + /// with the same `SslContext` as this `Ssl`. + #[corresponds(SSL_set_session)] + pub unsafe fn set_session(&mut self, session: &SslSessionRef) -> Result<(), ErrorStack> { + cvt(ffi::SSL_set_session(self.as_ptr(), session.as_ptr())).map(|_| ()) + } + + /// Determines if the session provided to `set_session` was successfully reused. + #[corresponds(SSL_session_reused)] + pub fn session_reused(&self) -> bool { + unsafe { ffi::SSL_session_reused(self.as_ptr()) != 0 } + } + + /// Sets the status response a client wishes the server to reply with. + #[corresponds(SSL_set_tlsext_status_type)] + pub fn set_status_type(&mut self, type_: StatusType) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::SSL_set_tlsext_status_type(self.as_ptr(), type_.as_raw()) as c_int).map(|_| ()) + } + } + + /// Determines if current session used Extended Master Secret + /// + /// Returns `None` if the handshake is still in-progress. + #[corresponds(SSL_get_extms_support)] + #[cfg(ossl110)] + pub fn extms_support(&self) -> Option<bool> { + unsafe { + match ffi::SSL_get_extms_support(self.as_ptr()) { + -1 => None, + ret => Some(ret != 0), + } + } + } + + /// Returns the server's OCSP response, if present. + #[corresponds(SSL_get_tlsext_status_ocsp_resp)] + #[cfg(not(boringssl))] + pub fn ocsp_status(&self) -> Option<&[u8]> { + unsafe { + let mut p = ptr::null_mut(); + let len = ffi::SSL_get_tlsext_status_ocsp_resp(self.as_ptr(), &mut p); + + if len < 0 { + None + } else { + Some(slice::from_raw_parts(p as *const u8, len as usize)) + } + } + } + + /// Sets the OCSP response to be returned to the client. + #[corresponds(SSL_set_tlsext_status_oscp_resp)] + #[cfg(not(boringssl))] + pub fn set_ocsp_status(&mut self, response: &[u8]) -> Result<(), ErrorStack> { + unsafe { + assert!(response.len() <= c_int::max_value() as usize); + let p = cvt_p(ffi::OPENSSL_malloc(response.len() as _))?; + ptr::copy_nonoverlapping(response.as_ptr(), p as *mut u8, response.len()); + cvt(ffi::SSL_set_tlsext_status_ocsp_resp( + self.as_ptr(), + p as *mut c_uchar, + response.len() as c_long, + ) as c_int) + .map(|_| ()) + .map_err(|e| { + ffi::OPENSSL_free(p); + e + }) + } + } + + /// Determines if this `Ssl` is configured for server-side or client-side use. + #[corresponds(SSL_is_server)] + pub fn is_server(&self) -> bool { + unsafe { SSL_is_server(self.as_ptr()) != 0 } + } + + /// Sets the extra data at the specified index. + /// + /// This can be used to provide data to callbacks registered with the context. Use the + /// `Ssl::new_ex_index` method to create an `Index`. + #[corresponds(SSL_set_ex_data)] + pub fn set_ex_data<T>(&mut self, index: Index<Ssl, T>, data: T) { + unsafe { + let data = Box::new(data); + ffi::SSL_set_ex_data( + self.as_ptr(), + index.as_raw(), + Box::into_raw(data) as *mut c_void, + ); + } + } + + /// Returns a reference to the extra data at the specified index. + #[corresponds(SSL_get_ex_data)] + pub fn ex_data<T>(&self, index: Index<Ssl, T>) -> Option<&T> { + unsafe { + let data = ffi::SSL_get_ex_data(self.as_ptr(), index.as_raw()); + if data.is_null() { + None + } else { + Some(&*(data as *const T)) + } + } + } + + /// Returns a mutable reference to the extra data at the specified index. + #[corresponds(SSL_get_ex_data)] + pub fn ex_data_mut<T>(&mut self, index: Index<Ssl, T>) -> Option<&mut T> { + unsafe { + let data = ffi::SSL_get_ex_data(self.as_ptr(), index.as_raw()); + if data.is_null() { + None + } else { + Some(&mut *(data as *mut T)) + } + } + } + + /// Sets the maximum amount of early data that will be accepted on this connection. + /// + /// Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer. + #[corresponds(SSL_set_max_early_data)] + #[cfg(any(ossl111, libressl340))] + pub fn set_max_early_data(&mut self, bytes: u32) -> Result<(), ErrorStack> { + if unsafe { ffi::SSL_set_max_early_data(self.as_ptr(), bytes) } == 1 { + Ok(()) + } else { + Err(ErrorStack::get()) + } + } + + /// Gets the maximum amount of early data that can be sent on this connection. + /// + /// Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer. + #[corresponds(SSL_get_max_early_data)] + #[cfg(any(ossl111, libressl340))] + pub fn max_early_data(&self) -> u32 { + unsafe { ffi::SSL_get_max_early_data(self.as_ptr()) } + } + + /// Copies the contents of the last Finished message sent to the peer into the provided buffer. + /// + /// The total size of the message is returned, so this can be used to determine the size of the + /// buffer required. + #[corresponds(SSL_get_finished)] + pub fn finished(&self, buf: &mut [u8]) -> usize { + unsafe { ffi::SSL_get_finished(self.as_ptr(), buf.as_mut_ptr() as *mut c_void, buf.len()) } + } + + /// Copies the contents of the last Finished message received from the peer into the provided + /// buffer. + /// + /// The total size of the message is returned, so this can be used to determine the size of the + /// buffer required. + #[corresponds(SSL_get_peer_finished)] + pub fn peer_finished(&self, buf: &mut [u8]) -> usize { + unsafe { + ffi::SSL_get_peer_finished(self.as_ptr(), buf.as_mut_ptr() as *mut c_void, buf.len()) + } + } + + /// Determines if the initial handshake has been completed. + #[corresponds(SSL_is_init_finished)] + #[cfg(ossl110)] + pub fn is_init_finished(&self) -> bool { + unsafe { ffi::SSL_is_init_finished(self.as_ptr()) != 0 } + } + + /// Determines if the client's hello message is in the SSLv2 format. + /// + /// This can only be used inside of the client hello callback. Otherwise, `false` is returned. + /// + /// Requires OpenSSL 1.1.1 or newer. + #[corresponds(SSL_client_hello_isv2)] + #[cfg(ossl111)] + pub fn client_hello_isv2(&self) -> bool { + unsafe { ffi::SSL_client_hello_isv2(self.as_ptr()) != 0 } + } + + /// Returns the legacy version field of the client's hello message. + /// + /// This can only be used inside of the client hello callback. Otherwise, `None` is returned. + /// + /// Requires OpenSSL 1.1.1 or newer. + #[corresponds(SSL_client_hello_get0_legacy_version)] + #[cfg(ossl111)] + pub fn client_hello_legacy_version(&self) -> Option<SslVersion> { + unsafe { + let version = ffi::SSL_client_hello_get0_legacy_version(self.as_ptr()); + if version == 0 { + None + } else { + Some(SslVersion(version as c_int)) + } + } + } + + /// Returns the random field of the client's hello message. + /// + /// This can only be used inside of the client hello callback. Otherwise, `None` is returned. + /// + /// Requires OpenSSL 1.1.1 or newer. + #[corresponds(SSL_client_hello_get0_random)] + #[cfg(ossl111)] + pub fn client_hello_random(&self) -> Option<&[u8]> { + unsafe { + let mut ptr = ptr::null(); + let len = ffi::SSL_client_hello_get0_random(self.as_ptr(), &mut ptr); + if len == 0 { + None + } else { + Some(slice::from_raw_parts(ptr, len)) + } + } + } + + /// Returns the session ID field of the client's hello message. + /// + /// This can only be used inside of the client hello callback. Otherwise, `None` is returned. + /// + /// Requires OpenSSL 1.1.1 or newer. + #[corresponds(SSL_client_hello_get0_session_id)] + #[cfg(ossl111)] + pub fn client_hello_session_id(&self) -> Option<&[u8]> { + unsafe { + let mut ptr = ptr::null(); + let len = ffi::SSL_client_hello_get0_session_id(self.as_ptr(), &mut ptr); + if len == 0 { + None + } else { + Some(slice::from_raw_parts(ptr, len)) + } + } + } + + /// Returns the ciphers field of the client's hello message. + /// + /// This can only be used inside of the client hello callback. Otherwise, `None` is returned. + /// + /// Requires OpenSSL 1.1.1 or newer. + #[corresponds(SSL_client_hello_get0_ciphers)] + #[cfg(ossl111)] + pub fn client_hello_ciphers(&self) -> Option<&[u8]> { + unsafe { + let mut ptr = ptr::null(); + let len = ffi::SSL_client_hello_get0_ciphers(self.as_ptr(), &mut ptr); + if len == 0 { + None + } else { + Some(slice::from_raw_parts(ptr, len)) + } + } + } + + /// Decodes a slice of wire-format cipher suite specification bytes. Unsupported cipher suites + /// are ignored. + /// + /// Requires OpenSSL 1.1.1 or newer. + #[corresponds(SSL_bytes_to_cipher_list)] + #[cfg(ossl111)] + pub fn bytes_to_cipher_list( + &self, + bytes: &[u8], + isv2format: bool, + ) -> Result<CipherLists, ErrorStack> { + unsafe { + let ptr = bytes.as_ptr(); + let len = bytes.len(); + let mut sk = ptr::null_mut(); + let mut scsvs = ptr::null_mut(); + let res = ffi::SSL_bytes_to_cipher_list( + self.as_ptr(), + ptr, + len, + isv2format as c_int, + &mut sk, + &mut scsvs, + ); + if res == 1 { + Ok(CipherLists { + suites: Stack::from_ptr(sk), + signalling_suites: Stack::from_ptr(scsvs), + }) + } else { + Err(ErrorStack::get()) + } + } + } + + /// Returns the compression methods field of the client's hello message. + /// + /// This can only be used inside of the client hello callback. Otherwise, `None` is returned. + /// + /// Requires OpenSSL 1.1.1 or newer. + #[corresponds(SSL_client_hello_get0_compression_methods)] + #[cfg(ossl111)] + pub fn client_hello_compression_methods(&self) -> Option<&[u8]> { + unsafe { + let mut ptr = ptr::null(); + let len = ffi::SSL_client_hello_get0_compression_methods(self.as_ptr(), &mut ptr); + if len == 0 { + None + } else { + Some(slice::from_raw_parts(ptr, len)) + } + } + } + + /// Sets the MTU used for DTLS connections. + #[corresponds(SSL_set_mtu)] + pub fn set_mtu(&mut self, mtu: u32) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::SSL_set_mtu(self.as_ptr(), mtu as MtuTy) as c_int).map(|_| ()) } + } + + /// Returns the PSK identity hint used during connection setup. + /// + /// May return `None` if no PSK identity hint was used during the connection setup. + #[corresponds(SSL_get_psk_identity_hint)] + #[cfg(not(osslconf = "OPENSSL_NO_PSK"))] + pub fn psk_identity_hint(&self) -> Option<&[u8]> { + unsafe { + let ptr = ffi::SSL_get_psk_identity_hint(self.as_ptr()); + if ptr.is_null() { + None + } else { + Some(CStr::from_ptr(ptr).to_bytes()) + } + } + } + + /// Returns the PSK identity used during connection setup. + #[corresponds(SSL_get_psk_identity)] + #[cfg(not(osslconf = "OPENSSL_NO_PSK"))] + pub fn psk_identity(&self) -> Option<&[u8]> { + unsafe { + let ptr = ffi::SSL_get_psk_identity(self.as_ptr()); + if ptr.is_null() { + None + } else { + Some(CStr::from_ptr(ptr).to_bytes()) + } + } + } + + #[corresponds(SSL_add0_chain_cert)] + #[cfg(ossl102)] + pub fn add_chain_cert(&mut self, chain: X509) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::SSL_add0_chain_cert(self.as_ptr(), chain.as_ptr()) as c_int).map(|_| ())?; + mem::forget(chain); + } + Ok(()) + } + + /// Sets a new default TLS/SSL method for SSL objects + #[cfg(not(boringssl))] + pub fn set_method(&mut self, method: SslMethod) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::SSL_set_ssl_method(self.as_ptr(), method.as_ptr()))?; + }; + Ok(()) + } + + /// Loads the private key from a file. + #[corresponds(SSL_use_Private_Key_file)] + pub fn set_private_key_file<P: AsRef<Path>>( + &mut self, + path: P, + ssl_file_type: SslFiletype, + ) -> Result<(), ErrorStack> { + let p = path.as_ref().as_os_str().to_str().unwrap(); + let key_file = CString::new(p).unwrap(); + unsafe { + cvt(ffi::SSL_use_PrivateKey_file( + self.as_ptr(), + key_file.as_ptr(), + ssl_file_type.as_raw(), + ))?; + }; + Ok(()) + } + + /// Sets the private key. + #[corresponds(SSL_use_PrivateKey)] + pub fn set_private_key(&mut self, pkey: &PKeyRef<Private>) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::SSL_use_PrivateKey(self.as_ptr(), pkey.as_ptr()))?; + }; + Ok(()) + } + + /// Sets the certificate + #[corresponds(SSL_use_certificate)] + pub fn set_certificate(&mut self, cert: &X509Ref) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::SSL_use_certificate(self.as_ptr(), cert.as_ptr()))?; + }; + Ok(()) + } + + /// Loads a certificate chain from a file. + /// + /// The file should contain a sequence of PEM-formatted certificates, the first being the leaf + /// certificate, and the remainder forming the chain of certificates up to and including the + /// trusted root certificate. + #[corresponds(SSL_use_certificate_chain_file)] + #[cfg(any(ossl110, libressl332))] + pub fn set_certificate_chain_file<P: AsRef<Path>>( + &mut self, + path: P, + ) -> Result<(), ErrorStack> { + let p = path.as_ref().as_os_str().to_str().unwrap(); + let cert_file = CString::new(p).unwrap(); + unsafe { + cvt(ffi::SSL_use_certificate_chain_file( + self.as_ptr(), + cert_file.as_ptr(), + ))?; + }; + Ok(()) + } + + /// Sets ca certificate that client trusted + #[corresponds(SSL_add_client_CA)] + pub fn add_client_ca(&mut self, cacert: &X509Ref) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::SSL_add_client_CA(self.as_ptr(), cacert.as_ptr()))?; + }; + Ok(()) + } + + // Sets the list of CAs sent to the client when requesting a client certificate for the chosen ssl + #[corresponds(SSL_set_client_CA_list)] + pub fn set_client_ca_list(&mut self, list: Stack<X509Name>) { + unsafe { ffi::SSL_set_client_CA_list(self.as_ptr(), list.as_ptr()) } + mem::forget(list); + } + + /// Sets the minimum supported protocol version. + /// + /// A value of `None` will enable protocol versions down to the lowest version supported by + /// OpenSSL. + /// + /// Requires OpenSSL 1.1.0 or LibreSSL 2.6.1 or newer. + #[corresponds(SSL_set_min_proto_version)] + #[cfg(any(ossl110, libressl261))] + pub fn set_min_proto_version(&mut self, version: Option<SslVersion>) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::SSL_set_min_proto_version( + self.as_ptr(), + version.map_or(0, |v| v.0 as _), + )) + .map(|_| ()) + } + } + + /// Sets the maximum supported protocol version. + /// + /// A value of `None` will enable protocol versions up to the highest version supported by + /// OpenSSL. + /// + /// Requires OpenSSL 1.1.0 or or LibreSSL 2.6.1 or newer. + #[corresponds(SSL_set_max_proto_version)] + #[cfg(any(ossl110, libressl261))] + pub fn set_max_proto_version(&mut self, version: Option<SslVersion>) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::SSL_set_max_proto_version( + self.as_ptr(), + version.map_or(0, |v| v.0 as _), + )) + .map(|_| ()) + } + } + + /// Sets the list of supported ciphers for the TLSv1.3 protocol. + /// + /// The `set_cipher_list` method controls the cipher suites for protocols before TLSv1.3. + /// + /// The format consists of TLSv1.3 cipher suite names separated by `:` characters in order of + /// preference. + /// + /// Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer. + #[corresponds(SSL_set_ciphersuites)] + #[cfg(any(ossl111, libressl340))] + pub fn set_ciphersuites(&mut self, cipher_list: &str) -> Result<(), ErrorStack> { + let cipher_list = CString::new(cipher_list).unwrap(); + unsafe { + cvt(ffi::SSL_set_ciphersuites( + self.as_ptr(), + cipher_list.as_ptr() as *const _, + )) + .map(|_| ()) + } + } + + /// Sets the list of supported ciphers for protocols before TLSv1.3. + /// + /// The `set_ciphersuites` method controls the cipher suites for TLSv1.3. + /// + /// See [`ciphers`] for details on the format. + /// + /// [`ciphers`]: https://www.openssl.org/docs/manmaster/apps/ciphers.html + #[corresponds(SSL_set_cipher_list)] + pub fn set_cipher_list(&mut self, cipher_list: &str) -> Result<(), ErrorStack> { + let cipher_list = CString::new(cipher_list).unwrap(); + unsafe { + cvt(ffi::SSL_set_cipher_list( + self.as_ptr(), + cipher_list.as_ptr() as *const _, + )) + .map(|_| ()) + } + } + + /// Set the certificate store used for certificate verification + #[corresponds(SSL_set_cert_store)] + #[cfg(ossl102)] + pub fn set_verify_cert_store(&mut self, cert_store: X509Store) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::SSL_set0_verify_cert_store(self.as_ptr(), cert_store.as_ptr()) as c_int)?; + mem::forget(cert_store); + Ok(()) + } + } + + /// Sets the number of TLS 1.3 session tickets that will be sent to a client after a full + /// handshake. + /// + /// Requires OpenSSL 1.1.1 or newer. + #[corresponds(SSL_set_num_tickets)] + #[cfg(ossl111)] + pub fn set_num_tickets(&mut self, num_tickets: usize) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::SSL_set_num_tickets(self.as_ptr(), num_tickets)).map(|_| ()) } + } + + /// Gets the number of TLS 1.3 session tickets that will be sent to a client after a full + /// handshake. + /// + /// Requires OpenSSL 1.1.1 or newer. + #[corresponds(SSL_get_num_tickets)] + #[cfg(ossl111)] + pub fn num_tickets(&self) -> usize { + unsafe { ffi::SSL_get_num_tickets(self.as_ptr()) } + } +} + +/// An SSL stream midway through the handshake process. +#[derive(Debug)] +pub struct MidHandshakeSslStream<S> { + stream: SslStream<S>, + error: Error, +} + +impl<S> MidHandshakeSslStream<S> { + /// Returns a shared reference to the inner stream. + pub fn get_ref(&self) -> &S { + self.stream.get_ref() + } + + /// Returns a mutable reference to the inner stream. + pub fn get_mut(&mut self) -> &mut S { + self.stream.get_mut() + } + + /// Returns a shared reference to the `Ssl` of the stream. + pub fn ssl(&self) -> &SslRef { + self.stream.ssl() + } + + /// Returns the underlying error which interrupted this handshake. + pub fn error(&self) -> &Error { + &self.error + } + + /// Consumes `self`, returning its error. + pub fn into_error(self) -> Error { + self.error + } +} + +impl<S> MidHandshakeSslStream<S> +where + S: Read + Write, +{ + /// Restarts the handshake process. + /// + /// This corresponds to [`SSL_do_handshake`]. + /// + /// [`SSL_do_handshake`]: https://www.openssl.org/docs/manmaster/man3/SSL_do_handshake.html + pub fn handshake(mut self) -> Result<SslStream<S>, HandshakeError<S>> { + match self.stream.do_handshake() { + Ok(()) => Ok(self.stream), + Err(error) => { + self.error = error; + match self.error.code() { + ErrorCode::WANT_READ | ErrorCode::WANT_WRITE => { + Err(HandshakeError::WouldBlock(self)) + } + _ => Err(HandshakeError::Failure(self)), + } + } + } + } +} + +/// A TLS session over a stream. +pub struct SslStream<S> { + ssl: ManuallyDrop<Ssl>, + method: ManuallyDrop<BioMethod>, + _p: PhantomData<S>, +} + +impl<S> Drop for SslStream<S> { + fn drop(&mut self) { + // ssl holds a reference to method internally so it has to drop first + unsafe { + ManuallyDrop::drop(&mut self.ssl); + ManuallyDrop::drop(&mut self.method); + } + } +} + +impl<S> fmt::Debug for SslStream<S> +where + S: fmt::Debug, +{ + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("SslStream") + .field("stream", &self.get_ref()) + .field("ssl", &self.ssl()) + .finish() + } +} + +impl<S: Read + Write> SslStream<S> { + /// Creates a new `SslStream`. + /// + /// This function performs no IO; the stream will not have performed any part of the handshake + /// with the peer. If the `Ssl` was configured with [`SslRef::set_connect_state`] or + /// [`SslRef::set_accept_state`], the handshake can be performed automatically during the first + /// call to read or write. Otherwise the `connect` and `accept` methods can be used to + /// explicitly perform the handshake. + #[corresponds(SSL_set_bio)] + pub fn new(ssl: Ssl, stream: S) -> Result<Self, ErrorStack> { + let (bio, method) = bio::new(stream)?; + unsafe { + ffi::SSL_set_bio(ssl.as_ptr(), bio, bio); + } + + Ok(SslStream { + ssl: ManuallyDrop::new(ssl), + method: ManuallyDrop::new(method), + _p: PhantomData, + }) + } + + /// Constructs an `SslStream` from a pointer to the underlying OpenSSL `SSL` struct. + /// + /// This is useful if the handshake has already been completed elsewhere. + /// + /// # Safety + /// + /// The caller must ensure the pointer is valid. + #[deprecated( + since = "0.10.32", + note = "use Ssl::from_ptr and SslStream::new instead" + )] + pub unsafe fn from_raw_parts(ssl: *mut ffi::SSL, stream: S) -> Self { + let ssl = Ssl::from_ptr(ssl); + Self::new(ssl, stream).unwrap() + } + + /// Read application data transmitted by a client before handshake completion. + /// + /// Useful for reducing latency, but vulnerable to replay attacks. Call + /// [`SslRef::set_accept_state`] first. + /// + /// Returns `Ok(0)` if all early data has been read. + /// + /// Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer. + #[corresponds(SSL_read_early_data)] + #[cfg(any(ossl111, libressl340))] + pub fn read_early_data(&mut self, buf: &mut [u8]) -> Result<usize, Error> { + let mut read = 0; + let ret = unsafe { + ffi::SSL_read_early_data( + self.ssl.as_ptr(), + buf.as_ptr() as *mut c_void, + buf.len(), + &mut read, + ) + }; + match ret { + ffi::SSL_READ_EARLY_DATA_ERROR => Err(self.make_error(ret)), + ffi::SSL_READ_EARLY_DATA_SUCCESS => Ok(read), + ffi::SSL_READ_EARLY_DATA_FINISH => Ok(0), + _ => unreachable!(), + } + } + + /// Send data to the server without blocking on handshake completion. + /// + /// Useful for reducing latency, but vulnerable to replay attacks. Call + /// [`SslRef::set_connect_state`] first. + /// + /// Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer. + #[corresponds(SSL_write_early_data)] + #[cfg(any(ossl111, libressl340))] + pub fn write_early_data(&mut self, buf: &[u8]) -> Result<usize, Error> { + let mut written = 0; + let ret = unsafe { + ffi::SSL_write_early_data( + self.ssl.as_ptr(), + buf.as_ptr() as *const c_void, + buf.len(), + &mut written, + ) + }; + if ret > 0 { + Ok(written) + } else { + Err(self.make_error(ret)) + } + } + + /// Initiates a client-side TLS handshake. + /// + /// # Warning + /// + /// OpenSSL's default configuration is insecure. It is highly recommended to use + /// `SslConnector` rather than `Ssl` directly, as it manages that configuration. + #[corresponds(SSL_connect)] + pub fn connect(&mut self) -> Result<(), Error> { + let ret = unsafe { ffi::SSL_connect(self.ssl.as_ptr()) }; + if ret > 0 { + Ok(()) + } else { + Err(self.make_error(ret)) + } + } + + /// Initiates a server-side TLS handshake. + /// + /// # Warning + /// + /// OpenSSL's default configuration is insecure. It is highly recommended to use + /// `SslAcceptor` rather than `Ssl` directly, as it manages that configuration. + #[corresponds(SSL_accept)] + pub fn accept(&mut self) -> Result<(), Error> { + let ret = unsafe { ffi::SSL_accept(self.ssl.as_ptr()) }; + if ret > 0 { + Ok(()) + } else { + Err(self.make_error(ret)) + } + } + + /// Initiates the handshake. + /// + /// This will fail if `set_accept_state` or `set_connect_state` was not called first. + #[corresponds(SSL_do_handshake)] + pub fn do_handshake(&mut self) -> Result<(), Error> { + let ret = unsafe { ffi::SSL_do_handshake(self.ssl.as_ptr()) }; + if ret > 0 { + Ok(()) + } else { + Err(self.make_error(ret)) + } + } + + /// Perform a stateless server-side handshake. + /// + /// Requires that cookie generation and verification callbacks were + /// set on the SSL context. + /// + /// Returns `Ok(true)` if a complete ClientHello containing a valid cookie + /// was read, in which case the handshake should be continued via + /// `accept`. If a HelloRetryRequest containing a fresh cookie was + /// transmitted, `Ok(false)` is returned instead. If the handshake cannot + /// proceed at all, `Err` is returned. + #[corresponds(SSL_stateless)] + #[cfg(ossl111)] + pub fn stateless(&mut self) -> Result<bool, ErrorStack> { + match unsafe { ffi::SSL_stateless(self.ssl.as_ptr()) } { + 1 => Ok(true), + 0 => Ok(false), + -1 => Err(ErrorStack::get()), + _ => unreachable!(), + } + } + + /// Like `read`, but returns an `ssl::Error` rather than an `io::Error`. + /// + /// It is particularly useful with a non-blocking socket, where the error value will identify if + /// OpenSSL is waiting on read or write readiness. + #[corresponds(SSL_read)] + pub fn ssl_read(&mut self, buf: &mut [u8]) -> Result<usize, Error> { + // The interpretation of the return code here is a little odd with a + // zero-length write. OpenSSL will likely correctly report back to us + // that it read zero bytes, but zero is also the sentinel for "error". + // To avoid that confusion short-circuit that logic and return quickly + // if `buf` has a length of zero. + if buf.is_empty() { + return Ok(0); + } + + let ret = self.ssl.read(buf); + if ret > 0 { + Ok(ret as usize) + } else { + Err(self.make_error(ret)) + } + } + + /// Like `write`, but returns an `ssl::Error` rather than an `io::Error`. + /// + /// It is particularly useful with a non-blocking socket, where the error value will identify if + /// OpenSSL is waiting on read or write readiness. + #[corresponds(SSL_write)] + pub fn ssl_write(&mut self, buf: &[u8]) -> Result<usize, Error> { + // See above for why we short-circuit on zero-length buffers + if buf.is_empty() { + return Ok(0); + } + + let ret = self.ssl.write(buf); + if ret > 0 { + Ok(ret as usize) + } else { + Err(self.make_error(ret)) + } + } + + /// Reads data from the stream, without removing it from the queue. + #[corresponds(SSL_peek)] + pub fn ssl_peek(&mut self, buf: &mut [u8]) -> Result<usize, Error> { + // See above for why we short-circuit on zero-length buffers + if buf.is_empty() { + return Ok(0); + } + + let ret = self.ssl.peek(buf); + if ret > 0 { + Ok(ret as usize) + } else { + Err(self.make_error(ret)) + } + } + + /// Shuts down the session. + /// + /// The shutdown process consists of two steps. The first step sends a close notify message to + /// the peer, after which `ShutdownResult::Sent` is returned. The second step awaits the receipt + /// of a close notify message from the peer, after which `ShutdownResult::Received` is returned. + /// + /// While the connection may be closed after the first step, it is recommended to fully shut the + /// session down. In particular, it must be fully shut down if the connection is to be used for + /// further communication in the future. + #[corresponds(SSL_shutdown)] + pub fn shutdown(&mut self) -> Result<ShutdownResult, Error> { + match unsafe { ffi::SSL_shutdown(self.ssl.as_ptr()) } { + 0 => Ok(ShutdownResult::Sent), + 1 => Ok(ShutdownResult::Received), + n => Err(self.make_error(n)), + } + } + + /// Returns the session's shutdown state. + #[corresponds(SSL_get_shutdown)] + pub fn get_shutdown(&mut self) -> ShutdownState { + unsafe { + let bits = ffi::SSL_get_shutdown(self.ssl.as_ptr()); + ShutdownState::from_bits_retain(bits) + } + } + + /// Sets the session's shutdown state. + /// + /// This can be used to tell OpenSSL that the session should be cached even if a full two-way + /// shutdown was not completed. + #[corresponds(SSL_set_shutdown)] + pub fn set_shutdown(&mut self, state: ShutdownState) { + unsafe { ffi::SSL_set_shutdown(self.ssl.as_ptr(), state.bits()) } + } +} + +impl<S> SslStream<S> { + fn make_error(&mut self, ret: c_int) -> Error { + self.check_panic(); + + let code = self.ssl.get_error(ret); + + let cause = match code { + ErrorCode::SSL => Some(InnerError::Ssl(ErrorStack::get())), + ErrorCode::SYSCALL => { + let errs = ErrorStack::get(); + if errs.errors().is_empty() { + self.get_bio_error().map(InnerError::Io) + } else { + Some(InnerError::Ssl(errs)) + } + } + ErrorCode::ZERO_RETURN => None, + ErrorCode::WANT_READ | ErrorCode::WANT_WRITE => { + self.get_bio_error().map(InnerError::Io) + } + _ => None, + }; + + Error { code, cause } + } + + fn check_panic(&mut self) { + if let Some(err) = unsafe { bio::take_panic::<S>(self.ssl.get_raw_rbio()) } { + resume_unwind(err) + } + } + + fn get_bio_error(&mut self) -> Option<io::Error> { + unsafe { bio::take_error::<S>(self.ssl.get_raw_rbio()) } + } + + /// Returns a shared reference to the underlying stream. + pub fn get_ref(&self) -> &S { + unsafe { + let bio = self.ssl.get_raw_rbio(); + bio::get_ref(bio) + } + } + + /// Returns a mutable reference to the underlying stream. + /// + /// # Warning + /// + /// It is inadvisable to read from or write to the underlying stream as it + /// will most likely corrupt the SSL session. + pub fn get_mut(&mut self) -> &mut S { + unsafe { + let bio = self.ssl.get_raw_rbio(); + bio::get_mut(bio) + } + } + + /// Returns a shared reference to the `Ssl` object associated with this stream. + pub fn ssl(&self) -> &SslRef { + &self.ssl + } +} + +impl<S: Read + Write> Read for SslStream<S> { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + loop { + match self.ssl_read(buf) { + Ok(n) => return Ok(n), + Err(ref e) if e.code() == ErrorCode::ZERO_RETURN => return Ok(0), + Err(ref e) if e.code() == ErrorCode::SYSCALL && e.io_error().is_none() => { + return Ok(0); + } + Err(ref e) if e.code() == ErrorCode::WANT_READ && e.io_error().is_none() => {} + Err(e) => { + return Err(e + .into_io_error() + .unwrap_or_else(|e| io::Error::new(io::ErrorKind::Other, e))); + } + } + } + } +} + +impl<S: Read + Write> Write for SslStream<S> { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + loop { + match self.ssl_write(buf) { + Ok(n) => return Ok(n), + Err(ref e) if e.code() == ErrorCode::WANT_READ && e.io_error().is_none() => {} + Err(e) => { + return Err(e + .into_io_error() + .unwrap_or_else(|e| io::Error::new(io::ErrorKind::Other, e))); + } + } + } + } + + fn flush(&mut self) -> io::Result<()> { + self.get_mut().flush() + } +} + +/// A partially constructed `SslStream`, useful for unusual handshakes. +#[deprecated( + since = "0.10.32", + note = "use the methods directly on Ssl/SslStream instead" +)] +pub struct SslStreamBuilder<S> { + inner: SslStream<S>, +} + +#[allow(deprecated)] +impl<S> SslStreamBuilder<S> +where + S: Read + Write, +{ + /// Begin creating an `SslStream` atop `stream` + pub fn new(ssl: Ssl, stream: S) -> Self { + Self { + inner: SslStream::new(ssl, stream).unwrap(), + } + } + + /// Perform a stateless server-side handshake + /// + /// Requires that cookie generation and verification callbacks were + /// set on the SSL context. + /// + /// Returns `Ok(true)` if a complete ClientHello containing a valid cookie + /// was read, in which case the handshake should be continued via + /// `accept`. If a HelloRetryRequest containing a fresh cookie was + /// transmitted, `Ok(false)` is returned instead. If the handshake cannot + /// proceed at all, `Err` is returned. + /// + /// This corresponds to [`SSL_stateless`] + /// + /// [`SSL_stateless`]: https://www.openssl.org/docs/manmaster/man3/SSL_stateless.html + #[cfg(ossl111)] + pub fn stateless(&mut self) -> Result<bool, ErrorStack> { + match unsafe { ffi::SSL_stateless(self.inner.ssl.as_ptr()) } { + 1 => Ok(true), + 0 => Ok(false), + -1 => Err(ErrorStack::get()), + _ => unreachable!(), + } + } + + /// Configure as an outgoing stream from a client. + /// + /// This corresponds to [`SSL_set_connect_state`]. + /// + /// [`SSL_set_connect_state`]: https://www.openssl.org/docs/manmaster/man3/SSL_set_connect_state.html + pub fn set_connect_state(&mut self) { + unsafe { ffi::SSL_set_connect_state(self.inner.ssl.as_ptr()) } + } + + /// Configure as an incoming stream to a server. + /// + /// This corresponds to [`SSL_set_accept_state`]. + /// + /// [`SSL_set_accept_state`]: https://www.openssl.org/docs/manmaster/man3/SSL_set_accept_state.html + pub fn set_accept_state(&mut self) { + unsafe { ffi::SSL_set_accept_state(self.inner.ssl.as_ptr()) } + } + + /// See `Ssl::connect` + pub fn connect(mut self) -> Result<SslStream<S>, HandshakeError<S>> { + match self.inner.connect() { + Ok(()) => Ok(self.inner), + Err(error) => match error.code() { + ErrorCode::WANT_READ | ErrorCode::WANT_WRITE => { + Err(HandshakeError::WouldBlock(MidHandshakeSslStream { + stream: self.inner, + error, + })) + } + _ => Err(HandshakeError::Failure(MidHandshakeSslStream { + stream: self.inner, + error, + })), + }, + } + } + + /// See `Ssl::accept` + pub fn accept(mut self) -> Result<SslStream<S>, HandshakeError<S>> { + match self.inner.accept() { + Ok(()) => Ok(self.inner), + Err(error) => match error.code() { + ErrorCode::WANT_READ | ErrorCode::WANT_WRITE => { + Err(HandshakeError::WouldBlock(MidHandshakeSslStream { + stream: self.inner, + error, + })) + } + _ => Err(HandshakeError::Failure(MidHandshakeSslStream { + stream: self.inner, + error, + })), + }, + } + } + + /// Initiates the handshake. + /// + /// This will fail if `set_accept_state` or `set_connect_state` was not called first. + /// + /// This corresponds to [`SSL_do_handshake`]. + /// + /// [`SSL_do_handshake`]: https://www.openssl.org/docs/manmaster/man3/SSL_do_handshake.html + pub fn handshake(mut self) -> Result<SslStream<S>, HandshakeError<S>> { + match self.inner.do_handshake() { + Ok(()) => Ok(self.inner), + Err(error) => match error.code() { + ErrorCode::WANT_READ | ErrorCode::WANT_WRITE => { + Err(HandshakeError::WouldBlock(MidHandshakeSslStream { + stream: self.inner, + error, + })) + } + _ => Err(HandshakeError::Failure(MidHandshakeSslStream { + stream: self.inner, + error, + })), + }, + } + } + + /// Read application data transmitted by a client before handshake + /// completion. + /// + /// Useful for reducing latency, but vulnerable to replay attacks. Call + /// `set_accept_state` first. + /// + /// Returns `Ok(0)` if all early data has been read. + /// + /// Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer. + /// + /// This corresponds to [`SSL_read_early_data`]. + /// + /// [`SSL_read_early_data`]: https://www.openssl.org/docs/manmaster/man3/SSL_read_early_data.html + #[cfg(any(ossl111, libressl340))] + pub fn read_early_data(&mut self, buf: &mut [u8]) -> Result<usize, Error> { + self.inner.read_early_data(buf) + } + + /// Send data to the server without blocking on handshake completion. + /// + /// Useful for reducing latency, but vulnerable to replay attacks. Call + /// `set_connect_state` first. + /// + /// Requires OpenSSL 1.1.1 or LibreSSL 3.4.0 or newer. + /// + /// This corresponds to [`SSL_write_early_data`]. + /// + /// [`SSL_write_early_data`]: https://www.openssl.org/docs/manmaster/man3/SSL_write_early_data.html + #[cfg(any(ossl111, libressl340))] + pub fn write_early_data(&mut self, buf: &[u8]) -> Result<usize, Error> { + self.inner.write_early_data(buf) + } +} + +#[allow(deprecated)] +impl<S> SslStreamBuilder<S> { + /// Returns a shared reference to the underlying stream. + pub fn get_ref(&self) -> &S { + unsafe { + let bio = self.inner.ssl.get_raw_rbio(); + bio::get_ref(bio) + } + } + + /// Returns a mutable reference to the underlying stream. + /// + /// # Warning + /// + /// It is inadvisable to read from or write to the underlying stream as it + /// will most likely corrupt the SSL session. + pub fn get_mut(&mut self) -> &mut S { + unsafe { + let bio = self.inner.ssl.get_raw_rbio(); + bio::get_mut(bio) + } + } + + /// Returns a shared reference to the `Ssl` object associated with this builder. + pub fn ssl(&self) -> &SslRef { + &self.inner.ssl + } + + /// Set the DTLS MTU size. + /// + /// It will be ignored if the value is smaller than the minimum packet size + /// the DTLS protocol requires. + /// + /// # Panics + /// This function panics if the given mtu size can't be represented in a positive `c_long` range + #[deprecated(note = "Use SslRef::set_mtu instead", since = "0.10.30")] + pub fn set_dtls_mtu_size(&mut self, mtu_size: usize) { + unsafe { + let bio = self.inner.ssl.get_raw_rbio(); + bio::set_dtls_mtu_size::<S>(bio, mtu_size); + } + } +} + +/// The result of a shutdown request. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum ShutdownResult { + /// A close notify message has been sent to the peer. + Sent, + + /// A close notify response message has been received from the peer. + Received, +} + +bitflags! { + /// The shutdown state of a session. + #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + #[repr(transparent)] + pub struct ShutdownState: c_int { + /// A close notify message has been sent to the peer. + const SENT = ffi::SSL_SENT_SHUTDOWN; + /// A close notify message has been received from the peer. + const RECEIVED = ffi::SSL_RECEIVED_SHUTDOWN; + } +} + +cfg_if! { + if #[cfg(any(boringssl, ossl110, libressl273))] { + use ffi::{SSL_CTX_up_ref, SSL_SESSION_get_master_key, SSL_SESSION_up_ref, SSL_is_server}; + } else { + #[allow(bad_style)] + pub unsafe fn SSL_CTX_up_ref(ssl: *mut ffi::SSL_CTX) -> c_int { + ffi::CRYPTO_add_lock( + &mut (*ssl).references, + 1, + ffi::CRYPTO_LOCK_SSL_CTX, + "mod.rs\0".as_ptr() as *const _, + line!() as c_int, + ); + 0 + } + + #[allow(bad_style)] + pub unsafe fn SSL_SESSION_get_master_key( + session: *const ffi::SSL_SESSION, + out: *mut c_uchar, + mut outlen: usize, + ) -> usize { + if outlen == 0 { + return (*session).master_key_length as usize; + } + if outlen > (*session).master_key_length as usize { + outlen = (*session).master_key_length as usize; + } + ptr::copy_nonoverlapping((*session).master_key.as_ptr(), out, outlen); + outlen + } + + #[allow(bad_style)] + pub unsafe fn SSL_is_server(s: *mut ffi::SSL) -> c_int { + (*s).server + } + + #[allow(bad_style)] + pub unsafe fn SSL_SESSION_up_ref(ses: *mut ffi::SSL_SESSION) -> c_int { + ffi::CRYPTO_add_lock( + &mut (*ses).references, + 1, + ffi::CRYPTO_LOCK_SSL_CTX, + "mod.rs\0".as_ptr() as *const _, + line!() as c_int, + ); + 0 + } + } +} + +cfg_if! { + if #[cfg(ossl300)] { + use ffi::SSL_get1_peer_certificate; + } else { + use ffi::SSL_get_peer_certificate as SSL_get1_peer_certificate; + } +} +cfg_if! { + if #[cfg(any(boringssl, ossl110, libressl291))] { + use ffi::{TLS_method, DTLS_method, TLS_client_method, TLS_server_method}; + } else { + use ffi::{ + SSLv23_method as TLS_method, DTLSv1_method as DTLS_method, SSLv23_client_method as TLS_client_method, + SSLv23_server_method as TLS_server_method, + }; + } +} +cfg_if! { + if #[cfg(ossl110)] { + unsafe fn get_new_idx(f: ffi::CRYPTO_EX_free) -> c_int { + ffi::CRYPTO_get_ex_new_index( + ffi::CRYPTO_EX_INDEX_SSL_CTX, + 0, + ptr::null_mut(), + None, + None, + Some(f), + ) + } + + unsafe fn get_new_ssl_idx(f: ffi::CRYPTO_EX_free) -> c_int { + ffi::CRYPTO_get_ex_new_index( + ffi::CRYPTO_EX_INDEX_SSL, + 0, + ptr::null_mut(), + None, + None, + Some(f), + ) + } + } else { + use std::sync::Once; + + unsafe fn get_new_idx(f: ffi::CRYPTO_EX_free) -> c_int { + // hack around https://rt.openssl.org/Ticket/Display.html?id=3710&user=guest&pass=guest + static ONCE: Once = Once::new(); + ONCE.call_once(|| { + cfg_if! { + if #[cfg(not(boringssl))] { + ffi::SSL_CTX_get_ex_new_index(0, ptr::null_mut(), None, None, None); + } else { + ffi::SSL_CTX_get_ex_new_index(0, ptr::null_mut(), ptr::null_mut(), None, None); + } + } + }); + + cfg_if! { + if #[cfg(not(boringssl))] { + ffi::SSL_CTX_get_ex_new_index(0, ptr::null_mut(), None, None, Some(f)) + } else { + ffi::SSL_CTX_get_ex_new_index(0, ptr::null_mut(), ptr::null_mut(), None, f) + } + } + } + + unsafe fn get_new_ssl_idx(f: ffi::CRYPTO_EX_free) -> c_int { + // hack around https://rt.openssl.org/Ticket/Display.html?id=3710&user=guest&pass=guest + static ONCE: Once = Once::new(); + ONCE.call_once(|| { + #[cfg(not(boringssl))] + ffi::SSL_get_ex_new_index(0, ptr::null_mut(), None, None, None); + #[cfg(boringssl)] + ffi::SSL_get_ex_new_index(0, ptr::null_mut(), ptr::null_mut(), None, None); + }); + + #[cfg(not(boringssl))] + return ffi::SSL_get_ex_new_index(0, ptr::null_mut(), None, None, Some(f)); + #[cfg(boringssl)] + return ffi::SSL_get_ex_new_index(0, ptr::null_mut(), ptr::null_mut(), None, f); + } + } +} diff --git a/vendor/openssl/src/ssl/test/mod.rs b/vendor/openssl/src/ssl/test/mod.rs new file mode 100644 index 0000000..7707af2 --- /dev/null +++ b/vendor/openssl/src/ssl/test/mod.rs @@ -0,0 +1,1570 @@ +#![allow(unused_imports)] + +use std::env; +use std::fs::File; +use std::io::prelude::*; +use std::io::{self, BufReader}; +use std::iter; +use std::mem; +use std::net::UdpSocket; +use std::net::{SocketAddr, TcpListener, TcpStream}; +use std::path::Path; +use std::process::{Child, ChildStdin, Command, Stdio}; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::thread; +use std::time::Duration; + +use crate::dh::Dh; +use crate::error::ErrorStack; +use crate::hash::MessageDigest; +#[cfg(not(boringssl))] +use crate::ocsp::{OcspResponse, OcspResponseStatus}; +use crate::pkey::PKey; +use crate::srtp::SrtpProfileId; +use crate::ssl::test::server::Server; +#[cfg(any(ossl110, ossl111, libressl261))] +use crate::ssl::SslVersion; +use crate::ssl::{self, NameType, SslConnectorBuilder}; +#[cfg(ossl111)] +use crate::ssl::{ClientHelloResponse, ExtensionContext}; +use crate::ssl::{ + Error, HandshakeError, MidHandshakeSslStream, ShutdownResult, ShutdownState, Ssl, SslAcceptor, + SslAcceptorBuilder, SslConnector, SslContext, SslContextBuilder, SslFiletype, SslMethod, + SslOptions, SslSessionCacheMode, SslStream, SslVerifyMode, StatusType, +}; +#[cfg(ossl102)] +use crate::x509::store::X509StoreBuilder; +#[cfg(ossl102)] +use crate::x509::verify::X509CheckFlags; +use crate::x509::{X509Name, X509StoreContext, X509VerifyResult, X509}; + +mod server; + +static ROOT_CERT: &[u8] = include_bytes!("../../../test/root-ca.pem"); +static CERT: &[u8] = include_bytes!("../../../test/cert.pem"); +static KEY: &[u8] = include_bytes!("../../../test/key.pem"); + +#[test] +fn verify_untrusted() { + let mut server = Server::builder(); + server.should_error(); + let server = server.build(); + + let mut client = server.client(); + client.ctx().set_verify(SslVerifyMode::PEER); + + client.connect_err(); +} + +#[test] +fn verify_trusted() { + let server = Server::builder().build(); + + let mut client = server.client(); + client.ctx().set_ca_file("test/root-ca.pem").unwrap(); + + client.connect(); +} + +#[test] +#[cfg(ossl102)] +fn verify_trusted_with_set_cert() { + let server = Server::builder().build(); + + let mut store = X509StoreBuilder::new().unwrap(); + let x509 = X509::from_pem(ROOT_CERT).unwrap(); + store.add_cert(x509).unwrap(); + + let mut client = server.client(); + client.ctx().set_verify(SslVerifyMode::PEER); + client.ctx().set_verify_cert_store(store.build()).unwrap(); + + client.connect(); +} + +#[test] +fn verify_untrusted_callback_override_ok() { + static CALLED_BACK: AtomicBool = AtomicBool::new(false); + + let server = Server::builder().build(); + + let mut client = server.client(); + client + .ctx() + .set_verify_callback(SslVerifyMode::PEER, |_, x509| { + CALLED_BACK.store(true, Ordering::SeqCst); + assert!(x509.current_cert().is_some()); + true + }); + + client.connect(); + assert!(CALLED_BACK.load(Ordering::SeqCst)); +} + +#[test] +fn verify_untrusted_callback_override_bad() { + let mut server = Server::builder(); + server.should_error(); + let server = server.build(); + + let mut client = server.client(); + client + .ctx() + .set_verify_callback(SslVerifyMode::PEER, |_, _| false); + + client.connect_err(); +} + +#[test] +fn verify_trusted_callback_override_ok() { + static CALLED_BACK: AtomicBool = AtomicBool::new(false); + + let server = Server::builder().build(); + + let mut client = server.client(); + client.ctx().set_ca_file("test/root-ca.pem").unwrap(); + client + .ctx() + .set_verify_callback(SslVerifyMode::PEER, |_, x509| { + CALLED_BACK.store(true, Ordering::SeqCst); + assert!(x509.current_cert().is_some()); + true + }); + + client.connect(); + assert!(CALLED_BACK.load(Ordering::SeqCst)); +} + +#[test] +fn verify_trusted_callback_override_bad() { + let mut server = Server::builder(); + server.should_error(); + let server = server.build(); + + let mut client = server.client(); + client.ctx().set_ca_file("test/root-ca.pem").unwrap(); + client + .ctx() + .set_verify_callback(SslVerifyMode::PEER, |_, _| false); + + client.connect_err(); +} + +#[test] +fn verify_callback_load_certs() { + static CALLED_BACK: AtomicBool = AtomicBool::new(false); + + let server = Server::builder().build(); + + let mut client = server.client(); + client + .ctx() + .set_verify_callback(SslVerifyMode::PEER, |_, x509| { + CALLED_BACK.store(true, Ordering::SeqCst); + assert!(x509.current_cert().is_some()); + true + }); + + client.connect(); + assert!(CALLED_BACK.load(Ordering::SeqCst)); +} + +#[test] +fn verify_trusted_get_error_ok() { + static CALLED_BACK: AtomicBool = AtomicBool::new(false); + + let server = Server::builder().build(); + + let mut client = server.client(); + client.ctx().set_ca_file("test/root-ca.pem").unwrap(); + client + .ctx() + .set_verify_callback(SslVerifyMode::PEER, |_, x509| { + CALLED_BACK.store(true, Ordering::SeqCst); + assert_eq!(x509.error(), X509VerifyResult::OK); + true + }); + + client.connect(); + assert!(CALLED_BACK.load(Ordering::SeqCst)); +} + +#[test] +fn verify_trusted_get_error_err() { + let mut server = Server::builder(); + server.should_error(); + let server = server.build(); + + let mut client = server.client(); + client + .ctx() + .set_verify_callback(SslVerifyMode::PEER, |_, x509| { + assert_ne!(x509.error(), X509VerifyResult::OK); + false + }); + + client.connect_err(); +} + +#[test] +fn verify_callback() { + static CALLED_BACK: AtomicBool = AtomicBool::new(false); + + let server = Server::builder().build(); + + let mut client = server.client(); + let expected = "59172d9313e84459bcff27f967e79e6e9217e584"; + client + .ctx() + .set_verify_callback(SslVerifyMode::PEER, move |_, x509| { + CALLED_BACK.store(true, Ordering::SeqCst); + let cert = x509.current_cert().unwrap(); + let digest = cert.digest(MessageDigest::sha1()).unwrap(); + assert_eq!(hex::encode(digest), expected); + true + }); + + client.connect(); + assert!(CALLED_BACK.load(Ordering::SeqCst)); +} + +#[test] +fn ssl_verify_callback() { + static CALLED_BACK: AtomicBool = AtomicBool::new(false); + + let server = Server::builder().build(); + + let mut client = server.client().build().builder(); + let expected = "59172d9313e84459bcff27f967e79e6e9217e584"; + client + .ssl() + .set_verify_callback(SslVerifyMode::PEER, move |_, x509| { + CALLED_BACK.store(true, Ordering::SeqCst); + let cert = x509.current_cert().unwrap(); + let digest = cert.digest(MessageDigest::sha1()).unwrap(); + assert_eq!(hex::encode(digest), expected); + true + }); + + client.connect(); + assert!(CALLED_BACK.load(Ordering::SeqCst)); +} + +#[test] +fn get_ctx_options() { + let ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.options(); +} + +#[test] +fn set_ctx_options() { + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + let opts = ctx.set_options(SslOptions::NO_TICKET); + assert!(opts.contains(SslOptions::NO_TICKET)); +} + +#[test] +#[cfg(not(boringssl))] +fn clear_ctx_options() { + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_options(SslOptions::ALL); + let opts = ctx.clear_options(SslOptions::ALL); + assert!(!opts.contains(SslOptions::ALL)); +} + +#[test] +fn zero_length_buffers() { + let server = Server::builder().build(); + + let mut s = server.client().connect(); + assert_eq!(s.write(&[]).unwrap(), 0); + assert_eq!(s.read(&mut []).unwrap(), 0); +} + +#[test] +fn peer_certificate() { + let server = Server::builder().build(); + + let s = server.client().connect(); + let cert = s.ssl().peer_certificate().unwrap(); + let fingerprint = cert.digest(MessageDigest::sha1()).unwrap(); + assert_eq!( + hex::encode(fingerprint), + "59172d9313e84459bcff27f967e79e6e9217e584" + ); +} + +#[test] +fn pending() { + let mut server = Server::builder(); + server.io_cb(|mut s| s.write_all(&[0; 10]).unwrap()); + let server = server.build(); + + let mut s = server.client().connect(); + s.read_exact(&mut [0]).unwrap(); + + assert_eq!(s.ssl().pending(), 9); + assert_eq!(s.read(&mut [0; 10]).unwrap(), 9); +} + +#[test] +fn state() { + let server = Server::builder().build(); + + let s = server.client().connect(); + #[cfg(not(boringssl))] + assert_eq!(s.ssl().state_string().trim(), "SSLOK"); + #[cfg(boringssl)] + assert_eq!(s.ssl().state_string(), "!!!!!!"); + assert_eq!( + s.ssl().state_string_long(), + "SSL negotiation finished successfully" + ); +} + +/// Tests that when both the client as well as the server use SRTP and their +/// lists of supported protocols have an overlap -- with only ONE protocol +/// being valid for both. +#[test] +fn test_connect_with_srtp_ctx() { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let addr = listener.local_addr().unwrap(); + + let guard = thread::spawn(move || { + let stream = listener.accept().unwrap().0; + let mut ctx = SslContext::builder(SslMethod::dtls()).unwrap(); + ctx.set_tlsext_use_srtp("SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32") + .unwrap(); + ctx.set_certificate_file(Path::new("test/cert.pem"), SslFiletype::PEM) + .unwrap(); + ctx.set_private_key_file(Path::new("test/key.pem"), SslFiletype::PEM) + .unwrap(); + let mut ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.set_mtu(1500).unwrap(); + let mut stream = ssl.accept(stream).unwrap(); + + let mut buf = [0; 60]; + stream + .ssl() + .export_keying_material(&mut buf, "EXTRACTOR-dtls_srtp", None) + .unwrap(); + + stream.write_all(&[0]).unwrap(); + + buf + }); + + let stream = TcpStream::connect(addr).unwrap(); + let mut ctx = SslContext::builder(SslMethod::dtls()).unwrap(); + ctx.set_tlsext_use_srtp("SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32") + .unwrap(); + let mut ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.set_mtu(1500).unwrap(); + let mut stream = ssl.connect(stream).unwrap(); + + let mut buf = [1; 60]; + { + let srtp_profile = stream.ssl().selected_srtp_profile().unwrap(); + assert_eq!("SRTP_AES128_CM_SHA1_80", srtp_profile.name()); + assert_eq!(SrtpProfileId::SRTP_AES128_CM_SHA1_80, srtp_profile.id()); + } + stream + .ssl() + .export_keying_material(&mut buf, "EXTRACTOR-dtls_srtp", None) + .expect("extract"); + + stream.read_exact(&mut [0]).unwrap(); + + let buf2 = guard.join().unwrap(); + + assert_eq!(buf[..], buf2[..]); +} + +/// Tests that when both the client as well as the server use SRTP and their +/// lists of supported protocols have an overlap -- with only ONE protocol +/// being valid for both. +#[test] +fn test_connect_with_srtp_ssl() { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let addr = listener.local_addr().unwrap(); + + let guard = thread::spawn(move || { + let stream = listener.accept().unwrap().0; + let mut ctx = SslContext::builder(SslMethod::dtls()).unwrap(); + ctx.set_certificate_file(Path::new("test/cert.pem"), SslFiletype::PEM) + .unwrap(); + ctx.set_private_key_file(Path::new("test/key.pem"), SslFiletype::PEM) + .unwrap(); + let mut ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.set_tlsext_use_srtp("SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32") + .unwrap(); + let mut profilenames = String::new(); + for profile in ssl.srtp_profiles().unwrap() { + if !profilenames.is_empty() { + profilenames.push(':'); + } + profilenames += profile.name(); + } + assert_eq!( + "SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32", + profilenames + ); + ssl.set_mtu(1500).unwrap(); + let mut stream = ssl.accept(stream).unwrap(); + + let mut buf = [0; 60]; + stream + .ssl() + .export_keying_material(&mut buf, "EXTRACTOR-dtls_srtp", None) + .unwrap(); + + stream.write_all(&[0]).unwrap(); + + buf + }); + + let stream = TcpStream::connect(addr).unwrap(); + let ctx = SslContext::builder(SslMethod::dtls()).unwrap(); + let mut ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.set_tlsext_use_srtp("SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32") + .unwrap(); + ssl.set_mtu(1500).unwrap(); + let mut stream = ssl.connect(stream).unwrap(); + + let mut buf = [1; 60]; + { + let srtp_profile = stream.ssl().selected_srtp_profile().unwrap(); + assert_eq!("SRTP_AES128_CM_SHA1_80", srtp_profile.name()); + assert_eq!(SrtpProfileId::SRTP_AES128_CM_SHA1_80, srtp_profile.id()); + } + stream + .ssl() + .export_keying_material(&mut buf, "EXTRACTOR-dtls_srtp", None) + .expect("extract"); + + stream.read_exact(&mut [0]).unwrap(); + + let buf2 = guard.join().unwrap(); + + assert_eq!(buf[..], buf2[..]); +} + +/// Tests that when the `SslStream` is created as a server stream, the protocols +/// are correctly advertised to the client. +#[test] +#[cfg(any(ossl102, libressl261))] +fn test_alpn_server_advertise_multiple() { + let mut server = Server::builder(); + server.ctx().set_alpn_select_callback(|_, client| { + ssl::select_next_proto(b"\x08http/1.1\x08spdy/3.1", client).ok_or(ssl::AlpnError::NOACK) + }); + let server = server.build(); + + let mut client = server.client(); + client.ctx().set_alpn_protos(b"\x08spdy/3.1").unwrap(); + let s = client.connect(); + assert_eq!(s.ssl().selected_alpn_protocol(), Some(&b"spdy/3.1"[..])); +} + +#[test] +#[cfg(ossl110)] +fn test_alpn_server_select_none_fatal() { + let mut server = Server::builder(); + server.ctx().set_alpn_select_callback(|_, client| { + ssl::select_next_proto(b"\x08http/1.1\x08spdy/3.1", client) + .ok_or(ssl::AlpnError::ALERT_FATAL) + }); + server.should_error(); + let server = server.build(); + + let mut client = server.client(); + client.ctx().set_alpn_protos(b"\x06http/2").unwrap(); + client.connect_err(); +} + +#[test] +#[cfg(any(ossl102, libressl261))] +fn test_alpn_server_select_none() { + static CALLED_BACK: AtomicBool = AtomicBool::new(false); + + let mut server = Server::builder(); + server.ctx().set_alpn_select_callback(|_, client| { + CALLED_BACK.store(true, Ordering::SeqCst); + ssl::select_next_proto(b"\x08http/1.1\x08spdy/3.1", client).ok_or(ssl::AlpnError::NOACK) + }); + let server = server.build(); + + let mut client = server.client(); + client.ctx().set_alpn_protos(b"\x06http/2").unwrap(); + let s = client.connect(); + assert_eq!(None, s.ssl().selected_alpn_protocol()); + assert!(CALLED_BACK.load(Ordering::SeqCst)); +} + +#[test] +#[cfg(any(ossl102, libressl261))] +fn test_alpn_server_unilateral() { + let server = Server::builder().build(); + + let mut client = server.client(); + client.ctx().set_alpn_protos(b"\x06http/2").unwrap(); + let s = client.connect(); + assert_eq!(None, s.ssl().selected_alpn_protocol()); +} + +#[test] +#[should_panic(expected = "blammo")] +fn write_panic() { + struct ExplodingStream(TcpStream); + + impl Read for ExplodingStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + self.0.read(buf) + } + } + + impl Write for ExplodingStream { + fn write(&mut self, _: &[u8]) -> io::Result<usize> { + panic!("blammo"); + } + + fn flush(&mut self) -> io::Result<()> { + self.0.flush() + } + } + + let mut server = Server::builder(); + server.should_error(); + let server = server.build(); + + let stream = ExplodingStream(server.connect_tcp()); + + let ctx = SslContext::builder(SslMethod::tls()).unwrap(); + let _ = Ssl::new(&ctx.build()).unwrap().connect(stream); +} + +#[test] +#[should_panic(expected = "blammo")] +fn read_panic() { + struct ExplodingStream(TcpStream); + + impl Read for ExplodingStream { + fn read(&mut self, _: &mut [u8]) -> io::Result<usize> { + panic!("blammo"); + } + } + + impl Write for ExplodingStream { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.0.write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.0.flush() + } + } + + let mut server = Server::builder(); + server.should_error(); + let server = server.build(); + + let stream = ExplodingStream(server.connect_tcp()); + + let ctx = SslContext::builder(SslMethod::tls()).unwrap(); + let _ = Ssl::new(&ctx.build()).unwrap().connect(stream); +} + +#[test] +#[cfg_attr(all(libressl321, not(libressl340)), ignore)] +#[should_panic(expected = "blammo")] +fn flush_panic() { + struct ExplodingStream(TcpStream); + + impl Read for ExplodingStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + self.0.read(buf) + } + } + + impl Write for ExplodingStream { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.0.write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + panic!("blammo"); + } + } + + let mut server = Server::builder(); + server.should_error(); + let server = server.build(); + + let stream = ExplodingStream(server.connect_tcp()); + + let ctx = SslContext::builder(SslMethod::tls()).unwrap(); + let _ = Ssl::new(&ctx.build()).unwrap().connect(stream); +} + +#[test] +fn refcount_ssl_context() { + let mut ssl = { + let ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ssl::Ssl::new(&ctx.build()).unwrap() + }; + + { + let new_ctx_a = SslContext::builder(SslMethod::tls()).unwrap().build(); + ssl.set_ssl_context(&new_ctx_a).unwrap(); + } +} + +#[test] +#[cfg_attr(libressl250, ignore)] +#[cfg_attr(target_os = "windows", ignore)] +#[cfg_attr(all(target_os = "macos", feature = "vendored"), ignore)] +fn default_verify_paths() { + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_default_verify_paths().unwrap(); + ctx.set_verify(SslVerifyMode::PEER); + let ctx = ctx.build(); + let s = match TcpStream::connect("google.com:443") { + Ok(s) => s, + Err(_) => return, + }; + let mut ssl = Ssl::new(&ctx).unwrap(); + ssl.set_hostname("google.com").unwrap(); + let mut socket = ssl.connect(s).unwrap(); + + socket.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap(); + let mut result = vec![]; + socket.read_to_end(&mut result).unwrap(); + + println!("{}", String::from_utf8_lossy(&result)); + assert!(result.starts_with(b"HTTP/1.0")); + assert!(result.ends_with(b"</HTML>\r\n") || result.ends_with(b"</html>")); +} + +#[test] +fn add_extra_chain_cert() { + let cert = X509::from_pem(CERT).unwrap(); + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.add_extra_chain_cert(cert).unwrap(); +} + +#[test] +#[cfg(ossl102)] +fn verify_valid_hostname() { + let server = Server::builder().build(); + + let mut client = server.client(); + client.ctx().set_ca_file("test/root-ca.pem").unwrap(); + client.ctx().set_verify(SslVerifyMode::PEER); + + let mut client = client.build().builder(); + client + .ssl() + .param_mut() + .set_hostflags(X509CheckFlags::NO_PARTIAL_WILDCARDS); + client.ssl().param_mut().set_host("foobar.com").unwrap(); + client.connect(); +} + +#[test] +#[cfg(ossl102)] +fn verify_invalid_hostname() { + let mut server = Server::builder(); + server.should_error(); + let server = server.build(); + + let mut client = server.client(); + client.ctx().set_ca_file("test/root-ca.pem").unwrap(); + client.ctx().set_verify(SslVerifyMode::PEER); + + let mut client = client.build().builder(); + client + .ssl() + .param_mut() + .set_hostflags(X509CheckFlags::NO_PARTIAL_WILDCARDS); + client.ssl().param_mut().set_host("bogus.com").unwrap(); + client.connect_err(); +} + +#[test] +fn connector_valid_hostname() { + let server = Server::builder().build(); + + let mut connector = SslConnector::builder(SslMethod::tls()).unwrap(); + connector.set_ca_file("test/root-ca.pem").unwrap(); + + let s = server.connect_tcp(); + let mut s = connector.build().connect("foobar.com", s).unwrap(); + s.read_exact(&mut [0]).unwrap(); +} + +#[test] +fn connector_invalid_hostname() { + let mut server = Server::builder(); + server.should_error(); + let server = server.build(); + + let mut connector = SslConnector::builder(SslMethod::tls()).unwrap(); + connector.set_ca_file("test/root-ca.pem").unwrap(); + + let s = server.connect_tcp(); + connector.build().connect("bogus.com", s).unwrap_err(); +} + +#[test] +fn connector_invalid_no_hostname_verification() { + let server = Server::builder().build(); + + let mut connector = SslConnector::builder(SslMethod::tls()).unwrap(); + connector.set_ca_file("test/root-ca.pem").unwrap(); + + let s = server.connect_tcp(); + let mut s = connector + .build() + .configure() + .unwrap() + .verify_hostname(false) + .connect("bogus.com", s) + .unwrap(); + s.read_exact(&mut [0]).unwrap(); +} + +#[test] +fn connector_no_hostname_still_verifies() { + let mut server = Server::builder(); + server.should_error(); + let server = server.build(); + + let connector = SslConnector::builder(SslMethod::tls()).unwrap().build(); + + let s = server.connect_tcp(); + assert!(connector + .configure() + .unwrap() + .verify_hostname(false) + .connect("fizzbuzz.com", s) + .is_err()); +} + +#[test] +fn connector_can_disable_verify() { + let server = Server::builder().build(); + + let mut connector = SslConnector::builder(SslMethod::tls()).unwrap(); + connector.set_verify(SslVerifyMode::NONE); + let connector = connector.build(); + + let s = server.connect_tcp(); + let mut s = connector + .configure() + .unwrap() + .connect("fizzbuzz.com", s) + .unwrap(); + s.read_exact(&mut [0]).unwrap(); +} + +#[test] +fn connector_does_use_sni_with_dnsnames() { + static CALLED_BACK: AtomicBool = AtomicBool::new(false); + + let mut builder = Server::builder(); + builder.ctx().set_servername_callback(|ssl, _| { + assert_eq!(ssl.servername(NameType::HOST_NAME), Some("foobar.com")); + CALLED_BACK.store(true, Ordering::SeqCst); + Ok(()) + }); + let server = builder.build(); + + let mut connector = SslConnector::builder(SslMethod::tls()).unwrap(); + connector.set_ca_file("test/root-ca.pem").unwrap(); + + let s = server.connect_tcp(); + let mut s = connector + .build() + .configure() + .unwrap() + .connect("foobar.com", s) + .unwrap(); + s.read_exact(&mut [0]).unwrap(); + + assert!(CALLED_BACK.load(Ordering::SeqCst)); +} + +#[test] +fn connector_doesnt_use_sni_with_ips() { + static CALLED_BACK: AtomicBool = AtomicBool::new(false); + + let mut builder = Server::builder(); + builder.ctx().set_servername_callback(|ssl, _| { + assert_eq!(ssl.servername(NameType::HOST_NAME), None); + CALLED_BACK.store(true, Ordering::SeqCst); + Ok(()) + }); + let server = builder.build(); + + let mut connector = SslConnector::builder(SslMethod::tls()).unwrap(); + // The server's cert isn't issued for 127.0.0.1 but we don't care for this test. + connector.set_verify(SslVerifyMode::NONE); + + let s = server.connect_tcp(); + let mut s = connector + .build() + .configure() + .unwrap() + .connect("127.0.0.1", s) + .unwrap(); + s.read_exact(&mut [0]).unwrap(); + + assert!(CALLED_BACK.load(Ordering::SeqCst)); +} + +fn test_mozilla_server(new: fn(SslMethod) -> Result<SslAcceptorBuilder, ErrorStack>) { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let port = listener.local_addr().unwrap().port(); + + let t = thread::spawn(move || { + let key = PKey::private_key_from_pem(KEY).unwrap(); + let cert = X509::from_pem(CERT).unwrap(); + let mut acceptor = new(SslMethod::tls()).unwrap(); + acceptor.set_private_key(&key).unwrap(); + acceptor.set_certificate(&cert).unwrap(); + let acceptor = acceptor.build(); + let stream = listener.accept().unwrap().0; + let mut stream = acceptor.accept(stream).unwrap(); + + stream.write_all(b"hello").unwrap(); + }); + + let mut connector = SslConnector::builder(SslMethod::tls()).unwrap(); + connector.set_ca_file("test/root-ca.pem").unwrap(); + let connector = connector.build(); + + let stream = TcpStream::connect(("127.0.0.1", port)).unwrap(); + let mut stream = connector.connect("foobar.com", stream).unwrap(); + + let mut buf = [0; 5]; + stream.read_exact(&mut buf).unwrap(); + assert_eq!(b"hello", &buf); + + t.join().unwrap(); +} + +#[test] +fn connector_client_server_mozilla_intermediate() { + test_mozilla_server(SslAcceptor::mozilla_intermediate); +} + +#[test] +fn connector_client_server_mozilla_modern() { + test_mozilla_server(SslAcceptor::mozilla_modern); +} + +#[test] +fn connector_client_server_mozilla_intermediate_v5() { + test_mozilla_server(SslAcceptor::mozilla_intermediate_v5); +} + +#[test] +#[cfg(any(ossl111, libressl340))] +fn connector_client_server_mozilla_modern_v5() { + test_mozilla_server(SslAcceptor::mozilla_modern_v5); +} + +#[test] +fn shutdown() { + let mut server = Server::builder(); + server.io_cb(|mut s| { + assert_eq!(s.read(&mut [0]).unwrap(), 0); + assert_eq!(s.shutdown().unwrap(), ShutdownResult::Received); + }); + let server = server.build(); + + let mut s = server.client().connect(); + + assert_eq!(s.get_shutdown(), ShutdownState::empty()); + assert_eq!(s.shutdown().unwrap(), ShutdownResult::Sent); + assert_eq!(s.get_shutdown(), ShutdownState::SENT); + assert_eq!(s.shutdown().unwrap(), ShutdownResult::Received); + assert_eq!( + s.get_shutdown(), + ShutdownState::SENT | ShutdownState::RECEIVED + ); +} + +#[test] +fn client_ca_list() { + let names = X509Name::load_client_ca_file("test/root-ca.pem").unwrap(); + assert_eq!(names.len(), 1); + + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_client_ca_list(names); +} + +#[test] +fn cert_store() { + let server = Server::builder().build(); + + let mut client = server.client(); + let cert = X509::from_pem(ROOT_CERT).unwrap(); + client.ctx().cert_store_mut().add_cert(cert).unwrap(); + client.ctx().set_verify(SslVerifyMode::PEER); + + client.connect(); +} + +#[test] +#[cfg_attr(any(all(libressl321, not(libressl340)), boringssl), ignore)] +fn tmp_dh_callback() { + static CALLED_BACK: AtomicBool = AtomicBool::new(false); + + let mut server = Server::builder(); + server.ctx().set_tmp_dh_callback(|_, _, _| { + CALLED_BACK.store(true, Ordering::SeqCst); + let dh = include_bytes!("../../../test/dhparams.pem"); + Dh::params_from_pem(dh) + }); + + let server = server.build(); + + let mut client = server.client(); + // TLS 1.3 has no DH suites, so make sure we don't pick that version + #[cfg(any(ossl111, libressl340))] + client.ctx().set_options(super::SslOptions::NO_TLSV1_3); + client.ctx().set_cipher_list("EDH").unwrap(); + client.connect(); + + assert!(CALLED_BACK.load(Ordering::SeqCst)); +} + +#[test] +#[cfg(all(ossl101, not(ossl110)))] +#[allow(deprecated)] +fn tmp_ecdh_callback() { + use crate::ec::EcKey; + use crate::nid::Nid; + + static CALLED_BACK: AtomicBool = AtomicBool::new(false); + + let mut server = Server::builder(); + server.ctx().set_tmp_ecdh_callback(|_, _, _| { + CALLED_BACK.store(true, Ordering::SeqCst); + EcKey::from_curve_name(Nid::X9_62_PRIME256V1) + }); + + let server = server.build(); + + let mut client = server.client(); + client.ctx().set_cipher_list("ECDH").unwrap(); + client.connect(); + + assert!(CALLED_BACK.load(Ordering::SeqCst)); +} + +#[test] +#[cfg_attr(any(all(libressl321, not(libressl340)), boringssl), ignore)] +fn tmp_dh_callback_ssl() { + static CALLED_BACK: AtomicBool = AtomicBool::new(false); + + let mut server = Server::builder(); + server.ssl_cb(|ssl| { + ssl.set_tmp_dh_callback(|_, _, _| { + CALLED_BACK.store(true, Ordering::SeqCst); + let dh = include_bytes!("../../../test/dhparams.pem"); + Dh::params_from_pem(dh) + }); + }); + + let server = server.build(); + + let mut client = server.client(); + // TLS 1.3 has no DH suites, so make sure we don't pick that version + #[cfg(any(ossl111, libressl340))] + client.ctx().set_options(super::SslOptions::NO_TLSV1_3); + client.ctx().set_cipher_list("EDH").unwrap(); + client.connect(); + + assert!(CALLED_BACK.load(Ordering::SeqCst)); +} + +#[test] +#[cfg(all(ossl101, not(ossl110)))] +#[allow(deprecated)] +fn tmp_ecdh_callback_ssl() { + use crate::ec::EcKey; + use crate::nid::Nid; + + static CALLED_BACK: AtomicBool = AtomicBool::new(false); + + let mut server = Server::builder(); + server.ssl_cb(|ssl| { + ssl.set_tmp_ecdh_callback(|_, _, _| { + CALLED_BACK.store(true, Ordering::SeqCst); + EcKey::from_curve_name(Nid::X9_62_PRIME256V1) + }); + }); + + let server = server.build(); + + let mut client = server.client(); + client.ctx().set_cipher_list("ECDH").unwrap(); + client.connect(); + + assert!(CALLED_BACK.load(Ordering::SeqCst)); +} + +#[test] +fn idle_session() { + let ctx = SslContext::builder(SslMethod::tls()).unwrap().build(); + let ssl = Ssl::new(&ctx).unwrap(); + assert!(ssl.session().is_none()); +} + +/// possible LibreSSL bug since 3.2.1 +#[test] +#[cfg_attr(libressl321, ignore)] +fn active_session() { + let server = Server::builder().build(); + + let s = server.client().connect(); + + let session = s.ssl().session().unwrap(); + let len = session.master_key_len(); + let mut buf = vec![0; len - 1]; + let copied = session.master_key(&mut buf); + assert_eq!(copied, buf.len()); + let mut buf = vec![0; len + 1]; + let copied = session.master_key(&mut buf); + assert_eq!(copied, len); +} + +#[test] +#[cfg(not(boringssl))] +fn status_callbacks() { + static CALLED_BACK_SERVER: AtomicBool = AtomicBool::new(false); + static CALLED_BACK_CLIENT: AtomicBool = AtomicBool::new(false); + + let mut server = Server::builder(); + server + .ctx() + .set_status_callback(|ssl| { + CALLED_BACK_SERVER.store(true, Ordering::SeqCst); + let response = OcspResponse::create(OcspResponseStatus::UNAUTHORIZED, None).unwrap(); + let response = response.to_der().unwrap(); + ssl.set_ocsp_status(&response).unwrap(); + Ok(true) + }) + .unwrap(); + + let server = server.build(); + + let mut client = server.client(); + client + .ctx() + .set_status_callback(|ssl| { + CALLED_BACK_CLIENT.store(true, Ordering::SeqCst); + let response = OcspResponse::from_der(ssl.ocsp_status().unwrap()).unwrap(); + assert_eq!(response.status(), OcspResponseStatus::UNAUTHORIZED); + Ok(true) + }) + .unwrap(); + + let mut client = client.build().builder(); + client.ssl().set_status_type(StatusType::OCSP).unwrap(); + + client.connect(); + + assert!(CALLED_BACK_SERVER.load(Ordering::SeqCst)); + assert!(CALLED_BACK_CLIENT.load(Ordering::SeqCst)); +} + +/// possible LibreSSL bug since 3.2.1 +#[test] +#[cfg_attr(libressl321, ignore)] +fn new_session_callback() { + static CALLED_BACK: AtomicBool = AtomicBool::new(false); + + let mut server = Server::builder(); + server.ctx().set_session_id_context(b"foo").unwrap(); + + let server = server.build(); + + let mut client = server.client(); + + client + .ctx() + .set_session_cache_mode(SslSessionCacheMode::CLIENT | SslSessionCacheMode::NO_INTERNAL); + client + .ctx() + .set_new_session_callback(|_, _| CALLED_BACK.store(true, Ordering::SeqCst)); + + client.connect(); + + assert!(CALLED_BACK.load(Ordering::SeqCst)); +} + +/// possible LibreSSL bug since 3.2.1 +#[test] +#[cfg_attr(libressl321, ignore)] +fn new_session_callback_swapped_ctx() { + static CALLED_BACK: AtomicBool = AtomicBool::new(false); + + let mut server = Server::builder(); + server.ctx().set_session_id_context(b"foo").unwrap(); + + let server = server.build(); + + let mut client = server.client(); + + client + .ctx() + .set_session_cache_mode(SslSessionCacheMode::CLIENT | SslSessionCacheMode::NO_INTERNAL); + client + .ctx() + .set_new_session_callback(|_, _| CALLED_BACK.store(true, Ordering::SeqCst)); + + let mut client = client.build().builder(); + + let ctx = SslContextBuilder::new(SslMethod::tls()).unwrap().build(); + client.ssl().set_ssl_context(&ctx).unwrap(); + + client.connect(); + + assert!(CALLED_BACK.load(Ordering::SeqCst)); +} + +#[test] +fn keying_export() { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let addr = listener.local_addr().unwrap(); + + let label = "EXPERIMENTAL test"; + let context = b"my context"; + + let guard = thread::spawn(move || { + let stream = listener.accept().unwrap().0; + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_certificate_file(Path::new("test/cert.pem"), SslFiletype::PEM) + .unwrap(); + ctx.set_private_key_file(Path::new("test/key.pem"), SslFiletype::PEM) + .unwrap(); + let ssl = Ssl::new(&ctx.build()).unwrap(); + let mut stream = ssl.accept(stream).unwrap(); + + let mut buf = [0; 32]; + stream + .ssl() + .export_keying_material(&mut buf, label, Some(context)) + .unwrap(); + + stream.write_all(&[0]).unwrap(); + + buf + }); + + let stream = TcpStream::connect(addr).unwrap(); + let ctx = SslContext::builder(SslMethod::tls()).unwrap(); + let ssl = Ssl::new(&ctx.build()).unwrap(); + let mut stream = ssl.connect(stream).unwrap(); + + let mut buf = [1; 32]; + stream + .ssl() + .export_keying_material(&mut buf, label, Some(context)) + .unwrap(); + + stream.read_exact(&mut [0]).unwrap(); + + let buf2 = guard.join().unwrap(); + + assert_eq!(buf, buf2); +} + +#[test] +#[cfg(any(ossl110, libressl261))] +fn no_version_overlap() { + let mut server = Server::builder(); + server.ctx().set_min_proto_version(None).unwrap(); + server + .ctx() + .set_max_proto_version(Some(SslVersion::TLS1_1)) + .unwrap(); + #[cfg(any(ossl110g, libressl270))] + assert_eq!(server.ctx().max_proto_version(), Some(SslVersion::TLS1_1)); + server.should_error(); + let server = server.build(); + + let mut client = server.client(); + client + .ctx() + .set_min_proto_version(Some(SslVersion::TLS1_2)) + .unwrap(); + #[cfg(ossl110g)] + assert_eq!(client.ctx().min_proto_version(), Some(SslVersion::TLS1_2)); + client.ctx().set_max_proto_version(None).unwrap(); + + client.connect_err(); +} + +#[test] +#[cfg(ossl111)] +fn custom_extensions() { + static FOUND_EXTENSION: AtomicBool = AtomicBool::new(false); + + let mut server = Server::builder(); + server + .ctx() + .add_custom_ext( + 12345, + ExtensionContext::CLIENT_HELLO, + |_, _, _| -> Result<Option<&'static [u8]>, _> { unreachable!() }, + |_, _, data, _| { + FOUND_EXTENSION.store(data == b"hello", Ordering::SeqCst); + Ok(()) + }, + ) + .unwrap(); + + let server = server.build(); + + let mut client = server.client(); + client + .ctx() + .add_custom_ext( + 12345, + ssl::ExtensionContext::CLIENT_HELLO, + |_, _, _| Ok(Some(b"hello")), + |_, _, _, _| unreachable!(), + ) + .unwrap(); + + client.connect(); + + assert!(FOUND_EXTENSION.load(Ordering::SeqCst)); +} + +fn _check_kinds() { + fn is_send<T: Send>() {} + fn is_sync<T: Sync>() {} + + is_send::<SslStream<TcpStream>>(); + is_sync::<SslStream<TcpStream>>(); +} + +#[test] +#[cfg(ossl111)] +fn stateless() { + use super::SslOptions; + + #[derive(Debug)] + struct MemoryStream { + incoming: io::Cursor<Vec<u8>>, + outgoing: Vec<u8>, + } + + impl MemoryStream { + pub fn new() -> Self { + Self { + incoming: io::Cursor::new(Vec::new()), + outgoing: Vec::new(), + } + } + + pub fn extend_incoming(&mut self, data: &[u8]) { + self.incoming.get_mut().extend_from_slice(data); + } + + pub fn take_outgoing(&mut self) -> Outgoing<'_> { + Outgoing(&mut self.outgoing) + } + } + + impl Read for MemoryStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + let n = self.incoming.read(buf)?; + if self.incoming.position() == self.incoming.get_ref().len() as u64 { + self.incoming.set_position(0); + self.incoming.get_mut().clear(); + } + if n == 0 { + return Err(io::Error::new( + io::ErrorKind::WouldBlock, + "no data available", + )); + } + Ok(n) + } + } + + impl Write for MemoryStream { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.outgoing.write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + } + + pub struct Outgoing<'a>(&'a mut Vec<u8>); + + impl<'a> Drop for Outgoing<'a> { + fn drop(&mut self) { + self.0.clear(); + } + } + + impl<'a> ::std::ops::Deref for Outgoing<'a> { + type Target = [u8]; + fn deref(&self) -> &[u8] { + self.0 + } + } + + impl<'a> AsRef<[u8]> for Outgoing<'a> { + fn as_ref(&self) -> &[u8] { + self.0 + } + } + + fn send(from: &mut MemoryStream, to: &mut MemoryStream) { + to.extend_incoming(&from.take_outgoing()); + } + + // + // Setup + // + + let mut client_ctx = SslContext::builder(SslMethod::tls()).unwrap(); + client_ctx.clear_options(SslOptions::ENABLE_MIDDLEBOX_COMPAT); + let mut client_stream = + SslStream::new(Ssl::new(&client_ctx.build()).unwrap(), MemoryStream::new()).unwrap(); + + let mut server_ctx = SslContext::builder(SslMethod::tls()).unwrap(); + server_ctx + .set_certificate_file(Path::new("test/cert.pem"), SslFiletype::PEM) + .unwrap(); + server_ctx + .set_private_key_file(Path::new("test/key.pem"), SslFiletype::PEM) + .unwrap(); + const COOKIE: &[u8] = b"chocolate chip"; + server_ctx.set_stateless_cookie_generate_cb(|_tls, buf| { + buf[0..COOKIE.len()].copy_from_slice(COOKIE); + Ok(COOKIE.len()) + }); + server_ctx.set_stateless_cookie_verify_cb(|_tls, buf| buf == COOKIE); + let mut server_stream = + SslStream::new(Ssl::new(&server_ctx.build()).unwrap(), MemoryStream::new()).unwrap(); + + // + // Handshake + // + + // Initial ClientHello + client_stream.connect().unwrap_err(); + send(client_stream.get_mut(), server_stream.get_mut()); + // HelloRetryRequest + assert!(!server_stream.stateless().unwrap()); + send(server_stream.get_mut(), client_stream.get_mut()); + // Second ClientHello + client_stream.do_handshake().unwrap_err(); + send(client_stream.get_mut(), server_stream.get_mut()); + // OldServerHello + assert!(server_stream.stateless().unwrap()); + server_stream.accept().unwrap_err(); + send(server_stream.get_mut(), client_stream.get_mut()); + // Finished + client_stream.do_handshake().unwrap(); + send(client_stream.get_mut(), server_stream.get_mut()); + server_stream.do_handshake().unwrap(); +} + +#[cfg(not(osslconf = "OPENSSL_NO_PSK"))] +#[test] +fn psk_ciphers() { + const CIPHER: &str = "PSK-AES256-CBC-SHA"; + const PSK: &[u8] = b"thisisaverysecurekey"; + const CLIENT_IDENT: &[u8] = b"thisisaclient"; + static CLIENT_CALLED: AtomicBool = AtomicBool::new(false); + static SERVER_CALLED: AtomicBool = AtomicBool::new(false); + + let mut server = Server::builder(); + server.ctx().set_cipher_list(CIPHER).unwrap(); + server.ctx().set_psk_server_callback(|_, identity, psk| { + assert!(identity.unwrap_or(&[]) == CLIENT_IDENT); + psk[..PSK.len()].copy_from_slice(PSK); + SERVER_CALLED.store(true, Ordering::SeqCst); + Ok(PSK.len()) + }); + + let server = server.build(); + + let mut client = server.client(); + // This test relies on TLS 1.2 suites + #[cfg(any(boringssl, ossl111))] + client.ctx().set_options(super::SslOptions::NO_TLSV1_3); + client.ctx().set_cipher_list(CIPHER).unwrap(); + client + .ctx() + .set_psk_client_callback(move |_, _, identity, psk| { + identity[..CLIENT_IDENT.len()].copy_from_slice(CLIENT_IDENT); + identity[CLIENT_IDENT.len()] = 0; + psk[..PSK.len()].copy_from_slice(PSK); + CLIENT_CALLED.store(true, Ordering::SeqCst); + Ok(PSK.len()) + }); + + client.connect(); + + assert!(SERVER_CALLED.load(Ordering::SeqCst)); + assert!(CLIENT_CALLED.load(Ordering::SeqCst)); +} + +#[test] +fn sni_callback_swapped_ctx() { + static CALLED_BACK: AtomicBool = AtomicBool::new(false); + + let mut server = Server::builder(); + + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_servername_callback(|_, _| { + CALLED_BACK.store(true, Ordering::SeqCst); + Ok(()) + }); + + let keyed_ctx = mem::replace(server.ctx(), ctx).build(); + server.ssl_cb(move |ssl| ssl.set_ssl_context(&keyed_ctx).unwrap()); + + let server = server.build(); + + server.client().connect(); + + assert!(CALLED_BACK.load(Ordering::SeqCst)); +} + +#[test] +#[cfg(ossl111)] +fn client_hello() { + static CALLED_BACK: AtomicBool = AtomicBool::new(false); + + let mut server = Server::builder(); + server.ctx().set_client_hello_callback(|ssl, _| { + assert!(!ssl.client_hello_isv2()); + assert_eq!(ssl.client_hello_legacy_version(), Some(SslVersion::TLS1_2)); + assert!(ssl.client_hello_random().is_some()); + assert!(ssl.client_hello_session_id().is_some()); + assert!(ssl.client_hello_ciphers().is_some()); + assert!(ssl.client_hello_compression_methods().is_some()); + assert!(ssl + .bytes_to_cipher_list(ssl.client_hello_ciphers().unwrap(), ssl.client_hello_isv2()) + .is_ok()); + + CALLED_BACK.store(true, Ordering::SeqCst); + Ok(ClientHelloResponse::SUCCESS) + }); + + let server = server.build(); + server.client().connect(); + + assert!(CALLED_BACK.load(Ordering::SeqCst)); +} + +#[test] +#[cfg(ossl111)] +fn openssl_cipher_name() { + assert_eq!( + super::cipher_name("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384"), + "ECDHE-RSA-AES256-SHA384", + ); + + assert_eq!(super::cipher_name("asdf"), "(NONE)"); +} + +#[test] +fn session_cache_size() { + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_session_cache_size(1234); + let ctx = ctx.build(); + assert_eq!(ctx.session_cache_size(), 1234); +} + +#[test] +#[cfg(ossl102)] +fn add_chain_cert() { + let ctx = SslContext::builder(SslMethod::tls()).unwrap().build(); + let cert = X509::from_pem(CERT).unwrap(); + let mut ssl = Ssl::new(&ctx).unwrap(); + assert!(ssl.add_chain_cert(cert).is_ok()); +} +#[test] +#[cfg(ossl111)] +fn set_ssl_certificate_key_related_api() { + let cert_str: &str = include_str!("../../../test/cert.pem"); + let key_str: &str = include_str!("../../../test/key.pem"); + let ctx = SslContext::builder(SslMethod::tls()).unwrap().build(); + let cert_x509 = X509::from_pem(CERT).unwrap(); + let mut ssl = Ssl::new(&ctx).unwrap(); + assert!(ssl.set_method(SslMethod::tls()).is_ok()); + ssl.set_private_key_file("test/key.pem", SslFiletype::PEM) + .unwrap(); + { + let pkey = String::from_utf8( + ssl.private_key() + .unwrap() + .private_key_to_pem_pkcs8() + .unwrap(), + ) + .unwrap(); + assert!(pkey.lines().eq(key_str.lines())); + } + let pkey = PKey::private_key_from_pem(KEY).unwrap(); + ssl.set_private_key(pkey.as_ref()).unwrap(); + { + let pkey = String::from_utf8( + ssl.private_key() + .unwrap() + .private_key_to_pem_pkcs8() + .unwrap(), + ) + .unwrap(); + assert!(pkey.lines().eq(key_str.lines())); + } + ssl.set_certificate(cert_x509.as_ref()).unwrap(); + let cert = String::from_utf8(ssl.certificate().unwrap().to_pem().unwrap()).unwrap(); + assert!(cert.lines().eq(cert_str.lines())); + ssl.add_client_ca(cert_x509.as_ref()).unwrap(); + ssl.set_min_proto_version(Some(SslVersion::TLS1_2)).unwrap(); + ssl.set_max_proto_version(Some(SslVersion::TLS1_3)).unwrap(); + ssl.set_cipher_list("HIGH:!aNULL:!MD5").unwrap(); + ssl.set_ciphersuites("TLS_AES_128_GCM_SHA256").unwrap(); + let x509 = X509::from_pem(ROOT_CERT).unwrap(); + let mut builder = X509StoreBuilder::new().unwrap(); + builder.add_cert(x509).unwrap(); + let store = builder.build(); + ssl.set_verify_cert_store(store).unwrap(); +} + +#[test] +#[cfg(ossl110)] +fn test_ssl_set_cert_chain_file() { + let ctx = SslContext::builder(SslMethod::tls()).unwrap().build(); + let mut ssl = Ssl::new(&ctx).unwrap(); + ssl.set_certificate_chain_file("test/cert.pem").unwrap(); +} + +#[test] +#[cfg(ossl111)] +fn set_num_tickets() { + let mut ctx = SslContext::builder(SslMethod::tls_server()).unwrap(); + ctx.set_num_tickets(3).unwrap(); + let ctx = ctx.build(); + assert_eq!(3, ctx.num_tickets()); + + let mut ssl = Ssl::new(&ctx).unwrap(); + ssl.set_num_tickets(5).unwrap(); + let ssl = ssl; + assert_eq!(5, ssl.num_tickets()); +} diff --git a/vendor/openssl/src/ssl/test/server.rs b/vendor/openssl/src/ssl/test/server.rs new file mode 100644 index 0000000..41677e5 --- /dev/null +++ b/vendor/openssl/src/ssl/test/server.rs @@ -0,0 +1,167 @@ +use std::io::{Read, Write}; +use std::net::{SocketAddr, TcpListener, TcpStream}; +use std::thread::{self, JoinHandle}; + +use crate::ssl::{Ssl, SslContext, SslContextBuilder, SslFiletype, SslMethod, SslRef, SslStream}; + +pub struct Server { + handle: Option<JoinHandle<()>>, + addr: SocketAddr, +} + +impl Drop for Server { + fn drop(&mut self) { + if !thread::panicking() { + self.handle.take().unwrap().join().unwrap(); + } + } +} + +impl Server { + pub fn builder() -> Builder { + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_certificate_chain_file("test/cert.pem").unwrap(); + ctx.set_private_key_file("test/key.pem", SslFiletype::PEM) + .unwrap(); + + Builder { + ctx, + ssl_cb: Box::new(|_| {}), + io_cb: Box::new(|_| {}), + should_error: false, + } + } + + pub fn client(&self) -> ClientBuilder { + ClientBuilder { + ctx: SslContext::builder(SslMethod::tls()).unwrap(), + addr: self.addr, + } + } + + pub fn connect_tcp(&self) -> TcpStream { + TcpStream::connect(self.addr).unwrap() + } +} + +pub struct Builder { + ctx: SslContextBuilder, + ssl_cb: Box<dyn FnMut(&mut SslRef) + Send>, + io_cb: Box<dyn FnMut(SslStream<TcpStream>) + Send>, + should_error: bool, +} + +impl Builder { + pub fn ctx(&mut self) -> &mut SslContextBuilder { + &mut self.ctx + } + + pub fn ssl_cb<F>(&mut self, cb: F) + where + F: 'static + FnMut(&mut SslRef) + Send, + { + self.ssl_cb = Box::new(cb); + } + + pub fn io_cb<F>(&mut self, cb: F) + where + F: 'static + FnMut(SslStream<TcpStream>) + Send, + { + self.io_cb = Box::new(cb); + } + + pub fn should_error(&mut self) { + self.should_error = true; + } + + pub fn build(self) -> Server { + let ctx = self.ctx.build(); + let socket = TcpListener::bind("127.0.0.1:0").unwrap(); + let addr = socket.local_addr().unwrap(); + let mut ssl_cb = self.ssl_cb; + let mut io_cb = self.io_cb; + let should_error = self.should_error; + + let handle = thread::spawn(move || { + let socket = socket.accept().unwrap().0; + let mut ssl = Ssl::new(&ctx).unwrap(); + ssl_cb(&mut ssl); + let r = ssl.accept(socket); + if should_error { + r.unwrap_err(); + } else { + let mut socket = r.unwrap(); + socket.write_all(&[0]).unwrap(); + io_cb(socket); + } + }); + + Server { + handle: Some(handle), + addr, + } + } +} + +pub struct ClientBuilder { + ctx: SslContextBuilder, + addr: SocketAddr, +} + +impl ClientBuilder { + pub fn ctx(&mut self) -> &mut SslContextBuilder { + &mut self.ctx + } + + pub fn build(self) -> Client { + Client { + ctx: self.ctx.build(), + addr: self.addr, + } + } + + pub fn connect(self) -> SslStream<TcpStream> { + self.build().builder().connect() + } + + pub fn connect_err(self) { + self.build().builder().connect_err(); + } +} + +pub struct Client { + ctx: SslContext, + addr: SocketAddr, +} + +impl Client { + pub fn builder(&self) -> ClientSslBuilder { + ClientSslBuilder { + ssl: Ssl::new(&self.ctx).unwrap(), + addr: self.addr, + } + } +} + +pub struct ClientSslBuilder { + ssl: Ssl, + addr: SocketAddr, +} + +impl ClientSslBuilder { + pub fn ssl(&mut self) -> &mut SslRef { + &mut self.ssl + } + + pub fn connect(self) -> SslStream<TcpStream> { + let socket = TcpStream::connect(self.addr).unwrap(); + let mut s = self.ssl.connect(socket).unwrap(); + s.read_exact(&mut [0]).unwrap(); + s + } + + pub fn connect_err(self) { + let socket = TcpStream::connect(self.addr).unwrap(); + self.ssl.connect(socket).unwrap_err(); + } +} diff --git a/vendor/openssl/src/stack.rs b/vendor/openssl/src/stack.rs new file mode 100644 index 0000000..416efd5 --- /dev/null +++ b/vendor/openssl/src/stack.rs @@ -0,0 +1,380 @@ +use cfg_if::cfg_if; +use foreign_types::{ForeignType, ForeignTypeRef, Opaque}; +use libc::c_int; +use std::borrow::Borrow; +use std::convert::AsRef; +use std::fmt; +use std::iter; +use std::marker::PhantomData; +use std::mem; +use std::ops::{Deref, DerefMut, Index, IndexMut, Range}; + +use crate::error::ErrorStack; +use crate::util::ForeignTypeExt; +use crate::{cvt, cvt_p, LenType}; + +cfg_if! { + if #[cfg(ossl110)] { + use ffi::{ + OPENSSL_sk_pop, OPENSSL_sk_free, OPENSSL_sk_num, OPENSSL_sk_value, OPENSSL_STACK, + OPENSSL_sk_new_null, OPENSSL_sk_push, + }; + } else { + use ffi::{ + sk_pop as OPENSSL_sk_pop, sk_free as OPENSSL_sk_free, sk_num as OPENSSL_sk_num, + sk_value as OPENSSL_sk_value, _STACK as OPENSSL_STACK, + sk_new_null as OPENSSL_sk_new_null, sk_push as OPENSSL_sk_push, + }; + } +} + +/// Trait implemented by types which can be placed in a stack. +/// +/// It should not be implemented for any type outside of this crate. +pub trait Stackable: ForeignType { + /// The C stack type for this element. + /// + /// Generally called `stack_st_{ELEMENT_TYPE}`, normally hidden by the + /// `STACK_OF(ELEMENT_TYPE)` macro in the OpenSSL API. + type StackType; +} + +/// An owned stack of `T`. +pub struct Stack<T: Stackable>(*mut T::StackType); + +unsafe impl<T: Stackable + Send> Send for Stack<T> {} +unsafe impl<T: Stackable + Sync> Sync for Stack<T> {} + +impl<T> fmt::Debug for Stack<T> +where + T: Stackable, + T::Ref: fmt::Debug, +{ + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_list().entries(self).finish() + } +} +impl<T: Stackable> Drop for Stack<T> { + fn drop(&mut self) { + unsafe { + while self.pop().is_some() {} + OPENSSL_sk_free(self.0 as *mut _); + } + } +} + +impl<T: Stackable> Stack<T> { + pub fn new() -> Result<Stack<T>, ErrorStack> { + unsafe { + ffi::init(); + let ptr = cvt_p(OPENSSL_sk_new_null())?; + Ok(Stack(ptr as *mut _)) + } + } +} + +impl<T: Stackable> iter::IntoIterator for Stack<T> { + type IntoIter = IntoIter<T>; + type Item = T; + + fn into_iter(self) -> IntoIter<T> { + let it = IntoIter { + stack: self.0, + idxs: 0..self.len() as LenType, + }; + mem::forget(self); + it + } +} + +impl<T: Stackable> AsRef<StackRef<T>> for Stack<T> { + fn as_ref(&self) -> &StackRef<T> { + self + } +} + +impl<T: Stackable> Borrow<StackRef<T>> for Stack<T> { + fn borrow(&self) -> &StackRef<T> { + self + } +} + +impl<T: Stackable> ForeignType for Stack<T> { + type CType = T::StackType; + type Ref = StackRef<T>; + + #[inline] + unsafe fn from_ptr(ptr: *mut T::StackType) -> Stack<T> { + assert!( + !ptr.is_null(), + "Must not instantiate a Stack from a null-ptr - use Stack::new() in \ + that case" + ); + Stack(ptr) + } + + #[inline] + fn as_ptr(&self) -> *mut T::StackType { + self.0 + } +} + +impl<T: Stackable> Deref for Stack<T> { + type Target = StackRef<T>; + + fn deref(&self) -> &StackRef<T> { + unsafe { StackRef::from_ptr(self.0) } + } +} + +impl<T: Stackable> DerefMut for Stack<T> { + fn deref_mut(&mut self) -> &mut StackRef<T> { + unsafe { StackRef::from_ptr_mut(self.0) } + } +} + +pub struct IntoIter<T: Stackable> { + stack: *mut T::StackType, + idxs: Range<LenType>, +} + +impl<T: Stackable> Drop for IntoIter<T> { + fn drop(&mut self) { + unsafe { + // https://github.com/rust-lang/rust-clippy/issues/7510 + #[allow(clippy::while_let_on_iterator)] + while let Some(_) = self.next() {} + OPENSSL_sk_free(self.stack as *mut _); + } + } +} + +impl<T: Stackable> Iterator for IntoIter<T> { + type Item = T; + + fn next(&mut self) -> Option<T> { + unsafe { + self.idxs + .next() + .map(|i| T::from_ptr(OPENSSL_sk_value(self.stack as *mut _, i) as *mut _)) + } + } + + fn size_hint(&self) -> (usize, Option<usize>) { + self.idxs.size_hint() + } +} + +impl<T: Stackable> DoubleEndedIterator for IntoIter<T> { + fn next_back(&mut self) -> Option<T> { + unsafe { + self.idxs + .next_back() + .map(|i| T::from_ptr(OPENSSL_sk_value(self.stack as *mut _, i) as *mut _)) + } + } +} + +impl<T: Stackable> ExactSizeIterator for IntoIter<T> {} + +pub struct StackRef<T: Stackable>(Opaque, PhantomData<T>); + +unsafe impl<T: Stackable + Send> Send for StackRef<T> {} +unsafe impl<T: Stackable + Sync> Sync for StackRef<T> {} + +impl<T: Stackable> ForeignTypeRef for StackRef<T> { + type CType = T::StackType; +} + +impl<T: Stackable> StackRef<T> { + fn as_stack(&self) -> *mut OPENSSL_STACK { + self.as_ptr() as *mut _ + } + + /// Returns the number of items in the stack. + pub fn len(&self) -> usize { + unsafe { OPENSSL_sk_num(self.as_stack()) as usize } + } + + /// Determines if the stack is empty. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + pub fn iter(&self) -> Iter<'_, T> { + Iter { + stack: self, + idxs: 0..self.len() as LenType, + } + } + + pub fn iter_mut(&mut self) -> IterMut<'_, T> { + IterMut { + idxs: 0..self.len() as LenType, + stack: self, + } + } + + /// Returns a reference to the element at the given index in the + /// stack or `None` if the index is out of bounds + pub fn get(&self, idx: usize) -> Option<&T::Ref> { + unsafe { + if idx >= self.len() { + return None; + } + + Some(T::Ref::from_ptr(self._get(idx))) + } + } + + /// Returns a mutable reference to the element at the given index in the + /// stack or `None` if the index is out of bounds + pub fn get_mut(&mut self, idx: usize) -> Option<&mut T::Ref> { + unsafe { + if idx >= self.len() { + return None; + } + + Some(T::Ref::from_ptr_mut(self._get(idx))) + } + } + + /// Pushes a value onto the top of the stack. + pub fn push(&mut self, data: T) -> Result<(), ErrorStack> { + unsafe { + cvt(OPENSSL_sk_push(self.as_stack(), data.as_ptr() as *mut _) as c_int)?; + mem::forget(data); + Ok(()) + } + } + + /// Removes the last element from the stack and returns it. + pub fn pop(&mut self) -> Option<T> { + unsafe { + let ptr = OPENSSL_sk_pop(self.as_stack()); + T::from_ptr_opt(ptr as *mut _) + } + } + + unsafe fn _get(&self, idx: usize) -> *mut T::CType { + OPENSSL_sk_value(self.as_stack(), idx as LenType) as *mut _ + } +} + +impl<T: Stackable> Index<usize> for StackRef<T> { + type Output = T::Ref; + + fn index(&self, index: usize) -> &T::Ref { + self.get(index).unwrap() + } +} + +impl<T: Stackable> IndexMut<usize> for StackRef<T> { + fn index_mut(&mut self, index: usize) -> &mut T::Ref { + self.get_mut(index).unwrap() + } +} + +impl<'a, T: Stackable> iter::IntoIterator for &'a StackRef<T> { + type Item = &'a T::Ref; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Iter<'a, T> { + self.iter() + } +} + +impl<'a, T: Stackable> iter::IntoIterator for &'a mut StackRef<T> { + type Item = &'a mut T::Ref; + type IntoIter = IterMut<'a, T>; + + fn into_iter(self) -> IterMut<'a, T> { + self.iter_mut() + } +} + +impl<'a, T: Stackable> iter::IntoIterator for &'a Stack<T> { + type Item = &'a T::Ref; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Iter<'a, T> { + self.iter() + } +} + +impl<'a, T: Stackable> iter::IntoIterator for &'a mut Stack<T> { + type Item = &'a mut T::Ref; + type IntoIter = IterMut<'a, T>; + + fn into_iter(self) -> IterMut<'a, T> { + self.iter_mut() + } +} + +/// An iterator over the stack's contents. +pub struct Iter<'a, T: Stackable> { + stack: &'a StackRef<T>, + idxs: Range<LenType>, +} + +impl<'a, T: Stackable> Iterator for Iter<'a, T> { + type Item = &'a T::Ref; + + fn next(&mut self) -> Option<&'a T::Ref> { + unsafe { + self.idxs + .next() + .map(|i| T::Ref::from_ptr(OPENSSL_sk_value(self.stack.as_stack(), i) as *mut _)) + } + } + + fn size_hint(&self) -> (usize, Option<usize>) { + self.idxs.size_hint() + } +} + +impl<'a, T: Stackable> DoubleEndedIterator for Iter<'a, T> { + fn next_back(&mut self) -> Option<&'a T::Ref> { + unsafe { + self.idxs + .next_back() + .map(|i| T::Ref::from_ptr(OPENSSL_sk_value(self.stack.as_stack(), i) as *mut _)) + } + } +} + +impl<'a, T: Stackable> ExactSizeIterator for Iter<'a, T> {} + +/// A mutable iterator over the stack's contents. +pub struct IterMut<'a, T: Stackable> { + stack: &'a mut StackRef<T>, + idxs: Range<LenType>, +} + +impl<'a, T: Stackable> Iterator for IterMut<'a, T> { + type Item = &'a mut T::Ref; + + fn next(&mut self) -> Option<&'a mut T::Ref> { + unsafe { + self.idxs + .next() + .map(|i| T::Ref::from_ptr_mut(OPENSSL_sk_value(self.stack.as_stack(), i) as *mut _)) + } + } + + fn size_hint(&self) -> (usize, Option<usize>) { + self.idxs.size_hint() + } +} + +impl<'a, T: Stackable> DoubleEndedIterator for IterMut<'a, T> { + fn next_back(&mut self) -> Option<&'a mut T::Ref> { + unsafe { + self.idxs + .next_back() + .map(|i| T::Ref::from_ptr_mut(OPENSSL_sk_value(self.stack.as_stack(), i) as *mut _)) + } + } +} + +impl<'a, T: Stackable> ExactSizeIterator for IterMut<'a, T> {} diff --git a/vendor/openssl/src/string.rs b/vendor/openssl/src/string.rs new file mode 100644 index 0000000..95494b5 --- /dev/null +++ b/vendor/openssl/src/string.rs @@ -0,0 +1,96 @@ +use foreign_types::ForeignTypeRef; +use libc::{c_char, c_void}; +use std::convert::AsRef; +use std::ffi::CStr; +use std::fmt; +use std::ops::Deref; +use std::str; + +use crate::stack::Stackable; + +foreign_type_and_impl_send_sync! { + type CType = c_char; + fn drop = free; + + pub struct OpensslString; + pub struct OpensslStringRef; +} + +impl fmt::Display for OpensslString { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&**self, f) + } +} + +impl fmt::Debug for OpensslString { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +impl Stackable for OpensslString { + type StackType = ffi::stack_st_OPENSSL_STRING; +} + +impl AsRef<str> for OpensslString { + fn as_ref(&self) -> &str { + self + } +} + +impl AsRef<[u8]> for OpensslString { + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + +impl Deref for OpensslStringRef { + type Target = str; + + fn deref(&self) -> &str { + unsafe { + let slice = CStr::from_ptr(self.as_ptr()).to_bytes(); + str::from_utf8_unchecked(slice) + } + } +} + +impl AsRef<str> for OpensslStringRef { + fn as_ref(&self) -> &str { + self + } +} + +impl AsRef<[u8]> for OpensslStringRef { + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + +impl fmt::Display for OpensslStringRef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&**self, f) + } +} + +impl fmt::Debug for OpensslStringRef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +#[inline] +#[cfg(not(boringssl))] +unsafe fn free(buf: *mut c_char) { + ffi::OPENSSL_free(buf as *mut c_void); +} + +#[inline] +#[cfg(boringssl)] +unsafe fn free(buf: *mut c_char) { + ffi::CRYPTO_free( + buf as *mut c_void, + concat!(file!(), "\0").as_ptr() as *const c_char, + line!() as ::libc::c_int, + ); +} diff --git a/vendor/openssl/src/symm.rs b/vendor/openssl/src/symm.rs new file mode 100644 index 0000000..7ebb703 --- /dev/null +++ b/vendor/openssl/src/symm.rs @@ -0,0 +1,1633 @@ +//! High level interface to certain symmetric ciphers. +//! +//! # Examples +//! +//! Encrypt data in AES128 CBC mode +//! +//! ``` +//! use openssl::symm::{encrypt, Cipher}; +//! +//! let cipher = Cipher::aes_128_cbc(); +//! let data = b"Some Crypto Text"; +//! let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"; +//! let iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07"; +//! let ciphertext = encrypt( +//! cipher, +//! key, +//! Some(iv), +//! data).unwrap(); +//! +//! assert_eq!( +//! b"\xB4\xB9\xE7\x30\xD6\xD6\xF7\xDE\x77\x3F\x1C\xFF\xB3\x3E\x44\x5A\x91\xD7\x27\x62\x87\x4D\ +//! \xFB\x3C\x5E\xC4\x59\x72\x4A\xF4\x7C\xA1", +//! &ciphertext[..]); +//! ``` +//! +//! Encrypting an asymmetric key with a symmetric cipher +//! +//! ``` +//! use openssl::rsa::{Padding, Rsa}; +//! use openssl::symm::Cipher; +//! +//! // Generate keypair and encrypt private key: +//! let keypair = Rsa::generate(2048).unwrap(); +//! let cipher = Cipher::aes_256_cbc(); +//! let pubkey_pem = keypair.public_key_to_pem_pkcs1().unwrap(); +//! let privkey_pem = keypair.private_key_to_pem_passphrase(cipher, b"Rust").unwrap(); +//! // pubkey_pem and privkey_pem could be written to file here. +//! +//! // Load private and public key from string: +//! let pubkey = Rsa::public_key_from_pem_pkcs1(&pubkey_pem).unwrap(); +//! let privkey = Rsa::private_key_from_pem_passphrase(&privkey_pem, b"Rust").unwrap(); +//! +//! // Use the asymmetric keys to encrypt and decrypt a short message: +//! let msg = b"Foo bar"; +//! let mut encrypted = vec![0; pubkey.size() as usize]; +//! let mut decrypted = vec![0; privkey.size() as usize]; +//! let len = pubkey.public_encrypt(msg, &mut encrypted, Padding::PKCS1).unwrap(); +//! assert!(len > msg.len()); +//! let len = privkey.private_decrypt(&encrypted, &mut decrypted, Padding::PKCS1).unwrap(); +//! let output_string = String::from_utf8(decrypted[..len].to_vec()).unwrap(); +//! assert_eq!("Foo bar", output_string); +//! println!("Decrypted: '{}'", output_string); +//! ``` +use crate::cipher::CipherRef; +use crate::cipher_ctx::{CipherCtx, CipherCtxRef}; +use crate::error::ErrorStack; +use crate::nid::Nid; +use cfg_if::cfg_if; +use foreign_types::ForeignTypeRef; + +#[derive(Copy, Clone)] +pub enum Mode { + Encrypt, + Decrypt, +} + +/// Represents a particular cipher algorithm. +/// +/// See OpenSSL doc at [`EVP_EncryptInit`] for more information on each algorithms. +/// +/// [`EVP_EncryptInit`]: https://www.openssl.org/docs/manmaster/crypto/EVP_EncryptInit.html +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct Cipher(*const ffi::EVP_CIPHER); + +impl Cipher { + /// Looks up the cipher for a certain nid. + /// + /// This corresponds to [`EVP_get_cipherbynid`] + /// + /// [`EVP_get_cipherbynid`]: https://www.openssl.org/docs/manmaster/crypto/EVP_get_cipherbyname.html + pub fn from_nid(nid: Nid) -> Option<Cipher> { + let ptr = unsafe { ffi::EVP_get_cipherbyname(ffi::OBJ_nid2sn(nid.as_raw())) }; + if ptr.is_null() { + None + } else { + Some(Cipher(ptr)) + } + } + + /// Returns the cipher's Nid. + /// + /// This corresponds to [`EVP_CIPHER_nid`] + /// + /// [`EVP_CIPHER_nid`]: https://www.openssl.org/docs/manmaster/crypto/EVP_CIPHER_nid.html + pub fn nid(&self) -> Nid { + let nid = unsafe { ffi::EVP_CIPHER_nid(self.0) }; + Nid::from_raw(nid) + } + + pub fn aes_128_ecb() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_128_ecb()) } + } + + pub fn aes_128_cbc() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_128_cbc()) } + } + + #[cfg(not(boringssl))] + pub fn aes_128_xts() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_128_xts()) } + } + + pub fn aes_128_ctr() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_128_ctr()) } + } + + #[cfg(not(boringssl))] + pub fn aes_128_cfb1() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_128_cfb1()) } + } + + pub fn aes_128_cfb128() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_128_cfb128()) } + } + + #[cfg(not(boringssl))] + pub fn aes_128_cfb8() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_128_cfb8()) } + } + + pub fn aes_128_gcm() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_128_gcm()) } + } + + #[cfg(not(boringssl))] + pub fn aes_128_ccm() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_128_ccm()) } + } + + pub fn aes_128_ofb() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_128_ofb()) } + } + + /// Requires OpenSSL 1.1.0 or newer. + #[cfg(all(ossl110, not(osslconf = "OPENSSL_NO_OCB")))] + pub fn aes_128_ocb() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_128_ocb()) } + } + + pub fn aes_192_ecb() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_192_ecb()) } + } + + pub fn aes_192_cbc() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_192_cbc()) } + } + + pub fn aes_192_ctr() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_192_ctr()) } + } + + #[cfg(not(boringssl))] + pub fn aes_192_cfb1() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_192_cfb1()) } + } + + pub fn aes_192_cfb128() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_192_cfb128()) } + } + + #[cfg(not(boringssl))] + pub fn aes_192_cfb8() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_192_cfb8()) } + } + + pub fn aes_192_gcm() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_192_gcm()) } + } + + #[cfg(not(boringssl))] + pub fn aes_192_ccm() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_192_ccm()) } + } + + pub fn aes_192_ofb() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_192_ofb()) } + } + + /// Requires OpenSSL 1.1.0 or newer. + #[cfg(all(ossl110, not(osslconf = "OPENSSL_NO_OCB")))] + pub fn aes_192_ocb() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_192_ocb()) } + } + + pub fn aes_256_ecb() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_256_ecb()) } + } + + pub fn aes_256_cbc() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_256_cbc()) } + } + + #[cfg(not(boringssl))] + pub fn aes_256_xts() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_256_xts()) } + } + + pub fn aes_256_ctr() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_256_ctr()) } + } + + #[cfg(not(boringssl))] + pub fn aes_256_cfb1() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_256_cfb1()) } + } + + pub fn aes_256_cfb128() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_256_cfb128()) } + } + + #[cfg(not(boringssl))] + pub fn aes_256_cfb8() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_256_cfb8()) } + } + + pub fn aes_256_gcm() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_256_gcm()) } + } + + #[cfg(not(boringssl))] + pub fn aes_256_ccm() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_256_ccm()) } + } + + pub fn aes_256_ofb() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_256_ofb()) } + } + + /// Requires OpenSSL 1.1.0 or newer. + #[cfg(all(ossl110, not(osslconf = "OPENSSL_NO_OCB")))] + pub fn aes_256_ocb() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_256_ocb()) } + } + + #[cfg(not(osslconf = "OPENSSL_NO_BF"))] + pub fn bf_cbc() -> Cipher { + unsafe { Cipher(ffi::EVP_bf_cbc()) } + } + + #[cfg(not(osslconf = "OPENSSL_NO_BF"))] + pub fn bf_ecb() -> Cipher { + unsafe { Cipher(ffi::EVP_bf_ecb()) } + } + + #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_BF")))] + pub fn bf_cfb64() -> Cipher { + unsafe { Cipher(ffi::EVP_bf_cfb64()) } + } + + #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_BF")))] + pub fn bf_ofb() -> Cipher { + unsafe { Cipher(ffi::EVP_bf_ofb()) } + } + + pub fn des_cbc() -> Cipher { + unsafe { Cipher(ffi::EVP_des_cbc()) } + } + + pub fn des_ecb() -> Cipher { + unsafe { Cipher(ffi::EVP_des_ecb()) } + } + + pub fn des_ede3() -> Cipher { + unsafe { Cipher(ffi::EVP_des_ede3()) } + } + + pub fn des_ede3_cbc() -> Cipher { + unsafe { Cipher(ffi::EVP_des_ede3_cbc()) } + } + + #[cfg(not(boringssl))] + pub fn des_ede3_cfb64() -> Cipher { + unsafe { Cipher(ffi::EVP_des_ede3_cfb64()) } + } + + #[cfg(not(osslconf = "OPENSSL_NO_RC4"))] + pub fn rc4() -> Cipher { + unsafe { Cipher(ffi::EVP_rc4()) } + } + + /// Requires OpenSSL 1.1.0 or newer. + #[cfg(all(ossl110, not(osslconf = "OPENSSL_NO_CHACHA")))] + pub fn chacha20() -> Cipher { + unsafe { Cipher(ffi::EVP_chacha20()) } + } + + /// Requires OpenSSL 1.1.0 or newer. + #[cfg(all(any(ossl110, libressl360), not(osslconf = "OPENSSL_NO_CHACHA")))] + pub fn chacha20_poly1305() -> Cipher { + unsafe { Cipher(ffi::EVP_chacha20_poly1305()) } + } + + #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_SEED")))] + pub fn seed_cbc() -> Cipher { + unsafe { Cipher(ffi::EVP_seed_cbc()) } + } + + #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_SEED")))] + pub fn seed_cfb128() -> Cipher { + unsafe { Cipher(ffi::EVP_seed_cfb128()) } + } + + #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_SEED")))] + pub fn seed_ecb() -> Cipher { + unsafe { Cipher(ffi::EVP_seed_ecb()) } + } + + #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_SEED")))] + pub fn seed_ofb() -> Cipher { + unsafe { Cipher(ffi::EVP_seed_ofb()) } + } + + #[cfg(all(any(ossl111, libressl291), not(osslconf = "OPENSSL_NO_SM4")))] + pub fn sm4_ecb() -> Cipher { + unsafe { Cipher(ffi::EVP_sm4_ecb()) } + } + + #[cfg(all(any(ossl111, libressl291), not(osslconf = "OPENSSL_NO_SM4")))] + pub fn sm4_cbc() -> Cipher { + unsafe { Cipher(ffi::EVP_sm4_cbc()) } + } + + #[cfg(all(any(ossl111, libressl291), not(osslconf = "OPENSSL_NO_SM4")))] + pub fn sm4_ctr() -> Cipher { + unsafe { Cipher(ffi::EVP_sm4_ctr()) } + } + + #[cfg(all(any(ossl111, libressl291), not(osslconf = "OPENSSL_NO_SM4")))] + pub fn sm4_cfb128() -> Cipher { + unsafe { Cipher(ffi::EVP_sm4_cfb128()) } + } + + #[cfg(all(any(ossl111, libressl291), not(osslconf = "OPENSSL_NO_SM4")))] + pub fn sm4_ofb() -> Cipher { + unsafe { Cipher(ffi::EVP_sm4_ofb()) } + } + + /// Creates a `Cipher` from a raw pointer to its OpenSSL type. + /// + /// # Safety + /// + /// The caller must ensure the pointer is valid for the `'static` lifetime. + pub unsafe fn from_ptr(ptr: *const ffi::EVP_CIPHER) -> Cipher { + Cipher(ptr) + } + + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn as_ptr(&self) -> *const ffi::EVP_CIPHER { + self.0 + } + + /// Returns the length of keys used with this cipher. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn key_len(&self) -> usize { + unsafe { EVP_CIPHER_key_length(self.0) as usize } + } + + /// Returns the length of the IV used with this cipher, or `None` if the + /// cipher does not use an IV. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn iv_len(&self) -> Option<usize> { + unsafe { + let len = EVP_CIPHER_iv_length(self.0) as usize; + if len == 0 { + None + } else { + Some(len) + } + } + } + + /// Returns the block size of the cipher. + /// + /// # Note + /// + /// Stream ciphers such as RC4 have a block size of 1. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn block_size(&self) -> usize { + unsafe { EVP_CIPHER_block_size(self.0) as usize } + } + + /// Determines whether the cipher is using CCM mode + #[cfg(not(boringssl))] + fn is_ccm(self) -> bool { + // NOTE: OpenSSL returns pointers to static structs, which makes this work as expected + self == Cipher::aes_128_ccm() || self == Cipher::aes_256_ccm() + } + + #[cfg(boringssl)] + fn is_ccm(self) -> bool { + false + } + + /// Determines whether the cipher is using OCB mode + #[cfg(all(ossl110, not(osslconf = "OPENSSL_NO_OCB")))] + fn is_ocb(self) -> bool { + self == Cipher::aes_128_ocb() + || self == Cipher::aes_192_ocb() + || self == Cipher::aes_256_ocb() + } + + #[cfg(any(not(ossl110), osslconf = "OPENSSL_NO_OCB"))] + const fn is_ocb(self) -> bool { + false + } +} + +unsafe impl Sync for Cipher {} +unsafe impl Send for Cipher {} + +/// Represents a symmetric cipher context. +/// +/// Padding is enabled by default. +/// +/// # Examples +/// +/// Encrypt some plaintext in chunks, then decrypt the ciphertext back into plaintext, in AES 128 +/// CBC mode. +/// +/// ``` +/// use openssl::symm::{Cipher, Mode, Crypter}; +/// +/// let plaintexts: [&[u8]; 2] = [b"Some Stream of", b" Crypto Text"]; +/// let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"; +/// let iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07"; +/// let data_len = plaintexts.iter().fold(0, |sum, x| sum + x.len()); +/// +/// // Create a cipher context for encryption. +/// let mut encrypter = Crypter::new( +/// Cipher::aes_128_cbc(), +/// Mode::Encrypt, +/// key, +/// Some(iv)).unwrap(); +/// +/// let block_size = Cipher::aes_128_cbc().block_size(); +/// let mut ciphertext = vec![0; data_len + block_size]; +/// +/// // Encrypt 2 chunks of plaintexts successively. +/// let mut count = encrypter.update(plaintexts[0], &mut ciphertext).unwrap(); +/// count += encrypter.update(plaintexts[1], &mut ciphertext[count..]).unwrap(); +/// count += encrypter.finalize(&mut ciphertext[count..]).unwrap(); +/// ciphertext.truncate(count); +/// +/// assert_eq!( +/// b"\x0F\x21\x83\x7E\xB2\x88\x04\xAF\xD9\xCC\xE2\x03\x49\xB4\x88\xF6\xC4\x61\x0E\x32\x1C\xF9\ +/// \x0D\x66\xB1\xE6\x2C\x77\x76\x18\x8D\x99", +/// &ciphertext[..] +/// ); +/// +/// +/// // Let's pretend we don't know the plaintext, and now decrypt the ciphertext. +/// let data_len = ciphertext.len(); +/// let ciphertexts = [&ciphertext[..9], &ciphertext[9..]]; +/// +/// // Create a cipher context for decryption. +/// let mut decrypter = Crypter::new( +/// Cipher::aes_128_cbc(), +/// Mode::Decrypt, +/// key, +/// Some(iv)).unwrap(); +/// let mut plaintext = vec![0; data_len + block_size]; +/// +/// // Decrypt 2 chunks of ciphertexts successively. +/// let mut count = decrypter.update(ciphertexts[0], &mut plaintext).unwrap(); +/// count += decrypter.update(ciphertexts[1], &mut plaintext[count..]).unwrap(); +/// count += decrypter.finalize(&mut plaintext[count..]).unwrap(); +/// plaintext.truncate(count); +/// +/// assert_eq!(b"Some Stream of Crypto Text", &plaintext[..]); +/// ``` +pub struct Crypter { + ctx: CipherCtx, +} + +impl Crypter { + /// Creates a new `Crypter`. The initialisation vector, `iv`, is not necessary for certain + /// types of `Cipher`. + /// + /// # Panics + /// + /// Panics if an IV is required by the cipher but not provided. Also make sure that the key + /// and IV size are appropriate for your cipher. + pub fn new( + t: Cipher, + mode: Mode, + key: &[u8], + iv: Option<&[u8]>, + ) -> Result<Crypter, ErrorStack> { + let mut ctx = CipherCtx::new()?; + + let f = match mode { + Mode::Encrypt => CipherCtxRef::encrypt_init, + Mode::Decrypt => CipherCtxRef::decrypt_init, + }; + + f( + &mut ctx, + Some(unsafe { CipherRef::from_ptr(t.as_ptr() as *mut _) }), + None, + None, + )?; + + ctx.set_key_length(key.len())?; + + if let (Some(iv), Some(iv_len)) = (iv, t.iv_len()) { + if iv.len() != iv_len { + ctx.set_iv_length(iv.len())?; + } + } + + f(&mut ctx, None, Some(key), iv)?; + + Ok(Crypter { ctx }) + } + + /// Enables or disables padding. + /// + /// If padding is disabled, total amount of data encrypted/decrypted must + /// be a multiple of the cipher's block size. + pub fn pad(&mut self, padding: bool) { + self.ctx.set_padding(padding) + } + + /// Sets the tag used to authenticate ciphertext in AEAD ciphers such as AES GCM. + /// + /// When decrypting cipher text using an AEAD cipher, this must be called before `finalize`. + pub fn set_tag(&mut self, tag: &[u8]) -> Result<(), ErrorStack> { + self.ctx.set_tag(tag) + } + + /// Sets the length of the authentication tag to generate in AES CCM. + /// + /// When encrypting with AES CCM, the tag length needs to be explicitly set in order + /// to use a value different than the default 12 bytes. + pub fn set_tag_len(&mut self, tag_len: usize) -> Result<(), ErrorStack> { + self.ctx.set_tag_length(tag_len) + } + + /// Feeds total plaintext length to the cipher. + /// + /// The total plaintext or ciphertext length MUST be passed to the cipher when it operates in + /// CCM mode. + pub fn set_data_len(&mut self, data_len: usize) -> Result<(), ErrorStack> { + self.ctx.set_data_len(data_len) + } + + /// Feeds Additional Authenticated Data (AAD) through the cipher. + /// + /// This can only be used with AEAD ciphers such as AES GCM. Data fed in is not encrypted, but + /// is factored into the authentication tag. It must be called before the first call to + /// `update`. + pub fn aad_update(&mut self, input: &[u8]) -> Result<(), ErrorStack> { + self.ctx.cipher_update(input, None)?; + Ok(()) + } + + /// Feeds data from `input` through the cipher, writing encrypted/decrypted + /// bytes into `output`. + /// + /// The number of bytes written to `output` is returned. Note that this may + /// not be equal to the length of `input`. + /// + /// # Panics + /// + /// Panics for stream ciphers if `output.len() < input.len()`. + /// + /// Panics for block ciphers if `output.len() < input.len() + block_size`, + /// where `block_size` is the block size of the cipher (see `Cipher::block_size`). + /// + /// Panics if `output.len() > c_int::max_value()`. + pub fn update(&mut self, input: &[u8], output: &mut [u8]) -> Result<usize, ErrorStack> { + self.ctx.cipher_update(input, Some(output)) + } + + /// Finishes the encryption/decryption process, writing any remaining data + /// to `output`. + /// + /// The number of bytes written to `output` is returned. + /// + /// `update` should not be called after this method. + /// + /// # Panics + /// + /// Panics for block ciphers if `output.len() < block_size`, + /// where `block_size` is the block size of the cipher (see `Cipher::block_size`). + pub fn finalize(&mut self, output: &mut [u8]) -> Result<usize, ErrorStack> { + self.ctx.cipher_final(output) + } + + /// Retrieves the authentication tag used to authenticate ciphertext in AEAD ciphers such + /// as AES GCM. + /// + /// When encrypting data with an AEAD cipher, this must be called after `finalize`. + /// + /// The size of the buffer indicates the required size of the tag. While some ciphers support a + /// range of tag sizes, it is recommended to pick the maximum size. For AES GCM, this is 16 + /// bytes, for example. + pub fn get_tag(&self, tag: &mut [u8]) -> Result<(), ErrorStack> { + self.ctx.tag(tag) + } +} + +/// Encrypts data in one go, and returns the encrypted data. +/// +/// Data is encrypted using the specified cipher type `t` in encrypt mode with the specified `key` +/// and initialization vector `iv`. Padding is enabled. +/// +/// This is a convenient interface to `Crypter` to encrypt all data in one go. To encrypt a stream +/// of data incrementally , use `Crypter` instead. +/// +/// # Examples +/// +/// Encrypt data in AES128 CBC mode +/// +/// ``` +/// use openssl::symm::{encrypt, Cipher}; +/// +/// let cipher = Cipher::aes_128_cbc(); +/// let data = b"Some Crypto Text"; +/// let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"; +/// let iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07"; +/// let ciphertext = encrypt( +/// cipher, +/// key, +/// Some(iv), +/// data).unwrap(); +/// +/// assert_eq!( +/// b"\xB4\xB9\xE7\x30\xD6\xD6\xF7\xDE\x77\x3F\x1C\xFF\xB3\x3E\x44\x5A\x91\xD7\x27\x62\x87\x4D\ +/// \xFB\x3C\x5E\xC4\x59\x72\x4A\xF4\x7C\xA1", +/// &ciphertext[..]); +/// ``` +pub fn encrypt( + t: Cipher, + key: &[u8], + iv: Option<&[u8]>, + data: &[u8], +) -> Result<Vec<u8>, ErrorStack> { + cipher(t, Mode::Encrypt, key, iv, data) +} + +/// Decrypts data in one go, and returns the decrypted data. +/// +/// Data is decrypted using the specified cipher type `t` in decrypt mode with the specified `key` +/// and initialization vector `iv`. Padding is enabled. +/// +/// This is a convenient interface to `Crypter` to decrypt all data in one go. To decrypt a stream +/// of data incrementally , use `Crypter` instead. +/// +/// # Examples +/// +/// Decrypt data in AES128 CBC mode +/// +/// ``` +/// use openssl::symm::{decrypt, Cipher}; +/// +/// let cipher = Cipher::aes_128_cbc(); +/// let data = b"\xB4\xB9\xE7\x30\xD6\xD6\xF7\xDE\x77\x3F\x1C\xFF\xB3\x3E\x44\x5A\x91\xD7\x27\x62\ +/// \x87\x4D\xFB\x3C\x5E\xC4\x59\x72\x4A\xF4\x7C\xA1"; +/// let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"; +/// let iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07"; +/// let ciphertext = decrypt( +/// cipher, +/// key, +/// Some(iv), +/// data).unwrap(); +/// +/// assert_eq!( +/// b"Some Crypto Text", +/// &ciphertext[..]); +/// ``` +pub fn decrypt( + t: Cipher, + key: &[u8], + iv: Option<&[u8]>, + data: &[u8], +) -> Result<Vec<u8>, ErrorStack> { + cipher(t, Mode::Decrypt, key, iv, data) +} + +fn cipher( + t: Cipher, + mode: Mode, + key: &[u8], + iv: Option<&[u8]>, + data: &[u8], +) -> Result<Vec<u8>, ErrorStack> { + let mut c = Crypter::new(t, mode, key, iv)?; + let mut out = vec![0; data.len() + t.block_size()]; + let count = c.update(data, &mut out)?; + let rest = c.finalize(&mut out[count..])?; + out.truncate(count + rest); + Ok(out) +} + +/// Like `encrypt`, but for AEAD ciphers such as AES GCM. +/// +/// Additional Authenticated Data can be provided in the `aad` field, and the authentication tag +/// will be copied into the `tag` field. +/// +/// The size of the `tag` buffer indicates the required size of the tag. While some ciphers support +/// a range of tag sizes, it is recommended to pick the maximum size. For AES GCM, this is 16 bytes, +/// for example. +pub fn encrypt_aead( + t: Cipher, + key: &[u8], + iv: Option<&[u8]>, + aad: &[u8], + data: &[u8], + tag: &mut [u8], +) -> Result<Vec<u8>, ErrorStack> { + let mut c = Crypter::new(t, Mode::Encrypt, key, iv)?; + let mut out = vec![0; data.len() + t.block_size()]; + + let is_ccm = t.is_ccm(); + if is_ccm || t.is_ocb() { + c.set_tag_len(tag.len())?; + if is_ccm { + c.set_data_len(data.len())?; + } + } + + c.aad_update(aad)?; + let count = c.update(data, &mut out)?; + let rest = c.finalize(&mut out[count..])?; + c.get_tag(tag)?; + out.truncate(count + rest); + Ok(out) +} + +/// Like `decrypt`, but for AEAD ciphers such as AES GCM. +/// +/// Additional Authenticated Data can be provided in the `aad` field, and the authentication tag +/// should be provided in the `tag` field. +pub fn decrypt_aead( + t: Cipher, + key: &[u8], + iv: Option<&[u8]>, + aad: &[u8], + data: &[u8], + tag: &[u8], +) -> Result<Vec<u8>, ErrorStack> { + let mut c = Crypter::new(t, Mode::Decrypt, key, iv)?; + let mut out = vec![0; data.len() + t.block_size()]; + + let is_ccm = t.is_ccm(); + if is_ccm || t.is_ocb() { + c.set_tag(tag)?; + if is_ccm { + c.set_data_len(data.len())?; + } + } + + c.aad_update(aad)?; + let count = c.update(data, &mut out)?; + + let rest = if t.is_ccm() { + 0 + } else { + c.set_tag(tag)?; + c.finalize(&mut out[count..])? + }; + + out.truncate(count + rest); + Ok(out) +} + +cfg_if! { + if #[cfg(any(boringssl, ossl110, libressl273))] { + use ffi::{EVP_CIPHER_block_size, EVP_CIPHER_iv_length, EVP_CIPHER_key_length}; + } else { + use crate::LenType; + + #[allow(bad_style)] + pub unsafe fn EVP_CIPHER_iv_length(ptr: *const ffi::EVP_CIPHER) -> LenType { + (*ptr).iv_len + } + + #[allow(bad_style)] + pub unsafe fn EVP_CIPHER_block_size(ptr: *const ffi::EVP_CIPHER) -> LenType { + (*ptr).block_size + } + + #[allow(bad_style)] + pub unsafe fn EVP_CIPHER_key_length(ptr: *const ffi::EVP_CIPHER) -> LenType { + (*ptr).key_len + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use hex::{self, FromHex}; + + #[test] + fn test_stream_cipher_output() { + let key = [0u8; 16]; + let iv = [0u8; 16]; + let mut c = super::Crypter::new( + super::Cipher::aes_128_ctr(), + super::Mode::Encrypt, + &key, + Some(&iv), + ) + .unwrap(); + + assert_eq!(c.update(&[0u8; 15], &mut [0u8; 15]).unwrap(), 15); + assert_eq!(c.update(&[0u8; 1], &mut [0u8; 1]).unwrap(), 1); + assert_eq!(c.finalize(&mut [0u8; 0]).unwrap(), 0); + } + + // Test vectors from FIPS-197: + // http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf + #[test] + fn test_aes_256_ecb() { + let k0 = [ + 0x00u8, 0x01u8, 0x02u8, 0x03u8, 0x04u8, 0x05u8, 0x06u8, 0x07u8, 0x08u8, 0x09u8, 0x0au8, + 0x0bu8, 0x0cu8, 0x0du8, 0x0eu8, 0x0fu8, 0x10u8, 0x11u8, 0x12u8, 0x13u8, 0x14u8, 0x15u8, + 0x16u8, 0x17u8, 0x18u8, 0x19u8, 0x1au8, 0x1bu8, 0x1cu8, 0x1du8, 0x1eu8, 0x1fu8, + ]; + let p0 = [ + 0x00u8, 0x11u8, 0x22u8, 0x33u8, 0x44u8, 0x55u8, 0x66u8, 0x77u8, 0x88u8, 0x99u8, 0xaau8, + 0xbbu8, 0xccu8, 0xddu8, 0xeeu8, 0xffu8, + ]; + let c0 = [ + 0x8eu8, 0xa2u8, 0xb7u8, 0xcau8, 0x51u8, 0x67u8, 0x45u8, 0xbfu8, 0xeau8, 0xfcu8, 0x49u8, + 0x90u8, 0x4bu8, 0x49u8, 0x60u8, 0x89u8, + ]; + let mut c = super::Crypter::new( + super::Cipher::aes_256_ecb(), + super::Mode::Encrypt, + &k0, + None, + ) + .unwrap(); + c.pad(false); + let mut r0 = vec![0; c0.len() + super::Cipher::aes_256_ecb().block_size()]; + let count = c.update(&p0, &mut r0).unwrap(); + let rest = c.finalize(&mut r0[count..]).unwrap(); + r0.truncate(count + rest); + assert_eq!(hex::encode(&r0), hex::encode(c0)); + + let mut c = super::Crypter::new( + super::Cipher::aes_256_ecb(), + super::Mode::Decrypt, + &k0, + None, + ) + .unwrap(); + c.pad(false); + let mut p1 = vec![0; r0.len() + super::Cipher::aes_256_ecb().block_size()]; + let count = c.update(&r0, &mut p1).unwrap(); + let rest = c.finalize(&mut p1[count..]).unwrap(); + p1.truncate(count + rest); + assert_eq!(hex::encode(p1), hex::encode(p0)); + } + + #[test] + fn test_aes_256_cbc_decrypt() { + let iv = [ + 4_u8, 223_u8, 153_u8, 219_u8, 28_u8, 142_u8, 234_u8, 68_u8, 227_u8, 69_u8, 98_u8, + 107_u8, 208_u8, 14_u8, 236_u8, 60_u8, + ]; + let data = [ + 143_u8, 210_u8, 75_u8, 63_u8, 214_u8, 179_u8, 155_u8, 241_u8, 242_u8, 31_u8, 154_u8, + 56_u8, 198_u8, 145_u8, 192_u8, 64_u8, 2_u8, 245_u8, 167_u8, 220_u8, 55_u8, 119_u8, + 233_u8, 136_u8, 139_u8, 27_u8, 71_u8, 242_u8, 119_u8, 175_u8, 65_u8, 207_u8, + ]; + let ciphered_data = [ + 0x4a_u8, 0x2e_u8, 0xe5_u8, 0x6_u8, 0xbf_u8, 0xcf_u8, 0xf2_u8, 0xd7_u8, 0xea_u8, + 0x2d_u8, 0xb1_u8, 0x85_u8, 0x6c_u8, 0x93_u8, 0x65_u8, 0x6f_u8, + ]; + let mut cr = super::Crypter::new( + super::Cipher::aes_256_cbc(), + super::Mode::Decrypt, + &data, + Some(&iv), + ) + .unwrap(); + cr.pad(false); + let mut unciphered_data = vec![0; data.len() + super::Cipher::aes_256_cbc().block_size()]; + let count = cr.update(&ciphered_data, &mut unciphered_data).unwrap(); + let rest = cr.finalize(&mut unciphered_data[count..]).unwrap(); + unciphered_data.truncate(count + rest); + + let expected_unciphered_data = b"I love turtles.\x01"; + + assert_eq!(&unciphered_data, expected_unciphered_data); + } + + fn cipher_test(ciphertype: super::Cipher, pt: &str, ct: &str, key: &str, iv: &str) { + let pt = Vec::from_hex(pt).unwrap(); + let ct = Vec::from_hex(ct).unwrap(); + let key = Vec::from_hex(key).unwrap(); + let iv = Vec::from_hex(iv).unwrap(); + + let computed = super::decrypt(ciphertype, &key, Some(&iv), &ct).unwrap(); + let expected = pt; + + if computed != expected { + println!("Computed: {}", hex::encode(&computed)); + println!("Expected: {}", hex::encode(&expected)); + if computed.len() != expected.len() { + println!( + "Lengths differ: {} in computed vs {} expected", + computed.len(), + expected.len() + ); + } + panic!("test failure"); + } + } + + #[cfg(not(boringssl))] + fn cipher_test_nopad(ciphertype: super::Cipher, pt: &str, ct: &str, key: &str, iv: &str) { + let pt = Vec::from_hex(pt).unwrap(); + let ct = Vec::from_hex(ct).unwrap(); + let key = Vec::from_hex(key).unwrap(); + let iv = Vec::from_hex(iv).unwrap(); + + let computed = { + let mut c = Crypter::new(ciphertype, Mode::Decrypt, &key, Some(&iv)).unwrap(); + c.pad(false); + let mut out = vec![0; ct.len() + ciphertype.block_size()]; + let count = c.update(&ct, &mut out).unwrap(); + let rest = c.finalize(&mut out[count..]).unwrap(); + out.truncate(count + rest); + out + }; + let expected = pt; + + if computed != expected { + println!("Computed: {}", hex::encode(&computed)); + println!("Expected: {}", hex::encode(&expected)); + if computed.len() != expected.len() { + println!( + "Lengths differ: {} in computed vs {} expected", + computed.len(), + expected.len() + ); + } + panic!("test failure"); + } + } + + #[test] + fn test_rc4() { + #[cfg(ossl300)] + let _provider = crate::provider::Provider::try_load(None, "legacy", true).unwrap(); + + let pt = "0000000000000000000000000000000000000000000000000000000000000000000000000000"; + let ct = "A68686B04D686AA107BD8D4CAB191A3EEC0A6294BC78B60F65C25CB47BD7BB3A48EFC4D26BE4"; + let key = "97CD440324DA5FD1F7955C1C13B6B466"; + let iv = ""; + + cipher_test(super::Cipher::rc4(), pt, ct, key, iv); + } + + #[test] + #[cfg(not(boringssl))] + fn test_aes256_xts() { + // Test case 174 from + // http://csrc.nist.gov/groups/STM/cavp/documents/aes/XTSTestVectors.zip + let pt = "77f4ef63d734ebd028508da66c22cdebdd52ecd6ee2ab0a50bc8ad0cfd692ca5fcd4e6dedc45df7f\ + 6503f462611dc542"; + let ct = "ce7d905a7776ac72f240d22aafed5e4eb7566cdc7211220e970da634ce015f131a5ecb8d400bc9e8\ + 4f0b81d8725dbbc7"; + let key = "b6bfef891f83b5ff073f2231267be51eb084b791fa19a154399c0684c8b2dfcb37de77d28bbda3b\ + 4180026ad640b74243b3133e7b9fae629403f6733423dae28"; + let iv = "db200efb7eaaa737dbdf40babb68953f"; + + cipher_test(super::Cipher::aes_256_xts(), pt, ct, key, iv); + } + + #[test] + fn test_aes128_ctr() { + let pt = "6BC1BEE22E409F96E93D7E117393172AAE2D8A571E03AC9C9EB76FAC45AF8E5130C81C46A35CE411\ + E5FBC1191A0A52EFF69F2445DF4F9B17AD2B417BE66C3710"; + let ct = "874D6191B620E3261BEF6864990DB6CE9806F66B7970FDFF8617187BB9FFFDFF5AE4DF3EDBD5D35E\ + 5B4F09020DB03EAB1E031DDA2FBE03D1792170A0F3009CEE"; + let key = "2B7E151628AED2A6ABF7158809CF4F3C"; + let iv = "F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF"; + + cipher_test(super::Cipher::aes_128_ctr(), pt, ct, key, iv); + } + + #[test] + #[cfg(not(boringssl))] + fn test_aes128_cfb1() { + // Lifted from http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + + let pt = "6bc1"; + let ct = "68b3"; + let key = "2b7e151628aed2a6abf7158809cf4f3c"; + let iv = "000102030405060708090a0b0c0d0e0f"; + + cipher_test(super::Cipher::aes_128_cfb1(), pt, ct, key, iv); + } + + #[test] + #[cfg(not(boringssl))] + fn test_aes128_cfb128() { + let pt = "6bc1bee22e409f96e93d7e117393172a"; + let ct = "3b3fd92eb72dad20333449f8e83cfb4a"; + let key = "2b7e151628aed2a6abf7158809cf4f3c"; + let iv = "000102030405060708090a0b0c0d0e0f"; + + cipher_test(super::Cipher::aes_128_cfb128(), pt, ct, key, iv); + } + + #[test] + #[cfg(not(boringssl))] + fn test_aes128_cfb8() { + let pt = "6bc1bee22e409f96e93d7e117393172aae2d"; + let ct = "3b79424c9c0dd436bace9e0ed4586a4f32b9"; + let key = "2b7e151628aed2a6abf7158809cf4f3c"; + let iv = "000102030405060708090a0b0c0d0e0f"; + + cipher_test(super::Cipher::aes_128_cfb8(), pt, ct, key, iv); + } + + #[test] + fn test_aes128_ofb() { + // Lifted from http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + + let pt = "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710"; + let ct = "3b3fd92eb72dad20333449f8e83cfb4a7789508d16918f03f53c52dac54ed8259740051e9c5fecf64344f7a82260edcc304c6528f659c77866a510d9c1d6ae5e"; + let key = "2b7e151628aed2a6abf7158809cf4f3c"; + let iv = "000102030405060708090a0b0c0d0e0f"; + + cipher_test(super::Cipher::aes_128_ofb(), pt, ct, key, iv); + } + + #[test] + fn test_aes192_ctr() { + // Lifted from http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + + let pt = "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710"; + let ct = "1abc932417521ca24f2b0459fe7e6e0b090339ec0aa6faefd5ccc2c6f4ce8e941e36b26bd1ebc670d1bd1d665620abf74f78a7f6d29809585a97daec58c6b050"; + let key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b"; + let iv = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"; + + cipher_test(super::Cipher::aes_192_ctr(), pt, ct, key, iv); + } + + #[test] + #[cfg(not(boringssl))] + fn test_aes192_cfb1() { + // Lifted from http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + + let pt = "6bc1"; + let ct = "9359"; + let key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b"; + let iv = "000102030405060708090a0b0c0d0e0f"; + + cipher_test(super::Cipher::aes_192_cfb1(), pt, ct, key, iv); + } + + #[test] + #[cfg(not(boringssl))] + fn test_aes192_cfb128() { + // Lifted from http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + + let pt = "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710"; + let ct = "cdc80d6fddf18cab34c25909c99a417467ce7f7f81173621961a2b70171d3d7a2e1e8a1dd59b88b1c8e60fed1efac4c9c05f9f9ca9834fa042ae8fba584b09ff"; + let key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b"; + let iv = "000102030405060708090a0b0c0d0e0f"; + + cipher_test(super::Cipher::aes_192_cfb128(), pt, ct, key, iv); + } + + #[test] + #[cfg(not(boringssl))] + fn test_aes192_cfb8() { + // Lifted from http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + + let pt = "6bc1bee22e409f96e93d7e117393172aae2d"; + let ct = "cda2521ef0a905ca44cd057cbf0d47a0678a"; + let key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b"; + let iv = "000102030405060708090a0b0c0d0e0f"; + + cipher_test(super::Cipher::aes_192_cfb8(), pt, ct, key, iv); + } + + #[test] + fn test_aes192_ofb() { + // Lifted from http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + + let pt = "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710"; + let ct = "cdc80d6fddf18cab34c25909c99a4174fcc28b8d4c63837c09e81700c11004018d9a9aeac0f6596f559c6d4daf59a5f26d9f200857ca6c3e9cac524bd9acc92a"; + let key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b"; + let iv = "000102030405060708090a0b0c0d0e0f"; + + cipher_test(super::Cipher::aes_192_ofb(), pt, ct, key, iv); + } + + #[test] + #[cfg(not(boringssl))] + fn test_aes256_cfb1() { + let pt = "6bc1"; + let ct = "9029"; + let key = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"; + let iv = "000102030405060708090a0b0c0d0e0f"; + + cipher_test(super::Cipher::aes_256_cfb1(), pt, ct, key, iv); + } + + #[test] + #[cfg(not(boringssl))] + fn test_aes256_cfb128() { + let pt = "6bc1bee22e409f96e93d7e117393172a"; + let ct = "dc7e84bfda79164b7ecd8486985d3860"; + let key = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"; + let iv = "000102030405060708090a0b0c0d0e0f"; + + cipher_test(super::Cipher::aes_256_cfb128(), pt, ct, key, iv); + } + + #[test] + #[cfg(not(boringssl))] + fn test_aes256_cfb8() { + let pt = "6bc1bee22e409f96e93d7e117393172aae2d"; + let ct = "dc1f1a8520a64db55fcc8ac554844e889700"; + let key = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"; + let iv = "000102030405060708090a0b0c0d0e0f"; + + cipher_test(super::Cipher::aes_256_cfb8(), pt, ct, key, iv); + } + + #[test] + fn test_aes256_ofb() { + // Lifted from http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + + let pt = "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710"; + let ct = "dc7e84bfda79164b7ecd8486985d38604febdc6740d20b3ac88f6ad82a4fb08d71ab47a086e86eedf39d1c5bba97c4080126141d67f37be8538f5a8be740e484"; + let key = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"; + let iv = "000102030405060708090a0b0c0d0e0f"; + + cipher_test(super::Cipher::aes_256_ofb(), pt, ct, key, iv); + } + + #[test] + #[cfg_attr(ossl300, ignore)] + #[cfg(not(boringssl))] + fn test_bf_cbc() { + #[cfg(ossl300)] + let _provider = crate::provider::Provider::try_load(None, "legacy", true).unwrap(); + + // https://www.schneier.com/code/vectors.txt + + let pt = "37363534333231204E6F77206973207468652074696D6520666F722000000000"; + let ct = "6B77B4D63006DEE605B156E27403979358DEB9E7154616D959F1652BD5FF92CC"; + let key = "0123456789ABCDEFF0E1D2C3B4A59687"; + let iv = "FEDCBA9876543210"; + + cipher_test_nopad(super::Cipher::bf_cbc(), pt, ct, key, iv); + } + + #[test] + #[cfg_attr(ossl300, ignore)] + #[cfg(not(boringssl))] + fn test_bf_ecb() { + #[cfg(ossl300)] + let _provider = crate::provider::Provider::try_load(None, "legacy", true).unwrap(); + + let pt = "5CD54CA83DEF57DA"; + let ct = "B1B8CC0B250F09A0"; + let key = "0131D9619DC1376E"; + let iv = "0000000000000000"; + + cipher_test_nopad(super::Cipher::bf_ecb(), pt, ct, key, iv); + } + + #[test] + #[cfg_attr(ossl300, ignore)] + #[cfg(not(boringssl))] + fn test_bf_cfb64() { + #[cfg(ossl300)] + let _provider = crate::provider::Provider::try_load(None, "legacy", true).unwrap(); + + let pt = "37363534333231204E6F77206973207468652074696D6520666F722000"; + let ct = "E73214A2822139CAF26ECF6D2EB9E76E3DA3DE04D1517200519D57A6C3"; + let key = "0123456789ABCDEFF0E1D2C3B4A59687"; + let iv = "FEDCBA9876543210"; + + cipher_test_nopad(super::Cipher::bf_cfb64(), pt, ct, key, iv); + } + + #[test] + #[cfg_attr(ossl300, ignore)] + #[cfg(not(boringssl))] + fn test_bf_ofb() { + #[cfg(ossl300)] + let _provider = crate::provider::Provider::try_load(None, "legacy", true).unwrap(); + + let pt = "37363534333231204E6F77206973207468652074696D6520666F722000"; + let ct = "E73214A2822139CA62B343CC5B65587310DD908D0C241B2263C2CF80DA"; + let key = "0123456789ABCDEFF0E1D2C3B4A59687"; + let iv = "FEDCBA9876543210"; + + cipher_test_nopad(super::Cipher::bf_ofb(), pt, ct, key, iv); + } + + #[test] + fn test_des_cbc() { + #[cfg(ossl300)] + let _provider = crate::provider::Provider::try_load(None, "legacy", true).unwrap(); + + let pt = "54686973206973206120746573742e"; + let ct = "6f2867cfefda048a4046ef7e556c7132"; + let key = "7cb66337f3d3c0fe"; + let iv = "0001020304050607"; + + cipher_test(super::Cipher::des_cbc(), pt, ct, key, iv); + } + + #[test] + fn test_des_ecb() { + #[cfg(ossl300)] + let _provider = crate::provider::Provider::try_load(None, "legacy", true).unwrap(); + + let pt = "54686973206973206120746573742e"; + let ct = "0050ab8aecec758843fe157b4dde938c"; + let key = "7cb66337f3d3c0fe"; + let iv = "0001020304050607"; + + cipher_test(super::Cipher::des_ecb(), pt, ct, key, iv); + } + + #[test] + fn test_des_ede3() { + let pt = "9994f4c69d40ae4f34ff403b5cf39d4c8207ea5d3e19a5fd"; + let ct = "9e5c4297d60582f81071ac8ab7d0698d4c79de8b94c519858207ea5d3e19a5fd"; + let key = "010203040506070801020304050607080102030405060708"; + let iv = "5cc118306dc702e4"; + + cipher_test(super::Cipher::des_ede3(), pt, ct, key, iv); + } + + #[test] + fn test_des_ede3_cbc() { + let pt = "54686973206973206120746573742e"; + let ct = "6f2867cfefda048a4046ef7e556c7132"; + let key = "7cb66337f3d3c0fe7cb66337f3d3c0fe7cb66337f3d3c0fe"; + let iv = "0001020304050607"; + + cipher_test(super::Cipher::des_ede3_cbc(), pt, ct, key, iv); + } + + #[test] + #[cfg(not(boringssl))] + fn test_des_ede3_cfb64() { + let pt = "2b1773784b5889dc788477367daa98ad"; + let ct = "6f2867cfefda048a4046ef7e556c7132"; + let key = "7cb66337f3d3c0fe7cb66337f3d3c0fe7cb66337f3d3c0fe"; + let iv = "0001020304050607"; + + cipher_test(super::Cipher::des_ede3_cfb64(), pt, ct, key, iv); + } + + #[test] + fn test_aes128_gcm() { + let key = "23dc8d23d95b6fd1251741a64f7d4f41"; + let iv = "f416f48ad44d9efa1179e167"; + let pt = "6cb9b71dd0ccd42cdf87e8e396fc581fd8e0d700e360f590593b748e105390de"; + let aad = "45074844c97d515c65bbe37c210a5a4b08c21c588efe5c5f73c4d9c17d34dacddc0bb6a8a53f7bf477b9780c1c2a928660df87016b2873fe876b2b887fb5886bfd63216b7eaecc046372a82c047eb043f0b063226ee52a12c69b"; + let ct = "8ad20486778e87387efb3f2574e509951c0626816722018129e578b2787969d3"; + let tag = "91e1bc09"; + + // this tag is smaller than you'd normally want, but I pulled this test from the part of + // the NIST test vectors that cover 4 byte tags. + let mut actual_tag = [0; 4]; + let out = encrypt_aead( + Cipher::aes_128_gcm(), + &Vec::from_hex(key).unwrap(), + Some(&Vec::from_hex(iv).unwrap()), + &Vec::from_hex(aad).unwrap(), + &Vec::from_hex(pt).unwrap(), + &mut actual_tag, + ) + .unwrap(); + assert_eq!(ct, hex::encode(out)); + assert_eq!(tag, hex::encode(actual_tag)); + + let out = decrypt_aead( + Cipher::aes_128_gcm(), + &Vec::from_hex(key).unwrap(), + Some(&Vec::from_hex(iv).unwrap()), + &Vec::from_hex(aad).unwrap(), + &Vec::from_hex(ct).unwrap(), + &Vec::from_hex(tag).unwrap(), + ) + .unwrap(); + assert_eq!(pt, hex::encode(out)); + } + + #[test] + #[cfg(not(boringssl))] + fn test_aes128_ccm() { + let key = "3ee186594f110fb788a8bf8aa8be5d4a"; + let nonce = "44f705d52acf27b7f17196aa9b"; + let aad = "2c16724296ff85e079627be3053ea95adf35722c21886baba343bd6c79b5cb57"; + + let pt = "d71864877f2578db092daba2d6a1f9f4698a9c356c7830a1"; + let ct = "b4dd74e7a0cc51aea45dfb401a41d5822c96901a83247ea0"; + let tag = "d6965f5aa6e31302a9cc2b36"; + + let mut actual_tag = [0; 12]; + let out = encrypt_aead( + Cipher::aes_128_ccm(), + &Vec::from_hex(key).unwrap(), + Some(&Vec::from_hex(nonce).unwrap()), + &Vec::from_hex(aad).unwrap(), + &Vec::from_hex(pt).unwrap(), + &mut actual_tag, + ) + .unwrap(); + + assert_eq!(ct, hex::encode(out)); + assert_eq!(tag, hex::encode(actual_tag)); + + let out = decrypt_aead( + Cipher::aes_128_ccm(), + &Vec::from_hex(key).unwrap(), + Some(&Vec::from_hex(nonce).unwrap()), + &Vec::from_hex(aad).unwrap(), + &Vec::from_hex(ct).unwrap(), + &Vec::from_hex(tag).unwrap(), + ) + .unwrap(); + assert_eq!(pt, hex::encode(out)); + } + + #[test] + #[cfg(not(boringssl))] + fn test_aes128_ccm_verify_fail() { + let key = "3ee186594f110fb788a8bf8aa8be5d4a"; + let nonce = "44f705d52acf27b7f17196aa9b"; + let aad = "2c16724296ff85e079627be3053ea95adf35722c21886baba343bd6c79b5cb57"; + + let ct = "b4dd74e7a0cc51aea45dfb401a41d5822c96901a83247ea0"; + let tag = "00005f5aa6e31302a9cc2b36"; + + let out = decrypt_aead( + Cipher::aes_128_ccm(), + &Vec::from_hex(key).unwrap(), + Some(&Vec::from_hex(nonce).unwrap()), + &Vec::from_hex(aad).unwrap(), + &Vec::from_hex(ct).unwrap(), + &Vec::from_hex(tag).unwrap(), + ); + assert!(out.is_err()); + } + + #[test] + #[cfg(not(boringssl))] + fn test_aes256_ccm() { + let key = "7f4af6765cad1d511db07e33aaafd57646ec279db629048aa6770af24849aa0d"; + let nonce = "dde2a362ce81b2b6913abc3095"; + let aad = "404f5df97ece7431987bc098cce994fc3c063b519ffa47b0365226a0015ef695"; + + let pt = "7ebef26bf4ecf6f0ebb2eb860edbf900f27b75b4a6340fdb"; + let ct = "353022db9c568bd7183a13c40b1ba30fcc768c54264aa2cd"; + let tag = "2927a053c9244d3217a7ad05"; + + let mut actual_tag = [0; 12]; + let out = encrypt_aead( + Cipher::aes_256_ccm(), + &Vec::from_hex(key).unwrap(), + Some(&Vec::from_hex(nonce).unwrap()), + &Vec::from_hex(aad).unwrap(), + &Vec::from_hex(pt).unwrap(), + &mut actual_tag, + ) + .unwrap(); + + assert_eq!(ct, hex::encode(out)); + assert_eq!(tag, hex::encode(actual_tag)); + + let out = decrypt_aead( + Cipher::aes_256_ccm(), + &Vec::from_hex(key).unwrap(), + Some(&Vec::from_hex(nonce).unwrap()), + &Vec::from_hex(aad).unwrap(), + &Vec::from_hex(ct).unwrap(), + &Vec::from_hex(tag).unwrap(), + ) + .unwrap(); + assert_eq!(pt, hex::encode(out)); + } + + #[test] + #[cfg(not(boringssl))] + fn test_aes256_ccm_verify_fail() { + let key = "7f4af6765cad1d511db07e33aaafd57646ec279db629048aa6770af24849aa0d"; + let nonce = "dde2a362ce81b2b6913abc3095"; + let aad = "404f5df97ece7431987bc098cce994fc3c063b519ffa47b0365226a0015ef695"; + + let ct = "353022db9c568bd7183a13c40b1ba30fcc768c54264aa2cd"; + let tag = "0000a053c9244d3217a7ad05"; + + let out = decrypt_aead( + Cipher::aes_256_ccm(), + &Vec::from_hex(key).unwrap(), + Some(&Vec::from_hex(nonce).unwrap()), + &Vec::from_hex(aad).unwrap(), + &Vec::from_hex(ct).unwrap(), + &Vec::from_hex(tag).unwrap(), + ); + assert!(out.is_err()); + } + + #[test] + #[cfg(all(ossl110, not(osslconf = "OPENSSL_NO_OCB")))] + fn test_aes_128_ocb() { + let key = "000102030405060708090a0b0c0d0e0f"; + let aad = "0001020304050607"; + let tag = "16dc76a46d47e1ead537209e8a96d14e"; + let iv = "000102030405060708090a0b"; + let pt = "0001020304050607"; + let ct = "92b657130a74b85a"; + + let mut actual_tag = [0; 16]; + let out = encrypt_aead( + Cipher::aes_128_ocb(), + &Vec::from_hex(key).unwrap(), + Some(&Vec::from_hex(iv).unwrap()), + &Vec::from_hex(aad).unwrap(), + &Vec::from_hex(pt).unwrap(), + &mut actual_tag, + ) + .unwrap(); + + assert_eq!(ct, hex::encode(out)); + assert_eq!(tag, hex::encode(actual_tag)); + + let out = decrypt_aead( + Cipher::aes_128_ocb(), + &Vec::from_hex(key).unwrap(), + Some(&Vec::from_hex(iv).unwrap()), + &Vec::from_hex(aad).unwrap(), + &Vec::from_hex(ct).unwrap(), + &Vec::from_hex(tag).unwrap(), + ) + .unwrap(); + assert_eq!(pt, hex::encode(out)); + } + + #[test] + #[cfg(all(ossl110, not(osslconf = "OPENSSL_NO_OCB")))] + fn test_aes_128_ocb_fail() { + let key = "000102030405060708090a0b0c0d0e0f"; + let aad = "0001020304050607"; + let tag = "16dc76a46d47e1ead537209e8a96d14e"; + let iv = "000000000405060708090a0b"; + let ct = "92b657130a74b85a"; + + let out = decrypt_aead( + Cipher::aes_128_ocb(), + &Vec::from_hex(key).unwrap(), + Some(&Vec::from_hex(iv).unwrap()), + &Vec::from_hex(aad).unwrap(), + &Vec::from_hex(ct).unwrap(), + &Vec::from_hex(tag).unwrap(), + ); + assert!(out.is_err()); + } + + #[test] + #[cfg(ossl110)] + fn test_chacha20() { + let key = "0000000000000000000000000000000000000000000000000000000000000000"; + let iv = "00000000000000000000000000000000"; + let pt = + "000000000000000000000000000000000000000000000000000000000000000000000000000000000\ + 00000000000000000000000000000000000000000000000"; + let ct = + "76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7\ + 724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586"; + + cipher_test(Cipher::chacha20(), pt, ct, key, iv); + } + + #[test] + #[cfg(any(ossl110, libressl360))] + fn test_chacha20_poly1305() { + let key = "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f"; + let iv = "070000004041424344454647"; + let aad = "50515253c0c1c2c3c4c5c6c7"; + let pt = + "4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393\ + a204966204920636f756c64206f6666657220796f75206f6e6c79206f6e652074697020666f722074\ + 6865206675747572652c2073756e73637265656e20776f756c642062652069742e"; + let ct = + "d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca967128\ + 2fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fa\ + b324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b6116"; + let tag = "1ae10b594f09e26a7e902ecbd0600691"; + + let mut actual_tag = [0; 16]; + let out = encrypt_aead( + Cipher::chacha20_poly1305(), + &Vec::from_hex(key).unwrap(), + Some(&Vec::from_hex(iv).unwrap()), + &Vec::from_hex(aad).unwrap(), + &Vec::from_hex(pt).unwrap(), + &mut actual_tag, + ) + .unwrap(); + assert_eq!(ct, hex::encode(out)); + assert_eq!(tag, hex::encode(actual_tag)); + + let out = decrypt_aead( + Cipher::chacha20_poly1305(), + &Vec::from_hex(key).unwrap(), + Some(&Vec::from_hex(iv).unwrap()), + &Vec::from_hex(aad).unwrap(), + &Vec::from_hex(ct).unwrap(), + &Vec::from_hex(tag).unwrap(), + ) + .unwrap(); + assert_eq!(pt, hex::encode(out)); + } + + #[test] + #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_SEED", ossl300)))] + fn test_seed_cbc() { + #[cfg(ossl300)] + let _provider = crate::provider::Provider::try_load(None, "legacy", true).unwrap(); + + let pt = "5363686f6b6f6c6164656e6b756368656e0a"; + let ct = "c2edf0fb2eb11bf7b2f39417a8528896d34b24b6fd79e5923b116dfcd2aba5a4"; + let key = "41414141414141414141414141414141"; + let iv = "41414141414141414141414141414141"; + + cipher_test(super::Cipher::seed_cbc(), pt, ct, key, iv); + } + + #[test] + #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_SEED", ossl300)))] + fn test_seed_cfb128() { + #[cfg(ossl300)] + let _provider = crate::provider::Provider::try_load(None, "legacy", true).unwrap(); + + let pt = "5363686f6b6f6c6164656e6b756368656e0a"; + let ct = "71d4d25fc1750cb7789259e7f34061939a41"; + let key = "41414141414141414141414141414141"; + let iv = "41414141414141414141414141414141"; + + cipher_test(super::Cipher::seed_cfb128(), pt, ct, key, iv); + } + + #[test] + #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_SEED", ossl300)))] + fn test_seed_ecb() { + #[cfg(ossl300)] + let _provider = crate::provider::Provider::try_load(None, "legacy", true).unwrap(); + + let pt = "5363686f6b6f6c6164656e6b756368656e0a"; + let ct = "0263a9cd498cf0edb0ef72a3231761d00ce601f7d08ad19ad74f0815f2c77f7e"; + let key = "41414141414141414141414141414141"; + let iv = "41414141414141414141414141414141"; + + cipher_test(super::Cipher::seed_ecb(), pt, ct, key, iv); + } + + #[test] + #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_SEED", ossl300)))] + fn test_seed_ofb() { + #[cfg(ossl300)] + let _provider = crate::provider::Provider::try_load(None, "legacy", true).unwrap(); + + let pt = "5363686f6b6f6c6164656e6b756368656e0a"; + let ct = "71d4d25fc1750cb7789259e7f34061930afd"; + let key = "41414141414141414141414141414141"; + let iv = "41414141414141414141414141414141"; + + cipher_test(super::Cipher::seed_ofb(), pt, ct, key, iv); + } + + // GB/T 32907-2016 + // http://openstd.samr.gov.cn/bzgk/gb/newGbInfo?hcno=7803DE42D3BC5E80B0C3E5D8E873D56A + #[test] + #[cfg(all(any(ossl111, libressl291), not(osslconf = "OPENSSL_NO_SM4")))] + fn test_sm4_ecb() { + use std::mem; + + let key = vec![ + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, + 0x32, 0x10, + ]; + let pt = vec![ + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, + 0x32, 0x10, + ]; + let ct = vec![ + 0x68, 0x1e, 0xdf, 0x34, 0xd2, 0x06, 0x96, 0x5e, 0x86, 0xb3, 0xe9, 0x4f, 0x53, 0x6e, + 0x42, 0x46, + ]; + let ct1 = vec![ + 0x59, 0x52, 0x98, 0xc7, 0xc6, 0xfd, 0x27, 0x1f, 0x04, 0x02, 0xf8, 0x04, 0xc3, 0x3d, + 0x3f, 0x66, + ]; + + let block_size = Cipher::sm4_ecb().block_size(); + let mut c = Crypter::new(Cipher::sm4_ecb(), Mode::Encrypt, &key, None).unwrap(); + c.pad(false); + + // 1 round + let mut r = vec![0; pt.len() + Cipher::sm4_ecb().block_size()]; + let count = c.update(&pt, &mut r).unwrap(); + assert_eq!(ct, &r[..count]); + + // 1000000 rounds + let mut r1 = vec![0; pt.len() + Cipher::sm4_ecb().block_size()]; + for _ in 0..999999 { + c.update(&r[..block_size], &mut r1).unwrap(); + mem::swap(&mut r, &mut r1); + } + assert_eq!(ct1, &r[..count]); + } +} diff --git a/vendor/openssl/src/util.rs b/vendor/openssl/src/util.rs new file mode 100644 index 0000000..d852a4b --- /dev/null +++ b/vendor/openssl/src/util.rs @@ -0,0 +1,93 @@ +use crate::error::ErrorStack; +use foreign_types::{ForeignType, ForeignTypeRef}; +use libc::{c_char, c_int, c_void}; +use std::any::Any; +use std::panic::{self, AssertUnwindSafe}; +use std::slice; + +/// Wraps a user-supplied callback and a slot for panics thrown inside the callback (while FFI +/// frames are on the stack). +/// +/// When dropped, checks if the callback has panicked, and resumes unwinding if so. +pub struct CallbackState<F> { + /// The user callback. Taken out of the `Option` when called. + cb: Option<F>, + /// If the callback panics, we place the panic object here, to be re-thrown once OpenSSL + /// returns. + panic: Option<Box<dyn Any + Send + 'static>>, +} + +impl<F> CallbackState<F> { + pub fn new(callback: F) -> Self { + CallbackState { + cb: Some(callback), + panic: None, + } + } +} + +impl<F> Drop for CallbackState<F> { + fn drop(&mut self) { + if let Some(panic) = self.panic.take() { + panic::resume_unwind(panic); + } + } +} + +/// Password callback function, passed to private key loading functions. +/// +/// `cb_state` is expected to be a pointer to a `CallbackState`. +pub unsafe extern "C" fn invoke_passwd_cb<F>( + buf: *mut c_char, + size: c_int, + _rwflag: c_int, + cb_state: *mut c_void, +) -> c_int +where + F: FnOnce(&mut [u8]) -> Result<usize, ErrorStack>, +{ + let callback = &mut *(cb_state as *mut CallbackState<F>); + + let result = panic::catch_unwind(AssertUnwindSafe(|| { + let pass_slice = slice::from_raw_parts_mut(buf as *mut u8, size as usize); + callback.cb.take().unwrap()(pass_slice) + })); + + match result { + Ok(Ok(len)) => len as c_int, + Ok(Err(_)) => { + // FIXME restore error stack + 0 + } + Err(err) => { + callback.panic = Some(err); + 0 + } + } +} + +pub trait ForeignTypeExt: ForeignType { + unsafe fn from_ptr_opt(ptr: *mut Self::CType) -> Option<Self> { + if ptr.is_null() { + None + } else { + Some(Self::from_ptr(ptr)) + } + } +} +impl<FT: ForeignType> ForeignTypeExt for FT {} + +pub trait ForeignTypeRefExt: ForeignTypeRef { + unsafe fn from_const_ptr<'a>(ptr: *const Self::CType) -> &'a Self { + Self::from_ptr(ptr as *mut Self::CType) + } + + unsafe fn from_const_ptr_opt<'a>(ptr: *const Self::CType) -> Option<&'a Self> { + if ptr.is_null() { + None + } else { + Some(Self::from_const_ptr(ptr as *mut Self::CType)) + } + } +} +impl<FT: ForeignTypeRef> ForeignTypeRefExt for FT {} diff --git a/vendor/openssl/src/version.rs b/vendor/openssl/src/version.rs new file mode 100644 index 0000000..f1a324c --- /dev/null +++ b/vendor/openssl/src/version.rs @@ -0,0 +1,135 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +//! Build and version information. + +use cfg_if::cfg_if; +use openssl_macros::corresponds; +use std::ffi::CStr; + +cfg_if! { + if #[cfg(any(ossl110, libressl271))] { + use ffi::{ + OPENSSL_VERSION, OPENSSL_CFLAGS, OPENSSL_BUILT_ON, OPENSSL_PLATFORM, OPENSSL_DIR, + OpenSSL_version_num, OpenSSL_version, + }; + } else { + use ffi::{ + SSLEAY_VERSION as OPENSSL_VERSION, SSLEAY_CFLAGS as OPENSSL_CFLAGS, + SSLEAY_BUILT_ON as OPENSSL_BUILT_ON, SSLEAY_PLATFORM as OPENSSL_PLATFORM, + SSLEAY_DIR as OPENSSL_DIR, SSLeay as OpenSSL_version_num, + SSLeay_version as OpenSSL_version, + }; + } +} + +/// OPENSSL_VERSION_NUMBER is a numeric release version identifier: +/// +/// `MNNFFPPS: major minor fix patch status` +/// +/// The status nibble has one of the values 0 for development, 1 to e for betas 1 to 14, and f for release. +/// +/// for example +/// +/// `0x000906000 == 0.9.6 dev` +/// `0x000906023 == 0.9.6b beta 3` +/// `0x00090605f == 0.9.6e release` +#[corresponds(OpenSSL_version_num)] +pub fn number() -> i64 { + unsafe { OpenSSL_version_num() as i64 } +} + +/// The text variant of the version number and the release date. For example, "OpenSSL 0.9.5a 1 Apr 2000". +#[corresponds(OpenSSL_version)] +pub fn version() -> &'static str { + unsafe { + CStr::from_ptr(OpenSSL_version(OPENSSL_VERSION)) + .to_str() + .unwrap() + } +} + +/// The compiler flags set for the compilation process in the form "compiler: ..." if available or +/// "compiler: information not available" otherwise. +#[corresponds(OpenSSL_version)] +pub fn c_flags() -> &'static str { + unsafe { + CStr::from_ptr(OpenSSL_version(OPENSSL_CFLAGS)) + .to_str() + .unwrap() + } +} + +/// The date of the build process in the form "built on: ..." if available or "built on: date not available" otherwise. +#[corresponds(OpenSSL_version)] +pub fn built_on() -> &'static str { + unsafe { + CStr::from_ptr(OpenSSL_version(OPENSSL_BUILT_ON)) + .to_str() + .unwrap() + } +} + +/// The "Configure" target of the library build in the form "platform: ..." if available or "platform: information not available" otherwise. +#[corresponds(OpenSSL_version)] +pub fn platform() -> &'static str { + unsafe { + CStr::from_ptr(OpenSSL_version(OPENSSL_PLATFORM)) + .to_str() + .unwrap() + } +} + +/// The "OPENSSLDIR" setting of the library build in the form "OPENSSLDIR: "..."" if available or "OPENSSLDIR: N/A" otherwise. +#[corresponds(OpenSSL_version)] +pub fn dir() -> &'static str { + unsafe { + CStr::from_ptr(OpenSSL_version(OPENSSL_DIR)) + .to_str() + .unwrap() + } +} + +/// This test ensures that we do not segfault when calling the functions of this module +/// and that the strings respect a reasonable format. +#[test] +fn test_versions() { + println!("Number: '{}'", number()); + println!("Version: '{}'", version()); + println!("C flags: '{}'", c_flags()); + println!("Built on: '{}'", built_on()); + println!("Platform: '{}'", platform()); + println!("Dir: '{}'", dir()); + + #[cfg(not(any(libressl, boringssl)))] + fn expected_name() -> &'static str { + "OpenSSL" + } + #[cfg(libressl)] + fn expected_name() -> &'static str { + "LibreSSL" + } + #[cfg(boringssl)] + fn expected_name() -> &'static str { + "BoringSSL" + } + + assert!(number() > 0); + assert!(version().starts_with(expected_name())); + assert!(c_flags().starts_with("compiler:")); + // some distributions patch out dates out of openssl so that the builds are reproducible + if !built_on().is_empty() { + assert!(built_on().starts_with("built on:")); + } + assert!(dir().starts_with("OPENSSLDIR:")); +} diff --git a/vendor/openssl/src/x509/extension.rs b/vendor/openssl/src/x509/extension.rs new file mode 100644 index 0000000..11e0151 --- /dev/null +++ b/vendor/openssl/src/x509/extension.rs @@ -0,0 +1,562 @@ +//! Add extensions to an `X509` certificate or certificate request. +//! +//! The extensions defined for X.509 v3 certificates provide methods for +//! associating additional attributes with users or public keys and for +//! managing relationships between CAs. The extensions created using this +//! module can be used with `X509v3Context` objects. +//! +//! # Example +//! +//! ```rust +//! use openssl::x509::extension::BasicConstraints; +//! use openssl::x509::X509Extension; +//! +//! let mut bc = BasicConstraints::new(); +//! let bc = bc.critical().ca().pathlen(1); +//! +//! let extension: X509Extension = bc.build().unwrap(); +//! ``` +use std::fmt::Write; + +use crate::asn1::Asn1Object; +use crate::error::ErrorStack; +use crate::nid::Nid; +use crate::x509::{GeneralName, Stack, X509Extension, X509v3Context}; +use foreign_types::ForeignType; + +/// An extension which indicates whether a certificate is a CA certificate. +pub struct BasicConstraints { + critical: bool, + ca: bool, + pathlen: Option<u32>, +} + +impl Default for BasicConstraints { + fn default() -> BasicConstraints { + BasicConstraints::new() + } +} + +impl BasicConstraints { + /// Construct a new `BasicConstraints` extension. + pub fn new() -> BasicConstraints { + BasicConstraints { + critical: false, + ca: false, + pathlen: None, + } + } + + /// Sets the `critical` flag to `true`. The extension will be critical. + pub fn critical(&mut self) -> &mut BasicConstraints { + self.critical = true; + self + } + + /// Sets the `ca` flag to `true`. + pub fn ca(&mut self) -> &mut BasicConstraints { + self.ca = true; + self + } + + /// Sets the `pathlen` to an optional non-negative value. The `pathlen` is the + /// maximum number of CAs that can appear below this one in a chain. + pub fn pathlen(&mut self, pathlen: u32) -> &mut BasicConstraints { + self.pathlen = Some(pathlen); + self + } + + /// Return the `BasicConstraints` extension as an `X509Extension`. + // Temporarily silence the deprecation warning - this should be ported to + // `X509Extension::new_internal`. + #[allow(deprecated)] + pub fn build(&self) -> Result<X509Extension, ErrorStack> { + let mut value = String::new(); + if self.critical { + value.push_str("critical,"); + } + value.push_str("CA:"); + if self.ca { + value.push_str("TRUE"); + } else { + value.push_str("FALSE"); + } + if let Some(pathlen) = self.pathlen { + write!(value, ",pathlen:{}", pathlen).unwrap(); + } + X509Extension::new_nid(None, None, Nid::BASIC_CONSTRAINTS, &value) + } +} + +/// An extension consisting of a list of names of the permitted key usages. +pub struct KeyUsage { + critical: bool, + digital_signature: bool, + non_repudiation: bool, + key_encipherment: bool, + data_encipherment: bool, + key_agreement: bool, + key_cert_sign: bool, + crl_sign: bool, + encipher_only: bool, + decipher_only: bool, +} + +impl Default for KeyUsage { + fn default() -> KeyUsage { + KeyUsage::new() + } +} + +impl KeyUsage { + /// Construct a new `KeyUsage` extension. + pub fn new() -> KeyUsage { + KeyUsage { + critical: false, + digital_signature: false, + non_repudiation: false, + key_encipherment: false, + data_encipherment: false, + key_agreement: false, + key_cert_sign: false, + crl_sign: false, + encipher_only: false, + decipher_only: false, + } + } + + /// Sets the `critical` flag to `true`. The extension will be critical. + pub fn critical(&mut self) -> &mut KeyUsage { + self.critical = true; + self + } + + /// Sets the `digitalSignature` flag to `true`. + pub fn digital_signature(&mut self) -> &mut KeyUsage { + self.digital_signature = true; + self + } + + /// Sets the `nonRepudiation` flag to `true`. + pub fn non_repudiation(&mut self) -> &mut KeyUsage { + self.non_repudiation = true; + self + } + + /// Sets the `keyEncipherment` flag to `true`. + pub fn key_encipherment(&mut self) -> &mut KeyUsage { + self.key_encipherment = true; + self + } + + /// Sets the `dataEncipherment` flag to `true`. + pub fn data_encipherment(&mut self) -> &mut KeyUsage { + self.data_encipherment = true; + self + } + + /// Sets the `keyAgreement` flag to `true`. + pub fn key_agreement(&mut self) -> &mut KeyUsage { + self.key_agreement = true; + self + } + + /// Sets the `keyCertSign` flag to `true`. + pub fn key_cert_sign(&mut self) -> &mut KeyUsage { + self.key_cert_sign = true; + self + } + + /// Sets the `cRLSign` flag to `true`. + pub fn crl_sign(&mut self) -> &mut KeyUsage { + self.crl_sign = true; + self + } + + /// Sets the `encipherOnly` flag to `true`. + pub fn encipher_only(&mut self) -> &mut KeyUsage { + self.encipher_only = true; + self + } + + /// Sets the `decipherOnly` flag to `true`. + pub fn decipher_only(&mut self) -> &mut KeyUsage { + self.decipher_only = true; + self + } + + /// Return the `KeyUsage` extension as an `X509Extension`. + // Temporarily silence the deprecation warning - this should be ported to + // `X509Extension::new_internal`. + #[allow(deprecated)] + pub fn build(&self) -> Result<X509Extension, ErrorStack> { + let mut value = String::new(); + let mut first = true; + append(&mut value, &mut first, self.critical, "critical"); + append( + &mut value, + &mut first, + self.digital_signature, + "digitalSignature", + ); + append( + &mut value, + &mut first, + self.non_repudiation, + "nonRepudiation", + ); + append( + &mut value, + &mut first, + self.key_encipherment, + "keyEncipherment", + ); + append( + &mut value, + &mut first, + self.data_encipherment, + "dataEncipherment", + ); + append(&mut value, &mut first, self.key_agreement, "keyAgreement"); + append(&mut value, &mut first, self.key_cert_sign, "keyCertSign"); + append(&mut value, &mut first, self.crl_sign, "cRLSign"); + append(&mut value, &mut first, self.encipher_only, "encipherOnly"); + append(&mut value, &mut first, self.decipher_only, "decipherOnly"); + X509Extension::new_nid(None, None, Nid::KEY_USAGE, &value) + } +} + +/// An extension consisting of a list of usages indicating purposes +/// for which the certificate public key can be used for. +pub struct ExtendedKeyUsage { + critical: bool, + items: Vec<String>, +} + +impl Default for ExtendedKeyUsage { + fn default() -> ExtendedKeyUsage { + ExtendedKeyUsage::new() + } +} + +impl ExtendedKeyUsage { + /// Construct a new `ExtendedKeyUsage` extension. + pub fn new() -> ExtendedKeyUsage { + ExtendedKeyUsage { + critical: false, + items: vec![], + } + } + + /// Sets the `critical` flag to `true`. The extension will be critical. + pub fn critical(&mut self) -> &mut ExtendedKeyUsage { + self.critical = true; + self + } + + /// Sets the `serverAuth` flag to `true`. + pub fn server_auth(&mut self) -> &mut ExtendedKeyUsage { + self.other("serverAuth") + } + + /// Sets the `clientAuth` flag to `true`. + pub fn client_auth(&mut self) -> &mut ExtendedKeyUsage { + self.other("clientAuth") + } + + /// Sets the `codeSigning` flag to `true`. + pub fn code_signing(&mut self) -> &mut ExtendedKeyUsage { + self.other("codeSigning") + } + + /// Sets the `emailProtection` flag to `true`. + pub fn email_protection(&mut self) -> &mut ExtendedKeyUsage { + self.other("emailProtection") + } + + /// Sets the `timeStamping` flag to `true`. + pub fn time_stamping(&mut self) -> &mut ExtendedKeyUsage { + self.other("timeStamping") + } + + /// Sets the `msCodeInd` flag to `true`. + pub fn ms_code_ind(&mut self) -> &mut ExtendedKeyUsage { + self.other("msCodeInd") + } + + /// Sets the `msCodeCom` flag to `true`. + pub fn ms_code_com(&mut self) -> &mut ExtendedKeyUsage { + self.other("msCodeCom") + } + + /// Sets the `msCTLSign` flag to `true`. + pub fn ms_ctl_sign(&mut self) -> &mut ExtendedKeyUsage { + self.other("msCTLSign") + } + + /// Sets the `msSGC` flag to `true`. + pub fn ms_sgc(&mut self) -> &mut ExtendedKeyUsage { + self.other("msSGC") + } + + /// Sets the `msEFS` flag to `true`. + pub fn ms_efs(&mut self) -> &mut ExtendedKeyUsage { + self.other("msEFS") + } + + /// Sets the `nsSGC` flag to `true`. + pub fn ns_sgc(&mut self) -> &mut ExtendedKeyUsage { + self.other("nsSGC") + } + + /// Sets a flag not already defined. + pub fn other(&mut self, other: &str) -> &mut ExtendedKeyUsage { + self.items.push(other.to_string()); + self + } + + /// Return the `ExtendedKeyUsage` extension as an `X509Extension`. + pub fn build(&self) -> Result<X509Extension, ErrorStack> { + let mut stack = Stack::new()?; + for item in &self.items { + stack.push(Asn1Object::from_str(item)?)?; + } + unsafe { + X509Extension::new_internal(Nid::EXT_KEY_USAGE, self.critical, stack.as_ptr().cast()) + } + } +} + +/// An extension that provides a means of identifying certificates that contain a +/// particular public key. +pub struct SubjectKeyIdentifier { + critical: bool, +} + +impl Default for SubjectKeyIdentifier { + fn default() -> SubjectKeyIdentifier { + SubjectKeyIdentifier::new() + } +} + +impl SubjectKeyIdentifier { + /// Construct a new `SubjectKeyIdentifier` extension. + pub fn new() -> SubjectKeyIdentifier { + SubjectKeyIdentifier { critical: false } + } + + /// Sets the `critical` flag to `true`. The extension will be critical. + pub fn critical(&mut self) -> &mut SubjectKeyIdentifier { + self.critical = true; + self + } + + /// Return a `SubjectKeyIdentifier` extension as an `X509Extension`. + // Temporarily silence the deprecation warning - this should be ported to + // `X509Extension::new_internal`. + #[allow(deprecated)] + pub fn build(&self, ctx: &X509v3Context<'_>) -> Result<X509Extension, ErrorStack> { + let mut value = String::new(); + let mut first = true; + append(&mut value, &mut first, self.critical, "critical"); + append(&mut value, &mut first, true, "hash"); + X509Extension::new_nid(None, Some(ctx), Nid::SUBJECT_KEY_IDENTIFIER, &value) + } +} + +/// An extension that provides a means of identifying the public key corresponding +/// to the private key used to sign a CRL. +pub struct AuthorityKeyIdentifier { + critical: bool, + keyid: Option<bool>, + issuer: Option<bool>, +} + +impl Default for AuthorityKeyIdentifier { + fn default() -> AuthorityKeyIdentifier { + AuthorityKeyIdentifier::new() + } +} + +impl AuthorityKeyIdentifier { + /// Construct a new `AuthorityKeyIdentifier` extension. + pub fn new() -> AuthorityKeyIdentifier { + AuthorityKeyIdentifier { + critical: false, + keyid: None, + issuer: None, + } + } + + /// Sets the `critical` flag to `true`. The extension will be critical. + pub fn critical(&mut self) -> &mut AuthorityKeyIdentifier { + self.critical = true; + self + } + + /// Sets the `keyid` flag. + pub fn keyid(&mut self, always: bool) -> &mut AuthorityKeyIdentifier { + self.keyid = Some(always); + self + } + + /// Sets the `issuer` flag. + pub fn issuer(&mut self, always: bool) -> &mut AuthorityKeyIdentifier { + self.issuer = Some(always); + self + } + + /// Return a `AuthorityKeyIdentifier` extension as an `X509Extension`. + // Temporarily silence the deprecation warning - this should be ported to + // `X509Extension::new_internal`. + #[allow(deprecated)] + pub fn build(&self, ctx: &X509v3Context<'_>) -> Result<X509Extension, ErrorStack> { + let mut value = String::new(); + let mut first = true; + append(&mut value, &mut first, self.critical, "critical"); + match self.keyid { + Some(true) => append(&mut value, &mut first, true, "keyid:always"), + Some(false) => append(&mut value, &mut first, true, "keyid"), + None => {} + } + match self.issuer { + Some(true) => append(&mut value, &mut first, true, "issuer:always"), + Some(false) => append(&mut value, &mut first, true, "issuer"), + None => {} + } + X509Extension::new_nid(None, Some(ctx), Nid::AUTHORITY_KEY_IDENTIFIER, &value) + } +} + +enum RustGeneralName { + Dns(String), + Email(String), + Uri(String), + Ip(String), + Rid(String), + OtherName(Asn1Object, Vec<u8>), +} + +/// An extension that allows additional identities to be bound to the subject +/// of the certificate. +pub struct SubjectAlternativeName { + critical: bool, + items: Vec<RustGeneralName>, +} + +impl Default for SubjectAlternativeName { + fn default() -> SubjectAlternativeName { + SubjectAlternativeName::new() + } +} + +impl SubjectAlternativeName { + /// Construct a new `SubjectAlternativeName` extension. + pub fn new() -> SubjectAlternativeName { + SubjectAlternativeName { + critical: false, + items: vec![], + } + } + + /// Sets the `critical` flag to `true`. The extension will be critical. + pub fn critical(&mut self) -> &mut SubjectAlternativeName { + self.critical = true; + self + } + + /// Sets the `email` flag. + pub fn email(&mut self, email: &str) -> &mut SubjectAlternativeName { + self.items.push(RustGeneralName::Email(email.to_string())); + self + } + + /// Sets the `uri` flag. + pub fn uri(&mut self, uri: &str) -> &mut SubjectAlternativeName { + self.items.push(RustGeneralName::Uri(uri.to_string())); + self + } + + /// Sets the `dns` flag. + pub fn dns(&mut self, dns: &str) -> &mut SubjectAlternativeName { + self.items.push(RustGeneralName::Dns(dns.to_string())); + self + } + + /// Sets the `rid` flag. + pub fn rid(&mut self, rid: &str) -> &mut SubjectAlternativeName { + self.items.push(RustGeneralName::Rid(rid.to_string())); + self + } + + /// Sets the `ip` flag. + pub fn ip(&mut self, ip: &str) -> &mut SubjectAlternativeName { + self.items.push(RustGeneralName::Ip(ip.to_string())); + self + } + + /// Sets the `dirName` flag. + /// + /// Not currently actually supported, always panics. + #[deprecated = "dir_name is deprecated and always panics. Please file a bug if you have a use case for this."] + pub fn dir_name(&mut self, _dir_name: &str) -> &mut SubjectAlternativeName { + unimplemented!( + "This has not yet been adapted for the new internals. File a bug if you need this." + ); + } + + /// Sets the `otherName` flag. + /// + /// Not currently actually supported, always panics. Please use other_name2 + #[deprecated = "other_name is deprecated and always panics. Please use other_name2."] + pub fn other_name(&mut self, _other_name: &str) -> &mut SubjectAlternativeName { + unimplemented!("This has not yet been adapted for the new internals. Use other_name2."); + } + + /// Sets the `otherName` flag. + /// + /// `content` must be a valid der encoded ASN1_TYPE + /// + /// If you want to add just a ia5string use `other_name_ia5string` + pub fn other_name2(&mut self, oid: Asn1Object, content: &[u8]) -> &mut SubjectAlternativeName { + self.items + .push(RustGeneralName::OtherName(oid, content.into())); + self + } + + /// Return a `SubjectAlternativeName` extension as an `X509Extension`. + pub fn build(&self, _ctx: &X509v3Context<'_>) -> Result<X509Extension, ErrorStack> { + let mut stack = Stack::new()?; + for item in &self.items { + let gn = match item { + RustGeneralName::Dns(s) => GeneralName::new_dns(s.as_bytes())?, + RustGeneralName::Email(s) => GeneralName::new_email(s.as_bytes())?, + RustGeneralName::Uri(s) => GeneralName::new_uri(s.as_bytes())?, + RustGeneralName::Ip(s) => { + GeneralName::new_ip(s.parse().map_err(|_| ErrorStack::get())?)? + } + RustGeneralName::Rid(s) => GeneralName::new_rid(Asn1Object::from_str(s)?)?, + RustGeneralName::OtherName(oid, content) => { + GeneralName::new_other_name(oid.clone(), content)? + } + }; + stack.push(gn)?; + } + + unsafe { + X509Extension::new_internal(Nid::SUBJECT_ALT_NAME, self.critical, stack.as_ptr().cast()) + } + } +} + +fn append(value: &mut String, first: &mut bool, should: bool, element: &str) { + if !should { + return; + } + + if !*first { + value.push(','); + } + *first = false; + value.push_str(element); +} diff --git a/vendor/openssl/src/x509/mod.rs b/vendor/openssl/src/x509/mod.rs new file mode 100644 index 0000000..24605df --- /dev/null +++ b/vendor/openssl/src/x509/mod.rs @@ -0,0 +1,2535 @@ +//! The standard defining the format of public key certificates. +//! +//! An `X509` certificate binds an identity to a public key, and is either +//! signed by a certificate authority (CA) or self-signed. An entity that gets +//! a hold of a certificate can both verify your identity (via a CA) and encrypt +//! data with the included public key. `X509` certificates are used in many +//! Internet protocols, including SSL/TLS, which is the basis for HTTPS, +//! the secure protocol for browsing the web. + +use cfg_if::cfg_if; +use foreign_types::{ForeignType, ForeignTypeRef, Opaque}; +use libc::{c_int, c_long, c_uint, c_void}; +use std::cmp::{self, Ordering}; +use std::convert::{TryFrom, TryInto}; +use std::error::Error; +use std::ffi::{CStr, CString}; +use std::fmt; +use std::marker::PhantomData; +use std::mem; +use std::net::IpAddr; +use std::path::Path; +use std::ptr; +use std::slice; +use std::str; + +use crate::asn1::{ + Asn1BitStringRef, Asn1Enumerated, Asn1IntegerRef, Asn1Object, Asn1ObjectRef, + Asn1OctetStringRef, Asn1StringRef, Asn1TimeRef, Asn1Type, +}; +use crate::bio::MemBioSlice; +use crate::conf::ConfRef; +use crate::error::ErrorStack; +use crate::ex_data::Index; +use crate::hash::{DigestBytes, MessageDigest}; +use crate::nid::Nid; +use crate::pkey::{HasPrivate, HasPublic, PKey, PKeyRef, Public}; +use crate::ssl::SslRef; +use crate::stack::{Stack, StackRef, Stackable}; +use crate::string::OpensslString; +use crate::util::{ForeignTypeExt, ForeignTypeRefExt}; +use crate::{cvt, cvt_n, cvt_p}; +use openssl_macros::corresponds; + +#[cfg(any(ossl102, libressl261))] +pub mod verify; + +pub mod extension; +pub mod store; + +#[cfg(test)] +mod tests; + +/// A type of X509 extension. +/// +/// # Safety +/// The value of NID and Output must match those in OpenSSL so that +/// `Output::from_ptr_opt(*_get_ext_d2i(*, NID, ...))` is valid. +pub unsafe trait ExtensionType { + const NID: Nid; + type Output: ForeignType; +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::X509_STORE_CTX; + fn drop = ffi::X509_STORE_CTX_free; + + /// An `X509` certificate store context. + pub struct X509StoreContext; + + /// A reference to an [`X509StoreContext`]. + pub struct X509StoreContextRef; +} + +impl X509StoreContext { + /// Returns the index which can be used to obtain a reference to the `Ssl` associated with a + /// context. + #[corresponds(SSL_get_ex_data_X509_STORE_CTX_idx)] + pub fn ssl_idx() -> Result<Index<X509StoreContext, SslRef>, ErrorStack> { + unsafe { cvt_n(ffi::SSL_get_ex_data_X509_STORE_CTX_idx()).map(|idx| Index::from_raw(idx)) } + } + + /// Creates a new `X509StoreContext` instance. + #[corresponds(X509_STORE_CTX_new)] + pub fn new() -> Result<X509StoreContext, ErrorStack> { + unsafe { + ffi::init(); + cvt_p(ffi::X509_STORE_CTX_new()).map(X509StoreContext) + } + } +} + +impl X509StoreContextRef { + /// Returns application data pertaining to an `X509` store context. + #[corresponds(X509_STORE_CTX_get_ex_data)] + pub fn ex_data<T>(&self, index: Index<X509StoreContext, T>) -> Option<&T> { + unsafe { + let data = ffi::X509_STORE_CTX_get_ex_data(self.as_ptr(), index.as_raw()); + if data.is_null() { + None + } else { + Some(&*(data as *const T)) + } + } + } + + /// Returns the error code of the context. + #[corresponds(X509_STORE_CTX_get_error)] + pub fn error(&self) -> X509VerifyResult { + unsafe { X509VerifyResult::from_raw(ffi::X509_STORE_CTX_get_error(self.as_ptr())) } + } + + /// Initializes this context with the given certificate, certificates chain and certificate + /// store. After initializing the context, the `with_context` closure is called with the prepared + /// context. As long as the closure is running, the context stays initialized and can be used + /// to e.g. verify a certificate. The context will be cleaned up, after the closure finished. + /// + /// * `trust` - The certificate store with the trusted certificates. + /// * `cert` - The certificate that should be verified. + /// * `cert_chain` - The certificates chain. + /// * `with_context` - The closure that is called with the initialized context. + /// + /// This corresponds to [`X509_STORE_CTX_init`] before calling `with_context` and to + /// [`X509_STORE_CTX_cleanup`] after calling `with_context`. + /// + /// [`X509_STORE_CTX_init`]: https://www.openssl.org/docs/manmaster/crypto/X509_STORE_CTX_init.html + /// [`X509_STORE_CTX_cleanup`]: https://www.openssl.org/docs/manmaster/crypto/X509_STORE_CTX_cleanup.html + pub fn init<F, T>( + &mut self, + trust: &store::X509StoreRef, + cert: &X509Ref, + cert_chain: &StackRef<X509>, + with_context: F, + ) -> Result<T, ErrorStack> + where + F: FnOnce(&mut X509StoreContextRef) -> Result<T, ErrorStack>, + { + struct Cleanup<'a>(&'a mut X509StoreContextRef); + + impl<'a> Drop for Cleanup<'a> { + fn drop(&mut self) { + unsafe { + ffi::X509_STORE_CTX_cleanup(self.0.as_ptr()); + } + } + } + + unsafe { + cvt(ffi::X509_STORE_CTX_init( + self.as_ptr(), + trust.as_ptr(), + cert.as_ptr(), + cert_chain.as_ptr(), + ))?; + + let cleanup = Cleanup(self); + with_context(cleanup.0) + } + } + + /// Verifies the stored certificate. + /// + /// Returns `true` if verification succeeds. The `error` method will return the specific + /// validation error if the certificate was not valid. + /// + /// This will only work inside of a call to `init`. + #[corresponds(X509_verify_cert)] + pub fn verify_cert(&mut self) -> Result<bool, ErrorStack> { + unsafe { cvt_n(ffi::X509_verify_cert(self.as_ptr())).map(|n| n != 0) } + } + + /// Set the error code of the context. + #[corresponds(X509_STORE_CTX_set_error)] + pub fn set_error(&mut self, result: X509VerifyResult) { + unsafe { + ffi::X509_STORE_CTX_set_error(self.as_ptr(), result.as_raw()); + } + } + + /// Returns a reference to the certificate which caused the error or None if + /// no certificate is relevant to the error. + #[corresponds(X509_STORE_CTX_get_current_cert)] + pub fn current_cert(&self) -> Option<&X509Ref> { + unsafe { + let ptr = ffi::X509_STORE_CTX_get_current_cert(self.as_ptr()); + X509Ref::from_const_ptr_opt(ptr) + } + } + + /// Returns a non-negative integer representing the depth in the certificate + /// chain where the error occurred. If it is zero it occurred in the end + /// entity certificate, one if it is the certificate which signed the end + /// entity certificate and so on. + #[corresponds(X509_STORE_CTX_get_error_depth)] + pub fn error_depth(&self) -> u32 { + unsafe { ffi::X509_STORE_CTX_get_error_depth(self.as_ptr()) as u32 } + } + + /// Returns a reference to a complete valid `X509` certificate chain. + #[corresponds(X509_STORE_CTX_get0_chain)] + pub fn chain(&self) -> Option<&StackRef<X509>> { + unsafe { + let chain = X509_STORE_CTX_get0_chain(self.as_ptr()); + + if chain.is_null() { + None + } else { + Some(StackRef::from_ptr(chain)) + } + } + } +} + +/// A builder used to construct an `X509`. +pub struct X509Builder(X509); + +impl X509Builder { + /// Creates a new builder. + #[corresponds(X509_new)] + pub fn new() -> Result<X509Builder, ErrorStack> { + unsafe { + ffi::init(); + cvt_p(ffi::X509_new()).map(|p| X509Builder(X509(p))) + } + } + + /// Sets the notAfter constraint on the certificate. + #[corresponds(X509_set1_notAfter)] + pub fn set_not_after(&mut self, not_after: &Asn1TimeRef) -> Result<(), ErrorStack> { + unsafe { cvt(X509_set1_notAfter(self.0.as_ptr(), not_after.as_ptr())).map(|_| ()) } + } + + /// Sets the notBefore constraint on the certificate. + #[corresponds(X509_set1_notBefore)] + pub fn set_not_before(&mut self, not_before: &Asn1TimeRef) -> Result<(), ErrorStack> { + unsafe { cvt(X509_set1_notBefore(self.0.as_ptr(), not_before.as_ptr())).map(|_| ()) } + } + + /// Sets the version of the certificate. + /// + /// Note that the version is zero-indexed; that is, a certificate corresponding to version 3 of + /// the X.509 standard should pass `2` to this method. + #[corresponds(X509_set_version)] + #[allow(clippy::useless_conversion)] + pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::X509_set_version(self.0.as_ptr(), version as c_long)).map(|_| ()) } + } + + /// Sets the serial number of the certificate. + #[corresponds(X509_set_serialNumber)] + pub fn set_serial_number(&mut self, serial_number: &Asn1IntegerRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::X509_set_serialNumber( + self.0.as_ptr(), + serial_number.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Sets the issuer name of the certificate. + #[corresponds(X509_set_issuer_name)] + pub fn set_issuer_name(&mut self, issuer_name: &X509NameRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::X509_set_issuer_name( + self.0.as_ptr(), + issuer_name.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Sets the subject name of the certificate. + /// + /// When building certificates, the `C`, `ST`, and `O` options are common when using the openssl command line tools. + /// The `CN` field is used for the common name, such as a DNS name. + /// + /// ``` + /// use openssl::x509::{X509, X509NameBuilder}; + /// + /// let mut x509_name = openssl::x509::X509NameBuilder::new().unwrap(); + /// x509_name.append_entry_by_text("C", "US").unwrap(); + /// x509_name.append_entry_by_text("ST", "CA").unwrap(); + /// x509_name.append_entry_by_text("O", "Some organization").unwrap(); + /// x509_name.append_entry_by_text("CN", "www.example.com").unwrap(); + /// let x509_name = x509_name.build(); + /// + /// let mut x509 = openssl::x509::X509::builder().unwrap(); + /// x509.set_subject_name(&x509_name).unwrap(); + /// ``` + #[corresponds(X509_set_subject_name)] + pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::X509_set_subject_name( + self.0.as_ptr(), + subject_name.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Sets the public key associated with the certificate. + #[corresponds(X509_set_pubkey)] + pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack> + where + T: HasPublic, + { + unsafe { cvt(ffi::X509_set_pubkey(self.0.as_ptr(), key.as_ptr())).map(|_| ()) } + } + + /// Returns a context object which is needed to create certain X509 extension values. + /// + /// Set `issuer` to `None` if the certificate will be self-signed. + #[corresponds(X509V3_set_ctx)] + pub fn x509v3_context<'a>( + &'a self, + issuer: Option<&'a X509Ref>, + conf: Option<&'a ConfRef>, + ) -> X509v3Context<'a> { + unsafe { + let mut ctx = mem::zeroed(); + + let issuer = match issuer { + Some(issuer) => issuer.as_ptr(), + None => self.0.as_ptr(), + }; + let subject = self.0.as_ptr(); + ffi::X509V3_set_ctx( + &mut ctx, + issuer, + subject, + ptr::null_mut(), + ptr::null_mut(), + 0, + ); + + // nodb case taken care of since we zeroed ctx above + if let Some(conf) = conf { + ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr()); + } + + X509v3Context(ctx, PhantomData) + } + } + + /// Adds an X509 extension value to the certificate. + /// + /// This works just as `append_extension` except it takes ownership of the `X509Extension`. + pub fn append_extension(&mut self, extension: X509Extension) -> Result<(), ErrorStack> { + self.append_extension2(&extension) + } + + /// Adds an X509 extension value to the certificate. + #[corresponds(X509_add_ext)] + pub fn append_extension2(&mut self, extension: &X509ExtensionRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::X509_add_ext(self.0.as_ptr(), extension.as_ptr(), -1))?; + Ok(()) + } + } + + /// Signs the certificate with a private key. + #[corresponds(X509_sign)] + pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack> + where + T: HasPrivate, + { + unsafe { cvt(ffi::X509_sign(self.0.as_ptr(), key.as_ptr(), hash.as_ptr())).map(|_| ()) } + } + + /// Consumes the builder, returning the certificate. + pub fn build(self) -> X509 { + self.0 + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::X509; + fn drop = ffi::X509_free; + + /// An `X509` public key certificate. + pub struct X509; + /// Reference to `X509`. + pub struct X509Ref; +} + +#[cfg(boringssl)] +type X509LenTy = c_uint; +#[cfg(not(boringssl))] +type X509LenTy = c_int; + +impl X509Ref { + /// Returns this certificate's subject name. + #[corresponds(X509_get_subject_name)] + pub fn subject_name(&self) -> &X509NameRef { + unsafe { + let name = ffi::X509_get_subject_name(self.as_ptr()); + X509NameRef::from_const_ptr_opt(name).expect("subject name must not be null") + } + } + + /// Returns the hash of the certificates subject + #[corresponds(X509_subject_name_hash)] + pub fn subject_name_hash(&self) -> u32 { + #[allow(clippy::unnecessary_cast)] + unsafe { + ffi::X509_subject_name_hash(self.as_ptr()) as u32 + } + } + + /// Returns this certificate's issuer name. + #[corresponds(X509_get_issuer_name)] + pub fn issuer_name(&self) -> &X509NameRef { + unsafe { + let name = ffi::X509_get_issuer_name(self.as_ptr()); + X509NameRef::from_const_ptr_opt(name).expect("issuer name must not be null") + } + } + + /// Returns the hash of the certificates issuer + #[corresponds(X509_issuer_name_hash)] + pub fn issuer_name_hash(&self) -> u32 { + #[allow(clippy::unnecessary_cast)] + unsafe { + ffi::X509_issuer_name_hash(self.as_ptr()) as u32 + } + } + + /// Returns this certificate's subject alternative name entries, if they exist. + #[corresponds(X509_get_ext_d2i)] + pub fn subject_alt_names(&self) -> Option<Stack<GeneralName>> { + unsafe { + let stack = ffi::X509_get_ext_d2i( + self.as_ptr(), + ffi::NID_subject_alt_name, + ptr::null_mut(), + ptr::null_mut(), + ); + Stack::from_ptr_opt(stack as *mut _) + } + } + + /// Returns this certificate's CRL distribution points, if they exist. + #[corresponds(X509_get_ext_d2i)] + pub fn crl_distribution_points(&self) -> Option<Stack<DistPoint>> { + unsafe { + let stack = ffi::X509_get_ext_d2i( + self.as_ptr(), + ffi::NID_crl_distribution_points, + ptr::null_mut(), + ptr::null_mut(), + ); + Stack::from_ptr_opt(stack as *mut _) + } + } + + /// Returns this certificate's issuer alternative name entries, if they exist. + #[corresponds(X509_get_ext_d2i)] + pub fn issuer_alt_names(&self) -> Option<Stack<GeneralName>> { + unsafe { + let stack = ffi::X509_get_ext_d2i( + self.as_ptr(), + ffi::NID_issuer_alt_name, + ptr::null_mut(), + ptr::null_mut(), + ); + Stack::from_ptr_opt(stack as *mut _) + } + } + + /// Returns this certificate's [`authority information access`] entries, if they exist. + /// + /// [`authority information access`]: https://tools.ietf.org/html/rfc5280#section-4.2.2.1 + #[corresponds(X509_get_ext_d2i)] + pub fn authority_info(&self) -> Option<Stack<AccessDescription>> { + unsafe { + let stack = ffi::X509_get_ext_d2i( + self.as_ptr(), + ffi::NID_info_access, + ptr::null_mut(), + ptr::null_mut(), + ); + Stack::from_ptr_opt(stack as *mut _) + } + } + + /// Retrieves the path length extension from a certificate, if it exists. + #[corresponds(X509_get_pathlen)] + #[cfg(ossl110)] + pub fn pathlen(&self) -> Option<u32> { + let v = unsafe { ffi::X509_get_pathlen(self.as_ptr()) }; + u32::try_from(v).ok() + } + + /// Returns this certificate's subject key id, if it exists. + #[corresponds(X509_get0_subject_key_id)] + #[cfg(ossl110)] + pub fn subject_key_id(&self) -> Option<&Asn1OctetStringRef> { + unsafe { + let data = ffi::X509_get0_subject_key_id(self.as_ptr()); + Asn1OctetStringRef::from_const_ptr_opt(data) + } + } + + /// Returns this certificate's authority key id, if it exists. + #[corresponds(X509_get0_authority_key_id)] + #[cfg(ossl110)] + pub fn authority_key_id(&self) -> Option<&Asn1OctetStringRef> { + unsafe { + let data = ffi::X509_get0_authority_key_id(self.as_ptr()); + Asn1OctetStringRef::from_const_ptr_opt(data) + } + } + + /// Returns this certificate's authority issuer name entries, if they exist. + #[corresponds(X509_get0_authority_issuer)] + #[cfg(ossl111d)] + pub fn authority_issuer(&self) -> Option<&StackRef<GeneralName>> { + unsafe { + let stack = ffi::X509_get0_authority_issuer(self.as_ptr()); + StackRef::from_const_ptr_opt(stack) + } + } + + /// Returns this certificate's authority serial number, if it exists. + #[corresponds(X509_get0_authority_serial)] + #[cfg(ossl111d)] + pub fn authority_serial(&self) -> Option<&Asn1IntegerRef> { + unsafe { + let r = ffi::X509_get0_authority_serial(self.as_ptr()); + Asn1IntegerRef::from_const_ptr_opt(r) + } + } + + #[corresponds(X509_get_pubkey)] + pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> { + unsafe { + let pkey = cvt_p(ffi::X509_get_pubkey(self.as_ptr()))?; + Ok(PKey::from_ptr(pkey)) + } + } + + /// Returns a digest of the DER representation of the certificate. + #[corresponds(X509_digest)] + pub fn digest(&self, hash_type: MessageDigest) -> Result<DigestBytes, ErrorStack> { + unsafe { + let mut digest = DigestBytes { + buf: [0; ffi::EVP_MAX_MD_SIZE as usize], + len: ffi::EVP_MAX_MD_SIZE as usize, + }; + let mut len = ffi::EVP_MAX_MD_SIZE as c_uint; + cvt(ffi::X509_digest( + self.as_ptr(), + hash_type.as_ptr(), + digest.buf.as_mut_ptr() as *mut _, + &mut len, + ))?; + digest.len = len as usize; + + Ok(digest) + } + } + + #[deprecated(since = "0.10.9", note = "renamed to digest")] + pub fn fingerprint(&self, hash_type: MessageDigest) -> Result<Vec<u8>, ErrorStack> { + self.digest(hash_type).map(|b| b.to_vec()) + } + + /// Returns the certificate's Not After validity period. + #[corresponds(X509_getm_notAfter)] + pub fn not_after(&self) -> &Asn1TimeRef { + unsafe { + let date = X509_getm_notAfter(self.as_ptr()); + Asn1TimeRef::from_const_ptr_opt(date).expect("not_after must not be null") + } + } + + /// Returns the certificate's Not Before validity period. + #[corresponds(X509_getm_notBefore)] + pub fn not_before(&self) -> &Asn1TimeRef { + unsafe { + let date = X509_getm_notBefore(self.as_ptr()); + Asn1TimeRef::from_const_ptr_opt(date).expect("not_before must not be null") + } + } + + /// Returns the certificate's signature + #[corresponds(X509_get0_signature)] + pub fn signature(&self) -> &Asn1BitStringRef { + unsafe { + let mut signature = ptr::null(); + X509_get0_signature(&mut signature, ptr::null_mut(), self.as_ptr()); + Asn1BitStringRef::from_const_ptr_opt(signature).expect("signature must not be null") + } + } + + /// Returns the certificate's signature algorithm. + #[corresponds(X509_get0_signature)] + pub fn signature_algorithm(&self) -> &X509AlgorithmRef { + unsafe { + let mut algor = ptr::null(); + X509_get0_signature(ptr::null_mut(), &mut algor, self.as_ptr()); + X509AlgorithmRef::from_const_ptr_opt(algor) + .expect("signature algorithm must not be null") + } + } + + /// Returns the list of OCSP responder URLs specified in the certificate's Authority Information + /// Access field. + #[corresponds(X509_get1_ocsp)] + pub fn ocsp_responders(&self) -> Result<Stack<OpensslString>, ErrorStack> { + unsafe { cvt_p(ffi::X509_get1_ocsp(self.as_ptr())).map(|p| Stack::from_ptr(p)) } + } + + /// Checks that this certificate issued `subject`. + #[corresponds(X509_check_issued)] + pub fn issued(&self, subject: &X509Ref) -> X509VerifyResult { + unsafe { + let r = ffi::X509_check_issued(self.as_ptr(), subject.as_ptr()); + X509VerifyResult::from_raw(r) + } + } + + /// Returns certificate version. If this certificate has no explicit version set, it defaults to + /// version 1. + /// + /// Note that `0` return value stands for version 1, `1` for version 2 and so on. + #[corresponds(X509_get_version)] + #[cfg(ossl110)] + #[allow(clippy::unnecessary_cast)] + pub fn version(&self) -> i32 { + unsafe { ffi::X509_get_version(self.as_ptr()) as i32 } + } + + /// Check if the certificate is signed using the given public key. + /// + /// Only the signature is checked: no other checks (such as certificate chain validity) + /// are performed. + /// + /// Returns `true` if verification succeeds. + #[corresponds(X509_verify)] + pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack> + where + T: HasPublic, + { + unsafe { cvt_n(ffi::X509_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) } + } + + /// Returns this certificate's serial number. + #[corresponds(X509_get_serialNumber)] + pub fn serial_number(&self) -> &Asn1IntegerRef { + unsafe { + let r = ffi::X509_get_serialNumber(self.as_ptr()); + Asn1IntegerRef::from_const_ptr_opt(r).expect("serial number must not be null") + } + } + + to_pem! { + /// Serializes the certificate into a PEM-encoded X509 structure. + /// + /// The output will have a header of `-----BEGIN CERTIFICATE-----`. + #[corresponds(PEM_write_bio_X509)] + to_pem, + ffi::PEM_write_bio_X509 + } + + to_der! { + /// Serializes the certificate into a DER-encoded X509 structure. + #[corresponds(i2d_X509)] + to_der, + ffi::i2d_X509 + } + + to_pem! { + /// Converts the certificate to human readable text. + #[corresponds(X509_print)] + to_text, + ffi::X509_print + } +} + +impl ToOwned for X509Ref { + type Owned = X509; + + fn to_owned(&self) -> X509 { + unsafe { + X509_up_ref(self.as_ptr()); + X509::from_ptr(self.as_ptr()) + } + } +} + +impl Ord for X509Ref { + fn cmp(&self, other: &Self) -> cmp::Ordering { + // X509_cmp returns a number <0 for less than, 0 for equal and >0 for greater than. + // It can't fail if both pointers are valid, which we know is true. + let cmp = unsafe { ffi::X509_cmp(self.as_ptr(), other.as_ptr()) }; + cmp.cmp(&0) + } +} + +impl PartialOrd for X509Ref { + fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { + Some(self.cmp(other)) + } +} + +impl PartialOrd<X509> for X509Ref { + fn partial_cmp(&self, other: &X509) -> Option<cmp::Ordering> { + <X509Ref as PartialOrd<X509Ref>>::partial_cmp(self, other) + } +} + +impl PartialEq for X509Ref { + fn eq(&self, other: &Self) -> bool { + self.cmp(other) == cmp::Ordering::Equal + } +} + +impl PartialEq<X509> for X509Ref { + fn eq(&self, other: &X509) -> bool { + <X509Ref as PartialEq<X509Ref>>::eq(self, other) + } +} + +impl Eq for X509Ref {} + +impl X509 { + /// Returns a new builder. + pub fn builder() -> Result<X509Builder, ErrorStack> { + X509Builder::new() + } + + from_pem! { + /// Deserializes a PEM-encoded X509 structure. + /// + /// The input should have a header of `-----BEGIN CERTIFICATE-----`. + #[corresponds(PEM_read_bio_X509)] + from_pem, + X509, + ffi::PEM_read_bio_X509 + } + + from_der! { + /// Deserializes a DER-encoded X509 structure. + #[corresponds(d2i_X509)] + from_der, + X509, + ffi::d2i_X509 + } + + /// Deserializes a list of PEM-formatted certificates. + #[corresponds(PEM_read_bio_X509)] + pub fn stack_from_pem(pem: &[u8]) -> Result<Vec<X509>, ErrorStack> { + unsafe { + ffi::init(); + let bio = MemBioSlice::new(pem)?; + + let mut certs = vec![]; + loop { + let r = + ffi::PEM_read_bio_X509(bio.as_ptr(), ptr::null_mut(), None, ptr::null_mut()); + if r.is_null() { + let err = ffi::ERR_peek_last_error(); + if ffi::ERR_GET_LIB(err) as X509LenTy == ffi::ERR_LIB_PEM + && ffi::ERR_GET_REASON(err) == ffi::PEM_R_NO_START_LINE + { + ffi::ERR_clear_error(); + break; + } + + return Err(ErrorStack::get()); + } else { + certs.push(X509(r)); + } + } + + Ok(certs) + } + } +} + +impl Clone for X509 { + fn clone(&self) -> X509 { + X509Ref::to_owned(self) + } +} + +impl fmt::Debug for X509 { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + let serial = match &self.serial_number().to_bn() { + Ok(bn) => match bn.to_hex_str() { + Ok(hex) => hex.to_string(), + Err(_) => "".to_string(), + }, + Err(_) => "".to_string(), + }; + let mut debug_struct = formatter.debug_struct("X509"); + debug_struct.field("serial_number", &serial); + debug_struct.field("signature_algorithm", &self.signature_algorithm().object()); + debug_struct.field("issuer", &self.issuer_name()); + debug_struct.field("subject", &self.subject_name()); + if let Some(subject_alt_names) = &self.subject_alt_names() { + debug_struct.field("subject_alt_names", subject_alt_names); + } + debug_struct.field("not_before", &self.not_before()); + debug_struct.field("not_after", &self.not_after()); + + if let Ok(public_key) = &self.public_key() { + debug_struct.field("public_key", public_key); + }; + // TODO: Print extensions once they are supported on the X509 struct. + + debug_struct.finish() + } +} + +impl AsRef<X509Ref> for X509Ref { + fn as_ref(&self) -> &X509Ref { + self + } +} + +impl Stackable for X509 { + type StackType = ffi::stack_st_X509; +} + +impl Ord for X509 { + fn cmp(&self, other: &Self) -> cmp::Ordering { + X509Ref::cmp(self, other) + } +} + +impl PartialOrd for X509 { + fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { + X509Ref::partial_cmp(self, other) + } +} + +impl PartialOrd<X509Ref> for X509 { + fn partial_cmp(&self, other: &X509Ref) -> Option<cmp::Ordering> { + X509Ref::partial_cmp(self, other) + } +} + +impl PartialEq for X509 { + fn eq(&self, other: &Self) -> bool { + X509Ref::eq(self, other) + } +} + +impl PartialEq<X509Ref> for X509 { + fn eq(&self, other: &X509Ref) -> bool { + X509Ref::eq(self, other) + } +} + +impl Eq for X509 {} + +/// A context object required to construct certain `X509` extension values. +pub struct X509v3Context<'a>(ffi::X509V3_CTX, PhantomData<(&'a X509Ref, &'a ConfRef)>); + +impl<'a> X509v3Context<'a> { + pub fn as_ptr(&self) -> *mut ffi::X509V3_CTX { + &self.0 as *const _ as *mut _ + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::X509_EXTENSION; + fn drop = ffi::X509_EXTENSION_free; + + /// Permit additional fields to be added to an `X509` v3 certificate. + pub struct X509Extension; + /// Reference to `X509Extension`. + pub struct X509ExtensionRef; +} + +impl Stackable for X509Extension { + type StackType = ffi::stack_st_X509_EXTENSION; +} + +impl X509Extension { + /// Constructs an X509 extension value. See `man x509v3_config` for information on supported + /// names and their value formats. + /// + /// Some extension types, such as `subjectAlternativeName`, require an `X509v3Context` to be + /// provided. + /// + /// DO NOT CALL THIS WITH UNTRUSTED `value`: `value` is an OpenSSL + /// mini-language that can read arbitrary files. + /// + /// See the extension module for builder types which will construct certain common extensions. + /// + /// This function is deprecated, `X509Extension::new_from_der` or the + /// types in `x509::extension` should be used in its place. + #[deprecated( + note = "Use x509::extension types or new_from_der instead", + since = "0.10.51" + )] + pub fn new( + conf: Option<&ConfRef>, + context: Option<&X509v3Context<'_>>, + name: &str, + value: &str, + ) -> Result<X509Extension, ErrorStack> { + let name = CString::new(name).unwrap(); + let value = CString::new(value).unwrap(); + let mut ctx; + unsafe { + ffi::init(); + let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr); + let context_ptr = match context { + Some(c) => c.as_ptr(), + None => { + ctx = mem::zeroed(); + + ffi::X509V3_set_ctx( + &mut ctx, + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + 0, + ); + &mut ctx + } + }; + let name = name.as_ptr() as *mut _; + let value = value.as_ptr() as *mut _; + + cvt_p(ffi::X509V3_EXT_nconf(conf, context_ptr, name, value)).map(X509Extension) + } + } + + /// Constructs an X509 extension value. See `man x509v3_config` for information on supported + /// extensions and their value formats. + /// + /// Some extension types, such as `nid::SUBJECT_ALTERNATIVE_NAME`, require an `X509v3Context` to + /// be provided. + /// + /// DO NOT CALL THIS WITH UNTRUSTED `value`: `value` is an OpenSSL + /// mini-language that can read arbitrary files. + /// + /// See the extension module for builder types which will construct certain common extensions. + /// + /// This function is deprecated, `X509Extension::new_from_der` or the + /// types in `x509::extension` should be used in its place. + #[deprecated( + note = "Use x509::extension types or new_from_der instead", + since = "0.10.51" + )] + pub fn new_nid( + conf: Option<&ConfRef>, + context: Option<&X509v3Context<'_>>, + name: Nid, + value: &str, + ) -> Result<X509Extension, ErrorStack> { + let value = CString::new(value).unwrap(); + let mut ctx; + unsafe { + ffi::init(); + let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr); + let context_ptr = match context { + Some(c) => c.as_ptr(), + None => { + ctx = mem::zeroed(); + + ffi::X509V3_set_ctx( + &mut ctx, + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + 0, + ); + &mut ctx + } + }; + let name = name.as_raw(); + let value = value.as_ptr() as *mut _; + + cvt_p(ffi::X509V3_EXT_nconf_nid(conf, context_ptr, name, value)).map(X509Extension) + } + } + + /// Constructs a new X509 extension value from its OID, whether it's + /// critical, and its DER contents. + /// + /// The extent structure of the DER value will vary based on the + /// extension type, and can generally be found in the RFC defining the + /// extension. + /// + /// For common extension types, there are Rust APIs provided in + /// `openssl::x509::extensions` which are more ergonomic. + pub fn new_from_der( + oid: &Asn1ObjectRef, + critical: bool, + der_contents: &Asn1OctetStringRef, + ) -> Result<X509Extension, ErrorStack> { + unsafe { + cvt_p(ffi::X509_EXTENSION_create_by_OBJ( + ptr::null_mut(), + oid.as_ptr(), + critical as _, + der_contents.as_ptr(), + )) + .map(X509Extension) + } + } + + pub(crate) unsafe fn new_internal( + nid: Nid, + critical: bool, + value: *mut c_void, + ) -> Result<X509Extension, ErrorStack> { + ffi::init(); + cvt_p(ffi::X509V3_EXT_i2d(nid.as_raw(), critical as _, value)).map(X509Extension) + } + + /// Adds an alias for an extension + /// + /// # Safety + /// + /// This method modifies global state without locking and therefore is not thread safe + #[corresponds(X509V3_EXT_add_alias)] + #[deprecated( + note = "Use x509::extension types or new_from_der and then this is not necessary", + since = "0.10.51" + )] + pub unsafe fn add_alias(to: Nid, from: Nid) -> Result<(), ErrorStack> { + ffi::init(); + cvt(ffi::X509V3_EXT_add_alias(to.as_raw(), from.as_raw())).map(|_| ()) + } +} + +impl X509ExtensionRef { + to_der! { + /// Serializes the Extension to its standard DER encoding. + #[corresponds(i2d_X509_EXTENSION)] + to_der, + ffi::i2d_X509_EXTENSION + } +} + +/// A builder used to construct an `X509Name`. +pub struct X509NameBuilder(X509Name); + +impl X509NameBuilder { + /// Creates a new builder. + pub fn new() -> Result<X509NameBuilder, ErrorStack> { + unsafe { + ffi::init(); + cvt_p(ffi::X509_NAME_new()).map(|p| X509NameBuilder(X509Name(p))) + } + } + + /// Add a name entry + #[corresponds(X509_NAME_add_entry)] + #[cfg(any(ossl101, libressl350))] + pub fn append_entry(&mut self, ne: &X509NameEntryRef) -> std::result::Result<(), ErrorStack> { + unsafe { + cvt(ffi::X509_NAME_add_entry( + self.0.as_ptr(), + ne.as_ptr(), + -1, + 0, + )) + .map(|_| ()) + } + } + + /// Add a field entry by str. + /// + /// This corresponds to [`X509_NAME_add_entry_by_txt`]. + /// + /// [`X509_NAME_add_entry_by_txt`]: https://www.openssl.org/docs/manmaster/crypto/X509_NAME_add_entry_by_txt.html + pub fn append_entry_by_text(&mut self, field: &str, value: &str) -> Result<(), ErrorStack> { + unsafe { + let field = CString::new(field).unwrap(); + assert!(value.len() <= crate::SLenType::max_value() as usize); + cvt(ffi::X509_NAME_add_entry_by_txt( + self.0.as_ptr(), + field.as_ptr() as *mut _, + ffi::MBSTRING_UTF8, + value.as_ptr(), + value.len() as crate::SLenType, + -1, + 0, + )) + .map(|_| ()) + } + } + + /// Add a field entry by str with a specific type. + /// + /// This corresponds to [`X509_NAME_add_entry_by_txt`]. + /// + /// [`X509_NAME_add_entry_by_txt`]: https://www.openssl.org/docs/manmaster/crypto/X509_NAME_add_entry_by_txt.html + pub fn append_entry_by_text_with_type( + &mut self, + field: &str, + value: &str, + ty: Asn1Type, + ) -> Result<(), ErrorStack> { + unsafe { + let field = CString::new(field).unwrap(); + assert!(value.len() <= crate::SLenType::max_value() as usize); + cvt(ffi::X509_NAME_add_entry_by_txt( + self.0.as_ptr(), + field.as_ptr() as *mut _, + ty.as_raw(), + value.as_ptr(), + value.len() as crate::SLenType, + -1, + 0, + )) + .map(|_| ()) + } + } + + /// Add a field entry by NID. + /// + /// This corresponds to [`X509_NAME_add_entry_by_NID`]. + /// + /// [`X509_NAME_add_entry_by_NID`]: https://www.openssl.org/docs/manmaster/crypto/X509_NAME_add_entry_by_NID.html + pub fn append_entry_by_nid(&mut self, field: Nid, value: &str) -> Result<(), ErrorStack> { + unsafe { + assert!(value.len() <= crate::SLenType::max_value() as usize); + cvt(ffi::X509_NAME_add_entry_by_NID( + self.0.as_ptr(), + field.as_raw(), + ffi::MBSTRING_UTF8, + value.as_ptr() as *mut _, + value.len() as crate::SLenType, + -1, + 0, + )) + .map(|_| ()) + } + } + + /// Add a field entry by NID with a specific type. + /// + /// This corresponds to [`X509_NAME_add_entry_by_NID`]. + /// + /// [`X509_NAME_add_entry_by_NID`]: https://www.openssl.org/docs/manmaster/crypto/X509_NAME_add_entry_by_NID.html + pub fn append_entry_by_nid_with_type( + &mut self, + field: Nid, + value: &str, + ty: Asn1Type, + ) -> Result<(), ErrorStack> { + unsafe { + assert!(value.len() <= crate::SLenType::max_value() as usize); + cvt(ffi::X509_NAME_add_entry_by_NID( + self.0.as_ptr(), + field.as_raw(), + ty.as_raw(), + value.as_ptr() as *mut _, + value.len() as crate::SLenType, + -1, + 0, + )) + .map(|_| ()) + } + } + + /// Return an `X509Name`. + pub fn build(self) -> X509Name { + // Round-trip through bytes because OpenSSL is not const correct and + // names in a "modified" state compute various things lazily. This can + // lead to data-races because OpenSSL doesn't have locks or anything. + X509Name::from_der(&self.0.to_der().unwrap()).unwrap() + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::X509_NAME; + fn drop = ffi::X509_NAME_free; + + /// The names of an `X509` certificate. + pub struct X509Name; + /// Reference to `X509Name`. + pub struct X509NameRef; +} + +impl X509Name { + /// Returns a new builder. + pub fn builder() -> Result<X509NameBuilder, ErrorStack> { + X509NameBuilder::new() + } + + /// Loads subject names from a file containing PEM-formatted certificates. + /// + /// This is commonly used in conjunction with `SslContextBuilder::set_client_ca_list`. + pub fn load_client_ca_file<P: AsRef<Path>>(file: P) -> Result<Stack<X509Name>, ErrorStack> { + let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap(); + unsafe { cvt_p(ffi::SSL_load_client_CA_file(file.as_ptr())).map(|p| Stack::from_ptr(p)) } + } + + from_der! { + /// Deserializes a DER-encoded X509 name structure. + /// + /// This corresponds to [`d2i_X509_NAME`]. + /// + /// [`d2i_X509_NAME`]: https://www.openssl.org/docs/manmaster/man3/d2i_X509_NAME.html + from_der, + X509Name, + ffi::d2i_X509_NAME + } +} + +impl Stackable for X509Name { + type StackType = ffi::stack_st_X509_NAME; +} + +impl X509NameRef { + /// Returns the name entries by the nid. + pub fn entries_by_nid(&self, nid: Nid) -> X509NameEntries<'_> { + X509NameEntries { + name: self, + nid: Some(nid), + loc: -1, + } + } + + /// Returns an iterator over all `X509NameEntry` values + pub fn entries(&self) -> X509NameEntries<'_> { + X509NameEntries { + name: self, + nid: None, + loc: -1, + } + } + + /// Compare two names, like [`Ord`] but it may fail. + /// + /// With OpenSSL versions from 3.0.0 this may return an error if the underlying `X509_NAME_cmp` + /// call fails. + /// For OpenSSL versions before 3.0.0 it will never return an error, but due to a bug it may + /// spuriously return `Ordering::Less` if the `X509_NAME_cmp` call fails. + #[corresponds(X509_NAME_cmp)] + pub fn try_cmp(&self, other: &X509NameRef) -> Result<Ordering, ErrorStack> { + let cmp = unsafe { ffi::X509_NAME_cmp(self.as_ptr(), other.as_ptr()) }; + if cfg!(ossl300) && cmp == -2 { + return Err(ErrorStack::get()); + } + Ok(cmp.cmp(&0)) + } + + /// Copies the name to a new `X509Name`. + #[corresponds(X509_NAME_dup)] + #[cfg(any(boringssl, ossl110, libressl270))] + pub fn to_owned(&self) -> Result<X509Name, ErrorStack> { + unsafe { cvt_p(ffi::X509_NAME_dup(self.as_ptr())).map(|n| X509Name::from_ptr(n)) } + } + + to_der! { + /// Serializes the certificate into a DER-encoded X509 name structure. + /// + /// This corresponds to [`i2d_X509_NAME`]. + /// + /// [`i2d_X509_NAME`]: https://www.openssl.org/docs/manmaster/crypto/i2d_X509_NAME.html + to_der, + ffi::i2d_X509_NAME + } +} + +impl fmt::Debug for X509NameRef { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.debug_list().entries(self.entries()).finish() + } +} + +/// A type to destructure and examine an `X509Name`. +pub struct X509NameEntries<'a> { + name: &'a X509NameRef, + nid: Option<Nid>, + loc: c_int, +} + +impl<'a> Iterator for X509NameEntries<'a> { + type Item = &'a X509NameEntryRef; + + fn next(&mut self) -> Option<&'a X509NameEntryRef> { + unsafe { + match self.nid { + Some(nid) => { + // There is a `Nid` specified to search for + self.loc = + ffi::X509_NAME_get_index_by_NID(self.name.as_ptr(), nid.as_raw(), self.loc); + if self.loc == -1 { + return None; + } + } + None => { + // Iterate over all `Nid`s + self.loc += 1; + if self.loc >= ffi::X509_NAME_entry_count(self.name.as_ptr()) { + return None; + } + } + } + + let entry = ffi::X509_NAME_get_entry(self.name.as_ptr(), self.loc); + + Some(X509NameEntryRef::from_const_ptr_opt(entry).expect("entry must not be null")) + } + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::X509_NAME_ENTRY; + fn drop = ffi::X509_NAME_ENTRY_free; + + /// A name entry associated with a `X509Name`. + pub struct X509NameEntry; + /// Reference to `X509NameEntry`. + pub struct X509NameEntryRef; +} + +impl X509NameEntryRef { + /// Returns the field value of an `X509NameEntry`. + /// + /// This corresponds to [`X509_NAME_ENTRY_get_data`]. + /// + /// [`X509_NAME_ENTRY_get_data`]: https://www.openssl.org/docs/manmaster/crypto/X509_NAME_ENTRY_get_data.html + pub fn data(&self) -> &Asn1StringRef { + unsafe { + let data = ffi::X509_NAME_ENTRY_get_data(self.as_ptr()); + Asn1StringRef::from_ptr(data) + } + } + + /// Returns the `Asn1Object` value of an `X509NameEntry`. + /// This is useful for finding out about the actual `Nid` when iterating over all `X509NameEntries`. + /// + /// This corresponds to [`X509_NAME_ENTRY_get_object`]. + /// + /// [`X509_NAME_ENTRY_get_object`]: https://www.openssl.org/docs/manmaster/crypto/X509_NAME_ENTRY_get_object.html + pub fn object(&self) -> &Asn1ObjectRef { + unsafe { + let object = ffi::X509_NAME_ENTRY_get_object(self.as_ptr()); + Asn1ObjectRef::from_ptr(object) + } + } +} + +impl fmt::Debug for X509NameEntryRef { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_fmt(format_args!("{:?} = {:?}", self.object(), self.data())) + } +} + +/// A builder used to construct an `X509Req`. +pub struct X509ReqBuilder(X509Req); + +impl X509ReqBuilder { + /// Returns a builder for a certificate request. + /// + /// This corresponds to [`X509_REQ_new`]. + /// + ///[`X509_REQ_new`]: https://www.openssl.org/docs/manmaster/crypto/X509_REQ_new.html + pub fn new() -> Result<X509ReqBuilder, ErrorStack> { + unsafe { + ffi::init(); + cvt_p(ffi::X509_REQ_new()).map(|p| X509ReqBuilder(X509Req(p))) + } + } + + /// Set the numerical value of the version field. + /// + /// This corresponds to [`X509_REQ_set_version`]. + /// + ///[`X509_REQ_set_version`]: https://www.openssl.org/docs/manmaster/crypto/X509_REQ_set_version.html + #[allow(clippy::useless_conversion)] + pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::X509_REQ_set_version( + self.0.as_ptr(), + version as c_long, + )) + .map(|_| ()) + } + } + + /// Set the issuer name. + /// + /// This corresponds to [`X509_REQ_set_subject_name`]. + /// + /// [`X509_REQ_set_subject_name`]: https://www.openssl.org/docs/manmaster/crypto/X509_REQ_set_subject_name.html + pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::X509_REQ_set_subject_name( + self.0.as_ptr(), + subject_name.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Set the public key. + /// + /// This corresponds to [`X509_REQ_set_pubkey`]. + /// + /// [`X509_REQ_set_pubkey`]: https://www.openssl.org/docs/manmaster/crypto/X509_REQ_set_pubkey.html + pub fn set_pubkey<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack> + where + T: HasPublic, + { + unsafe { cvt(ffi::X509_REQ_set_pubkey(self.0.as_ptr(), key.as_ptr())).map(|_| ()) } + } + + /// Return an `X509v3Context`. This context object can be used to construct + /// certain `X509` extensions. + pub fn x509v3_context<'a>(&'a self, conf: Option<&'a ConfRef>) -> X509v3Context<'a> { + unsafe { + let mut ctx = mem::zeroed(); + + ffi::X509V3_set_ctx( + &mut ctx, + ptr::null_mut(), + ptr::null_mut(), + self.0.as_ptr(), + ptr::null_mut(), + 0, + ); + + // nodb case taken care of since we zeroed ctx above + if let Some(conf) = conf { + ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr()); + } + + X509v3Context(ctx, PhantomData) + } + } + + /// Permits any number of extension fields to be added to the certificate. + pub fn add_extensions( + &mut self, + extensions: &StackRef<X509Extension>, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::X509_REQ_add_extensions( + self.0.as_ptr(), + extensions.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Sign the request using a private key. + /// + /// This corresponds to [`X509_REQ_sign`]. + /// + /// [`X509_REQ_sign`]: https://www.openssl.org/docs/manmaster/crypto/X509_REQ_sign.html + pub fn sign<T>(&mut self, key: &PKeyRef<T>, hash: MessageDigest) -> Result<(), ErrorStack> + where + T: HasPrivate, + { + unsafe { + cvt(ffi::X509_REQ_sign( + self.0.as_ptr(), + key.as_ptr(), + hash.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Returns the `X509Req`. + pub fn build(self) -> X509Req { + self.0 + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::X509_REQ; + fn drop = ffi::X509_REQ_free; + + /// An `X509` certificate request. + pub struct X509Req; + /// Reference to `X509Req`. + pub struct X509ReqRef; +} + +impl X509Req { + /// A builder for `X509Req`. + pub fn builder() -> Result<X509ReqBuilder, ErrorStack> { + X509ReqBuilder::new() + } + + from_pem! { + /// Deserializes a PEM-encoded PKCS#10 certificate request structure. + /// + /// The input should have a header of `-----BEGIN CERTIFICATE REQUEST-----`. + /// + /// This corresponds to [`PEM_read_bio_X509_REQ`]. + /// + /// [`PEM_read_bio_X509_REQ`]: https://www.openssl.org/docs/manmaster/crypto/PEM_read_bio_X509_REQ.html + from_pem, + X509Req, + ffi::PEM_read_bio_X509_REQ + } + + from_der! { + /// Deserializes a DER-encoded PKCS#10 certificate request structure. + /// + /// This corresponds to [`d2i_X509_REQ`]. + /// + /// [`d2i_X509_REQ`]: https://www.openssl.org/docs/manmaster/crypto/d2i_X509_REQ.html + from_der, + X509Req, + ffi::d2i_X509_REQ + } +} + +impl X509ReqRef { + to_pem! { + /// Serializes the certificate request to a PEM-encoded PKCS#10 structure. + /// + /// The output will have a header of `-----BEGIN CERTIFICATE REQUEST-----`. + /// + /// This corresponds to [`PEM_write_bio_X509_REQ`]. + /// + /// [`PEM_write_bio_X509_REQ`]: https://www.openssl.org/docs/manmaster/crypto/PEM_write_bio_X509_REQ.html + to_pem, + ffi::PEM_write_bio_X509_REQ + } + + to_der! { + /// Serializes the certificate request to a DER-encoded PKCS#10 structure. + /// + /// This corresponds to [`i2d_X509_REQ`]. + /// + /// [`i2d_X509_REQ`]: https://www.openssl.org/docs/manmaster/crypto/i2d_X509_REQ.html + to_der, + ffi::i2d_X509_REQ + } + + to_pem! { + /// Converts the request to human readable text. + #[corresponds(X509_Req_print)] + to_text, + ffi::X509_REQ_print + } + + /// Returns the numerical value of the version field of the certificate request. + /// + /// This corresponds to [`X509_REQ_get_version`] + /// + /// [`X509_REQ_get_version`]: https://www.openssl.org/docs/manmaster/crypto/X509_REQ_get_version.html + #[allow(clippy::unnecessary_cast)] + pub fn version(&self) -> i32 { + unsafe { X509_REQ_get_version(self.as_ptr()) as i32 } + } + + /// Returns the subject name of the certificate request. + /// + /// This corresponds to [`X509_REQ_get_subject_name`] + /// + /// [`X509_REQ_get_subject_name`]: https://www.openssl.org/docs/manmaster/crypto/X509_REQ_get_subject_name.html + pub fn subject_name(&self) -> &X509NameRef { + unsafe { + let name = X509_REQ_get_subject_name(self.as_ptr()); + X509NameRef::from_const_ptr_opt(name).expect("subject name must not be null") + } + } + + /// Returns the public key of the certificate request. + /// + /// This corresponds to [`X509_REQ_get_pubkey"] + /// + /// [`X509_REQ_get_pubkey`]: https://www.openssl.org/docs/manmaster/crypto/X509_REQ_get_pubkey.html + pub fn public_key(&self) -> Result<PKey<Public>, ErrorStack> { + unsafe { + let key = cvt_p(ffi::X509_REQ_get_pubkey(self.as_ptr()))?; + Ok(PKey::from_ptr(key)) + } + } + + /// Check if the certificate request is signed using the given public key. + /// + /// Returns `true` if verification succeeds. + /// + /// This corresponds to [`X509_REQ_verify"]. + /// + /// [`X509_REQ_verify`]: https://www.openssl.org/docs/manmaster/crypto/X509_REQ_verify.html + pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack> + where + T: HasPublic, + { + unsafe { cvt_n(ffi::X509_REQ_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) } + } + + /// Returns the extensions of the certificate request. + /// + /// This corresponds to [`X509_REQ_get_extensions"] + pub fn extensions(&self) -> Result<Stack<X509Extension>, ErrorStack> { + unsafe { + let extensions = cvt_p(ffi::X509_REQ_get_extensions(self.as_ptr()))?; + Ok(Stack::from_ptr(extensions)) + } + } +} + +/// The reason that a certificate was revoked. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct CrlReason(c_int); + +#[allow(missing_docs)] // no need to document the constants +impl CrlReason { + pub const UNSPECIFIED: CrlReason = CrlReason(ffi::CRL_REASON_UNSPECIFIED); + pub const KEY_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_KEY_COMPROMISE); + pub const CA_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_CA_COMPROMISE); + pub const AFFILIATION_CHANGED: CrlReason = CrlReason(ffi::CRL_REASON_AFFILIATION_CHANGED); + pub const SUPERSEDED: CrlReason = CrlReason(ffi::CRL_REASON_SUPERSEDED); + pub const CESSATION_OF_OPERATION: CrlReason = CrlReason(ffi::CRL_REASON_CESSATION_OF_OPERATION); + pub const CERTIFICATE_HOLD: CrlReason = CrlReason(ffi::CRL_REASON_CERTIFICATE_HOLD); + pub const REMOVE_FROM_CRL: CrlReason = CrlReason(ffi::CRL_REASON_REMOVE_FROM_CRL); + pub const PRIVILEGE_WITHDRAWN: CrlReason = CrlReason(ffi::CRL_REASON_PRIVILEGE_WITHDRAWN); + pub const AA_COMPROMISE: CrlReason = CrlReason(ffi::CRL_REASON_AA_COMPROMISE); + + /// Constructs an `CrlReason` from a raw OpenSSL value. + pub const fn from_raw(value: c_int) -> Self { + CrlReason(value) + } + + /// Returns the raw OpenSSL value represented by this type. + pub const fn as_raw(&self) -> c_int { + self.0 + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::X509_REVOKED; + fn drop = ffi::X509_REVOKED_free; + + /// An `X509` certificate revocation status. + pub struct X509Revoked; + /// Reference to `X509Revoked`. + pub struct X509RevokedRef; +} + +impl Stackable for X509Revoked { + type StackType = ffi::stack_st_X509_REVOKED; +} + +impl X509Revoked { + from_der! { + /// Deserializes a DER-encoded certificate revocation status + #[corresponds(d2i_X509_REVOKED)] + from_der, + X509Revoked, + ffi::d2i_X509_REVOKED + } +} + +impl X509RevokedRef { + to_der! { + /// Serializes the certificate request to a DER-encoded certificate revocation status + #[corresponds(d2i_X509_REVOKED)] + to_der, + ffi::i2d_X509_REVOKED + } + + /// Copies the entry to a new `X509Revoked`. + #[corresponds(X509_NAME_dup)] + #[cfg(any(boringssl, ossl110, libressl270))] + pub fn to_owned(&self) -> Result<X509Revoked, ErrorStack> { + unsafe { cvt_p(ffi::X509_REVOKED_dup(self.as_ptr())).map(|n| X509Revoked::from_ptr(n)) } + } + + /// Get the date that the certificate was revoked + #[corresponds(X509_REVOKED_get0_revocationDate)] + pub fn revocation_date(&self) -> &Asn1TimeRef { + unsafe { + let r = X509_REVOKED_get0_revocationDate(self.as_ptr() as *const _); + assert!(!r.is_null()); + Asn1TimeRef::from_ptr(r as *mut _) + } + } + + /// Get the serial number of the revoked certificate + #[corresponds(X509_REVOKED_get0_serialNumber)] + pub fn serial_number(&self) -> &Asn1IntegerRef { + unsafe { + let r = X509_REVOKED_get0_serialNumber(self.as_ptr() as *const _); + assert!(!r.is_null()); + Asn1IntegerRef::from_ptr(r as *mut _) + } + } + + /// Get the criticality and value of an extension. + /// + /// This returns None if the extension is not present or occurs multiple times. + #[corresponds(X509_REVOKED_get_ext_d2i)] + pub fn extension<T: ExtensionType>(&self) -> Result<Option<(bool, T::Output)>, ErrorStack> { + let mut critical = -1; + let out = unsafe { + // SAFETY: self.as_ptr() is a valid pointer to an X509_REVOKED. + let ext = ffi::X509_REVOKED_get_ext_d2i( + self.as_ptr(), + T::NID.as_raw(), + &mut critical as *mut _, + ptr::null_mut(), + ); + // SAFETY: Extensions's contract promises that the type returned by + // OpenSSL here is T::Output. + T::Output::from_ptr_opt(ext as *mut _) + }; + match (critical, out) { + (0, Some(out)) => Ok(Some((false, out))), + (1, Some(out)) => Ok(Some((true, out))), + // -1 means the extension wasn't found, -2 means multiple were found. + (-1 | -2, _) => Ok(None), + // A critical value of 0 or 1 suggests success, but a null pointer + // was returned so something went wrong. + (0 | 1, None) => Err(ErrorStack::get()), + (c_int::MIN..=-2 | 2.., _) => panic!("OpenSSL should only return -2, -1, 0, or 1 for an extension's criticality but it returned {}", critical), + } + } +} + +/// The CRL entry extension identifying the reason for revocation see [`CrlReason`], +/// this is as defined in RFC 5280 Section 5.3.1. +pub enum ReasonCode {} + +// SAFETY: CertificateIssuer is defined to be a stack of GeneralName in the RFC +// and in OpenSSL. +unsafe impl ExtensionType for ReasonCode { + const NID: Nid = Nid::from_raw(ffi::NID_crl_reason); + + type Output = Asn1Enumerated; +} + +/// The CRL entry extension identifying the issuer of a certificate used in +/// indirect CRLs, as defined in RFC 5280 Section 5.3.3. +pub enum CertificateIssuer {} + +// SAFETY: CertificateIssuer is defined to be a stack of GeneralName in the RFC +// and in OpenSSL. +unsafe impl ExtensionType for CertificateIssuer { + const NID: Nid = Nid::from_raw(ffi::NID_certificate_issuer); + + type Output = Stack<GeneralName>; +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::X509_CRL; + fn drop = ffi::X509_CRL_free; + + /// An `X509` certificate revocation list. + pub struct X509Crl; + /// Reference to `X509Crl`. + pub struct X509CrlRef; +} + +/// The status of a certificate in a revoction list +/// +/// Corresponds to the return value from the [`X509_CRL_get0_by_*`] methods. +/// +/// [`X509_CRL_get0_by_*`]: https://www.openssl.org/docs/man1.1.0/man3/X509_CRL_get0_by_serial.html +pub enum CrlStatus<'a> { + /// The certificate is not present in the list + NotRevoked, + /// The certificate is in the list and is revoked + Revoked(&'a X509RevokedRef), + /// The certificate is in the list, but has the "removeFromCrl" status. + /// + /// This can occur if the certificate was revoked with the "CertificateHold" + /// reason, and has since been unrevoked. + RemoveFromCrl(&'a X509RevokedRef), +} + +impl<'a> CrlStatus<'a> { + // Helper used by the X509_CRL_get0_by_* methods to convert their return + // value to the status enum. + // Safety note: the returned CrlStatus must not outlive the owner of the + // revoked_entry pointer. + unsafe fn from_ffi_status( + status: c_int, + revoked_entry: *mut ffi::X509_REVOKED, + ) -> CrlStatus<'a> { + match status { + 0 => CrlStatus::NotRevoked, + 1 => { + assert!(!revoked_entry.is_null()); + CrlStatus::Revoked(X509RevokedRef::from_ptr(revoked_entry)) + } + 2 => { + assert!(!revoked_entry.is_null()); + CrlStatus::RemoveFromCrl(X509RevokedRef::from_ptr(revoked_entry)) + } + _ => unreachable!( + "{}", + "X509_CRL_get0_by_{{serial,cert}} should only return 0, 1, or 2." + ), + } + } +} + +impl X509Crl { + from_pem! { + /// Deserializes a PEM-encoded Certificate Revocation List + /// + /// The input should have a header of `-----BEGIN X509 CRL-----`. + #[corresponds(PEM_read_bio_X509_CRL)] + from_pem, + X509Crl, + ffi::PEM_read_bio_X509_CRL + } + + from_der! { + /// Deserializes a DER-encoded Certificate Revocation List + #[corresponds(d2i_X509_CRL)] + from_der, + X509Crl, + ffi::d2i_X509_CRL + } +} + +impl X509CrlRef { + to_pem! { + /// Serializes the certificate request to a PEM-encoded Certificate Revocation List. + /// + /// The output will have a header of `-----BEGIN X509 CRL-----`. + #[corresponds(PEM_write_bio_X509_CRL)] + to_pem, + ffi::PEM_write_bio_X509_CRL + } + + to_der! { + /// Serializes the certificate request to a DER-encoded Certificate Revocation List. + #[corresponds(i2d_X509_CRL)] + to_der, + ffi::i2d_X509_CRL + } + + /// Get the stack of revocation entries + pub fn get_revoked(&self) -> Option<&StackRef<X509Revoked>> { + unsafe { + let revoked = X509_CRL_get_REVOKED(self.as_ptr()); + if revoked.is_null() { + None + } else { + Some(StackRef::from_ptr(revoked)) + } + } + } + + /// Returns the CRL's `lastUpdate` time. + #[corresponds(X509_CRL_get0_lastUpdate)] + pub fn last_update(&self) -> &Asn1TimeRef { + unsafe { + let date = X509_CRL_get0_lastUpdate(self.as_ptr()); + assert!(!date.is_null()); + Asn1TimeRef::from_ptr(date as *mut _) + } + } + + /// Returns the CRL's `nextUpdate` time. + /// + /// If the `nextUpdate` field is missing, returns `None`. + #[corresponds(X509_CRL_get0_nextUpdate)] + pub fn next_update(&self) -> Option<&Asn1TimeRef> { + unsafe { + let date = X509_CRL_get0_nextUpdate(self.as_ptr()); + Asn1TimeRef::from_const_ptr_opt(date) + } + } + + /// Get the revocation status of a certificate by its serial number + #[corresponds(X509_CRL_get0_by_serial)] + pub fn get_by_serial<'a>(&'a self, serial: &Asn1IntegerRef) -> CrlStatus<'a> { + unsafe { + let mut ret = ptr::null_mut::<ffi::X509_REVOKED>(); + let status = + ffi::X509_CRL_get0_by_serial(self.as_ptr(), &mut ret as *mut _, serial.as_ptr()); + CrlStatus::from_ffi_status(status, ret) + } + } + + /// Get the revocation status of a certificate + #[corresponds(X509_CRL_get0_by_cert)] + pub fn get_by_cert<'a>(&'a self, cert: &X509) -> CrlStatus<'a> { + unsafe { + let mut ret = ptr::null_mut::<ffi::X509_REVOKED>(); + let status = + ffi::X509_CRL_get0_by_cert(self.as_ptr(), &mut ret as *mut _, cert.as_ptr()); + CrlStatus::from_ffi_status(status, ret) + } + } + + /// Get the issuer name from the revocation list. + #[corresponds(X509_CRL_get_issuer)] + pub fn issuer_name(&self) -> &X509NameRef { + unsafe { + let name = X509_CRL_get_issuer(self.as_ptr()); + assert!(!name.is_null()); + X509NameRef::from_ptr(name) + } + } + + /// Check if the CRL is signed using the given public key. + /// + /// Only the signature is checked: no other checks (such as certificate chain validity) + /// are performed. + /// + /// Returns `true` if verification succeeds. + #[corresponds(X509_CRL_verify)] + pub fn verify<T>(&self, key: &PKeyRef<T>) -> Result<bool, ErrorStack> + where + T: HasPublic, + { + unsafe { cvt_n(ffi::X509_CRL_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) } + } +} + +/// The result of peer certificate verification. +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct X509VerifyResult(c_int); + +impl fmt::Debug for X509VerifyResult { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("X509VerifyResult") + .field("code", &self.0) + .field("error", &self.error_string()) + .finish() + } +} + +impl fmt::Display for X509VerifyResult { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.write_str(self.error_string()) + } +} + +impl Error for X509VerifyResult {} + +impl X509VerifyResult { + /// Creates an `X509VerifyResult` from a raw error number. + /// + /// # Safety + /// + /// Some methods on `X509VerifyResult` are not thread safe if the error + /// number is invalid. + pub unsafe fn from_raw(err: c_int) -> X509VerifyResult { + X509VerifyResult(err) + } + + /// Return the integer representation of an `X509VerifyResult`. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn as_raw(&self) -> c_int { + self.0 + } + + /// Return a human readable error string from the verification error. + /// + /// This corresponds to [`X509_verify_cert_error_string`]. + /// + /// [`X509_verify_cert_error_string`]: https://www.openssl.org/docs/manmaster/crypto/X509_verify_cert_error_string.html + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn error_string(&self) -> &'static str { + ffi::init(); + + unsafe { + let s = ffi::X509_verify_cert_error_string(self.0 as c_long); + str::from_utf8(CStr::from_ptr(s).to_bytes()).unwrap() + } + } + + /// Successful peer certificate verification. + pub const OK: X509VerifyResult = X509VerifyResult(ffi::X509_V_OK); + /// Application verification failure. + pub const APPLICATION_VERIFICATION: X509VerifyResult = + X509VerifyResult(ffi::X509_V_ERR_APPLICATION_VERIFICATION); +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::GENERAL_NAME; + fn drop = ffi::GENERAL_NAME_free; + + /// An `X509` certificate alternative names. + pub struct GeneralName; + /// Reference to `GeneralName`. + pub struct GeneralNameRef; +} + +impl GeneralName { + unsafe fn new( + type_: c_int, + asn1_type: Asn1Type, + value: &[u8], + ) -> Result<GeneralName, ErrorStack> { + ffi::init(); + let gn = GeneralName::from_ptr(cvt_p(ffi::GENERAL_NAME_new())?); + (*gn.as_ptr()).type_ = type_; + let s = cvt_p(ffi::ASN1_STRING_type_new(asn1_type.as_raw()))?; + ffi::ASN1_STRING_set(s, value.as_ptr().cast(), value.len().try_into().unwrap()); + + #[cfg(boringssl)] + { + (*gn.as_ptr()).d.ptr = s.cast(); + } + #[cfg(not(boringssl))] + { + (*gn.as_ptr()).d = s.cast(); + } + + Ok(gn) + } + + pub(crate) fn new_email(email: &[u8]) -> Result<GeneralName, ErrorStack> { + unsafe { GeneralName::new(ffi::GEN_EMAIL, Asn1Type::IA5STRING, email) } + } + + pub(crate) fn new_dns(dns: &[u8]) -> Result<GeneralName, ErrorStack> { + unsafe { GeneralName::new(ffi::GEN_DNS, Asn1Type::IA5STRING, dns) } + } + + pub(crate) fn new_uri(uri: &[u8]) -> Result<GeneralName, ErrorStack> { + unsafe { GeneralName::new(ffi::GEN_URI, Asn1Type::IA5STRING, uri) } + } + + pub(crate) fn new_ip(ip: IpAddr) -> Result<GeneralName, ErrorStack> { + match ip { + IpAddr::V4(addr) => unsafe { + GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets()) + }, + IpAddr::V6(addr) => unsafe { + GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets()) + }, + } + } + + pub(crate) fn new_rid(oid: Asn1Object) -> Result<GeneralName, ErrorStack> { + unsafe { + ffi::init(); + let gn = cvt_p(ffi::GENERAL_NAME_new())?; + (*gn).type_ = ffi::GEN_RID; + + #[cfg(boringssl)] + { + (*gn).d.registeredID = oid.as_ptr(); + } + #[cfg(not(boringssl))] + { + (*gn).d = oid.as_ptr().cast(); + } + + mem::forget(oid); + + Ok(GeneralName::from_ptr(gn)) + } + } + + pub(crate) fn new_other_name( + oid: Asn1Object, + value: &Vec<u8>, + ) -> Result<GeneralName, ErrorStack> { + unsafe { + ffi::init(); + + let typ = cvt_p(ffi::d2i_ASN1_TYPE( + ptr::null_mut(), + &mut value.as_ptr().cast(), + value.len().try_into().unwrap(), + ))?; + + let gn = cvt_p(ffi::GENERAL_NAME_new())?; + (*gn).type_ = ffi::GEN_OTHERNAME; + + if let Err(e) = cvt(ffi::GENERAL_NAME_set0_othername( + gn, + oid.as_ptr().cast(), + typ, + )) { + ffi::GENERAL_NAME_free(gn); + return Err(e); + } + + mem::forget(oid); + + Ok(GeneralName::from_ptr(gn)) + } + } +} + +impl GeneralNameRef { + fn ia5_string(&self, ffi_type: c_int) -> Option<&str> { + unsafe { + if (*self.as_ptr()).type_ != ffi_type { + return None; + } + + #[cfg(boringssl)] + let d = (*self.as_ptr()).d.ptr; + #[cfg(not(boringssl))] + let d = (*self.as_ptr()).d; + + let ptr = ASN1_STRING_get0_data(d as *mut _); + let len = ffi::ASN1_STRING_length(d as *mut _); + + #[allow(clippy::unnecessary_cast)] + let slice = slice::from_raw_parts(ptr as *const u8, len as usize); + // IA5Strings are stated to be ASCII (specifically IA5). Hopefully + // OpenSSL checks that when loading a certificate but if not we'll + // use this instead of from_utf8_unchecked just in case. + str::from_utf8(slice).ok() + } + } + + /// Returns the contents of this `GeneralName` if it is an `rfc822Name`. + pub fn email(&self) -> Option<&str> { + self.ia5_string(ffi::GEN_EMAIL) + } + + /// Returns the contents of this `GeneralName` if it is a `directoryName`. + pub fn directory_name(&self) -> Option<&X509NameRef> { + unsafe { + if (*self.as_ptr()).type_ != ffi::GEN_DIRNAME { + return None; + } + + #[cfg(boringssl)] + let d = (*self.as_ptr()).d.ptr; + #[cfg(not(boringssl))] + let d = (*self.as_ptr()).d; + + Some(X509NameRef::from_const_ptr(d as *const _)) + } + } + + /// Returns the contents of this `GeneralName` if it is a `dNSName`. + pub fn dnsname(&self) -> Option<&str> { + self.ia5_string(ffi::GEN_DNS) + } + + /// Returns the contents of this `GeneralName` if it is an `uniformResourceIdentifier`. + pub fn uri(&self) -> Option<&str> { + self.ia5_string(ffi::GEN_URI) + } + + /// Returns the contents of this `GeneralName` if it is an `iPAddress`. + pub fn ipaddress(&self) -> Option<&[u8]> { + unsafe { + if (*self.as_ptr()).type_ != ffi::GEN_IPADD { + return None; + } + #[cfg(boringssl)] + let d: *const ffi::ASN1_STRING = std::mem::transmute((*self.as_ptr()).d); + #[cfg(not(boringssl))] + let d = (*self.as_ptr()).d; + + let ptr = ASN1_STRING_get0_data(d as *mut _); + let len = ffi::ASN1_STRING_length(d as *mut _); + + #[allow(clippy::unnecessary_cast)] + Some(slice::from_raw_parts(ptr as *const u8, len as usize)) + } + } +} + +impl fmt::Debug for GeneralNameRef { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(email) = self.email() { + formatter.write_str(email) + } else if let Some(dnsname) = self.dnsname() { + formatter.write_str(dnsname) + } else if let Some(uri) = self.uri() { + formatter.write_str(uri) + } else if let Some(ipaddress) = self.ipaddress() { + let address = <[u8; 16]>::try_from(ipaddress) + .map(IpAddr::from) + .or_else(|_| <[u8; 4]>::try_from(ipaddress).map(IpAddr::from)); + match address { + Ok(a) => fmt::Debug::fmt(&a, formatter), + Err(_) => fmt::Debug::fmt(ipaddress, formatter), + } + } else { + formatter.write_str("(empty)") + } + } +} + +impl Stackable for GeneralName { + type StackType = ffi::stack_st_GENERAL_NAME; +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::DIST_POINT; + fn drop = ffi::DIST_POINT_free; + + /// A `X509` distribution point. + pub struct DistPoint; + /// Reference to `DistPoint`. + pub struct DistPointRef; +} + +impl DistPointRef { + /// Returns the name of this distribution point if it exists + pub fn distpoint(&self) -> Option<&DistPointNameRef> { + unsafe { DistPointNameRef::from_const_ptr_opt((*self.as_ptr()).distpoint) } + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::DIST_POINT_NAME; + fn drop = ffi::DIST_POINT_NAME_free; + + /// A `X509` distribution point. + pub struct DistPointName; + /// Reference to `DistPointName`. + pub struct DistPointNameRef; +} + +impl DistPointNameRef { + /// Returns the contents of this DistPointName if it is a fullname. + pub fn fullname(&self) -> Option<&StackRef<GeneralName>> { + unsafe { + if (*self.as_ptr()).type_ != 0 { + return None; + } + StackRef::from_const_ptr_opt((*self.as_ptr()).name.fullname) + } + } +} + +impl Stackable for DistPoint { + type StackType = ffi::stack_st_DIST_POINT; +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::ACCESS_DESCRIPTION; + fn drop = ffi::ACCESS_DESCRIPTION_free; + + /// `AccessDescription` of certificate authority information. + pub struct AccessDescription; + /// Reference to `AccessDescription`. + pub struct AccessDescriptionRef; +} + +impl AccessDescriptionRef { + /// Returns the access method OID. + pub fn method(&self) -> &Asn1ObjectRef { + unsafe { Asn1ObjectRef::from_ptr((*self.as_ptr()).method) } + } + + // Returns the access location. + pub fn location(&self) -> &GeneralNameRef { + unsafe { GeneralNameRef::from_ptr((*self.as_ptr()).location) } + } +} + +impl Stackable for AccessDescription { + type StackType = ffi::stack_st_ACCESS_DESCRIPTION; +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::X509_ALGOR; + fn drop = ffi::X509_ALGOR_free; + + /// An `X509` certificate signature algorithm. + pub struct X509Algorithm; + /// Reference to `X509Algorithm`. + pub struct X509AlgorithmRef; +} + +impl X509AlgorithmRef { + /// Returns the ASN.1 OID of this algorithm. + pub fn object(&self) -> &Asn1ObjectRef { + unsafe { + let mut oid = ptr::null(); + X509_ALGOR_get0(&mut oid, ptr::null_mut(), ptr::null_mut(), self.as_ptr()); + Asn1ObjectRef::from_const_ptr_opt(oid).expect("algorithm oid must not be null") + } + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::X509_OBJECT; + fn drop = X509_OBJECT_free; + + /// An `X509` or an X509 certificate revocation list. + pub struct X509Object; + /// Reference to `X509Object` + pub struct X509ObjectRef; +} + +impl X509ObjectRef { + pub fn x509(&self) -> Option<&X509Ref> { + unsafe { + let ptr = X509_OBJECT_get0_X509(self.as_ptr()); + X509Ref::from_const_ptr_opt(ptr) + } + } +} + +impl Stackable for X509Object { + type StackType = ffi::stack_st_X509_OBJECT; +} + +cfg_if! { + if #[cfg(any(boringssl, ossl110, libressl273))] { + use ffi::{X509_getm_notAfter, X509_getm_notBefore, X509_up_ref, X509_get0_signature}; + } else { + #[allow(bad_style)] + unsafe fn X509_getm_notAfter(x: *mut ffi::X509) -> *mut ffi::ASN1_TIME { + (*(*(*x).cert_info).validity).notAfter + } + + #[allow(bad_style)] + unsafe fn X509_getm_notBefore(x: *mut ffi::X509) -> *mut ffi::ASN1_TIME { + (*(*(*x).cert_info).validity).notBefore + } + + #[allow(bad_style)] + unsafe fn X509_up_ref(x: *mut ffi::X509) { + ffi::CRYPTO_add_lock( + &mut (*x).references, + 1, + ffi::CRYPTO_LOCK_X509, + "mod.rs\0".as_ptr() as *const _, + line!() as c_int, + ); + } + + #[allow(bad_style)] + unsafe fn X509_get0_signature( + psig: *mut *const ffi::ASN1_BIT_STRING, + palg: *mut *const ffi::X509_ALGOR, + x: *const ffi::X509, + ) { + if !psig.is_null() { + *psig = (*x).signature; + } + if !palg.is_null() { + *palg = (*x).sig_alg; + } + } + } +} + +cfg_if! { + if #[cfg(any(boringssl, ossl110, libressl350))] { + use ffi::{ + X509_ALGOR_get0, ASN1_STRING_get0_data, X509_STORE_CTX_get0_chain, X509_set1_notAfter, + X509_set1_notBefore, X509_REQ_get_version, X509_REQ_get_subject_name, + }; + } else { + use ffi::{ + ASN1_STRING_data as ASN1_STRING_get0_data, + X509_STORE_CTX_get_chain as X509_STORE_CTX_get0_chain, + X509_set_notAfter as X509_set1_notAfter, + X509_set_notBefore as X509_set1_notBefore, + }; + + #[allow(bad_style)] + unsafe fn X509_REQ_get_version(x: *mut ffi::X509_REQ) -> ::libc::c_long { + ffi::ASN1_INTEGER_get((*(*x).req_info).version) + } + + #[allow(bad_style)] + unsafe fn X509_REQ_get_subject_name(x: *mut ffi::X509_REQ) -> *mut ::ffi::X509_NAME { + (*(*x).req_info).subject + } + + #[allow(bad_style)] + unsafe fn X509_ALGOR_get0( + paobj: *mut *const ffi::ASN1_OBJECT, + pptype: *mut c_int, + pval: *mut *mut ::libc::c_void, + alg: *const ffi::X509_ALGOR, + ) { + if !paobj.is_null() { + *paobj = (*alg).algorithm; + } + assert!(pptype.is_null()); + assert!(pval.is_null()); + } + } +} + +cfg_if! { + if #[cfg(any(ossl110, boringssl, libressl270))] { + use ffi::X509_OBJECT_get0_X509; + } else { + #[allow(bad_style)] + unsafe fn X509_OBJECT_get0_X509(x: *mut ffi::X509_OBJECT) -> *mut ffi::X509 { + if (*x).type_ == ffi::X509_LU_X509 { + (*x).data.x509 + } else { + ptr::null_mut() + } + } + } +} + +cfg_if! { + if #[cfg(any(ossl110, libressl350))] { + use ffi::X509_OBJECT_free; + } else if #[cfg(boringssl)] { + use ffi::X509_OBJECT_free_contents as X509_OBJECT_free; + } else { + #[allow(bad_style)] + unsafe fn X509_OBJECT_free(x: *mut ffi::X509_OBJECT) { + ffi::X509_OBJECT_free_contents(x); + ffi::CRYPTO_free(x as *mut libc::c_void); + } + } +} + +cfg_if! { + if #[cfg(any(ossl110, libressl350, boringssl))] { + use ffi::{ + X509_CRL_get_issuer, X509_CRL_get0_nextUpdate, X509_CRL_get0_lastUpdate, + X509_CRL_get_REVOKED, + X509_REVOKED_get0_revocationDate, X509_REVOKED_get0_serialNumber, + }; + } else { + #[allow(bad_style)] + unsafe fn X509_CRL_get0_lastUpdate(x: *const ffi::X509_CRL) -> *mut ffi::ASN1_TIME { + (*(*x).crl).lastUpdate + } + #[allow(bad_style)] + unsafe fn X509_CRL_get0_nextUpdate(x: *const ffi::X509_CRL) -> *mut ffi::ASN1_TIME { + (*(*x).crl).nextUpdate + } + #[allow(bad_style)] + unsafe fn X509_CRL_get_issuer(x: *const ffi::X509_CRL) -> *mut ffi::X509_NAME { + (*(*x).crl).issuer + } + #[allow(bad_style)] + unsafe fn X509_CRL_get_REVOKED(x: *const ffi::X509_CRL) -> *mut ffi::stack_st_X509_REVOKED { + (*(*x).crl).revoked + } + #[allow(bad_style)] + unsafe fn X509_REVOKED_get0_serialNumber(x: *const ffi::X509_REVOKED) -> *mut ffi::ASN1_INTEGER { + (*x).serialNumber + } + #[allow(bad_style)] + unsafe fn X509_REVOKED_get0_revocationDate(x: *const ffi::X509_REVOKED) -> *mut ffi::ASN1_TIME { + (*x).revocationDate + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct X509PurposeId(c_int); + +impl X509PurposeId { + pub const SSL_CLIENT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_CLIENT); + pub const SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SSL_SERVER); + pub const NS_SSL_SERVER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_NS_SSL_SERVER); + pub const SMIME_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_SIGN); + pub const SMIME_ENCRYPT: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_SMIME_ENCRYPT); + pub const CRL_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_CRL_SIGN); + pub const ANY: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_ANY); + pub const OCSP_HELPER: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_OCSP_HELPER); + pub const TIMESTAMP_SIGN: X509PurposeId = X509PurposeId(ffi::X509_PURPOSE_TIMESTAMP_SIGN); + + /// Constructs an `X509PurposeId` from a raw OpenSSL value. + pub fn from_raw(id: c_int) -> Self { + X509PurposeId(id) + } + + /// Returns the raw OpenSSL value represented by this type. + pub fn as_raw(&self) -> c_int { + self.0 + } +} + +/// A reference to an [`X509_PURPOSE`]. +pub struct X509PurposeRef(Opaque); + +/// Implements a wrapper type for the static `X509_PURPOSE` table in OpenSSL. +impl ForeignTypeRef for X509PurposeRef { + type CType = ffi::X509_PURPOSE; +} + +impl X509PurposeRef { + /// Get the internal table index of an X509_PURPOSE for a given short name. Valid short + /// names include + /// - "sslclient", + /// - "sslserver", + /// - "nssslserver", + /// - "smimesign", + /// - "smimeencrypt", + /// - "crlsign", + /// - "any", + /// - "ocsphelper", + /// - "timestampsign" + /// The index can be used with `X509PurposeRef::from_idx()` to get the purpose. + #[allow(clippy::unnecessary_cast)] + pub fn get_by_sname(sname: &str) -> Result<c_int, ErrorStack> { + unsafe { + let sname = CString::new(sname).unwrap(); + cfg_if! { + if #[cfg(any(ossl110, libressl280))] { + let purpose = cvt_n(ffi::X509_PURPOSE_get_by_sname(sname.as_ptr() as *const _))?; + } else { + let purpose = cvt_n(ffi::X509_PURPOSE_get_by_sname(sname.as_ptr() as *mut _))?; + } + } + Ok(purpose) + } + } + /// Get an `X509PurposeRef` for a given index value. The index can be obtained from e.g. + /// `X509PurposeRef::get_by_sname()`. + #[corresponds(X509_PURPOSE_get0)] + pub fn from_idx(idx: c_int) -> Result<&'static X509PurposeRef, ErrorStack> { + unsafe { + let ptr = cvt_p(ffi::X509_PURPOSE_get0(idx))?; + Ok(X509PurposeRef::from_ptr(ptr)) + } + } + + /// Get the purpose value from an X509Purpose structure. This value is one of + /// - `X509_PURPOSE_SSL_CLIENT` + /// - `X509_PURPOSE_SSL_SERVER` + /// - `X509_PURPOSE_NS_SSL_SERVER` + /// - `X509_PURPOSE_SMIME_SIGN` + /// - `X509_PURPOSE_SMIME_ENCRYPT` + /// - `X509_PURPOSE_CRL_SIGN` + /// - `X509_PURPOSE_ANY` + /// - `X509_PURPOSE_OCSP_HELPER` + /// - `X509_PURPOSE_TIMESTAMP_SIGN` + pub fn purpose(&self) -> X509PurposeId { + unsafe { + let x509_purpose: *mut ffi::X509_PURPOSE = self.as_ptr(); + X509PurposeId::from_raw((*x509_purpose).purpose) + } + } +} diff --git a/vendor/openssl/src/x509/store.rs b/vendor/openssl/src/x509/store.rs new file mode 100644 index 0000000..a90bf35 --- /dev/null +++ b/vendor/openssl/src/x509/store.rs @@ -0,0 +1,287 @@ +//! Describe a context in which to verify an `X509` certificate. +//! +//! The `X509` certificate store holds trusted CA certificates used to verify +//! peer certificates. +//! +//! # Example +//! +//! ```rust +//! use openssl::x509::store::{X509StoreBuilder, X509Store}; +//! use openssl::x509::{X509, X509Name}; +//! use openssl::asn1::Asn1Time; +//! use openssl::pkey::PKey; +//! use openssl::hash::MessageDigest; +//! use openssl::rsa::Rsa; +//! use openssl::nid::Nid; +//! +//! 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, "foobar.com").unwrap(); +//! let name = name.build(); +//! +//! // Sep 27th, 2016 +//! let sample_time = Asn1Time::from_unix(1474934400).unwrap(); +//! +//! let mut builder = X509::builder().unwrap(); +//! builder.set_version(2).unwrap(); +//! builder.set_subject_name(&name).unwrap(); +//! builder.set_issuer_name(&name).unwrap(); +//! builder.set_pubkey(&pkey).unwrap(); +//! builder.set_not_before(&sample_time); +//! builder.set_not_after(&sample_time); +//! builder.sign(&pkey, MessageDigest::sha256()).unwrap(); +//! +//! let certificate: X509 = builder.build(); +//! +//! let mut builder = X509StoreBuilder::new().unwrap(); +//! let _ = builder.add_cert(certificate); +//! +//! let store: X509Store = builder.build(); +//! ``` + +use cfg_if::cfg_if; +use foreign_types::ForeignTypeRef; +use std::mem; + +use crate::error::ErrorStack; +#[cfg(not(boringssl))] +use crate::ssl::SslFiletype; +use crate::stack::StackRef; +#[cfg(any(ossl102, libressl261))] +use crate::x509::verify::{X509VerifyFlags, X509VerifyParamRef}; +use crate::x509::{X509Object, X509PurposeId, X509}; +use crate::{cvt, cvt_p}; +use openssl_macros::corresponds; +#[cfg(not(boringssl))] +use std::ffi::CString; +#[cfg(not(boringssl))] +use std::path::Path; + +foreign_type_and_impl_send_sync! { + type CType = ffi::X509_STORE; + fn drop = ffi::X509_STORE_free; + + /// A builder type used to construct an `X509Store`. + pub struct X509StoreBuilder; + /// A reference to an [`X509StoreBuilder`]. + pub struct X509StoreBuilderRef; +} + +impl X509StoreBuilder { + /// Returns a builder for a certificate store. + /// + /// The store is initially empty. + #[corresponds(X509_STORE_new)] + pub fn new() -> Result<X509StoreBuilder, ErrorStack> { + unsafe { + ffi::init(); + + cvt_p(ffi::X509_STORE_new()).map(X509StoreBuilder) + } + } + + /// Constructs the `X509Store`. + pub fn build(self) -> X509Store { + let store = X509Store(self.0); + mem::forget(self); + store + } +} + +impl X509StoreBuilderRef { + /// Adds a certificate to the certificate store. + // FIXME should take an &X509Ref + #[corresponds(X509_STORE_add_cert)] + pub fn add_cert(&mut self, cert: X509) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::X509_STORE_add_cert(self.as_ptr(), cert.as_ptr())).map(|_| ()) } + } + + /// Load certificates from their default locations. + /// + /// These locations are read from the `SSL_CERT_FILE` and `SSL_CERT_DIR` + /// environment variables if present, or defaults specified at OpenSSL + /// build time otherwise. + #[corresponds(X509_STORE_set_default_paths)] + pub fn set_default_paths(&mut self) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::X509_STORE_set_default_paths(self.as_ptr())).map(|_| ()) } + } + + /// Adds a lookup method to the store. + #[corresponds(X509_STORE_add_lookup)] + pub fn add_lookup<T>( + &mut self, + method: &'static X509LookupMethodRef<T>, + ) -> Result<&mut X509LookupRef<T>, ErrorStack> { + let lookup = unsafe { ffi::X509_STORE_add_lookup(self.as_ptr(), method.as_ptr()) }; + cvt_p(lookup).map(|ptr| unsafe { X509LookupRef::from_ptr_mut(ptr) }) + } + + /// Sets certificate chain validation related flags. + #[corresponds(X509_STORE_set_flags)] + #[cfg(any(ossl102, libressl261))] + pub fn set_flags(&mut self, flags: X509VerifyFlags) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::X509_STORE_set_flags(self.as_ptr(), flags.bits())).map(|_| ()) } + } + + /// Sets the certificate purpose. + /// The purpose value can be obtained by `X509PurposeRef::get_by_sname()` + #[corresponds(X509_STORE_set_purpose)] + pub fn set_purpose(&mut self, purpose: X509PurposeId) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::X509_STORE_set_purpose(self.as_ptr(), purpose.as_raw())).map(|_| ()) } + } + + /// Sets certificate chain validation related parameters. + #[corresponds[X509_STORE_set1_param]] + #[cfg(any(ossl102, libressl261))] + pub fn set_param(&mut self, param: &X509VerifyParamRef) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::X509_STORE_set1_param(self.as_ptr(), param.as_ptr())).map(|_| ()) } + } +} + +generic_foreign_type_and_impl_send_sync! { + type CType = ffi::X509_LOOKUP; + fn drop = ffi::X509_LOOKUP_free; + + /// Information used by an `X509Store` to look up certificates and CRLs. + pub struct X509Lookup<T>; + /// A reference to an [`X509Lookup`]. + pub struct X509LookupRef<T>; +} + +/// Marker type corresponding to the [`X509_LOOKUP_hash_dir`] lookup method. +/// +/// [`X509_LOOKUP_hash_dir`]: https://www.openssl.org/docs/manmaster/crypto/X509_LOOKUP_hash_dir.html +// FIXME should be an enum +pub struct HashDir; + +impl X509Lookup<HashDir> { + /// Lookup method that loads certificates and CRLs on demand and caches + /// them in memory once they are loaded. It also checks for newer CRLs upon + /// each lookup, so that newer CRLs are used as soon as they appear in the + /// directory. + #[corresponds(X509_LOOKUP_hash_dir)] + pub fn hash_dir() -> &'static X509LookupMethodRef<HashDir> { + unsafe { X509LookupMethodRef::from_ptr(ffi::X509_LOOKUP_hash_dir()) } + } +} + +#[cfg(not(boringssl))] +impl X509LookupRef<HashDir> { + /// Specifies a directory from which certificates and CRLs will be loaded + /// on-demand. Must be used with `X509Lookup::hash_dir`. + #[corresponds(X509_LOOKUP_add_dir)] + pub fn add_dir(&mut self, name: &str, file_type: SslFiletype) -> Result<(), ErrorStack> { + let name = CString::new(name).unwrap(); + unsafe { + cvt(ffi::X509_LOOKUP_add_dir( + self.as_ptr(), + name.as_ptr(), + file_type.as_raw(), + )) + .map(|_| ()) + } + } +} + +/// Marker type corresponding to the [`X509_LOOKUP_file`] lookup method. +/// +/// [`X509_LOOKUP_file`]: https://www.openssl.org/docs/man1.1.1/man3/X509_LOOKUP_file.html +pub struct File; + +impl X509Lookup<File> { + /// Lookup method loads all the certificates or CRLs present in a file + /// into memory at the time the file is added as a lookup source. + #[corresponds(X509_LOOKUP_file)] + pub fn file() -> &'static X509LookupMethodRef<File> { + unsafe { X509LookupMethodRef::from_ptr(ffi::X509_LOOKUP_file()) } + } +} + +#[cfg(not(boringssl))] +impl X509LookupRef<File> { + /// Specifies a file from which certificates will be loaded + #[corresponds(X509_load_cert_file)] + // FIXME should return 'Result<i32, ErrorStack' like load_crl_file + pub fn load_cert_file<P: AsRef<Path>>( + &mut self, + file: P, + file_type: SslFiletype, + ) -> Result<(), ErrorStack> { + let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap(); + unsafe { + cvt(ffi::X509_load_cert_file( + self.as_ptr(), + file.as_ptr(), + file_type.as_raw(), + )) + .map(|_| ()) + } + } + + /// Specifies a file from which certificate revocation lists will be loaded + #[corresponds(X509_load_crl_file)] + pub fn load_crl_file<P: AsRef<Path>>( + &mut self, + file: P, + file_type: SslFiletype, + ) -> Result<i32, ErrorStack> { + let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap(); + unsafe { + cvt(ffi::X509_load_crl_file( + self.as_ptr(), + file.as_ptr(), + file_type.as_raw(), + )) + } + } +} + +generic_foreign_type_and_impl_send_sync! { + type CType = ffi::X509_LOOKUP_METHOD; + fn drop = X509_LOOKUP_meth_free; + + /// Method used to look up certificates and CRLs. + pub struct X509LookupMethod<T>; + /// A reference to an [`X509LookupMethod`]. + pub struct X509LookupMethodRef<T>; +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::X509_STORE; + fn drop = ffi::X509_STORE_free; + + /// A certificate store to hold trusted `X509` certificates. + pub struct X509Store; + /// Reference to an `X509Store`. + pub struct X509StoreRef; +} + +impl X509StoreRef { + /// Get a reference to the cache of certificates in this store. + #[corresponds(X509_STORE_get0_objects)] + pub fn objects(&self) -> &StackRef<X509Object> { + unsafe { StackRef::from_ptr(X509_STORE_get0_objects(self.as_ptr())) } + } +} + +cfg_if! { + if #[cfg(any(boringssl, ossl110, libressl270))] { + use ffi::X509_STORE_get0_objects; + } else { + #[allow(bad_style)] + unsafe fn X509_STORE_get0_objects(x: *mut ffi::X509_STORE) -> *mut ffi::stack_st_X509_OBJECT { + (*x).objs + } + } +} + +cfg_if! { + if #[cfg(ossl110)] { + use ffi::X509_LOOKUP_meth_free; + } else { + #[allow(bad_style)] + unsafe fn X509_LOOKUP_meth_free(_x: *mut ffi::X509_LOOKUP_METHOD) {} + } +} diff --git a/vendor/openssl/src/x509/tests.rs b/vendor/openssl/src/x509/tests.rs new file mode 100644 index 0000000..da3ce2f --- /dev/null +++ b/vendor/openssl/src/x509/tests.rs @@ -0,0 +1,1161 @@ +use std::cmp::Ordering; + +use crate::asn1::{Asn1Object, Asn1OctetString, Asn1Time}; +use crate::bn::{BigNum, MsbOption}; +use crate::hash::MessageDigest; +use crate::nid::Nid; +use crate::pkey::{PKey, Private}; +use crate::rsa::Rsa; +#[cfg(not(boringssl))] +use crate::ssl::SslFiletype; +use crate::stack::Stack; +use crate::x509::extension::{ + AuthorityKeyIdentifier, BasicConstraints, ExtendedKeyUsage, KeyUsage, SubjectAlternativeName, + SubjectKeyIdentifier, +}; +#[cfg(not(boringssl))] +use crate::x509::store::X509Lookup; +use crate::x509::store::X509StoreBuilder; +#[cfg(any(ossl102, libressl261))] +use crate::x509::verify::{X509VerifyFlags, X509VerifyParam}; +#[cfg(ossl102)] +use crate::x509::X509PurposeId; +#[cfg(any(ossl102, libressl261))] +use crate::x509::X509PurposeRef; +#[cfg(ossl110)] +use crate::x509::{CrlReason, X509Builder}; +use crate::x509::{ + CrlStatus, X509Crl, X509Extension, X509Name, X509Req, X509StoreContext, X509VerifyResult, X509, +}; + +#[cfg(ossl110)] +use foreign_types::ForeignType; +use hex::{self, FromHex}; +#[cfg(any(ossl102, libressl261))] +use libc::time_t; + +use super::{CertificateIssuer, ReasonCode}; + +fn pkey() -> PKey<Private> { + let rsa = Rsa::generate(2048).unwrap(); + PKey::from_rsa(rsa).unwrap() +} + +#[test] +fn test_cert_loading() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let fingerprint = cert.digest(MessageDigest::sha1()).unwrap(); + + let hash_str = "59172d9313e84459bcff27f967e79e6e9217e584"; + let hash_vec = Vec::from_hex(hash_str).unwrap(); + + assert_eq!(hash_vec, &*fingerprint); +} + +#[test] +fn test_debug() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let debugged = format!("{:#?}", cert); + #[cfg(boringssl)] + assert!(debugged.contains(r#"serial_number: "8771f7bdee982fa5""#)); + #[cfg(not(boringssl))] + assert!(debugged.contains(r#"serial_number: "8771F7BDEE982FA5""#)); + assert!(debugged.contains(r#"signature_algorithm: sha256WithRSAEncryption"#)); + assert!(debugged.contains(r#"countryName = "AU""#)); + assert!(debugged.contains(r#"stateOrProvinceName = "Some-State""#)); + assert!(debugged.contains(r#"not_before: Aug 14 17:00:03 2016 GMT"#)); + assert!(debugged.contains(r#"not_after: Aug 12 17:00:03 2026 GMT"#)); +} + +#[test] +fn test_cert_issue_validity() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let not_before = cert.not_before().to_string(); + let not_after = cert.not_after().to_string(); + + assert_eq!(not_before, "Aug 14 17:00:03 2016 GMT"); + assert_eq!(not_after, "Aug 12 17:00:03 2026 GMT"); +} + +#[test] +fn test_save_der() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + + let der = cert.to_der().unwrap(); + assert!(!der.is_empty()); +} + +#[test] +fn test_subject_read_cn() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let subject = cert.subject_name(); + let cn = subject.entries_by_nid(Nid::COMMONNAME).next().unwrap(); + assert_eq!(cn.data().as_slice(), b"foobar.com") +} + +#[test] +fn test_nid_values() { + let cert = include_bytes!("../../test/nid_test_cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let subject = cert.subject_name(); + + let cn = subject.entries_by_nid(Nid::COMMONNAME).next().unwrap(); + assert_eq!(cn.data().as_slice(), b"example.com"); + + let email = subject + .entries_by_nid(Nid::PKCS9_EMAILADDRESS) + .next() + .unwrap(); + assert_eq!(email.data().as_slice(), b"test@example.com"); + + let friendly = subject.entries_by_nid(Nid::FRIENDLYNAME).next().unwrap(); + assert_eq!(&**friendly.data().as_utf8().unwrap(), "Example"); +} + +#[test] +fn test_nameref_iterator() { + let cert = include_bytes!("../../test/nid_test_cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let subject = cert.subject_name(); + let mut all_entries = subject.entries(); + + let email = all_entries.next().unwrap(); + assert_eq!( + email.object().nid().as_raw(), + Nid::PKCS9_EMAILADDRESS.as_raw() + ); + assert_eq!(email.data().as_slice(), b"test@example.com"); + + let cn = all_entries.next().unwrap(); + assert_eq!(cn.object().nid().as_raw(), Nid::COMMONNAME.as_raw()); + assert_eq!(cn.data().as_slice(), b"example.com"); + + let friendly = all_entries.next().unwrap(); + assert_eq!(friendly.object().nid().as_raw(), Nid::FRIENDLYNAME.as_raw()); + assert_eq!(&**friendly.data().as_utf8().unwrap(), "Example"); + + if all_entries.next().is_some() { + panic!(); + } +} + +#[test] +fn test_nid_uid_value() { + let cert = include_bytes!("../../test/nid_uid_test_cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let subject = cert.subject_name(); + + let cn = subject.entries_by_nid(Nid::USERID).next().unwrap(); + assert_eq!(cn.data().as_slice(), b"this is the userId"); +} + +#[test] +fn test_subject_alt_name() { + let cert = include_bytes!("../../test/alt_name_cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + + let subject_alt_names = cert.subject_alt_names().unwrap(); + assert_eq!(5, subject_alt_names.len()); + assert_eq!(Some("example.com"), subject_alt_names[0].dnsname()); + assert_eq!(subject_alt_names[1].ipaddress(), Some(&[127, 0, 0, 1][..])); + assert_eq!( + subject_alt_names[2].ipaddress(), + Some(&b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01"[..]) + ); + assert_eq!(Some("test@example.com"), subject_alt_names[3].email()); + assert_eq!(Some("http://www.example.com"), subject_alt_names[4].uri()); +} + +#[test] +#[cfg(ossl110)] +fn test_retrieve_pathlen() { + let cert = include_bytes!("../../test/root-ca.pem"); + let cert = X509::from_pem(cert).unwrap(); + assert_eq!(cert.pathlen(), None); + + let cert = include_bytes!("../../test/intermediate-ca.pem"); + let cert = X509::from_pem(cert).unwrap(); + assert_eq!(cert.pathlen(), Some(0)); + + let cert = include_bytes!("../../test/alt_name_cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + assert_eq!(cert.pathlen(), None); +} + +#[test] +#[cfg(ossl110)] +fn test_subject_key_id() { + let cert = include_bytes!("../../test/certv3.pem"); + let cert = X509::from_pem(cert).unwrap(); + + let subject_key_id = cert.subject_key_id().unwrap(); + assert_eq!( + subject_key_id.as_slice(), + &b"\xB6\x73\x2F\x61\xA5\x4B\xA1\xEF\x48\x2C\x15\xB1\x9F\xF3\xDC\x34\x2F\xBC\xAC\x30"[..] + ); +} + +#[test] +#[cfg(ossl110)] +fn test_authority_key_id() { + let cert = include_bytes!("../../test/certv3.pem"); + let cert = X509::from_pem(cert).unwrap(); + + let authority_key_id = cert.authority_key_id().unwrap(); + assert_eq!( + authority_key_id.as_slice(), + &b"\x6C\xD3\xA5\x03\xAB\x0D\x5F\x2C\xC9\x8D\x8A\x9C\x88\xA7\x88\x77\xB8\x37\xFD\x9A"[..] + ); +} + +#[test] +#[cfg(ossl111d)] +fn test_authority_issuer_and_serial() { + let cert = include_bytes!("../../test/authority_key_identifier.pem"); + let cert = X509::from_pem(cert).unwrap(); + + let authority_issuer = cert.authority_issuer().unwrap(); + assert_eq!(1, authority_issuer.len()); + let dn = authority_issuer[0].directory_name().unwrap(); + let mut o = dn.entries_by_nid(Nid::ORGANIZATIONNAME); + let o = o.next().unwrap().data().as_utf8().unwrap(); + assert_eq!(o.as_bytes(), b"PyCA"); + let mut cn = dn.entries_by_nid(Nid::COMMONNAME); + let cn = cn.next().unwrap().data().as_utf8().unwrap(); + assert_eq!(cn.as_bytes(), b"cryptography.io"); + + let authority_serial = cert.authority_serial().unwrap(); + let serial = authority_serial.to_bn().unwrap(); + let expected = BigNum::from_u32(3).unwrap(); + assert_eq!(serial, expected); +} + +#[test] +fn test_subject_alt_name_iter() { + let cert = include_bytes!("../../test/alt_name_cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + + let subject_alt_names = cert.subject_alt_names().unwrap(); + let mut subject_alt_names_iter = subject_alt_names.iter(); + assert_eq!( + subject_alt_names_iter.next().unwrap().dnsname(), + Some("example.com") + ); + assert_eq!( + subject_alt_names_iter.next().unwrap().ipaddress(), + Some(&[127, 0, 0, 1][..]) + ); + assert_eq!( + subject_alt_names_iter.next().unwrap().ipaddress(), + Some(&b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01"[..]) + ); + assert_eq!( + subject_alt_names_iter.next().unwrap().email(), + Some("test@example.com") + ); + assert_eq!( + subject_alt_names_iter.next().unwrap().uri(), + Some("http://www.example.com") + ); + assert!(subject_alt_names_iter.next().is_none()); +} + +#[test] +fn test_aia_ca_issuer() { + // With AIA + let cert = include_bytes!("../../test/aia_test_cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let authority_info = cert.authority_info().unwrap(); + assert_eq!(authority_info.len(), 1); + assert_eq!(authority_info[0].method().to_string(), "CA Issuers"); + assert_eq!( + authority_info[0].location().uri(), + Some("http://www.example.com/cert.pem") + ); + // Without AIA + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + assert!(cert.authority_info().is_none()); +} + +#[test] +fn x509_builder() { + let pkey = pkey(); + + let mut name = X509Name::builder().unwrap(); + name.append_entry_by_nid(Nid::COMMONNAME, "foobar.com") + .unwrap(); + let name = name.build(); + + let mut builder = X509::builder().unwrap(); + builder.set_version(2).unwrap(); + builder.set_subject_name(&name).unwrap(); + builder.set_issuer_name(&name).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_pubkey(&pkey).unwrap(); + + let mut serial = BigNum::new().unwrap(); + serial.rand(128, MsbOption::MAYBE_ZERO, false).unwrap(); + builder + .set_serial_number(&serial.to_asn1_integer().unwrap()) + .unwrap(); + + let basic_constraints = BasicConstraints::new().critical().ca().build().unwrap(); + builder.append_extension(basic_constraints).unwrap(); + let key_usage = KeyUsage::new() + .digital_signature() + .key_encipherment() + .build() + .unwrap(); + builder.append_extension(key_usage).unwrap(); + let ext_key_usage = ExtendedKeyUsage::new() + .client_auth() + .server_auth() + .other("2.999.1") + .build() + .unwrap(); + builder.append_extension(ext_key_usage).unwrap(); + let subject_key_identifier = SubjectKeyIdentifier::new() + .build(&builder.x509v3_context(None, None)) + .unwrap(); + builder.append_extension(subject_key_identifier).unwrap(); + let authority_key_identifier = AuthorityKeyIdentifier::new() + .keyid(true) + .build(&builder.x509v3_context(None, None)) + .unwrap(); + builder.append_extension(authority_key_identifier).unwrap(); + let subject_alternative_name = SubjectAlternativeName::new() + .dns("example.com") + .build(&builder.x509v3_context(None, None)) + .unwrap(); + builder.append_extension(subject_alternative_name).unwrap(); + + builder.sign(&pkey, MessageDigest::sha256()).unwrap(); + + let x509 = builder.build(); + + assert!(pkey.public_eq(&x509.public_key().unwrap())); + assert!(x509.verify(&pkey).unwrap()); + + let cn = x509 + .subject_name() + .entries_by_nid(Nid::COMMONNAME) + .next() + .unwrap(); + assert_eq!(cn.data().as_slice(), b"foobar.com"); + assert_eq!(serial, x509.serial_number().to_bn().unwrap()); +} + +#[test] +// This tests `X509Extension::new`, even though its deprecated. +#[allow(deprecated)] +fn x509_extension_new() { + assert!(X509Extension::new(None, None, "crlDistributionPoints", "section").is_err()); + assert!(X509Extension::new(None, None, "proxyCertInfo", "").is_err()); + assert!(X509Extension::new(None, None, "certificatePolicies", "").is_err()); + assert!(X509Extension::new(None, None, "subjectAltName", "dirName:section").is_err()); +} + +#[test] +fn x509_extension_new_from_der() { + let ext = X509Extension::new_from_der( + &Asn1Object::from_str("2.5.29.19").unwrap(), + true, + &Asn1OctetString::new_from_bytes(b"\x30\x03\x01\x01\xff").unwrap(), + ) + .unwrap(); + assert_eq!( + ext.to_der().unwrap(), + b"0\x0f\x06\x03U\x1d\x13\x01\x01\xff\x04\x050\x03\x01\x01\xff" + ); +} + +#[test] +fn x509_extension_to_der() { + let builder = X509::builder().unwrap(); + + for (ext, expected) in [ + ( + BasicConstraints::new().critical().ca().build().unwrap(), + b"0\x0f\x06\x03U\x1d\x13\x01\x01\xff\x04\x050\x03\x01\x01\xff" as &[u8], + ), + ( + SubjectAlternativeName::new() + .dns("example.com,DNS:example2.com") + .build(&builder.x509v3_context(None, None)) + .unwrap(), + b"0'\x06\x03U\x1d\x11\x04 0\x1e\x82\x1cexample.com,DNS:example2.com", + ), + ( + SubjectAlternativeName::new() + .rid("1.2.3.4") + .uri("https://example.com") + .build(&builder.x509v3_context(None, None)) + .unwrap(), + b"0#\x06\x03U\x1d\x11\x04\x1c0\x1a\x88\x03*\x03\x04\x86\x13https://example.com", + ), + ( + ExtendedKeyUsage::new() + .server_auth() + .other("2.999.1") + .other("clientAuth") + .build() + .unwrap(), + b"0\x22\x06\x03U\x1d%\x04\x1b0\x19\x06\x08+\x06\x01\x05\x05\x07\x03\x01\x06\x03\x887\x01\x06\x08+\x06\x01\x05\x05\x07\x03\x02", + ), + ] { + assert_eq!(&ext.to_der().unwrap(), expected); + } +} + +#[test] +fn eku_invalid_other() { + assert!(ExtendedKeyUsage::new() + .other("1.1.1.1.1,2.2.2.2.2") + .build() + .is_err()); +} + +#[test] +fn x509_req_builder() { + let pkey = pkey(); + + let mut name = X509Name::builder().unwrap(); + name.append_entry_by_nid(Nid::COMMONNAME, "foobar.com") + .unwrap(); + let name = name.build(); + + let mut builder = X509Req::builder().unwrap(); + builder.set_version(0).unwrap(); + builder.set_subject_name(&name).unwrap(); + builder.set_pubkey(&pkey).unwrap(); + + let mut extensions = Stack::new().unwrap(); + let key_usage = KeyUsage::new() + .digital_signature() + .key_encipherment() + .build() + .unwrap(); + extensions.push(key_usage).unwrap(); + let subject_alternative_name = SubjectAlternativeName::new() + .dns("example.com") + .build(&builder.x509v3_context(None)) + .unwrap(); + extensions.push(subject_alternative_name).unwrap(); + builder.add_extensions(&extensions).unwrap(); + + builder.sign(&pkey, MessageDigest::sha256()).unwrap(); + + let req = builder.build(); + assert!(req.public_key().unwrap().public_eq(&pkey)); + assert_eq!(req.extensions().unwrap().len(), extensions.len()); + assert!(req.verify(&pkey).unwrap()); +} + +#[test] +fn test_stack_from_pem() { + let certs = include_bytes!("../../test/certs.pem"); + let certs = X509::stack_from_pem(certs).unwrap(); + + assert_eq!(certs.len(), 2); + assert_eq!( + hex::encode(certs[0].digest(MessageDigest::sha1()).unwrap()), + "59172d9313e84459bcff27f967e79e6e9217e584" + ); + assert_eq!( + hex::encode(certs[1].digest(MessageDigest::sha1()).unwrap()), + "c0cbdf7cdd03c9773e5468e1f6d2da7d5cbb1875" + ); +} + +#[test] +fn issued() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + + assert_eq!(ca.issued(&cert), X509VerifyResult::OK); + assert_ne!(cert.issued(&cert), X509VerifyResult::OK); +} + +#[test] +fn signature() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let signature = cert.signature(); + assert_eq!( + hex::encode(signature.as_slice()), + "4af607b889790b43470442cfa551cdb8b6d0b0340d2958f76b9e3ef6ad4992230cead6842587f0ecad5\ + 78e6e11a221521e940187e3d6652de14e84e82f6671f097cc47932e022add3c0cb54a26bf27fa84c107\ + 4971caa6bee2e42d34a5b066c427f2d452038082b8073993399548088429de034fdd589dcfb0dd33be7\ + ebdfdf698a28d628a89568881d658151276bde333600969502c4e62e1d3470a683364dfb241f78d310a\ + 89c119297df093eb36b7fd7540224f488806780305d1e79ffc938fe2275441726522ab36d88348e6c51\ + f13dcc46b5e1cdac23c974fd5ef86aa41e91c9311655090a52333bc79687c748d833595d4c5f987508f\ + e121997410d37c" + ); + let algorithm = cert.signature_algorithm(); + assert_eq!(algorithm.object().nid(), Nid::SHA256WITHRSAENCRYPTION); + assert_eq!(algorithm.object().to_string(), "sha256WithRSAEncryption"); +} + +#[test] +#[allow(clippy::redundant_clone)] +fn clone_x509() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + drop(cert.clone()); +} + +#[test] +fn test_verify_cert() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + let chain = Stack::new().unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + store_bldr.add_cert(ca).unwrap(); + let store = store_bldr.build(); + + let mut context = X509StoreContext::new().unwrap(); + assert!(context + .init(&store, &cert, &chain, |c| c.verify_cert()) + .unwrap()); + assert!(context + .init(&store, &cert, &chain, |c| c.verify_cert()) + .unwrap()); +} + +#[test] +fn test_verify_fails() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let ca = include_bytes!("../../test/alt_name_cert.pem"); + let ca = X509::from_pem(ca).unwrap(); + let chain = Stack::new().unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + store_bldr.add_cert(ca).unwrap(); + let store = store_bldr.build(); + + let mut context = X509StoreContext::new().unwrap(); + assert!(!context + .init(&store, &cert, &chain, |c| c.verify_cert()) + .unwrap()); +} + +#[test] +#[cfg(any(ossl102, libressl261))] +fn test_verify_fails_with_crl_flag_set_and_no_crl() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + let chain = Stack::new().unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + store_bldr.add_cert(ca).unwrap(); + store_bldr.set_flags(X509VerifyFlags::CRL_CHECK).unwrap(); + let store = store_bldr.build(); + + let mut context = X509StoreContext::new().unwrap(); + assert_eq!( + context + .init(&store, &cert, &chain, |c| { + c.verify_cert()?; + Ok(c.error()) + }) + .unwrap() + .error_string(), + "unable to get certificate CRL" + ) +} + +#[test] +#[cfg(any(ossl102, libressl261))] +fn test_verify_cert_with_purpose() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + let chain = Stack::new().unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + let purpose_idx = X509PurposeRef::get_by_sname("sslserver") + .expect("Getting certificate purpose 'sslserver' failed"); + let x509_purposeref = + X509PurposeRef::from_idx(purpose_idx).expect("Getting certificate purpose failed"); + store_bldr + .set_purpose(x509_purposeref.purpose()) + .expect("Setting certificate purpose failed"); + store_bldr.add_cert(ca).unwrap(); + + let store = store_bldr.build(); + + let mut context = X509StoreContext::new().unwrap(); + assert!(context + .init(&store, &cert, &chain, |c| c.verify_cert()) + .unwrap()); +} + +#[test] +#[cfg(any(ossl102, libressl261))] +fn test_verify_cert_with_wrong_purpose_fails() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + let chain = Stack::new().unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + let purpose_idx = X509PurposeRef::get_by_sname("timestampsign") + .expect("Getting certificate purpose 'timestampsign' failed"); + let x509_purpose = + X509PurposeRef::from_idx(purpose_idx).expect("Getting certificate purpose failed"); + store_bldr + .set_purpose(x509_purpose.purpose()) + .expect("Setting certificate purpose failed"); + store_bldr.add_cert(ca).unwrap(); + + let store = store_bldr.build(); + + let expected_error = ffi::X509_V_ERR_INVALID_PURPOSE; + let mut context = X509StoreContext::new().unwrap(); + assert_eq!( + context + .init(&store, &cert, &chain, |c| { + c.verify_cert()?; + Ok(c.error()) + }) + .unwrap() + .as_raw(), + expected_error + ) +} + +#[cfg(ossl110)] +#[test] +fn x509_ref_version() { + let mut builder = X509Builder::new().unwrap(); + let expected_version = 2; + builder + .set_version(expected_version) + .expect("Failed to set certificate version"); + let cert = builder.build(); + let actual_version = cert.version(); + assert_eq!( + expected_version, actual_version, + "Obtained certificate version is incorrect", + ); +} + +#[cfg(ossl110)] +#[test] +fn x509_ref_version_no_version_set() { + let cert = X509Builder::new().unwrap().build(); + let actual_version = cert.version(); + assert_eq!( + 0, actual_version, + "Default certificate version is incorrect", + ); +} + +#[test] +fn test_load_crl() { + let ca = include_bytes!("../../test/crl-ca.crt"); + let ca = X509::from_pem(ca).unwrap(); + + let crl = include_bytes!("../../test/test.crl"); + let crl = X509Crl::from_der(crl).unwrap(); + assert!(crl.verify(&ca.public_key().unwrap()).unwrap()); + + let cert = include_bytes!("../../test/subca.crt"); + let cert = X509::from_pem(cert).unwrap(); + + let revoked = match crl.get_by_cert(&cert) { + CrlStatus::Revoked(revoked) => revoked, + _ => panic!("cert should be revoked"), + }; + + assert_eq!( + revoked.serial_number().to_bn().unwrap(), + cert.serial_number().to_bn().unwrap(), + "revoked and cert serial numbers should match" + ); +} + +#[test] +fn test_crl_entry_extensions() { + let crl = include_bytes!("../../test/entry_extensions.crl"); + let crl = X509Crl::from_pem(crl).unwrap(); + + let revoked_certs = crl.get_revoked().unwrap(); + let entry = &revoked_certs[0]; + + let (critical, issuer) = entry + .extension::<CertificateIssuer>() + .unwrap() + .expect("Certificate issuer extension should be present"); + assert!(critical, "Certificate issuer extension is critical"); + assert_eq!(issuer.len(), 1, "Certificate issuer should have one entry"); + let issuer = issuer[0] + .directory_name() + .expect("Issuer should be a directory name"); + assert_eq!( + format!("{:?}", issuer), + r#"[countryName = "GB", commonName = "Test CA"]"# + ); + + // reason_code can't be inspected without ossl110 + #[allow(unused_variables)] + let (critical, reason_code) = entry + .extension::<ReasonCode>() + .unwrap() + .expect("Reason code extension should be present"); + assert!(!critical, "Reason code extension is not critical"); + #[cfg(ossl110)] + assert_eq!( + CrlReason::KEY_COMPROMISE, + CrlReason::from_raw(reason_code.get_i64().unwrap() as ffi::c_int) + ); +} + +#[test] +fn test_save_subject_der() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + + let der = cert.subject_name().to_der().unwrap(); + println!("der: {:?}", der); + assert!(!der.is_empty()); +} + +#[test] +fn test_load_subject_der() { + // The subject from ../../test/cert.pem + const SUBJECT_DER: &[u8] = &[ + 48, 90, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 65, 85, 49, 19, 48, 17, 6, 3, 85, 4, 8, 12, + 10, 83, 111, 109, 101, 45, 83, 116, 97, 116, 101, 49, 33, 48, 31, 6, 3, 85, 4, 10, 12, 24, + 73, 110, 116, 101, 114, 110, 101, 116, 32, 87, 105, 100, 103, 105, 116, 115, 32, 80, 116, + 121, 32, 76, 116, 100, 49, 19, 48, 17, 6, 3, 85, 4, 3, 12, 10, 102, 111, 111, 98, 97, 114, + 46, 99, 111, 109, + ]; + X509Name::from_der(SUBJECT_DER).unwrap(); +} + +#[test] +fn test_convert_to_text() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + + const SUBSTRINGS: &[&str] = &[ + "Certificate:\n", + "Serial Number:", + "Signature Algorithm:", + "Issuer: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd\n", + "Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=foobar.com\n", + "Subject Public Key Info:", + ]; + + let text = String::from_utf8(cert.to_text().unwrap()).unwrap(); + + for substring in SUBSTRINGS { + assert!( + text.contains(substring), + "{:?} not found inside {}", + substring, + text + ); + } +} + +#[test] +fn test_convert_req_to_text() { + let csr = include_bytes!("../../test/csr.pem"); + let csr = X509Req::from_pem(csr).unwrap(); + + const SUBSTRINGS: &[&str] = &[ + "Certificate Request:\n", + "Version:", + "Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=foobar.com\n", + "Subject Public Key Info:", + "Signature Algorithm:", + ]; + + let text = String::from_utf8(csr.to_text().unwrap()).unwrap(); + + for substring in SUBSTRINGS { + assert!( + text.contains(substring), + "{:?} not found inside {}", + substring, + text + ); + } +} + +#[test] +fn test_name_cmp() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + + let subject = cert.subject_name(); + let issuer = cert.issuer_name(); + assert_eq!(Ordering::Equal, subject.try_cmp(subject).unwrap()); + assert_eq!(Ordering::Greater, subject.try_cmp(issuer).unwrap()); +} + +#[test] +#[cfg(any(boringssl, ossl110, libressl270))] +fn test_name_to_owned() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let name = cert.subject_name(); + let copied_name = name.to_owned().unwrap(); + assert_eq!(Ordering::Equal, name.try_cmp(&copied_name).unwrap()); +} + +#[test] +#[cfg(any(ossl102, libressl261))] +fn test_verify_param_set_time_fails_verification() { + const TEST_T_2030: time_t = 1893456000; + + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + let chain = Stack::new().unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + store_bldr.add_cert(ca).unwrap(); + let mut verify_params = X509VerifyParam::new().unwrap(); + verify_params.set_time(TEST_T_2030); + store_bldr.set_param(&verify_params).unwrap(); + let store = store_bldr.build(); + + let mut context = X509StoreContext::new().unwrap(); + assert_eq!( + context + .init(&store, &cert, &chain, |c| { + c.verify_cert()?; + Ok(c.error()) + }) + .unwrap() + .error_string(), + "certificate has expired" + ) +} + +#[test] +#[cfg(any(ossl102, libressl261))] +fn test_verify_param_set_time() { + const TEST_T_2020: time_t = 1577836800; + + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + let chain = Stack::new().unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + store_bldr.add_cert(ca).unwrap(); + let mut verify_params = X509VerifyParam::new().unwrap(); + verify_params.set_time(TEST_T_2020); + store_bldr.set_param(&verify_params).unwrap(); + let store = store_bldr.build(); + + let mut context = X509StoreContext::new().unwrap(); + assert!(context + .init(&store, &cert, &chain, |c| c.verify_cert()) + .unwrap()); +} + +#[test] +#[cfg(any(ossl102, libressl261))] +fn test_verify_param_set_depth() { + let cert = include_bytes!("../../test/leaf.pem"); + let cert = X509::from_pem(cert).unwrap(); + let intermediate_ca = include_bytes!("../../test/intermediate-ca.pem"); + let intermediate_ca = X509::from_pem(intermediate_ca).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + let mut chain = Stack::new().unwrap(); + chain.push(intermediate_ca).unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + store_bldr.add_cert(ca).unwrap(); + let mut verify_params = X509VerifyParam::new().unwrap(); + // OpenSSL 1.1.0+ considers the root certificate to not be part of the chain, while 1.0.2 and LibreSSL do + let expected_depth = if cfg!(any(ossl110)) { 1 } else { 2 }; + verify_params.set_depth(expected_depth); + store_bldr.set_param(&verify_params).unwrap(); + let store = store_bldr.build(); + + let mut context = X509StoreContext::new().unwrap(); + assert!(context + .init(&store, &cert, &chain, |c| c.verify_cert()) + .unwrap()); +} + +#[test] +#[cfg(any(ossl102, libressl261))] +#[allow(clippy::bool_to_int_with_if)] +fn test_verify_param_set_depth_fails_verification() { + let cert = include_bytes!("../../test/leaf.pem"); + let cert = X509::from_pem(cert).unwrap(); + let intermediate_ca = include_bytes!("../../test/intermediate-ca.pem"); + let intermediate_ca = X509::from_pem(intermediate_ca).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + let mut chain = Stack::new().unwrap(); + chain.push(intermediate_ca).unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + store_bldr.add_cert(ca).unwrap(); + let mut verify_params = X509VerifyParam::new().unwrap(); + // OpenSSL 1.1.0+ considers the root certificate to not be part of the chain, while 1.0.2 and LibreSSL do + let expected_depth = if cfg!(any(ossl110)) { 0 } else { 1 }; + verify_params.set_depth(expected_depth); + store_bldr.set_param(&verify_params).unwrap(); + let store = store_bldr.build(); + + // OpenSSL 1.1.0+ added support for X509_V_ERR_CERT_CHAIN_TOO_LONG, while 1.0.2 simply ignores the intermediate + let expected_error = if cfg!(any(ossl110, libressl261)) { + "certificate chain too long" + } else { + "unable to get local issuer certificate" + }; + + let mut context = X509StoreContext::new().unwrap(); + assert_eq!( + context + .init(&store, &cert, &chain, |c| { + c.verify_cert()?; + Ok(c.error()) + }) + .unwrap() + .error_string(), + expected_error + ) +} + +#[test] +#[cfg(not(boringssl))] +fn test_load_cert_file() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let chain = Stack::new().unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + let lookup = store_bldr.add_lookup(X509Lookup::file()).unwrap(); + lookup + .load_cert_file("test/root-ca.pem", SslFiletype::PEM) + .unwrap(); + let store = store_bldr.build(); + + let mut context = X509StoreContext::new().unwrap(); + assert!(context + .init(&store, &cert, &chain, |c| c.verify_cert()) + .unwrap()); +} + +#[test] +#[cfg(ossl110)] +fn test_verify_param_auth_level() { + let mut param = X509VerifyParam::new().unwrap(); + let auth_lvl = 2; + let auth_lvl_default = -1; + + assert_eq!(param.auth_level(), auth_lvl_default); + + param.set_auth_level(auth_lvl); + assert_eq!(param.auth_level(), auth_lvl); +} + +#[test] +#[cfg(ossl102)] +fn test_set_purpose() { + let cert = include_bytes!("../../test/leaf.pem"); + let cert = X509::from_pem(cert).unwrap(); + let intermediate_ca = include_bytes!("../../test/intermediate-ca.pem"); + let intermediate_ca = X509::from_pem(intermediate_ca).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + let mut chain = Stack::new().unwrap(); + chain.push(intermediate_ca).unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + store_bldr.add_cert(ca).unwrap(); + let mut verify_params = X509VerifyParam::new().unwrap(); + verify_params.set_purpose(X509PurposeId::ANY).unwrap(); + store_bldr.set_param(&verify_params).unwrap(); + let store = store_bldr.build(); + let mut context = X509StoreContext::new().unwrap(); + + assert!(context + .init(&store, &cert, &chain, |c| c.verify_cert()) + .unwrap()); +} + +#[test] +#[cfg(ossl102)] +fn test_set_purpose_fails_verification() { + let cert = include_bytes!("../../test/leaf.pem"); + let cert = X509::from_pem(cert).unwrap(); + let intermediate_ca = include_bytes!("../../test/intermediate-ca.pem"); + let intermediate_ca = X509::from_pem(intermediate_ca).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + let mut chain = Stack::new().unwrap(); + chain.push(intermediate_ca).unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + store_bldr.add_cert(ca).unwrap(); + let mut verify_params = X509VerifyParam::new().unwrap(); + verify_params + .set_purpose(X509PurposeId::TIMESTAMP_SIGN) + .unwrap(); + store_bldr.set_param(&verify_params).unwrap(); + let store = store_bldr.build(); + + let expected_error = ffi::X509_V_ERR_INVALID_PURPOSE; + let mut context = X509StoreContext::new().unwrap(); + assert_eq!( + context + .init(&store, &cert, &chain, |c| { + c.verify_cert()?; + Ok(c.error()) + }) + .unwrap() + .as_raw(), + expected_error + ) +} + +#[test] +#[cfg(any(ossl101, libressl350))] +fn test_add_name_entry() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let inp_name = cert.subject_name().entries().next().unwrap(); + + let mut names = X509Name::builder().unwrap(); + names.append_entry(inp_name).unwrap(); + let names = names.build(); + + let mut entries = names.entries(); + let outp_name = entries.next().unwrap(); + assert_eq!(outp_name.object().nid(), inp_name.object().nid()); + assert_eq!(outp_name.data().as_slice(), inp_name.data().as_slice()); + assert!(entries.next().is_none()); +} + +#[test] +#[cfg(not(boringssl))] +fn test_load_crl_file_fail() { + let mut store_bldr = X509StoreBuilder::new().unwrap(); + let lookup = store_bldr.add_lookup(X509Lookup::file()).unwrap(); + let res = lookup.load_crl_file("test/root-ca.pem", SslFiletype::PEM); + assert!(res.is_err()); +} + +#[cfg(ossl110)] +fn ipaddress_as_subject_alternative_name_is_formatted_in_debug<T>(expected_ip: T) +where + T: Into<std::net::IpAddr>, +{ + let expected_ip = format!("{:?}", expected_ip.into()); + let mut builder = X509Builder::new().unwrap(); + let san = SubjectAlternativeName::new() + .ip(&expected_ip) + .build(&builder.x509v3_context(None, None)) + .unwrap(); + builder.append_extension(san).unwrap(); + let cert = builder.build(); + let actual_ip = cert + .subject_alt_names() + .into_iter() + .flatten() + .map(|n| format!("{:?}", *n)) + .next() + .unwrap(); + assert_eq!(actual_ip, expected_ip); +} + +#[cfg(ossl110)] +#[test] +fn ipv4_as_subject_alternative_name_is_formatted_in_debug() { + ipaddress_as_subject_alternative_name_is_formatted_in_debug([8u8, 8, 8, 128]); +} + +#[cfg(ossl110)] +#[test] +fn ipv6_as_subject_alternative_name_is_formatted_in_debug() { + ipaddress_as_subject_alternative_name_is_formatted_in_debug([ + 8u8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 128, + ]); +} + +#[cfg(ossl110)] +#[test] +fn other_name_as_subject_alternative_name() { + let oid = Asn1Object::from_str("1.3.6.1.5.5.7.8.11").unwrap(); + // this is the hex representation of "test" encoded as a ia5string + let content = [0x16, 0x04, 0x74, 0x65, 0x73, 0x74]; + + let mut builder = X509Builder::new().unwrap(); + let san = SubjectAlternativeName::new() + .other_name2(oid, &content) + .build(&builder.x509v3_context(None, None)) + .unwrap(); + builder.append_extension(san).unwrap(); + let cert = builder.build(); + let general_name = cert + .subject_alt_names() + .into_iter() + .flatten() + .next() + .unwrap(); + unsafe { + assert_eq!((*general_name.as_ptr()).type_, 0); + } +} + +#[test] +fn test_dist_point() { + let cert = include_bytes!("../../test/certv3.pem"); + let cert = X509::from_pem(cert).unwrap(); + + let dps = cert.crl_distribution_points().unwrap(); + let dp = dps.get(0).unwrap(); + let dp_nm = dp.distpoint().unwrap(); + let dp_gns = dp_nm.fullname().unwrap(); + let dp_gn = dp_gns.get(0).unwrap(); + assert_eq!(dp_gn.uri().unwrap(), "http://example.com/crl.pem"); + + let dp = dps.get(1).unwrap(); + let dp_nm = dp.distpoint().unwrap(); + let dp_gns = dp_nm.fullname().unwrap(); + let dp_gn = dp_gns.get(0).unwrap(); + assert_eq!(dp_gn.uri().unwrap(), "http://example.com/crl2.pem"); + assert!(dps.get(2).is_none()) +} + +#[test] +fn test_dist_point_null() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + assert!(cert.crl_distribution_points().is_none()); +} diff --git a/vendor/openssl/src/x509/verify.rs b/vendor/openssl/src/x509/verify.rs new file mode 100644 index 0000000..541cd82 --- /dev/null +++ b/vendor/openssl/src/x509/verify.rs @@ -0,0 +1,215 @@ +use bitflags::bitflags; +use foreign_types::ForeignTypeRef; +use libc::{c_int, c_uint, c_ulong, time_t}; +use std::net::IpAddr; + +use crate::error::ErrorStack; +#[cfg(ossl102)] +use crate::x509::X509PurposeId; +use crate::{cvt, cvt_p}; +use openssl_macros::corresponds; + +bitflags! { + /// Flags used to check an `X509` certificate. + #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + #[repr(transparent)] + pub struct X509CheckFlags: c_uint { + const ALWAYS_CHECK_SUBJECT = ffi::X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT; + const NO_WILDCARDS = ffi::X509_CHECK_FLAG_NO_WILDCARDS; + const NO_PARTIAL_WILDCARDS = ffi::X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS; + const MULTI_LABEL_WILDCARDS = ffi::X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS; + const SINGLE_LABEL_SUBDOMAINS = ffi::X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS; + /// Requires OpenSSL 1.1.0 or newer. + #[cfg(any(ossl110))] + const NEVER_CHECK_SUBJECT = ffi::X509_CHECK_FLAG_NEVER_CHECK_SUBJECT; + + #[deprecated(since = "0.10.6", note = "renamed to NO_WILDCARDS")] + const FLAG_NO_WILDCARDS = ffi::X509_CHECK_FLAG_NO_WILDCARDS; + } +} + +bitflags! { + /// Flags used to verify an `X509` certificate chain. + #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + #[repr(transparent)] + pub struct X509VerifyFlags: c_ulong { + const CB_ISSUER_CHECK = ffi::X509_V_FLAG_CB_ISSUER_CHECK; + const USE_CHECK_TIME = ffi::X509_V_FLAG_USE_CHECK_TIME; + const CRL_CHECK = ffi::X509_V_FLAG_CRL_CHECK; + const CRL_CHECK_ALL = ffi::X509_V_FLAG_CRL_CHECK_ALL; + const IGNORE_CRITICAL = ffi::X509_V_FLAG_IGNORE_CRITICAL; + const X509_STRICT = ffi::X509_V_FLAG_X509_STRICT; + const ALLOW_PROXY_CERTS = ffi::X509_V_FLAG_ALLOW_PROXY_CERTS; + const POLICY_CHECK = ffi::X509_V_FLAG_POLICY_CHECK; + const EXPLICIT_POLICY = ffi::X509_V_FLAG_EXPLICIT_POLICY; + const INHIBIT_ANY = ffi::X509_V_FLAG_INHIBIT_ANY; + const INHIBIT_MAP = ffi::X509_V_FLAG_INHIBIT_MAP; + const NOTIFY_POLICY = ffi::X509_V_FLAG_NOTIFY_POLICY; + const EXTENDED_CRL_SUPPORT = ffi::X509_V_FLAG_EXTENDED_CRL_SUPPORT; + const USE_DELTAS = ffi::X509_V_FLAG_USE_DELTAS; + const CHECK_SS_SIGNATURE = ffi::X509_V_FLAG_CHECK_SS_SIGNATURE; + #[cfg(ossl102)] + const TRUSTED_FIRST = ffi::X509_V_FLAG_TRUSTED_FIRST; + #[cfg(ossl102)] + const SUITEB_128_LOS_ONLY = ffi::X509_V_FLAG_SUITEB_128_LOS_ONLY; + #[cfg(ossl102)] + const SUITEB_192_LOS = ffi::X509_V_FLAG_SUITEB_128_LOS; + #[cfg(ossl102)] + const SUITEB_128_LOS = ffi::X509_V_FLAG_SUITEB_192_LOS; + #[cfg(ossl102)] + const PARTIAL_CHAIN = ffi::X509_V_FLAG_PARTIAL_CHAIN; + #[cfg(ossl110)] + const NO_ALT_CHAINS = ffi::X509_V_FLAG_NO_ALT_CHAINS; + #[cfg(ossl110)] + const NO_CHECK_TIME = ffi::X509_V_FLAG_NO_CHECK_TIME; + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::X509_VERIFY_PARAM; + fn drop = ffi::X509_VERIFY_PARAM_free; + + /// Adjust parameters associated with certificate verification. + pub struct X509VerifyParam; + /// Reference to `X509VerifyParam`. + pub struct X509VerifyParamRef; +} + +impl X509VerifyParam { + /// Create an X509VerifyParam + #[corresponds(X509_VERIFY_PARAM_new)] + pub fn new() -> Result<X509VerifyParam, ErrorStack> { + unsafe { + ffi::init(); + cvt_p(ffi::X509_VERIFY_PARAM_new()).map(X509VerifyParam) + } + } +} + +impl X509VerifyParamRef { + /// Set the host flags. + #[corresponds(X509_VERIFY_PARAM_set_hostflags)] + pub fn set_hostflags(&mut self, hostflags: X509CheckFlags) { + unsafe { + ffi::X509_VERIFY_PARAM_set_hostflags(self.as_ptr(), hostflags.bits()); + } + } + + /// Set verification flags. + #[corresponds(X509_VERIFY_PARAM_set_flags)] + pub fn set_flags(&mut self, flags: X509VerifyFlags) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::X509_VERIFY_PARAM_set_flags( + self.as_ptr(), + flags.bits(), + )) + .map(|_| ()) + } + } + + /// Clear verification flags. + #[corresponds(X509_VERIFY_PARAM_clear_flags)] + pub fn clear_flags(&mut self, flags: X509VerifyFlags) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::X509_VERIFY_PARAM_clear_flags( + self.as_ptr(), + flags.bits(), + )) + .map(|_| ()) + } + } + + /// Gets verification flags. + #[corresponds(X509_VERIFY_PARAM_get_flags)] + pub fn flags(&mut self) -> X509VerifyFlags { + let bits = unsafe { ffi::X509_VERIFY_PARAM_get_flags(self.as_ptr()) }; + X509VerifyFlags::from_bits_retain(bits) + } + + /// Set the expected DNS hostname. + #[corresponds(X509_VERIFY_PARAM_set1_host)] + pub fn set_host(&mut self, host: &str) -> Result<(), ErrorStack> { + unsafe { + // len == 0 means "run strlen" :( + let raw_host = if host.is_empty() { "\0" } else { host }; + cvt(ffi::X509_VERIFY_PARAM_set1_host( + self.as_ptr(), + raw_host.as_ptr() as *const _, + host.len(), + )) + .map(|_| ()) + } + } + + /// Set the expected email address. + #[corresponds(X509_VERIFY_PARAM_set1_email)] + pub fn set_email(&mut self, email: &str) -> Result<(), ErrorStack> { + unsafe { + // len == 0 means "run strlen" :( + let raw_email = if email.is_empty() { "\0" } else { email }; + cvt(ffi::X509_VERIFY_PARAM_set1_email( + self.as_ptr(), + raw_email.as_ptr() as *const _, + email.len(), + )) + .map(|_| ()) + } + } + + /// Set the expected IPv4 or IPv6 address. + #[corresponds(X509_VERIFY_PARAM_set1_ip)] + pub fn set_ip(&mut self, ip: IpAddr) -> Result<(), ErrorStack> { + unsafe { + let mut buf = [0; 16]; + let len = match ip { + IpAddr::V4(addr) => { + buf[..4].copy_from_slice(&addr.octets()); + 4 + } + IpAddr::V6(addr) => { + buf.copy_from_slice(&addr.octets()); + 16 + } + }; + cvt(ffi::X509_VERIFY_PARAM_set1_ip( + self.as_ptr(), + buf.as_ptr() as *const _, + len, + )) + .map(|_| ()) + } + } + + /// Set the verification time, where time is of type time_t, traditionaly defined as seconds since the epoch + #[corresponds(X509_VERIFY_PARAM_set_time)] + pub fn set_time(&mut self, time: time_t) { + unsafe { ffi::X509_VERIFY_PARAM_set_time(self.as_ptr(), time) } + } + + /// Set the verification depth + #[corresponds(X509_VERIFY_PARAM_set_depth)] + pub fn set_depth(&mut self, depth: c_int) { + unsafe { ffi::X509_VERIFY_PARAM_set_depth(self.as_ptr(), depth) } + } + + /// Sets the authentication security level to auth_level + #[corresponds(X509_VERIFY_PARAM_set_auth_level)] + #[cfg(ossl110)] + pub fn set_auth_level(&mut self, lvl: c_int) { + unsafe { ffi::X509_VERIFY_PARAM_set_auth_level(self.as_ptr(), lvl) } + } + + /// Gets the current authentication security level + #[corresponds(X509_VERIFY_PARAM_get_auth_level)] + #[cfg(ossl110)] + pub fn auth_level(&self) -> i32 { + unsafe { ffi::X509_VERIFY_PARAM_get_auth_level(self.as_ptr()) } + } + + /// Sets the verification purpose + #[corresponds(X509_VERIFY_PARAM_set_purpose)] + #[cfg(ossl102)] + pub fn set_purpose(&mut self, purpose: X509PurposeId) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::X509_VERIFY_PARAM_set_purpose(self.as_ptr(), purpose.0)).map(|_| ()) } + } +} |