1
0
Fork 0
mirror of https://github.com/BLAKE3-team/BLAKE3 synced 2024-05-28 01:16:02 +02:00

move xof tests

This commit is contained in:
Jack O'Connor 2023-07-09 20:35:57 -07:00
parent 097225a43c
commit 2c62c3ba6c
3 changed files with 149 additions and 141 deletions

View File

@ -233,4 +233,15 @@ mod test {
fn test_chunks_and_parents_vs_reference() {
crate::test::test_chunks_and_parents_vs_reference(&implementation());
}
// This is circular but do it anyway.
#[test]
fn test_xof_vs_portable() {
crate::test::test_xof_vs_portable(&implementation());
}
#[test]
fn test_xof_vs_reference() {
crate::test::test_xof_vs_reference(&implementation());
}
}

View File

@ -98,12 +98,13 @@ pub fn test_hash_chunks_vs_portable(test_impl: &Implementation) {
let mut input_2_lengths = vec![1];
let mut next_len = CHUNK_LEN;
loop {
input_2_lengths.push(next_len - 1);
// 95 is one whole block plus one interesting part of another
input_2_lengths.push(next_len - 95);
input_2_lengths.push(next_len);
if next_len == MAX_SIMD_DEGREE * CHUNK_LEN {
break;
}
input_2_lengths.push(next_len + 1);
input_2_lengths.push(next_len + 95);
next_len += CHUNK_LEN;
}
for input_2_len in input_2_lengths {
@ -257,12 +258,13 @@ pub fn test_chunks_and_parents_vs_reference(test_impl: &Implementation) {
let mut test_lengths = vec![CHUNK_LEN + 1];
let mut next_len = 2 * CHUNK_LEN;
loop {
test_lengths.push(next_len - 1);
// 95 is one whole block plus one interesting part of another
test_lengths.push(next_len - 95);
test_lengths.push(next_len);
if next_len == MAX_INPUT_LEN {
break;
}
test_lengths.push(next_len + 1);
test_lengths.push(next_len + 95);
next_len += CHUNK_LEN;
}
for test_len in test_lengths {
@ -279,3 +281,135 @@ pub fn test_chunks_and_parents_vs_reference(test_impl: &Implementation) {
assert_eq!(ref_hash, test_hash);
}
}
pub fn test_xof_vs_portable(test_impl: &Implementation) {
let flags = CHUNK_START | CHUNK_END | ROOT | KEYED_HASH;
for counter in INITIAL_COUNTERS {
dbg!(counter);
for input_len in [0, 1, BLOCK_LEN] {
dbg!(input_len);
let mut input_block = [0u8; BLOCK_LEN];
for byte_index in 0..input_len {
input_block[byte_index] = byte_index as u8 + 42;
}
// Try equal to and partway through every whole number of output blocks.
const MAX_OUTPUT_LEN: usize = 2 * MAX_SIMD_DEGREE * BLOCK_LEN;
let mut output_lengths = Vec::new();
let mut next_len = 0;
loop {
output_lengths.push(next_len);
if next_len == MAX_OUTPUT_LEN {
break;
}
output_lengths.push(next_len + 31);
next_len += BLOCK_LEN;
}
for output_len in output_lengths {
dbg!(output_len);
let mut portable_output = [0xff; MAX_OUTPUT_LEN];
portable::implementation().xof(
&input_block,
input_len as u32,
&TEST_KEY,
counter,
flags,
&mut portable_output[..output_len],
);
let mut test_output = [0xff; MAX_OUTPUT_LEN];
test_impl.xof(
&input_block,
input_len as u32,
&TEST_KEY,
counter,
flags,
&mut test_output[..output_len],
);
assert_eq!(portable_output, test_output);
// Double check that the implementation didn't overwrite.
assert!(test_output[output_len..].iter().all(|&b| b == 0xff));
// The first XOR cancels out the output.
test_impl.xof_xor(
&input_block,
input_len as u32,
&TEST_KEY,
counter,
flags,
&mut test_output[..output_len],
);
assert!(test_output[..output_len].iter().all(|&b| b == 0));
assert!(test_output[output_len..].iter().all(|&b| b == 0xff));
// The second XOR restores out the output.
test_impl.xof_xor(
&input_block,
input_len as u32,
&TEST_KEY,
counter,
flags,
&mut test_output[..output_len],
);
assert_eq!(portable_output, test_output);
assert!(test_output[output_len..].iter().all(|&b| b == 0xff));
}
}
}
}
pub fn test_xof_vs_reference(test_impl: &Implementation) {
let input = b"hello world";
let mut input_block = [0; BLOCK_LEN];
input_block[..input.len()].copy_from_slice(input);
const MAX_OUTPUT_LEN: usize = 2 * MAX_SIMD_DEGREE * BLOCK_LEN;
let mut ref_output = [0; MAX_OUTPUT_LEN];
let mut ref_hasher = reference_impl::Hasher::new_keyed(&TEST_KEY);
ref_hasher.update(input);
ref_hasher.finalize(&mut ref_output);
// Try equal to and partway through every whole number of output blocks.
let mut output_lengths = vec![0, 1, 31];
let mut next_len = BLOCK_LEN;
loop {
output_lengths.push(next_len);
if next_len == MAX_OUTPUT_LEN {
break;
}
output_lengths.push(next_len + 31);
next_len += BLOCK_LEN;
}
for output_len in output_lengths {
dbg!(output_len);
let mut test_output = [0; MAX_OUTPUT_LEN];
test_impl.xof(
&input_block,
input.len() as u32,
&TEST_KEY,
0,
crate::KEYED_HASH | crate::CHUNK_START | crate::CHUNK_END | crate::ROOT,
&mut test_output[..output_len],
);
assert_eq!(ref_output[..output_len], test_output[..output_len]);
// Double check that the implementation didn't overwrite.
assert!(test_output[output_len..].iter().all(|&b| b == 0));
// Do it again starting from block 1.
if output_len >= BLOCK_LEN {
test_impl.xof(
&input_block,
input.len() as u32,
&TEST_KEY,
1,
crate::KEYED_HASH | crate::CHUNK_START | crate::CHUNK_END | crate::ROOT,
&mut test_output[..output_len - BLOCK_LEN],
);
assert_eq!(
ref_output[BLOCK_LEN..output_len],
test_output[..output_len - BLOCK_LEN],
);
}
}
}

View File

@ -51,143 +51,6 @@ pub const TEST_CASES_MAX: usize = 100 * CHUNK_LEN;
pub const TEST_KEY: &CVBytes = b"whats the Elvish word for friend";
pub const TEST_KEY_WORDS: &CVWords = &guts::words_from_le_bytes_32(TEST_KEY);
// Both xof() and xof_xof() have this signature.
type XofFn = unsafe fn(
block: &[u8; BLOCK_LEN],
block_len: u8,
cv: &[u32; 8],
counter: u64,
flags: u8,
out: &mut [u8],
);
pub fn test_xof_and_xor_fns(target_xof: XofFn, target_xof_xor: XofFn) {
// 31 (16 + 8 + 4 + 2 + 1) outputs
const NUM_OUTPUTS: usize = 31;
let different_flags = [
crate::CHUNK_START | crate::CHUNK_END | crate::ROOT,
crate::PARENT | crate::ROOT | crate::KEYED_HASH,
];
for input_len in [0, 1, BLOCK_LEN] {
let mut input_block = [0u8; BLOCK_LEN];
paint_test_input(&mut input_block[..input_len]);
for output_len in [0, 1, BLOCK_LEN, BLOCK_LEN + 1, BLOCK_LEN * NUM_OUTPUTS] {
let mut test_output_buf = [0xff; BLOCK_LEN * NUM_OUTPUTS];
for &counter in INITIAL_COUNTERS {
for flags in different_flags {
let mut expected_output_buf = [0xff; BLOCK_LEN * NUM_OUTPUTS];
crate::portable::xof(
&input_block,
input_len as u8,
TEST_KEY_WORDS,
counter,
flags,
&mut expected_output_buf[..output_len],
);
unsafe {
target_xof(
&input_block,
input_len as u8,
TEST_KEY_WORDS,
counter,
flags,
&mut test_output_buf[..output_len],
);
}
assert_eq!(
expected_output_buf[..output_len],
test_output_buf[..output_len],
);
// Make sure unsafe implementations don't overwrite. This shouldn't be possible in the
// portable implementation, which is all safe code, but it could happen in others.
assert!(test_output_buf[output_len..].iter().all(|&b| b == 0xff));
// The first XOR cancels out the output.
unsafe {
target_xof_xor(
&input_block,
input_len as u8,
TEST_KEY_WORDS,
counter,
flags,
&mut test_output_buf[..output_len],
);
}
assert!(test_output_buf[..output_len].iter().all(|&b| b == 0));
assert!(test_output_buf[output_len..].iter().all(|&b| b == 0xff));
// The second XOR restores out the output.
unsafe {
target_xof_xor(
&input_block,
input_len as u8,
TEST_KEY_WORDS,
counter,
flags,
&mut test_output_buf[..output_len],
);
}
assert_eq!(
expected_output_buf[..output_len],
test_output_buf[..output_len],
);
assert!(test_output_buf[output_len..].iter().all(|&b| b == 0xff));
}
}
}
}
}
#[test]
fn test_compare_reference_impl_xof() {
const NUM_OUTPUTS: usize = 31;
let input = b"hello world";
let mut input_block = [0; BLOCK_LEN];
input_block[..input.len()].copy_from_slice(input);
let mut reference_output_buf = [0; BLOCK_LEN * NUM_OUTPUTS];
let mut reference_hasher = reference_impl::Hasher::new_keyed(TEST_KEY);
reference_hasher.update(input);
reference_hasher.finalize(&mut reference_output_buf);
for output_len in [0, 1, BLOCK_LEN, BLOCK_LEN + 1, BLOCK_LEN * NUM_OUTPUTS] {
let mut test_output_buf = [0; BLOCK_LEN * NUM_OUTPUTS];
crate::platform::Platform::detect().xof(
&input_block,
input.len() as u8,
TEST_KEY_WORDS,
0,
crate::KEYED_HASH | crate::CHUNK_START | crate::CHUNK_END | crate::ROOT,
&mut test_output_buf[..output_len],
);
assert_eq!(
reference_output_buf[..output_len],
test_output_buf[..output_len],
);
// Make sure unsafe implementations don't overwrite. This shouldn't be possible in the
// portable implementation, which is all safe code, but it could happen in others.
assert!(test_output_buf[output_len..].iter().all(|&b| b == 0));
// Do it again starting from block 1.
if output_len >= BLOCK_LEN {
crate::platform::Platform::detect().xof(
&input_block,
input.len() as u8,
TEST_KEY_WORDS,
1,
crate::KEYED_HASH | crate::CHUNK_START | crate::CHUNK_END | crate::ROOT,
&mut test_output_buf[..output_len - BLOCK_LEN],
);
assert_eq!(
reference_output_buf[BLOCK_LEN..output_len],
test_output_buf[..output_len - BLOCK_LEN],
);
}
}
}
type UniversalHashFn =
unsafe fn(input: &[u8], key: &[u32; 8], counter: u64) -> [u8; UNIVERSAL_HASH_LEN];