//! PKCS#1 v1.5 padding for encryption and signatures (RFC 8017).
//!
//! Encryption padding: `0x00 || 0x02 || PS || 0x00 || M`
//! Signature padding: `0x00 || 0x01 || PS || 0x00 || DigestInfo || H`
//!
//! # ⚠ Use OAEP / PSS for new deployments
//!
//! PKCS#1 v1.5 is the **legacy** padding scheme. New code should
//! prefer:
//! - [`super::oaep`] for encryption (RFC 8017 §7.1).
//! - [`super::pss`] for signatures (RFC 8017 §8.1).
//!
//! v1.5 is preserved here because it is still the only padding
//! supported by some embedded TLS stacks and HSMs. Where the
//! choice exists, do not select it.
//!
//! # Side-channel posture — Bleichenbacher
//!
//! The PKCS#1 v1.5 **encryption** scheme is the historical
//! Bleichenbacher target (CRYPTO 1998): a padding oracle on the
//! `02` prefix recovers the plaintext in ~2²⁰ – 2²² queries. RFC
//! 8017 §7.2.2 defines a constant-time decrypt that **always**
//! returns the same number of bytes whether or not padding parses
//! correctly, with the data being an internally-derived
//! deterministic dummy in the failure path. Roadmap item `T2-J`
//! tracks the audit + tightening of `pkcs1::decrypt` against this
//! recipe.
//!
//! The PKCS#1 v1.5 **signature** scheme has no analogous oracle,
//! but inherits the underlying [`super::rsa::rsa_decrypt_raw`]
//! Bellcore exposure (roadmap item `T1-C`).

use super::bigint::BigInt;
use super::rsa::{RsaPublicKey, RsaSecretKey, rsa_decrypt_raw, rsa_encrypt_raw};
use crate::Hasher;
use crate::hash::ripemd160::Ripemd160;
use crate::hash::sha1::Sha1;
use crate::hash::sha3::{Sha3_256, Sha3_384, Sha3_512};
use crate::hash::sha256::Sha256;
use crate::hash::sha384::Sha384;
use crate::hash::sha512::Sha512;

/// Hash algorithm identifiers for PKCS#1 v1.5 signatures.
///
/// Each variant carries the DER-encoded `DigestInfo` prefix that
/// gets prepended to the hash output `H` to form `T = prefix || H`.
/// Per RFC 8017 §9.2 Note 1 the prefix encodes
///
/// ```text
/// DigestInfo ::= SEQUENCE {
///     digestAlgorithm  SEQUENCE { OID, NULL },
///     digest           OCTET STRING
/// }
/// ```
///
/// SHA-3 prefixes use the OIDs assigned by NIST CSOR
/// (`2.16.840.1.101.3.4.2.{8,9,10}`) which were standardised after
/// RFC 8017 was published, but follow the same template as the
/// SHA-2 prefixes.
///
/// **Note on RIPEMD-160**: included for backwards compatibility
/// with legacy systems (Bitcoin signatures, some X.509 CAs from
/// the 2000s). Not recommended for new designs.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum HashAlg {
    /// SHA-1 (FIPS 180-4). 20-byte output. **Legacy / collision-broken**;
    /// included only for compatibility with old certificates and HMAC-SHA-1.
    Sha1,
    /// SHA-256 (FIPS 180-4). 32-byte output.
    Sha256,
    /// SHA-384 (FIPS 180-4). 48-byte output.
    Sha384,
    /// SHA-512 (FIPS 180-4). 64-byte output.
    Sha512,
    /// SHA3-256 (FIPS 202). 32-byte output.
    Sha3_256,
    /// SHA3-384 (FIPS 202). 48-byte output.
    Sha3_384,
    /// SHA3-512 (FIPS 202). 64-byte output.
    Sha3_512,
    /// RIPEMD-160 (ISO/IEC 10118-3). 20-byte output. **Legacy**;
    /// included for Bitcoin and 2000s European X.509 CAs.
    Ripemd160,
}

