1
0
Fork 0
mirror of https://github.com/BLAKE3-team/BLAKE3 synced 2024-04-25 06:35:17 +02:00

rename "offset" to "counter" and always increment it by 1

This is simpler than sometimes incrementing by CHUNK_LEN and other times
incrementing by BLOCK_LEN.
This commit is contained in:
Jack O'Connor 2019-12-12 18:21:17 -05:00
parent a5cc3b2867
commit b5f1e925f7
14 changed files with 462 additions and 447 deletions

View File

@ -56,7 +56,7 @@ fn round(state: &mut [u32; 16], m: &[u32; 16], schedule: &[usize; 16]) {
fn compress(
chaining_value: &[u32; 8],
block_words: &[u32; 16],
offset: u64,
counter: u64,
block_len: u32,
flags: u32,
) -> [u32; 16] {
@ -73,8 +73,8 @@ fn compress(
IV[1],
IV[2],
IV[3],
offset as u32,
(offset >> 32) as u32,
counter as u32,
(counter >> 32) as u32,
block_len,
flags,
];
@ -104,7 +104,7 @@ fn words_from_litte_endian_bytes(bytes: &[u8], words: &mut [u32]) {
struct Output {
input_chaining_value: [u32; 8],
block_words: [u32; 16],
offset: u64,
counter: u64,
block_len: u32,
flags: u32,
}
@ -114,19 +114,22 @@ impl Output {
first_8_words(compress(
&self.input_chaining_value,
&self.block_words,
self.offset,
self.counter, // the input chunk counter, or 0 for parent nodes
self.block_len,
self.flags,
))
}
fn root_output_bytes(&self, out_slice: &mut [u8]) {
let mut offset = 0;
// Root output comes from either the first chunk, or from a parent
// node, so the counter value when we get here is always 0.
debug_assert_eq!(self.counter, 0);
let mut output_block_counter = 0;
for out_block in out_slice.chunks_mut(2 * OUT_LEN) {
let words = compress(
&self.input_chaining_value,
&self.block_words,
offset,
output_block_counter,
self.block_len,
self.flags | ROOT,
);
@ -134,14 +137,14 @@ impl Output {
for (word, out_word) in words.iter().zip(out_block.chunks_mut(4)) {
out_word.copy_from_slice(&word.to_le_bytes()[..out_word.len()]);
}
offset += 2 * OUT_LEN as u64;
output_block_counter += 1;
}
}
}
struct ChunkState {
chaining_value: [u32; 8],
offset: u64,
chunk_counter: u64,
block: [u8; BLOCK_LEN],
block_len: u8,
blocks_compressed: u8,
@ -149,10 +152,10 @@ struct ChunkState {
}
impl ChunkState {
fn new(key: &[u32; 8], offset: u64, flags: u32) -> Self {
fn new(key: &[u32; 8], chunk_counter: u64, flags: u32) -> Self {
Self {
chaining_value: *key,
offset,
chunk_counter,
block: [0; BLOCK_LEN],
block_len: 0,
blocks_compressed: 0,
@ -180,7 +183,7 @@ impl ChunkState {
self.chaining_value = first_8_words(compress(
&self.chaining_value,
&block_words,
self.offset,
self.chunk_counter,
BLOCK_LEN as u32,
self.flags | self.start_flag(),
));
@ -204,7 +207,7 @@ impl ChunkState {
input_chaining_value: self.chaining_value,
block_words,
block_len: self.block_len as u32,
offset: self.offset,
counter: self.chunk_counter,
flags: self.flags | self.start_flag() | CHUNK_END,
}
}
@ -222,7 +225,7 @@ fn parent_output(
Output {
input_chaining_value: *key,
block_words,
offset: 0, // Always 0 for parent nodes.
counter: 0, // Always 0 for parent nodes.
block_len: BLOCK_LEN as u32, // Always BLOCK_LEN (64) for parent nodes.
flags: PARENT | flags,
}
@ -275,14 +278,14 @@ impl Hasher {
self.cv_stack[self.cv_stack_len as usize]
}
fn push_chunk_chaining_value(&mut self, mut cv: [u32; 8], total_bytes: u64) {
fn push_chunk_chaining_value(&mut self, mut cv: [u32; 8], total_chunks: u64) {
// The new chunk chaining value might complete some subtrees along the
// right edge of the growing tree. For each completed subtree, pop its
// left child CV off the stack and compress a new parent CV. After as
// many parent compressions as possible, push the new CV onto the
// stack. The final length of the stack will be the count of 1 bits in
// the total number of chunks or (equivalently) input bytes so far.
let final_stack_len = total_bytes.count_ones() as u8;
// the total number of chunks so far.
let final_stack_len = total_chunks.count_ones() as u8;
while self.cv_stack_len >= final_stack_len {
cv = parent_output(&self.pop_stack(), &cv, &self.key, self.chunk_state.flags)
.chaining_value();
@ -295,10 +298,9 @@ impl Hasher {
while !input.is_empty() {
if self.chunk_state.len() == CHUNK_LEN {
let chunk_cv = self.chunk_state.output().chaining_value();
let new_chunk_offset = self.chunk_state.offset + CHUNK_LEN as u64;
self.push_chunk_chaining_value(chunk_cv, new_chunk_offset);
self.chunk_state =
ChunkState::new(&self.key, new_chunk_offset, self.chunk_state.flags);
let total_chunks = self.chunk_state.chunk_counter + 1;
self.push_chunk_chaining_value(chunk_cv, total_chunks);
self.chunk_state = ChunkState::new(&self.key, total_chunks, self.chunk_state.flags);
}
let want = CHUNK_LEN - self.chunk_state.len();

View File

@ -3,7 +3,9 @@ use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
use crate::{offset_high, offset_low, CVWords, OffsetDeltas, BLOCK_LEN, IV, MSG_SCHEDULE, OUT_LEN};
use crate::{
counter_high, counter_low, CVWords, IncrementCounter, BLOCK_LEN, IV, MSG_SCHEDULE, OUT_LEN,
};
use arrayref::{array_mut_ref, mut_array_refs};
pub const DEGREE: usize = 8;
@ -270,27 +272,28 @@ unsafe fn transpose_msg_vecs(inputs: &[*const u8; DEGREE], block_offset: usize)
}
#[inline(always)]
unsafe fn load_offsets(offset: u64, offset_deltas: &OffsetDeltas) -> (__m256i, __m256i) {
unsafe fn load_counters(counter: u64, increment_counter: IncrementCounter) -> (__m256i, __m256i) {
let mask = if increment_counter.yes() { !0 } else { 0 };
(
set8(
offset_low(offset + offset_deltas[0]),
offset_low(offset + offset_deltas[1]),
offset_low(offset + offset_deltas[2]),
offset_low(offset + offset_deltas[3]),
offset_low(offset + offset_deltas[4]),
offset_low(offset + offset_deltas[5]),
offset_low(offset + offset_deltas[6]),
offset_low(offset + offset_deltas[7]),
counter_low(counter + (mask & 0)),
counter_low(counter + (mask & 1)),
counter_low(counter + (mask & 2)),
counter_low(counter + (mask & 3)),
counter_low(counter + (mask & 4)),
counter_low(counter + (mask & 5)),
counter_low(counter + (mask & 6)),
counter_low(counter + (mask & 7)),
),
set8(
offset_high(offset + offset_deltas[0]),
offset_high(offset + offset_deltas[1]),
offset_high(offset + offset_deltas[2]),
offset_high(offset + offset_deltas[3]),
offset_high(offset + offset_deltas[4]),
offset_high(offset + offset_deltas[5]),
offset_high(offset + offset_deltas[6]),
offset_high(offset + offset_deltas[7]),
counter_high(counter + (mask & 0)),
counter_high(counter + (mask & 1)),
counter_high(counter + (mask & 2)),
counter_high(counter + (mask & 3)),
counter_high(counter + (mask & 4)),
counter_high(counter + (mask & 5)),
counter_high(counter + (mask & 6)),
counter_high(counter + (mask & 7)),
),
)
}
@ -300,8 +303,8 @@ pub unsafe fn hash8(
inputs: &[*const u8; DEGREE],
blocks: usize,
key: &CVWords,
offset: u64,
offset_deltas: &OffsetDeltas,
counter: u64,
increment_counter: IncrementCounter,
flags: u8,
flags_start: u8,
flags_end: u8,
@ -317,7 +320,7 @@ pub unsafe fn hash8(
set1(key[6]),
set1(key[7]),
];
let (offset_low_vec, offset_high_vec) = load_offsets(offset, offset_deltas);
let (counter_low_vec, counter_high_vec) = load_counters(counter, increment_counter);
let mut block_flags = flags | flags_start;
for block in 0..blocks {
@ -345,8 +348,8 @@ pub unsafe fn hash8(
set1(IV[1]),
set1(IV[2]),
set1(IV[3]),
offset_low_vec,
offset_high_vec,
counter_low_vec,
counter_high_vec,
block_len_vec,
block_flags_vec,
];
@ -384,8 +387,8 @@ pub unsafe fn hash8(
pub unsafe fn hash_many<A: arrayvec::Array<Item = u8>>(
mut inputs: &[&A],
key: &CVWords,
mut offset: u64,
offset_deltas: &OffsetDeltas,
mut counter: u64,
increment_counter: IncrementCounter,
flags: u8,
flags_start: u8,
flags_end: u8,
@ -401,22 +404,24 @@ pub unsafe fn hash_many<A: arrayvec::Array<Item = u8>>(
input_ptrs,
blocks,
key,
offset,
offset_deltas,
counter,
increment_counter,
flags,
flags_start,
flags_end,
array_mut_ref!(out, 0, DEGREE * OUT_LEN),
);
if increment_counter.yes() {
counter += DEGREE as u64;
}
inputs = &inputs[DEGREE..];
offset += DEGREE as u64 * offset_deltas[1];
out = &mut out[DEGREE * OUT_LEN..];
}
crate::sse41::hash_many(
inputs,
key,
offset,
offset_deltas,
counter,
increment_counter,
flags,
flags_start,
flags_end,

View File

@ -11,10 +11,10 @@
typedef struct {
uint32_t cv[8];
uint64_t offset;
uint16_t count;
uint64_t chunk_counter;
uint8_t buf[BLAKE3_BLOCK_LEN];
uint8_t buf_len;
uint8_t blocks_compressed;
uint8_t flags;
} blake3_chunk_state;

View File

@ -111,12 +111,12 @@ INLINE void undiagonalize(__m128i *row0, __m128i *row2, __m128i *row3) {
INLINE void compress_pre(__m128i rows[4], const uint32_t cv[8],
const uint8_t block[BLAKE3_BLOCK_LEN],
uint8_t block_len, uint64_t offset, uint8_t flags) {
uint8_t block_len, uint64_t counter, uint8_t flags) {
rows[0] = loadu_128((uint8_t *)&cv[0]);
rows[1] = loadu_128((uint8_t *)&cv[4]);
rows[2] = set4(IV[0], IV[1], IV[2], IV[3]);
rows[3] = set4(offset_low(offset), offset_high(offset), (uint32_t)block_len,
(uint32_t)flags);
rows[3] = set4(counter_low(counter), counter_high(counter),
(uint32_t)block_len, (uint32_t)flags);
__m128i m0 = loadu_128(&block[sizeof(__m128i) * 0]);
__m128i m1 = loadu_128(&block[sizeof(__m128i) * 1]);
@ -281,10 +281,10 @@ INLINE void compress_pre(__m128i rows[4], const uint32_t cv[8],
void blake3_compress_xof_avx512(const uint32_t cv[8],
const uint8_t block[BLAKE3_BLOCK_LEN],
uint8_t block_len, uint64_t offset,
uint8_t block_len, uint64_t counter,
uint8_t flags, uint8_t out[64]) {
__m128i rows[4];
compress_pre(rows, cv, block, block_len, offset, flags);
compress_pre(rows, cv, block, block_len, counter, flags);
storeu_128(xor_128(rows[0], rows[2]), &out[0]);
storeu_128(xor_128(rows[1], rows[3]), &out[16]);
storeu_128(xor_128(rows[2], loadu_128((uint8_t *)&cv[0])), &out[32]);
@ -293,10 +293,10 @@ void blake3_compress_xof_avx512(const uint32_t cv[8],
void blake3_compress_in_place_avx512(uint32_t cv[8],
const uint8_t block[BLAKE3_BLOCK_LEN],
uint8_t block_len, uint64_t offset,
uint8_t block_len, uint64_t counter,
uint8_t flags) {
__m128i rows[4];
compress_pre(rows, cv, block, block_len, offset, flags);
compress_pre(rows, cv, block, block_len, counter, flags);
storeu_128(xor_128(rows[0], rows[2]), (uint8_t *)&cv[0]);
storeu_128(xor_128(rows[1], rows[3]), (uint8_t *)&cv[4]);
}
@ -468,24 +468,29 @@ INLINE void transpose_msg_vecs4(const uint8_t *const *inputs,
transpose_vecs_128(&out[12]);
}
INLINE void load_offsets4(uint64_t offset, const uint64_t deltas[4],
__m128i *out_lo, __m128i *out_hi) {
__m256i a = _mm256_add_epi64(_mm256_set1_epi64x((int64_t)offset),
_mm256_loadu_si256((const __m256i *)deltas));
*out_lo = _mm256_cvtepi64_epi32(a);
*out_hi = _mm256_cvtepi64_epi32(_mm256_srli_epi64(a, 32));
INLINE void load_counters4(uint64_t counter, bool increment_counter,
__m128i *out_lo, __m128i *out_hi) {
uint64_t mask = (increment_counter ? ~0 : 0);
__m256i mask_vec = _mm256_set1_epi64x(mask);
__m256i deltas = _mm256_setr_epi64x(0, 1, 2, 3);
deltas = _mm256_and_si256(mask_vec, deltas);
__m256i counters =
_mm256_add_epi64(_mm256_set1_epi64x((int64_t)counter), deltas);
*out_lo = _mm256_cvtepi64_epi32(counters);
*out_hi = _mm256_cvtepi64_epi32(_mm256_srli_epi64(counters, 32));
}
void blake3_hash4_avx512(const uint8_t *const *inputs, size_t blocks,
const uint32_t key[8], uint64_t offset,
offset_deltas_t offset_deltas, uint8_t flags,
const uint32_t key[8], uint64_t counter,
bool increment_counter, uint8_t flags,
uint8_t flags_start, uint8_t flags_end, uint8_t *out) {
__m128i h_vecs[8] = {
set1_128(key[0]), set1_128(key[1]), set1_128(key[2]), set1_128(key[3]),
set1_128(key[4]), set1_128(key[5]), set1_128(key[6]), set1_128(key[7]),
};
__m128i offset_low_vec, offset_high_vec;
load_offsets4(offset, offset_deltas, &offset_low_vec, &offset_high_vec);
__m128i counter_low_vec, counter_high_vec;
load_counters4(counter, increment_counter, &counter_low_vec,
&counter_high_vec);
uint8_t block_flags = flags | flags_start;
for (size_t block = 0; block < blocks; block++) {
@ -498,10 +503,10 @@ void blake3_hash4_avx512(const uint8_t *const *inputs, size_t blocks,
transpose_msg_vecs4(inputs, block * BLAKE3_BLOCK_LEN, msg_vecs);
__m128i v[16] = {
h_vecs[0], h_vecs[1], h_vecs[2], h_vecs[3],
h_vecs[4], h_vecs[5], h_vecs[6], h_vecs[7],
set1_128(IV[0]), set1_128(IV[1]), set1_128(IV[2]), set1_128(IV[3]),
offset_low_vec, offset_high_vec, block_len_vec, block_flags_vec,
h_vecs[0], h_vecs[1], h_vecs[2], h_vecs[3],
h_vecs[4], h_vecs[5], h_vecs[6], h_vecs[7],
set1_128(IV[0]), set1_128(IV[1]), set1_128(IV[2]), set1_128(IV[3]),
counter_low_vec, counter_high_vec, block_len_vec, block_flags_vec,
};
round_fn4(v, msg_vecs, 0);
round_fn4(v, msg_vecs, 1);
@ -714,24 +719,29 @@ INLINE void transpose_msg_vecs8(const uint8_t *const *inputs,
transpose_vecs_256(&out[8]);
}
INLINE void load_offsets8(uint64_t offset, const uint64_t deltas[8],
__m256i *out_lo, __m256i *out_hi) {
__m512i a = _mm512_add_epi64(_mm512_set1_epi64((int64_t)offset),
_mm512_loadu_si512((const __m512i *)deltas));
*out_lo = _mm512_cvtepi64_epi32(a);
*out_hi = _mm512_cvtepi64_epi32(_mm512_srli_epi64(a, 32));
INLINE void load_counters8(uint64_t counter, bool increment_counter,
__m256i *out_lo, __m256i *out_hi) {
uint64_t mask = (increment_counter ? ~0 : 0);
__m512i mask_vec = _mm512_set1_epi64(mask);
__m512i deltas = _mm512_setr_epi64(0, 1, 2, 3, 4, 5, 6, 7);
deltas = _mm512_and_si512(mask_vec, deltas);
__m512i counters =
_mm512_add_epi64(_mm512_set1_epi64((int64_t)counter), deltas);
*out_lo = _mm512_cvtepi64_epi32(counters);
*out_hi = _mm512_cvtepi64_epi32(_mm512_srli_epi64(counters, 32));
}
void blake3_hash8_avx512(const uint8_t *const *inputs, size_t blocks,
const uint32_t key[8], uint64_t offset,
offset_deltas_t offset_deltas, uint8_t flags,
const uint32_t key[8], uint64_t counter,
bool increment_counter, uint8_t flags,
uint8_t flags_start, uint8_t flags_end, uint8_t *out) {
__m256i h_vecs[8] = {
set1_256(key[0]), set1_256(key[1]), set1_256(key[2]), set1_256(key[3]),
set1_256(key[4]), set1_256(key[5]), set1_256(key[6]), set1_256(key[7]),
};
__m256i offset_low_vec, offset_high_vec;
load_offsets8(offset, offset_deltas, &offset_low_vec, &offset_high_vec);
__m256i counter_low_vec, counter_high_vec;
load_counters8(counter, increment_counter, &counter_low_vec,
&counter_high_vec);
uint8_t block_flags = flags | flags_start;
for (size_t block = 0; block < blocks; block++) {
@ -744,10 +754,10 @@ void blake3_hash8_avx512(const uint8_t *const *inputs, size_t blocks,
transpose_msg_vecs8(inputs, block * BLAKE3_BLOCK_LEN, msg_vecs);
__m256i v[16] = {
h_vecs[0], h_vecs[1], h_vecs[2], h_vecs[3],
h_vecs[4], h_vecs[5], h_vecs[6], h_vecs[7],
set1_256(IV[0]), set1_256(IV[1]), set1_256(IV[2]), set1_256(IV[3]),
offset_low_vec, offset_high_vec, block_len_vec, block_flags_vec,
h_vecs[0], h_vecs[1], h_vecs[2], h_vecs[3],
h_vecs[4], h_vecs[5], h_vecs[6], h_vecs[7],
set1_256(IV[0]), set1_256(IV[1]), set1_256(IV[2]), set1_256(IV[3]),
counter_low_vec, counter_high_vec, block_len_vec, block_flags_vec,
};
round_fn8(v, msg_vecs, 0);
round_fn8(v, msg_vecs, 1);
@ -1018,12 +1028,16 @@ INLINE void transpose_msg_vecs16(const uint8_t *const *inputs,
transpose_vecs_512(out);
}
INLINE void load_offsets16(uint64_t offset, const uint64_t deltas[16],
__m512i *out_lo, __m512i *out_hi) {
__m512i a = _mm512_add_epi64(_mm512_set1_epi64((int64_t)offset),
_mm512_loadu_si512((const __m512i *)&deltas[0]));
__m512i b = _mm512_add_epi64(_mm512_set1_epi64((int64_t)offset),
_mm512_loadu_si512((const __m512i *)&deltas[8]));
INLINE void load_counters16(uint64_t counter, bool increment_counter,
__m512i *out_lo, __m512i *out_hi) {
uint64_t mask = (increment_counter ? ~0 : 0);
__m512i mask_vec = _mm512_set1_epi64(mask);
__m512i deltas_a = _mm512_setr_epi64(0, 1, 2, 3, 4, 5, 6, 7);
deltas_a = _mm512_and_si512(mask_vec, deltas_a);
__m512i deltas_b = _mm512_setr_epi64(8, 9, 10, 11, 12, 13, 14, 15);
deltas_b = _mm512_and_si512(mask_vec, deltas_b);
__m512i a = _mm512_add_epi64(_mm512_set1_epi64((int64_t)counter), deltas_a);
__m512i b = _mm512_add_epi64(_mm512_set1_epi64((int64_t)counter), deltas_b);
__m512i lo_indexes = _mm512_setr_epi32(0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20,
22, 24, 26, 28, 30);
__m512i hi_indexes = _mm512_setr_epi32(1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21,
@ -1033,16 +1047,17 @@ INLINE void load_offsets16(uint64_t offset, const uint64_t deltas[16],
}
void blake3_hash16_avx512(const uint8_t *const *inputs, size_t blocks,
const uint32_t key[8], uint64_t offset,
offset_deltas_t offset_deltas, uint8_t flags,
const uint32_t key[8], uint64_t counter,
bool increment_counter, uint8_t flags,
uint8_t flags_start, uint8_t flags_end,
uint8_t *out) {
__m512i h_vecs[8] = {
set1_512(key[0]), set1_512(key[1]), set1_512(key[2]), set1_512(key[3]),
set1_512(key[4]), set1_512(key[5]), set1_512(key[6]), set1_512(key[7]),
};
__m512i offset_low_vec, offset_high_vec;
load_offsets16(offset, offset_deltas, &offset_low_vec, &offset_high_vec);
__m512i counter_low_vec, counter_high_vec;
load_counters16(counter, increment_counter, &counter_low_vec,
&counter_high_vec);
uint8_t block_flags = flags | flags_start;
for (size_t block = 0; block < blocks; block++) {
@ -1055,10 +1070,10 @@ void blake3_hash16_avx512(const uint8_t *const *inputs, size_t blocks,
transpose_msg_vecs16(inputs, block * BLAKE3_BLOCK_LEN, msg_vecs);
__m512i v[16] = {
h_vecs[0], h_vecs[1], h_vecs[2], h_vecs[3],
h_vecs[4], h_vecs[5], h_vecs[6], h_vecs[7],
set1_512(IV[0]), set1_512(IV[1]), set1_512(IV[2]), set1_512(IV[3]),
offset_low_vec, offset_high_vec, block_len_vec, block_flags_vec,
h_vecs[0], h_vecs[1], h_vecs[2], h_vecs[3],
h_vecs[4], h_vecs[5], h_vecs[6], h_vecs[7],
set1_512(IV[0]), set1_512(IV[1]), set1_512(IV[2]), set1_512(IV[3]),
counter_low_vec, counter_high_vec, block_len_vec, block_flags_vec,
};
round_fn16(v, msg_vecs, 0);
round_fn16(v, msg_vecs, 1);
@ -1114,7 +1129,7 @@ void blake3_hash16_avx512(const uint8_t *const *inputs, size_t blocks,
*/
INLINE void hash_one_avx512(const uint8_t *input, size_t blocks,
const uint32_t key[8], uint64_t offset,
const uint32_t key[8], uint64_t counter,
uint8_t flags, uint8_t flags_start,
uint8_t flags_end, uint8_t out[BLAKE3_OUT_LEN]) {
uint32_t cv[8];
@ -1124,7 +1139,7 @@ INLINE void hash_one_avx512(const uint8_t *input, size_t blocks,
if (blocks == 1) {
block_flags |= flags_end;
}
blake3_compress_in_place_avx512(cv, input, BLAKE3_BLOCK_LEN, offset,
blake3_compress_in_place_avx512(cv, input, BLAKE3_BLOCK_LEN, counter,
block_flags);
input = &input[BLAKE3_BLOCK_LEN];
blocks -= 1;
@ -1135,39 +1150,47 @@ INLINE void hash_one_avx512(const uint8_t *input, size_t blocks,
void blake3_hash_many_avx512(const uint8_t *const *inputs, size_t num_inputs,
size_t blocks, const uint32_t key[8],
uint64_t offset, offset_deltas_t offset_deltas,
uint64_t counter, bool increment_counter,
uint8_t flags, uint8_t flags_start,
uint8_t flags_end, uint8_t *out) {
while (num_inputs >= 16) {
blake3_hash16_avx512(inputs, blocks, key, offset, offset_deltas, flags,
blake3_hash16_avx512(inputs, blocks, key, counter, increment_counter, flags,
flags_start, flags_end, out);
if (increment_counter) {
counter += 16;
}
inputs += 16;
num_inputs -= 16;
offset += offset_deltas[16];
out = &out[16 * BLAKE3_OUT_LEN];
}
while (num_inputs >= 8) {
blake3_hash8_avx512(inputs, blocks, key, offset, offset_deltas, flags,
blake3_hash8_avx512(inputs, blocks, key, counter, increment_counter, flags,
flags_start, flags_end, out);
if (increment_counter) {
counter += 8;
}
inputs += 8;
num_inputs -= 8;
offset += offset_deltas[8];
out = &out[8 * BLAKE3_OUT_LEN];
}
while (num_inputs >= 4) {
blake3_hash4_avx512(inputs, blocks, key, offset, offset_deltas, flags,
blake3_hash4_avx512(inputs, blocks, key, counter, increment_counter, flags,
flags_start, flags_end, out);
if (increment_counter) {
counter += 4;
}
inputs += 4;
num_inputs -= 4;
offset += offset_deltas[4];
out = &out[4 * BLAKE3_OUT_LEN];
}
while (num_inputs > 0) {
hash_one_avx512(inputs[0], blocks, key, offset, flags, flags_start,
hash_one_avx512(inputs[0], blocks, key, counter, flags, flags_start,
flags_end, out);
if (increment_counter) {
counter += 1;
}
inputs += 1;
num_inputs -= 1;
offset += offset_deltas[1];
out = &out[BLAKE3_OUT_LEN];
}
}

View File

@ -42,19 +42,6 @@ static const uint8_t MSG_SCHEDULE[7][16] = {
{12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11},
};
// 17 is 1 + the largest supported SIMD degree. Each hash_many() implementation
// can thus do `offset += offset_deltas[DEGREE]` at the end of each batch.
typedef const uint64_t offset_deltas_t[17];
static offset_deltas_t CHUNK_OFFSET_DELTAS = {
BLAKE3_CHUNK_LEN * 0, BLAKE3_CHUNK_LEN * 1, BLAKE3_CHUNK_LEN * 2,
BLAKE3_CHUNK_LEN * 3, BLAKE3_CHUNK_LEN * 4, BLAKE3_CHUNK_LEN * 5,
BLAKE3_CHUNK_LEN * 6, BLAKE3_CHUNK_LEN * 7, BLAKE3_CHUNK_LEN * 8,
BLAKE3_CHUNK_LEN * 9, BLAKE3_CHUNK_LEN * 10, BLAKE3_CHUNK_LEN * 11,
BLAKE3_CHUNK_LEN * 12, BLAKE3_CHUNK_LEN * 13, BLAKE3_CHUNK_LEN * 14,
BLAKE3_CHUNK_LEN * 15, BLAKE3_CHUNK_LEN * 16,
};
// Count the number of 1 bits.
INLINE uint8_t popcnt(uint64_t x) {
#if __POPCNT__
@ -69,10 +56,10 @@ INLINE uint8_t popcnt(uint64_t x) {
#endif
}
INLINE uint32_t offset_low(uint64_t offset) { return (uint32_t)offset; }
INLINE uint32_t counter_low(uint64_t counter) { return (uint32_t)counter; }
INLINE uint32_t offset_high(uint64_t offset) {
return (uint32_t)(offset >> 32);
INLINE uint32_t counter_high(uint64_t counter) {
return (uint32_t)(counter >> 32);
}
INLINE uint32_t load32(const void *src) {
@ -96,50 +83,50 @@ INLINE void load_key_words(const uint8_t key[BLAKE3_KEY_LEN],
// Declarations for implementation-specific functions.
void blake3_compress_in_place_portable(uint32_t cv[8],
const uint8_t block[BLAKE3_BLOCK_LEN],
uint8_t block_len, uint64_t offset,
uint8_t block_len, uint64_t counter,
uint8_t flags);
void blake3_compress_in_place_sse41(uint32_t cv[8],
const uint8_t block[BLAKE3_BLOCK_LEN],
uint8_t block_len, uint64_t offset,
uint8_t block_len, uint64_t counter,
uint8_t flags);
void blake3_compress_in_place_avx512(uint32_t cv[8],
const uint8_t block[BLAKE3_BLOCK_LEN],
uint8_t block_len, uint64_t offset,
uint8_t block_len, uint64_t counter,
uint8_t flags);
void blake3_compress_xof_portable(const uint32_t cv[8],
const uint8_t block[BLAKE3_BLOCK_LEN],
uint8_t block_len, uint64_t offset,
uint8_t block_len, uint64_t counter,
uint8_t flags, uint8_t out[64]);
void blake3_compress_xof_sse41(const uint32_t cv[8],
const uint8_t block[BLAKE3_BLOCK_LEN],
uint8_t block_len, uint64_t offset,
uint8_t block_len, uint64_t counter,
uint8_t flags, uint8_t out[64]);
void blake3_compress_xof_avx512(const uint32_t cv[8],
const uint8_t block[BLAKE3_BLOCK_LEN],
uint8_t block_len, uint64_t offset,
uint8_t block_len, uint64_t counter,
uint8_t flags, uint8_t out[64]);
void blake3_hash_many_portable(const uint8_t *const *inputs, size_t num_inputs,
size_t blocks, const uint32_t key[8],
uint64_t offset, offset_deltas_t od,
uint64_t counter, bool increment_counter,
uint8_t flags, uint8_t flags_start,
uint8_t flags_end, uint8_t *out);
void blake3_hash_many_sse41(const uint8_t *const *inputs, size_t num_inputs,
size_t blocks, const uint32_t key[8],
uint64_t offset, offset_deltas_t od, uint8_t flags,
uint8_t flags_start, uint8_t flags_end,
uint8_t *out);
uint64_t counter, bool increment_counter,
uint8_t flags, uint8_t flags_start,
uint8_t flags_end, uint8_t *out);
void blake3_hash_many_avx2(const uint8_t *const *inputs, size_t num_inputs,
size_t blocks, const uint32_t key[8],
uint64_t offset, offset_deltas_t od, uint8_t flags,
uint8_t flags_start, uint8_t flags_end,
uint8_t *out);
uint64_t counter, bool increment_counter,
uint8_t flags, uint8_t flags_start,
uint8_t flags_end, uint8_t *out);
void blake3_hash_many_avx512(const uint8_t *const *inputs, size_t num_inputs,
size_t blocks, const uint32_t key[8],
uint64_t offset, offset_deltas_t od, uint8_t flags,
uint8_t flags_start, uint8_t flags_end,
uint8_t *out);
uint64_t counter, bool increment_counter,
uint8_t flags, uint8_t flags_start,
uint8_t flags_end, uint8_t *out);
void blake3_hash_many_neon(const uint8_t *const *inputs, size_t num_inputs,
size_t blocks, const uint32_t key[8],
uint64_t offset, offset_deltas_t od, uint8_t flags,
uint8_t flags_start, uint8_t flags_end,
uint8_t *out);
uint64_t counter, bool increment_counter,
uint8_t flags, uint8_t flags_start,
uint8_t flags_end, uint8_t *out);

View File

@ -212,26 +212,28 @@ INLINE void transpose_msg_vecs4(const uint8_t *const *inputs,
transpose_vecs_128(&out[12]);
}
INLINE void load_offsets4(uint64_t offset, const uint64_t deltas[4],
uint32x4_t *out_lo, uint32x4_t *out_hi) {
*out_lo =
set4(offset_low(offset + deltas[0]), offset_low(offset + deltas[1]),
offset_low(offset + deltas[2]), offset_low(offset + deltas[3]));
*out_hi =
set4(offset_high(offset + deltas[0]), offset_high(offset + deltas[1]),
offset_high(offset + deltas[2]), offset_high(offset + deltas[3]));
INLINE void load_counters4(uint64_t counter, bool increment_counter,
uint32x4_t *out_low, uint32x4_t *out_high) {
uint64_t mask = (increment_counter ? ~0 : 0);
*out_low = set4(
counter_low(counter + (mask & 0)), counter_low(counter + (mask & 1)),
counter_low(counter + (mask & 2)), counter_low(counter + (mask & 3)));
*out_high = set4(
counter_high(counter + (mask & 0)), counter_high(counter + (mask & 1)),
counter_high(counter + (mask & 2)), counter_high(counter + (mask & 3)));
}
void blake3_hash4_neon(const uint8_t *const *inputs, size_t blocks,
const uint32_t key[8], uint64_t offset,
offset_deltas_t offset_deltas, uint8_t flags,
const uint32_t key[8], uint64_t counter,
bool increment_counter, uint8_t flags,
uint8_t flags_start, uint8_t flags_end, uint8_t *out) {
uint32x4_t h_vecs[8] = {
set1_128(key[0]), set1_128(key[1]), set1_128(key[2]), set1_128(key[3]),
set1_128(key[4]), set1_128(key[5]), set1_128(key[6]), set1_128(key[7]),
};
uint32x4_t offset_low_vec, offset_high_vec;
load_offsets4(offset, offset_deltas, &offset_low_vec, &offset_high_vec);
uint32x4_t counter_low_vec, counter_high_vec;
load_counters4(counter, increment_counter, &counter_low_vec,
&counter_high_vec);
uint8_t block_flags = flags | flags_start;
for (size_t block = 0; block < blocks; block++) {
@ -244,10 +246,10 @@ void blake3_hash4_neon(const uint8_t *const *inputs, size_t blocks,
transpose_msg_vecs4(inputs, block * BLAKE3_BLOCK_LEN, msg_vecs);
uint32x4_t v[16] = {
h_vecs[0], h_vecs[1], h_vecs[2], h_vecs[3],
h_vecs[4], h_vecs[5], h_vecs[6], h_vecs[7],
set1_128(IV[0]), set1_128(IV[1]), set1_128(IV[2]), set1_128(IV[3]),
offset_low_vec, offset_high_vec, block_len_vec, block_flags_vec,
h_vecs[0], h_vecs[1], h_vecs[2], h_vecs[3],
h_vecs[4], h_vecs[5], h_vecs[6], h_vecs[7],
set1_128(IV[0]), set1_128(IV[1]), set1_128(IV[2]), set1_128(IV[3]),
counter_low_vec, counter_high_vec, block_len_vec, block_flags_vec,
};
round_fn4(v, msg_vecs, 0);
round_fn4(v, msg_vecs, 1);
@ -289,8 +291,8 @@ void blake3_hash4_neon(const uint8_t *const *inputs, size_t blocks,
*/
INLINE void hash_one_neon(const uint8_t *input, size_t blocks,
const uint32_t key[8], uint64_t offset, uint8_t flags,
uint8_t flags_start, uint8_t flags_end,
const uint32_t key[8], uint64_t counter,
uint8_t flags, uint8_t flags_start, uint8_t flags_end,
uint8_t out[BLAKE3_OUT_LEN]) {
uint32_t cv[8];
memcpy(cv, key, BLAKE3_KEY_LEN);
@ -302,7 +304,7 @@ INLINE void hash_one_neon(const uint8_t *input, size_t blocks,
// TODO: Implement compress_neon. However note that according to
// https://github.com/BLAKE2/BLAKE2/commit/7965d3e6e1b4193438b8d3a656787587d2579227,
// compress_neon might not be any faster than compress_portable.
blake3_compress_in_place_portable(cv, input, BLAKE3_BLOCK_LEN, offset,
blake3_compress_in_place_portable(cv, input, BLAKE3_BLOCK_LEN, counter,
block_flags);
input = &input[BLAKE3_BLOCK_LEN];
blocks -= 1;
@ -313,23 +315,27 @@ INLINE void hash_one_neon(const uint8_t *input, size_t blocks,
void blake3_hash_many_neon(const uint8_t *const *inputs, size_t num_inputs,
size_t blocks, const uint32_t key[8],
uint64_t offset, offset_deltas_t offset_deltas,
uint64_t counter, bool increment_counter,
uint8_t flags, uint8_t flags_start,
uint8_t flags_end, uint8_t *out) {
while (num_inputs >= 4) {
blake3_hash4_neon(inputs, blocks, key, offset, offset_deltas, flags,
blake3_hash4_neon(inputs, blocks, key, counter, increment_counter, flags,
flags_start, flags_end, out);
if (increment_counter) {
counter += 4;
}
inputs += 4;
num_inputs -= 4;
offset += offset_deltas[4];
out = &out[4 * BLAKE3_OUT_LEN];
}
while (num_inputs > 0) {
hash_one_neon(inputs[0], blocks, key, offset, flags, flags_start, flags_end,
out);
hash_one_neon(inputs[0], blocks, key, counter, flags, flags_start,
flags_end, out);
if (increment_counter) {
counter += 1;
}
inputs += 1;
num_inputs -= 1;
offset += offset_deltas[1];
out = &out[BLAKE3_OUT_LEN];
}
}

View File

@ -1,4 +1,4 @@
use crate::{CVWords, OffsetDeltas, BLOCK_LEN, OUT_LEN};
use crate::{CVWords, IncrementCounter, BLOCK_LEN, OUT_LEN};
pub const DEGREE: usize = 16;
@ -7,10 +7,10 @@ pub unsafe fn compress_in_place(
cv: &mut CVWords,
block: &[u8; BLOCK_LEN],
block_len: u8,
offset: u64,
counter: u64,
flags: u8,
) {
ffi::blake3_compress_in_place_avx512(cv.as_mut_ptr(), block.as_ptr(), block_len, offset, flags)
ffi::blake3_compress_in_place_avx512(cv.as_mut_ptr(), block.as_ptr(), block_len, counter, flags)
}
// Unsafe because this may only be called on platforms supporting AVX-512.
@ -18,7 +18,7 @@ pub unsafe fn compress_xof(
cv: &CVWords,
block: &[u8; BLOCK_LEN],
block_len: u8,
offset: u64,
counter: u64,
flags: u8,
) -> [u8; 64] {
let mut out = [0u8; 64];
@ -26,7 +26,7 @@ pub unsafe fn compress_xof(
cv.as_ptr(),
block.as_ptr(),
block_len,
offset,
counter,
flags,
out.as_mut_ptr(),
);
@ -37,8 +37,8 @@ pub unsafe fn compress_xof(
pub unsafe fn hash_many<A: arrayvec::Array<Item = u8>>(
inputs: &[&A],
key: &CVWords,
offset: u64,
offset_deltas: &OffsetDeltas,
counter: u64,
increment_counter: IncrementCounter,
flags: u8,
flags_start: u8,
flags_end: u8,
@ -53,8 +53,8 @@ pub unsafe fn hash_many<A: arrayvec::Array<Item = u8>>(
inputs.len(),
A::CAPACITY / BLOCK_LEN,
key.as_ptr(),
offset,
offset_deltas.as_ptr(),
counter,
increment_counter.yes(),
flags,
flags_start,
flags_end,
@ -68,14 +68,14 @@ pub mod ffi {
cv: *mut u32,
block: *const u8,
block_len: u8,
offset: u64,
counter: u64,
flags: u8,
);
pub fn blake3_compress_xof_avx512(
cv: *const u32,
block: *const u8,
block_len: u8,
offset: u64,
counter: u64,
flags: u8,
out: *mut u8,
);
@ -84,13 +84,14 @@ pub mod ffi {
num_inputs: usize,
blocks: usize,
key: *const u32,
offset: u64,
offset_deltas: *const u64,
counter: u64,
increment_counter: bool,
flags: u8,
flags_start: u8,
flags_end: u8,
out: *mut u8,
);
}
}

View File

@ -1,4 +1,4 @@
use crate::{CVWords, OffsetDeltas, BLOCK_LEN, OUT_LEN};
use crate::{CVWords, IncrementCounter, BLOCK_LEN, OUT_LEN};
pub const DEGREE: usize = 4;
@ -6,8 +6,8 @@ pub const DEGREE: usize = 4;
pub unsafe fn hash_many<A: arrayvec::Array<Item = u8>>(
inputs: &[&A],
key: &CVWords,
offset: u64,
offset_deltas: &OffsetDeltas,
counter: u64,
increment_counter: IncrementCounter,
flags: u8,
flags_start: u8,
flags_end: u8,
@ -22,8 +22,8 @@ pub unsafe fn hash_many<A: arrayvec::Array<Item = u8>>(
inputs.len(),
A::CAPACITY / BLOCK_LEN,
key.as_ptr(),
offset,
offset_deltas.as_ptr(),
counter,
increment_counter.yes(),
flags,
flags_start,
flags_end,
@ -40,7 +40,7 @@ pub extern "C" fn blake3_compress_in_place_portable(
cv: *mut u32,
block: *const u8,
block_len: u8,
offset: u64,
counter: u64,
flags: u8,
) {
unsafe {
@ -48,7 +48,7 @@ pub extern "C" fn blake3_compress_in_place_portable(
&mut *(cv as *mut [u32; 8]),
&*(block as *const [u8; 64]),
block_len,
offset,
counter,
flags,
)
}
@ -61,8 +61,8 @@ pub mod ffi {
num_inputs: usize,
blocks: usize,
key: *const u32,
offset: u64,
offset_deltas: *const u64,
counter: u64,
increment_counter: bool,
flags: u8,
flags_start: u8,
flags_end: u8,

View File

@ -63,33 +63,6 @@ const MSG_SCHEDULE: [[usize; 16]; 7] = [
[12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
];
// 17 is 1 + the largest supported SIMD degree (including AVX-512, currently in C).
// Each hash_many() implementation can thus do `offset += offset_deltas[DEGREE]`
// at the end of each batch.
type OffsetDeltas = [u64; 17];
const CHUNK_OFFSET_DELTAS: &OffsetDeltas = &[
CHUNK_LEN as u64 * 0,
CHUNK_LEN as u64 * 1,
CHUNK_LEN as u64 * 2,
CHUNK_LEN as u64 * 3,
CHUNK_LEN as u64 * 4,
CHUNK_LEN as u64 * 5,
CHUNK_LEN as u64 * 6,
CHUNK_LEN as u64 * 7,
CHUNK_LEN as u64 * 8,
CHUNK_LEN as u64 * 9,
CHUNK_LEN as u64 * 10,
CHUNK_LEN as u64 * 11,
CHUNK_LEN as u64 * 12,
CHUNK_LEN as u64 * 13,
CHUNK_LEN as u64 * 14,
CHUNK_LEN as u64 * 15,
CHUNK_LEN as u64 * 16,
];
const PARENT_OFFSET_DELTAS: &OffsetDeltas = &[0; 17];
// These are the internal flags that we use to domain separate root/non-root,
// chunk/parent, and chunk beginning/middle/end. These get set at the high end
// of the block flags word in the compression function, so their values start
@ -101,12 +74,12 @@ const ROOT: u8 = 1 << 3;
const KEYED_HASH: u8 = 1 << 4;
const DERIVE_KEY: u8 = 1 << 5;
fn offset_low(offset: u64) -> u32 {
offset as u32
fn counter_low(counter: u64) -> u32 {
counter as u32
}
fn offset_high(offset: u64) -> u32 {
(offset >> 32) as u32
fn counter_high(counter: u64) -> u32 {
(counter >> 32) as u32
}
/// A BLAKE3 output of the default size, 32 bytes, which implements
@ -181,7 +154,7 @@ struct Output {
input_chaining_value: CVWords,
block: [u8; 64],
block_len: u8,
offset: u64,
counter: u64,
flags: u8,
platform: Platform,
}
@ -193,14 +166,14 @@ impl Output {
&mut cv,
&self.block,
self.block_len,
self.offset,
self.counter,
self.flags,
);
platform::le_bytes_from_words_32(&cv)
}
fn root_hash(&self) -> Hash {
debug_assert_eq!(self.offset, 0);
debug_assert_eq!(self.counter, 0);
let mut cv = self.input_chaining_value;
self.platform
.compress_in_place(&mut cv, &self.block, self.block_len, 0, self.flags | ROOT);
@ -212,7 +185,7 @@ impl Output {
&self.input_chaining_value,
&self.block,
self.block_len,
self.offset,
self.counter,
self.flags | ROOT,
)
}
@ -221,7 +194,7 @@ impl Output {
#[derive(Clone)]
struct ChunkState {
cv: CVWords,
offset: u64,
chunk_counter: u64,
buf: [u8; BLOCK_LEN],
buf_len: u8,
blocks_compressed: u8,
@ -230,10 +203,10 @@ struct ChunkState {
}
impl ChunkState {
fn new(key: &CVWords, offset: u64, flags: u8, platform: Platform) -> Self {
fn new(key: &CVWords, chunk_counter: u64, flags: u8, platform: Platform) -> Self {
Self {
cv: *key,
offset,
chunk_counter,
buf: [0; BLOCK_LEN],
buf_len: 0,
blocks_compressed: 0,
@ -242,15 +215,6 @@ impl ChunkState {
}
}
fn reset(&mut self, key: &CVWords, new_offset: u64) {
debug_assert_eq!(new_offset % CHUNK_LEN as u64, 0);
self.cv = *key;
self.offset = new_offset;
self.buf = [0; BLOCK_LEN];
self.buf_len = 0;
self.blocks_compressed = 0;
}
fn len(&self) -> usize {
BLOCK_LEN * self.blocks_compressed as usize + self.buf_len as usize
}
@ -283,7 +247,7 @@ impl ChunkState {
&mut self.cv,
&self.buf,
BLOCK_LEN as u8,
self.offset,
self.chunk_counter,
block_flags,
);
self.buf_len = 0;
@ -299,7 +263,7 @@ impl ChunkState {
&mut self.cv,
array_ref!(input, 0, BLOCK_LEN),
BLOCK_LEN as u8,
self.offset,
self.chunk_counter,
block_flags,
);
self.blocks_compressed += 1;
@ -318,7 +282,7 @@ impl ChunkState {
input_chaining_value: self.cv,
block: self.buf,
block_len: self.buf_len,
offset: self.offset,
counter: self.chunk_counter,
flags: block_flags,
platform: self.platform,
}
@ -330,9 +294,9 @@ impl fmt::Debug for ChunkState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"ChunkState {{ len: {}, offset: {}, flags: {:?}, platform: {:?} }}",
"ChunkState {{ len: {}, chunk_counter: {}, flags: {:?}, platform: {:?} }}",
self.len(),
self.offset,
self.chunk_counter,
self.flags,
self.platform
)
@ -354,6 +318,23 @@ impl fmt::Debug for ChunkState {
// use full-width SIMD vectors for parent hashing. Without parallel parent
// hashing, we lose about 10% of overall throughput on AVX2 and AVX-512.
// pub for benchmarks
#[doc(hidden)]
#[derive(Clone, Copy)]
pub enum IncrementCounter {
Yes,
No,
}
impl IncrementCounter {
fn yes(&self) -> bool {
match self {
IncrementCounter::Yes => true,
IncrementCounter::No => false,
}
}
}
// The largest power of two less than or equal to `n`, used for left_len()
// immediately below, and also directly in Hasher::update().
fn largest_power_of_two_leq(n: usize) -> usize {
@ -395,14 +376,13 @@ where
fn compress_chunks_parallel(
input: &[u8],
key: &CVWords,
offset: u64,
chunk_counter: u64,
flags: u8,
platform: Platform,
out: &mut [u8],
) -> usize {
debug_assert!(!input.is_empty(), "empty chunks below the root");
debug_assert!(input.len() <= MAX_SIMD_DEGREE * CHUNK_LEN);
debug_assert_eq!(offset % CHUNK_LEN as u64, 0, "invalid offset");
let mut chunks_exact = input.chunks_exact(CHUNK_LEN);
let mut chunks_array = ArrayVec::<[&[u8; CHUNK_LEN]; MAX_SIMD_DEGREE]>::new();
@ -412,8 +392,8 @@ fn compress_chunks_parallel(
platform.hash_many(
&chunks_array,
key,
offset,
CHUNK_OFFSET_DELTAS,
chunk_counter,
IncrementCounter::Yes,
flags,
CHUNK_START,
CHUNK_END,
@ -424,8 +404,8 @@ fn compress_chunks_parallel(
// chunk (meaning the empty message) is a different codepath.
let chunks_so_far = chunks_array.len();
if !chunks_exact.remainder().is_empty() {
let chunk_offset = offset + (chunks_so_far * CHUNK_LEN) as u64;
let mut chunk_state = ChunkState::new(key, chunk_offset, flags, platform);
let counter = chunk_counter + chunks_so_far as u64;
let mut chunk_state = ChunkState::new(key, counter, flags, platform);
chunk_state.update(chunks_exact.remainder());
*array_mut_ref!(out, chunks_so_far * OUT_LEN, OUT_LEN) =
chunk_state.output().chaining_value();
@ -462,8 +442,8 @@ fn compress_parents_parallel(
platform.hash_many(
&parents_array,
key,
0, // Parents always use offset 0.
PARENT_OFFSET_DELTAS,
0, // Parents always use counter 0.
IncrementCounter::No,
flags | PARENT,
0, // Parents have no start flags.
0, // Parents have no end flags.
@ -496,7 +476,7 @@ fn compress_parents_parallel(
fn compress_subtree_wide(
input: &[u8],
key: &CVWords,
offset: u64,
chunk_counter: u64,
flags: u8,
platform: Platform,
out: &mut [u8],
@ -505,7 +485,7 @@ fn compress_subtree_wide(
// when it is 1. This allows Rayon the option of multi-threading even the
// 2-chunk case, which can help performance on smaller platforms.
if input.len() <= platform.simd_degree() * CHUNK_LEN {
return compress_chunks_parallel(input, key, offset, flags, platform, out);
return compress_chunks_parallel(input, key, chunk_counter, flags, platform, out);
}
// With more than simd_degree chunks, we need to recurse. Start by dividing
@ -514,7 +494,7 @@ fn compress_subtree_wide(
// of 3 or something, we'll need a more complicated strategy.)
debug_assert_eq!(platform.simd_degree().count_ones(), 1, "power of 2");
let (left, right) = input.split_at(left_len(input.len()));
let right_offset = offset + left.len() as u64;
let right_chunk_counter = chunk_counter + (left.len() / CHUNK_LEN) as u64;
// Make space for the child outputs. Here we use MAX_SIMD_DEGREE_OR_2 to
// account for the special case of returning 2 outputs when the SIMD degree
@ -531,8 +511,8 @@ fn compress_subtree_wide(
// Recurse! This uses multiple threads if the "rayon" feature is enabled.
let (left_n, right_n) = join(
|| compress_subtree_wide(left, key, offset, flags, platform, left_out),
|| compress_subtree_wide(right, key, right_offset, flags, platform, right_out),
|| compress_subtree_wide(left, key, chunk_counter, flags, platform, left_out),
|| compress_subtree_wide(right, key, right_chunk_counter, flags, platform, right_out),
);
// The special case again. If simd_degree=1, then we'll have left_n=1 and
@ -568,13 +548,14 @@ fn compress_subtree_wide(
fn compress_subtree_to_parent_node(
input: &[u8],
key: &CVWords,
offset: u64,
chunk_counter: u64,
flags: u8,
platform: Platform,
) -> [u8; BLOCK_LEN] {
debug_assert!(input.len() > CHUNK_LEN);
let mut cv_array = [0; 2 * MAX_SIMD_DEGREE_OR_2 * OUT_LEN];
let mut num_cvs = compress_subtree_wide(input, &key, offset, flags, platform, &mut cv_array);
let mut num_cvs =
compress_subtree_wide(input, &key, chunk_counter, flags, platform, &mut cv_array);
debug_assert!(num_cvs >= 2);
// If MAX_SIMD_DEGREE is greater than 2 and there's enough input,
@ -607,7 +588,7 @@ fn hash_all_at_once(input: &[u8], key: &CVWords, flags: u8) -> Output {
input_chaining_value: *key,
block: compress_subtree_to_parent_node(input, key, 0, flags, platform),
block_len: BLOCK_LEN as u8,
offset: 0,
counter: 0,
flags: flags | PARENT,
platform,
}
@ -646,7 +627,7 @@ fn parent_node_output(
input_chaining_value: *key,
block,
block_len: BLOCK_LEN as u8,
offset: 0,
counter: 0,
flags: flags | PARENT,
platform,
}
@ -694,11 +675,6 @@ impl Hasher {
Self::new_internal(&key_words, DERIVE_KEY)
}
/// The total number of input bytes so far.
pub fn count(&self) -> u64 {
self.chunk_state.offset + self.chunk_state.len() as u64
}
// See comment in push_cv.
fn merge_cv_stack(&mut self, total_len: u64) {
let post_merge_stack_len = total_len.count_ones() as usize;
@ -716,7 +692,7 @@ impl Hasher {
}
}
fn push_cv(&mut self, new_cv: &CVBytes, offset: u64) {
fn push_cv(&mut self, new_cv: &CVBytes, chunk_counter: u64) {
// In reference_impl.rs, we merge the new CV with existing CVs from the
// stack before pushing it. We can do that because we know more input
// is coming, so we know none of the merges are root.
@ -739,10 +715,10 @@ impl Hasher {
// merging with the new CV itself.
//
// We still use the "count the 1 bits" algorithm, adjusted slightly for
// this setting, using the offset (the start of the new CV's bytes)
// rather than the final total (the end of the new CV's bytes). That
// this setting, using the new chunk's counter numer (the previous
// total number of chunks) rather than new total number of chunks. That
// algorithm is explained in detail in the spec.
self.merge_cv_stack(offset);
self.merge_cv_stack(chunk_counter);
self.cv_stack.push(*new_cv);
}
@ -762,9 +738,13 @@ impl Hasher {
// Then we'll proceed to hashing whole chunks below.
debug_assert_eq!(self.chunk_state.len(), CHUNK_LEN);
let chunk_cv = self.chunk_state.output().chaining_value();
self.push_cv(&chunk_cv, self.chunk_state.offset);
let new_offset = self.chunk_state.offset + CHUNK_LEN as u64;
self.chunk_state.reset(&self.key, new_offset);
self.push_cv(&chunk_cv, self.chunk_state.chunk_counter);
self.chunk_state = ChunkState::new(
&self.key,
self.chunk_state.chunk_counter + 1,
self.chunk_state.flags,
self.chunk_state.platform,
);
} else {
return self;
}
@ -786,32 +766,33 @@ impl Hasher {
while input.len() > CHUNK_LEN {
debug_assert_eq!(self.chunk_state.len(), 0, "no partial chunk data");
debug_assert_eq!(CHUNK_LEN.count_ones(), 1, "power of 2 chunk len");
debug_assert_eq!(self.chunk_state.offset % CHUNK_LEN as u64, 0);
let mut subtree_len = largest_power_of_two_leq(input.len());
let count_so_far = self.chunk_state.chunk_counter * CHUNK_LEN as u64;
// Shrink the subtree_len until it evenly divides the count so far.
// We know it's a power of 2, so we can use a bitmask rather than
// the more expensive modulus operation. Note that if the caller
// consistently passes power-of-2 inputs of the same size (as is
// hopefully typical), we'll always skip over this loop.
while (subtree_len - 1) as u64 & self.chunk_state.offset != 0 {
while (subtree_len - 1) as u64 & count_so_far != 0 {
subtree_len /= 2;
}
// The shrunken subtree_len might now be 1 chunk long. If so, hash
// that one chunk by itself. Otherwise, compress the subtree into a
// pair of CVs.
let subtree_chunks = (subtree_len / CHUNK_LEN) as u64;
if subtree_len <= CHUNK_LEN {
debug_assert_eq!(subtree_len, CHUNK_LEN);
self.push_cv(
&ChunkState::new(
&self.key,
self.chunk_state.offset,
self.chunk_state.chunk_counter,
self.chunk_state.flags,
self.chunk_state.platform,
)
.update(&input[..subtree_len])
.output()
.chaining_value(),
self.chunk_state.offset,
self.chunk_state.chunk_counter,
);
} else {
// This is the high-performance happy path, though getting here
@ -819,7 +800,7 @@ impl Hasher {
let cv_pair = compress_subtree_to_parent_node(
&input[..subtree_len],
&self.key,
self.chunk_state.offset,
self.chunk_state.chunk_counter,
self.chunk_state.flags,
self.chunk_state.platform,
);
@ -828,10 +809,13 @@ impl Hasher {
// Push the two CVs we received into the CV stack in order. Because
// the stack merges lazily, this guarantees we aren't merging the
// root.
self.push_cv(left_cv, self.chunk_state.offset);
self.push_cv(right_cv, self.chunk_state.offset + (subtree_len as u64 / 2));
self.push_cv(left_cv, self.chunk_state.chunk_counter);
self.push_cv(
right_cv,
self.chunk_state.chunk_counter + (subtree_chunks / 2),
);
}
self.chunk_state.offset += subtree_len as u64;
self.chunk_state.chunk_counter += subtree_chunks;
input = &input[subtree_len..];
}
@ -842,7 +826,7 @@ impl Hasher {
// Having added some input to the chunk_state, we know what's in
// the CV stack won't become the root node, and we can do an extra
// merge. This simplifies finalize().
self.merge_cv_stack(self.chunk_state.offset);
self.merge_cv_stack(self.chunk_state.chunk_counter);
}
self
@ -853,7 +837,7 @@ impl Hasher {
// also. Convert it directly into an Output. Otherwise, we need to
// merge subtrees below.
if self.cv_stack.is_empty() {
debug_assert_eq!(self.chunk_state.offset, 0);
debug_assert_eq!(self.chunk_state.chunk_counter, 0);
return self.chunk_state.output();
}
@ -874,7 +858,7 @@ impl Hasher {
if self.chunk_state.len() > 0 {
debug_assert_eq!(
self.cv_stack.len(),
self.chunk_state.offset.count_ones() as usize,
self.chunk_state.chunk_counter.count_ones() as usize,
"cv stack does not need a merge"
);
output = self.chunk_state.output();
@ -930,10 +914,8 @@ impl fmt::Debug for Hasher {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Hasher {{ count: {}, flags: {:?}, platform: {:?} }}",
self.count(),
self.chunk_state.flags,
self.chunk_state.platform
"Hasher {{ flags: {:?}, platform: {:?} }}",
self.chunk_state.flags, self.chunk_state.platform
)
}
}
@ -982,19 +964,19 @@ impl OutputReader {
buf = &mut buf[take..];
self.position_within_block += take as u8;
if self.position_within_block == BLOCK_LEN as u8 {
self.inner.offset += BLOCK_LEN as u64;
self.inner.counter += 1;
self.position_within_block = 0;
}
}
}
pub fn position(&self) -> u64 {
self.inner.offset + self.position_within_block as u64
self.inner.counter * BLOCK_LEN as u64 + self.position_within_block as u64
}
pub fn set_position(&mut self, position: u64) {
self.position_within_block = (position % BLOCK_LEN as u64) as u8;
self.inner.offset = position - self.position_within_block as u64;
self.inner.counter = position / BLOCK_LEN as u64;
}
}

View File

@ -1,4 +1,4 @@
use crate::{portable, CVWords, OffsetDeltas, BLOCK_LEN};
use crate::{portable, CVWords, IncrementCounter, BLOCK_LEN};
use arrayref::{array_mut_ref, array_ref};
#[cfg(feature = "c_avx512")]
@ -108,25 +108,25 @@ impl Platform {
cv: &mut CVWords,
block: &[u8; BLOCK_LEN],
block_len: u8,
offset: u64,
counter: u64,
flags: u8,
) {
match self {
Platform::Portable => portable::compress_in_place(cv, block, block_len, offset, flags),
Platform::Portable => portable::compress_in_place(cv, block, block_len, counter, flags),
// Safe because detect() checked for platform support.
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
Platform::SSE41 | Platform::AVX2 => unsafe {
sse41::compress_in_place(cv, block, block_len, offset, flags)
sse41::compress_in_place(cv, block, block_len, counter, flags)
},
// Safe because detect() checked for platform support.
#[cfg(feature = "c_avx512")]
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
Platform::AVX512 => unsafe {
c_avx512::compress_in_place(cv, block, block_len, offset, flags)
c_avx512::compress_in_place(cv, block, block_len, counter, flags)
},
// No NEON compress_in_place() implementation yet.
#[cfg(feature = "c_neon")]
Platform::NEON => portable::compress_in_place(cv, block, block_len, offset, flags),
Platform::NEON => portable::compress_in_place(cv, block, block_len, counter, flags),
}
}
@ -135,25 +135,25 @@ impl Platform {
cv: &CVWords,
block: &[u8; BLOCK_LEN],
block_len: u8,
offset: u64,
counter: u64,
flags: u8,
) -> [u8; 64] {
match self {
Platform::Portable => portable::compress_xof(cv, block, block_len, offset, flags),
Platform::Portable => portable::compress_xof(cv, block, block_len, counter, flags),
// Safe because detect() checked for platform support.
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
Platform::SSE41 | Platform::AVX2 => unsafe {
sse41::compress_xof(cv, block, block_len, offset, flags)
sse41::compress_xof(cv, block, block_len, counter, flags)
},
// Safe because detect() checked for platform support.
#[cfg(feature = "c_avx512")]
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
Platform::AVX512 => unsafe {
c_avx512::compress_xof(cv, block, block_len, offset, flags)
c_avx512::compress_xof(cv, block, block_len, counter, flags)
},
// No NEON compress_xof() implementation yet.
#[cfg(feature = "c_neon")]
Platform::NEON => portable::compress_xof(cv, block, block_len, offset, flags),
Platform::NEON => portable::compress_xof(cv, block, block_len, counter, flags),
}
}
@ -171,8 +171,8 @@ impl Platform {
&self,
inputs: &[&A],
key: &CVWords,
offset: u64,
offset_deltas: &OffsetDeltas,
counter: u64,
increment_counter: IncrementCounter,
flags: u8,
flags_start: u8,
flags_end: u8,
@ -182,8 +182,8 @@ impl Platform {
Platform::Portable => portable::hash_many(
inputs,
key,
offset,
offset_deltas,
counter,
increment_counter,
flags,
flags_start,
flags_end,
@ -195,8 +195,8 @@ impl Platform {
sse41::hash_many(
inputs,
key,
offset,
offset_deltas,
counter,
increment_counter,
flags,
flags_start,
flags_end,
@ -209,8 +209,8 @@ impl Platform {
avx2::hash_many(
inputs,
key,
offset,
offset_deltas,
counter,
increment_counter,
flags,
flags_start,
flags_end,
@ -224,8 +224,8 @@ impl Platform {
c_avx512::hash_many(
inputs,
key,
offset,
offset_deltas,
counter,
increment_counter,
flags,
flags_start,
flags_end,
@ -238,8 +238,8 @@ impl Platform {
c_neon::hash_many(
inputs,
key,
offset,
offset_deltas,
counter,
increment_counter,
flags,
flags_start,
flags_end,

View File

@ -1,5 +1,6 @@
use crate::{
offset_high, offset_low, CVBytes, CVWords, OffsetDeltas, BLOCK_LEN, IV, MSG_SCHEDULE, OUT_LEN,
counter_high, counter_low, CVBytes, CVWords, IncrementCounter, BLOCK_LEN, IV, MSG_SCHEDULE,
OUT_LEN,
};
use arrayref::{array_mut_ref, array_ref};
@ -38,7 +39,7 @@ fn compress_pre(
cv: &CVWords,
block: &[u8; BLOCK_LEN],
block_len: u8,
offset: u64,
counter: u64,
flags: u8,
) -> [u32; 16] {
let block_words = crate::platform::words_from_le_bytes_64(block);
@ -56,8 +57,8 @@ fn compress_pre(
IV[1],
IV[2],
IV[3],
offset_low(offset),
offset_high(offset),
counter_low(counter),
counter_high(counter),
block_len as u32,
flags as u32,
];
@ -77,10 +78,10 @@ pub fn compress_in_place(
cv: &mut CVWords,
block: &[u8; BLOCK_LEN],
block_len: u8,
offset: u64,
counter: u64,
flags: u8,
) {
let state = compress_pre(cv, block, block_len, offset, flags);
let state = compress_pre(cv, block, block_len, counter, flags);
cv[0] = state[0] ^ state[8];
cv[1] = state[1] ^ state[9];
@ -96,10 +97,10 @@ pub fn compress_xof(
cv: &CVWords,
block: &[u8; BLOCK_LEN],
block_len: u8,
offset: u64,
counter: u64,
flags: u8,
) -> [u8; 64] {
let mut state = compress_pre(cv, block, block_len, offset, flags);
let mut state = compress_pre(cv, block, block_len, counter, flags);
state[0] ^= state[8];
state[1] ^= state[9];
state[2] ^= state[10];
@ -122,7 +123,7 @@ pub fn compress_xof(
pub fn hash1<A: arrayvec::Array<Item = u8>>(
input: &A,
key: &CVWords,
offset: u64,
counter: u64,
flags: u8,
flags_start: u8,
flags_end: u8,
@ -140,7 +141,7 @@ pub fn hash1<A: arrayvec::Array<Item = u8>>(
&mut cv,
array_ref!(slice, 0, BLOCK_LEN),
BLOCK_LEN as u8,
offset,
counter,
block_flags,
);
block_flags = flags;
@ -152,8 +153,8 @@ pub fn hash1<A: arrayvec::Array<Item = u8>>(
pub fn hash_many<A: arrayvec::Array<Item = u8>>(
inputs: &[&A],
key: &CVWords,
mut offset: u64,
offset_deltas: &OffsetDeltas,
mut counter: u64,
increment_counter: IncrementCounter,
flags: u8,
flags_start: u8,
flags_end: u8,
@ -164,13 +165,15 @@ pub fn hash_many<A: arrayvec::Array<Item = u8>>(
hash1(
input,
key,
offset,
counter,
flags,
flags_start,
flags_end,
array_mut_ref!(output, 0, OUT_LEN),
);
offset += offset_deltas[1];
if increment_counter.yes() {
counter += 1;
}
}
}

View File

@ -4,7 +4,8 @@ use core::arch::x86::*;
use core::arch::x86_64::*;
use crate::{
offset_high, offset_low, CVBytes, CVWords, OffsetDeltas, BLOCK_LEN, IV, MSG_SCHEDULE, OUT_LEN,
counter_high, counter_low, CVBytes, CVWords, IncrementCounter, BLOCK_LEN, IV, MSG_SCHEDULE,
OUT_LEN,
};
use arrayref::{array_mut_ref, array_ref, mut_array_refs};
@ -129,15 +130,15 @@ unsafe fn compress_pre(
cv: &CVWords,
block: &[u8; BLOCK_LEN],
block_len: u8,
offset: u64,
counter: u64,
flags: u8,
) -> [__m128i; 4] {
let row1 = &mut loadu(cv.as_ptr().add(0) as *const u8);
let row2 = &mut loadu(cv.as_ptr().add(4) as *const u8);
let row3 = &mut set4(IV[0], IV[1], IV[2], IV[3]);
let row4 = &mut set4(
offset_low(offset),
offset_high(offset),
counter_low(counter),
counter_high(counter),
block_len as u32,
flags as u32,
);
@ -313,10 +314,10 @@ pub unsafe fn compress_in_place(
cv: &mut CVWords,
block: &[u8; BLOCK_LEN],
block_len: u8,
offset: u64,
counter: u64,
flags: u8,
) {
let [row1, row2, row3, row4] = compress_pre(cv, block, block_len, offset, flags);
let [row1, row2, row3, row4] = compress_pre(cv, block, block_len, counter, flags);
storeu(xor(row1, row3), cv.as_mut_ptr().add(0) as *mut u8);
storeu(xor(row2, row4), cv.as_mut_ptr().add(4) as *mut u8);
}
@ -326,11 +327,11 @@ pub unsafe fn compress_xof(
cv: &CVWords,
block: &[u8; BLOCK_LEN],
block_len: u8,
offset: u64,
counter: u64,
flags: u8,
) -> [u8; 64] {
let [mut row1, mut row2, mut row3, mut row4] =
compress_pre(cv, block, block_len, offset, flags);
compress_pre(cv, block, block_len, counter, flags);
row1 = xor(row1, row3);
row2 = xor(row2, row4);
row3 = xor(row3, loadu(cv.as_ptr().add(0) as *const u8));
@ -506,19 +507,20 @@ unsafe fn transpose_msg_vecs(inputs: &[*const u8; DEGREE], block_offset: usize)
}
#[inline(always)]
unsafe fn load_offsets(offset: u64, offset_deltas: &OffsetDeltas) -> (__m128i, __m128i) {
unsafe fn load_counters(counter: u64, increment_counter: IncrementCounter) -> (__m128i, __m128i) {
let mask = if increment_counter.yes() { !0 } else { 0 };
(
set4(
offset_low(offset + offset_deltas[0]),
offset_low(offset + offset_deltas[1]),
offset_low(offset + offset_deltas[2]),
offset_low(offset + offset_deltas[3]),
counter_low(counter + (mask & 0)),
counter_low(counter + (mask & 1)),
counter_low(counter + (mask & 2)),
counter_low(counter + (mask & 3)),
),
set4(
offset_high(offset + offset_deltas[0]),
offset_high(offset + offset_deltas[1]),
offset_high(offset + offset_deltas[2]),
offset_high(offset + offset_deltas[3]),
counter_high(counter + (mask & 0)),
counter_high(counter + (mask & 1)),
counter_high(counter + (mask & 2)),
counter_high(counter + (mask & 3)),
),
)
}
@ -528,8 +530,8 @@ pub unsafe fn hash4(
inputs: &[*const u8; DEGREE],
blocks: usize,
key: &CVWords,
offset: u64,
offset_deltas: &OffsetDeltas,
counter: u64,
increment_counter: IncrementCounter,
flags: u8,
flags_start: u8,
flags_end: u8,
@ -545,7 +547,7 @@ pub unsafe fn hash4(
set1(key[6]),
set1(key[7]),
];
let (offset_low_vec, offset_high_vec) = load_offsets(offset, offset_deltas);
let (counter_low_vec, counter_high_vec) = load_counters(counter, increment_counter);
let mut block_flags = flags | flags_start;
for block in 0..blocks {
@ -573,8 +575,8 @@ pub unsafe fn hash4(
set1(IV[1]),
set1(IV[2]),
set1(IV[3]),
offset_low_vec,
offset_high_vec,
counter_low_vec,
counter_high_vec,
block_len_vec,
block_flags_vec,
];
@ -616,7 +618,7 @@ pub unsafe fn hash4(
unsafe fn hash1<A: arrayvec::Array<Item = u8>>(
input: &A,
key: &CVWords,
offset: u64,
counter: u64,
flags: u8,
flags_start: u8,
flags_end: u8,
@ -634,7 +636,7 @@ unsafe fn hash1<A: arrayvec::Array<Item = u8>>(
&mut cv,
array_ref!(slice, 0, BLOCK_LEN),
BLOCK_LEN as u8,
offset,
counter,
block_flags,
);
block_flags = flags;
@ -647,8 +649,8 @@ unsafe fn hash1<A: arrayvec::Array<Item = u8>>(
pub unsafe fn hash_many<A: arrayvec::Array<Item = u8>>(
mut inputs: &[&A],
key: &CVWords,
mut offset: u64,
offset_deltas: &OffsetDeltas,
mut counter: u64,
increment_counter: IncrementCounter,
flags: u8,
flags_start: u8,
flags_end: u8,
@ -664,28 +666,32 @@ pub unsafe fn hash_many<A: arrayvec::Array<Item = u8>>(
input_ptrs,
blocks,
key,
offset,
offset_deltas,
counter,
increment_counter,
flags,
flags_start,
flags_end,
array_mut_ref!(out, 0, DEGREE * OUT_LEN),
);
if increment_counter.yes() {
counter += DEGREE as u64;
}
inputs = &inputs[DEGREE..];
offset += DEGREE as u64 * offset_deltas[1];
out = &mut out[DEGREE * OUT_LEN..];
}
for (&input, output) in inputs.iter().zip(out.chunks_exact_mut(OUT_LEN)) {
hash1(
input,
key,
offset,
counter,
flags,
flags_start,
flags_end,
array_mut_ref!(output, 0, OUT_LEN),
);
offset += offset_deltas[1];
if increment_counter.yes() {
counter += 1;
}
}
}

View File

@ -1,4 +1,4 @@
use crate::{CVBytes, CVWords, OffsetDeltas, BLOCK_LEN, CHUNK_LEN, OUT_LEN};
use crate::{CVBytes, CVWords, IncrementCounter, BLOCK_LEN, CHUNK_LEN, OUT_LEN};
use arrayref::array_ref;
use arrayvec::ArrayVec;
use core::usize;
@ -48,13 +48,13 @@ pub fn paint_test_input(buf: &mut [u8]) {
}
type CompressInPlaceFn =
unsafe fn(cv: &mut CVWords, block: &[u8; BLOCK_LEN], block_len: u8, offset: u64, flags: u8);
unsafe fn(cv: &mut CVWords, block: &[u8; BLOCK_LEN], block_len: u8, counter: u64, flags: u8);
type CompressXofFn = unsafe fn(
cv: &CVWords,
block: &[u8; BLOCK_LEN],
block_len: u8,
offset: u64,
counter: u64,
flags: u8,
) -> [u8; 64];
@ -64,18 +64,18 @@ pub fn test_compress_fn(compress_in_place_fn: CompressInPlaceFn, compress_xof_fn
let block_len: u8 = 61;
let mut block = [0; BLOCK_LEN];
paint_test_input(&mut block[..block_len as usize]);
// Use an offset with set bits in both 32-bit words.
let offset = ((5 * CHUNK_LEN as u64) << 32) + 6 * CHUNK_LEN as u64;
// Use a counter with set bits in both 32-bit words.
let counter = (5u64 << 32) + 6;
let flags = crate::CHUNK_END | crate::ROOT | crate::KEYED_HASH;
let portable_out =
crate::portable::compress_xof(&initial_state, &block, block_len, offset as u64, flags);
crate::portable::compress_xof(&initial_state, &block, block_len, counter as u64, flags);
let mut test_state = initial_state;
unsafe { compress_in_place_fn(&mut test_state, &block, block_len, offset as u64, flags) };
unsafe { compress_in_place_fn(&mut test_state, &block, block_len, counter as u64, flags) };
let test_state_bytes = crate::platform::le_bytes_from_words_32(&test_state);
let test_xof =
unsafe { compress_xof_fn(&initial_state, &block, block_len, offset as u64, flags) };
unsafe { compress_xof_fn(&initial_state, &block, block_len, counter as u64, flags) };
assert_eq!(&portable_out[..32], &test_state_bytes[..]);
assert_eq!(&portable_out[..], &test_xof[..]);
@ -84,8 +84,8 @@ pub fn test_compress_fn(compress_in_place_fn: CompressInPlaceFn, compress_xof_fn
type HashManyFn<A> = unsafe fn(
inputs: &[&A],
key: &CVWords,
offset: u64,
offset_deltas: &OffsetDeltas,
counter: u64,
increment_counter: IncrementCounter,
flags: u8,
flags_start: u8,
flags_end: u8,
@ -101,8 +101,8 @@ pub fn test_hash_many_fn(
const NUM_INPUTS: usize = 31;
let mut input_buf = [0; CHUNK_LEN * NUM_INPUTS];
crate::test::paint_test_input(&mut input_buf);
// An offset just prior to u32::MAX.
let offset = (1 << 32) - CHUNK_LEN as u64;
// A counter just prior to u32::MAX.
let counter = (1u64 << 32) - 1;
// First hash chunks.
let mut chunks = ArrayVec::<[&[u8; CHUNK_LEN]; NUM_INPUTS]>::new();
@ -113,8 +113,8 @@ pub fn test_hash_many_fn(
crate::portable::hash_many(
&chunks,
&TEST_KEY_WORDS,
offset,
crate::CHUNK_OFFSET_DELTAS,
counter,
IncrementCounter::Yes,
crate::DERIVE_KEY,
crate::CHUNK_START,
crate::CHUNK_END,
@ -126,8 +126,8 @@ pub fn test_hash_many_fn(
hash_many_chunks_fn(
&chunks[..],
&TEST_KEY_WORDS,
offset,
crate::CHUNK_OFFSET_DELTAS,
counter,
IncrementCounter::Yes,
crate::DERIVE_KEY,
crate::CHUNK_START,
crate::CHUNK_END,
@ -153,7 +153,7 @@ pub fn test_hash_many_fn(
&parents,
&TEST_KEY_WORDS,
0,
crate::PARENT_OFFSET_DELTAS,
IncrementCounter::No,
crate::DERIVE_KEY | crate::PARENT,
0,
0,
@ -166,7 +166,7 @@ pub fn test_hash_many_fn(
&parents[..],
&TEST_KEY_WORDS,
0,
crate::PARENT_OFFSET_DELTAS,
IncrementCounter::No,
crate::DERIVE_KEY | crate::PARENT,
0,
0,
@ -202,10 +202,10 @@ fn test_reference_impl_size() {
}
#[test]
fn test_offset_words() {
let offset: u64 = (1 << 32) + 2;
assert_eq!(crate::offset_low(offset), 2);
assert_eq!(crate::offset_high(offset), 1);
fn test_counter_words() {
let counter: u64 = (1 << 32) + 2;
assert_eq!(crate::counter_low(counter), 2);
assert_eq!(crate::counter_high(counter), 1);
}
#[test]

View File

@ -4,129 +4,129 @@
"cases": [
{
"input_len": 0,
"hash": "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262e00f03e7b69af26b7faaf09fcd333050338ddfe085b8cc869ca98b206c08243ab607bd3c1a5b38f203d2049aa8800e5843af3c4c1fae0d536bf729bf45062cab89d75a67caea7c33a6eb981b911536724ef3f69833745b678a9c590ef2c47a1b5d18e6",
"keyed_hash": "92b2b75604ed3c761f9d6f62392c8a9227ad0ea3f09573e783f1498a4ed60d26b18171a2f22a4b94822c701f107153dba24918c4bae4d2945c20ece13387627da236e1daf0dd8c57c064c3bc7facab60a00dcf8c0836a73ec31e76d8f25bb502e1d146b226cca759f10e2e5a2b67781566e16758cf1908eebf62025ea41f66410a4f39",
"derive_key": "0f42e871bf9115e065cb0f67325b0ffad401cdf2f40b71148b98aae90a932e3f00ee95d66f5a73943a666f9a367b22ef258edd60fa146867e1ee2a449c9de4d7853c6a281bcfdcbeac1448bbda9b3a9b0c8f874bb428290e9808e6ab8c041ddbebdf4980c311e9a60ddea8a5e9656d44a6d6abfd3594a3e333a2822ab0dfc584133b80"
"hash": "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262e00f03e7b69af26b7faaf09fcd333050338ddfe085b8cc869ca98b206c08243a26f5487789e8f660afe6c99ef9e0c52b92e7393024a80459cf91f476f9ffdbda7001c22e159b402631f277ca96f2defdf1078282314e763699a31c5363165421cce14d",
"keyed_hash": "92b2b75604ed3c761f9d6f62392c8a9227ad0ea3f09573e783f1498a4ed60d26b18171a2f22a4b94822c701f107153dba24918c4bae4d2945c20ece13387627d3b73cbf97b797d5e59948c7ef788f54372df45e45e4293c7dc18c1d41144a9758be58960856be1eabbe22c2653190de560ca3b2ac4aa692a9210694254c371e851bc8f",
"derive_key": "0f42e871bf9115e065cb0f67325b0ffad401cdf2f40b71148b98aae90a932e3f00ee95d66f5a73943a666f9a367b22ef258edd60fa146867e1ee2a449c9de4d7edae320241aaaed9b763df20298bfd6ffba6887917b5c0d0a155c4065160dba02f28edebb1dff1a7a36739204682c6df43ce0f3dd33c9d93dcc032ac8f48442992645b"
},
{
"input_len": 1,
"hash": "2d3adedff11b61f14c886e35afa036736dcd87a74d27b5c1510225d0f592e213c3a6cb8bf623e20cdb535f8d1a5ffb86342d9c0b64aca3bce1d31f60adfa137b17996ff9e364afe56d5032bb481afddabdfa039c28b3ab9e0ee609f7fb0765a14201ab99cd173d0e4d0084c708860926c6a0e0bd18ea40607e9b36aa8ee2f1fc4e81fc",
"keyed_hash": "6d7878dfff2f485635d39013278ae14f1454b8c0a3a2d34bc1ab38228a80c95b6568c0490609413006fbd428eb3fd14e7756d90f73a4725fad147f7bf70fd61c5e4f5c1e904cd458701835b471654366e98f0b3538f2f5c8892fefb8023551112c5ec72c639257c02d1bd6acbacd3be6c96ff3b8822d43b7c5e8d70f22043845124fa2",
"derive_key": "b6b2fdb9a45d8251f811db7af9478d4d886db22e6ea282b9c27a45f20107902a4ce1e4c58a35bf64eccd3f3d8421b101538a9ea2304ca8f4586e07c9704d683c55bb09d6f35eca99d449cbe084feefc39e2fabfef841f9d257b9e805ea3b5327c65e1549ab228162ba39d1b618e9b6761bbd8c4ab5a77d66f6c3bedbaa791236407e8b"
"hash": "2d3adedff11b61f14c886e35afa036736dcd87a74d27b5c1510225d0f592e213c3a6cb8bf623e20cdb535f8d1a5ffb86342d9c0b64aca3bce1d31f60adfa137b358ad4d79f97b47c3d5e79f179df87a3b9776ef8325f8329886ba42f07fb138bb502f4081cbcec3195c5871e6c23e2cc97d3c69a613eba131e5f1351f3f1da786545e5",
"keyed_hash": "6d7878dfff2f485635d39013278ae14f1454b8c0a3a2d34bc1ab38228a80c95b6568c0490609413006fbd428eb3fd14e7756d90f73a4725fad147f7bf70fd61c4e0cf7074885e92b0e3f125978b4154986d4fb202a3f331a3fb6cf349a3a70e49990f98fe4289761c8602c4e6ab1138d31d3b62218078b2f3ba9a88e1d08d0dd4cea11",
"derive_key": "b6b2fdb9a45d8251f811db7af9478d4d886db22e6ea282b9c27a45f20107902a4ce1e4c58a35bf64eccd3f3d8421b101538a9ea2304ca8f4586e07c9704d683c8212a3281e68d01e8a2531412f3e42e29ff041d966d3c6426db9d87a983e0b1c2565b570b7413b8d09ebffd6bcf974d9a326ba55d3524659dcf782ebec11bfedba6486"
},
{
"input_len": 1023,
"hash": "9c93cdcb05286dc72e0c998c3055b0fa44cb2fa05a284bc597a4e216dfecf23b89e4ec3126046332385bc0f30bf83a80b37292c8376f6d2060a3ae721b96b22ce11c93e711bd3fd291de8caeacd941fdfac8f937bf06a11a097fa69d46b677bd412734e9ac0837ae2bb8038545d8b1abf9cd27e27f4515d3a8f5102f0a30ca58ff081b",
"keyed_hash": "682ff353dcb9322468d991f26d9268d3af0e82acf9b13b278e3f74393742e9ecd996c6e6d6f5a5e63b16ca6aec851ba9c9690115443109f8a6b131c518d657386dec12129906d4ad37938b67a5d8d61335442dafcd205a401bf4dda96902866a245e3b64157f89644c36734da60c346976716ca8d9eeb9bedaefcbd6281a08ec617935",
"derive_key": "2256b460666a52b426cae83c406eb91e5569e8821d488e0e1c8ae639a6e95f334620d3f7da0371baaf1d427cdd445c331ba85f3f5cd818de819c63f7b9b762ba9cf5cdbb69c4005f0bfdf43552da14a42110c126e61d2426f087718f4be43176f3acb1de07fed8512d5ccd70475a57672a1e7915b83026babef9d2cd058dfd7720b6e2"
"hash": "9c93cdcb05286dc72e0c998c3055b0fa44cb2fa05a284bc597a4e216dfecf23b89e4ec3126046332385bc0f30bf83a80b37292c8376f6d2060a3ae721b96b22c71d4b3a1069e5e4aff53ba5c5c45bd8d007062bcc75084ec6befcb6e725c6d198652e6d97eaaa2b7c4c577ebf49a07c9b3b8d4d08a0f4038b7c4545936e95559916c5d",
"keyed_hash": "682ff353dcb9322468d991f26d9268d3af0e82acf9b13b278e3f74393742e9ecd996c6e6d6f5a5e63b16ca6aec851ba9c9690115443109f8a6b131c518d657386a74d209756031884e716da99decd2c0d0defd35af60c3895f69cdee6e7d6ad1b26d8c7ec4ff1f3400834c52ade9678ca3d8317e3315daf79d85a64365211daf777e95",
"derive_key": "2256b460666a52b426cae83c406eb91e5569e8821d488e0e1c8ae639a6e95f334620d3f7da0371baaf1d427cdd445c331ba85f3f5cd818de819c63f7b9b762baca6695ff69ea817faa05bc1138b10cad75424a107e90c3589a02c36b24c5c0f5b42f6ee84f112dc053aa7e5dff7bd07c89dbd0b80f3affca80828aa3f2983f5dad65b8"
},
{
"input_len": 1024,
"hash": "b4342a9e49dceeb14dee0dcaefe92ac2fdd0cc37d880df8dce450bdb2daed817b00342c7db27ffbcfcf46eaecab2a7460d60c6812437ce101286c51df816934ba7880349fdd3e4139ae82be444e4dfb69167905042af54ba6db02826c8e13d3a44dec1cb8b31478bc9972dbca22b5f80a34381c1fe4eec0a0dacf30e1574fca902de15",
"keyed_hash": "71c777f92b6609192ccacba3f3672a44659b472b08a9972dc3e1d01ab83c54a71f050eae60ca5a295e393eba6e4f0358b5a053c131f39a9ef496411ecdeab76ee5076cae3125e419773e3311329a6623e5f36aa52413dfd5c0d12f40e3ef133fe7557f703124dd4608855ed9726ec0e8e7f0856312284c1926327b9898739fbc14f4f1",
"derive_key": "763c5d853f488fada8ea4a3931f82584a9b90d90947d4cd6d288389b808d7d1f91e27a68e35502df3f4e56d4c4e018dfbc14225be81bdd4277804ade7e00fa34259483ae69803f018ffc4b3ec56e72f4e37f4f6b18aa9851e6132512e19ae9f4e5e73f835fa5747400e7dc502782b14272686421adc61a8d13914dd21936ae3a811c63"
"hash": "b4342a9e49dceeb14dee0dcaefe92ac2fdd0cc37d880df8dce450bdb2daed817b00342c7db27ffbcfcf46eaecab2a7460d60c6812437ce101286c51df816934b3fbe2d8ee6a6db61556552fdd0992b906fd3f91f5eefe39abbba06d6dc9f3a02c8e4f8805443344016de267647a2806890f17b43e80a11d9d8e41e505e7dc10da250af",
"keyed_hash": "71c777f92b6609192ccacba3f3672a44659b472b08a9972dc3e1d01ab83c54a71f050eae60ca5a295e393eba6e4f0358b5a053c131f39a9ef496411ecdeab76e43b5003c3ac968c83b0a5e791cab5fb390c5fc83164c1b450c2ef250a57361f06b41d1501111fc5b01eb8fb9c5efc2371547f0dca60dd80e0f00b4f8687f0150c18cf0",
"derive_key": "763c5d853f488fada8ea4a3931f82584a9b90d90947d4cd6d288389b808d7d1f91e27a68e35502df3f4e56d4c4e018dfbc14225be81bdd4277804ade7e00fa3414091cb7a8f3c86b0016b493ca1ee7594c31c4be1cea149e800e2e8261495ba6b49612af5e5d9d444dba37d118dbf138e78621ca5d1d9c16015cb0b92b54d50766cdc7"
},
{
"input_len": 1025,
"hash": "be7c2c51e8b1c9554c1a9b87929d800a2bfd9608f466d421bb63d28d8dde426991f319fdf656e81182f97960fcbc4a713d2d1650b9467e61a1722797ffd4423904e7e8cbaa8f5141c270c82a360017a92da13640a1a2200643da05d772fd9a6b18c5f49dd23613a6d5bd8dcf0ae3bf7402e639231a1dbb67259054e2d719f0ae90740f",
"keyed_hash": "977b97ce7c83b043283d27f24cfeb25ece562ea7e97efdc2c1a24b4e5de6c62fcb8be06d9ff457a16b1a6607ef8177a0985bd0026fa5e58db371b7077701be7f92a2bd66a8737cb98b4242696ccb9a87410c1e65c63e22138ecda8701a44c983bfddf2291c2713502d5e6745dd13325ac2ec77f803a394016ee34e75a4515359d35712",
"derive_key": "58f725284304a8f8226b8d649d919d94acaf2d204d2183bad516d1dcf704c663ae871520f6e65548be9d5c304767cd5b2b854e59e727335404ccd020f04f8770cde80f40ace7b5a74012c915709ae27b3e0b039d0518b0f0b7a2a4854270c7385e0dd25d399eaab4dbfd21d658bf61c8a2febce553f46aa0e81343f88958191265bbbd"
"hash": "016ec9e10c1dbae0ba41f75462379b0f94719c544d024eb7bb5ce76d4bb522259e0df61c24ac2ca8092a1340b8c9ca0975d4d35205403ecacb62285b4b00f139e99dadf514d64b4165d0f3fc1c290763371d2d8cdf10123cde3a3f0b341094aa15b893f16a42ddb0eb4d4b32821aee0b52b9e70b721714f917cd47334d995aa1624bea",
"keyed_hash": "a3c4908e3580319031ac309266f4fdfb37f157116b60df6b0006ab055cf2a0c6c16c51d4169d72d170610d3414bdbad9e94c18c79a47c6b4dfca10510c3216b6b554c3c6390fed862d20155db21863d21ac03965c698a4c5f91cc75ea289a912c65e57373fde28d84d60f778c7f834230bc3324be2d4f1a527f3793aff84fd764114c5",
"derive_key": "42f3adf310f965449ec13d7f1505c9dd4c7fd48cccd04eccb983785f0c9ecd7535cfaeacfaace1afaafb5b9037e44efac33b3f1203230590f28f7949b87638f7315d8082aa1115ec093e09941d7d0d7d94a03021f0bbcbf48f5f3bbb8151d92dcc77a7d0d0fc10652caa55bc54e7d83c372e9fc5b2c6b8b4decf88b492908834f743db"
},
{
"input_len": 2048,
"hash": "90cb8696162c911ed9651719e7e29c90a8d770feb975e2249138071faa3c6403fd48c59958cbe101a36211c8ff5779c7ae205b777fae3defef5265b62d46fab32aba1d5487bf480030af1e008a0f58cd3426de4c54e646d073fb770a0b82430260ada42b3bda632ee6dfdbaff54631a9ad73ed1dd95981851b46d721a87f2c1e175e81",
"keyed_hash": "b05a0132353cc132450299b13c12af6162910af2331fdc5d45560085ac7890893f4069e98236a4115b7c21fa0d60390b98e1ac2726160e8e9cd6afe48107486748ee834a271f561aed1d5ba10052d49da51835edfd00d3508bd35bfe15fa5ef68ca64feca0732c28a918abdfcdffdb6243310c4321acdc51507e6943f88891cd48d81c",
"derive_key": "a3e1c322ca6816f8c24a6a5edcfc4b55dcac00c010326efb26993a743f3023d23fc9148f2b58b9d923c4180a874d1417b19e25223c156e7afe85234d4dc285087d9cf1efb854b72e5ba8a2fd0c71377d8e07530c44453778a651d28764a300066d7390d0836df263d687909692dfc684a582c40b1f7b72635738123076b1b596efd6ab"
"hash": "c35f5b29380287b01c9b7c805a948a43e818aa409f2527601a9ce0ce9f10d50833964400fe36ac38994ea663326841a7f3fde074a19e355772776557e0e7e8a768d03c18c4f0704c49dd58a1de01cb202f228aff76c4bc29a0fb5528c67a84fb5dee5b599f5f84726e5892311009984d720413d91eabd66b3ef7aca60fccfbe22a6e7f",
"keyed_hash": "1d527087c25ab15fd3f624036a39cb621b2383bd8dcb2597db4b23e5af39808119cf6005cfabb0625dc5247e69a0f8d703b564cd7952cce1a6e6eebebe056abfc045618db7f2c92a635cbe477dec98fc0816ec7d5a16f94cd6219cdcb3ebb3c7403500e089225f67d2281248bb6404f87b14112bcbd109f28ce42ab85bff8a68cab9bf",
"derive_key": "9b33156a08406f1bd1d1d6b3a02a979761b7b42554ca97ec7b9d8beebba91e16d80e5cedccc414047a317fa3c6e6d90915e155630caa4c4d0b75913732f23adbc629d25b4711a743690b43071c1610c90450368dec4c3c94806299d53c43e95933490f1fed26955570fd8cc37c9d120f82870e21387c6a7712ae32a24e475a61baa4a7"
},
{
"input_len": 2049,
"hash": "a28751ab119bcc3a98b33c044e57bd576628fd1d822b884692b0c04500f2f12b0ab4578404e9db0d4b6ac8fee93ef64918c5171a964c4962dcca5a03b06b8056478731f1dc37b41998348a47a98bb73a86bd3405a8bcb71fce13e582d375abf2529ebb331d749de069d2276491e18b0309e844bae9d89f3f1ad8989538a63a694f22bc",
"keyed_hash": "0c63186b996b3a63d2bf41cd85e2844634daaeac09f6f9c6981ebcef6408fcef0d9622d98b787bb26f47907282065ae21381b0caeb3e1774f350b2e8ea165586ad4c8bad6a3a618e5b8705531f04de6cfeca048954de7c9db5b300cbbfbe20997df617701d47ea977e72a71728f36a570646d7f9fe002217cf6a83fe4aa6618ed122ca",
"derive_key": "0a60670236c26fd7257bb685496e9a37c207cf791ec2e816e4b9ee15658b9bf1cd9e4033f777d450ff4ccefdc2b905fe95fe7926b6c783107796cd76d65dc780a6841bc79758b6b7af0fd09fd6fdcb4cc868abcc837da7e5782dff35b617f1bb8725499afab9c046028c508e375cd64c9578fcb0e2879251cac9cf8240200f32b3c34c"
"hash": "050250e2f40977b9439b90d81c5d0a8306c4d81cb0f2dc41f856aa5415950ac082e35eaaabd579c8b32a79b6ffa5e0d63fbbdeafcc887438760fe0fbd3a7dbf679f9b287da1b3d2ae5645ae7deda633f581beb255ba41b097f6f890946d55c770bb48f628a727492de73230e365b330af3e786878592c0a54ccf1d9040ef627f120cf5",
"keyed_hash": "0ab87b84668045b57b7ad2311175b039ab7078e7b82c263d6f2595fe7215006b1a1ccad2d7afe150f4bd238d9fca73331d26749268ba0c54366fd7aca3d071277185824a2b2678141eb82d59394a27d3db522a9b40cc1364ac189a78f4c91122a67900838cd572d6a4f6531ad4497ce9e4505fdb59b444ed91e670de4116e66ff89ff7",
"derive_key": "14970357d73b88688221bfa7b2871fb0e644738efe3b7aa695c185a95dfc3381ebed05ad4d5f73963cca9840a985a00b3af3fcd61541902e0d9e1420bda0dfad226c98f2b036d0993d48fcc027ea1055bda4d48775e7b10245a96821cb6206f6ce1b9d10ad08a0fd5bdc0f1f11ef7d47b4b200565abaa0d69bdb6e316495612246fdb8"
},
{
"input_len": 3072,
"hash": "ea1e8f063f8e82be32cf1b8803131ee6c766f4a1454edddc43fb42f68348cf95576e418d1b821af80eba6780bddc5f2aef927c5a4375fbfc4ee5a6492c145e945f4a96a2eac530874bc00ebfa4322f193674db54f9b89b73c7eafb7014244f45a78183f13a1d11f837733469c5f70da09987622fc64633d8f5f095613ffac265e2d4e3",
"keyed_hash": "37eacd872e6f1c739f5d8049c385096d6a70319269b001016775be97f25c308ccd7de3d96ab0d2d1be70433c18a3d0bbefc4e2384aec6d3d1dee7d66b50db92f6f51011cae661df899bc804d2ee2dc48939a2f2104474e0ba8f3a1916787708c90b05005bd1ee864adcf35b19cd2d54bf100af14c81bad4d52c5babc1f45bdf3ac1ed4",
"derive_key": "e457352e8131aa9af35395b764691933a90bc80ca0c6ac4fda7b144e80889c53e0559e29f033491b13e19465e4fe6ea8d878067757da4be7fe5abc6e595abebca5079d28184609ca5dd0718af94afab032d2e63e432d09c399db3bee2995b44b714538cc2147bb50b228ed27a42da92a25f1560c98a3138c4ac608edd0ad17b0567e41"
"hash": "f4ee67a9c3c1d72129216ad7d59ea92d2573a1cf4149f09d394efc1bb3da11ebdeab0a745a4a75bec19edc1592fb110fc3b6e15601959d1f4fa3cca2751a2c9dad9a28a818a51a3d8eb117fdbb0c761023657cd04657a4c442b9e929059f89efda17d99d2eafbae767cd4dd2193027d7deb5e8374c9ed117a9a78b5d3f4db8d9703453",
"keyed_hash": "1376b94975b3d3531b00f2b1d402eae8f62f0a5240eb41eb81876f9bdb06250cb1e1830fde5d967fd79c247b42f53c00826db7f621f0dc8cb4fede40973025737b987281b6a5bac8dbfafa68e33475940c0e55191a31e0e4e8985a279cf311a6bc6ff4f7a15d63c195e0195790114d8033a91527097d4023aad7e7758b6c630e717651",
"derive_key": "14b84998443e84d23c306d317b9a501f58aad6c83b2b634ee0b821e0e5b1cb62358b136523aad7650d97d5db3ddea17975367b121ec251ff9888abb6f0b664caf77e164febd8954e2d4dd3656aa211fbece28c330d139981655a58e9d8736c0671638aeef667af8b62c3ce7e85ae948ac8f9870176a7fc2ed0744b6ae555425711a86e"
},
{
"input_len": 3073,
"hash": "4f900ecad968a614daf5fa3c1476feb4ebd33c767eac451ca02235d728480c504b8ebc5a36a551879b74eb9ba74d63e1321dc1899112ef934d7a377b016ef3815d348e517cb214e2468cad5d3c23a272e0cdcfa7e05d86b205825cfbebe93e968d86b35fae9f55d37a024886f84b456501e79501b9322bd7da3fc13c61a66201a5b93f",
"keyed_hash": "a892306443763d18ac33a590c4f7eb525165b4a562906730e10a9d1fe0a4468abf06e87370a686619423d98928ba83bfcf30868ce64a490a6f3b80a08808b3b611d3cc9689df83f92abadc63a191f463db690e5355555479212131b117730289c477d7d235e2d8a7db1d828fd9f8a6e2c2d2c20cdde5451d3b967ca4c0d6c6b5394f36",
"derive_key": "27af9b9042561bd6a67c7b2e743844747564d5bf53a85a1185e005a5963bbb8dc1f915dfe6f44e156e35903888bc90569f438c37a629f66bcd762b18414f930e2fd91824ed0321315f24a7399164168285fd773bb2a74540e15101b0d1d39a9aed73245e6ab468bcbc47e50aa6d373a53a7549e48beae6c23a91662b0b5bfad38fabbe"
"hash": "b88af2464b231dc122dcf87d20430a78a4027be457eaee96e7fc0f56fc870c304c39b27c29f265a117bea9031af40d63a60014ba35ea970bbeb1874175e07ea64f34f00b47dcce8914e3bab4fd2776f2c1898df2413c46d16d99a66007d3de7ff461e0efe1a58be47d2aed35e099524a67bc25a7585509f73ad858148c649159b786a0",
"keyed_hash": "3a5f83ddce3de57a5928fdc80f899263acbe100d30ab958cd41c2520e64454727466cf3fc0929991b23d224a087aa49f974e53c0b5a77ba8586a7dd3a25a52fb4c4d8b57ab361589f2077fcbbaafcf4d96b3b19e88f0eceb377f357d1e691c7d416f786dbcd478706f726365fc6d035da3c6849635b905bd022ebed1b6c19d835bfc51",
"derive_key": "87ffbf5cbb3107cdc147fe6dbf25dad1b42cdb5c294cdf7d962e7a67aecb246d6ba70aaf678b03ccf2513da9333dd1226bbae39dd20c1fea418ba6b1ea55f1a8018ad029d31cad2f64132218efa5372554941cd13e9f3b9af7c67cabb5309d1eae57811a29ea98a4c1c50c80698819fa5a39bba042c6855dc31b6e83bd688c8aab9a87"
},
{
"input_len": 4096,
"hash": "4bca118455d6296e7d3f1d2f584d9f8cd376de35b2833306f6597071598efb15c16e4fed9da03ba70b978488101e8bcb71669dacbb3a9c7b09be8abbfb0291fc25dfc36eb39a270f63c156e495969ffa45b26f698767f408b7386f049367f897c30a50d833d4505ed11bfa753165ca035b6d181f847a90c8c96912de68bfed90837cb8",
"keyed_hash": "ca94009dd0f62fa4409551fc694dbb173a7470ec483aabc8d6c550325602d63cbb950727acbeb0f11ca1d93da30e31c8fa30f9bc114691d11133070c35122589ce46e85996d8b7e831e361b8da2f19d570c0b2b8ffbf2716251a83d77ea7f23037a258dbb2cb1af87eebda81d0f68738e3596a58cccda4d2e2fd8b3492332e6017e799",
"derive_key": "ae68d95c4e280482913a2bd2eac1f53d1c0780c97b7dc02c8df0ca6b384b6116473cd2f725f4c8d2b793be01ee8d3b0db0d13334f0167fd77c9ae7cdb69351cf66cbcb1976cb0e77c914b659804ba4525c20c4ad006df7b4437129a89df5c4183cd284d9332d2edb10141780b6c3fac57dbdbdfaa7f1ebd2c1bc8546b590917bf670ba"
"hash": "56068fe2417d482caddf41981d3821706c4fb55a9cc1eb7bc7f70f81323fb2b5d31581421f19697bd513b5527ca701ed4f0519568932c1d0e514a1daa976ce159f032fd7d4afc6b52756c0049cc0c5f6fbbaa27295c60d7be229d69c84046b7afcc5dfd76824f3e58aac78e0fd109ad1e09acdf10afb8f2d6b8a0c7974643971d01614",
"keyed_hash": "b9e6c35b491aff9a80dcfa9706fa163dc114ff6120d329b421b2adf9e8bd4abf8b8c8ab3f0fc827c302069e2c9900ed3548365b2b67e0ee19cd78b9f1737f7876e09c036fc776d62ae0aaf2a1784fb8825df9f1550d8d907d733cb479ea218468cda2ce25ba17835ec3bff8e7f6cff65ac856da18df30b5091a278fb5a789606df8ff8",
"derive_key": "a5780a5c3f8be52f12d97baa2cb0fa7f863f580dea0e35f8e30dbe496eec9a0641fc4902c66678c0db4433aca0426321ed95b1d06dd19c39a12baf821df5ac1f3304c0f5b169fd5cd3f06ba8acf3286737d1c2e9209b005d209a1cfc60713e4eaeafa0e21de4455f60d5db438c124edfbb7f93a14119214f0b2ecd1e836a15e0f43c90"
},
{
"input_len": 4097,
"hash": "66af1962444e322cffb12a0cfe07386bf7f99a2526f2c10410d072b1b2b6c30b8926ed584ca289a7e447baf38d66a90fb7cfd8c27162fc2b16b360fac1c89099131eba37b48db6bba143d7917dfc19681f2f2749ed34ed80c30f8f85cf2ce97ae6b02eef2475dda15b0e08ba8993147d9242ac6efbc2ffad58aa6c3280f2b4f499840d",
"keyed_hash": "badd7f7f0f3ce8cce0c98ee0d5dbf290ae95e47fe4e429426dab3f0a720434b90057a4607dfbc5d94f2f24c830753e9be18afd0aaa839a78a264f40ac325312370594ae563845b97bd206a2f0656ee460f7f0f28f16163f55f3a21529797eb896766c010054d14091eba78ff0f3c7ea06b957238678994f16c948c97c44471d1e940a1",
"derive_key": "80e9096ff8c50c47e160e53962cee307337244de753ad7373b6bda9fa04194af8427228b93d5686a6a997f9939fcf731bab99b919366d20355b0fae1d325ac4f505bc7c65a0209703a947a5b1dbe88f191384b0058b24f03317c48a729cccfb03fc73f6ca29aa91773df70ed9711776026502e256c4a8de0fff5d62408701909409d63"
"hash": "05cd1eb97346a2dfa22cfe210a4b13c03ccf947ed0f26a8fe279f78d2b442509242d4f69b7f23a607e74a4d67b69ab3ac5ac2f3cacc576dad666776fefad878b2934d137e569f7d794f5636b1731b874f48b12e887edadacacbcc715dc5c9522994e0fffbf699586dde80f2e80c6c0aafaf15cc4ea2f6dfea12d7e6759e1dc24a1a31d",
"keyed_hash": "3456202ab8bf4d8ea1694b9756659bc3a85255670304d3c67eb451888221c93780a9fe5e7e092fe78bcf716b4423a97a8c03b56aca1fe038c100684762f1ce9c7620a56abfba69f979e14d0ddeade8f4ebbaa0a6307d0e8fe518f2a70cb0a23c5b0a8a8523e1d0b9fa74296640176f8f592e461a3151f2601524c011a634079efab019",
"derive_key": "47022f8bc16d4655853000b2c3ff13c5c153694314a450ad9f18d61d996296b029e1e0da445f5bf8a4e5b2e32364f8cf676c0533dae0412afa69f127f71bf74f233e4a2d56de3fcbd39b04e9c1fee26758b1cff5608962d6593704fec9cbf9087fb17890f81245f742c8a50275df7fd1bd54a75b28a3ae40785d28b8113161e20f07f9"
},
{
"input_len": 5120,
"hash": "2bf3db3a9ac67954f18bc05e524106212e25c85d99265a2bfe9cb4a2e45eaedc6629733fc97aa8cb08b24a7f293938424c60ffb9b54efaceba57ff28cb45043ce37e5b51995e37ff40e853717674ee13712b7d1df2beec8dcb8ea59118b905edd122b59617b387fa1e01e57646af8ce9038302941ee97d978dc7d7ab48d7521cdef3a5",
"keyed_hash": "28df76283da2fbdaf49a778488ba2e06aea9bc591b447c6928a4b16e29f9a01e4c2b65506b68cbc443548da6d0d5dfbbc34d38e0539e780e5256ed0b7146e7afb7998e0bb63411c798594716f5126b833958c8ad3dac5b1e407023fe94706961bbaee4be70aac9983882f6b3ca21e2d141703b5f45f9363120ea15434169200af7ce0e",
"derive_key": "ac04576c26b281a4aefdbf800537a7a078f9b1dfe9fa51473ba23fdfabf4e5d7815ff748cc8035f1c2788266d340ecdb27bb0b64ae4f06282b8f23bd4a356a90c9de9d39448c51695f6622c9d9358f315f7ea6c7833b5c2d88df64bc8e7ffa7977f08feae4efff912545e41f3546a3bdf93af4d5e984b77158fdce648df4a439ef1f2b"
"hash": "10e426fca9481e052ae4e4861b83ae5153b683962d81480cb69afbc611c6c2ac716ce4fcd4c44456450220d29494ba65dd77c9efb450048283ac982d59aeec935a73f16d4a92642a9d61e7a0e448ed95e35b5834b8cfa8bb36f09f3bf1812cb5ebea77d0e7fea35fd5f8530b28eca28577e77486595a0bf83b6d0b1835d4655d1ec199",
"keyed_hash": "50c1881a1e247bcd482b1254ae8f77a9d953fcef15e12cc26334d158f0079146351e29a26ca6c9e268ce2f32c1b0443b92d0458465d8611c94e47d477aaad3ccde6ab4ef21b99fbfa0e87bd2c6d05400e871725609e7f4b8a22cd570132483639ea364382c7643c1b08911cbec29322a82f1957414537125c5c20975bbbe3abe0d9ad8",
"derive_key": "5249571928c0009ee6dcffa8b8b9ff952490e85d8abd3f0459ecac3613dbce83a718eb430d121695a919011f0edecdcaec78fcda5201c4377c94d18d2bb5dc37528370ef616c31cf285c3bf9e2de1a6c8dea79f62061c7604f1b736a47559c664e8fdc3b2ff3af03b86693c55b5ea2362a99a3c59704df9c68e17fcc81a0a774e80c95"
},
{
"input_len": 5121,
"hash": "5e9622dfb874df3f5dbfa740c52a11744fa0e5a366f62718dcee5db9cf5446d355845ce73cf2f3bdf96598b5354cfe5c80fc14172bc0934ca5e31f07a6b204859795118fc2bf91a23ad61a7404bb3d156444181785ca003ffff916fa0386fb3f5b9a657ff1abcb91ac9f3b3778a651b2269978fe1fa0d068baf08eabda69118278f981",
"keyed_hash": "fe21c21c0d9e01ecb420e9684061d221d042983344048587803f89cdae4c0b26384ff777e98094144b160febfb13b31f469f2bab36de22f152939caf915cb2264d1bb419d1f88eb6cf8b75a248a2009d7f87c7cc5b62f6fcc6ab743f32dbb1d9410c8151c6eef345085305ba3541e70275fc76932bf9b63eb9a363e75de6bb3ea300c4",
"derive_key": "c6d5245f68b25bb62058c3d798df2fa90a3986e877124c6c6628f6de466406813c2c69baf84242435a5dd17340d0e0176ed78646f113259c7fb45829339f51e35f0734f1dc44a329cf513b67ae0afab8f89f745669ee0f77a7274c8ae3acbb4671d46956d5913dce6d077fc7778d867f4961b0a4f021c0e51e9fed6078716384c1ee77"
"hash": "2e80caea9b43674ae1770f26a1c5d1fc9fae62348ded8725b6ee447e361cca3876b8f44d9be09fd25dddea5c319eb718202122bb97add2fa3a262fe0322ac0213464cf9f7c754ae5e0c7fd697bd58537ceb6592a1cc2572deabeeadf859743b5c57dd3b3c4e92fa97fede8c13d05017c29d310e6e5168dc94a6ee2a6b470f391606027",
"keyed_hash": "2f4ed41ef09baf4698f34cdf3638c0b9af1c7c20ce6bbc15c4ba589158d63e3ee2d5d873a0508fc51c1c3fb587a0aeeada084c9303ed5e21d1c89edf50a54bdb92591fbdacbd8bf070f61f0d4b2ef0979b14051b7ad6ceb3150605b4e9b8a78a5339de5f6723ee53664f45e6fadc435a380d50b7662a2a344394c7fb483bcaea81751d",
"derive_key": "818bb413621498d4d3d6b9835d80dee994538ddad7857d0b29b4ce39a02e324c281715cbfae95f2d6ac54123dba02b2028bdfac984a279ba6917ce1f961e1a46c47e61fa370c85eb0b7932eef03e06174fef2f67f3d67104d6fca65b3dabdca3e1c3175775f438abe670d81a035fdb5fe77bf197584656a43228948c8bd7ea2c78e330"
},
{
"input_len": 6144,
"hash": "fae6cc978712056c3cca480fe6caf918fabc5acb58a3ef43787871e3cc25646addb14faf48770247edc13b82ecc806f87c39211e433a63086d72a2a342d1bfaf3b8ad7881531f1df8cc351f50f422ecb731d093457cbc315ad07a20604d589de4615962f40844a4cc4e584bffbcdfa7f64974ed3b27ba394d938cfb8fdf96d75aa6089",
"keyed_hash": "05b923426dabdb0da6172cb832b2c11c2ec8503565903e66f63be3ad2722be9c117c02d19c405b86f6898b5d0f5358e16e223297ebee3f1edfa323eafb851354ab198d00d90a6942b0eb48782e58e3d304c26771f5dda4be9bfea776864e8bf79a267b8e2b969b4e1c87fe798dfc53252894b8f734070f70eda49f9d00e3077e721562",
"derive_key": "0290a0a0f3219b00b4978d27e882b349b4135b48f20df555eda78c8e83bd8cc6d64deeb660a02213701fcfc70363b6123a90aa2d708659e2ef9821c3f3abe1ff0c83592930840f4895f5df4cc1b073167a5df660fcb39cdb1f6587c468cd48ee9d6b49d03d9e28efed5a954a6aff69b95f6df4b6399e7eb12442acaafcd4550d3f52cc"
"hash": "1f2a63135b07210fca1f6b5dc8dc982328981a427d67c2b3b09ae9b0280ad6842b57f174f315d306e5b3743bfc5b6f66e13fa8ef10aa371e228c34fd9e19a2b467f4a4cbc6160050b6288615e391ae8027441ec90b12db1d7961b2d51c66acb881444f91181371153100f1dd615b82e0f8f11bf6f7635dd96132ff25e15588ef52b2db",
"keyed_hash": "8459fab047883448b07406ba3a2e5fe8651ff5c2b41e8451ba6c24a77c3a54254c043ae924f1620250f3f900b9b9b55013a634a77b18187c20b40a5d292c2fc324fdd5a719b63300067f5d914c6791737400dccf9924320e324be9f1af6956535aac86df8a5f56b42b97c0e31e6e9edc18ed1e44300ca33f4c6f4544cbd44373ac8ea1",
"derive_key": "b4cf2d3b0f2273acbf1dfcf0c1a7d74b13a95b9669c2f276cd31ebd7fedd0bc768e5baff5e019dd221a58dbc96099ee33bbcec5152915d20b82af6a30853719176d0be6014f97d7abe16c2d6f94be4a3b07b52aa6a0bef1b76fb02c916c6607463f6b199af2085e0b8bbeb3a602ff40d86c902f922f8671e9b4278a4514e903e903f65"
},
{
"input_len": 6145,
"hash": "269a8c070b08440b7aa4f0713aff0b424e174c4b67162734ac3c3a4ddc558cb0189011e1dec9d489c2622726e16797d8b8d58af23195fa14a3a3d8955c4d654a7c0bbbf2c359d0c57880c6af4b4390c3ac80210d4c148d850612f100898c2c70970948ed4f0e50cddef502ac239da3b76dee4833f5ba998bf666e4f76ea50fd29e2696",
"keyed_hash": "ba9b49b4f676d664410057302acc2334951d6683cea540405467e0d3a12e2e09bdeea1671303c0da269520f7e2380ff8bd53bdc80cb7322b9f87819d8fcdbf08941abfa7ab291c4f37dea5023da93a7fefa1a15ae43a14c4369f04662196e403cb9cd020c7d1bdeb675afb354037239be12791c6453f35a8b6fbee5560dd353b4ba86b",
"derive_key": "98d9420d1f6b67132532ac65bc54157791798e7372954dcc96a62bbfb7800b93d868aafb1cd5d6cbeba702fd947ae265178619425fc2739dccf5eef0282d72614935ef098d400745035f4d53a3eb4cbe810175eca7a11731254b007e35b06d4088e6266ce9f220297dbf577f952f785edd1cf6aab6c230ffcb98c55557826f28d39144"
"hash": "23a825db8dee7749a2ec23924f602209a0fddca6ec469fbccd28c28d3267ba862ff53154fe87bb22ca6a8413390d3c68e7fa970b0ff928c0540c64afa8f85706ee5de39fef4f116328d7caa3115e987b97058a52af568f095e5a3a5e7ea4cd28fbea9a23fa9af797f94a9a8dc160b777ef40d765148ccc710ef262377d3c09a7ecae8b",
"keyed_hash": "13619b046067dfe8febcfbed54d843f9f62fd0c1e19a5f97264fb94dffefcecbf894eaaec77a91909dd0ebb9443ba58bbc203604a0142768ed9799166bfa2b70daa052dda42c52735e48fc87b074930b568b57fa190c899408e94eecaba8a2c2dda0d70e1b10a71189e15146919d81e8694cc0de9a437463588f1372e9b04a2288288f",
"derive_key": "147a2420fdbba21e63feba2456e956be30516f3eaf287ef4aa68446d83972f80c7b60407b4caa6695e035050638b33b01350772dd4e17719b16124cf7c90e64e2d6288c2d56a7dd65d924b4f43a9f95d15871658dd4d4ddcc4d71a587b93b1766de9cb360023d694881ff7c45cba9f2b8b82dfb0a2850f4ad8c26775086c95505b2511"
},
{
"input_len": 7168,
"hash": "3cd036c2e4b96b2117f3d68a669771c00a9ff12805200bb7c59b73eb3faba8a45f8592abe44f1650f104b0f4b8d38821cbb23c17f88250562ea212396ea8e8d89f5a2c746a9d8593a6fae913a69a0bb4d3ce745cf968e55a94235e8a7842169065c6e957a00dd6a3d488a3f119e3a7de5b48000ec909f77b5390b7f729a89b0867274c",
"keyed_hash": "160e57ed3521ee795483b74313574a9bfd219048201d0068387da564d373fd5a6bbe81a0b89c284ec2e2678a40732f48349efe8d024032b6354b84162aa408b959fa3689e4c14d1339c444f7dcb3782da52bf82f6f997445c32940fed87b3da33d5841ac3c4bb3290ca48be6be3d27ba4814889a4527eaf84f958aaa88ccc4562072b9",
"derive_key": "7be85bdee89c85a63b9771f151af9d9c35a43bb0cac97e8dd09c328e2076b56c6568245127dc0301f5dc3e7ac37b909c43d6a9d46520a6f94d4fe67f0de7d990efb48753ae1f80234fa5d5c4a5f351652593d1bedd377db00ad6a9fe6a23f09ed6ad1f406af58e2c6c7b6cdf8437a321527524acbd8be29e0728e41d0528c5863c0061"
"hash": "73aff5e142aba955db9a11bc4bf5e875be2d27720f13aee122816262a821ccb3bc7cecd468884bff8aa86d760248984be22e4a6a72bfb2f4f2d945fe94e62a597f97bb969b53fd54bc089033ff04aa4b4a974874c8e49c0b58367aae97ff87329e1ba8bf3a740e115574eab72960e02b82a93ad1b2bc4dd40850b7614f3198f9d5f514",
"keyed_hash": "2277e49eff7f0e353bb647985e25adead0607e10b01a3af0e4e83efc9e04ba95ed6995304e472646b297b3917f8682b7f1802e2f9b07fa24a86da90620ac2de930489463481c36b1efe122509d39dcd3cb3610e960da46e462a840f43df9afb1be5e0512b972bc58a7d773f1830ec79b124554dc2d5c9f9f95400b264475e8cc522603",
"derive_key": "802b65135ec5390020ddb9032f231bb2fd4bbbafb1cded2bc16d09e1bac490591bca6b8e761f2548ac56dcf47d1be689382b72cb0bf3902ff2a3cc7fb091bd5778b574c5b802233983723bc4a16a4eaaa9db6659873de839f9785a4e2bfe0f1179ae8c2054adabe2cd22a8e24d93998e792a73775618394b302898f5c113aa3045ec16"
},
{
"input_len": 7169,
"hash": "fedf5839cfc5bd5791a6805ce071840d627b4211fab87921bcca22cf4f584001c0f0f7c695ec161a49d992657ce471f2427bca39fd2ef87fa1077a6df35d94dc5a01ab8a6e501b0e1d5dbb148964441bc39edaa0e9d8933c58f52add3a7ff8d31043c7aea51822a574e4be8a1dccd3898b87f906774d1747e0f41c6254bd30ec686039",
"keyed_hash": "fec85287842490e49b8fc8a65bcb2e3465af89425e7e67adfaafed99a2f54000a19c6214636c804582f228bdce6e601b3083a92faec734836704bd0056d5a94cdb01f85e027e0f0d8275a4c42f5d5e37aeaf3b40377888e3ce01b2a3a1dcb3785409b67374daef499e1b9f51bf7cf86d4ba3dc5ac4eed0793fd5cd57e6a1e9e62b04b6",
"derive_key": "8ef331f79f3410b0a1bb13f0eb7e18360051c33bdb1f5c0dbbee05c9278ada93cfa072c0be23962d452d30434657d0dd3922b12376a203eb87b70e06a61e5d7054ef55ea173d441ac18890d8c954be41afb0d2580511cfc8d6a4d4b5ebf5913c90280f81536ec387cf318c10eebbca8f2e32caa814717256a039f597a6d90f8f4eb4ec"
"hash": "2ebec7780331a3794aff5e6b10ff3e8f12bde652796d74503214869c89c9e9ea680df2f90a252c723abbeff8ff8cbacfdcd20bb0a8de6917aaae0fb1bdc639eed856df1778a9b8359c2d412ec82bdf759028606fe1d00894449bdd043f3860eb0f309515d576fc111502e5247e1ec7bb589a1459291410fd4b4a8806ac55d843bea017",
"keyed_hash": "897802df709fc320f73dc4cec70823c1209850ce4f8bc6a94f82cb33ee3abfee9d9ad6fa577390151cb106ee3f56a6d6416500ab0604fcc8c1b827bba8416508964fc0b68a7308df1bb2ee6499d7bdef2892ac4d6dc5c02755fcfff72e4341d95c085e9906928661a427b69d087c4469f62888395348fd54f2aa0bc4dc032691e780e0",
"derive_key": "87beb52a6089a58f97e896340b4a4f42c183dcd325f18120933dd779988f1a5154013290c315892353841868ff7fa35a4702ce427ae90be5963794e92b88ff3304a52b3e9826a898accb69e0ca09ea9a8f2fc87356e9aa19ad738fb46a04cae0abcbff10b7cf4eaca0f874f330f3fa2dd81c52e368782af9d3f00fb2aeaaf320b3b3bb"
},
{
"input_len": 8192,
"hash": "ccece675f5ba317ba73fa85fb35f18abadab40cb301b5208e002bf42231334460f135ecdd67929a27d108368c40216657760959a1ac06781c48b436cbb1e8282eb4034e1a42779fe79ae60e05c12c5ae9ff5df76aad0bd4e2cf5d8e56e6577b2f64bd3a558d5290217cad36c89e5279e7985158d3c857180b73aa868ac4edc460a644b",
"keyed_hash": "53d6e81471efda10d8498c10477560bcede5f5784c44a9242f682a907a283ceb53ab024f471c5504b53fcf45f9641906210f77a619e03cb6ebfe238cc3cb2b1d7d6f3be3a2981667a7c0732af76720eeee494261361c0cd3b1a9c1c7264b6b191d84904bdac97f229c19cb78a8a36ad43bfc7f7ea2416eead346ab0157ba6ca75a6ca8",
"derive_key": "df8d41f7a7e9d16ab990518ab9b2dcd657def484af65650ed45fb41b8545d258901896a4d0f6bffe0d3d142f9713203ca84bdd2b81994707bb48d287ae84d76e7834b7c1da15e668b56a761cb6993797d23d51941693b975454cffe55d38e1be4fdcf38a91cf35921fa1e5879ff5115c48ede0c6af7f4bc7ed42f4ce679168a37ddae1"
"hash": "97fa001fbec31ce77600fef5d53b074f2c9f35ba1843f03fcdff567a8204159083023093a92e61c9ae6156b42b31eae6bbdeef422b35151cdb50d410aa65bf41936efee65850ddaf0c8daeb0b158498d77bf0341cff689c152a9eb421b1a4453f2c777d1671626c95decce350b3d32900303406944c82ab50d5b3258f627ab1468cee9",
"keyed_hash": "459b6e7c3b67dbc2ded5ee7edf0c423a43b79f7f11445fece829e484876ebc304a50e5e3008c584f45dd0c448d6bddf09f6a88289852fae087bdf3531b20ffbcb3fc80ff03d39b03c0799a9c3face24161cab1622eac41c2536cc6f967b36c9d39fef5eb7a30cf24b0594d4bdf21f2532854593ea16c00da88dcab97cd106e75ad7c1d",
"derive_key": "ae4136573e28893f6b1637821415009223ae197ef5ed229ff15980c0519b39c225321ac9fb1ddd18decd9121b8fcd03f44aa4c9b6512eec25c1db075900b3818c6d7bf930fafaf50189fcc5dc4d2aef9c72427a747f3a8fbf13011ae46d4f3b1222b7b52bbed523ba0a41c83660969ea83374694ee6fa209ff1f312d06dd43e683cc72"
},
{
"input_len": 8193,
"hash": "d8dd94ee742d0556e9cd68ebddd3aa08bf9582a6844fbedf7fa858bbebf7d9370f4cbe1f7c5f74719d72cc66bba64cc7e52450bfb2d4310aada7160f931d39503108e328dc23916a8e075587b51bebb5bedabeb6e4c727446a33d5e2784e3faf9dc880b2096ba0f7050334a841c5c57e4c0c8c576785fa14f4781fda190ec28ebe5be6",
"keyed_hash": "ab35071411f03c9c19a05e6c8f975a4964bc29d8c1198c5aa7316a15b09228f4da60757193ff53db79823863fb001b685067f09b59f1ba458eefc1f2945002aa58862b9c869819bcecc30d1fe3eb5f29887fa410bee6899af6764c7923278d626ec8c53a1eb09c278e06f42a748cdd474d4e70a4dad433957df9f6c00238ea0e9ca623",
"derive_key": "742f1f233f37a11686900cf33f542d492d67a599642fec222d1469acca0b08cb0b7090b69ee1a384d6b37f8adb3d34d9f6b2c2918fbde322fa4df6669e779acf07754dc61b47419ac6dea0a6491e64f7b4cd32db2dcc71cc25514494bae295941982a85991c48d3bafb07bf8f549b3167c2d6aa636a084864ee0f0181fe8b5130305d2"
"hash": "5130f9d488e0397a5b33aaf5dc07dcff5791a363f6c8a3c1212773f106debc5e8abe5659a570881a84088cde123c8cd9452d445518198375bfda9dfd3a17de5150576777dcb23f25ef62870bd17350ddc2e545ca21c5ccb5c415994fc933a48fe30bfb7cbad580bb83ed971710eddd03918dcb3321d6d43086666d43ac40838ac84d49",
"keyed_hash": "9f1468dd575f4d04617377620f3f0ee8f6f0e28b2e1adbbf00ddcc7763f64e4ea2e24637b0587facb504bed148971183ccae1ea94272c430868df99954fc8e875396832527ae09da201a4526abbaa92a6dd0d24958e8397137ab1a4c5064fb23fe4647512138288020bc9362212a9e8efda0aee1912cdcc8d166c8583958e8229af890",
"derive_key": "a9d29e855ff49e38e7a513790117488bc482e00f7b3372452f6b04b465a601c2350e55f8c913814aba733cbb3b8088574243f209e93ea167584df60888795bcc912fc5908bbbe5871c108900c7b68515a8ffbabb1f2c66e0336aea6d613e7081691fcab803e3ce0222c6b9036e56f0b7e415c8742dc203a2ab422ad61ff6f3426107bd"
},
{
"input_len": 16384,
"hash": "e46b2e84531c07db7826b4203418ade89cbf1a4b73c9664d8d12344f629c805eb16ca3dfcbda2c5717d1911bc28dc55a36a4836887acf39007e1364e691e052db0784b1698720de7bac11a4ba00481640705725d260a39e5f83fc9a06550e68cc00a08e64422718ad1e440de636fb8905e4a396f19d66624f83db6d5fe196b56808616",
"keyed_hash": "fa4f54874d46cb0fc6cc60e98885ae58242afd31638b9c4e2a419491be84de2c23f11570f25c29ed5989e9268972505601b603225d54786e6a98be2fb64e183e500ff2712435c2abd9c7a5b02c16e1e1e2b25d348c5c4c0e7cf579eab898a22ee93334ba6d4a6e482ba233ceddde9571a27e82deeaec78099f785d0e98e8ec95ac14a1",
"derive_key": "dc72c1d4efc6082c5a67cecd2c689c9bc3302328affbaf3e636d1a5101d799f9fc685877ca66c0034ee621e8688a2ba73a9d02dc3f08c39b11aefb9720df953a2701332d24b08b2c1ac55a4262d38b5bb02a05840cbd42487720603d7e914f4d7afb00a56ea3c6b116383cf1f43f684f855bb10f57d8273e001ce58b98b3e5f3f142f3"
"hash": "f91f20dfad46cb08c3196ca0060f0b5c3840445030f0184ffa2ebbc4cf6c46e63dfdf6f6cd998aac06c43860322d75f050cd385862a826eb5e83552feddb5a7685a1d66463ee6a0aec4da1574d9a11d086bbc566c4fcd1b0a2f5a58ce29d663912ee1f9be6a63d5c93c1796103fc8a09f6cfea3d8cbd0835e045f7d5440bc9552dff96",
"keyed_hash": "8b666ddf0a92535ee4056d663641b9528c9f5506bdb84498bd57a27a50ab770fe1a4a77a0998d88ce17080ee110b0104f3fb65c6498d9aafea4e9d710fd9f2ff8644342a42e1c31e777a3a74537a8dd5d3ae5dc690184a47a50f0df13d2ad26101f747a3646e489cda3eb939a37f21e7c42928369278d50ece2ea39c9763cc6e7bef98",
"derive_key": "f4061899074349ef06f61ceaa653086c6377924df70aaf3d03c68c9652462aa1adf740fdd86435bd1b789bb9c2e61b4a14bcfd3eb795a1feb141d4aac18031afb02d80599c597706c9151e5b59730914bbdb0e803d53c0c5359fdaf168072fa192c814fc97bc6850010facc5709735aa56df37362146fc27d696ef619f48468f53ddb3"
},
{
"input_len": 31744,
"hash": "8fb8f76bfd089b55f269d09e07ab8313a9e42546987e38bc26fe0057958727ca642e4412204988abb5bc3998b7a376d8cc497404d2b5420694c848e71ad0fb677c497d6cb52d00b740ada75900f6d4293b6cdcf1e810161822c566b8bca08da9713ddd020d5277ac9fce5c7b5dbf3e535916245c10e54b66632c2ac17b3f97be54c8d4",
"keyed_hash": "7b617dd103082420762aea126fb943f9bb6dc061928e01330b0e58876936dffbb6f0eaf05c534d22c92e325908e85359b04af937de835bc6cbc89fcfca959bebf674f300a92d04667be2a41c51fe8419a0d671412db5b9441dac220859c0dc36b1e63a5ec457f52806e8a6422b37d9a2c6b4c2b4779f8b094d974b2cda9ff1ee2b0f5a",
"derive_key": "d563755f619459199584e5b2e8d9c3f46c38c8a0ef242319f581d8fa2a2c6ace74454405ca5f9f140f32dfd8878c333c62c1831fd5de349575b2095b470e3e8052b311d97aec086d2b57372ad1765f141cc34be19323ceb0097a6210e79b52b28699b9e60e7005c1b311efd0e27832e3448c71d5f2d12282033aa00755ff9f1704a56f"
"hash": "58a4d5c7cb22a2ea1a0e78cfcb79104d4b3e8bfd8fa384c995f42519bc2546d949d85b50f85673969fd1ccc0dad15e29249dc2be51eb9886ee637462f0c97834f6b777776cd3beaa578618e945323d2452f0ba654a999550ce487d7583a3e92e785d07641be903624a5882f44ac4e4a3b94b04b7fda352b4211793c830abf62ae0dd6e",
"keyed_hash": "3de3905be6e33bf796994a710c10b07447ace37c29d65437d32c51dd5396074e0fe56076eeaf3509791f0dc24370cebc0aabcc048de03dfd11b3173963ccbb0f71c2456aba58ce1437a08834590020966307cc67437750df3b8df0bfc393f37629b13754309831d6533653847079e3031c2766bf3b15377f7021d05c65bbca76bdac65",
"derive_key": "d604f1a0e759ce81d762b870b2ca8a3d65ffa8ca9e00d265e03ea39f3acb4a9a70df332e3b9fd01d987c87279e6162b4da2f6cfd9a3e5c2ba5a99ff5bea10cddcc49969fcd2da18937664e724a2b90eb56b624452de8a152ce9dd4b430e2159c89ed9d47774779c831457c4ae45b9ea58fa15ca10ce9e0177ff1d0f1940ddb7b5c545d"
}
]
}