| From 66e1daa9b4a345617bf9a090d56fa09ccd9b1d35 Mon Sep 17 00:00:00 2001 |
| From: Maurice Lam <yukl@google.com> |
| Date: Wed, 26 Apr 2023 02:21:36 +0000 |
| Subject: [PATCH] Apply Android patches |
| |
| --- |
| openssl/src/bio.rs | 6 +- |
| openssl/src/bn.rs | 2 +- |
| openssl/src/cipher.rs | 4 + |
| openssl/src/dh.rs | 2 +- |
| openssl/src/ec.rs | 20 + |
| openssl/src/encrypt.rs | 4 +- |
| openssl/src/hkdf.rs | 89 ++ |
| openssl/src/hmac.rs | 217 ++++ |
| openssl/src/lib.rs | 12 + |
| openssl/src/pkey.rs | 20 +- |
| openssl/src/sign.rs | 10 +- |
| openssl/src/symm.rs | 7 +- |
| openssl/src/x509/mod.rs | 52 +- |
| openssl/src/x509/mod.rs.orig | 1879 ++++++++++++++++++++++++++++++++++ |
| 14 files changed, 2292 insertions(+), 32 deletions(-) |
| create mode 100644 openssl/src/hkdf.rs |
| create mode 100644 openssl/src/hmac.rs |
| create mode 100644 openssl/src/x509/mod.rs.orig |
| |
| diff --git a/openssl/src/bio.rs b/openssl/src/bio.rs |
| index 6a72552a..03242188 100644 |
| --- a/openssl/src/bio.rs |
| +++ b/openssl/src/bio.rs |
| @@ -4,7 +4,7 @@ use std::marker::PhantomData; |
| use std::ptr; |
| use std::slice; |
| |
| -use crate::cvt_p; |
| +use crate::{cvt_p, SignedLenType}; |
| use crate::error::ErrorStack; |
| |
| pub struct MemBioSlice<'a>(*mut ffi::BIO, PhantomData<&'a [u8]>); |
| @@ -25,7 +25,7 @@ impl<'a> MemBioSlice<'a> { |
| let bio = unsafe { |
| cvt_p(BIO_new_mem_buf( |
| buf.as_ptr() as *const _, |
| - buf.len() as c_int, |
| + buf.len() as SignedLenType, |
| ))? |
| }; |
| |
| @@ -78,7 +78,7 @@ cfg_if! { |
| 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 { |
| + unsafe fn BIO_new_mem_buf(buf: *const ::libc::c_void, len: SignedLenType) -> *mut ffi::BIO { |
| ffi::BIO_new_mem_buf(buf as *mut _, len) |
| } |
| } |
| diff --git a/openssl/src/bn.rs b/openssl/src/bn.rs |
| index 0328730a..fe820a2c 100644 |
| --- a/openssl/src/bn.rs |
| +++ b/openssl/src/bn.rs |
| @@ -814,7 +814,7 @@ impl BigNumRef { |
| /// assert_eq!(&bn_vec, &[0, 0, 0x45, 0x43]); |
| /// ``` |
| #[corresponds(BN_bn2binpad)] |
| - #[cfg(ossl110)] |
| + #[cfg(any(boringssl, ossl110))] |
| pub fn to_vec_padded(&self, pad_to: i32) -> Result<Vec<u8>, ErrorStack> { |
| let mut v = Vec::with_capacity(pad_to as usize); |
| unsafe { |
| diff --git a/openssl/src/cipher.rs b/openssl/src/cipher.rs |
| index aeedf459..570794ca 100644 |
| --- a/openssl/src/cipher.rs |
| +++ b/openssl/src/cipher.rs |
| @@ -208,6 +208,7 @@ impl Cipher { |
| unsafe { CipherRef::from_ptr(ffi::EVP_aes_192_cfb1() as *mut _) } |
| } |
| |
| + #[cfg(not(boringssl))] |
| pub fn aes_192_cfb128() -> &'static CipherRef { |
| unsafe { CipherRef::from_ptr(ffi::EVP_aes_192_cfb128() as *mut _) } |
| } |
| @@ -253,6 +254,7 @@ impl Cipher { |
| unsafe { CipherRef::from_ptr(ffi::EVP_aes_256_cfb1() as *mut _) } |
| } |
| |
| + #[cfg(not(boringssl))] |
| pub fn aes_256_cfb128() -> &'static CipherRef { |
| unsafe { CipherRef::from_ptr(ffi::EVP_aes_256_cfb128() as *mut _) } |
| } |
| @@ -282,11 +284,13 @@ impl Cipher { |
| } |
| |
| #[cfg(not(osslconf = "OPENSSL_NO_BF"))] |
| + #[cfg(not(boringssl))] |
| pub fn bf_cbc() -> &'static CipherRef { |
| unsafe { CipherRef::from_ptr(ffi::EVP_bf_cbc() as *mut _) } |
| } |
| |
| #[cfg(not(osslconf = "OPENSSL_NO_BF"))] |
| + #[cfg(not(boringssl))] |
| pub fn bf_ecb() -> &'static CipherRef { |
| unsafe { CipherRef::from_ptr(ffi::EVP_bf_ecb() as *mut _) } |
| } |
| diff --git a/openssl/src/dh.rs b/openssl/src/dh.rs |
| index 12170b99..e781543e 100644 |
| --- a/openssl/src/dh.rs |
| +++ b/openssl/src/dh.rs |
| @@ -239,7 +239,7 @@ where |
| } |
| |
| cfg_if! { |
| - if #[cfg(any(ossl110, libressl270))] { |
| + if #[cfg(any(ossl110, libressl270, boringssl))] { |
| use ffi::{DH_set0_pqg, DH_get0_pqg, DH_get0_key, DH_set0_key}; |
| } else { |
| #[allow(bad_style)] |
| diff --git a/openssl/src/ec.rs b/openssl/src/ec.rs |
| index 248ced3e..c56f5da7 100644 |
| --- a/openssl/src/ec.rs |
| +++ b/openssl/src/ec.rs |
| @@ -954,6 +954,26 @@ impl EcKey<Private> { |
| EcKey<Private>, |
| ffi::d2i_ECPrivateKey |
| } |
| + |
| + /// Decodes a DER-encoded elliptic curve private key structure for the specified curve. |
| + #[corresponds(EC_KEY_parse_private_key)] |
| + #[cfg(boringssl)] |
| + pub fn private_key_from_der_for_group( |
| + der: &[u8], |
| + group: &EcGroupRef, |
| + ) -> Result<EcKey<Private>, ErrorStack> { |
| + unsafe { |
| + let mut cbs = ffi::CBS { |
| + data: der.as_ptr(), |
| + len: der.len(), |
| + }; |
| + cvt_p(ffi::EC_KEY_parse_private_key( |
| + &mut cbs as *mut ffi::CBS, |
| + group.as_ptr(), |
| + )) |
| + .map(|p| EcKey::from_ptr(p)) |
| + } |
| + } |
| } |
| |
| impl<T> Clone for EcKey<T> { |
| diff --git a/openssl/src/encrypt.rs b/openssl/src/encrypt.rs |
| index d3db0fd4..19a9a459 100644 |
| --- a/openssl/src/encrypt.rs |
| +++ b/openssl/src/encrypt.rs |
| @@ -148,7 +148,7 @@ impl<'a> Encrypter<'a> { |
| /// 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))] |
| + #[cfg(any(ossl102, libressl310, boringssl))] |
| pub fn set_rsa_oaep_md(&mut self, md: MessageDigest) -> Result<(), ErrorStack> { |
| unsafe { |
| cvt(ffi::EVP_PKEY_CTX_set_rsa_oaep_md( |
| @@ -352,7 +352,7 @@ impl<'a> Decrypter<'a> { |
| /// 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))] |
| + #[cfg(any(ossl102, libressl310, boringssl))] |
| pub fn set_rsa_oaep_md(&mut self, md: MessageDigest) -> Result<(), ErrorStack> { |
| unsafe { |
| cvt(ffi::EVP_PKEY_CTX_set_rsa_oaep_md( |
| diff --git a/openssl/src/hkdf.rs b/openssl/src/hkdf.rs |
| new file mode 100644 |
| index 00000000..cc7e5b3a |
| --- /dev/null |
| +++ b/openssl/src/hkdf.rs |
| @@ -0,0 +1,89 @@ |
| +use crate::cvt; |
| +use crate::error::ErrorStack; |
| +use crate::md::MdRef; |
| +use foreign_types::ForeignTypeRef; |
| +use openssl_macros::corresponds; |
| + |
| +/// Computes HKDF (as specified by RFC 5869). |
| +/// |
| +/// HKDF is an Extract-and-Expand algorithm. It does not do any key stretching, |
| +/// and as such, is not suited to be used alone to generate a key from a |
| +/// password. |
| +#[corresponds(HKDF)] |
| +#[inline] |
| +pub fn hkdf( |
| + out_key: &mut [u8], |
| + md: &MdRef, |
| + secret: &[u8], |
| + salt: &[u8], |
| + info: &[u8], |
| +) -> Result<(), ErrorStack> { |
| + unsafe { |
| + cvt(ffi::HKDF( |
| + out_key.as_mut_ptr(), |
| + out_key.len(), |
| + md.as_ptr(), |
| + secret.as_ptr(), |
| + secret.len(), |
| + salt.as_ptr(), |
| + salt.len(), |
| + info.as_ptr(), |
| + info.len(), |
| + ))?; |
| + } |
| + |
| + Ok(()) |
| +} |
| + |
| +/// Computes a HKDF PRK (as specified by RFC 5869). |
| +/// |
| +/// WARNING: This function orders the inputs differently from RFC 5869 |
| +/// specification. Double-check which parameter is the secret/IKM and which is |
| +/// the salt when using. |
| +#[corresponds(HKDF_extract)] |
| +#[inline] |
| +pub fn hkdf_extract<'a>( |
| + out_key: &'a mut [u8], |
| + md: &MdRef, |
| + secret: &[u8], |
| + salt: &[u8], |
| +) -> Result<&'a [u8], ErrorStack> { |
| + let mut out_len = out_key.len(); |
| + unsafe { |
| + cvt(ffi::HKDF_extract( |
| + out_key.as_mut_ptr(), |
| + &mut out_len, |
| + md.as_ptr(), |
| + secret.as_ptr(), |
| + secret.len(), |
| + salt.as_ptr(), |
| + salt.len(), |
| + ))?; |
| + } |
| + |
| + Ok(&out_key[..out_len]) |
| +} |
| + |
| +/// Computes a HKDF OKM (as specified by RFC 5869). |
| +#[corresponds(HKDF_expand)] |
| +#[inline] |
| +pub fn hkdf_expand( |
| + out_key: &mut [u8], |
| + md: &MdRef, |
| + prk: &[u8], |
| + info: &[u8], |
| +) -> Result<(), ErrorStack> { |
| + unsafe { |
| + cvt(ffi::HKDF_expand( |
| + out_key.as_mut_ptr(), |
| + out_key.len(), |
| + md.as_ptr(), |
| + prk.as_ptr(), |
| + prk.len(), |
| + info.as_ptr(), |
| + info.len(), |
| + ))?; |
| + } |
| + |
| + Ok(()) |
| +} |
| diff --git a/openssl/src/hmac.rs b/openssl/src/hmac.rs |
| new file mode 100644 |
| index 00000000..465781e2 |
| --- /dev/null |
| +++ b/openssl/src/hmac.rs |
| @@ -0,0 +1,217 @@ |
| +use crate::error::ErrorStack; |
| +use crate::md::MdRef; |
| +use crate::{cvt, cvt_p}; |
| +use ffi::HMAC_CTX; |
| +use foreign_types::ForeignTypeRef; |
| +use libc::{c_uint, c_void}; |
| +use openssl_macros::corresponds; |
| +use std::convert::TryFrom; |
| +use std::ptr; |
| + |
| +/// Computes the HMAC as a one-shot operation. |
| +/// |
| +/// Calculates the HMAC of data, using the given |key| |
| +/// and hash function |md|, and returns the result re-using the space from |
| +/// buffer |out|. On entry, |out| must contain at least |EVP_MD_size| bytes |
| +/// of space. The actual length of the result is used to resize the returned |
| +/// slice. An output size of |EVP_MAX_MD_SIZE| will always be large enough. |
| +/// It returns a resized |out| or ErrorStack on error. |
| +#[corresponds(HMAC)] |
| +#[inline] |
| +pub fn hmac<'a>( |
| + md: &MdRef, |
| + key: &[u8], |
| + data: &[u8], |
| + out: &'a mut [u8], |
| +) -> Result<&'a [u8], ErrorStack> { |
| + assert!(out.len() >= md.size()); |
| + let mut out_len = c_uint::try_from(out.len()).unwrap(); |
| + unsafe { |
| + cvt_p(ffi::HMAC( |
| + md.as_ptr(), |
| + key.as_ptr() as *const c_void, |
| + key.len(), |
| + data.as_ptr(), |
| + data.len(), |
| + out.as_mut_ptr(), |
| + &mut out_len, |
| + ))?; |
| + } |
| + Ok(&out[..out_len as usize]) |
| +} |
| + |
| +/// A context object used to perform HMAC operations. |
| +/// |
| +/// HMAC is a MAC (message authentication code), i.e. a keyed hash function used for message |
| +/// authentication, which is based on a hash function. |
| +/// |
| +/// Note: Only available in boringssl. For openssl, use `PKey::hmac` instead. |
| +#[cfg(boringssl)] |
| +pub struct HmacCtx { |
| + ctx: *mut HMAC_CTX, |
| + output_size: usize, |
| +} |
| + |
| +#[cfg(boringssl)] |
| +impl HmacCtx { |
| + /// Creates a new [HmacCtx] to use the hash function `md` and key `key`. |
| + #[corresponds(HMAC_Init_ex)] |
| + pub fn new(key: &[u8], md: &MdRef) -> Result<Self, ErrorStack> { |
| + unsafe { |
| + // Safety: If an error occurred, the resulting null from HMAC_CTX_new is converted into |
| + // ErrorStack in the returned result by `cvt_p`. |
| + let ctx = cvt_p(ffi::HMAC_CTX_new())?; |
| + // Safety: |
| + // - HMAC_Init_ex must be called with a context previously created with HMAC_CTX_new, |
| + // which is the line above. |
| + // - HMAC_Init_ex may return an error if key is null but the md is different from |
| + // before. This is avoided here since key is guaranteed to be non-null. |
| + cvt(ffi::HMAC_Init_ex( |
| + ctx, |
| + key.as_ptr() as *const c_void, |
| + key.len(), |
| + md.as_ptr(), |
| + ptr::null_mut(), |
| + ))?; |
| + Ok(Self { |
| + ctx, |
| + output_size: md.size(), |
| + }) |
| + } |
| + } |
| + |
| + /// `update` can be called repeatedly with chunks of the message `data` to be authenticated. |
| + #[corresponds(HMAC_Update)] |
| + pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> { |
| + unsafe { |
| + // Safety: HMAC_Update returns 0 on error, and that is converted into ErrorStack in the |
| + // returned result by `cvt`. |
| + cvt(ffi::HMAC_Update(self.ctx, data.as_ptr(), data.len())).map(|_| ()) |
| + } |
| + } |
| + |
| + /// Finishes the HMAC process, and places the message authentication code in `output`. |
| + /// The number of bytes written to `output` is returned. |
| + /// |
| + /// # Panics |
| + /// |
| + /// Panics if the `output` is smaller than the required size. The output size is indicated by |
| + /// `md.size()` for the `Md` instance passed in [new]. An output size of |EVP_MAX_MD_SIZE| will |
| + /// always be large enough. |
| + #[corresponds(HMAC_Final)] |
| + pub fn finalize(&mut self, output: &mut [u8]) -> Result<usize, ErrorStack> { |
| + assert!(output.len() >= self.output_size); |
| + unsafe { |
| + // Safety: The length assertion above makes sure that `HMAC_Final` will not write longer |
| + // than the length of `output`. |
| + let mut size: c_uint = 0; |
| + cvt(ffi::HMAC_Final( |
| + self.ctx, |
| + output.as_mut_ptr(), |
| + &mut size as *mut c_uint, |
| + )) |
| + .map(|_| size as usize) |
| + } |
| + } |
| +} |
| + |
| +impl Drop for HmacCtx { |
| + #[corresponds(HMAC_CTX_free)] |
| + fn drop(&mut self) { |
| + unsafe { |
| + ffi::HMAC_CTX_free(self.ctx); |
| + } |
| + } |
| +} |
| + |
| +#[cfg(test)] |
| +mod tests { |
| + use super::*; |
| + use crate::md::Md; |
| + |
| + const SHA_256_DIGEST_SIZE: usize = 32; |
| + |
| + #[test] |
| + fn hmac_sha256_test() { |
| + let expected_hmac = [ |
| + 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb, |
| + 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c, |
| + 0x2e, 0x32, 0xcf, 0xf7, |
| + ]; |
| + let mut out: [u8; SHA_256_DIGEST_SIZE] = [0; SHA_256_DIGEST_SIZE]; |
| + let key: [u8; 20] = [0x0b; 20]; |
| + let data = b"Hi There"; |
| + let hmac_result = |
| + hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac"); |
| + assert_eq!(&hmac_result, &expected_hmac); |
| + } |
| + |
| + #[test] |
| + #[should_panic] |
| + fn hmac_sha256_output_too_short() { |
| + let mut out = vec![0_u8; 1]; |
| + let key: [u8; 20] = [0x0b; 20]; |
| + let data = b"Hi There"; |
| + hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac"); |
| + } |
| + |
| + #[test] |
| + fn hmac_sha256_test_big_buffer() { |
| + let expected_hmac = [ |
| + 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb, |
| + 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c, |
| + 0x2e, 0x32, 0xcf, 0xf7, |
| + ]; |
| + let mut out: [u8; 100] = [0; 100]; |
| + let key: [u8; 20] = [0x0b; 20]; |
| + let data = b"Hi There"; |
| + let hmac_result = |
| + hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac"); |
| + assert_eq!(hmac_result.len(), SHA_256_DIGEST_SIZE); |
| + assert_eq!(&hmac_result, &expected_hmac); |
| + } |
| + |
| + #[test] |
| + fn hmac_sha256_update_test() { |
| + let expected_hmac = [ |
| + 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb, |
| + 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c, |
| + 0x2e, 0x32, 0xcf, 0xf7, |
| + ]; |
| + let mut out: [u8; SHA_256_DIGEST_SIZE] = [0; SHA_256_DIGEST_SIZE]; |
| + let key: [u8; 20] = [0x0b; 20]; |
| + let data = b"Hi There"; |
| + let mut hmac_ctx = HmacCtx::new(&key, Md::sha256()).unwrap(); |
| + hmac_ctx.update(data).unwrap(); |
| + let size = hmac_ctx.finalize(&mut out).unwrap(); |
| + assert_eq!(&out, &expected_hmac); |
| + assert_eq!(size, SHA_256_DIGEST_SIZE); |
| + } |
| + |
| + #[test] |
| + fn hmac_sha256_update_chunks_test() { |
| + let expected_hmac = [ |
| + 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb, |
| + 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c, |
| + 0x2e, 0x32, 0xcf, 0xf7, |
| + ]; |
| + let mut out: [u8; SHA_256_DIGEST_SIZE] = [0; SHA_256_DIGEST_SIZE]; |
| + let key: [u8; 20] = [0x0b; 20]; |
| + let mut hmac_ctx = HmacCtx::new(&key, Md::sha256()).unwrap(); |
| + hmac_ctx.update(b"Hi").unwrap(); |
| + hmac_ctx.update(b" There").unwrap(); |
| + let size = hmac_ctx.finalize(&mut out).unwrap(); |
| + assert_eq!(&out, &expected_hmac); |
| + assert_eq!(size, SHA_256_DIGEST_SIZE); |
| + } |
| + |
| + #[test] |
| + #[should_panic] |
| + fn hmac_sha256_update_output_too_short() { |
| + let mut out = vec![0_u8; 1]; |
| + let key: [u8; 20] = [0x0b; 20]; |
| + let mut hmac_ctx = HmacCtx::new(&key, Md::sha256()).unwrap(); |
| + hmac_ctx.update(b"Hi There").unwrap(); |
| + hmac_ctx.finalize(&mut out).unwrap(); |
| + } |
| +} |
| diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs |
| index 035c90c6..02a51dbb 100644 |
| --- a/openssl/src/lib.rs |
| +++ b/openssl/src/lib.rs |
| @@ -120,6 +120,9 @@ |
| #![doc(html_root_url = "https://docs.rs/openssl/0.10")] |
| #![warn(rust_2018_idioms)] |
| |
| +#[cfg(all(soong, boringssl))] |
| +extern crate bssl_ffi as ffi; |
| + |
| #[doc(inline)] |
| pub use ffi::init; |
| |
| @@ -155,6 +158,10 @@ pub mod ex_data; |
| #[cfg(not(any(libressl, ossl300)))] |
| pub mod fips; |
| pub mod hash; |
| +#[cfg(boringssl)] |
| +pub mod hkdf; |
| +#[cfg(boringssl)] |
| +pub mod hmac; |
| #[cfg(ossl300)] |
| pub mod lib_ctx; |
| pub mod md; |
| @@ -189,6 +196,11 @@ type LenType = libc::size_t; |
| #[cfg(not(boringssl))] |
| type LenType = libc::c_int; |
| |
| +#[cfg(boringssl)] |
| +type SignedLenType = libc::ssize_t; |
| +#[cfg(not(boringssl))] |
| +type SignedLenType = libc::c_int; |
| + |
| #[inline] |
| fn cvt_p<T>(r: *mut T) -> Result<*mut T, ErrorStack> { |
| if r.is_null() { |
| diff --git a/openssl/src/pkey.rs b/openssl/src/pkey.rs |
| index 780bd637..92daa882 100644 |
| --- a/openssl/src/pkey.rs |
| +++ b/openssl/src/pkey.rs |
| @@ -47,7 +47,7 @@ use crate::dh::Dh; |
| use crate::dsa::Dsa; |
| use crate::ec::EcKey; |
| use crate::error::ErrorStack; |
| -#[cfg(ossl110)] |
| +#[cfg(any(boringssl, ossl110))] |
| use crate::pkey_ctx::PkeyCtx; |
| use crate::rsa::Rsa; |
| use crate::symm::Cipher; |
| @@ -89,11 +89,11 @@ impl Id { |
| #[cfg(ossl110)] |
| pub const HKDF: Id = Id(ffi::EVP_PKEY_HKDF); |
| |
| - #[cfg(ossl111)] |
| + #[cfg(any(boringssl, ossl111))] |
| pub const ED25519: Id = Id(ffi::EVP_PKEY_ED25519); |
| #[cfg(ossl111)] |
| pub const ED448: Id = Id(ffi::EVP_PKEY_ED448); |
| - #[cfg(ossl111)] |
| + #[cfg(any(boringssl, ossl111))] |
| pub const X25519: Id = Id(ffi::EVP_PKEY_X25519); |
| #[cfg(ossl111)] |
| pub const X448: Id = Id(ffi::EVP_PKEY_X448); |
| @@ -252,7 +252,7 @@ where |
| /// 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(ossl111)] |
| + #[cfg(any(boringssl, ossl111))] |
| pub fn raw_public_key(&self) -> Result<Vec<u8>, ErrorStack> { |
| unsafe { |
| let mut len = 0; |
| @@ -303,7 +303,7 @@ where |
| /// 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(ossl111)] |
| + #[cfg(any(boringssl, ossl111))] |
| pub fn raw_private_key(&self) -> Result<Vec<u8>, ErrorStack> { |
| unsafe { |
| let mut len = 0; |
| @@ -503,7 +503,7 @@ impl PKey<Private> { |
| ctx.keygen() |
| } |
| |
| - #[cfg(ossl111)] |
| + #[cfg(any(boringssl, ossl111))] |
| fn generate_eddsa(id: Id) -> Result<PKey<Private>, ErrorStack> { |
| let mut ctx = PkeyCtx::new_id(id)?; |
| ctx.keygen_init()?; |
| @@ -533,7 +533,7 @@ impl PKey<Private> { |
| /// assert_eq!(secret.len(), 32); |
| /// # Ok(()) } |
| /// ``` |
| - #[cfg(ossl111)] |
| + #[cfg(any(boringssl, ossl111))] |
| pub fn generate_x25519() -> Result<PKey<Private>, ErrorStack> { |
| PKey::generate_eddsa(Id::X25519) |
| } |
| @@ -587,7 +587,7 @@ impl PKey<Private> { |
| /// assert_eq!(signature.len(), 64); |
| /// # Ok(()) } |
| /// ``` |
| - #[cfg(ossl111)] |
| + #[cfg(any(boringssl, ossl111))] |
| pub fn generate_ed25519() -> Result<PKey<Private>, ErrorStack> { |
| PKey::generate_eddsa(Id::ED25519) |
| } |
| @@ -737,7 +737,7 @@ impl PKey<Private> { |
| /// |
| /// Algorithm types that support raw private keys are HMAC, X25519, ED25519, X448 or ED448 |
| #[corresponds(EVP_PKEY_new_raw_private_key)] |
| - #[cfg(ossl111)] |
| + #[cfg(any(boringssl, ossl111))] |
| pub fn private_key_from_raw_bytes( |
| bytes: &[u8], |
| key_type: Id, |
| @@ -778,7 +778,7 @@ impl PKey<Public> { |
| /// |
| /// Algorithm types that support raw public keys are X25519, ED25519, X448 or ED448 |
| #[corresponds(EVP_PKEY_new_raw_public_key)] |
| - #[cfg(ossl111)] |
| + #[cfg(any(boringssl, ossl111))] |
| pub fn public_key_from_raw_bytes( |
| bytes: &[u8], |
| key_type: Id, |
| diff --git a/openssl/src/sign.rs b/openssl/src/sign.rs |
| index 9cfda481..e5b17a87 100644 |
| --- a/openssl/src/sign.rs |
| +++ b/openssl/src/sign.rs |
| @@ -290,7 +290,7 @@ impl<'a> Signer<'a> { |
| self.len_intern() |
| } |
| |
| - #[cfg(not(ossl111))] |
| + #[cfg(not(any(boringssl, ossl111)))] |
| fn len_intern(&self) -> Result<usize, ErrorStack> { |
| unsafe { |
| let mut len = 0; |
| @@ -303,7 +303,7 @@ impl<'a> Signer<'a> { |
| } |
| } |
| |
| - #[cfg(ossl111)] |
| + #[cfg(any(boringssl, ossl111))] |
| fn len_intern(&self) -> Result<usize, ErrorStack> { |
| unsafe { |
| let mut len = 0; |
| @@ -360,7 +360,7 @@ impl<'a> Signer<'a> { |
| /// OpenSSL documentation at [`EVP_DigestSign`]. |
| /// |
| /// [`EVP_DigestSign`]: https://www.openssl.org/docs/man1.1.1/man3/EVP_DigestSign.html |
| - #[cfg(ossl111)] |
| + #[cfg(any(boringssl, ossl111))] |
| pub fn sign_oneshot( |
| &mut self, |
| sig_buf: &mut [u8], |
| @@ -382,7 +382,7 @@ impl<'a> Signer<'a> { |
| /// Returns the signature. |
| /// |
| /// This is a simple convenience wrapper over `len` and `sign_oneshot`. |
| - #[cfg(ossl111)] |
| + #[cfg(any(boringssl, ossl111))] |
| 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)?; |
| @@ -596,7 +596,7 @@ impl<'a> Verifier<'a> { |
| /// OpenSSL documentation at [`EVP_DigestVerify`]. |
| /// |
| /// [`EVP_DigestVerify`]: https://www.openssl.org/docs/man1.1.1/man3/EVP_DigestVerify.html |
| - #[cfg(ossl111)] |
| + #[cfg(any(boringssl, ossl111))] |
| pub fn verify_oneshot(&mut self, signature: &[u8], buf: &[u8]) -> Result<bool, ErrorStack> { |
| unsafe { |
| let r = ffi::EVP_DigestVerify( |
| diff --git a/openssl/src/symm.rs b/openssl/src/symm.rs |
| index 911a7ab2..75b0365c 100644 |
| --- a/openssl/src/symm.rs |
| +++ b/openssl/src/symm.rs |
| @@ -119,6 +119,7 @@ impl Cipher { |
| unsafe { Cipher(ffi::EVP_aes_128_cfb1()) } |
| } |
| |
| + #[cfg(not(boringssl))] |
| pub fn aes_128_cfb128() -> Cipher { |
| unsafe { Cipher(ffi::EVP_aes_128_cfb128()) } |
| } |
| @@ -164,6 +165,7 @@ impl Cipher { |
| unsafe { Cipher(ffi::EVP_aes_192_cfb1()) } |
| } |
| |
| + #[cfg(not(boringssl))] |
| pub fn aes_192_cfb128() -> Cipher { |
| unsafe { Cipher(ffi::EVP_aes_192_cfb128()) } |
| } |
| @@ -214,6 +216,7 @@ impl Cipher { |
| unsafe { Cipher(ffi::EVP_aes_256_cfb1()) } |
| } |
| |
| + #[cfg(not(boringssl))] |
| pub fn aes_256_cfb128() -> Cipher { |
| unsafe { Cipher(ffi::EVP_aes_256_cfb128()) } |
| } |
| @@ -242,12 +245,12 @@ impl Cipher { |
| unsafe { Cipher(ffi::EVP_aes_256_ocb()) } |
| } |
| |
| - #[cfg(not(osslconf = "OPENSSL_NO_BF"))] |
| + #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_BF")))] |
| pub fn bf_cbc() -> Cipher { |
| unsafe { Cipher(ffi::EVP_bf_cbc()) } |
| } |
| |
| - #[cfg(not(osslconf = "OPENSSL_NO_BF"))] |
| + #[cfg(not(any(boringssl, osslconf = "OPENSSL_NO_BF")))] |
| pub fn bf_ecb() -> Cipher { |
| unsafe { Cipher(ffi::EVP_bf_ecb()) } |
| } |
| diff --git a/openssl/src/x509/mod.rs b/openssl/src/x509/mod.rs |
| index 940c8c9c..f9477e4a 100644 |
| --- a/openssl/src/x509/mod.rs |
| +++ b/openssl/src/x509/mod.rs |
| @@ -356,6 +356,19 @@ impl X509Builder { |
| unsafe { cvt(ffi::X509_sign(self.0.as_ptr(), key.as_ptr(), hash.as_ptr())).map(|_| ()) } |
| } |
| |
| + /// Signs the certificate with a private key but without a digest. |
| + /// |
| + /// This is the only way to sign with Ed25519 keys as BoringSSL doesn't support the null |
| + /// message digest. |
| + #[cfg(boringssl)] |
| + #[corresponds(X509_sign)] |
| + pub fn sign_without_digest<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack> |
| + where |
| + T: HasPrivate, |
| + { |
| + unsafe { cvt(ffi::X509_sign(self.0.as_ptr(), key.as_ptr(), ptr::null())).map(|_| ()) } |
| + } |
| + |
| /// Consumes the builder, returning the certificate. |
| pub fn build(self) -> X509 { |
| self.0 |
| @@ -898,13 +911,13 @@ impl X509NameBuilder { |
| pub fn append_entry_by_text(&mut self, field: &str, value: &str) -> Result<(), ErrorStack> { |
| unsafe { |
| let field = CString::new(field).unwrap(); |
| - assert!(value.len() <= c_int::max_value() as usize); |
| + assert!(value.len() <= isize::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 c_int, |
| + value.len() as isize, |
| -1, |
| 0, |
| )) |
| @@ -925,13 +938,13 @@ impl X509NameBuilder { |
| ) -> Result<(), ErrorStack> { |
| unsafe { |
| let field = CString::new(field).unwrap(); |
| - assert!(value.len() <= c_int::max_value() as usize); |
| + assert!(value.len() <= isize::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 c_int, |
| + value.len() as isize, |
| -1, |
| 0, |
| )) |
| @@ -946,13 +959,13 @@ impl X509NameBuilder { |
| /// [`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() <= c_int::max_value() as usize); |
| + assert!(value.len() <= isize::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 c_int, |
| + value.len() as isize, |
| -1, |
| 0, |
| )) |
| @@ -972,13 +985,13 @@ impl X509NameBuilder { |
| ty: Asn1Type, |
| ) -> Result<(), ErrorStack> { |
| unsafe { |
| - assert!(value.len() <= c_int::max_value() as usize); |
| + assert!(value.len() <= isize::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 c_int, |
| + value.len() as isize, |
| -1, |
| 0, |
| )) |
| @@ -1286,6 +1299,29 @@ impl X509ReqBuilder { |
| } |
| } |
| |
| + /// Sign the request using a private key without a digest. |
| + /// |
| + /// This is the only way to sign with Ed25519 keys as BoringSSL doesn't support the null |
| + /// message digest. |
| + /// |
| + /// This corresponds to [`X509_REQ_sign`]. |
| + /// |
| + /// [`X509_REQ_sign`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_REQ_sign.html |
| + #[cfg(boringssl)] |
| + pub fn sign_without_digest<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack> |
| + where |
| + T: HasPrivate, |
| + { |
| + unsafe { |
| + cvt(ffi::X509_REQ_sign( |
| + self.0.as_ptr(), |
| + key.as_ptr(), |
| + ptr::null(), |
| + )) |
| + .map(|_| ()) |
| + } |
| + } |
| + |
| /// Returns the `X509Req`. |
| pub fn build(self) -> X509Req { |
| self.0 |
| diff --git a/openssl/src/x509/mod.rs.orig b/openssl/src/x509/mod.rs.orig |
| new file mode 100644 |
| index 00000000..34b86c10 |
| --- /dev/null |
| +++ b/openssl/src/x509/mod.rs.orig |
| @@ -0,0 +1,1879 @@ |
| +//! 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}; |
| +use std::cmp::{self, Ordering}; |
| +use std::convert::TryFrom; |
| +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, Asn1IntegerRef, Asn1ObjectRef, 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; |
| + |
| +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(|_| ()) } |
| + } |
| + |
| + /// Signs the certificate with a private key but without a digest. |
| + /// |
| + /// This is the only way to sign with Ed25519 keys as BoringSSL doesn't support the null |
| + /// message digest. |
| + #[cfg(boringssl)] |
| + #[corresponds(X509_sign)] |
| + pub fn sign_without_digest<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack> |
| + where |
| + T: HasPrivate, |
| + { |
| + unsafe { cvt(ffi::X509_sign(self.0.as_ptr(), key.as_ptr(), ptr::null())).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 { |
| + 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 { |
| + 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 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 _) |
| + } |
| + } |
| + |
| + #[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)] |
| + 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. |
| + /// |
| + /// See the extension module for builder types which will construct certain common extensions. |
| + 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(); |
| + unsafe { |
| + ffi::init(); |
| + let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr); |
| + let context = context.map_or(ptr::null_mut(), X509v3Context::as_ptr); |
| + let name = name.as_ptr() as *mut _; |
| + let value = value.as_ptr() as *mut _; |
| + |
| + cvt_p(ffi::X509V3_EXT_nconf(conf, context, 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. |
| + /// |
| + /// See the extension module for builder types which will construct certain common extensions. |
| + pub fn new_nid( |
| + conf: Option<&ConfRef>, |
| + context: Option<&X509v3Context<'_>>, |
| + name: Nid, |
| + value: &str, |
| + ) -> Result<X509Extension, ErrorStack> { |
| + let value = CString::new(value).unwrap(); |
| + unsafe { |
| + ffi::init(); |
| + let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr); |
| + let context = context.map_or(ptr::null_mut(), X509v3Context::as_ptr); |
| + let name = name.as_raw(); |
| + let value = value.as_ptr() as *mut _; |
| + |
| + cvt_p(ffi::X509V3_EXT_nconf_nid(conf, context, name, 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)] |
| + 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(|_| ()) |
| + } |
| +} |
| + |
| +/// 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() <= c_int::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 c_int, |
| + -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() <= c_int::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 c_int, |
| + -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() <= c_int::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 c_int, |
| + -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() <= c_int::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 c_int, |
| + -1, |
| + 0, |
| + )) |
| + .map(|_| ()) |
| + } |
| + } |
| + |
| + /// Return an `X509Name`. |
| + pub fn build(self) -> X509Name { |
| + self.0 |
| + } |
| +} |
| + |
| +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(|_| ()) |
| + } |
| + } |
| + |
| + /// Sign the request using a private key without a digest. |
| + /// |
| + /// This is the only way to sign with Ed25519 keys as BoringSSL doesn't support the null |
| + /// message digest. |
| + /// |
| + /// This corresponds to [`X509_REQ_sign`]. |
| + /// |
| + /// [`X509_REQ_sign`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_REQ_sign.html |
| + #[cfg(boringssl)] |
| + pub fn sign_without_digest<T>(&mut self, key: &PKeyRef<T>) -> Result<(), ErrorStack> |
| + where |
| + T: HasPrivate, |
| + { |
| + unsafe { |
| + cvt(ffi::X509_REQ_sign( |
| + self.0.as_ptr(), |
| + key.as_ptr(), |
| + ptr::null(), |
| + )) |
| + .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 |
| + 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 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 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 _); |
| + |
| + 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 `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 _); |
| + |
| + 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::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); |
| + } |
| + } |
| +} |
| + |
| +#[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) |
| + } |
| + } |
| +} |
| -- |
| 2.40.1.495.gc816e09b53d-goog |
| |