mirror of
https://github.com/RustCrypto/hashes.git
synced 2026-01-25 04:18:20 +00:00
FSB hash function (#256)
This commit is contained in:
2
.github/workflows/workspace.yml
vendored
2
.github/workflows/workspace.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: 1.41.0 # MSRV
|
||||
toolchain: 1.47.0
|
||||
components: clippy
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
11
Cargo.lock
generated
11
Cargo.lock
generated
@@ -85,6 +85,17 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fsb"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"digest",
|
||||
"hex-literal",
|
||||
"opaque-debug",
|
||||
"whirlpool",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.4"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"fsb",
|
||||
"blake2",
|
||||
"gost94",
|
||||
"groestl",
|
||||
|
||||
3
fsb/.gitignore
vendored
Normal file
3
fsb/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
/target
|
||||
.idea
|
||||
Cargo.lock
|
||||
20
fsb/Cargo.toml
Normal file
20
fsb/Cargo.toml
Normal file
@@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "fsb"
|
||||
version = "0.1.0"
|
||||
description = "FSB hash function"
|
||||
authors = ["RustCrypto Developers"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
readme = "README.md"
|
||||
edition = "2018"
|
||||
repository = "https://github.com/RustCrypto/hashes"
|
||||
keywords = ["crypto", "fsb", "hash", "digest"]
|
||||
categories = ["cryptography", "no-std"]
|
||||
|
||||
[dependencies]
|
||||
whirlpool = { version = "0.9", path = "../whirlpool", default-features = false }
|
||||
digest = "0.9"
|
||||
block-buffer = { version = "0.9", features = ["block-padding"] }
|
||||
opaque-debug = "0.3"
|
||||
|
||||
[dev-dependencies]
|
||||
hex-literal = "0.2"
|
||||
56
fsb/README.md
Normal file
56
fsb/README.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# RustCrypto: FSB
|
||||
|
||||
[![crate][crate-image]][crate-link]
|
||||
[![Docs][docs-image]][docs-link]
|
||||
[![Build Status][build-image]][build-link]
|
||||
![Apache2/MIT licensed][license-image]
|
||||
![Rust Version][rustc-image]
|
||||
[![Project Chat][chat-image]][chat-link]
|
||||
|
||||
Pure Rust implementation of the [FSB hash function][1] family.
|
||||
|
||||
[Documentation][docs-link]
|
||||
|
||||
## Minimum Supported Rust Version
|
||||
|
||||
Rust **1.41** or higher.
|
||||
|
||||
Minimum supported Rust version can be changed in the future, but it will be
|
||||
done with a minor version bump.
|
||||
|
||||
## SemVer Policy
|
||||
|
||||
- All on-by-default features of this library are covered by SemVer
|
||||
- MSRV is considered exempt from SemVer as noted above
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of:
|
||||
|
||||
* [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* [MIT license](http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
|
||||
dual licensed as above, without any additional terms or conditions.
|
||||
|
||||
[//]: # (badges)
|
||||
|
||||
[crate-image]: https://img.shields.io/crates/v/fsb.svg
|
||||
[crate-link]: https://crates.io/crates/fsb
|
||||
[docs-image]: https://docs.rs/fsb/badge.svg
|
||||
[docs-link]: https://docs.rs/fsb/
|
||||
[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg
|
||||
[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg
|
||||
[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260041-hashes
|
||||
[rustc-image]: https://img.shields.io/badge/rustc-1.41+-blue.svg
|
||||
[build-image]: https://github.com/RustCrypto/hashes/workflows/fsb/badge.svg?branch=master
|
||||
[build-link]: https://github.com/RustCrypto/hashes/actions?query=workflow%3Afsb
|
||||
|
||||
[//]: # (general links)
|
||||
|
||||
[1]: https://www.paris.inria.fr/secret/CBCrypto/index.php?pg=fsb
|
||||
75
fsb/src/lib.rs
Normal file
75
fsb/src/lib.rs
Normal file
@@ -0,0 +1,75 @@
|
||||
//! An implementation of the [FSB][1] cryptographic hash algorithms.
|
||||
//! The FSB hash function was one of the submissions to SHA-3,
|
||||
//! the cryptographic hash algorithm competition organized by the NIST.
|
||||
//!
|
||||
//! There are 5 standard versions of the FSB hash function:
|
||||
//!
|
||||
//! * `FSB-160`
|
||||
//! * `FSB-224`
|
||||
//! * `FSB-256`
|
||||
//! * `FSB-384`
|
||||
//! * `FSB-512`
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! Output size of FSB-256 is fixed, so its functionality is usually
|
||||
//! accessed via the `Digest` trait:
|
||||
//!
|
||||
//! ```
|
||||
//! use hex_literal::hex;
|
||||
//! use fsb::{Digest, Fsb256};
|
||||
//!
|
||||
//! // create a FSB-256 object
|
||||
//! let mut hasher = Fsb256::new();
|
||||
//!
|
||||
//! // write input message
|
||||
//! hasher.update(b"hello");
|
||||
//!
|
||||
//! // read hash digest
|
||||
//! let result = hasher.finalize();
|
||||
//!
|
||||
//! assert_eq!(result[..], hex!("
|
||||
//! 0f036dc3761aed2cba9de586a85976eedde6fa8f115c0190763decc02f28edbc
|
||||
//! ")[..]);
|
||||
//! ```
|
||||
//! Also see [RustCrypto/hashes][2] readme.
|
||||
//!
|
||||
//! [1]: https://www.paris.inria.fr/secret/CBCrypto/index.php?pg=fsb
|
||||
//! [2]: https://github.com/RustCrypto/hashes
|
||||
|
||||
// #![no_std]
|
||||
#![doc(
|
||||
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
|
||||
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
|
||||
)]
|
||||
#![deny(unsafe_code)]
|
||||
#![warn(missing_docs, rust_2018_idioms)]
|
||||
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
extern crate std;
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
extern crate alloc;
|
||||
use alloc::vec;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
mod pi;
|
||||
|
||||
pub use digest::{self, Digest};
|
||||
use whirlpool::Whirlpool;
|
||||
|
||||
use core::convert::TryInto;
|
||||
|
||||
use block_buffer::BlockBuffer;
|
||||
use digest::{generic_array::GenericArray};
|
||||
use digest::{BlockInput, FixedOutputDirty, Reset, Update};
|
||||
use crate::pi::PI;
|
||||
|
||||
fsb_impl!(Fsb160, 160, U60, U20, 5 << 18, 80, 640, 653, 1120, "FSB-160 hash function.");
|
||||
fsb_impl!(Fsb224, 224, U84, U28, 7 << 18, 112, 896, 907, 1568, "FSB-224 hash function.");
|
||||
fsb_impl!(Fsb256, 256, U96, U32, 1 << 21, 128, 1024, 1061, 1792, "FSB-256 hash function.");
|
||||
fsb_impl!(Fsb384, 384, U115, U48, 23 << 16, 184, 1472, 1483, 2392, "FSB-384 hash function.");
|
||||
fsb_impl!(Fsb512, 512, U155, U64, 31 << 16, 248, 1984, 1987, 3224, "FSB-512 hash function.");
|
||||
337
fsb/src/macros.rs
Normal file
337
fsb/src/macros.rs
Normal file
@@ -0,0 +1,337 @@
|
||||
macro_rules! fsb_impl {
|
||||
(
|
||||
$state:ident, $state_num:expr, $blocksize:ident, $outputsize:ident, $n:expr, $w:expr,
|
||||
$r:expr, $p:expr, $s:expr, $doc:expr
|
||||
) => {
|
||||
use digest::consts::{$blocksize, $outputsize};
|
||||
|
||||
#[derive(Clone)]
|
||||
#[doc=$doc]
|
||||
pub struct $state {
|
||||
/// bit size of the message till the current moment (the bit size is represented by a 64 bit
|
||||
/// number)
|
||||
bit_length: u64,
|
||||
/// size of the message being processed
|
||||
buffer: BlockBuffer<$blocksize>,
|
||||
/// value of the input vector
|
||||
hash: [u8; $r / 8],
|
||||
}
|
||||
|
||||
impl $state {
|
||||
// constants
|
||||
const SIZE_OUTPUT_COMPRESS: usize = $r / 8;
|
||||
const SIZE_INPUT_COMPRESS: usize = $s / 8;
|
||||
const HASH_OUTPUT_SIZE: usize = $state_num / 8;
|
||||
const SIZE_MSG_CHUNKS: usize = Self::SIZE_INPUT_COMPRESS - Self::SIZE_OUTPUT_COMPRESS;
|
||||
const SIZE_VECTORS: usize = $p / 8 + 1;
|
||||
const SHIFT: u8 = 8 - ($p % 8) as u8;
|
||||
|
||||
fn update_len(&mut self, len: u64) {
|
||||
self.bit_length += len * 8;
|
||||
}
|
||||
|
||||
fn finalize_inner(&mut self) {
|
||||
let hash = &mut self.hash;
|
||||
let pos = self.buffer.position();
|
||||
if pos < Self::SIZE_MSG_CHUNKS - 8 {
|
||||
let mut padding = vec![0; Self::SIZE_MSG_CHUNKS - pos - 8];
|
||||
padding[0] = 128u8;
|
||||
padding.extend_from_slice(&Self::helper_transform_usize(self.bit_length));
|
||||
self.buffer
|
||||
.input_block(&padding, |b| Self::compress(hash, Self::convert(b)));
|
||||
} else {
|
||||
let mut padding = vec![0; Self::SIZE_MSG_CHUNKS - pos];
|
||||
padding[0] = 128u8;
|
||||
self.buffer
|
||||
.input_block(&padding, |b| Self::compress(hash, Self::convert(b)));
|
||||
let mut second_padding = vec![0; Self::SIZE_MSG_CHUNKS - 8];
|
||||
second_padding
|
||||
.extend_from_slice(&Self::helper_transform_usize(self.bit_length));
|
||||
self.buffer
|
||||
.input_block(&second_padding, |b| Self::compress(hash, Self::convert(b)));
|
||||
}
|
||||
}
|
||||
|
||||
fn define_iv(index: usize) -> [u8; Self::SIZE_VECTORS] {
|
||||
let mut subset_pi: [u8; Self::SIZE_VECTORS] = [0u8; Self::SIZE_VECTORS];
|
||||
subset_pi.copy_from_slice(
|
||||
&PI[index * Self::SIZE_VECTORS..(index + 1) * Self::SIZE_VECTORS],
|
||||
);
|
||||
|
||||
// Now we change the last byte of the vector. We shift right and left, basically to
|
||||
// replace the last `shift` bits by zero.
|
||||
if let Some(last) = subset_pi.last_mut() {
|
||||
*last >>= Self::SHIFT;
|
||||
*last <<= Self::SHIFT;
|
||||
}
|
||||
|
||||
subset_pi
|
||||
}
|
||||
|
||||
/// Vector XORing. Given the s input bits of the function, we derive a set of w indexes
|
||||
/// $(W_i)_{i\in[0;w-1]}$ between $0$ and $n - 1$. The value of each $W_i$ is computed
|
||||
/// from the inputs bits like this:
|
||||
/// $W_i = i \times (n / w) + IV_i + M_i \times 2^{r / w}.
|
||||
fn computing_W_indices(
|
||||
input_vector: &[u8; Self::SIZE_OUTPUT_COMPRESS],
|
||||
message: &[u8; Self::SIZE_MSG_CHUNKS],
|
||||
) -> [u32; $w] {
|
||||
let mut W_indices: [u32; $w] = [0; $w];
|
||||
let divided_message: [u8; $w] = Self::dividing_bits(message, ($s - $r) / $w);
|
||||
for i in 0..($w) {
|
||||
let message_i = divided_message[i] as u32;
|
||||
|
||||
W_indices[i] = (i * $n / $w) as u32
|
||||
+ input_vector[i] as u32
|
||||
+ (message_i << ($r / $w) as u8);
|
||||
}
|
||||
|
||||
W_indices
|
||||
}
|
||||
|
||||
/// This function servers the purpose presented in table 3, of breaking a bit array into
|
||||
/// batches of size not multiple of 8. Note that the IV will be broken always in size 8, which
|
||||
/// is quite convenient. Also, the only numbers we'll have to worry for are 5 and 6.
|
||||
fn dividing_bits(
|
||||
input_bits: &[u8; Self::SIZE_MSG_CHUNKS],
|
||||
size_batches: usize,
|
||||
) -> [u8; $w] {
|
||||
if size_batches != 5usize && size_batches != 6usize {
|
||||
panic!(
|
||||
"Expecting batches of size 5 or 6. Other values do not follow \
|
||||
the standard specification"
|
||||
)
|
||||
}
|
||||
|
||||
let mut new_bits = [0u8; $w];
|
||||
let shifting_factor = (8 - size_batches) as u8;
|
||||
for (i, new_bit) in new_bits.iter_mut().enumerate().take($w - 1) {
|
||||
let position = i * size_batches;
|
||||
let initial_byte = position / 8;
|
||||
let initial_bit = position % 8;
|
||||
let switch = (initial_bit + size_batches - 1) / 8; // check if we use the next byte
|
||||
|
||||
if switch == 1 {
|
||||
*new_bit = (input_bits[initial_byte] << initial_bit as u8
|
||||
| input_bits[initial_byte + 1] >> (8 - initial_bit as u8))
|
||||
>> shifting_factor;
|
||||
} else {
|
||||
*new_bit =
|
||||
(input_bits[initial_byte] << initial_bit as u8) >> shifting_factor;
|
||||
}
|
||||
}
|
||||
new_bits[$w - 1] =
|
||||
(input_bits[Self::SIZE_MSG_CHUNKS - 1] << shifting_factor) >> shifting_factor;
|
||||
|
||||
new_bits
|
||||
}
|
||||
|
||||
/// This function outputs r bits, which are used to chain to the next iteration.
|
||||
fn compress(
|
||||
hash: &mut [u8; Self::SIZE_OUTPUT_COMPRESS],
|
||||
message_block: &[u8; Self::SIZE_MSG_CHUNKS],
|
||||
) {
|
||||
let mut initial_vector = [0u8; Self::SIZE_OUTPUT_COMPRESS];
|
||||
|
||||
let w_indices = Self::computing_W_indices(hash, message_block);
|
||||
for w_index in w_indices.iter() {
|
||||
let chosen_vec = w_index / $r as u32;
|
||||
let shift_value = w_index % $r as u32;
|
||||
let mut vector = Self::define_iv(chosen_vec as usize);
|
||||
let truncated = Self::shift_and_truncate(&mut vector, shift_value);
|
||||
|
||||
initial_vector
|
||||
.iter_mut()
|
||||
.zip(truncated.iter())
|
||||
.for_each(|(x1, x2)| *x1 ^= *x2);
|
||||
}
|
||||
|
||||
*hash = initial_vector;
|
||||
}
|
||||
|
||||
fn final_compression(
|
||||
initial_vector: [u8; Self::SIZE_OUTPUT_COMPRESS],
|
||||
) -> [u8; Self::HASH_OUTPUT_SIZE] {
|
||||
// Now we use Whirpool
|
||||
let mut result = [0u8; Self::HASH_OUTPUT_SIZE];
|
||||
let mut hasher = Whirlpool::new();
|
||||
|
||||
Update::update(&mut hasher, &initial_vector);
|
||||
result.copy_from_slice(&hasher.finalize()[..Self::HASH_OUTPUT_SIZE]);
|
||||
result
|
||||
}
|
||||
|
||||
fn shift_and_truncate(
|
||||
array: &mut [u8; Self::SIZE_VECTORS],
|
||||
shift_value: u32,
|
||||
) -> [u8; Self::SIZE_OUTPUT_COMPRESS] {
|
||||
let array_len = array.len();
|
||||
let bits_in_cue = ($p % 8) as u8;
|
||||
let mut truncated = [0u8; Self::SIZE_OUTPUT_COMPRESS];
|
||||
|
||||
if shift_value == 0 {
|
||||
array[..Self::SIZE_OUTPUT_COMPRESS]
|
||||
.try_into()
|
||||
.expect("SIZE_VECTORS is always bigger than SIZE_OUTPUT_COMPRESS")
|
||||
} else if shift_value <= (bits_in_cue as u32) {
|
||||
let bytes_to_shift = 1;
|
||||
let starting_byte = (array_len - bytes_to_shift) as usize;
|
||||
|
||||
truncated[0] = array[starting_byte] << (bits_in_cue - shift_value as u8);
|
||||
truncated[0] ^= array[0] >> shift_value;
|
||||
for position in 1..Self::SIZE_OUTPUT_COMPRESS {
|
||||
truncated[position] ^= array[position - 1] >> (8 - shift_value);
|
||||
truncated[position] ^= array[position] << shift_value;
|
||||
}
|
||||
|
||||
truncated
|
||||
} else {
|
||||
// First we need to decide which is the last byte and bit that will go to the first position.
|
||||
// Then, we build our truncated array from there. Recall that the last byte is not complete,
|
||||
// and we have a total of P % 8 hanging bits (this will always happen).
|
||||
let bytes_to_shift =
|
||||
(((shift_value - bits_in_cue as u32 - 1) / 8) + 2) as usize;
|
||||
// So then, the starting byte will be:
|
||||
let starting_byte = (array_len - bytes_to_shift) as usize;
|
||||
|
||||
// And the starting bit:
|
||||
let remaining_bits = ((shift_value - bits_in_cue as u32) % 8) as u8;
|
||||
|
||||
if remaining_bits != 0 {
|
||||
for position in 0..(bytes_to_shift - 1) {
|
||||
truncated[position] = array[starting_byte + position]
|
||||
<< (8 - remaining_bits)
|
||||
| array[starting_byte + position + 1] >> remaining_bits;
|
||||
}
|
||||
|
||||
// The last case is different, as we don't know if there are sufficient bits in the cue to fill
|
||||
// up a full byte. We have three cases: 1. where P % 8 (bits_in_cue) is larger than
|
||||
// starting_bit, 2. where it is equal, and 3. where it is smaller. But we can fill the bits, and
|
||||
// then decide how to proceed depending on the difference.
|
||||
let difference = bits_in_cue.checked_sub(8 - remaining_bits);
|
||||
|
||||
match difference {
|
||||
Some(x) => {
|
||||
if x > 0 {
|
||||
// the next position takes starting_bits from the byte with the remaining zeros, and
|
||||
// `difference` from the first byte. Then we iterate by shifting 8 - difference bits.
|
||||
truncated[bytes_to_shift - 1] ^= array
|
||||
[starting_byte + bytes_to_shift - 1]
|
||||
<< (bits_in_cue - x);
|
||||
truncated[bytes_to_shift - 1] ^= array[0] >> x;
|
||||
for (index, position) in
|
||||
(bytes_to_shift..Self::SIZE_OUTPUT_COMPRESS).enumerate()
|
||||
{
|
||||
truncated[position] ^= array[index] << (8 - x);
|
||||
truncated[position] ^= array[index + 1] >> x;
|
||||
}
|
||||
} else {
|
||||
for (index, position) in ((bytes_to_shift - 1)
|
||||
..Self::SIZE_OUTPUT_COMPRESS)
|
||||
.enumerate()
|
||||
{
|
||||
truncated[position] = array[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let positive_diff = (8 - remaining_bits) - bits_in_cue;
|
||||
// we need to fill the remainder with bits of the next byte.
|
||||
truncated[bytes_to_shift - 2] ^= array[0] >> (8 - positive_diff);
|
||||
for (index, position) in
|
||||
((bytes_to_shift - 1)..Self::SIZE_OUTPUT_COMPRESS).enumerate()
|
||||
{
|
||||
truncated[position] ^= array[index] << positive_diff;
|
||||
truncated[position] ^= array[index + 1] >> (8 - positive_diff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
truncated
|
||||
} else {
|
||||
truncated[..bytes_to_shift].clone_from_slice(
|
||||
&array[starting_byte..(starting_byte + bytes_to_shift)],
|
||||
);
|
||||
|
||||
// we need to fill the remainder with bits of the next byte.
|
||||
truncated[bytes_to_shift - 1] ^= array[0] >> bits_in_cue;
|
||||
for (index, position) in
|
||||
(bytes_to_shift..Self::SIZE_OUTPUT_COMPRESS).enumerate()
|
||||
{
|
||||
truncated[position] ^= array[index] << (8 - bits_in_cue);
|
||||
truncated[position] ^= array[index + 1] >> bits_in_cue;
|
||||
}
|
||||
truncated
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// I'm trying to avoid use unsafe code for this transformation. We are certain that the bit
|
||||
// size of the buffer can be represented in 8 bytes.
|
||||
fn helper_transform_usize(x: u64) -> [u8; 8] {
|
||||
let b1: u8 = ((x >> 56) & 0xff) as u8;
|
||||
let b2: u8 = ((x >> 48) & 0xff) as u8;
|
||||
let b3: u8 = ((x >> 40) & 0xff) as u8;
|
||||
let b4: u8 = ((x >> 32) & 0xff) as u8;
|
||||
let b5: u8 = ((x >> 24) & 0xff) as u8;
|
||||
let b6: u8 = ((x >> 16) & 0xff) as u8;
|
||||
let b7: u8 = ((x >> 8) & 0xff) as u8;
|
||||
let b8: u8 = (x & 0xff) as u8;
|
||||
[b1, b2, b3, b4, b5, b6, b7, b8]
|
||||
}
|
||||
|
||||
fn convert(block: &GenericArray<u8, $blocksize>) -> &[u8; Self::SIZE_MSG_CHUNKS] {
|
||||
#[allow(unsafe_code)]
|
||||
unsafe {
|
||||
&*(block.as_ptr() as *const [u8; Self::SIZE_MSG_CHUNKS])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for $state {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
bit_length: 0u64,
|
||||
buffer: BlockBuffer::default(),
|
||||
hash: [0u8; $r / 8],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockInput for $state {
|
||||
type BlockSize = $blocksize;
|
||||
}
|
||||
|
||||
impl Update for $state {
|
||||
fn update(&mut self, input: impl AsRef<[u8]>) {
|
||||
let input = input.as_ref();
|
||||
self.update_len(input.len() as u64);
|
||||
|
||||
let hash = &mut self.hash;
|
||||
self.buffer
|
||||
.input_block(input, |b| $state::compress(hash, $state::convert(b)));
|
||||
}
|
||||
}
|
||||
|
||||
impl FixedOutputDirty for $state {
|
||||
type OutputSize = $outputsize;
|
||||
|
||||
fn finalize_into_dirty(&mut self, out: &mut GenericArray<u8, Self::OutputSize>) {
|
||||
self.finalize_inner();
|
||||
let final_whirpool = $state::final_compression(self.hash);
|
||||
out.copy_from_slice(&final_whirpool)
|
||||
}
|
||||
}
|
||||
|
||||
impl Reset for $state {
|
||||
fn reset(&mut self) {
|
||||
self.buffer.reset();
|
||||
for v in self.hash.iter_mut() {
|
||||
*v = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
opaque_debug::implement!($state);
|
||||
digest::impl_write!($state);
|
||||
};
|
||||
}
|
||||
17027
fsb/src/pi.rs
Normal file
17027
fsb/src/pi.rs
Normal file
File diff suppressed because it is too large
Load Diff
86
fsb/tests/lib.rs
Normal file
86
fsb/tests/lib.rs
Normal file
@@ -0,0 +1,86 @@
|
||||
use hex_literal::hex;
|
||||
|
||||
use digest::Digest;
|
||||
use fsb::*;
|
||||
|
||||
#[test]
|
||||
fn main() {
|
||||
let msg_1 = b"hello";
|
||||
let msg_2 = b"The quick brown fox jumps over the lazy dog";
|
||||
let msg_3 = b"tiriri tralala potompompom";
|
||||
|
||||
assert_eq!(
|
||||
Fsb160::digest(msg_1)[..],
|
||||
hex!("6e8ce7998e4c46a4ca7c5e8f6498a5778140d14b")[..],
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Fsb160::digest(msg_2)[..],
|
||||
hex!("a25f6e24c6fb67533f0a25233ac5cc09d5793e8a")[..]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Fsb160::digest(msg_3)[..],
|
||||
hex!("bfbd2f301a8ffbcfb60f3964d96d07e6569824f9")[..]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Fsb224::digest(msg_1)[..],
|
||||
hex!("5b04d5f3c350d00f8815f018d21a2e7289bc6993b4fa167976962537")[..]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Fsb224::digest(msg_2)[..],
|
||||
hex!("1dd28d92cad63335fcca4c64a5e1133ccaa8c3e6083ad15591280701")[..]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Fsb224::digest(msg_3)[..],
|
||||
hex!("bd9cc65169789ab20fbba27910a9f5323d0559f107eff3c55656dd23")[..]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Fsb256::digest(msg_1)[..],
|
||||
hex!("0f036dc3761aed2cba9de586a85976eedde6fa8f115c0190763decc02f28edbc")[..]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Fsb256::digest(msg_2)[..],
|
||||
hex!("a0751229aac5aeba6aeb1c0533988302e5084bb11029e7bb0ada7a653491df24")[..]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Fsb256::digest(msg_3)[..],
|
||||
hex!("f997ac523044618f2837407ad76bf41a194bb558cf50ea1c64b379be2f5f2b5e")[..]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Fsb384::digest(msg_1)[..],
|
||||
hex!("010d14a04da89df22685138b6b7795501ebdc109b6c714364126fcb46a0b570a9d714bc992455f8cf2099c8750cdb90b")[..]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Fsb384::digest(msg_2)[..],
|
||||
hex!("4983ecfa3930e3cf61ac4c82695c01a394016b39cf22b5d6dcba447ef8cbcda46ac341ccf5835f331fed0abe73e9bf1c")[..]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Fsb384::digest(msg_3)[..],
|
||||
hex!("0597e317f2a3f311db2485f0b8335607e6bcc6f918d07f6b0dc14bc044c558a9bcd0f5f346ad85bb043ff097f43f4f95")[..]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Fsb512::digest(msg_1)[..],
|
||||
hex!("0c6bb476d9727b90a1f063435e8d611aacdc904e9680fe585b65442f2a3ac5043a3979ff252adf6cc9d34ef0b179a90ae2f2e8789f8797bff2426c90a58fb28b")[..]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Fsb512::digest(msg_2)[..],
|
||||
hex!("6f87b9dc051330bfb0dd7ad35c05d6a2040e9a6110b06886368934d6ae25694fd9790b1bf1086af9da4b15619609b688fa576376f136adbd3b5a51ae1a1f2158")[..]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Fsb512::digest(msg_3)[..],
|
||||
hex!("7dd5255dafac0796df851d278eb70f554a539cc3dfdfe0a3d73e46df1ab51c029d3634db022fcd032ee8376ea777e34af118821fb1ff2b34b7378e517eacdc73")[..]
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user