/// Test algorithm implementations for the CryptoProvider
///
/// This contains tests run against the installed crypto provider to verify
/// hash, cipher and signature implementations against known test vectors.
use str0m::crypto::CryptoProvider;

mod common;
use common::init_crypto_default;

fn get_provider() -> &'static CryptoProvider {
    init_crypto_default();
    CryptoProvider::get_default().unwrap()
}

/// Convert an ASCII hex array into a byte array at compile time.
macro_rules! hex_as_bytes {
    ($input:expr) => {{
        const fn from_hex_char(c: u8) -> u8 {
            match c {
                b'0'..=b'9' => c - b'0',
                b'a'..=b'f' => c - b'a' + 10,
                b'A'..=b'F' => c - b'A' + 10,
                _ => panic!("Invalid hex character"),
            }
        }

        const INPUT: &[u8] = $input;
        const LEN: usize = INPUT.len();
        const OUTPUT_LEN: usize = LEN / 2;

        const fn convert() -> [u8; OUTPUT_LEN] {
            assert!(LEN % 2 == 0, "Hex string length must be even");

            let mut out = [0u8; OUTPUT_LEN];
            let mut i = 0;
            while i < LEN {
                out[i / 2] = (from_hex_char(INPUT[i]) << 4) | from_hex_char(INPUT[i + 1]);
                i += 2;
            }
            out
        }

        convert()
    }};
}

mod sha256 {
    fn verify_test_vector(input: &[u8], expected: &[u8; 32]) {
        let sha256_provider = super::get_provider().sha256_provider;
        let hash = sha256_provider.sha256(input);
        assert_eq!(hash, *expected);
    }

    #[test]
    fn test_hello_world() {
        verify_test_vector(
            b"hello world",
            &hex_as_bytes!(b"b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"),
        );
    }

    #[test]
    fn test_empty() {
        // SHA-256 of "" - NIST test vector
        verify_test_vector(
            b"",
            &hex_as_bytes!(b"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"),
        );
    }

    #[test]
    fn test_abc() {
        // SHA-256 of "abc" - NIST test vector
        verify_test_vector(
            b"abc",
            &hex_as_bytes!(b"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"),
        );
    }
}

mod sha1_hmac {
    fn verify_test_vector(key: &[u8], payloads: &[&[u8]], expected: &[u8; 20]) {
        let sha1_hmac_provider = super::get_provider().sha1_hmac_provider;
        let hmac = sha1_hmac_provider.sha1_hmac(key, payloads);
        assert_eq!(hmac, *expected);
    }

    #[test]
    fn test_rfc2202_test_case_1() {
        verify_test_vector(
            &[0x0b; 20],
            &[b"Hi There"],
            &hex_as_bytes!(b"b617318655057264e28bc0b6fb378c8ef146be00"),
        );
    }

    #[test]
    fn test_rfc2202_test_case_2() {
        verify_test_vector(
            b"Jefe",
            &[b"what do ya want for nothing?"],
            &hex_as_bytes!(b"effcdf6ae5eb2fa2d27416d5f184df9c259a7c79"),
        );
    }

    #[test]
    fn test_rfc2202_test_case_3() {
        verify_test_vector(
            &[0xaa; 20],
            &[&[0xddu8; 50]],
            &hex_as_bytes!(b"125d7342b9ac11cd91a39af48aa17b4f63f175d3"),
        );
    }

    #[test]
    fn test_rfc2202_test_case_4() {
        verify_test_vector(
            &[
                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
                24, 25,
            ],
            &[&[0xcdu8; 50]],
            &hex_as_bytes!(b"4c9007f4026250c6bc8414f9bf50c86c2d7235da"),
        );
    }

    #[test]
    fn test_rfc2202_test_case_5() {
        verify_test_vector(
            &[0x0c; 20],
            &[b"Test With Truncation"],
            &hex_as_bytes!(b"4c1a03424b55e07fe7f27be1d58bb9324a9a5a04"),
        );
    }

    #[test]
    fn test_rfc2202_test_case_6() {
        verify_test_vector(
            &[0xaa; 80],
            &[b"Test Using Larger Than Block-Size Key - Hash Key First"],
            &hex_as_bytes!(b"aa4ae5e15272d00e95705637ce8a3b55ed402112"),
        );
    }

