From d2a8b5109f09dfcba456ddbc7e81393a792fb754 Mon Sep 17 00:00:00 2001 From: Wiktor Kwapisiewicz Date: Wed, 19 Oct 2022 11:36:15 +0200 Subject: [PATCH] Add openssl::dsa::DsaSig Adds safe interface for parsing and constructing DSA signatures and their components: `r` and `s`. --- openssl-sys/src/handwritten/dsa.rs | 22 ++++ openssl/src/dsa.rs | 173 +++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+) diff --git a/openssl-sys/src/handwritten/dsa.rs b/openssl-sys/src/handwritten/dsa.rs index 604c68f032..d3176b0005 100644 --- a/openssl-sys/src/handwritten/dsa.rs +++ b/openssl-sys/src/handwritten/dsa.rs @@ -2,6 +2,18 @@ use libc::*; use *; +cfg_if! { + if #[cfg(any(ossl110, libressl280))] { + pub enum DSA_SIG {} + } else { + #[repr(C)] + pub struct DSA_SIG { + pub r: *mut BIGNUM, + pub s: *mut BIGNUM, + } + } +} + extern "C" { pub fn DSA_new() -> *mut DSA; pub fn DSA_free(dsa: *mut DSA); @@ -55,4 +67,14 @@ extern "C" { pub fn DSA_get0_key(d: *const DSA, pub_key: *mut *const BIGNUM, priv_key: *mut *const BIGNUM); #[cfg(any(ossl110, libressl273))] pub fn DSA_set0_key(d: *mut DSA, pub_key: *mut BIGNUM, priv_key: *mut BIGNUM) -> c_int; + pub fn d2i_DSA_SIG(sig: *mut *mut DSA_SIG, pp: *mut *const c_uchar, length: c_long) -> *mut DSA_SIG; + pub fn i2d_DSA_SIG(a: *const DSA_SIG, pp: *mut *mut c_uchar) -> c_int; + + pub fn DSA_SIG_new() -> *mut DSA_SIG; + pub fn DSA_SIG_free(sig: *mut DSA_SIG); + + pub fn DSA_SIG_get0(sig: *const DSA_SIG, pr: *mut *const BIGNUM, ps: *mut *const BIGNUM); + + #[cfg(any(ossl110, libressl273))] + pub fn DSA_SIG_set0(sig: *mut DSA_SIG, pr: *mut BIGNUM, ps: *mut BIGNUM) -> c_int; } diff --git a/openssl/src/dsa.rs b/openssl/src/dsa.rs index 78fa1c25dc..6c454bfce4 100644 --- a/openssl/src/dsa.rs +++ b/openssl/src/dsa.rs @@ -344,6 +344,151 @@ cfg_if! { } } + +foreign_type_and_impl_send_sync! { + type CType = ffi::DSA_SIG; + fn drop = ffi::DSA_SIG_free; + + /// Object representing DSA signature. + /// + /// DSA signatures consist of two components: `r` and `s`. + /// + /// # Examples + /// + /// ``` + /// use std::convert::TryInto; + /// + /// use openssl::bn::BigNum; + /// use openssl::dsa::{Dsa, DsaSig}; + /// use openssl::hash::MessageDigest; + /// use openssl::pkey::PKey; + /// use openssl::sign::{Signer, Verifier}; + /// + /// const TEST_DATA: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + /// let dsa_ref = Dsa::generate(1024).unwrap(); + /// + /// let pub_key: PKey<_> = dsa_ref.clone().try_into().unwrap(); + /// let priv_key: PKey<_> = dsa_ref.try_into().unwrap(); + /// + /// let mut signer = Signer::new(MessageDigest::sha256(), &priv_key).unwrap(); + /// signer.update(TEST_DATA).unwrap(); + /// + /// let signature = signer.sign_to_vec().unwrap(); + /// // Parse DER-encoded DSA signature + /// let signature = DsaSig::from_der(&signature).unwrap(); + /// + /// // Extract components `r` and `s` + /// let r = BigNum::from_slice(&signature.r().to_vec()).unwrap(); + /// let s = BigNum::from_slice(&signature.s().to_vec()).unwrap(); + /// + /// // Construct new DSA signature from components + /// let signature = DsaSig::from_private_components(r, s).unwrap(); + /// + /// // Serialize DSA signature to DER + /// let signature = signature.to_der().unwrap(); + /// + /// let mut verifier = Verifier::new(MessageDigest::sha256(), &pub_key).unwrap(); + /// verifier.update(TEST_DATA).unwrap(); + /// assert!(verifier.verify(&signature[..]).unwrap()); + /// ``` + pub struct DsaSig; + + /// Reference to a [`DsaSig`]. + pub struct DsaSigRef; +} + +impl DsaSig { + /// Returns a new `DsaSig` by setting the `r` and `s` values associated with an DSA signature. + #[corresponds(DSA_SIG_set0)] + pub fn from_private_components(r: BigNum, s: BigNum) -> Result { + unsafe { + let sig = cvt_p(ffi::DSA_SIG_new())?; + DSA_SIG_set0(sig, r.as_ptr(), s.as_ptr()); + mem::forget((r, s)); + Ok(DsaSig::from_ptr(sig)) + } + } + + from_der! { + /// Decodes a DER-encoded DSA signature. + #[corresponds(d2i_DSA_SIG)] + from_der, + DsaSig, + ffi::d2i_DSA_SIG + } +} + +impl fmt::Debug for DsaSig { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "DSA Sig") + } +} + +impl DsaSigRef { + to_der! { + /// Serializes the DSA signature into a DER-encoded `DSASignature` structure. + #[corresponds(i2d_DSA_SIG)] + to_der, + ffi::i2d_DSA_SIG + } + + /// Returns internal component `r` of an `DsaSig`. + #[corresponds(DSA_SIG_get0)] + pub fn r(&self) -> &BigNumRef { + unsafe { + let mut r = ptr::null(); + DSA_SIG_get0(self.as_ptr(), &mut r, ptr::null_mut()); + BigNumRef::from_const_ptr(r) + } + } + + /// Returns internal component `s` of an `DsaSig`. + #[corresponds(DSA_SIG_get0)] + pub fn s(&self) -> &BigNumRef { + unsafe { + let mut s = ptr::null(); + DSA_SIG_get0(self.as_ptr(), ptr::null_mut(), &mut s); + BigNumRef::from_const_ptr(s) + } + } +} + +cfg_if! { + if #[cfg(any(ossl110, libressl273))] { + use ffi::{DSA_SIG_set0, DSA_SIG_get0}; + } else { + #[allow(bad_style)] + unsafe fn DSA_SIG_set0( + sig: *mut ffi::DSA_SIG, + r: *mut ffi::BIGNUM, + s: *mut ffi::BIGNUM, + ) -> c_int { + if r.is_null() || s.is_null() { + return 0; + } + ffi::BN_clear_free((*sig).r); + ffi::BN_clear_free((*sig).s); + (*sig).r = r; + (*sig).s = s; + 1 + } + + #[allow(bad_style)] + unsafe fn DSA_SIG_get0( + sig: *const ffi::DSA_SIG, + pr: *mut *const ffi::BIGNUM, + ps: *mut *const ffi::BIGNUM) + { + if !pr.is_null() { + (*pr) = (*sig).r; + } + if !ps.is_null() { + (*ps) = (*sig).s; + } + } + } +} + #[cfg(test)] mod test { use super::*; @@ -444,6 +589,34 @@ mod test { assert!(verifier.verify(&signature[..]).unwrap()); } + #[test] + #[cfg(not(boringssl))] + fn test_signature_der() { + use std::convert::TryInto; + + const TEST_DATA: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + let dsa_ref = Dsa::generate(1024).unwrap(); + + let pub_key: PKey<_> = dsa_ref.clone().try_into().unwrap(); + let priv_key: PKey<_> = dsa_ref.try_into().unwrap(); + + let mut signer = Signer::new(MessageDigest::sha256(), &priv_key).unwrap(); + signer.update(TEST_DATA).unwrap(); + + let signature = signer.sign_to_vec().unwrap(); + let signature = DsaSig::from_der(&signature).unwrap(); + + let r = BigNum::from_slice(&signature.r().to_vec()).unwrap(); + let s = BigNum::from_slice(&signature.s().to_vec()).unwrap(); + + let signature = DsaSig::from_private_components(r, s).unwrap(); + let signature = signature.to_der().unwrap(); + + let mut verifier = Verifier::new(MessageDigest::sha256(), &pub_key).unwrap(); + verifier.update(TEST_DATA).unwrap(); + assert!(verifier.verify(&signature[..]).unwrap()); + } + #[test] #[allow(clippy::redundant_clone)] fn clone() {