impl HashAlg {
    /// DER-encoded DigestInfo prefix for this hash algorithm
    /// (RFC 8017 §9.2 Note 1, plus the SHA-3 family OIDs from
    /// NIST CSOR `2.16.840.1.101.3.4.2.{8,9,10}`, and the
    /// TeleTrusT OID `1.3.36.3.2.1` for RIPEMD-160).
    fn digest_info_prefix(&self) -> &'static [u8] {
        match self {
            // SHA-1: OID 1.3.14.3.2.26 (5-byte OID)
            // -> prefix 30 21 30 09 06 05 2b 0e 03 02 1a 05 00 04 14
            HashAlg::Sha1 => &[
                0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14,
            ],
            // SHA-256: OID 2.16.840.1.101.3.4.2.1
            HashAlg::Sha256 => &[
                0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00,
                0x04, 0x20,
            ],
            // SHA-384: OID 2.16.840.1.101.3.4.2.2
            HashAlg::Sha384 => &[
                0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00,
                0x04, 0x30,
            ],
            // SHA-512: OID 2.16.840.1.101.3.4.2.3
            HashAlg::Sha512 => &[
                0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00,
                0x04, 0x40,
            ],
            // SHA3-256: OID 2.16.840.1.101.3.4.2.8
            HashAlg::Sha3_256 => &[
                0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x08, 0x05, 0x00,
                0x04, 0x20,
            ],
            // SHA3-384: OID 2.16.840.1.101.3.4.2.9
            HashAlg::Sha3_384 => &[
                0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x09, 0x05, 0x00,
                0x04, 0x30,
            ],
            // SHA3-512: OID 2.16.840.1.101.3.4.2.10
            HashAlg::Sha3_512 => &[
                0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0a, 0x05, 0x00,
                0x04, 0x40,
            ],
            // RIPEMD-160: OID 1.3.36.3.2.1 (5-byte OID, same shape as SHA-1)
            // -> prefix 30 21 30 09 06 05 2b 24 03 02 01 05 00 04 14
            HashAlg::Ripemd160 => &[
                0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03, 0x02, 0x01, 0x05, 0x00, 0x04, 0x14,
            ],
        }
    }

    /// Output length of the hash in bytes.
    fn hash_len(&self) -> usize {
        match self {
            HashAlg::Sha1 | HashAlg::Ripemd160 => 20,
            HashAlg::Sha256 | HashAlg::Sha3_256 => 32,
            HashAlg::Sha384 | HashAlg::Sha3_384 => 48,
            HashAlg::Sha512 | HashAlg::Sha3_512 => 64,
        }
    }
}

/// PKCS#1 v1.5 encryption.
///
/// Pads `msg` and encrypts with `pk`. `rng` fills buffers with random bytes.
/// Returns the ciphertext as a byte vector of length equal to the modulus.
pub fn pkcs1v15_encrypt(pk: &RsaPublicKey, msg: &[u8], rng: &mut dyn FnMut(&mut [u8])) -> Vec<u8> {
    let k = pk.modulus_byte_len();
    assert!(
        msg.len() <= k - 11,
        "PKCS1v15 encrypt: message too long (max {} bytes, got {})",
        k - 11,
        msg.len()
    );

    // EM = 0x00 || 0x02 || PS || 0x00 || M
    let ps_len = k - msg.len() - 3;
    let mut em = vec![0u8; k];
    em[0] = 0x00;
    em[1] = 0x02;

    // PS: random non-zero bytes.
    rng(&mut em[2..2 + ps_len]);
    for b in em[2..2 + ps_len].iter_mut() {
        while *b == 0 {
            let mut tmp = [0u8; 1];
            rng(&mut tmp);
            *b = tmp[0];
        }
    }

    em[2 + ps_len] = 0x00;
    em[3 + ps_len..].copy_from_slice(msg);

    let m = BigInt::from_be_bytes(&em);
    let c = rsa_encrypt_raw(pk, &m);
    c.to_be_bytes(k)
}

/// PKCS#1 v1.5 decryption.
///
/// Decrypts `ct` with `sk` and removes padding.
/// Returns `None` if padding is invalid.
pub fn pkcs1v15_decrypt(sk: &RsaSecretKey, ct: &[u8]) -> Option<Vec<u8>> {
    let k = sk.modulus_byte_len();
    if ct.len() != k || k < 11 {
        return None;
    }

    let c = BigInt::from_be_bytes(ct);
    let m = rsa_decrypt_raw(sk, &c);
    let em = m.to_be_bytes(k);

    // Check format: 0x00 || 0x02 || PS || 0x00 || M
    if em[0] != 0x00 || em[1] != 0x02 {
        return None;
    }

    // Find the 0x00 separator after PS (PS must be at least 8 bytes).
    let mut sep = None;
    for i in 2..em.len() {
        if em[i] == 0x00 {
            if i < 10 {
                // PS too short
                return None;
            }
            sep = Some(i);
            break;
        }
    }

    let sep = sep?;
    Some(em[sep + 1..].to_vec())
}

