hashes: Rename length field and use u64

The hash engine types have a `length` field that is used to cache the
number of bytes hashed so far, as such it is an arbitrary number and
could use a `u64` instead of `usize`.

While we are at it rename `length` to `bytes_hashed` to remove any
ambiguity of what this field is. Note this field is private, we already
have the public getter `n_bytes_hashes` to get the value.

Introduce a private function `incomplete_block_size`, the purpose of
this function is to put all the casts in one place so they can be well
documented and easily understood.

Fix: #3016
This commit is contained in:
Tobin C. Harding 2024-09-04 17:40:53 +10:00
parent e2ff08635e
commit cbfddb0394
No known key found for this signature in database
GPG Key ID: 40BF9E4C269D6607
12 changed files with 86 additions and 78 deletions

View File

@ -34,7 +34,7 @@ impl Default for HashEngine {
impl crate::HashEngine for HashEngine {
const BLOCK_SIZE: usize = 64; // Same as sha256::HashEngine::BLOCK_SIZE;
fn input(&mut self, data: &[u8]) { self.0.input(data) }
fn n_bytes_hashed(&self) -> usize { self.0.n_bytes_hashed() }
fn n_bytes_hashed(&self) -> u64 { self.0.n_bytes_hashed() }
}
fn from_engine(e: HashEngine) -> Hash {

View File

@ -102,7 +102,7 @@ impl<T: GeneralHash> HmacEngine<T> {
impl<T: GeneralHash> HashEngine for HmacEngine<T> {
const BLOCK_SIZE: usize = T::Engine::BLOCK_SIZE;
fn n_bytes_hashed(&self) -> usize { self.iengine.n_bytes_hashed() }
fn n_bytes_hashed(&self) -> u64 { self.iengine.n_bytes_hashed() }
fn input(&mut self, buf: &[u8]) { self.iengine.input(buf) }
}

View File

@ -187,8 +187,8 @@ pub trait HashEngine: Clone {
/// Add data to the hash engine.
fn input(&mut self, data: &[u8]);
/// Return the number of bytes already n_bytes_hashed(inputted).
fn n_bytes_hashed(&self) -> usize;
/// Return the number of bytes already input into the engine.
fn n_bytes_hashed(&self) -> u64;
}
/// Trait describing hash digests which can be constructed by hashing arbitrary data.
@ -314,6 +314,13 @@ mod sealed {
impl<const N: usize> IsByteArray for [u8; N] { }
}
fn incomplete_block_len<H: HashEngine>(eng: &H) -> usize {
let block_size = <H as HashEngine>::BLOCK_SIZE as u64; // Cast usize to u64 is ok.
// After modulo operation we know cast u64 to usize as ok.
(eng.n_bytes_hashed() % block_size) as usize
}
/// Attempted to create a hash from an invalid length slice.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FromSliceError {

View File

@ -6,7 +6,7 @@ use core::cmp;
use core::ops::Index;
use core::slice::SliceIndex;
use crate::HashEngine as _;
use crate::{incomplete_block_len, HashEngine as _};
crate::internal_macros::hash_type! {
160,
@ -17,19 +17,19 @@ crate::internal_macros::hash_type! {
#[cfg(not(hashes_fuzz))]
fn from_engine(mut e: HashEngine) -> Hash {
// pad buffer with a single 1-bit then all 0s, until there are exactly 8 bytes remaining
let data_len = e.length as u64;
let n_bytes_hashed = e.bytes_hashed;
let zeroes = [0; BLOCK_SIZE - 8];
e.input(&[0x80]);
if e.length % BLOCK_SIZE > zeroes.len() {
if crate::incomplete_block_len(&e) > zeroes.len() {
e.input(&zeroes);
}
let pad_length = zeroes.len() - (e.length % BLOCK_SIZE);
let pad_length = zeroes.len() - incomplete_block_len(&e);
e.input(&zeroes[..pad_length]);
debug_assert_eq!(e.length % BLOCK_SIZE, zeroes.len());
debug_assert_eq!(incomplete_block_len(&e), zeroes.len());
e.input(&(8 * data_len).to_le_bytes());
debug_assert_eq!(e.length % BLOCK_SIZE, 0);
e.input(&(8 * n_bytes_hashed).to_le_bytes());
debug_assert_eq!(incomplete_block_len(&e), 0);
Hash(e.midstate())
}
@ -48,7 +48,7 @@ const BLOCK_SIZE: usize = 64;
pub struct HashEngine {
buffer: [u8; BLOCK_SIZE],
h: [u32; 5],
length: usize,
bytes_hashed: u64,
}
impl HashEngine {
@ -56,7 +56,7 @@ impl HashEngine {
pub const fn new() -> Self {
Self {
h: [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0],
length: 0,
bytes_hashed: 0,
buffer: [0; BLOCK_SIZE],
}
}
@ -85,7 +85,7 @@ impl Default for HashEngine {
impl crate::HashEngine for HashEngine {
const BLOCK_SIZE: usize = 64;
fn n_bytes_hashed(&self) -> usize { self.length }
fn n_bytes_hashed(&self) -> u64 { self.bytes_hashed }
engine_input_impl!();
}

View File

@ -6,7 +6,7 @@ use core::cmp;
use core::ops::Index;
use core::slice::SliceIndex;
use crate::HashEngine as _;
use crate::{incomplete_block_len, HashEngine as _};
crate::internal_macros::hash_type! {
160,
@ -16,19 +16,19 @@ crate::internal_macros::hash_type! {
fn from_engine(mut e: HashEngine) -> Hash {
// pad buffer with a single 1-bit then all 0s, until there are exactly 8 bytes remaining
let data_len = e.length as u64;
let n_bytes_hashed = e.bytes_hashed;
let zeroes = [0; BLOCK_SIZE - 8];
e.input(&[0x80]);
if e.length % BLOCK_SIZE > zeroes.len() {
if incomplete_block_len(&e) > zeroes.len() {
e.input(&zeroes);
}
let pad_length = zeroes.len() - (e.length % BLOCK_SIZE);
let pad_length = zeroes.len() - incomplete_block_len(&e);
e.input(&zeroes[..pad_length]);
debug_assert_eq!(e.length % BLOCK_SIZE, zeroes.len());
debug_assert_eq!(incomplete_block_len(&e), zeroes.len());
e.input(&(8 * data_len).to_be_bytes());
debug_assert_eq!(e.length % BLOCK_SIZE, 0);
e.input(&(8 * n_bytes_hashed).to_be_bytes());
debug_assert_eq!(incomplete_block_len(&e), 0);
Hash(e.midstate())
}
@ -40,7 +40,7 @@ const BLOCK_SIZE: usize = 64;
pub struct HashEngine {
buffer: [u8; BLOCK_SIZE],
h: [u32; 5],
length: usize,
bytes_hashed: u64,
}
impl HashEngine {
@ -48,7 +48,7 @@ impl HashEngine {
pub const fn new() -> Self {
Self {
h: [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0],
length: 0,
bytes_hashed: 0,
buffer: [0; BLOCK_SIZE],
}
}
@ -77,7 +77,7 @@ impl Default for HashEngine {
impl crate::HashEngine for HashEngine {
const BLOCK_SIZE: usize = 64;
fn n_bytes_hashed(&self) -> usize { self.length }
fn n_bytes_hashed(&self) -> u64 { self.bytes_hashed }
engine_input_impl!();
}

View File

@ -12,7 +12,7 @@ use core::{cmp, convert, fmt};
use hex::DisplayHex;
use crate::{sha256d, HashEngine as _};
use crate::{incomplete_block_len, sha256d, HashEngine as _};
#[cfg(doc)]
use crate::{sha256t, sha256t_tag};
@ -25,19 +25,19 @@ crate::internal_macros::hash_type! {
#[cfg(not(hashes_fuzz))]
fn from_engine(mut e: HashEngine) -> Hash {
// pad buffer with a single 1-bit then all 0s, until there are exactly 8 bytes remaining
let data_len = e.length as u64;
let n_bytes_hashed = e.bytes_hashed;
let zeroes = [0; BLOCK_SIZE - 8];
e.input(&[0x80]);
if e.length % BLOCK_SIZE > zeroes.len() {
if incomplete_block_len(&e) > zeroes.len() {
e.input(&zeroes);
}
let pad_length = zeroes.len() - (e.length % BLOCK_SIZE);
let pad_length = zeroes.len() - incomplete_block_len(&e);
e.input(&zeroes[..pad_length]);
debug_assert_eq!(e.length % BLOCK_SIZE, zeroes.len());
debug_assert_eq!(incomplete_block_len(&e), zeroes.len());
e.input(&(8 * data_len).to_be_bytes());
debug_assert_eq!(e.length % BLOCK_SIZE, 0);
e.input(&(8 * n_bytes_hashed).to_be_bytes());
debug_assert_eq!(incomplete_block_len(&e), 0);
Hash(e.midstate_unchecked().bytes)
}
@ -60,7 +60,7 @@ const BLOCK_SIZE: usize = 64;
pub struct HashEngine {
buffer: [u8; BLOCK_SIZE],
h: [u32; 8],
length: usize,
bytes_hashed: u64,
}
impl HashEngine {
@ -71,7 +71,7 @@ impl HashEngine {
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab,
0x5be0cd19,
],
length: 0,
bytes_hashed: 0,
buffer: [0; BLOCK_SIZE],
}
}
@ -85,7 +85,7 @@ impl HashEngine {
*ret_val = u32::from_be_bytes(midstate_bytes.try_into().expect("4 byte slice"));
}
HashEngine { buffer: [0; BLOCK_SIZE], h: ret, length: midstate.length }
HashEngine { buffer: [0; BLOCK_SIZE], h: ret, bytes_hashed: midstate.bytes_hashed }
}
/// Returns `true` if the midstate can be extracted from this engine.
@ -94,14 +94,14 @@ impl HashEngine {
/// the hash engine is a multiple of 64. See caveat on [`Self::midstate`].
///
/// Please see docs on [`Midstate`] before using this function.
pub const fn can_extract_midstate(&self) -> bool { self.length % 64 == 0 }
pub const fn can_extract_midstate(&self) -> bool { self.bytes_hashed % 64 == 0 }
/// Outputs the midstate of the hash engine.
///
/// Please see docs on [`Midstate`] before using this function.
pub fn midstate(&self) -> Result<Midstate, MidstateError> {
if !self.can_extract_midstate() {
return Err(MidstateError { invalid_length: self.length });
return Err(MidstateError { invalid_n_bytes_hashed: self.bytes_hashed });
}
Ok(self.midstate_unchecked())
}
@ -113,7 +113,7 @@ impl HashEngine {
for (val, ret_bytes) in self.h.iter().zip(ret.chunks_exact_mut(4)) {
ret_bytes.copy_from_slice(&val.to_be_bytes());
}
Midstate { bytes: ret, length: self.length }
Midstate { bytes: ret, bytes_hashed: self.bytes_hashed }
}
// Does not check that `HashEngine::can_extract_midstate`.
@ -121,7 +121,7 @@ impl HashEngine {
fn midstate_unchecked(&self) -> Midstate {
let mut ret = [0; 32];
ret.copy_from_slice(&self.buffer[..32]);
Midstate { bytes: ret, length: self.length }
Midstate { bytes: ret, bytes_hashed: self.bytes_hashed }
}
}
@ -132,7 +132,7 @@ impl Default for HashEngine {
impl crate::HashEngine for HashEngine {
const BLOCK_SIZE: usize = 64;
fn n_bytes_hashed(&self) -> usize { self.length }
fn n_bytes_hashed(&self) -> u64 { self.bytes_hashed }
engine_input_impl!();
}
@ -177,7 +177,7 @@ pub struct Midstate {
bytes: [u8; 32],
/// Number of bytes hashed to achieve this midstate.
// INVARIANT must always be a multiple of 64.
length: usize,
bytes_hashed: u64,
}
impl Midstate {
@ -186,19 +186,19 @@ impl Midstate {
/// # Panics
///
/// Panics if `bytes_hashed` is not a multiple of 64.
pub const fn new(state: [u8; 32], bytes_hashed: usize) -> Self {
pub const fn new(state: [u8; 32], bytes_hashed: u64) -> Self {
if bytes_hashed % 64 != 0 {
panic!("bytes hashed is not a multiple of 64");
}
Midstate { bytes: state, length: bytes_hashed }
Midstate { bytes: state, bytes_hashed }
}
/// Deconstructs the [`Midstate`], returning the underlying byte array and number of bytes hashed.
pub const fn as_parts(&self) -> (&[u8; 32], usize) { (&self.bytes, self.length) }
pub const fn as_parts(&self) -> (&[u8; 32], u64) { (&self.bytes, self.bytes_hashed) }
/// Deconstructs the [`Midstate`], returning the underlying byte array and number of bytes hashed.
pub const fn into_parts(self) -> ([u8; 32], usize) { (self.bytes, self.length) }
pub const fn into_parts(self) -> ([u8; 32], u64) { (self.bytes, self.bytes_hashed) }
/// Creates midstate for tagged hashes.
///
@ -222,7 +222,7 @@ impl fmt::Debug for Midstate {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Midstate")
.field("bytes", &self.bytes.as_hex())
.field("length", &self.length)
.field("length", &self.bytes_hashed)
.finish()
}
}
@ -235,7 +235,7 @@ impl convert::AsRef<[u8]> for Midstate {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MidstateError {
/// The invalid number of bytes hashed.
invalid_length: usize,
invalid_n_bytes_hashed: u64,
}
impl fmt::Display for MidstateError {
@ -243,7 +243,7 @@ impl fmt::Display for MidstateError {
write!(
f,
"invalid number of bytes hashed {} (should have been a multiple of 64)",
self.invalid_length
self.invalid_n_bytes_hashed
)
}
}
@ -485,7 +485,7 @@ impl Midstate {
output[i * 4 + 3] = (state[i + 0] >> 0) as u8;
i += 1;
}
Midstate { bytes: output, length: bytes.len() }
Midstate { bytes: output, bytes_hashed: bytes.len() as u64 }
}
}
@ -996,7 +996,7 @@ mod tests {
let mut midstate_engine =
sha256::HashEngine::from_midstate(engine.midstate_unchecked());
assert_eq!(engine.h, midstate_engine.h);
assert_eq!(engine.length, midstate_engine.length);
assert_eq!(engine.bytes_hashed, midstate_engine.bytes_hashed);
engine.input(data);
midstate_engine.input(data);
assert_eq!(engine.h, midstate_engine.h);
@ -1037,7 +1037,7 @@ mod tests {
assert_eq!(
Hash::hash(bytes),
Hash::hash_unoptimized(bytes),
"hashes don't match for length {}",
"hashes don't match for n_bytes_hashed {}",
i + 1
);
}

View File

@ -29,7 +29,7 @@ impl Default for HashEngine {
impl crate::HashEngine for HashEngine {
const BLOCK_SIZE: usize = 64; // Same as sha256::HashEngine::BLOCK_SIZE;
fn input(&mut self, data: &[u8]) { self.0.input(data) }
fn n_bytes_hashed(&self) -> usize { self.0.n_bytes_hashed() }
fn n_bytes_hashed(&self) -> u64 { self.0.n_bytes_hashed() }
}
fn from_engine(e: HashEngine) -> Hash {

View File

@ -35,7 +35,7 @@ impl Default for HashEngine {
impl crate::HashEngine for HashEngine {
const BLOCK_SIZE: usize = sha512::BLOCK_SIZE;
fn n_bytes_hashed(&self) -> usize { self.0.n_bytes_hashed() }
fn n_bytes_hashed(&self) -> u64 { self.0.n_bytes_hashed() }
fn input(&mut self, inp: &[u8]) { self.0.input(inp); }
}

View File

@ -6,7 +6,7 @@ use core::cmp;
use core::ops::Index;
use core::slice::SliceIndex;
use crate::HashEngine as _;
use crate::{incomplete_block_len, HashEngine as _};
crate::internal_macros::hash_type! {
512,
@ -17,20 +17,20 @@ crate::internal_macros::hash_type! {
#[cfg(not(hashes_fuzz))]
pub(crate) fn from_engine(mut e: HashEngine) -> Hash {
// pad buffer with a single 1-bit then all 0s, until there are exactly 16 bytes remaining
let data_len = e.length as u64;
let n_bytes_hashed = e.bytes_hashed;
let zeroes = [0; BLOCK_SIZE - 16];
e.input(&[0x80]);
if e.length % BLOCK_SIZE > zeroes.len() {
if incomplete_block_len(&e) > zeroes.len() {
e.input(&zeroes);
}
let pad_length = zeroes.len() - (e.length % BLOCK_SIZE);
let pad_length = zeroes.len() - incomplete_block_len(&e);
e.input(&zeroes[..pad_length]);
debug_assert_eq!(e.length % BLOCK_SIZE, zeroes.len());
debug_assert_eq!(incomplete_block_len(&e), zeroes.len());
e.input(&[0; 8]);
e.input(&(8 * data_len).to_be_bytes());
debug_assert_eq!(e.length % BLOCK_SIZE, 0);
e.input(&(8 * n_bytes_hashed).to_be_bytes());
debug_assert_eq!(incomplete_block_len(&e), 0);
Hash(e.midstate())
}
@ -48,7 +48,7 @@ pub(crate) const BLOCK_SIZE: usize = 128;
#[derive(Clone)]
pub struct HashEngine {
h: [u64; 8],
length: usize,
bytes_hashed: u64,
buffer: [u8; BLOCK_SIZE],
}
@ -61,7 +61,7 @@ impl HashEngine {
0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
],
length: 0,
bytes_hashed: 0,
buffer: [0; BLOCK_SIZE],
}
}
@ -96,7 +96,7 @@ impl HashEngine {
0x22312194fc2bf72c, 0x9f555fa3c84c64c2, 0x2393b86b6f53b151, 0x963877195940eabd,
0x96283ee2a88effe3, 0xbe5e1e2553863992, 0x2b0199fc2c85b8aa, 0x0eb72ddc81c52ca2,
],
length: 0,
bytes_hashed: 0,
buffer: [0; BLOCK_SIZE],
}
}
@ -109,7 +109,7 @@ impl HashEngine {
0xcbbb9d5dc1059ed8, 0x629a292a367cd507, 0x9159015a3070dd17, 0x152fecd8f70e5939,
0x67332667ffc00b31, 0x8eb44a8768581511, 0xdb0c2e0d64f98fa7, 0x47b5481dbefa4fa4,
],
length: 0,
bytes_hashed: 0,
buffer: [0; BLOCK_SIZE],
}
}
@ -118,7 +118,7 @@ impl HashEngine {
impl crate::HashEngine for HashEngine {
const BLOCK_SIZE: usize = 128;
fn n_bytes_hashed(&self) -> usize { self.length }
fn n_bytes_hashed(&self) -> u64 { self.bytes_hashed }
engine_input_impl!();
}

View File

@ -45,7 +45,7 @@ impl Default for HashEngine {
impl crate::HashEngine for HashEngine {
const BLOCK_SIZE: usize = sha512::BLOCK_SIZE;
fn n_bytes_hashed(&self) -> usize { self.0.n_bytes_hashed() }
fn n_bytes_hashed(&self) -> u64 { self.0.n_bytes_hashed() }
fn input(&mut self, inp: &[u8]) { self.0.input(inp); }
}

View File

@ -81,7 +81,7 @@ pub struct State {
pub struct HashEngine {
k0: u64,
k1: u64,
length: usize, // how many bytes we've processed
bytes_hashed: u64, // how many bytes we've processed
state: State, // hash State
tail: u64, // unprocessed bytes le
ntail: usize, // how many bytes in tail are valid
@ -94,7 +94,7 @@ impl HashEngine {
HashEngine {
k0,
k1,
length: 0,
bytes_hashed: 0,
state: State {
v0: k0 ^ 0x736f6d6570736575,
v1: k1 ^ 0x646f72616e646f6d,
@ -129,16 +129,16 @@ impl crate::HashEngine for HashEngine {
#[inline]
fn input(&mut self, msg: &[u8]) {
let length = msg.len();
self.length += length;
let bytes_hashed = msg.len();
self.bytes_hashed += bytes_hashed as u64; // Cast usize to u64 is ok.
let mut needed = 0;
if self.ntail != 0 {
needed = 8 - self.ntail;
self.tail |= unsafe { u8to64_le(msg, 0, cmp::min(length, needed)) } << (8 * self.ntail);
if length < needed {
self.ntail += length;
self.tail |= unsafe { u8to64_le(msg, 0, cmp::min(bytes_hashed, needed)) } << (8 * self.ntail);
if bytes_hashed < needed {
self.ntail += bytes_hashed;
return;
} else {
self.state.v3 ^= self.tail;
@ -149,7 +149,7 @@ impl crate::HashEngine for HashEngine {
}
// Buffered tail is now flushed, process new input.
let len = length - needed;
let len = bytes_hashed - needed;
let left = len & 0x7;
let mut i = needed;
@ -167,7 +167,7 @@ impl crate::HashEngine for HashEngine {
self.ntail = left;
}
fn n_bytes_hashed(&self) -> usize { self.length }
fn n_bytes_hashed(&self) -> u64 { self.bytes_hashed }
}
impl Hash {
@ -190,7 +190,7 @@ impl Hash {
pub fn from_engine_to_u64(e: HashEngine) -> u64 {
let mut state = e.state;
let b: u64 = ((e.length as u64 & 0xff) << 56) | e.tail;
let b: u64 = ((e.bytes_hashed & 0xff) << 56) | e.tail;
state.v3 ^= b;
HashEngine::c_rounds(&mut state);

View File

@ -70,15 +70,16 @@ macro_rules! engine_input_impl(
() => (
#[cfg(not(hashes_fuzz))]
fn input(&mut self, mut inp: &[u8]) {
while !inp.is_empty() {
let buf_idx = self.length % <Self as crate::HashEngine>::BLOCK_SIZE;
let buf_idx = $crate::incomplete_block_len(self);
let rem_len = <Self as crate::HashEngine>::BLOCK_SIZE - buf_idx;
let write_len = cmp::min(rem_len, inp.len());
self.buffer[buf_idx..buf_idx + write_len]
.copy_from_slice(&inp[..write_len]);
self.length += write_len;
if self.length % <Self as crate::HashEngine>::BLOCK_SIZE == 0 {
self.bytes_hashed += write_len as u64;
if $crate::incomplete_block_len(self) == 0 {
self.process_block();
}
inp = &inp[write_len..];