    #[test]
    fn test_rfc2202_test_case_7() {
        verify_test_vector(
            &[0xaa; 80],
            &[
                b"Test Using Larger Than Block-Size Key and Larger ",
                b"Than One Block-Size Data",
            ],
            &hex_as_bytes!(b"e8e99d0f45237d786d6bbaa7965c7808bbff1a91"),
        );
    }
}

mod aes_128_cm_sha1_80 {
    fn verify_test_vector(
        key: [u8; 16],
        iv: [u8; 16],
        encrypt: bool,
        input: &[u8],
        expected: &[u8],
    ) {
        let srtp_provider = super::get_provider().srtp_provider;
        let cipher_provider = srtp_provider.aes_128_cm_sha1_80();
        let mut cipher = cipher_provider.create_cipher(key, encrypt);

        let mut output = vec![0u8; input.len()];
        if encrypt {
            cipher.encrypt(&iv, input, &mut output).unwrap();
        } else {
            cipher.decrypt(&iv, input, &mut output).unwrap();
        }
        assert_eq!(expected, &output)
    }

    // AES-128-CTR Test Vectors from NIST SP 800-38A
    // https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf
    // Section F.5.1

    #[test]
    fn test_ctr_encrypt() {
        verify_test_vector(
            hex_as_bytes!(b"2b7e151628aed2a6abf7158809cf4f3c"),
            hex_as_bytes!(b"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"),
            true,
            &hex_as_bytes!(b"6bc1bee22e409f96e93d7e117393172a"),
            &hex_as_bytes!(b"874d6191b620e3261bef6864990db6ce"),
        );
    }

    #[test]
    fn test_decrypt() {
        verify_test_vector(
            hex_as_bytes!(b"2b7e151628aed2a6abf7158809cf4f3c"),
            hex_as_bytes!(b"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"),
            false,
            &hex_as_bytes!(b"874d6191b620e3261bef6864990db6ce"),
            &hex_as_bytes!(b"6bc1bee22e409f96e93d7e117393172a"),
        );
    }

    #[test]
    fn test_multiple_blocks() {
        verify_test_vector(
            hex_as_bytes!(b"2b7e151628aed2a6abf7158809cf4f3c"),
            hex_as_bytes!(b"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"),
            true,
            &hex_as_bytes!(
                b"6bc1bee22e409f96e93d7e117393172a\
             ae2d8a571e03ac9c9eb76fac45af8e51\
             30c81c46a35ce411e5fbc1191a0a52ef"
            ),
            &hex_as_bytes!(
                b"874d6191b620e3261bef6864990db6ce\
             9806f66b7970fdff8617187bb9fffdff\
             5ae4df3edbd5d35e5b4f09020db03eab"
            ),
        );
    }
}

mod aead_aes_128_gcm {
    fn verify_test_vector(
        key: [u8; 16],
        iv: [u8; 12],
        encrypt: bool,
        aads: &[&[u8]],
        input: &[u8],
        expected: &[u8],
    ) {
        let srtp_provider = super::get_provider().srtp_provider;
        let cipher_provider = srtp_provider.aead_aes_128_gcm();
        let mut cipher = cipher_provider.create_cipher(key, encrypt);

        let output_len = if encrypt {
            input.len() + 16
        } else {
            input.len() - 16
        };
        let mut output = vec![0u8; output_len];
        if encrypt {
            cipher.encrypt(&iv, aads[0], input, &mut output).unwrap();
        } else {
            cipher.decrypt(&iv, aads, input, &mut output).unwrap();
        }
        assert_eq!(expected, &output)
    }

    // AES-128-GCM Test Vectors from NIST SP 800-38D
    // https://csrc.nist.gov/publications/detail/sp/800-38d/final
    // Test Case 4

    #[test]
    fn test_encrypt() {
        verify_test_vector(
            hex_as_bytes!(b"feffe9928665731c6d6a8f9467308308"),
            hex_as_bytes!(b"cafebabefacedbaddecaf888"),
            true,
            &[&hex_as_bytes!(
                b"feedfacedeadbeeffeedfacedeadbeef\
                abaddad2"
            )],
            &hex_as_bytes!(
                b"d9313225f88406e5a55909c5aff5269a\
                86a7a9531534f7da2e4c303d8a318a72\
                1c3c0c95956809532fcf0e2449a6b525\
                b16aedf5aa0de657ba637b39"
            ),
            &hex_as_bytes!(
                b"42831ec2217774244b7221b784d0d49c\
                e3aa212f2c02a4e035c17e2329aca12e\
                21d514b25466931c7d8f6a5aac84aa05\
                1ba30b396a0aac973d58e091\
                5bc94fbc3221a5db94fae95ae7121a47"
            ),
        );
    }