/// PKCS#1 v1.5 signature generation.
///
/// Signs the pre-computed `hash` (of `hash_alg` type) with `sk`.
pub fn pkcs1v15_sign(sk: &RsaSecretKey, hash: &[u8], hash_alg: HashAlg) -> Vec<u8> {
    let k = sk.modulus_byte_len();
    let prefix = hash_alg.digest_info_prefix();
    let t_len = prefix.len() + hash_alg.hash_len();

    assert!(hash.len() == hash_alg.hash_len(), "Hash length mismatch");
    assert!(k >= t_len + 11, "Modulus too short for PKCS1v15 signature");

    // EM = 0x00 || 0x01 || PS || 0x00 || T
    let ps_len = k - t_len - 3;
    let mut em = vec![0u8; k];
    em[0] = 0x00;
    em[1] = 0x01;
    for i in 0..ps_len {
        em[2 + i] = 0xFF;
    }
    em[2 + ps_len] = 0x00;
    em[3 + ps_len..3 + ps_len + prefix.len()].copy_from_slice(prefix);
    em[3 + ps_len + prefix.len()..].copy_from_slice(hash);

    let m = BigInt::from_be_bytes(&em);
    let s = rsa_decrypt_raw(sk, &m); // signing = decryption with private key
    s.to_be_bytes(k)
}

/// PKCS#1 v1.5 signature verification.
///
/// Verifies that `sig` is a valid signature of `hash` under `pk`.
pub fn pkcs1v15_verify(pk: &RsaPublicKey, hash: &[u8], hash_alg: HashAlg, sig: &[u8]) -> bool {
    let k = pk.modulus_byte_len();
    if sig.len() != k {
        return false;
    }
    if hash.len() != hash_alg.hash_len() {
        return false;
    }

    let s = BigInt::from_be_bytes(sig);
    let m = rsa_encrypt_raw(pk, &s); // verification = encryption with public key
    let em = m.to_be_bytes(k);

    // Reconstruct expected EM.
    let prefix = hash_alg.digest_info_prefix();
    let t_len = prefix.len() + hash_alg.hash_len();
    if k < t_len + 11 {
        return false;
    }

    let ps_len = k - t_len - 3;
    let mut expected = vec![0u8; k];
    expected[0] = 0x00;
    expected[1] = 0x01;
    for i in 0..ps_len {
        expected[2 + i] = 0xFF;
    }
    expected[2 + ps_len] = 0x00;
    expected[3 + ps_len..3 + ps_len + prefix.len()].copy_from_slice(prefix);
    expected[3 + ps_len + prefix.len()..].copy_from_slice(hash);

    // Constant-time comparison.
    let mut diff = 0u8;
    for (a, b) in em.iter().zip(expected.iter()) {
        diff |= a ^ b;
    }
    diff == 0 && em.len() == expected.len()
}

// ============================================================================
// Convenience helpers: hash a message with the chosen hash, then sign / verify.
// ============================================================================
//
// One pair per supported (hash, HashAlg variant). The macro factors the
// trivial 2-line body so adding a new hash in the future is a single line.
// SHA-256 keeps its standalone definition (and not the macro) to preserve
// the byte-for-byte stability of the public symbol that pre-existed this
// commit -- this avoids any chance of accidentally changing its inlining
// behaviour.

/// Convenience: hash a message with SHA-256, then sign.
pub fn pkcs1v15_sign_sha256(sk: &RsaSecretKey, message: &[u8]) -> Vec<u8> {
    let hash = Sha256::hash(message);
    pkcs1v15_sign(sk, &hash, HashAlg::Sha256)
}

/// Convenience: hash a message with SHA-256, then verify.
pub fn pkcs1v15_verify_sha256(pk: &RsaPublicKey, message: &[u8], sig: &[u8]) -> bool {
    let hash = Sha256::hash(message);
    pkcs1v15_verify(pk, &hash, HashAlg::Sha256, sig)
}

macro_rules! convenience_pair {
    ($sign_fn:ident, $verify_fn:ident, $hasher:ty, $alg:expr, $doc:literal) => {
        #[doc = $doc]
        pub fn $sign_fn(sk: &RsaSecretKey, message: &[u8]) -> Vec<u8> {
            let hash = <$hasher as Hasher>::hash(message);
            pkcs1v15_sign(sk, &hash, $alg)
        }

        #[doc = $doc]
        pub fn $verify_fn(pk: &RsaPublicKey, message: &[u8], sig: &[u8]) -> bool {
            let hash = <$hasher as Hasher>::hash(message);
            pkcs1v15_verify(pk, &hash, $alg, sig)
        }
    };
}

convenience_pair!(
    pkcs1v15_sign_sha1,
    pkcs1v15_verify_sha1,
    Sha1,
    HashAlg::Sha1,
    "Convenience: hash a message with SHA-1, then sign / verify. \
     **Legacy**: do not use for new designs; SHA-1 is collision-broken."
);

