2019-12-03 19:44:30 +01:00
|
|
|
#![cfg_attr(not(feature = "std"), no_std)]
|
|
|
|
|
2019-12-03 17:00:47 +01:00
|
|
|
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
|
|
|
mod avx2;
|
2019-12-03 19:27:28 +01:00
|
|
|
mod platform;
|
2019-12-02 23:30:55 +01:00
|
|
|
mod portable;
|
2019-12-03 00:02:11 +01:00
|
|
|
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
|
|
|
mod sse41;
|
2019-12-02 23:30:55 +01:00
|
|
|
#[cfg(test)]
|
|
|
|
mod test;
|
|
|
|
|
2019-12-03 21:18:08 +01:00
|
|
|
use arrayref::array_ref;
|
|
|
|
use arrayvec::ArrayString;
|
|
|
|
use core::fmt;
|
|
|
|
use platform::Platform;
|
|
|
|
|
2019-12-02 23:30:55 +01:00
|
|
|
/// The default number of bytes in a hash, 32.
|
|
|
|
pub const OUT_LEN: usize = 32;
|
|
|
|
|
|
|
|
/// The number of bytes in a key, 32.
|
|
|
|
pub const KEY_LEN: usize = 32;
|
|
|
|
|
|
|
|
// These are pub for tests and benchmarks. Callers don't need them.
|
|
|
|
#[doc(hidden)]
|
|
|
|
pub const BLOCK_LEN: usize = 64;
|
|
|
|
#[doc(hidden)]
|
|
|
|
pub const CHUNK_LEN: usize = 2048;
|
|
|
|
|
|
|
|
const IV: [u32; 8] = [
|
|
|
|
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19,
|
|
|
|
];
|
|
|
|
|
|
|
|
const MSG_SCHEDULE: [[usize; 16]; 7] = [
|
|
|
|
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
|
|
|
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
|
|
|
|
[11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
|
|
|
|
[7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
|
|
|
|
[9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
|
|
|
|
[2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
|
|
|
|
[12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
|
|
|
|
];
|
|
|
|
|
|
|
|
const CHUNK_OFFSET_DELTAS: &[u64; 16] = &[
|
|
|
|
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,
|
|
|
|
];
|
|
|
|
|
|
|
|
const PARENT_OFFSET_DELTAS: &[u64; 16] = &[0; 16];
|
|
|
|
|
|
|
|
// 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
|
|
|
|
// high and go down.
|
|
|
|
bitflags::bitflags! {
|
|
|
|
struct Flags: u8 {
|
|
|
|
const CHUNK_START = 1 << 0;
|
|
|
|
const CHUNK_END = 1 << 1;
|
|
|
|
const PARENT = 1 << 2;
|
|
|
|
const ROOT = 1 << 3;
|
|
|
|
const KEYED_HASH = 1 << 4;
|
|
|
|
const DERIVE_KEY = 1 << 5;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn offset_low(offset: u64) -> u32 {
|
|
|
|
offset as u32
|
|
|
|
}
|
|
|
|
|
|
|
|
fn offset_high(offset: u64) -> u32 {
|
|
|
|
(offset >> 32) as u32
|
|
|
|
}
|
2019-12-03 19:34:12 +01:00
|
|
|
|
|
|
|
/// A BLAKE3 output of the default size, 32 bytes, which implements
|
|
|
|
/// constant-time equality.
|
|
|
|
#[derive(Clone, Copy)]
|
|
|
|
pub struct Hash([u8; OUT_LEN]);
|
|
|
|
|
|
|
|
impl Hash {
|
|
|
|
pub fn as_bytes(&self) -> &[u8; OUT_LEN] {
|
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn to_hex(&self) -> ArrayString<[u8; 2 * OUT_LEN]> {
|
|
|
|
let mut s = ArrayString::new();
|
|
|
|
let table = b"0123456789abcdef";
|
|
|
|
for &b in self.0.iter() {
|
|
|
|
s.push(table[(b >> 4) as usize] as char);
|
|
|
|
s.push(table[(b & 0xf) as usize] as char);
|
|
|
|
}
|
|
|
|
s
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<[u8; OUT_LEN]> for Hash {
|
|
|
|
fn from(bytes: [u8; OUT_LEN]) -> Self {
|
|
|
|
Self(bytes)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<Hash> for [u8; OUT_LEN] {
|
|
|
|
fn from(hash: Hash) -> Self {
|
|
|
|
hash.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This implementation is constant-time.
|
|
|
|
impl PartialEq for Hash {
|
|
|
|
fn eq(&self, other: &Hash) -> bool {
|
|
|
|
constant_time_eq::constant_time_eq(&self.0[..], &other.0[..])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This implementation is constant-time.
|
|
|
|
impl PartialEq<[u8; OUT_LEN]> for Hash {
|
|
|
|
fn eq(&self, other: &[u8; OUT_LEN]) -> bool {
|
|
|
|
constant_time_eq::constant_time_eq(&self.0[..], other)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Eq for Hash {}
|
|
|
|
|
|
|
|
impl fmt::Debug for Hash {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
write!(f, "Hash(0x{})", self.to_hex())
|
|
|
|
}
|
|
|
|
}
|