//! Integration tests for signatures.

#![cfg(feature = "hazmat")]
#![allow(deprecated)]

use digest::Digest;
use dsa::{Components, KeySize, Signature, SigningKey};
use getrandom::{SysRng, rand_core::UnwrapErr};
use hex_literal::hex;
use pkcs8::der::{Decode, Encode};
use sha2::Sha256;
use signature::{
    DigestVerifier, RandomizedDigestSigner, Signer, Verifier,
    hazmat::{PrehashSigner, PrehashVerifier},
};

/// Message to be signed/verified
const MESSAGE: &[u8] = b"test";

/// Message signed by this crate
const MESSAGE_SIGNATURE_CRATE_ASN1: &[u8] = &[
    0x30, 0x2c, 0x2, 0x14, 0x4e, 0x12, 0x27, 0x75, 0x18, 0xf6, 0x40, 0xe3, 0x3a, 0xdb, 0x80, 0x6d,
    0xe7, 0x98, 0xd3, 0xa3, 0x40, 0xf5, 0x9d, 0xf, 0x2, 0x14, 0x17, 0x78, 0x1e, 0xc8, 0x53, 0x58,
    0x91, 0xe0, 0x3f, 0x2d, 0x36, 0x27, 0x36, 0x6b, 0xac, 0x8e, 0xd7, 0xf9, 0xa4, 0xcf,
];

/// Message signed by OpenSSL
///
/// This signature was generated using the SHA-256 digest
const MESSAGE_SIGNATURE_OPENSSL_ASN1: &[u8] = &hex!(
    "302c 0214 3a2b 2250 3e2d 21b3 22a1 c834
     1668 2c0c 98df 2dee 0214 501d 1ffb bbba
     9925 a1d1 7bb8 c835 ca27 0931 ca6a"
);

/// Generate a random DSA keypair
fn generate_random_keypair() -> SigningKey {
    let mut rng = UnwrapErr(SysRng);
    let components =
        Components::try_generate_from_rng_with_key_size(&mut rng, KeySize::DSA_1024_160).unwrap();
    SigningKey::try_generate_from_rng_with_components(&mut rng, components).unwrap()
}

#[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_der()
        .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_der()
        .expect("Failed to encode signature");

    assert_eq!(MESSAGE_SIGNATURE_CRATE_ASN1, encoded_signature_crate);
}

#[test]
fn sign_verify_message() {
    let signing_key = generate_random_keypair();
    let mut rng = UnwrapErr(SysRng);
    let generated_signature =
        signing_key.sign_digest_with_rng(&mut rng, |digest: &mut Sha256| digest.update(MESSAGE));

    assert!(
        signing_key
            .verifying_key()
            .verify_digest(
                |digest: &mut Sha256| {
                    digest.update(MESSAGE);
                    Ok(())
                },
                &generated_signature
            )
            .is_ok()
    );
}

#[test]
fn signer_verifier_signature() {
    let signing_key = generate_random_keypair();
    let verifying_key = signing_key.verifying_key();
    let message = b"Hello world! This is the message signed as part of the testing process.";

    // construct signature manually and by `Signer` defaults. Ensure results are identical.
    let manual_digest = Sha256::new_with_prefix(message).finalize();
    let manual_signature = signing_key.sign_prehash(&manual_digest).unwrap();
    let signer_signature = signing_key.sign(message);
    verifying_key.verify(message, &manual_signature).unwrap();
    verifying_key.verify(message, &signer_signature).unwrap();
    assert_eq!(manual_signature, signer_signature);

    // verify signature manually and by `Verifier` defaults. Ensure signatures can be applied interchangeably.
    verifying_key
        .verify_prehash(&manual_digest, &manual_signature)
        .unwrap();
    verifying_key
        .verify_prehash(&manual_digest, &signer_signature)
        .unwrap();
    verifying_key.verify(message, &manual_signature).unwrap();
    verifying_key.verify(message, &signer_signature).unwrap();
}

/// This test forces the r and s of the signature to a bit precision different to what would
/// otherwise be expected
#[test]
#[allow(clippy::slow_vector_initialization)]
fn verify_signature_precision() {
    use der::{Sequence, asn1::Uint};

    let signing_key = generate_random_keypair();
    let verifying_key = signing_key.verifying_key();

    #[derive(Sequence)]
    struct MockSignature {
        r: Uint,
        s: Uint,
    }

    for value in &[
        {
            let mut value = vec![];
            value.resize(512, 0);
            value.push(1);
            value
        },
        {
            let mut value = vec![];
            value.push(1);
            value.resize(512, 0);
            value
        },
    ] {
        let asn1 = MockSignature {
            r: Uint::new(value).unwrap(),
            s: Uint::new(value).unwrap(),
        }
        .to_der()
        .expect("Failed to serialize signature");

        let signature = Signature::from_der(&asn1)
            .expect("Failed to parse ASN.1 representation of the test signature");

        let _ = verifying_key.verify_digest(
            |digest: &mut Sha256| {
                digest.update(MESSAGE);
                Ok(())
            },
            &signature,
        );
    }
}