convenience_pair!(
    pkcs1v15_sign_sha384,
    pkcs1v15_verify_sha384,
    Sha384,
    HashAlg::Sha384,
    "Convenience: hash a message with SHA-384, then sign / verify."
);

convenience_pair!(
    pkcs1v15_sign_sha512,
    pkcs1v15_verify_sha512,
    Sha512,
    HashAlg::Sha512,
    "Convenience: hash a message with SHA-512, then sign / verify."
);

convenience_pair!(
    pkcs1v15_sign_sha3_256,
    pkcs1v15_verify_sha3_256,
    Sha3_256,
    HashAlg::Sha3_256,
    "Convenience: hash a message with SHA3-256, then sign / verify."
);

convenience_pair!(
    pkcs1v15_sign_sha3_384,
    pkcs1v15_verify_sha3_384,
    Sha3_384,
    HashAlg::Sha3_384,
    "Convenience: hash a message with SHA3-384, then sign / verify."
);

convenience_pair!(
    pkcs1v15_sign_sha3_512,
    pkcs1v15_verify_sha3_512,
    Sha3_512,
    HashAlg::Sha3_512,
    "Convenience: hash a message with SHA3-512, then sign / verify."
);

convenience_pair!(
    pkcs1v15_sign_ripemd160,
    pkcs1v15_verify_ripemd160,
    Ripemd160,
    HashAlg::Ripemd160,
    "Convenience: hash a message with RIPEMD-160, then sign / verify. \
     **Legacy**: included for compatibility with older systems \
     (Bitcoin, some 2000s X.509 CAs); not recommended for new designs."
);

#[cfg(test)]
mod tests {
    use super::*;

    fn test_rng() -> impl FnMut(&mut [u8]) {
        let mut state: u64 = 0xdeadbeefcafebabe;
        move |buf: &mut [u8]| {
            for b in buf.iter_mut() {
                state = state
                    .wrapping_mul(6364136223846793005)
                    .wrapping_add(1442695040888963407);
                *b = (state >> 33) as u8;
            }
        }
    }

    #[test]
    fn test_pkcs1v15_encrypt_decrypt_roundtrip() {
        let mut rng = test_rng();
        let (pk, sk) = super::super::rsa::rsa_keygen(512, &mut rng);
        let msg = b"Hello, RSA!";
        let ct = pkcs1v15_encrypt(&pk, msg, &mut rng);
        let pt = pkcs1v15_decrypt(&sk, &ct).expect("decryption failed");
        assert_eq!(&pt, msg);
    }

    #[test]
    fn test_pkcs1v15_sign_verify_roundtrip() {
        let mut rng = test_rng();
        let (pk, sk) = super::super::rsa::rsa_keygen(512, &mut rng);
        let message = b"Sign me!";
        let sig = pkcs1v15_sign_sha256(&sk, message);
        assert!(pkcs1v15_verify_sha256(&pk, message, &sig));
        // Tamper with signature => should fail.
        let mut bad_sig = sig.clone();
        bad_sig[0] ^= 0xFF;
        assert!(!pkcs1v15_verify_sha256(&pk, message, &bad_sig));
    }

    /// Round-trip every supported hash through the convenience helpers.
    /// Uses a single 1024-bit key (regenerating per hash would multiply
    /// the test runtime by 7 with no extra coverage).
    ///
    /// 1024 bits is the smallest modulus that fits all 7 EMSA-PKCS1-v1_5
    /// padded values: SHA-512 / SHA3-512 need `t_len = 19 + 64 = 83`
    /// bytes plus 11 bytes of mandatory padding overhead = 94 bytes
    /// minimum, so a 768-bit (96-byte) modulus would just barely work
    /// and 1024 bits (128 bytes) is the next standard size.
    #[test]
    fn pkcs1v15_all_supported_hashes_roundtrip() {
        let mut rng = test_rng();
        let (pk, sk) = super::super::rsa::rsa_keygen(1024, &mut rng);
        let msg = b"hash flexibility test";

        // SHA-1
        let sig = pkcs1v15_sign_sha1(&sk, msg);
        assert!(pkcs1v15_verify_sha1(&pk, msg, &sig));
        // SHA-256 (already covered by the older test, but include here too)
        let sig = pkcs1v15_sign_sha256(&sk, msg);
        assert!(pkcs1v15_verify_sha256(&pk, msg, &sig));
        // SHA-384
        let sig = pkcs1v15_sign_sha384(&sk, msg);
        assert!(pkcs1v15_verify_sha384(&pk, msg, &sig));
        // SHA-512
        let sig = pkcs1v15_sign_sha512(&sk, msg);
        assert!(pkcs1v15_verify_sha512(&pk, msg, &sig));
        // SHA3-256
        let sig = pkcs1v15_sign_sha3_256(&sk, msg);
        assert!(pkcs1v15_verify_sha3_256(&pk, msg, &sig));
        // SHA3-384
        let sig = pkcs1v15_sign_sha3_384(&sk, msg);
        assert!(pkcs1v15_verify_sha3_384(&pk, msg, &sig));
        // SHA3-512
        let sig = pkcs1v15_sign_sha3_512(&sk, msg);
        assert!(pkcs1v15_verify_sha3_512(&pk, msg, &sig));
        // RIPEMD-160
        let sig = pkcs1v15_sign_ripemd160(&sk, msg);
        assert!(pkcs1v15_verify_ripemd160(&pk, msg, &sig));
    }

