bash-prg-hash 0.1.1

bash-prg-hash function (STB 34.101.77-2020)
Documentation
use bash_prg_hash::{
    BashPrgHash1281, BashPrgHash1282, BashPrgHash1921, BashPrgHash1922, BashPrgHash2561,
    BashPrgHash2562,
};
use digest::{ExtendableOutput, TryCustomizedInit, Update};
use hex_literal::hex;
use std::fmt::Debug;

#[derive(Debug, Clone, Copy)]
struct TestVector {
    customization: &'static [u8],
    input: &'static [u8],
    output: &'static [u8],
}

fn bash_prg_hash_test<D: TryCustomizedInit + ExtendableOutput + Clone>(
    &TestVector {
        customization,
        input,
        output,
    }: &TestVector,
) -> Result<(), &'static str> {
    let Ok(mut hasher) = D::try_new_customized(customization) else {
        return Err("initialization error");
    };
    let mut buf = [0u8; 1024];
    let buf = &mut buf[..output.len()];
    // Test that it works when accepting the message all at once
    hasher.update(input);
    hasher.finalize_xof_into(buf);
    if buf != output {
        return Err("whole message");
    }
    buf.iter_mut().for_each(|b| *b = 0);

    // Test that it works when accepting the message in chunks
    for n in 1..core::cmp::min(17, input.len()) {
        let Ok(mut hasher) = D::try_new_customized(customization) else {
            return Err("initialization error");
        };
        for chunk in input.chunks(n) {
            hasher.update(chunk);
        }
        hasher.finalize_xof_into(buf);
        if buf != output {
            return Err("message in chunks");
        }
        buf.iter_mut().for_each(|b| *b = 0);
    }

    Ok(())
}

macro_rules! new_bash_prg_hash_test {
    ($name:ident, $hasher:ty $(,)?) => {
        #[test]
        fn $name() {
            digest::dev::blobby::parse_into_structs!(
                include_bytes!(concat!("data/", stringify!($name), ".blb"));
                static TEST_VECTORS: &[TestVector { customization, input, output }];
            );

            for (i, tv) in TEST_VECTORS.iter().enumerate() {
                if let Err(reason) = bash_prg_hash_test::<$hasher>(tv) {
                    panic!(
                        "\n\
                         Failed test #{i}\n\
                         reason:\t{reason}
                         test vector:\t{tv:?}\n"
                    );
                }
            }
        }
    };
}

// Test vectors generated with bee2 library: https://github.com/agievich/bee2
// Messages is the first N bytes of `beltH()` (belt S-box constant) for N = 0, 127, 128, 143, 144, 150
// Plus 3 tests with customization:
//   - 06075316 (4 bytes) + "Fifty four byte..." message
//   - 0102030405060708 (8 bytes) + "Fifty four byte..." message
//   - FFEEDDCC (4 bytes) + beltH()[0..100]
new_bash_prg_hash_test!(bashprg_l128_d1, BashPrgHash1281);
new_bash_prg_hash_test!(bashprg_l128_d2, BashPrgHash1282);
new_bash_prg_hash_test!(bashprg_l192_d1, BashPrgHash1921);
new_bash_prg_hash_test!(bashprg_l192_d2, BashPrgHash1922);
new_bash_prg_hash_test!(bashprg_l256_d1, BashPrgHash2561);
new_bash_prg_hash_test!(bashprg_l256_d2, BashPrgHash2562);

macro_rules! test_bash_prg_rand {
    ($name:ident, $hasher:ty, $expected:expr) => {
        #[test]
        fn $name() {
            let mut h = <$hasher>::default();
            digest::dev::feed_rand_16mib(&mut h);
            let mut output = [0u8; 64];
            h.finalize_xof_into(&mut output);
            assert_eq!(&output[..], $expected);
        }
    };
}

test_bash_prg_rand!(
    bashprg1282_rand,
    BashPrgHash1282,
    hex!(
        "BF15805CDEAE220A9DD50C325A4A0BDF326C6ED853CFA89592A9E2BEB4D0585C"
        "891AF66C1CA514390311FDFB51D467FC11439AE4907863A5C3861CDCF7F360EC"
    )
);

test_bash_prg_rand!(
    bashprg1921_rand,
    BashPrgHash1921,
    hex!(
        "82176D6DAF4F631E251CA41A7688FEB643B954383186C7902AB09D80EB5AB17C"
        "BA286D16912EBBACEC3D8143966107F6DFB5F4AC4F88B64F20AB49CEAD817E45"
    )
);

test_bash_prg_rand!(
    bashprg2562_rand,
    BashPrgHash2562,
    hex!(
        "AD07A8D61928296F4115F9E51AAA5FA986899BFDA8443F139D969600064EBCE2"
        "D591F583FA27F6B0F7E73DA2B29AF382AC2374C04463B91A27F1C48FEE8AAB2C"
    )
);

// Test from the https://apmi.bsu.by/assets/files/std/met-v10.zip
macro_rules! test_bash_prg_million {
    ($name:ident, $hasher:ty, $expected:expr) => {
        #[test]
        fn $name() {
            let mut h = <$hasher>::default();
            let block = [0x61u8; 1000];
            for _ in 0..1000 {
                h.update(&block);
            }
            let mut output = [0u8; 64];
            h.finalize_xof_into(&mut output);
            assert_eq!(&output[..$expected.len()], $expected);
        }
    };
}

// BASH.PRG.HASH128.5
test_bash_prg_million!(
    bashprg1282_million,
    BashPrgHash1282,
    hex!("F84CA85F610711674BB8ADDB87B747C6000A6B6735664687854052C60CE0F7B0")
);

// BASH.PRG.HASH192.5
test_bash_prg_million!(
    bashprg1922_million,
    BashPrgHash1922,
    hex!(
        "C9B4ABF055F1A2ED025385AA5480BA9ADFB1608409D3102BCC557C41B22C2866"
        "233212327DF735BE22D85DE44CB48C2E"
    )
);

// BASH.PRG.HASH256.5
test_bash_prg_million!(
    bashprg2562_million,
    BashPrgHash2562,
    hex!(
        "45911D1B40F84E5D53C3B27FBB91D696E96924C5F3BD61CC589F26FA9502F3F5"
        "847DF96A5D6B44D17388DFF413D0B8AE0D81073D010F1637A20892E06DAB1AF0"
    )
);