From 40e7fd28ca20115429f7594aa055b0df5609a9a7 Mon Sep 17 00:00:00 2001 From: aumetra Date: Fri, 13 May 2022 21:06:45 +0200 Subject: [PATCH] dsa: Add signature tests --- Cargo.lock | 1 + dsa/Cargo.toml | 1 + dsa/src/sig.rs | 24 ++++-------- dsa/tests/signature.rs | 89 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 16 deletions(-) create mode 100644 dsa/tests/signature.rs diff --git a/Cargo.lock b/Cargo.lock index d1ac11eb..2b848469 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -151,6 +151,7 @@ dependencies = [ "paste", "pkcs8", "rand 0.8.5", + "rand_chacha 0.3.1", "rfc6979", "sha1", "sha2 0.10.2", diff --git a/dsa/Cargo.toml b/dsa/Cargo.toml index b769011a..afd2fe20 100644 --- a/dsa/Cargo.toml +++ b/dsa/Cargo.toml @@ -26,5 +26,6 @@ default = [] [dev-dependencies] pkcs8 = { version = "0.9.0", default-features = false, features = ["pem"] } rand = "0.8.5" +rand_chacha = "0.3.1" sha1 = "0.10.1" sha2 = "0.10.2" diff --git a/dsa/src/sig.rs b/dsa/src/sig.rs index 20b0ce3e..0c291de8 100644 --- a/dsa/src/sig.rs +++ b/dsa/src/sig.rs @@ -4,7 +4,7 @@ use alloc::vec::Vec; use num_bigint::BigUint; -use pkcs8::der::{self, asn1::UIntRef, Decode, Encode, Reader, Sequence, SliceReader}; +use pkcs8::der::{self, asn1::UIntRef, Decode, Encode, Reader, Sequence}; /// Container of the DSA signature #[derive(Clone)] @@ -35,16 +35,6 @@ impl Signature { signature } - /// Decode a Signature from its DER representation - /// - /// # Errors - /// - /// See the [`der` errors](::pkcs8::der::Error) - pub fn from_der(data: &[u8]) -> der::Result { - let mut reader = SliceReader::new(data)?; - reader.decode() - } - /// Signature part r #[must_use] pub fn r(&self) -> &BigUint { @@ -66,13 +56,15 @@ impl AsRef<[u8]> for Signature { impl<'a> Decode<'a> for Signature { fn decode>(reader: &mut R) -> der::Result { - let r = reader.decode::>()?; - let s = reader.decode::>()?; + reader.sequence(|sequence| { + let r = sequence.decode::>()?; + let s = sequence.decode::>()?; - let r = BigUint::from_bytes_be(r.as_bytes()); - let s = BigUint::from_bytes_be(s.as_bytes()); + let r = BigUint::from_bytes_be(r.as_bytes()); + let s = BigUint::from_bytes_be(s.as_bytes()); - Ok(Self::from_components(r, s)) + Ok(Self::from_components(r, s)) + }) } } diff --git a/dsa/tests/signature.rs b/dsa/tests/signature.rs new file mode 100644 index 00000000..8d4634f4 --- /dev/null +++ b/dsa/tests/signature.rs @@ -0,0 +1,89 @@ +#![allow(deprecated)] + +use digest::Digest; +use dsa::{consts::DSA_1024_160, Components, PrivateKey, Signature}; +use pkcs8::der::{Decode, Encode}; +use rand::{CryptoRng, RngCore, SeedableRng}; +use rand_chacha::ChaCha8Rng; +use sha2::Sha256; +use signature::{DigestVerifier, RandomizedDigestSigner}; + +/// Seed used for the ChaCha8 RNG +const SEED: u64 = 0x2103_1949; + +/// Message to be signed/verified +const MESSAGE: &[u8] = b"test"; + +/// Message signed by this crate using the keys generated by this CSPRNG +/// +/// This signature was generated using the keys generated by this CSPRNG (the per-message `k` component was also generated using the CSPRNG) +const MESSAGE_SIGNATURE_CRATE_ASN1: &[u8] = &[ + 0x30, 0x2C, 0x02, 0x14, 0x45, 0x1D, 0xE5, 0x76, 0x21, 0xD8, 0xFD, 0x76, 0xC1, 0x6F, 0x45, 0x4E, + 0xDE, 0x5F, 0x09, 0x79, 0x76, 0x52, 0xF3, 0xA5, 0x02, 0x14, 0x53, 0x60, 0xE6, 0xB7, 0xF0, 0xCF, + 0xAE, 0x49, 0xB1, 0x58, 0x5C, 0xCF, 0x5F, 0x3F, 0x94, 0x49, 0x21, 0xA0, 0xBF, 0xD2, +]; + +/// Message signed by OpenSSL using the keys generated by this CSPRNG +/// +/// This signature was generated using the SHA-256 digest +const MESSAGE_SIGNATURE_OPENSSL_ASN1: &[u8] = &[ + 0x30, 0x2C, 0x02, 0x14, 0x6D, 0xB3, 0x8E, 0xAF, 0x97, 0x13, 0x7E, 0x07, 0xFF, 0x24, 0xB8, 0x66, + 0x97, 0x18, 0xE1, 0x6F, 0xD7, 0x9A, 0x28, 0x2D, 0x02, 0x14, 0x47, 0x8C, 0x0B, 0x96, 0x51, 0x08, + 0x08, 0xC8, 0x34, 0x9D, 0x0D, 0x41, 0xC7, 0x73, 0x0F, 0xB5, 0x9C, 0xBB, 0x00, 0x34, +]; + +/// Get the seeded CSPRNG +fn seeded_csprng() -> impl CryptoRng + RngCore { + ChaCha8Rng::seed_from_u64(SEED) +} + +/// Generate a DSA keypair using a seeded CSPRNG +fn generate_deterministic_keypair() -> PrivateKey { + let mut rng = seeded_csprng(); + let components = Components::generate(&mut rng, DSA_1024_160); + PrivateKey::generate(&mut rng, components) +} + +#[test] +fn decode_encode_signature() { + let signature_openssl = + Signature::from_der(MESSAGE_SIGNATURE_OPENSSL_ASN1).expect("Failed to decode signature"); + let encoded_signature_openssl = signature_openssl + .to_vec() + .expect("Failed to encode signature"); + + assert_eq!(MESSAGE_SIGNATURE_OPENSSL_ASN1, encoded_signature_openssl); + + let signature_crate = + Signature::from_der(MESSAGE_SIGNATURE_CRATE_ASN1).expect("Failed to decode signature"); + let encoded_signature_crate = signature_crate + .to_vec() + .expect("Failed to encode signature"); + + assert_eq!(MESSAGE_SIGNATURE_CRATE_ASN1, encoded_signature_crate); +} + +#[test] +fn sign_message() { + let private_key = generate_deterministic_keypair(); + let generated_signature = + private_key.sign_digest_with_rng(seeded_csprng(), Sha256::new().chain_update(MESSAGE)); + + let expected_signature = + Signature::from_der(MESSAGE_SIGNATURE_CRATE_ASN1).expect("Failed to decode signature"); + + assert_eq!(generated_signature, expected_signature); +} + +#[test] +fn verify_signature() { + let private_key = generate_deterministic_keypair(); + let public_key = private_key.public_key(); + + let signature = Signature::from_der(MESSAGE_SIGNATURE_OPENSSL_ASN1) + .expect("Failed to parse ASN.1 representation of the test signature"); + + assert!(public_key + .verify_digest(Sha256::new().chain_update(MESSAGE), &signature) + .is_ok()); +}