    /// Verify must reject when the **declared hash algorithm** does
    /// not match what the signer used. This is the property that
    /// makes the DigestInfo prefix actually meaningful: a SHA-256
    /// signature must NOT verify as if it were a SHA-512 signature
    /// even with the same RSA key and the same message bytes.
    ///
    /// Catches a class of attacks where a downgrade in the declared
    /// hash would let an attacker substitute a precomputed-collision
    /// payload under a weaker hash.
    #[test]
    fn pkcs1v15_hash_mismatch_rejected() {
        let mut rng = test_rng();
        let (pk, sk) = super::super::rsa::rsa_keygen(1024, &mut rng);
        let msg = b"hash mismatch test";

        // Sign with SHA-256, verify as SHA-256 -> OK.
        let sig256 = pkcs1v15_sign_sha256(&sk, msg);
        assert!(pkcs1v15_verify_sha256(&pk, msg, &sig256));

        // Same signature, verify as SHA-512 -> must reject (different
        // DigestInfo prefix, different hash length, completely
        // different reconstructed EM).
        assert!(!pkcs1v15_verify_sha512(&pk, msg, &sig256));

        // Same signature, verify as SHA3-256 -> must reject (same
        // hash length but different OID byte in the DigestInfo
        // prefix, so the reconstructed EM differs by exactly one
        // byte). This is the strictest possible mismatch test.
        assert!(!pkcs1v15_verify_sha3_256(&pk, msg, &sig256));

        // Sign with SHA-512, verify as SHA-256 -> must reject.
        let sig512 = pkcs1v15_sign_sha512(&sk, msg);
        assert!(pkcs1v15_verify_sha512(&pk, msg, &sig512));
        assert!(!pkcs1v15_verify_sha256(&pk, msg, &sig512));

        // Sign with SHA3-256, verify as SHA-256 -> must reject (the
        // critical hash-family mixup case).
        let sig3_256 = pkcs1v15_sign_sha3_256(&sk, msg);
        assert!(pkcs1v15_verify_sha3_256(&pk, msg, &sig3_256));
        assert!(!pkcs1v15_verify_sha256(&pk, msg, &sig3_256));
    }

    /// `HashAlg::hash_len()` must agree with the actual `OUTPUT_LEN`
    /// of the underlying `Hasher` implementation. If they ever
    /// drifted (e.g. someone changed SHA-3 truncation), the sign
    /// path would assert-fail at runtime; this test catches that
    /// at compile-then-run time.
    #[test]
    fn hashalg_lengths_agree_with_hasher_output_len() {
        assert_eq!(HashAlg::Sha1.hash_len(), <Sha1 as Hasher>::OUTPUT_LEN);
        assert_eq!(HashAlg::Sha256.hash_len(), <Sha256 as Hasher>::OUTPUT_LEN);
        assert_eq!(HashAlg::Sha384.hash_len(), <Sha384 as Hasher>::OUTPUT_LEN);
        assert_eq!(HashAlg::Sha512.hash_len(), <Sha512 as Hasher>::OUTPUT_LEN);
        assert_eq!(HashAlg::Sha3_256.hash_len(), <Sha3_256 as Hasher>::OUTPUT_LEN);
        assert_eq!(HashAlg::Sha3_384.hash_len(), <Sha3_384 as Hasher>::OUTPUT_LEN);
        assert_eq!(HashAlg::Sha3_512.hash_len(), <Sha3_512 as Hasher>::OUTPUT_LEN);
        assert_eq!(HashAlg::Ripemd160.hash_len(), <Ripemd160 as Hasher>::OUTPUT_LEN);
    }
}