    #[test]
    fn test_decrypt() {
        verify_test_vector(
            hex_as_bytes!(b"feffe9928665731c6d6a8f9467308308"),
            hex_as_bytes!(b"cafebabefacedbaddecaf888"),
            false,
            &[&hex_as_bytes!(
                b"feedfacedeadbeeffeedfacedeadbeef\
                abaddad2"
            )],
            &hex_as_bytes!(
                b"42831ec2217774244b7221b784d0d49c\
                e3aa212f2c02a4e035c17e2329aca12e\
                21d514b25466931c7d8f6a5aac84aa05\
                1ba30b396a0aac973d58e091\
                5bc94fbc3221a5db94fae95ae7121a47"
            ),
            &hex_as_bytes!(
                b"d9313225f88406e5a55909c5aff5269a\
                86a7a9531534f7da2e4c303d8a318a72\
                1c3c0c95956809532fcf0e2449a6b525\
                b16aedf5aa0de657ba637b39"
            ),
        );
    }

    #[test]
    fn test_decrypt_invalid_tag() {
        let srtp_provider = super::get_provider().srtp_provider;
        let cipher_provider = srtp_provider.aead_aes_128_gcm();
        let mut cipher = cipher_provider
            .create_cipher(hex_as_bytes!(b"feffe9928665731c6d6a8f9467308308"), false);

        let mut output = vec![0u8; 1024];
        let result = cipher.decrypt(
            &hex_as_bytes!(b"cafebabefacedbaddecaf888"),
            &[&hex_as_bytes!(
                b"feedfacedeadbeeffeedfacedeadbeef\
                abaddad2"
            )],
            &hex_as_bytes!(
                b"42831ec2217774244b7221b784d0d49c\
                e3aa212f2c02a4e035c17e2329aca12e\
                21d514b25466931c7d8f6a5aac84aa05\
                1ba30b396a0aac973d58e091\
                000000000000000000000000000000"
            ),
            &mut output,
        );
        assert!(result.is_err());
    }
}

mod aead_aes_256_gcm {
    fn verify_test_vector(
        key: [u8; 32],
        iv: [u8; 12],
        encrypt: bool,
        aads: &[&[u8]],
        input: &[u8],
        expected: &[u8],
    ) {
        let srtp_provider = super::get_provider().srtp_provider;
        let cipher_provider = srtp_provider.aead_aes_256_gcm();
        let mut cipher = cipher_provider.create_cipher(key, encrypt);

        let output_len = if encrypt {
            input.len() + 16
        } else {
            input.len() - 16
        };
        let mut output = vec![0u8; output_len];
        if encrypt {
            cipher.encrypt(&iv, aads[0], input, &mut output).unwrap();
        } else {
            cipher.decrypt(&iv, aads, input, &mut output).unwrap();
        }
        assert_eq!(expected, &output)
    }

    // AES-256-GCM Test Vectors from NIST SP 800-38D
    // Test Case 16

    #[test]
    fn test_encrypt() {
        verify_test_vector(
            hex_as_bytes!(
                b"feffe9928665731c6d6a8f9467308308\
                feffe9928665731c6d6a8f9467308308"
            ),
            hex_as_bytes!(b"cafebabefacedbaddecaf888"),
            true,
            &[&hex_as_bytes!(
                b"feedfacedeadbeeffeedfacedeadbeef\
                abaddad2"
            )],
            &hex_as_bytes!(
                b"d9313225f88406e5a55909c5aff5269a\
                86a7a9531534f7da2e4c303d8a318a72\
                1c3c0c95956809532fcf0e2449a6b525\
                b16aedf5aa0de657ba637b39"
            ),
            &hex_as_bytes!(
                b"522dc1f099567d07f47f37a32a84427d\
                643a8cdcbfe5c0c97598a2bd2555d1aa\
                8cb08e48590dbb3da7b08b1056828838\
                c5f61e6393ba7a0abcc9f662\
                76fc6ece0f4e1768cddf8853bb2d551b"
            ),
        );
    }

