diff --git a/.circleci/config.yml b/.circleci/config.yml index af6536da0a860402d4618a04f0b6df12e1792abe..4363e37697df997b6c4f2c9f680dde0f196554fa 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -92,7 +92,7 @@ macos_job: &MACOS_JOB openssl_111: &OPENSSL_111 LIBRARY: openssl - VERSION: 1.1.1-pre1 + VERSION: 1.1.1-pre2 openssl_110: &OPENSSL_110 LIBRARY: openssl VERSION: 1.1.0g diff --git a/openssl-sys/src/lib.rs b/openssl-sys/src/lib.rs index d0697435b4da77331f532805c18a9c4ec6a8641f..5a09eae13db6fbe6fd144961c883a4bd21cefa6e 100644 --- a/openssl-sys/src/lib.rs +++ b/openssl-sys/src/lib.rs @@ -228,9 +228,24 @@ pub const EVP_PKEY_EC: c_int = NID_X9_62_id_ecPublicKey; pub const EVP_PKEY_ALG_CTRL: c_int = 0x1000; pub const EVP_PKEY_CTRL_RSA_PADDING: c_int = EVP_PKEY_ALG_CTRL + 1; +pub const EVP_PKEY_CTRL_RSA_PSS_SALTLEN: c_int = EVP_PKEY_ALG_CTRL + 2; +pub const EVP_PKEY_CTRL_RSA_MGF1_MD: c_int = EVP_PKEY_ALG_CTRL + 5; pub const EVP_PKEY_CTRL_GET_RSA_PADDING: c_int = EVP_PKEY_ALG_CTRL + 6; +pub const EVP_PKEY_OP_SIGN: c_int = 1 << 3; +pub const EVP_PKEY_OP_VERIFY: c_int = 1 << 4; +pub const EVP_PKEY_OP_VERIFYRECOVER: c_int = 1 << 5; +pub const EVP_PKEY_OP_SIGNCTX: c_int = 1 << 6; +pub const EVP_PKEY_OP_VERIFYCTX: c_int = 1 << 7; +pub const EVP_PKEY_OP_ENCRYPT: c_int = 1 << 8; +pub const EVP_PKEY_OP_DECRYPT: c_int = 1 << 9; + +pub const EVP_PKEY_OP_TYPE_SIG: c_int = EVP_PKEY_OP_SIGN | EVP_PKEY_OP_VERIFY + | EVP_PKEY_OP_VERIFYRECOVER | EVP_PKEY_OP_SIGNCTX | EVP_PKEY_OP_VERIFYCTX; + +pub const EVP_PKEY_OP_TYPE_CRYPT: c_int = EVP_PKEY_OP_ENCRYPT | EVP_PKEY_OP_DECRYPT; + pub const EVP_CTRL_GCM_SET_IVLEN: c_int = 0x9; pub const EVP_CTRL_GCM_GET_TAG: c_int = 0x10; pub const EVP_CTRL_GCM_SET_TAG: c_int = 0x11; @@ -1200,6 +1215,7 @@ pub const RSA_SSLV23_PADDING: c_int = 2; pub const RSA_NO_PADDING: c_int = 3; pub const RSA_PKCS1_OAEP_PADDING: c_int = 4; pub const RSA_X931_PADDING: c_int = 5; +pub const RSA_PKCS1_PSS_PADDING: c_int = 6; pub const SHA_LBLOCK: c_int = 16; @@ -1483,6 +1499,28 @@ pub unsafe fn EVP_PKEY_CTX_get_rsa_padding(ctx: *mut EVP_PKEY_CTX, ppad: *mut c_ ) } +pub unsafe fn EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx: *mut EVP_PKEY_CTX, len: c_int) -> c_int { + EVP_PKEY_CTX_ctrl( + ctx, + EVP_PKEY_RSA, + EVP_PKEY_OP_SIGN | EVP_PKEY_OP_VERIFY, + EVP_PKEY_CTRL_RSA_PSS_SALTLEN, + len, + ptr::null_mut(), + ) +} + +pub unsafe fn EVP_PKEY_CTX_set_rsa_mgf1_md(ctx: *mut EVP_PKEY_CTX, md: *mut EVP_MD) -> c_int { + EVP_PKEY_CTX_ctrl( + ctx, + EVP_PKEY_RSA, + EVP_PKEY_OP_TYPE_SIG | EVP_PKEY_OP_TYPE_CRYPT, + EVP_PKEY_CTRL_RSA_MGF1_MD, + 0, + md as *mut c_void, + ) +} + pub unsafe fn SSL_CTX_set_mode(ctx: *mut SSL_CTX, op: c_long) -> c_long { SSL_CTX_ctrl(ctx, SSL_CTRL_MODE, op, ptr::null_mut()) } @@ -1882,6 +1920,12 @@ extern "C" { ctx: *mut BN_CTX, ) -> c_int; + pub fn ECDSA_SIG_new() -> *mut ECDSA_SIG; + pub fn ECDSA_SIG_free(sig: *mut ECDSA_SIG); + pub fn ECDSA_do_verify(dgst: *const c_uchar, dgst_len: c_int, + sig: *const ECDSA_SIG, eckey: *mut EC_KEY) -> c_int; + pub fn ECDSA_do_sign(dgst: *const c_uchar, dgst_len: c_int, eckey: *mut EC_KEY) -> *mut ECDSA_SIG; + pub fn ERR_peek_last_error() -> c_ulong; pub fn ERR_get_error() -> c_ulong; pub fn ERR_get_error_line_data( @@ -2029,6 +2073,7 @@ extern "C" { pub fn EVP_PKEY_get1_DH(k: *mut EVP_PKEY) -> *mut DH; pub fn EVP_PKEY_get1_EC_KEY(k: *mut EVP_PKEY) -> *mut EC_KEY; pub fn EVP_PKEY_cmp(a: *const EVP_PKEY, b: *const EVP_PKEY) -> c_int; + pub fn EVP_PKEY_id(pkey: *const EVP_PKEY) -> c_int; pub fn EVP_PKEY_new_mac_key( type_: c_int, e: *mut ENGINE, @@ -2611,6 +2656,7 @@ extern "C" { pub fn X509_sign(x: *mut X509, pkey: *mut EVP_PKEY, md: *const EVP_MD) -> c_int; pub fn X509_get_pubkey(x: *mut X509) -> *mut EVP_PKEY; pub fn X509_to_X509_REQ(x: *mut X509, pkey: *mut EVP_PKEY, md: *const EVP_MD) -> *mut X509_REQ; + pub fn X509_verify_cert(ctx: *mut X509_STORE_CTX) -> c_int; pub fn X509_verify_cert_error_string(n: c_long) -> *const c_char; pub fn X509_get1_ocsp(x: *mut X509) -> *mut stack_st_OPENSSL_STRING; pub fn X509_check_issued(issuer: *mut X509, subject: *mut X509) -> c_int; @@ -2644,6 +2690,9 @@ extern "C" { pub fn X509_STORE_add_cert(store: *mut X509_STORE, x: *mut X509) -> c_int; pub fn X509_STORE_set_default_paths(store: *mut X509_STORE) -> c_int; + pub fn X509_STORE_CTX_new() -> *mut X509_STORE_CTX; + pub fn X509_STORE_CTX_cleanup(ctx: *mut X509_STORE_CTX); + pub fn X509_STORE_CTX_init(ctx: *mut X509_STORE_CTX, store: *mut X509_STORE, x509: *mut X509, chain: *mut stack_st_X509) -> c_int; pub fn X509_STORE_CTX_free(ctx: *mut X509_STORE_CTX); pub fn X509_STORE_CTX_get_current_cert(ctx: *mut X509_STORE_CTX) -> *mut X509; pub fn X509_STORE_CTX_get_error(ctx: *mut X509_STORE_CTX) -> c_int; diff --git a/openssl-sys/src/libressl/mod.rs b/openssl-sys/src/libressl/mod.rs index 8454a78ac7a939145448501771192efbe8642b95..c47363ca2923034192581879981dc1ad85e62b60 100644 --- a/openssl-sys/src/libressl/mod.rs +++ b/openssl-sys/src/libressl/mod.rs @@ -133,6 +133,12 @@ pub struct DSA { pub engine: *mut ::ENGINE, } +#[repr(C)] +pub struct ECDSA_SIG { + pub r: *mut ::BIGNUM, + pub s: *mut ::BIGNUM +} + #[repr(C)] pub struct EVP_PKEY { pub type_: c_int, diff --git a/openssl-sys/src/ossl10x.rs b/openssl-sys/src/ossl10x.rs index 87024ac41d851960e5db5e67459c6a41100ee21e..9b79fb5703065a7e990b751fdcd16c8adab54710 100644 --- a/openssl-sys/src/ossl10x.rs +++ b/openssl-sys/src/ossl10x.rs @@ -128,6 +128,12 @@ pub struct DSA { pub engine: *mut ::ENGINE, } +#[repr(C)] +pub struct ECDSA_SIG { + pub r: *mut BIGNUM, + pub s: *mut BIGNUM +} + #[repr(C)] pub struct EVP_PKEY { pub type_: c_int, diff --git a/openssl-sys/src/ossl110.rs b/openssl-sys/src/ossl110.rs index 1ab307723323705066e8151f124ff74a5bb78650..580c3f724943ca57b5f3424969ff92f4550fd3d6 100644 --- a/openssl-sys/src/ossl110.rs +++ b/openssl-sys/src/ossl110.rs @@ -8,6 +8,7 @@ pub enum BIO_METHOD {} pub enum CRYPTO_EX_DATA {} pub enum DH {} pub enum DSA {} +pub enum ECDSA_SIG {} pub enum EVP_CIPHER {} pub enum EVP_MD_CTX {} pub enum EVP_PKEY {} @@ -364,4 +365,6 @@ extern "C" { pub fn X509_REQ_get_version(req: *const X509_REQ) -> c_long; pub fn X509_REQ_get_subject_name(req: *const X509_REQ) -> *mut ::X509_NAME; pub fn SSL_extension_supported(ext_type: c_uint) -> c_int; + pub fn ECDSA_SIG_get0(sig: *const ECDSA_SIG, pr: *mut *const BIGNUM, ps: *mut *const BIGNUM); + pub fn ECDSA_SIG_set0(sig: *mut ECDSA_SIG, pr: *mut BIGNUM, ps: *mut BIGNUM) -> c_int; } diff --git a/openssl/src/derive.rs b/openssl/src/derive.rs index fb7bbf140e13da8f8e5ad362c9efa2bae915f1e8..30d7dc2570ccd921528453a48cb31ef7b6c64efc 100644 --- a/openssl/src/derive.rs +++ b/openssl/src/derive.rs @@ -11,6 +11,9 @@ use pkey::{HasPrivate, HasPublic, PKeyRef}; /// A type used to derive a shared secret between two keys. pub struct Deriver<'a>(*mut ffi::EVP_PKEY_CTX, PhantomData<&'a ()>); +unsafe impl<'a> Sync for Deriver<'a> {} +unsafe impl<'a> Send for Deriver<'a> {} + impl<'a> Deriver<'a> { /// Creates a new `Deriver` using the provided private key. /// diff --git a/openssl/src/ecdsa.rs b/openssl/src/ecdsa.rs new file mode 100644 index 0000000000000000000000000000000000000000..b220350c140e727ec539e797be298618a86a28fa --- /dev/null +++ b/openssl/src/ecdsa.rs @@ -0,0 +1,196 @@ +//! Low level Elliptic Curve Digital Signature Algorithm (ECDSA) functions. +//! + + +use bn::{BigNum, BigNumRef}; +use {cvt, cvt_n, cvt_p}; +use ec::EcKeyRef; +use error::ErrorStack; +use ffi; +use foreign_types::{ForeignType, ForeignTypeRef}; +use pkey::{Private, Public}; +use std::mem; + + +foreign_type_and_impl_send_sync! { + type CType = ffi::ECDSA_SIG; + fn drop = ffi::ECDSA_SIG_free; + + /// A low level interface to ECDSA + /// + /// OpenSSL documentation at [`ECDSA_sign`] + /// + /// [`ECDSA_sign`]: https://www.openssl.org/docs/man1.1.0/crypto/ECDSA_sign.html + pub struct EcdsaSig; + /// Reference to [`EcdsaSig`] + /// + /// [`EcdsaSig`]: struct.EcdsaSig.html + pub struct EcdsaSigRef; +} + +impl EcdsaSig { + + /// Computes a digital signature of the hash value `data` using the private EC key eckey. + /// + /// OpenSSL documentation at [`ECDSA_do_sign`] + /// + /// [`ECDSA_do_sign`]: https://www.openssl.org/docs/man1.1.0/crypto/ECDSA_do_sign.html + pub fn sign(data: &[u8], eckey: &EcKeyRef) -> Result { + unsafe { + let sig = cvt_p(ffi::ECDSA_do_sign(data.as_ptr(), data.len() as i32, eckey.as_ptr()))?; + Ok(EcdsaSig::from_ptr(sig as *mut _)) + } + } + + /// Returns a new `EcdsaSig` by setting the `r` and `s` values associated with a + /// ECDSA signature. + /// + /// OpenSSL documentation at [`ECDSA_SIG_set0`] + /// + /// [`ECDSA_SIG_set0`]: https://www.openssl.org/docs/man1.1.0/crypto/ECDSA_SIG_set0.html + pub fn from_private_components(r: BigNum, s: BigNum) -> Result { + unsafe { + let sig = cvt_p(ffi::ECDSA_SIG_new())?; + cvt(compat::set_numbers(sig, r.as_ptr(), s.as_ptr()))?; + mem::forget((r, s)); + Ok(EcdsaSig::from_ptr(sig as *mut _)) + } + } + + /// Verifies if the signature is a valid ECDSA signature using the given public key + /// + /// OpenSSL documentation at [`ECDSA_do_verify`] + /// + /// [`ECDSA_do_verify`]: https://www.openssl.org/docs/man1.1.0/crypto/ECDSA_do_verify.html + pub fn verify(&self, data: &[u8], eckey: &EcKeyRef) -> Result { + unsafe { + let x = cvt_n(ffi::ECDSA_do_verify(data.as_ptr(), data.len() as i32, self.as_ptr(), eckey.as_ptr()))?; + Ok(x == 1) + } + } + + /// Returns internal component: `r` of a `EcdsaSig`. (See X9.62 or FIPS 186-2) + /// + /// OpenSSL documentation at [`ECDSA_SIG_get0`] + /// + /// [`ECDSA_SIG_get0`]: https://www.openssl.org/docs/man1.1.0/crypto/ECDSA_SIG_get0.html + pub fn private_component_r(&self) -> Option<&BigNumRef> { + unsafe { + let xs = compat::get_numbers(self.as_ptr()); + let r = if xs[0].is_null() { None } else { Some(BigNumRef::from_ptr(xs[0] as *mut _)) }; + r + } + } + + /// Returns internal components: `s` of a `EcdsaSig`. (See X9.62 or FIPS 186-2) + /// + /// OpenSSL documentation at [`ECDSA_SIG_get0`] + /// + /// [`ECDSA_SIG_get0`]: https://www.openssl.org/docs/man1.1.0/crypto/ECDSA_SIG_get0.html + pub fn private_component_s(&self) -> Option<&BigNumRef> { + unsafe { + let xs = compat::get_numbers(self.as_ptr()); + let s = if xs[1].is_null() { None } else { Some(BigNumRef::from_ptr(xs[1] as *mut _)) }; + s + } + } + +} + +#[cfg(ossl110)] +mod compat { + use std::ptr; + + use libc::c_int; + use ffi::{self, BIGNUM, ECDSA_SIG}; + + pub unsafe fn set_numbers(sig: *mut ECDSA_SIG, r: *mut BIGNUM, s: *mut BIGNUM) -> c_int { + ffi::ECDSA_SIG_set0(sig, r, s) + } + + pub unsafe fn get_numbers(sig: *mut ECDSA_SIG) -> [*const BIGNUM; 2] { + let (mut r, mut s) = (ptr::null(), ptr::null()); + ffi::ECDSA_SIG_get0(sig, &mut r, &mut s); + [r, s] + } +} + +#[cfg(ossl10x)] +mod compat { + use libc::c_int; + use ffi::{BIGNUM, ECDSA_SIG}; + + pub unsafe fn set_numbers(sig: *mut ECDSA_SIG, r: *mut BIGNUM, s: *mut BIGNUM) -> c_int { + (*sig).r = r; + (*sig).s = s; + 1 + } + + pub unsafe fn get_numbers(sig: *mut ECDSA_SIG) -> [*const BIGNUM; 2] { + [(*sig).r, (*sig).s] + } + +} + +#[cfg(test)] +mod test { + use nid::Nid; + use ec::EcGroup; + use ec::EcKey; + use super::*; + + #[cfg(not(osslconf = "OPENSSL_NO_EC2M"))] + static CURVE_IDENTIFER: Nid = Nid::X9_62_PRIME192V1; + + #[cfg(osslconf = "OPENSSL_NO_EC2M")] + static CURVE_IDENTIFER: Nid = Nid::X9_62_C2TNB191V1; + + fn get_public_key(group: &EcGroup, x: &EcKey) -> Result, ErrorStack> { + let public_key_point = x.public_key(); + Ok(EcKey::from_public_key(group, public_key_point)?) + } + + #[test] + fn sign_and_verify() { + let group = EcGroup::from_curve_name(CURVE_IDENTIFER).unwrap(); + let private_key = EcKey::generate(&group).unwrap(); + let public_key = get_public_key(&group, &private_key).unwrap(); + + let private_key2 = EcKey::generate(&group).unwrap(); + let public_key2 = get_public_key(&group, &private_key2).unwrap(); + + let data = String::from("hello"); + let res = EcdsaSig::sign(data.as_bytes(), &private_key).unwrap(); + + // Signature can be verified using the correct data & correct public key + let verification = res.verify(data.as_bytes(), &public_key).unwrap(); + assert!(verification); + + // Signature will not be verified using the incorrect data but the correct public key + let verification2 = res.verify(String::from("hello2").as_bytes(), &public_key).unwrap(); + assert!(verification2 == false); + + // Signature will not be verified using the correct data but the incorrect public key + let verification3 = res.verify(data.as_bytes(), &public_key2).unwrap(); + assert!(verification3 == false); + } + + #[test] + fn check_private_components() { + let group = EcGroup::from_curve_name(CURVE_IDENTIFER).unwrap(); + let private_key = EcKey::generate(&group).unwrap(); + let public_key = get_public_key(&group, &private_key).unwrap(); + let data = String::from("hello"); + let res = EcdsaSig::sign(data.as_bytes(), &private_key).unwrap(); + + let verification = res.verify(data.as_bytes(), &public_key).unwrap(); + assert!(verification); + + let r = res.private_component_r().unwrap().to_owned().unwrap(); + let s = res.private_component_s().unwrap().to_owned().unwrap(); + + let res2 = EcdsaSig::from_private_components(r, s).unwrap(); + let verification2 = res2.verify(data.as_bytes(), &public_key).unwrap(); + assert!(verification2); + } +} \ No newline at end of file diff --git a/openssl/src/hash.rs b/openssl/src/hash.rs index 103a7ae3df8949d69b2a1b7d60df8bcbb94a2583..c6d4c862a673debc15b3b19d426633da6e8ec136 100644 --- a/openssl/src/hash.rs +++ b/openssl/src/hash.rs @@ -49,6 +49,9 @@ impl MessageDigest { } } +unsafe impl Sync for MessageDigest {} +unsafe impl Send for MessageDigest {} + #[derive(PartialEq, Copy, Clone)] enum State { Reset, @@ -99,6 +102,9 @@ pub struct Hasher { state: State, } +unsafe impl Sync for Hasher {} +unsafe impl Send for Hasher {} + impl Hasher { /// Creates a new `Hasher` with the specified hash type. pub fn new(ty: MessageDigest) -> Result { diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs index 321a301f244398e14618212a5e9f129ec0067778..e4b621eff74ab818357feef2079340100b830589 100644 --- a/openssl/src/lib.rs +++ b/openssl/src/lib.rs @@ -39,6 +39,7 @@ pub mod derive; pub mod dh; pub mod dsa; pub mod ec; +pub mod ecdsa; pub mod error; pub mod ex_data; #[cfg(not(libressl))] diff --git a/openssl/src/pkey.rs b/openssl/src/pkey.rs index 8f7c4e4e8e4405bcda57e8d90684b8850f671cfd..66ff33d62773e384922e89d4cbe312413b081045 100644 --- a/openssl/src/pkey.rs +++ b/openssl/src/pkey.rs @@ -70,6 +70,28 @@ pub enum Public {} /// A tag type indicating that a key has private components. pub enum Private {} +/// An identifier of a kind of key. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct Id(c_int); + +impl Id { + /// Creates a `Id` from an integer representation. + pub fn from_raw(value: c_int) -> Id { + Id(value) + } + + /// Returns the integer representation of the `Id`. + pub fn as_raw(&self) -> c_int { + self.0 + } + + pub const RSA: Id = Id(ffi::EVP_PKEY_RSA); + pub const HMAC: Id = Id(ffi::EVP_PKEY_HMAC); + pub const DSA: Id = Id(ffi::EVP_PKEY_DSA); + pub const DH: Id = Id(ffi::EVP_PKEY_DH); + pub const EC: Id = Id(ffi::EVP_PKEY_EC); +} + /// A trait indicating that a key has parameters. pub unsafe trait HasParams {} @@ -155,6 +177,17 @@ impl PKeyRef { Ok(EcKey::from_ptr(ec_key)) } } + + /// Returns the `Id` that represents the type of this key. + /// + /// This corresponds to [`EVP_PKEY_id`]. + /// + /// [`EVP_PKEY_id`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_id.html + pub fn id(&self) -> Id { + unsafe { + Id::from_raw(ffi::EVP_PKEY_id(self.as_ptr())) + } + } } impl PKeyRef @@ -531,6 +564,7 @@ mod tests { let rsa = Rsa::generate(2048).unwrap(); let pkey = PKey::from_rsa(rsa).unwrap(); pkey.rsa().unwrap(); + assert_eq!(pkey.id(), Id::RSA); assert!(pkey.dsa().is_err()); } @@ -539,6 +573,7 @@ mod tests { let dsa = Dsa::generate(2048).unwrap(); let pkey = PKey::from_dsa(dsa).unwrap(); pkey.dsa().unwrap(); + assert_eq!(pkey.id(), Id::DSA); assert!(pkey.rsa().is_err()); } @@ -548,6 +583,7 @@ mod tests { let dh = Dh::params_from_pem(dh).unwrap(); let pkey = PKey::from_dh(dh).unwrap(); pkey.dh().unwrap(); + assert_eq!(pkey.id(), Id::DH); assert!(pkey.rsa().is_err()); } @@ -556,6 +592,7 @@ mod tests { let ec_key = EcKey::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); let pkey = PKey::from_ec_key(ec_key).unwrap(); pkey.ec_key().unwrap(); + assert_eq!(pkey.id(), Id::EC); assert!(pkey.rsa().is_err()); } } diff --git a/openssl/src/rsa.rs b/openssl/src/rsa.rs index 02240948a91213cde31860a0eb873aed12e66832..6a591b69ab6cbd58cc77a7ac9c1d56142fcbe7fb 100644 --- a/openssl/src/rsa.rs +++ b/openssl/src/rsa.rs @@ -63,6 +63,7 @@ impl Padding { pub const NONE: Padding = Padding(ffi::RSA_NO_PADDING); pub const PKCS1: Padding = Padding(ffi::RSA_PKCS1_PADDING); pub const PKCS1_OAEP: Padding = Padding(ffi::RSA_PKCS1_OAEP_PADDING); + pub const PKCS1_PSS: Padding = Padding(ffi::RSA_PKCS1_PSS_PADDING); } generic_foreign_type_and_impl_send_sync! { diff --git a/openssl/src/sign.rs b/openssl/src/sign.rs index a61d883b5b278f87450e3324b4ba7c3d19cb64bf..6a49cb49fb1a6100423926b3d46f1d695f68f86b 100644 --- a/openssl/src/sign.rs +++ b/openssl/src/sign.rs @@ -66,6 +66,7 @@ use foreign_types::ForeignTypeRef; use std::io::{self, Write}; use std::marker::PhantomData; use std::ptr; +use libc::c_int; use {cvt, cvt_p}; use hash::MessageDigest; @@ -78,6 +79,28 @@ use ffi::{EVP_MD_CTX_free, EVP_MD_CTX_new}; #[cfg(any(ossl101, ossl102))] use ffi::{EVP_MD_CTX_create as EVP_MD_CTX_new, EVP_MD_CTX_destroy as EVP_MD_CTX_free}; +/// Salt lengths that must be used with `set_rsa_pss_saltlen`. +pub struct RsaPssSaltlen(c_int); + +impl RsaPssSaltlen { + /// Returns the integer representation of `RsaPssSaltlen`. + fn as_raw(&self) -> c_int { + self.0 + } + + /// Sets the salt length to the given value. + pub fn custom(val: c_int) -> RsaPssSaltlen { + RsaPssSaltlen(val) + } + + /// The salt length is set to the digest length. + /// Corresponds to the special value `-1`. + pub const DIGEST_LENGTH: RsaPssSaltlen = RsaPssSaltlen(-1); + /// The salt length is set to the maximum permissible value. + /// Corresponds to the special value `-2`. + pub const MAXIMUM_LENGTH: RsaPssSaltlen = RsaPssSaltlen(-2); +} + /// A type which computes cryptographic signatures of data. pub struct Signer<'a> { md_ctx: *mut ffi::EVP_MD_CTX, @@ -85,6 +108,9 @@ pub struct Signer<'a> { _p: PhantomData<&'a ()>, } +unsafe impl<'a> Sync for Signer<'a> {} +unsafe impl<'a> Send for Signer<'a> {} + impl<'a> Drop for Signer<'a> { fn drop(&mut self) { // pkey_ctx is owned by the md_ctx, so no need to explicitly free it. @@ -160,6 +186,38 @@ impl<'a> Signer<'a> { } } + /// Sets the RSA PSS salt length. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to [`EVP_PKEY_CTX_set_rsa_pss_saltlen`]. + /// + /// [`EVP_PKEY_CTX_set_rsa_pss_saltlen`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_CTX_set_rsa_pss_saltlen.html + pub fn set_rsa_pss_saltlen(&mut self, len: RsaPssSaltlen) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_rsa_pss_saltlen( + self.pctx, + len.as_raw(), + )).map(|_| ()) + } + } + + /// Sets the RSA MGF1 algorithm. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to [`EVP_PKEY_CTX_set_rsa_mgf1_md`]. + /// + /// [`EVP_PKEY_CTX_set_rsa_mgf1_md`]: https://www.openssl.org/docs/manmaster/man7/RSA-PSS.html + pub fn set_rsa_mgf1_md(&mut self, md: MessageDigest) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_rsa_mgf1_md( + self.pctx, + md.as_ptr() as *mut _, + )).map(|_| ()) + } + } + /// Feeds more data into the `Signer`. /// /// OpenSSL documentation at [`EVP_DigestUpdate`]. @@ -244,6 +302,9 @@ pub struct Verifier<'a> { pkey_pd: PhantomData<&'a ()>, } +unsafe impl<'a> Sync for Verifier<'a> {} +unsafe impl<'a> Send for Verifier<'a> {} + impl<'a> Drop for Verifier<'a> { fn drop(&mut self) { // pkey_ctx is owned by the md_ctx, so no need to explicitly free it. @@ -320,6 +381,38 @@ impl<'a> Verifier<'a> { } } + /// Sets the RSA PSS salt length. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to [`EVP_PKEY_CTX_set_rsa_pss_saltlen`]. + /// + /// [`EVP_PKEY_CTX_set_rsa_pss_saltlen`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_CTX_set_rsa_pss_saltlen.html + pub fn set_rsa_pss_saltlen(&mut self, len: RsaPssSaltlen) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_rsa_pss_saltlen( + self.pctx, + len.as_raw(), + )).map(|_| ()) + } + } + + /// Sets the RSA MGF1 algorithm. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to [`EVP_PKEY_CTX_set_rsa_mgf1_md`]. + /// + /// [`EVP_PKEY_CTX_set_rsa_mgf1_md`]: https://www.openssl.org/docs/manmaster/man7/RSA-PSS.html + pub fn set_rsa_mgf1_md(&mut self, md: MessageDigest) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_rsa_mgf1_md( + self.pctx, + md.as_ptr() as *mut _, + )).map(|_| ()) + } + } + /// Feeds more data into the `Verifier`. /// /// OpenSSL documentation at [`EVP_DigestUpdate`]. @@ -386,7 +479,7 @@ mod test { use std::iter; use hash::MessageDigest; - use sign::{Signer, Verifier}; + use sign::{Signer, Verifier, RsaPssSaltlen}; use ec::{EcGroup, EcKey}; use nid::Nid; use rsa::{Padding, Rsa}; @@ -559,4 +652,26 @@ mod test { verifier.update(b"hello world").unwrap(); assert!(verifier.verify(&signature).unwrap()); } + + #[test] + fn rsa_sign_verify() { + let key = include_bytes!("../test/rsa.pem"); + let private_key = Rsa::private_key_from_pem(key).unwrap(); + let pkey = PKey::from_rsa(private_key).unwrap(); + + let mut signer = Signer::new(MessageDigest::sha256(), &pkey).unwrap(); + signer.set_rsa_padding(Padding::PKCS1_PSS).unwrap(); + assert_eq!(signer.rsa_padding().unwrap(), Padding::PKCS1_PSS); + signer.set_rsa_pss_saltlen(RsaPssSaltlen::DIGEST_LENGTH).unwrap(); + signer.set_rsa_mgf1_md(MessageDigest::sha256()).unwrap(); + signer.update(&Vec::from_hex(INPUT).unwrap()).unwrap(); + let signature = signer.sign_to_vec().unwrap(); + + let mut verifier = Verifier::new(MessageDigest::sha256(), &pkey).unwrap(); + verifier.set_rsa_padding(Padding::PKCS1_PSS).unwrap(); + verifier.set_rsa_pss_saltlen(RsaPssSaltlen::DIGEST_LENGTH).unwrap(); + verifier.set_rsa_mgf1_md(MessageDigest::sha256()).unwrap(); + verifier.update(&Vec::from_hex(INPUT).unwrap()).unwrap(); + assert!(verifier.verify(&signature).unwrap()); + } } diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index 6a140c140521b478793773aed91201f7bb805935..a18ef3a0b8b04e695affa535b6ae8327843f4562 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -305,6 +305,9 @@ impl SslMethod { } } +unsafe impl Sync for SslMethod {} +unsafe impl Send for SslMethod {} + bitflags! { /// Options controling the behavior of certificate verification. pub struct SslVerifyMode: i32 { diff --git a/openssl/src/symm.rs b/openssl/src/symm.rs index 630f4ab6aea81ab01a6396b51de71bfcd0bf1a05..1cb2ef8116ac9654b8562d1305bc656dc2c1c167 100644 --- a/openssl/src/symm.rs +++ b/openssl/src/symm.rs @@ -223,6 +223,9 @@ impl Cipher { } } +unsafe impl Sync for Cipher {} +unsafe impl Send for Cipher {} + /// Represents a symmetric cipher context. /// /// Padding is enabled by default. @@ -288,6 +291,9 @@ pub struct Crypter { block_size: usize, } +unsafe impl Sync for Crypter {} +unsafe impl Send for Crypter {} + impl Crypter { /// Creates a new `Crypter`. The initialisation vector, `iv`, is not necesarry for certain /// types of `Cipher`. @@ -963,7 +969,6 @@ mod tests { #[test] fn test_des_ede3_cbc() { - let pt = "54686973206973206120746573742e"; let ct = "6f2867cfefda048a4046ef7e556c7132"; let key = "7cb66337f3d3c0fe7cb66337f3d3c0fe7cb66337f3d3c0fe"; diff --git a/openssl/src/x509/mod.rs b/openssl/src/x509/mod.rs index a4bbb5f0cd23feb48aac511f4b171873d1c35de9..ef4b57e55dd8bbadc3e80645a58074085ff60e78 100644 --- a/openssl/src/x509/mod.rs +++ b/openssl/src/x509/mod.rs @@ -6,39 +6,6 @@ //! 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. -//! -//! # Example -//! -//! Build an `X509` certificate and use a generated RSA key to sign it. -//! -//! ```rust -//! -//! extern crate openssl; -//! -//! use openssl::x509::{X509, X509Name}; -//! use openssl::pkey::PKey; -//! use openssl::hash::MessageDigest; -//! use openssl::rsa::Rsa; -//! use openssl::nid::Nid; -//! -//! fn main() { -//! let rsa = Rsa::generate(2048).unwrap(); -//! let pkey = PKey::from_rsa(rsa).unwrap(); -//! -//! let mut name = X509Name::builder().unwrap(); -//! name.append_entry_by_nid(Nid::COMMONNAME, "foobar.com").unwrap(); -//! let name = name.build(); -//! -//! let mut builder = X509::builder().unwrap(); -//! builder.set_version(2).unwrap(); -//! builder.set_subject_name(&name).unwrap(); -//! builder.set_issuer_name(&name).unwrap(); -//! builder.set_pubkey(&pkey).unwrap(); -//! builder.sign(&pkey, MessageDigest::sha256()).unwrap(); -//! -//! let certificate: X509 = builder.build(); -//! } -//! ``` use libc::{c_int, c_long}; use ffi; @@ -100,6 +67,18 @@ impl X509StoreContext { pub fn ssl_idx() -> Result, ErrorStack> { unsafe { cvt_n(ffi::SSL_get_ex_data_X509_STORE_CTX_idx()).map(|idx| Index::from_raw(idx)) } } + + /// Creates a new `X509StoreContext` instance. + /// + /// This corresponds to [`X509_STORE_CTX_new`]. + /// + /// [`X509_STORE_CTX_new`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_STORE_CTX_new.html + pub fn new() -> Result { + unsafe { + ffi::init(); + cvt_p(ffi::X509_STORE_CTX_new()).map(|p| X509StoreContext(p)) + } + } } impl X509StoreContextRef { @@ -128,6 +107,68 @@ impl X509StoreContextRef { 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/man1.0.2/crypto/X509_STORE_CTX_init.html + /// [`X509_STORE_CTX_cleanup`]: https://www.openssl.org/docs/man1.0.2/crypto/X509_STORE_CTX_cleanup.html + pub fn init( + &mut self, + trust: &store::X509StoreRef, + cert: &X509Ref, + cert_chain: &StackRef, + with_context: F, + ) -> Result + where + F: FnOnce(&mut X509StoreContextRef) -> Result, + { + 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`. + /// + /// This corresponds to [`X509_verify_cert`]. + /// + /// [`X509_verify_cert`]: https://www.openssl.org/docs/man1.0.2/crypto/X509_verify_cert.html + pub fn verify_cert(&mut self) -> Result { + unsafe { cvt_n(ffi::X509_verify_cert(self.as_ptr())).map(|n| n != 0) } + } + /// Set the error code of the context. /// /// This corresponds to [`X509_STORE_CTX_set_error`]. diff --git a/openssl/src/x509/tests.rs b/openssl/src/x509/tests.rs index 6f6b430ab21d96ebbbd1ed63f18f5b641d5f931d..ecc7f7de5eec63bffa2bfd937717cdf6395fea5f 100644 --- a/openssl/src/x509/tests.rs +++ b/openssl/src/x509/tests.rs @@ -7,9 +7,10 @@ use nid::Nid; use pkey::{PKey, Private}; use rsa::Rsa; use stack::Stack; -use x509::{X509, X509Name, X509Req, X509VerifyResult}; +use x509::{X509, X509Name, X509Req, X509StoreContext, X509VerifyResult}; use x509::extension::{AuthorityKeyIdentifier, BasicConstraints, ExtendedKeyUsage, KeyUsage, SubjectAlternativeName, SubjectKeyIdentifier}; +use x509::store::X509StoreBuilder; fn pkey() -> PKey { let rsa = Rsa::generate(2048).unwrap(); @@ -241,15 +242,11 @@ fn test_stack_from_pem() { assert_eq!(certs.len(), 2); assert_eq!( - hex::encode(certs[0] - .fingerprint(MessageDigest::sha1()) - .unwrap()), + hex::encode(certs[0].fingerprint(MessageDigest::sha1()).unwrap()), "59172d9313e84459bcff27f967e79e6e9217e584" ); assert_eq!( - hex::encode(certs[1] - .fingerprint(MessageDigest::sha1()) - .unwrap()), + hex::encode(certs[1].fingerprint(MessageDigest::sha1()).unwrap()), "c0cbdf7cdd03c9773e5468e1f6d2da7d5cbb1875" ); } @@ -291,3 +288,46 @@ fn clone_x509() { let cert = X509::from_pem(cert).unwrap(); cert.clone(); } + +#[test] +fn test_verify_cert() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + let chain = Stack::new().unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + store_bldr.add_cert(ca).unwrap(); + let store = store_bldr.build(); + + let mut context = X509StoreContext::new().unwrap(); + assert!( + context + .init(&store, &cert, &chain, |c| c.verify_cert()) + .unwrap() + ); + assert!( + context + .init(&store, &cert, &chain, |c| c.verify_cert()) + .unwrap() + ); +} + +#[test] +fn test_verify_fails() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let ca = include_bytes!("../../test/alt_name_cert.pem"); + let ca = X509::from_pem(ca).unwrap(); + let chain = Stack::new().unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + store_bldr.add_cert(ca).unwrap(); + let store = store_bldr.build(); + + let mut context = X509StoreContext::new().unwrap(); + assert!(!context + .init(&store, &cert, &chain, |c| c.verify_cert()) + .unwrap()); +}