| diff --git a/.cargo/config.toml b/.cargo/config.toml |
| new file mode 100644 |
| index 0000000..e2b197d |
| --- /dev/null |
| +++ b/.cargo/config.toml |
| @@ -0,0 +1,2 @@ |
| +[patch.crates-io] |
| +bssl-ffi = { package = "bssl-sys", version = "0.1.0", path = "../../../boringssl/build/rust", optional=true } |
| diff --git a/src/cipher.rs b/src/cipher.rs |
| index ab5f49d..84a8265 100644 |
| --- a/src/cipher.rs |
| +++ b/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/src/ec.rs b/src/ec.rs |
| index a6a6dc9..88dcaaf 100644 |
| --- a/src/ec.rs |
| +++ b/src/ec.rs |
| @@ -908,6 +908,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/src/encrypt.rs b/src/encrypt.rs |
| index 3cb10fc..34a9eb8 100644 |
| --- a/src/encrypt.rs |
| +++ b/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/src/hkdf.rs b/src/hkdf.rs |
| new file mode 100644 |
| index 0000000..cc7e5b3 |
| --- /dev/null |
| +++ b/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/src/hmac.rs b/src/hmac.rs |
| new file mode 100644 |
| index 0000000..601ae01 |
| --- /dev/null |
| +++ b/src/hmac.rs |
| @@ -0,0 +1,68 @@ |
| +use crate::cvt_p; |
| +use crate::error::ErrorStack; |
| +use crate::md::MdRef; |
| +use foreign_types::ForeignTypeRef; |
| +use openssl_macros::corresponds; |
| +use libc::{c_void, c_uint}; |
| +use std::convert::TryFrom; |
| + |
| +/// 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> { |
| + 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]) |
| +} |
| + |
| +#[cfg(test)] |
| +mod tests { |
| + use super::*; |
| + use crate::md::Md; |
| + use crate::memcmp; |
| + |
| + 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"); |
| + expect!(memcmp::eq(&hmac_result, &expected_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"); |
| + expect_eq!(hmac_result.len(), SHA_256_DIGEST_SIZE); |
| + expect!(memcmp::eq(&hmac_result, &expected_hmac)); |
| + } |
| +} |
| diff --git a/src/lib.rs b/src/lib.rs |
| index 891651e..48b7ed1 100644 |
| --- a/src/lib.rs |
| +++ b/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; |
| diff --git a/src/pkey.rs b/src/pkey.rs |
| index 7d438eb..7eaf068 100644 |
| --- a/src/pkey.rs |
| +++ b/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); |
| @@ -243,7 +243,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; |
| @@ -294,7 +294,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; |
| @@ -475,7 +475,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()?; |
| @@ -505,7 +505,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) |
| } |
| @@ -559,7 +559,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) |
| } |
| @@ -709,7 +709,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, |
| @@ -750,7 +750,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/src/sign.rs b/src/sign.rs |
| index 457ff12..4de8ad0 100644 |
| --- a/src/sign.rs |
| +++ b/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)?; |
| @@ -594,7 +594,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/src/symm.rs b/src/symm.rs |
| index c75bbc0..beff5fc 100644 |
| --- a/src/symm.rs |
| +++ b/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/src/x509/mod.rs b/src/x509/mod.rs |
| index edd54aa..45f2467 100644 |
| --- a/src/x509/mod.rs |
| +++ b/src/x509/mod.rs |
| @@ -353,6 +353,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 |
| @@ -1260,6 +1273,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 |