/*
 * Copyright (C) 2026, Stephan Mueller <smueller@chronox.de>
 *
 * License: see LICENSE file in root directory
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
 * WHICH ARE HEREBY DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */

/*
 * Tests are derived from https://github.com/fortanix/rustls-mbedtls-provider/blob/master/rustls-mbedcrypto-provider/src/self_tests.rs
 */

//! This module defines self-tests. Running tests in this module (i.e., calling
//! [`self_tests()`](self::self_tests()))
//! at runtime can help with [FIPS 140-3] compliance.
use rustls::crypto::tls13::{Hkdf, HkdfUsingHmac, expand};
use std::vec::Vec;

// test copied from rustls repo
/// TLS 1.3 KDF [test case 1](https://datatracker.ietf.org/doc/html/rfc5869#appendix-A.1).
pub fn tls13_kdf_test_case_1() {
    let hkdf = HkdfUsingHmac(&super::hmac::HMAC_SHA256);
    let ikm = &[0x0b; 22];
    let salt = &[
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
        0x0c,
    ];
    let info: &[&[u8]] = &[
        &[0xf0, 0xf1, 0xf2],
        &[0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9],
    ];

    let output: [u8; 42] =
        expand(hkdf.extract_from_secret(Some(salt), ikm).as_ref(), info);

    assert_eq!(
        &output,
        &[
            0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, 0x90, 0x43, 0x4f,
            0x64, 0xd0, 0x36, 0x2f, 0x2a, 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a,
            0x5a, 0x4c, 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf, 0x34,
            0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, 0x58, 0x65
        ]
    );
}

// test copied from rustls repo
/// TLS 1.3 KDF [test case 2](https://datatracker.ietf.org/doc/html/rfc5869#appendix-A.2).
pub fn tls13_kdf_test_case_2() {
    let hkdf = HkdfUsingHmac(&super::hmac::HMAC_SHA256);
    let ikm: Vec<u8> = (0x00u8..=0x4f).collect();
    let salt: Vec<u8> = (0x60u8..=0xaf).collect();
    let info: Vec<u8> = (0xb0u8..=0xff).collect();

    let output: [u8; 82] = expand(
        hkdf.extract_from_secret(Some(&salt), &ikm).as_ref(),
        &[&info],
    );

    assert_eq!(
        &output,
        &[
            0xb1, 0x1e, 0x39, 0x8d, 0xc8, 0x03, 0x27, 0xa1, 0xc8, 0xe7, 0xf7,
            0x8c, 0x59, 0x6a, 0x49, 0x34, 0x4f, 0x01, 0x2e, 0xda, 0x2d, 0x4e,
            0xfa, 0xd8, 0xa0, 0x50, 0xcc, 0x4c, 0x19, 0xaf, 0xa9, 0x7c, 0x59,
            0x04, 0x5a, 0x99, 0xca, 0xc7, 0x82, 0x72, 0x71, 0xcb, 0x41, 0xc6,
            0x5e, 0x59, 0x0e, 0x09, 0xda, 0x32, 0x75, 0x60, 0x0c, 0x2f, 0x09,
            0xb8, 0x36, 0x77, 0x93, 0xa9, 0xac, 0xa3, 0xdb, 0x71, 0xcc, 0x30,
            0xc5, 0x81, 0x79, 0xec, 0x3e, 0x87, 0xc1, 0x4c, 0x01, 0xd5, 0xc1,
            0xf3, 0x43, 0x4f, 0x1d, 0x87
        ]
    );
}

// test copied from rustls repo
/// TLS 1.3 KDF [test case 3](https://datatracker.ietf.org/doc/html/rfc5869#appendix-A.3).
pub fn tls13_kdf_test_case_3() {
    let hkdf = HkdfUsingHmac(&super::hmac::HMAC_SHA256);
    let ikm = &[0x0b; 22];
    let salt = &[];
    let info = &[];

    let output: [u8; 42] =
        expand(hkdf.extract_from_secret(Some(salt), ikm).as_ref(), info);

    assert_eq!(
        &output,
        &[
            0x8d, 0xa4, 0xe7, 0x75, 0xa5, 0x63, 0xc1, 0x8f, 0x71, 0x5f, 0x80,
            0x2a, 0x06, 0x3c, 0x5a, 0x31, 0xb8, 0xa1, 0x1f, 0x5c, 0x5e, 0xe1,
            0x87, 0x9e, 0xc3, 0x45, 0x4e, 0x5f, 0x3c, 0x73, 0x8d, 0x2d, 0x9d,
            0x20, 0x13, 0x95, 0xfa, 0xa4, 0xb6, 0x1a, 0x96, 0xc8
        ]
    );
}

/// Run all the self_tests. If any test fails, this function will panic.
///
/// If `verbose` is true, print messages about tests that have been executed.
pub fn self_tests(verbose: bool) {
    macro_rules! print_msg {
		($($tt: tt)*) => {
			if verbose { std::println!($($tt)*) }
		};
	}

    tls13_kdf_test_case_1();
    print_msg!("tls13_kdf_test_case_1 passed.");
    tls13_kdf_test_case_2();
    print_msg!("tls13_kdf_test_case_2 passed.");
    tls13_kdf_test_case_3();
    print_msg!("tls13_kdf_test_case_3 passed.");

    print_msg!("All rustls-leancrypto provider self-tests passed.");
}

#[test]
fn self_tests_succeed() {
    self_tests(true)
}
