Skip to content

Commit

Permalink
Add openssl::dsa::DsaSig
Browse files Browse the repository at this point in the history
Adds safe interface for parsing and constructing DSA signatures and
their components: `r` and `s`.
  • Loading branch information
wiktor-k committed Oct 27, 2022
1 parent bbdcaf7 commit 1d4a438
Show file tree
Hide file tree
Showing 2 changed files with 205 additions and 0 deletions.
27 changes: 27 additions & 0 deletions openssl-sys/src/handwritten/dsa.rs
Expand Up @@ -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);
Expand Down Expand Up @@ -55,4 +67,19 @@ 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);

#[cfg(any(ossl110, libressl273))]
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;
}
178 changes: 178 additions & 0 deletions openssl/src/dsa.rs
Expand Up @@ -344,6 +344,156 @@ 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 = if let Ok(signer) = Signer::new(MessageDigest::sha256(), &priv_key) {
/// signer
/// } else {
/// // DSA signing is not supported (eg. BoringSSL)
/// return;
/// };
///
/// signer.update(TEST_DATA).unwrap();
///
/// let signature = signer.sign_to_vec().unwrap();
/// // Parse DER-encoded DSA signature
/// let signature = DsaSig::from_der(&signature).unwrap();
///
/// // Extract components `r` and `s`
/// let r = BigNum::from_slice(&signature.r().to_vec()).unwrap();
/// let s = BigNum::from_slice(&signature.s().to_vec()).unwrap();
///
/// // Construct new DSA signature from components
/// let signature = DsaSig::from_private_components(r, s).unwrap();
///
/// // Serialize DSA signature to DER
/// let signature = signature.to_der().unwrap();
///
/// let mut verifier = Verifier::new(MessageDigest::sha256(), &pub_key).unwrap();
/// verifier.update(TEST_DATA).unwrap();
/// assert!(verifier.verify(&signature[..]).unwrap());
/// ```
pub struct DsaSig;

/// Reference to a [`DsaSig`].
pub struct DsaSigRef;
}

impl DsaSig {
/// Returns a new `DsaSig` by setting the `r` and `s` values associated with an DSA signature.
#[corresponds(DSA_SIG_set0)]
pub fn from_private_components(r: BigNum, s: BigNum) -> Result<Self, ErrorStack> {
unsafe {
let sig = cvt_p(ffi::DSA_SIG_new())?;
DSA_SIG_set0(sig, r.as_ptr(), s.as_ptr());
mem::forget((r, s));
Ok(DsaSig::from_ptr(sig))
}
}

from_der! {
/// Decodes a DER-encoded DSA signature.
#[corresponds(d2i_DSA_SIG)]
from_der,
DsaSig,
ffi::d2i_DSA_SIG
}
}

impl fmt::Debug for DsaSig {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
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::*;
Expand Down Expand Up @@ -444,6 +594,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() {
Expand Down

0 comments on commit 1d4a438

Please sign in to comment.