    #[test]
    fn test_decrypt() {
        verify_test_vector(
            hex_as_bytes!(
                b"feffe9928665731c6d6a8f9467308308\
                feffe9928665731c6d6a8f9467308308"
            ),
            hex_as_bytes!(b"cafebabefacedbaddecaf888"),
            false,
            &[&hex_as_bytes!(
                b"feedfacedeadbeeffeedfacedeadbeef\
                abaddad2"
            )],
            &hex_as_bytes!(
                b"522dc1f099567d07f47f37a32a84427d\
                643a8cdcbfe5c0c97598a2bd2555d1aa\
                8cb08e48590dbb3da7b08b1056828838\
                c5f61e6393ba7a0abcc9f662\
                76fc6ece0f4e1768cddf8853bb2d551b"
            ),
            &hex_as_bytes!(
                b"d9313225f88406e5a55909c5aff5269a\
                86a7a9531534f7da2e4c303d8a318a72\
                1c3c0c95956809532fcf0e2449a6b525\
                b16aedf5aa0de657ba637b39"
            ),
        );
    }
}

mod srtp_aes_128_ecb_round {
    const TEST_KEY: [u8; 16] = hex_as_bytes!(b"2b7e151628aed2a6abf7158809cf4f3c");

    fn verify_test_vector(key: &[u8], input: &[u8], expected: &[u8]) {
        let srtp_provider = super::get_provider().srtp_provider;
        let mut out = [0u8; 2048];
        srtp_provider.srtp_aes_128_ecb_round(key, input, out.as_mut_slice());
        assert_eq!(expected, &out[0..input.len()])
    }

    // Test vectors from NIST SP 800-38A:
    // https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf

    #[test]
    fn test_vec_1() {
        verify_test_vector(
            &TEST_KEY,
            &hex_as_bytes!(b"6bc1bee22e409f96e93d7e117393172a"),
            &hex_as_bytes!(b"3ad77bb40d7a3660a89ecaf32466ef97"),
        );
    }

    #[test]
    fn test_vec_2() {
        verify_test_vector(
            &TEST_KEY,
            &hex_as_bytes!(b"ae2d8a571e03ac9c9eb76fac45af8e51"),
            &hex_as_bytes!(b"f5d3d58503b9699de785895a96fdbaaf"),
        );
    }

    #[test]
    fn test_vec_3() {
        verify_test_vector(
            &TEST_KEY,
            &hex_as_bytes!(b"30c81c46a35ce411e5fbc1191a0a52ef"),
            &hex_as_bytes!(b"43b1cd7f598ece23881b00e3ed030688"),
        );
    }

    #[test]
    fn test_vec_4() {
        verify_test_vector(
            &TEST_KEY,
            &hex_as_bytes!(b"f69f2445df4f9b17ad2b417be66c3710"),
            &hex_as_bytes!(b"7b0c785e27e8ad3f8223207104725dd4"),
        );
    }
}

mod srtp_aes_256_ecb_round {
    const TEST_KEY: [u8; 32] =
        hex_as_bytes!(b"603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4");

    fn verify_test_vector(key: &[u8], input: &[u8], expected: &[u8]) {
        let srtp_provider = super::get_provider().srtp_provider;
        let mut out = [0u8; 2048];
        srtp_provider.srtp_aes_256_ecb_round(key, input, out.as_mut_slice());
        assert_eq!(expected, &out[0..input.len()])
    }

    // Test vectors from NIST SP 800-38A:
    // https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf

    #[test]
    fn test_vec_1() {
        verify_test_vector(
            &TEST_KEY,
            &hex_as_bytes!(b"6bc1bee22e409f96e93d7e117393172a"),
            &hex_as_bytes!(b"f3eed1bdb5d2a03c064b5a7e3db181f8"),
        );
    }

    #[test]
    fn test_vec_2() {
        verify_test_vector(
            &TEST_KEY,
            &hex_as_bytes!(b"ae2d8a571e03ac9c9eb76fac45af8e51"),
            &hex_as_bytes!(b"591ccb10d410ed26dc5ba74a31362870"),
        );
    }

    #[test]
    fn test_vec_3() {
        verify_test_vector(
            &TEST_KEY,
            &hex_as_bytes!(b"30c81c46a35ce411e5fbc1191a0a52ef"),
            &hex_as_bytes!(b"b6ed21b99ca6f4f9f153e7b1beafed1d"),
        );
    }

    #[test]
    fn test_vec_4() {
        verify_test_vector(
            &TEST_KEY,
            &hex_as_bytes!(b"f69f2445df4f9b17ad2b417be66c3710"),
            &hex_as_bytes!(b"23304b7a39f9f3ff067d8d8f9e24ecc7"),
        );
    }
}
