Bug 1770894 - Patch redox_users with an empty crate. r=emilio

We don't support redox, so we might as well avoid pulling dependencies
we'll never need and may pull duplicates.

Differential Revision: https://phabricator.services.mozilla.com/D147250
This commit is contained in:
Mike Hommey 2022-05-30 21:37:33 +00:00
parent f9d9d6369f
commit a10c04a7dc
51 changed files with 15 additions and 10392 deletions

38
Cargo.lock generated
View File

@ -498,17 +498,6 @@ dependencies = [
"xpcom",
]
[[package]]
name = "blake2b_simd"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587"
dependencies = [
"arrayref",
"arrayvec 0.5.2",
"constant_time_eq",
]
[[package]]
name = "block"
version = "0.1.6"
@ -764,12 +753,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "constant_time_eq"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]]
name = "cookie"
version = "0.12.0"
@ -4282,14 +4265,7 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
[[package]]
name = "redox_users"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d"
dependencies = [
"getrandom",
"redox_syscall",
"rust-argon2",
]
version = "0.3.999"
[[package]]
name = "regalloc"
@ -4431,18 +4407,6 @@ dependencies = [
"smallvec",
]
[[package]]
name = "rust-argon2"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb"
dependencies = [
"base64 0.13.0",
"blake2b_simd",
"constant_time_eq",
"crossbeam-utils 0.8.8",
]
[[package]]
name = "rust-ini"
version = "0.10.3"

View File

@ -105,6 +105,9 @@ cfg-if = { path = "build/rust/cfg-if" }
# Patch itertools 0.8 to 1.0
itertools = { path = "build/rust/itertools" }
# Patch redox_users to an empty crate
redox_users = { path = "build/rust/redox_users" }
# Patch autocfg to hide rustc output. Workaround for https://github.com/cuviper/autocfg/issues/30
autocfg = { path = "third_party/rust/autocfg" }

View File

@ -0,0 +1,8 @@
[package]
name = "redox_users"
version = "0.3.999"
edition = "2018"
license = "MPL-2.0"
[lib]
path = "lib.rs"

View File

@ -0,0 +1,3 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

View File

@ -1 +0,0 @@
{"files":{"Cargo.toml":"b79e2347297dee32b9483747ee3cf145bd60e8c9d14cc02929287dabde4685ff","README.md":"b90c7a163a0fbff29e2c049953968c8cbfe8aa616d7e36775f0a565761dcc671","src/avx2.rs":"b79f36e3a7442d241e3fdf2ec159866aea31b78883660a63cdc9b1a262f18d10","src/blake2bp.rs":"83577d4a22db3b92030d9bd4563aa9ad440f23c64a6ad5f10a9d709f22d50589","src/guts.rs":"1189cab87b18eaaf2abd5bcb3d7d799c75401a312cee6f1f65fdaad30203eb6f","src/lib.rs":"b0404c81988e4de8d8864437c512937d9e888c681ef4739ef2d5db1650bd8766","src/many.rs":"60d07e4d7ad63949fb5432ad05f7c6a525a3eee39d325f7d4e65e901b466be95","src/portable.rs":"a274acd298a394c014096a8214a0dc1db7439d1e920bd2ad75707fadcc501e10","src/sse41.rs":"58e9e2ec97d266e9fb4cfa874f8cfcf5ee046911837824ceea4b99cd4007560b","src/test.rs":"1685eec6fedc30fca1332cbb78c85e6c9b56eca962b6c6343c91ba69eefac754"},"package":"afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587"}

View File

@ -1,37 +0,0 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies
#
# If you believe there's an error in this file please file an
# issue against the rust-lang/cargo repository. If you're
# editing this file be aware that the upstream Cargo.toml
# will likely look very different (and much more reasonable)
[package]
edition = "2018"
name = "blake2b_simd"
version = "0.5.11"
authors = ["Jack O'Connor"]
description = "a pure Rust BLAKE2b implementation with dynamic SIMD"
documentation = "https://docs.rs/blake2b_simd"
readme = "README.md"
keywords = ["blake2b", "blake2bp", "blake2"]
license = "MIT"
repository = "https://github.com/oconnor663/blake2_simd"
[dependencies.arrayref]
version = "0.3.5"
[dependencies.arrayvec]
version = "0.5.0"
default-features = false
[dependencies.constant_time_eq]
version = "0.1.3"
[features]
default = ["std"]
std = []
uninline_portable = []

View File

@ -1,42 +0,0 @@
# blake2b_simd [![GitHub](https://img.shields.io/github/tag/oconnor663/blake2_simd.svg?label=GitHub)](https://github.com/oconnor663/blake2_simd) [![crates.io](https://img.shields.io/crates/v/blake2b_simd.svg)](https://crates.io/crates/blake2b_simd) [![Actions Status](https://github.com/oconnor663/blake2_simd/workflows/tests/badge.svg)](https://github.com/oconnor663/blake2_simd/actions)
An implementation of the BLAKE2b and BLAKE2bp hash functions. See also
[`blake2s_simd`](../blake2s).
This crate includes:
- 100% stable Rust.
- SIMD implementations based on Samuel Neves' [`blake2-avx2`](https://github.com/sneves/blake2-avx2).
These are very fast. For benchmarks, see [the Performance section of the
README](https://github.com/oconnor663/blake2_simd#performance).
- Portable, safe implementations for other platforms.
- Dynamic CPU feature detection. Binaries include multiple implementations by default and
choose the fastest one the processor supports at runtime.
- All the features from the [the BLAKE2 spec](https://blake2.net/blake2.pdf), like adjustable
length, keying, and associated data for tree hashing.
- `no_std` support. The `std` Cargo feature is on by default, for CPU feature detection and
for implementing `std::io::Write`.
- Support for computing multiple BLAKE2b hashes in parallel, matching the efficiency of
BLAKE2bp. See the [`many`](many/index.html) module.
# Example
```
use blake2b_simd::{blake2b, Params};
let expected = "ca002330e69d3e6b84a46a56a6533fd79d51d97a3bb7cad6c2ff43b354185d6d\
c1e723fb3db4ae0737e120378424c714bb982d9dc5bbd7a0ab318240ddd18f8d";
let hash = blake2b(b"foo");
assert_eq!(expected, &hash.to_hex());
let hash = Params::new()
.hash_length(16)
.key(b"The Magic Words are Squeamish Ossifrage")
.personal(b"L. P. Waterhouse")
.to_state()
.update(b"foo")
.update(b"bar")
.update(b"baz")
.finalize();
assert_eq!("ee8ff4e9be887297cf79348dc35dab56", &hash.to_hex());
```

View File

@ -1,929 +0,0 @@
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
use crate::guts::{
assemble_count, count_high, count_low, final_block, flag_word, input_debug_asserts, Finalize,
Job, LastNode, Stride,
};
use crate::{Count, Word, BLOCKBYTES, IV, SIGMA};
use arrayref::{array_refs, mut_array_refs};
use core::cmp;
use core::mem;
pub const DEGREE: usize = 4;
#[inline(always)]
unsafe fn loadu(src: *const [Word; DEGREE]) -> __m256i {
// This is an unaligned load, so the pointer cast is allowed.
_mm256_loadu_si256(src as *const __m256i)
}
#[inline(always)]
unsafe fn storeu(src: __m256i, dest: *mut [Word; DEGREE]) {
// This is an unaligned store, so the pointer cast is allowed.
_mm256_storeu_si256(dest as *mut __m256i, src)
}
#[inline(always)]
unsafe fn loadu_128(mem_addr: &[u8; 16]) -> __m128i {
_mm_loadu_si128(mem_addr.as_ptr() as *const __m128i)
}
#[inline(always)]
unsafe fn add(a: __m256i, b: __m256i) -> __m256i {
_mm256_add_epi64(a, b)
}
#[inline(always)]
unsafe fn eq(a: __m256i, b: __m256i) -> __m256i {
_mm256_cmpeq_epi64(a, b)
}
#[inline(always)]
unsafe fn and(a: __m256i, b: __m256i) -> __m256i {
_mm256_and_si256(a, b)
}
#[inline(always)]
unsafe fn negate_and(a: __m256i, b: __m256i) -> __m256i {
// Note that "and not" implies the reverse of the actual arg order.
_mm256_andnot_si256(a, b)
}
#[inline(always)]
unsafe fn xor(a: __m256i, b: __m256i) -> __m256i {
_mm256_xor_si256(a, b)
}
#[inline(always)]
unsafe fn set1(x: u64) -> __m256i {
_mm256_set1_epi64x(x as i64)
}
#[inline(always)]
unsafe fn set4(a: u64, b: u64, c: u64, d: u64) -> __m256i {
_mm256_setr_epi64x(a as i64, b as i64, c as i64, d as i64)
}
// Adapted from https://github.com/rust-lang-nursery/stdsimd/pull/479.
macro_rules! _MM_SHUFFLE {
($z:expr, $y:expr, $x:expr, $w:expr) => {
($z << 6) | ($y << 4) | ($x << 2) | $w
};
}
// These rotations are the "simple version". For the "complicated version", see
// https://github.com/sneves/blake2-avx2/blob/b3723921f668df09ece52dcd225a36d4a4eea1d9/blake2b-common.h#L43-L46.
// For a discussion of the tradeoffs, see
// https://github.com/sneves/blake2-avx2/pull/5. In short:
// - Due to an LLVM bug (https://bugs.llvm.org/show_bug.cgi?id=44379), this
// version performs better on recent x86 chips.
// - LLVM is able to optimize this version to AVX-512 rotation instructions
// when those are enabled.
#[inline(always)]
unsafe fn rot32(x: __m256i) -> __m256i {
_mm256_or_si256(_mm256_srli_epi64(x, 32), _mm256_slli_epi64(x, 64 - 32))
}
#[inline(always)]
unsafe fn rot24(x: __m256i) -> __m256i {
_mm256_or_si256(_mm256_srli_epi64(x, 24), _mm256_slli_epi64(x, 64 - 24))
}
#[inline(always)]
unsafe fn rot16(x: __m256i) -> __m256i {
_mm256_or_si256(_mm256_srli_epi64(x, 16), _mm256_slli_epi64(x, 64 - 16))
}
#[inline(always)]
unsafe fn rot63(x: __m256i) -> __m256i {
_mm256_or_si256(_mm256_srli_epi64(x, 63), _mm256_slli_epi64(x, 64 - 63))
}
#[inline(always)]
unsafe fn g1(a: &mut __m256i, b: &mut __m256i, c: &mut __m256i, d: &mut __m256i, m: &mut __m256i) {
*a = add(*a, *m);
*a = add(*a, *b);
*d = xor(*d, *a);
*d = rot32(*d);
*c = add(*c, *d);
*b = xor(*b, *c);
*b = rot24(*b);
}
#[inline(always)]
unsafe fn g2(a: &mut __m256i, b: &mut __m256i, c: &mut __m256i, d: &mut __m256i, m: &mut __m256i) {
*a = add(*a, *m);
*a = add(*a, *b);
*d = xor(*d, *a);
*d = rot16(*d);
*c = add(*c, *d);
*b = xor(*b, *c);
*b = rot63(*b);
}
// Note the optimization here of leaving b as the unrotated row, rather than a.
// All the message loads below are adjusted to compensate for this. See
// discussion at https://github.com/sneves/blake2-avx2/pull/4
#[inline(always)]
unsafe fn diagonalize(a: &mut __m256i, _b: &mut __m256i, c: &mut __m256i, d: &mut __m256i) {
*a = _mm256_permute4x64_epi64(*a, _MM_SHUFFLE!(2, 1, 0, 3));
*d = _mm256_permute4x64_epi64(*d, _MM_SHUFFLE!(1, 0, 3, 2));
*c = _mm256_permute4x64_epi64(*c, _MM_SHUFFLE!(0, 3, 2, 1));
}
#[inline(always)]
unsafe fn undiagonalize(a: &mut __m256i, _b: &mut __m256i, c: &mut __m256i, d: &mut __m256i) {
*a = _mm256_permute4x64_epi64(*a, _MM_SHUFFLE!(0, 3, 2, 1));
*d = _mm256_permute4x64_epi64(*d, _MM_SHUFFLE!(1, 0, 3, 2));
*c = _mm256_permute4x64_epi64(*c, _MM_SHUFFLE!(2, 1, 0, 3));
}
#[inline(always)]
unsafe fn compress_block(
block: &[u8; BLOCKBYTES],
words: &mut [Word; 8],
count: Count,
last_block: Word,
last_node: Word,
) {
let (words_low, words_high) = mut_array_refs!(words, DEGREE, DEGREE);
let (iv_low, iv_high) = array_refs!(&IV, DEGREE, DEGREE);
let mut a = loadu(words_low);
let mut b = loadu(words_high);
let mut c = loadu(iv_low);
let flags = set4(count_low(count), count_high(count), last_block, last_node);
let mut d = xor(loadu(iv_high), flags);
let msg_chunks = array_refs!(block, 16, 16, 16, 16, 16, 16, 16, 16);
let m0 = _mm256_broadcastsi128_si256(loadu_128(msg_chunks.0));
let m1 = _mm256_broadcastsi128_si256(loadu_128(msg_chunks.1));
let m2 = _mm256_broadcastsi128_si256(loadu_128(msg_chunks.2));
let m3 = _mm256_broadcastsi128_si256(loadu_128(msg_chunks.3));
let m4 = _mm256_broadcastsi128_si256(loadu_128(msg_chunks.4));
let m5 = _mm256_broadcastsi128_si256(loadu_128(msg_chunks.5));
let m6 = _mm256_broadcastsi128_si256(loadu_128(msg_chunks.6));
let m7 = _mm256_broadcastsi128_si256(loadu_128(msg_chunks.7));
let iv0 = a;
let iv1 = b;
let mut t0;
let mut t1;
let mut b0;
// round 1
t0 = _mm256_unpacklo_epi64(m0, m1);
t1 = _mm256_unpacklo_epi64(m2, m3);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g1(&mut a, &mut b, &mut c, &mut d, &mut b0);
t0 = _mm256_unpackhi_epi64(m0, m1);
t1 = _mm256_unpackhi_epi64(m2, m3);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g2(&mut a, &mut b, &mut c, &mut d, &mut b0);
diagonalize(&mut a, &mut b, &mut c, &mut d);
t0 = _mm256_unpacklo_epi64(m7, m4);
t1 = _mm256_unpacklo_epi64(m5, m6);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g1(&mut a, &mut b, &mut c, &mut d, &mut b0);
t0 = _mm256_unpackhi_epi64(m7, m4);
t1 = _mm256_unpackhi_epi64(m5, m6);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g2(&mut a, &mut b, &mut c, &mut d, &mut b0);
undiagonalize(&mut a, &mut b, &mut c, &mut d);
// round 2
t0 = _mm256_unpacklo_epi64(m7, m2);
t1 = _mm256_unpackhi_epi64(m4, m6);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g1(&mut a, &mut b, &mut c, &mut d, &mut b0);
t0 = _mm256_unpacklo_epi64(m5, m4);
t1 = _mm256_alignr_epi8(m3, m7, 8);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g2(&mut a, &mut b, &mut c, &mut d, &mut b0);
diagonalize(&mut a, &mut b, &mut c, &mut d);
t0 = _mm256_unpackhi_epi64(m2, m0);
t1 = _mm256_blend_epi32(m5, m0, 0x33);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g1(&mut a, &mut b, &mut c, &mut d, &mut b0);
t0 = _mm256_alignr_epi8(m6, m1, 8);
t1 = _mm256_blend_epi32(m3, m1, 0x33);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g2(&mut a, &mut b, &mut c, &mut d, &mut b0);
undiagonalize(&mut a, &mut b, &mut c, &mut d);
// round 3
t0 = _mm256_alignr_epi8(m6, m5, 8);
t1 = _mm256_unpackhi_epi64(m2, m7);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g1(&mut a, &mut b, &mut c, &mut d, &mut b0);
t0 = _mm256_unpacklo_epi64(m4, m0);
t1 = _mm256_blend_epi32(m6, m1, 0x33);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g2(&mut a, &mut b, &mut c, &mut d, &mut b0);
diagonalize(&mut a, &mut b, &mut c, &mut d);
t0 = _mm256_alignr_epi8(m5, m4, 8);
t1 = _mm256_unpackhi_epi64(m1, m3);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g1(&mut a, &mut b, &mut c, &mut d, &mut b0);
t0 = _mm256_unpacklo_epi64(m2, m7);
t1 = _mm256_blend_epi32(m0, m3, 0x33);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g2(&mut a, &mut b, &mut c, &mut d, &mut b0);
undiagonalize(&mut a, &mut b, &mut c, &mut d);
// round 4
t0 = _mm256_unpackhi_epi64(m3, m1);
t1 = _mm256_unpackhi_epi64(m6, m5);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g1(&mut a, &mut b, &mut c, &mut d, &mut b0);
t0 = _mm256_unpackhi_epi64(m4, m0);
t1 = _mm256_unpacklo_epi64(m6, m7);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g2(&mut a, &mut b, &mut c, &mut d, &mut b0);
diagonalize(&mut a, &mut b, &mut c, &mut d);
t0 = _mm256_alignr_epi8(m1, m7, 8);
t1 = _mm256_shuffle_epi32(m2, _MM_SHUFFLE!(1, 0, 3, 2));
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g1(&mut a, &mut b, &mut c, &mut d, &mut b0);
t0 = _mm256_unpacklo_epi64(m4, m3);
t1 = _mm256_unpacklo_epi64(m5, m0);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g2(&mut a, &mut b, &mut c, &mut d, &mut b0);
undiagonalize(&mut a, &mut b, &mut c, &mut d);
// round 5
t0 = _mm256_unpackhi_epi64(m4, m2);
t1 = _mm256_unpacklo_epi64(m1, m5);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g1(&mut a, &mut b, &mut c, &mut d, &mut b0);
t0 = _mm256_blend_epi32(m3, m0, 0x33);
t1 = _mm256_blend_epi32(m7, m2, 0x33);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g2(&mut a, &mut b, &mut c, &mut d, &mut b0);
diagonalize(&mut a, &mut b, &mut c, &mut d);
t0 = _mm256_alignr_epi8(m7, m1, 8);
t1 = _mm256_alignr_epi8(m3, m5, 8);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g1(&mut a, &mut b, &mut c, &mut d, &mut b0);
t0 = _mm256_unpackhi_epi64(m6, m0);
t1 = _mm256_unpacklo_epi64(m6, m4);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g2(&mut a, &mut b, &mut c, &mut d, &mut b0);
undiagonalize(&mut a, &mut b, &mut c, &mut d);
// round 6
t0 = _mm256_unpacklo_epi64(m1, m3);
t1 = _mm256_unpacklo_epi64(m0, m4);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g1(&mut a, &mut b, &mut c, &mut d, &mut b0);
t0 = _mm256_unpacklo_epi64(m6, m5);
t1 = _mm256_unpackhi_epi64(m5, m1);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g2(&mut a, &mut b, &mut c, &mut d, &mut b0);
diagonalize(&mut a, &mut b, &mut c, &mut d);
t0 = _mm256_alignr_epi8(m2, m0, 8);
t1 = _mm256_unpackhi_epi64(m3, m7);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g1(&mut a, &mut b, &mut c, &mut d, &mut b0);
t0 = _mm256_unpackhi_epi64(m4, m6);
t1 = _mm256_alignr_epi8(m7, m2, 8);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g2(&mut a, &mut b, &mut c, &mut d, &mut b0);
undiagonalize(&mut a, &mut b, &mut c, &mut d);
// round 7
t0 = _mm256_blend_epi32(m0, m6, 0x33);
t1 = _mm256_unpacklo_epi64(m7, m2);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g1(&mut a, &mut b, &mut c, &mut d, &mut b0);
t0 = _mm256_unpackhi_epi64(m2, m7);
t1 = _mm256_alignr_epi8(m5, m6, 8);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g2(&mut a, &mut b, &mut c, &mut d, &mut b0);
diagonalize(&mut a, &mut b, &mut c, &mut d);
t0 = _mm256_unpacklo_epi64(m4, m0);
t1 = _mm256_blend_epi32(m4, m3, 0x33);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g1(&mut a, &mut b, &mut c, &mut d, &mut b0);
t0 = _mm256_unpackhi_epi64(m5, m3);
t1 = _mm256_shuffle_epi32(m1, _MM_SHUFFLE!(1, 0, 3, 2));
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g2(&mut a, &mut b, &mut c, &mut d, &mut b0);
undiagonalize(&mut a, &mut b, &mut c, &mut d);
// round 8
t0 = _mm256_unpackhi_epi64(m6, m3);
t1 = _mm256_blend_epi32(m1, m6, 0x33);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g1(&mut a, &mut b, &mut c, &mut d, &mut b0);
t0 = _mm256_alignr_epi8(m7, m5, 8);
t1 = _mm256_unpackhi_epi64(m0, m4);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g2(&mut a, &mut b, &mut c, &mut d, &mut b0);
diagonalize(&mut a, &mut b, &mut c, &mut d);
t0 = _mm256_blend_epi32(m2, m1, 0x33);
t1 = _mm256_alignr_epi8(m4, m7, 8);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g1(&mut a, &mut b, &mut c, &mut d, &mut b0);
t0 = _mm256_unpacklo_epi64(m5, m0);
t1 = _mm256_unpacklo_epi64(m2, m3);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g2(&mut a, &mut b, &mut c, &mut d, &mut b0);
undiagonalize(&mut a, &mut b, &mut c, &mut d);
// round 9
t0 = _mm256_unpacklo_epi64(m3, m7);
t1 = _mm256_alignr_epi8(m0, m5, 8);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g1(&mut a, &mut b, &mut c, &mut d, &mut b0);
t0 = _mm256_unpackhi_epi64(m7, m4);
t1 = _mm256_alignr_epi8(m4, m1, 8);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g2(&mut a, &mut b, &mut c, &mut d, &mut b0);
diagonalize(&mut a, &mut b, &mut c, &mut d);
t0 = _mm256_unpacklo_epi64(m5, m6);
t1 = _mm256_unpackhi_epi64(m6, m0);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g1(&mut a, &mut b, &mut c, &mut d, &mut b0);
t0 = _mm256_alignr_epi8(m1, m2, 8);
t1 = _mm256_alignr_epi8(m2, m3, 8);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g2(&mut a, &mut b, &mut c, &mut d, &mut b0);
undiagonalize(&mut a, &mut b, &mut c, &mut d);
// round 10
t0 = _mm256_unpacklo_epi64(m5, m4);
t1 = _mm256_unpackhi_epi64(m3, m0);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g1(&mut a, &mut b, &mut c, &mut d, &mut b0);
t0 = _mm256_unpacklo_epi64(m1, m2);
t1 = _mm256_blend_epi32(m2, m3, 0x33);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g2(&mut a, &mut b, &mut c, &mut d, &mut b0);
diagonalize(&mut a, &mut b, &mut c, &mut d);
t0 = _mm256_unpackhi_epi64(m6, m7);
t1 = _mm256_unpackhi_epi64(m4, m1);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g1(&mut a, &mut b, &mut c, &mut d, &mut b0);
t0 = _mm256_blend_epi32(m5, m0, 0x33);
t1 = _mm256_unpacklo_epi64(m7, m6);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g2(&mut a, &mut b, &mut c, &mut d, &mut b0);
undiagonalize(&mut a, &mut b, &mut c, &mut d);
// round 11
t0 = _mm256_unpacklo_epi64(m0, m1);
t1 = _mm256_unpacklo_epi64(m2, m3);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g1(&mut a, &mut b, &mut c, &mut d, &mut b0);
t0 = _mm256_unpackhi_epi64(m0, m1);
t1 = _mm256_unpackhi_epi64(m2, m3);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g2(&mut a, &mut b, &mut c, &mut d, &mut b0);
diagonalize(&mut a, &mut b, &mut c, &mut d);
t0 = _mm256_unpacklo_epi64(m7, m4);
t1 = _mm256_unpacklo_epi64(m5, m6);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g1(&mut a, &mut b, &mut c, &mut d, &mut b0);
t0 = _mm256_unpackhi_epi64(m7, m4);
t1 = _mm256_unpackhi_epi64(m5, m6);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g2(&mut a, &mut b, &mut c, &mut d, &mut b0);
undiagonalize(&mut a, &mut b, &mut c, &mut d);
// round 12
t0 = _mm256_unpacklo_epi64(m7, m2);
t1 = _mm256_unpackhi_epi64(m4, m6);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g1(&mut a, &mut b, &mut c, &mut d, &mut b0);
t0 = _mm256_unpacklo_epi64(m5, m4);
t1 = _mm256_alignr_epi8(m3, m7, 8);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g2(&mut a, &mut b, &mut c, &mut d, &mut b0);
diagonalize(&mut a, &mut b, &mut c, &mut d);
t0 = _mm256_unpackhi_epi64(m2, m0);
t1 = _mm256_blend_epi32(m5, m0, 0x33);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g1(&mut a, &mut b, &mut c, &mut d, &mut b0);
t0 = _mm256_alignr_epi8(m6, m1, 8);
t1 = _mm256_blend_epi32(m3, m1, 0x33);
b0 = _mm256_blend_epi32(t0, t1, 0xF0);
g2(&mut a, &mut b, &mut c, &mut d, &mut b0);
undiagonalize(&mut a, &mut b, &mut c, &mut d);
a = xor(a, c);
b = xor(b, d);
a = xor(a, iv0);
b = xor(b, iv1);
storeu(a, words_low);
storeu(b, words_high);
}
#[target_feature(enable = "avx2")]
pub unsafe fn compress1_loop(
input: &[u8],
words: &mut [Word; 8],
mut count: Count,
last_node: LastNode,
finalize: Finalize,
stride: Stride,
) {
input_debug_asserts(input, finalize);
let mut local_words = *words;
let mut fin_offset = input.len().saturating_sub(1);
fin_offset -= fin_offset % stride.padded_blockbytes();
let mut buf = [0; BLOCKBYTES];
let (fin_block, fin_len, _) = final_block(input, fin_offset, &mut buf, stride);
let fin_last_block = flag_word(finalize.yes());
let fin_last_node = flag_word(finalize.yes() && last_node.yes());
let mut offset = 0;
loop {
let block;
let count_delta;
let last_block;
let last_node;
if offset == fin_offset {
block = fin_block;
count_delta = fin_len;
last_block = fin_last_block;
last_node = fin_last_node;
} else {
// This unsafe cast avoids bounds checks. There's guaranteed to be
// enough input because `offset < fin_offset`.
block = &*(input.as_ptr().add(offset) as *const [u8; BLOCKBYTES]);
count_delta = BLOCKBYTES;
last_block = flag_word(false);
last_node = flag_word(false);
};
count = count.wrapping_add(count_delta as Count);
compress_block(block, &mut local_words, count, last_block, last_node);
// Check for termination before bumping the offset, to avoid overflow.
if offset == fin_offset {
break;
}
offset += stride.padded_blockbytes();
}
*words = local_words;
}
// Performance note: Factoring out a G function here doesn't hurt performance,
// unlike in the case of BLAKE2s where it hurts substantially. In fact, on my
// machine, it helps a tiny bit. But the difference it tiny, so I'm going to
// stick to the approach used by https://github.com/sneves/blake2-avx2
// until/unless I can be sure the (tiny) improvement is consistent across
// different Intel microarchitectures. Smaller code size is nice, but a
// divergence between the BLAKE2b and BLAKE2s implementations is less nice.
#[inline(always)]
unsafe fn round(v: &mut [__m256i; 16], m: &[__m256i; 16], r: usize) {
v[0] = add(v[0], m[SIGMA[r][0] as usize]);
v[1] = add(v[1], m[SIGMA[r][2] as usize]);
v[2] = add(v[2], m[SIGMA[r][4] as usize]);
v[3] = add(v[3], m[SIGMA[r][6] as usize]);
v[0] = add(v[0], v[4]);
v[1] = add(v[1], v[5]);
v[2] = add(v[2], v[6]);
v[3] = add(v[3], v[7]);
v[12] = xor(v[12], v[0]);
v[13] = xor(v[13], v[1]);
v[14] = xor(v[14], v[2]);
v[15] = xor(v[15], v[3]);
v[12] = rot32(v[12]);
v[13] = rot32(v[13]);
v[14] = rot32(v[14]);
v[15] = rot32(v[15]);
v[8] = add(v[8], v[12]);
v[9] = add(v[9], v[13]);
v[10] = add(v[10], v[14]);
v[11] = add(v[11], v[15]);
v[4] = xor(v[4], v[8]);
v[5] = xor(v[5], v[9]);
v[6] = xor(v[6], v[10]);
v[7] = xor(v[7], v[11]);
v[4] = rot24(v[4]);
v[5] = rot24(v[5]);
v[6] = rot24(v[6]);
v[7] = rot24(v[7]);
v[0] = add(v[0], m[SIGMA[r][1] as usize]);
v[1] = add(v[1], m[SIGMA[r][3] as usize]);
v[2] = add(v[2], m[SIGMA[r][5] as usize]);
v[3] = add(v[3], m[SIGMA[r][7] as usize]);
v[0] = add(v[0], v[4]);
v[1] = add(v[1], v[5]);
v[2] = add(v[2], v[6]);
v[3] = add(v[3], v[7]);
v[12] = xor(v[12], v[0]);
v[13] = xor(v[13], v[1]);
v[14] = xor(v[14], v[2]);
v[15] = xor(v[15], v[3]);
v[12] = rot16(v[12]);
v[13] = rot16(v[13]);
v[14] = rot16(v[14]);
v[15] = rot16(v[15]);
v[8] = add(v[8], v[12]);
v[9] = add(v[9], v[13]);
v[10] = add(v[10], v[14]);
v[11] = add(v[11], v[15]);
v[4] = xor(v[4], v[8]);
v[5] = xor(v[5], v[9]);
v[6] = xor(v[6], v[10]);
v[7] = xor(v[7], v[11]);
v[4] = rot63(v[4]);
v[5] = rot63(v[5]);
v[6] = rot63(v[6]);
v[7] = rot63(v[7]);
v[0] = add(v[0], m[SIGMA[r][8] as usize]);
v[1] = add(v[1], m[SIGMA[r][10] as usize]);
v[2] = add(v[2], m[SIGMA[r][12] as usize]);
v[3] = add(v[3], m[SIGMA[r][14] as usize]);
v[0] = add(v[0], v[5]);
v[1] = add(v[1], v[6]);
v[2] = add(v[2], v[7]);
v[3] = add(v[3], v[4]);
v[15] = xor(v[15], v[0]);
v[12] = xor(v[12], v[1]);
v[13] = xor(v[13], v[2]);
v[14] = xor(v[14], v[3]);
v[15] = rot32(v[15]);
v[12] = rot32(v[12]);
v[13] = rot32(v[13]);
v[14] = rot32(v[14]);
v[10] = add(v[10], v[15]);
v[11] = add(v[11], v[12]);
v[8] = add(v[8], v[13]);
v[9] = add(v[9], v[14]);
v[5] = xor(v[5], v[10]);
v[6] = xor(v[6], v[11]);
v[7] = xor(v[7], v[8]);
v[4] = xor(v[4], v[9]);
v[5] = rot24(v[5]);
v[6] = rot24(v[6]);
v[7] = rot24(v[7]);
v[4] = rot24(v[4]);
v[0] = add(v[0], m[SIGMA[r][9] as usize]);
v[1] = add(v[1], m[SIGMA[r][11] as usize]);
v[2] = add(v[2], m[SIGMA[r][13] as usize]);
v[3] = add(v[3], m[SIGMA[r][15] as usize]);
v[0] = add(v[0], v[5]);
v[1] = add(v[1], v[6]);
v[2] = add(v[2], v[7]);
v[3] = add(v[3], v[4]);
v[15] = xor(v[15], v[0]);
v[12] = xor(v[12], v[1]);
v[13] = xor(v[13], v[2]);
v[14] = xor(v[14], v[3]);
v[15] = rot16(v[15]);
v[12] = rot16(v[12]);
v[13] = rot16(v[13]);
v[14] = rot16(v[14]);
v[10] = add(v[10], v[15]);
v[11] = add(v[11], v[12]);
v[8] = add(v[8], v[13]);
v[9] = add(v[9], v[14]);
v[5] = xor(v[5], v[10]);
v[6] = xor(v[6], v[11]);
v[7] = xor(v[7], v[8]);
v[4] = xor(v[4], v[9]);
v[5] = rot63(v[5]);
v[6] = rot63(v[6]);
v[7] = rot63(v[7]);
v[4] = rot63(v[4]);
}
// We'd rather make this a regular function with #[inline(always)], but for
// some reason that blows up compile times by about 10 seconds, at least in
// some cases (BLAKE2b avx2.rs). This macro seems to get the same performance
// result, without the compile time issue.
macro_rules! compress4_transposed {
(
$h_vecs:expr,
$msg_vecs:expr,
$count_low:expr,
$count_high:expr,
$lastblock:expr,
$lastnode:expr,
) => {
let h_vecs: &mut [__m256i; 8] = $h_vecs;
let msg_vecs: &[__m256i; 16] = $msg_vecs;
let count_low: __m256i = $count_low;
let count_high: __m256i = $count_high;
let lastblock: __m256i = $lastblock;
let lastnode: __m256i = $lastnode;
let mut v = [
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(IV[0]),
set1(IV[1]),
set1(IV[2]),
set1(IV[3]),
xor(set1(IV[4]), count_low),
xor(set1(IV[5]), count_high),
xor(set1(IV[6]), lastblock),
xor(set1(IV[7]), lastnode),
];
round(&mut v, &msg_vecs, 0);
round(&mut v, &msg_vecs, 1);
round(&mut v, &msg_vecs, 2);
round(&mut v, &msg_vecs, 3);
round(&mut v, &msg_vecs, 4);
round(&mut v, &msg_vecs, 5);
round(&mut v, &msg_vecs, 6);
round(&mut v, &msg_vecs, 7);
round(&mut v, &msg_vecs, 8);
round(&mut v, &msg_vecs, 9);
round(&mut v, &msg_vecs, 10);
round(&mut v, &msg_vecs, 11);
h_vecs[0] = xor(xor(h_vecs[0], v[0]), v[8]);
h_vecs[1] = xor(xor(h_vecs[1], v[1]), v[9]);
h_vecs[2] = xor(xor(h_vecs[2], v[2]), v[10]);
h_vecs[3] = xor(xor(h_vecs[3], v[3]), v[11]);
h_vecs[4] = xor(xor(h_vecs[4], v[4]), v[12]);
h_vecs[5] = xor(xor(h_vecs[5], v[5]), v[13]);
h_vecs[6] = xor(xor(h_vecs[6], v[6]), v[14]);
h_vecs[7] = xor(xor(h_vecs[7], v[7]), v[15]);
};
}
#[inline(always)]
unsafe fn interleave128(a: __m256i, b: __m256i) -> (__m256i, __m256i) {
(
_mm256_permute2x128_si256(a, b, 0x20),
_mm256_permute2x128_si256(a, b, 0x31),
)
}
// There are several ways to do a transposition. We could do it naively, with 8 separate
// _mm256_set_epi64x instructions, referencing each of the 64 words explicitly. Or we could copy
// the vecs into contiguous storage and then use gather instructions. This third approach is to use
// a series of unpack instructions to interleave the vectors. In my benchmarks, interleaving is the
// fastest approach. To test this, run `cargo +nightly bench --bench libtest load_4` in the
// https://github.com/oconnor663/bao_experiments repo.
#[inline(always)]
unsafe fn transpose_vecs(
vec_a: __m256i,
vec_b: __m256i,
vec_c: __m256i,
vec_d: __m256i,
) -> [__m256i; DEGREE] {
// Interleave 64-bit lates. The low unpack is lanes 00/22 and the high is 11/33.
let ab_02 = _mm256_unpacklo_epi64(vec_a, vec_b);
let ab_13 = _mm256_unpackhi_epi64(vec_a, vec_b);
let cd_02 = _mm256_unpacklo_epi64(vec_c, vec_d);
let cd_13 = _mm256_unpackhi_epi64(vec_c, vec_d);
// Interleave 128-bit lanes.
let (abcd_0, abcd_2) = interleave128(ab_02, cd_02);
let (abcd_1, abcd_3) = interleave128(ab_13, cd_13);
[abcd_0, abcd_1, abcd_2, abcd_3]
}
#[inline(always)]
unsafe fn transpose_state_vecs(jobs: &[Job; DEGREE]) -> [__m256i; 8] {
// Load all the state words into transposed vectors, where the first vector
// has the first word of each state, etc. Transposing once at the beginning
// and once at the end is more efficient that repeating it for each block.
let words0 = array_refs!(&jobs[0].words, DEGREE, DEGREE);
let words1 = array_refs!(&jobs[1].words, DEGREE, DEGREE);
let words2 = array_refs!(&jobs[2].words, DEGREE, DEGREE);
let words3 = array_refs!(&jobs[3].words, DEGREE, DEGREE);
let [h0, h1, h2, h3] = transpose_vecs(
loadu(words0.0),
loadu(words1.0),
loadu(words2.0),
loadu(words3.0),
);
let [h4, h5, h6, h7] = transpose_vecs(
loadu(words0.1),
loadu(words1.1),
loadu(words2.1),
loadu(words3.1),
);
[h0, h1, h2, h3, h4, h5, h6, h7]
}
#[inline(always)]
unsafe fn untranspose_state_vecs(h_vecs: &[__m256i; 8], jobs: &mut [Job; DEGREE]) {
// Un-transpose the updated state vectors back into the caller's arrays.
let [job0, job1, job2, job3] = jobs;
let words0 = mut_array_refs!(&mut job0.words, DEGREE, DEGREE);
let words1 = mut_array_refs!(&mut job1.words, DEGREE, DEGREE);
let words2 = mut_array_refs!(&mut job2.words, DEGREE, DEGREE);
let words3 = mut_array_refs!(&mut job3.words, DEGREE, DEGREE);
let out = transpose_vecs(h_vecs[0], h_vecs[1], h_vecs[2], h_vecs[3]);
storeu(out[0], words0.0);
storeu(out[1], words1.0);
storeu(out[2], words2.0);
storeu(out[3], words3.0);
let out = transpose_vecs(h_vecs[4], h_vecs[5], h_vecs[6], h_vecs[7]);
storeu(out[0], words0.1);
storeu(out[1], words1.1);
storeu(out[2], words2.1);
storeu(out[3], words3.1);
}
#[inline(always)]
unsafe fn transpose_msg_vecs(blocks: [*const [u8; BLOCKBYTES]; DEGREE]) -> [__m256i; 16] {
// These input arrays have no particular alignment, so we use unaligned
// loads to read from them.
let block0 = blocks[0] as *const [Word; DEGREE];
let block1 = blocks[1] as *const [Word; DEGREE];
let block2 = blocks[2] as *const [Word; DEGREE];
let block3 = blocks[3] as *const [Word; DEGREE];
let [m0, m1, m2, m3] = transpose_vecs(
loadu(block0.add(0)),
loadu(block1.add(0)),
loadu(block2.add(0)),
loadu(block3.add(0)),
);
let [m4, m5, m6, m7] = transpose_vecs(
loadu(block0.add(1)),
loadu(block1.add(1)),
loadu(block2.add(1)),
loadu(block3.add(1)),
);
let [m8, m9, m10, m11] = transpose_vecs(
loadu(block0.add(2)),
loadu(block1.add(2)),
loadu(block2.add(2)),
loadu(block3.add(2)),
);
let [m12, m13, m14, m15] = transpose_vecs(
loadu(block0.add(3)),
loadu(block1.add(3)),
loadu(block2.add(3)),
loadu(block3.add(3)),
);
[
m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15,
]
}
#[inline(always)]
unsafe fn load_counts(jobs: &[Job; DEGREE]) -> (__m256i, __m256i) {
(
set4(
count_low(jobs[0].count),
count_low(jobs[1].count),
count_low(jobs[2].count),
count_low(jobs[3].count),
),
set4(
count_high(jobs[0].count),
count_high(jobs[1].count),
count_high(jobs[2].count),
count_high(jobs[3].count),
),
)
}
#[inline(always)]
unsafe fn store_counts(jobs: &mut [Job; DEGREE], low: __m256i, high: __m256i) {
let low_ints: [Word; DEGREE] = mem::transmute(low);
let high_ints: [Word; DEGREE] = mem::transmute(high);
for i in 0..DEGREE {
jobs[i].count = assemble_count(low_ints[i], high_ints[i]);
}
}
#[inline(always)]
unsafe fn add_to_counts(lo: &mut __m256i, hi: &mut __m256i, delta: __m256i) {
// If the low counts reach zero, that means they wrapped, unless the delta
// was also zero.
*lo = add(*lo, delta);
let lo_reached_zero = eq(*lo, set1(0));
let delta_was_zero = eq(delta, set1(0));
let hi_inc = and(set1(1), negate_and(delta_was_zero, lo_reached_zero));
*hi = add(*hi, hi_inc);
}
#[inline(always)]
unsafe fn flags_vec(flags: [bool; DEGREE]) -> __m256i {
set4(
flag_word(flags[0]),
flag_word(flags[1]),
flag_word(flags[2]),
flag_word(flags[3]),
)
}
#[target_feature(enable = "avx2")]
pub unsafe fn compress4_loop(jobs: &mut [Job; DEGREE], finalize: Finalize, stride: Stride) {
// If we're not finalizing, there can't be a partial block at the end.
for job in jobs.iter() {
input_debug_asserts(job.input, finalize);
}
let msg_ptrs = [
jobs[0].input.as_ptr(),
jobs[1].input.as_ptr(),
jobs[2].input.as_ptr(),
jobs[3].input.as_ptr(),
];
let mut h_vecs = transpose_state_vecs(&jobs);
let (mut counts_lo, mut counts_hi) = load_counts(&jobs);
// Prepare the final blocks (note, which could be empty if the input is
// empty). Do all this before entering the main loop.
let min_len = jobs.iter().map(|job| job.input.len()).min().unwrap();
let mut fin_offset = min_len.saturating_sub(1);
fin_offset -= fin_offset % stride.padded_blockbytes();
// Performance note, making these buffers mem::uninitialized() seems to
// cause problems in the optimizer.
let mut buf0: [u8; BLOCKBYTES] = [0; BLOCKBYTES];
let mut buf1: [u8; BLOCKBYTES] = [0; BLOCKBYTES];
let mut buf2: [u8; BLOCKBYTES] = [0; BLOCKBYTES];
let mut buf3: [u8; BLOCKBYTES] = [0; BLOCKBYTES];
let (block0, len0, finalize0) = final_block(jobs[0].input, fin_offset, &mut buf0, stride);
let (block1, len1, finalize1) = final_block(jobs[1].input, fin_offset, &mut buf1, stride);
let (block2, len2, finalize2) = final_block(jobs[2].input, fin_offset, &mut buf2, stride);
let (block3, len3, finalize3) = final_block(jobs[3].input, fin_offset, &mut buf3, stride);
let fin_blocks: [*const [u8; BLOCKBYTES]; DEGREE] = [block0, block1, block2, block3];
let fin_counts_delta = set4(len0 as Word, len1 as Word, len2 as Word, len3 as Word);
let fin_last_block;
let fin_last_node;
if finalize.yes() {
fin_last_block = flags_vec([finalize0, finalize1, finalize2, finalize3]);
fin_last_node = flags_vec([
finalize0 && jobs[0].last_node.yes(),
finalize1 && jobs[1].last_node.yes(),
finalize2 && jobs[2].last_node.yes(),
finalize3 && jobs[3].last_node.yes(),
]);
} else {
fin_last_block = set1(0);
fin_last_node = set1(0);
}
// The main loop.
let mut offset = 0;
loop {
let blocks;
let counts_delta;
let last_block;
let last_node;
if offset == fin_offset {
blocks = fin_blocks;
counts_delta = fin_counts_delta;
last_block = fin_last_block;
last_node = fin_last_node;
} else {
blocks = [
msg_ptrs[0].add(offset) as *const [u8; BLOCKBYTES],
msg_ptrs[1].add(offset) as *const [u8; BLOCKBYTES],
msg_ptrs[2].add(offset) as *const [u8; BLOCKBYTES],
msg_ptrs[3].add(offset) as *const [u8; BLOCKBYTES],
];
counts_delta = set1(BLOCKBYTES as Word);
last_block = set1(0);
last_node = set1(0);
};
let m_vecs = transpose_msg_vecs(blocks);
add_to_counts(&mut counts_lo, &mut counts_hi, counts_delta);
compress4_transposed!(
&mut h_vecs,
&m_vecs,
counts_lo,
counts_hi,
last_block,
last_node,
);
// Check for termination before bumping the offset, to avoid overflow.
if offset == fin_offset {
break;
}
offset += stride.padded_blockbytes();
}
// Write out the results.
untranspose_state_vecs(&h_vecs, &mut *jobs);
store_counts(&mut *jobs, counts_lo, counts_hi);
let max_consumed = offset.saturating_add(stride.padded_blockbytes());
for job in jobs.iter_mut() {
let consumed = cmp::min(max_consumed, job.input.len());
job.input = &job.input[consumed..];
}
}

View File

@ -1,570 +0,0 @@
//! BLAKE2bp, a variant of BLAKE2b that uses SIMD more efficiently.
//!
//! The AVX2 implementation of BLAKE2bp is about twice as fast that of BLAKE2b.
//! However, note that it's a different hash function, and it gives a different
//! hash from BLAKE2b for the same input.
//!
//! # Example
//!
//! ```
//! use blake2b_simd::blake2bp;
//!
//! let hash = blake2bp::Params::new()
//! .hash_length(16)
//! .key(b"The Magic Words are Squeamish Ossifrage")
//! .to_state()
//! .update(b"foo")
//! .update(b"bar")
//! .update(b"baz")
//! .finalize();
//! assert_eq!("e69c7d2c42a5ac14948772231c68c552", &hash.to_hex());
//! ```
use crate::guts::{Finalize, Implementation, Job, LastNode, Stride};
use crate::many;
use crate::Count;
use crate::Hash;
use crate::Word;
use crate::BLOCKBYTES;
use crate::KEYBYTES;
use crate::OUTBYTES;
use core::cmp;
use core::fmt;
use core::mem::size_of;
#[cfg(feature = "std")]
use std;
pub(crate) const DEGREE: usize = 4;
/// Compute the BLAKE2bp hash of a slice of bytes all at once, using default
/// parameters.
///
/// # Example
///
/// ```
/// # use blake2b_simd::blake2bp::blake2bp;
/// let expected = "8ca9ccee7946afcb686fe7556628b5ba1bf9a691da37ca58cd049354d99f3704\
/// 2c007427e5f219b9ab5063707ec6823872dee413ee014b4d02f2ebb6abb5f643";
/// let hash = blake2bp(b"foo");
/// assert_eq!(expected, &hash.to_hex());
/// ```
pub fn blake2bp(input: &[u8]) -> Hash {
Params::new().hash(input)
}
/// A parameter builder for BLAKE2bp, just like the [`Params`](../struct.Params.html) type for
/// BLAKE2b.
///
/// This builder only supports configuring the hash length and a secret key. This matches the
/// options provided by the [reference
/// implementation](https://github.com/BLAKE2/BLAKE2/blob/320c325437539ae91091ce62efec1913cd8093c2/ref/blake2.h#L162-L165).
///
/// # Example
///
/// ```
/// use blake2b_simd::blake2bp;
/// let mut state = blake2bp::Params::new().hash_length(32).to_state();
/// ```
#[derive(Clone)]
pub struct Params {
hash_length: u8,
key_length: u8,
key: [u8; KEYBYTES],
implementation: Implementation,
}
impl Params {
/// Equivalent to `Params::default()`.
pub fn new() -> Self {
Self {
hash_length: OUTBYTES as u8,
key_length: 0,
key: [0; KEYBYTES],
implementation: Implementation::detect(),
}
}
fn to_words(&self) -> ([[Word; 8]; DEGREE], [Word; 8]) {
let mut base_params = crate::Params::new();
base_params
.hash_length(self.hash_length as usize)
.key(&self.key[..self.key_length as usize])
.fanout(DEGREE as u8)
.max_depth(2)
.max_leaf_length(0)
// Note that inner_hash_length is always OUTBYTES, regardless of the hash_length
// parameter. This isn't documented in the spec, but it matches the behavior of the
// reference implementation: https://github.com/BLAKE2/BLAKE2/blob/320c325437539ae91091ce62efec1913cd8093c2/ref/blake2bp-ref.c#L55
.inner_hash_length(OUTBYTES);
let leaf_words = |worker_index| {
base_params
.clone()
.node_offset(worker_index)
.node_depth(0)
// Note that setting the last_node flag here has no effect,
// because it isn't included in the state words.
.to_words()
};
let leaf_words = [leaf_words(0), leaf_words(1), leaf_words(2), leaf_words(3)];
let root_words = base_params
.clone()
.node_offset(0)
.node_depth(1)
// Note that setting the last_node flag here has no effect, because
// it isn't included in the state words. Also note that because
// we're only preserving its state words, the root node won't hash
// any key bytes.
.to_words();
(leaf_words, root_words)
}
/// Hash an input all at once with these parameters.
pub fn hash(&self, input: &[u8]) -> Hash {
// If there's a key, just fall back to using the State.
if self.key_length > 0 {
return self.to_state().update(input).finalize();
}
let (mut leaf_words, mut root_words) = self.to_words();
// Hash each leaf in parallel.
let jobs = leaf_words.iter_mut().enumerate().map(|(i, words)| {
let input_start = cmp::min(input.len(), i * BLOCKBYTES);
Job {
input: &input[input_start..],
words,
count: 0,
last_node: if i == DEGREE - 1 {
LastNode::Yes
} else {
LastNode::No
},
}
});
many::compress_many(jobs, self.implementation, Finalize::Yes, Stride::Parallel);
// Hash each leaf into the root.
finalize_root_words(
&leaf_words,
&mut root_words,
self.hash_length,
self.implementation,
)
}
/// Construct a BLAKE2bp `State` object based on these parameters.
pub fn to_state(&self) -> State {
State::with_params(self)
}
/// Set the length of the final hash, from 1 to `OUTBYTES` (64). Apart from controlling the
/// length of the final `Hash`, this is also associated data, and changing it will result in a
/// totally different hash.
pub fn hash_length(&mut self, length: usize) -> &mut Self {
assert!(
1 <= length && length <= OUTBYTES,
"Bad hash length: {}",
length
);
self.hash_length = length as u8;
self
}
/// Use a secret key, so that BLAKE2bp acts as a MAC. The maximum key length is `KEYBYTES`
/// (64). An empty key is equivalent to having no key at all.
pub fn key(&mut self, key: &[u8]) -> &mut Self {
assert!(key.len() <= KEYBYTES, "Bad key length: {}", key.len());
self.key_length = key.len() as u8;
self.key = [0; KEYBYTES];
self.key[..key.len()].copy_from_slice(key);
self
}
}
impl Default for Params {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for Params {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Params {{ hash_length: {}, key_length: {} }}",
self.hash_length,
// NB: Don't print the key itself. Debug shouldn't leak secrets.
self.key_length,
)
}
}
/// An incremental hasher for BLAKE2bp, just like the [`State`](../struct.State.html) type for
/// BLAKE2b.
///
/// # Example
///
/// ```
/// use blake2b_simd::blake2bp;
///
/// let mut state = blake2bp::State::new();
/// state.update(b"foo");
/// state.update(b"bar");
/// let hash = state.finalize();
///
/// let expected = "e654427b6ef02949471712263e59071abbb6aa94855674c1daeed6cfaf127c33\
/// dfa3205f7f7f71e4f0673d25fa82a368488911f446bccd323af3ab03f53e56e5";
/// assert_eq!(expected, &hash.to_hex());
/// ```
#[derive(Clone)]
pub struct State {
leaf_words: [[Word; 8]; DEGREE],
root_words: [Word; 8],
// Note that this buffer is twice as large as what compress4 needs. That guarantees that we
// have enough input when we compress to know we don't need to finalize any of the leaves.
buf: [u8; 2 * DEGREE * BLOCKBYTES],
buf_len: u16,
// Note that this is the *per-leaf* count.
count: Count,
hash_length: u8,
implementation: Implementation,
is_keyed: bool,
}
impl State {
/// Equivalent to `State::default()` or `Params::default().to_state()`.
pub fn new() -> Self {
Self::with_params(&Params::default())
}
fn with_params(params: &Params) -> Self {
let (leaf_words, root_words) = params.to_words();
// If a key is set, initalize the buffer to contain the key bytes. Note
// that only the leaves hash key bytes. The root doesn't, even though
// the key length it still set in its parameters. Again this isn't
// documented in the spec, but it matches the behavior of the reference
// implementation:
// https://github.com/BLAKE2/BLAKE2/blob/320c325437539ae91091ce62efec1913cd8093c2/ref/blake2bp-ref.c#L128
// This particular behavior (though not the inner hash length behavior
// above) is also corroborated by the official test vectors; see
// tests/vector_tests.rs.
let mut buf = [0; 2 * DEGREE * BLOCKBYTES];
let mut buf_len = 0;
if params.key_length > 0 {
for i in 0..DEGREE {
let keybytes = &params.key[..params.key_length as usize];
buf[i * BLOCKBYTES..][..keybytes.len()].copy_from_slice(keybytes);
buf_len = BLOCKBYTES * DEGREE;
}
}
Self {
leaf_words,
root_words,
buf,
buf_len: buf_len as u16,
count: 0, // count gets updated in self.compress()
hash_length: params.hash_length,
implementation: params.implementation,
is_keyed: params.key_length > 0,
}
}
fn fill_buf(&mut self, input: &mut &[u8]) {
let take = cmp::min(self.buf.len() - self.buf_len as usize, input.len());
self.buf[self.buf_len as usize..][..take].copy_from_slice(&input[..take]);
self.buf_len += take as u16;
*input = &input[take..];
}
fn compress_to_leaves(
leaves: &mut [[Word; 8]; DEGREE],
input: &[u8],
count: &mut Count,
implementation: Implementation,
) {
// Input is assumed to be an even number of blocks for each leaf. Since
// we're not finilizing, debug asserts will fire otherwise.
let jobs = leaves.iter_mut().enumerate().map(|(i, words)| {
Job {
input: &input[i * BLOCKBYTES..],
words,
count: *count,
last_node: LastNode::No, // irrelevant when not finalizing
}
});
many::compress_many(jobs, implementation, Finalize::No, Stride::Parallel);
// Note that count is the bytes input *per-leaf*.
*count = count.wrapping_add((input.len() / DEGREE) as Count);
}
/// Add input to the hash. You can call `update` any number of times.
pub fn update(&mut self, mut input: &[u8]) -> &mut Self {
// If we have a partial buffer, try to complete it. If we complete it and there's more
// input waiting, we need to compress to make more room. However, because we need to be
// sure that *none* of the leaves would need to be finalized as part of this round of
// compression, we need to buffer more than we would for BLAKE2b.
if self.buf_len > 0 {
self.fill_buf(&mut input);
// The buffer is large enough for two compressions. If we've filled
// the buffer and there's still more input coming, then we have to
// do at least one compression. If there's enough input still
// coming that all the leaves are guaranteed to get more, do both
// compressions in the buffer. Otherwise, do just one and shift the
// back half of the buffer to the front.
if !input.is_empty() {
if input.len() > (DEGREE - 1) * BLOCKBYTES {
// Enough input coming to do both compressions.
Self::compress_to_leaves(
&mut self.leaf_words,
&self.buf,
&mut self.count,
self.implementation,
);
self.buf_len = 0;
} else {
// Only enough input coming for one compression.
Self::compress_to_leaves(
&mut self.leaf_words,
&self.buf[..DEGREE * BLOCKBYTES],
&mut self.count,
self.implementation,
);
self.buf_len = (DEGREE * BLOCKBYTES) as u16;
let (buf_front, buf_back) = self.buf.split_at_mut(DEGREE * BLOCKBYTES);
buf_front.copy_from_slice(buf_back);
}
}
}
// Now we directly compress as much input as possible, without copying
// it into the buffer. We need to make sure we buffer at least one byte
// for each of the leaves, so that we know we don't need to finalize
// them.
let needed_tail = (DEGREE - 1) * BLOCKBYTES + 1;
let mut bulk_bytes = input.len().saturating_sub(needed_tail);
bulk_bytes -= bulk_bytes % (DEGREE * BLOCKBYTES);
if bulk_bytes > 0 {
Self::compress_to_leaves(
&mut self.leaf_words,
&input[..bulk_bytes],
&mut self.count,
self.implementation,
);
input = &input[bulk_bytes..];
}
// Buffer any remaining input, to be either compressed or finalized in
// a subsequent call.
self.fill_buf(&mut input);
debug_assert_eq!(0, input.len());
self
}
/// Finalize the state and return a `Hash`. This method is idempotent, and calling it multiple
/// times will give the same result. It's also possible to `update` with more input in between.
pub fn finalize(&self) -> Hash {
// Hash whatever's remaining in the buffer and finalize the leaves.
let buf_len = self.buf_len as usize;
let mut leaves_copy = self.leaf_words;
let jobs = leaves_copy
.iter_mut()
.enumerate()
.map(|(leaf_index, leaf_words)| {
let input = &self.buf[cmp::min(leaf_index * BLOCKBYTES, buf_len)..buf_len];
Job {
input,
words: leaf_words,
count: self.count,
last_node: if leaf_index == DEGREE - 1 {
LastNode::Yes
} else {
LastNode::No
},
}
});
many::compress_many(jobs, self.implementation, Finalize::Yes, Stride::Parallel);
// Concatenate each leaf into the root and hash that.
let mut root_words_copy = self.root_words;
finalize_root_words(
&leaves_copy,
&mut root_words_copy,
self.hash_length,
self.implementation,
)
}
/// Return the total number of bytes input so far.
///
/// Note that `count` doesn't include the bytes of the key block, if any.
/// It's exactly the total number of input bytes fed to `update`.
pub fn count(&self) -> Count {
// Remember that self.count is *per-leaf*.
let mut ret = self
.count
.wrapping_mul(DEGREE as Count)
.wrapping_add(self.buf_len as Count);
if self.is_keyed {
ret -= (DEGREE * BLOCKBYTES) as Count;
}
ret
}
}
#[cfg(feature = "std")]
impl std::io::Write for State {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.update(buf);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
impl fmt::Debug for State {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"State {{ count: {}, hash_length: {} }}",
self.count(),
self.hash_length,
)
}
}
impl Default for State {
fn default() -> Self {
Self::with_params(&Params::default())
}
}
// Compress each of the four finalized hashes into the root words as input,
// using two compressions. Note that even if a future version of this
// implementation supports the hash_length parameter and sets it as associated
// data for all nodes, this step must still use the untruncated output of each
// leaf. Note also that, as mentioned above, the root node doesn't hash any key
// bytes.
fn finalize_root_words(
leaf_words: &[[Word; 8]; DEGREE],
root_words: &mut [Word; 8],
hash_length: u8,
imp: Implementation,
) -> Hash {
debug_assert_eq!(OUTBYTES, 8 * size_of::<Word>());
let mut block = [0; DEGREE * OUTBYTES];
for (word, chunk) in leaf_words
.iter()
.flat_map(|words| words.iter())
.zip(block.chunks_exact_mut(size_of::<Word>()))
{
chunk.copy_from_slice(&word.to_le_bytes());
}
imp.compress1_loop(
&block,
root_words,
0,
LastNode::Yes,
Finalize::Yes,
Stride::Serial,
);
Hash {
bytes: crate::state_words_to_bytes(&root_words),
len: hash_length,
}
}
pub(crate) fn force_portable(params: &mut Params) {
params.implementation = Implementation::portable();
}
#[cfg(test)]
pub(crate) mod test {
use super::*;
use crate::paint_test_input;
// This is a simple reference implementation without the complicated buffering or parameter
// support of the real implementation. We need this because the official test vectors don't
// include any inputs large enough to exercise all the branches in the buffering logic.
fn blake2bp_reference(input: &[u8]) -> Hash {
let mut leaves = arrayvec::ArrayVec::<[_; DEGREE]>::new();
for leaf_index in 0..DEGREE {
leaves.push(
crate::Params::new()
.fanout(DEGREE as u8)
.max_depth(2)
.node_offset(leaf_index as u64)
.inner_hash_length(OUTBYTES)
.to_state(),
);
}
leaves[DEGREE - 1].set_last_node(true);
for (i, chunk) in input.chunks(BLOCKBYTES).enumerate() {
leaves[i % DEGREE].update(chunk);
}
let mut root = crate::Params::new()
.fanout(DEGREE as u8)
.max_depth(2)
.node_depth(1)
.inner_hash_length(OUTBYTES)
.last_node(true)
.to_state();
for leaf in &mut leaves {
root.update(leaf.finalize().as_bytes());
}
root.finalize()
}
#[test]
fn test_against_reference() {
let mut buf = [0; 21 * BLOCKBYTES];
paint_test_input(&mut buf);
// - 8 blocks is just enought to fill the double buffer.
// - 9 blocks triggers the "perform one compression on the double buffer" case.
// - 11 blocks is the largest input where only one compression may be performed, on the
// first half of the buffer, because there's not enough input to avoid needing to
// finalize the second half.
// - 12 blocks triggers the "perform both compressions in the double buffer" case.
// - 15 blocks is the largest input where, after compressing 8 blocks from the buffer,
// there's not enough input to hash directly from memory.
// - 16 blocks triggers "after emptying the buffer, hash directly from memory".
for num_blocks in 0..=20 {
for &extra in &[0, 1, BLOCKBYTES - 1] {
for &portable in &[false, true] {
// eprintln!("\ncase -----");
// dbg!(num_blocks);
// dbg!(extra);
// dbg!(portable);
// First hash the input all at once, as a sanity check.
let mut params = Params::new();
if portable {
force_portable(&mut params);
}
let input = &buf[..num_blocks * BLOCKBYTES + extra];
let expected = blake2bp_reference(&input);
let mut state = params.to_state();
let found = state.update(input).finalize();
assert_eq!(expected, found);
// Then, do it again, but buffer 1 byte of input first. That causes the buffering
// branch to trigger.
let mut state = params.to_state();
let maybe_one = cmp::min(1, input.len());
state.update(&input[..maybe_one]);
assert_eq!(maybe_one as Count, state.count());
// Do a throwaway finalize here to check for idempotency.
state.finalize();
state.update(&input[maybe_one..]);
assert_eq!(input.len() as Count, state.count());
let found = state.finalize();
assert_eq!(expected, found);
// Finally, do it again with the all-at-once interface.
assert_eq!(expected, blake2bp(input));
}
}
}
}
}

View File

@ -1,565 +0,0 @@
use crate::*;
use arrayref::array_ref;
use core::cmp;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub const MAX_DEGREE: usize = 4;
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
pub const MAX_DEGREE: usize = 1;
// Variants other than Portable are unreachable in no_std, unless CPU features
// are explicitly enabled for the build with e.g. RUSTFLAGS="-C target-feature=avx2".
// This might change in the future if is_x86_feature_detected moves into libcore.
#[allow(dead_code)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum Platform {
Portable,
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
SSE41,
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
AVX2,
}
#[derive(Clone, Copy, Debug)]
pub struct Implementation(Platform);
impl Implementation {
pub fn detect() -> Self {
// Try the different implementations in order of how fast/modern they
// are. Currently on non-x86, everything just uses portable.
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{
if let Some(avx2_impl) = Self::avx2_if_supported() {
return avx2_impl;
}
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
{
if let Some(sse41_impl) = Self::sse41_if_supported() {
return sse41_impl;
}
}
Self::portable()
}
pub fn portable() -> Self {
Implementation(Platform::Portable)
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[allow(unreachable_code)]
pub fn sse41_if_supported() -> Option<Self> {
// Check whether SSE4.1 support is assumed by the build.
#[cfg(target_feature = "sse4.1")]
{
return Some(Implementation(Platform::SSE41));
}
// Otherwise dynamically check for support if we can.
#[cfg(feature = "std")]
{
if is_x86_feature_detected!("sse4.1") {
return Some(Implementation(Platform::SSE41));
}
}
None
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[allow(unreachable_code)]
pub fn avx2_if_supported() -> Option<Self> {
// Check whether AVX2 support is assumed by the build.
#[cfg(target_feature = "avx2")]
{
return Some(Implementation(Platform::AVX2));
}
// Otherwise dynamically check for support if we can.
#[cfg(feature = "std")]
{
if is_x86_feature_detected!("avx2") {
return Some(Implementation(Platform::AVX2));
}
}
None
}
pub fn degree(&self) -> usize {
match self.0 {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
Platform::AVX2 => avx2::DEGREE,
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
Platform::SSE41 => sse41::DEGREE,
Platform::Portable => 1,
}
}
pub fn compress1_loop(
&self,
input: &[u8],
words: &mut [Word; 8],
count: Count,
last_node: LastNode,
finalize: Finalize,
stride: Stride,
) {
match self.0 {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
Platform::AVX2 => unsafe {
avx2::compress1_loop(input, words, count, last_node, finalize, stride);
},
// Note that there's an SSE version of compress1 in the official C
// implementation, but I haven't ported it yet.
_ => {
portable::compress1_loop(input, words, count, last_node, finalize, stride);
}
}
}
pub fn compress2_loop(&self, jobs: &mut [Job; 2], finalize: Finalize, stride: Stride) {
match self.0 {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
Platform::AVX2 | Platform::SSE41 => unsafe {
sse41::compress2_loop(jobs, finalize, stride)
},
_ => panic!("unsupported"),
}
}
pub fn compress4_loop(&self, jobs: &mut [Job; 4], finalize: Finalize, stride: Stride) {
match self.0 {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
Platform::AVX2 => unsafe { avx2::compress4_loop(jobs, finalize, stride) },
_ => panic!("unsupported"),
}
}
}
pub struct Job<'a, 'b> {
pub input: &'a [u8],
pub words: &'b mut [Word; 8],
pub count: Count,
pub last_node: LastNode,
}
impl<'a, 'b> core::fmt::Debug for Job<'a, 'b> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// NB: Don't print the words. Leaking them would allow length extension.
write!(
f,
"Job {{ input_len: {}, count: {}, last_node: {} }}",
self.input.len(),
self.count,
self.last_node.yes(),
)
}
}
// Finalize could just be a bool, but this is easier to read at callsites.
#[derive(Clone, Copy, Debug)]
pub enum Finalize {
Yes,
No,
}
impl Finalize {
pub fn yes(&self) -> bool {
match self {
Finalize::Yes => true,
Finalize::No => false,
}
}
}
// Like Finalize, this is easier to read at callsites.
#[derive(Clone, Copy, Debug)]
pub enum LastNode {
Yes,
No,
}
impl LastNode {
pub fn yes(&self) -> bool {
match self {
LastNode::Yes => true,
LastNode::No => false,
}
}
}
#[derive(Clone, Copy, Debug)]
pub enum Stride {
Serial, // BLAKE2b/BLAKE2s
Parallel, // BLAKE2bp/BLAKE2sp
}
impl Stride {
pub fn padded_blockbytes(&self) -> usize {
match self {
Stride::Serial => BLOCKBYTES,
Stride::Parallel => blake2bp::DEGREE * BLOCKBYTES,
}
}
}
pub(crate) fn count_low(count: Count) -> Word {
count as Word
}
pub(crate) fn count_high(count: Count) -> Word {
(count >> 8 * size_of::<Word>()) as Word
}
pub(crate) fn assemble_count(low: Word, high: Word) -> Count {
low as Count + ((high as Count) << 8 * size_of::<Word>())
}
pub(crate) fn flag_word(flag: bool) -> Word {
if flag {
!0
} else {
0
}
}
// Pull a array reference at the given offset straight from the input, if
// there's a full block of input available. If there's only a partial block,
// copy it into the provided buffer, and return an array reference that. Along
// with the array, return the number of bytes of real input, and whether the
// input can be finalized (i.e. whether there aren't any more bytes after this
// block). Note that this is written so that the optimizer can elide bounds
// checks, see: https://godbolt.org/z/0hH2bC
pub fn final_block<'a>(
input: &'a [u8],
offset: usize,
buffer: &'a mut [u8; BLOCKBYTES],
stride: Stride,
) -> (&'a [u8; BLOCKBYTES], usize, bool) {
let capped_offset = cmp::min(offset, input.len());
let offset_slice = &input[capped_offset..];
if offset_slice.len() >= BLOCKBYTES {
let block = array_ref!(offset_slice, 0, BLOCKBYTES);
let should_finalize = offset_slice.len() <= stride.padded_blockbytes();
(block, BLOCKBYTES, should_finalize)
} else {
// Copy the final block to the front of the block buffer. The rest of
// the buffer is assumed to be initialized to zero.
buffer[..offset_slice.len()].copy_from_slice(offset_slice);
(buffer, offset_slice.len(), true)
}
}
pub fn input_debug_asserts(input: &[u8], finalize: Finalize) {
// If we're not finalizing, the input must not be empty, and it must be an
// even multiple of the block size.
if !finalize.yes() {
debug_assert!(!input.is_empty());
debug_assert_eq!(0, input.len() % BLOCKBYTES);
}
}
#[cfg(test)]
mod test {
use super::*;
use arrayvec::ArrayVec;
use core::mem::size_of;
#[test]
fn test_detection() {
assert_eq!(Platform::Portable, Implementation::portable().0);
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[cfg(feature = "std")]
{
if is_x86_feature_detected!("avx2") {
assert_eq!(Platform::AVX2, Implementation::detect().0);
assert_eq!(
Platform::AVX2,
Implementation::avx2_if_supported().unwrap().0
);
assert_eq!(
Platform::SSE41,
Implementation::sse41_if_supported().unwrap().0
);
} else if is_x86_feature_detected!("sse4.1") {
assert_eq!(Platform::SSE41, Implementation::detect().0);
assert!(Implementation::avx2_if_supported().is_none());
assert_eq!(
Platform::SSE41,
Implementation::sse41_if_supported().unwrap().0
);
} else {
assert_eq!(Platform::Portable, Implementation::detect().0);
assert!(Implementation::avx2_if_supported().is_none());
assert!(Implementation::sse41_if_supported().is_none());
}
}
}
// TODO: Move all of these case tests into the implementation files.
fn exercise_cases<F>(mut f: F)
where
F: FnMut(Stride, usize, LastNode, Finalize, Count),
{
// Chose counts to hit the relevant overflow cases.
let counts = &[
(0 as Count),
((1 as Count) << (8 * size_of::<Word>())) - BLOCKBYTES as Count,
(0 as Count).wrapping_sub(BLOCKBYTES as Count),
];
for &stride in &[Stride::Serial, Stride::Parallel] {
let lengths = [
0,
1,
BLOCKBYTES - 1,
BLOCKBYTES,
BLOCKBYTES + 1,
2 * BLOCKBYTES - 1,
2 * BLOCKBYTES,
2 * BLOCKBYTES + 1,
stride.padded_blockbytes() - 1,
stride.padded_blockbytes(),
stride.padded_blockbytes() + 1,
2 * stride.padded_blockbytes() - 1,
2 * stride.padded_blockbytes(),
2 * stride.padded_blockbytes() + 1,
];
for &length in &lengths {
for &last_node in &[LastNode::No, LastNode::Yes] {
for &finalize in &[Finalize::No, Finalize::Yes] {
if !finalize.yes() && (length == 0 || length % BLOCKBYTES != 0) {
// Skip these cases, they're invalid.
continue;
}
for &count in counts {
// eprintln!("\ncase -----");
// dbg!(stride);
// dbg!(length);
// dbg!(last_node);
// dbg!(finalize);
// dbg!(count);
f(stride, length, last_node, finalize, count);
}
}
}
}
}
}
fn initial_test_words(input_index: usize) -> [Word; 8] {
crate::Params::new()
.node_offset(input_index as u64)
.to_words()
}
// Use the portable implementation, one block at a time, to compute the
// final state words expected for a given test case.
fn reference_compression(
input: &[u8],
stride: Stride,
last_node: LastNode,
finalize: Finalize,
mut count: Count,
input_index: usize,
) -> [Word; 8] {
let mut words = initial_test_words(input_index);
let mut offset = 0;
while offset == 0 || offset < input.len() {
let block_size = cmp::min(BLOCKBYTES, input.len() - offset);
let maybe_finalize = if offset + stride.padded_blockbytes() < input.len() {
Finalize::No
} else {
finalize
};
portable::compress1_loop(
&input[offset..][..block_size],
&mut words,
count,
last_node,
maybe_finalize,
Stride::Serial,
);
offset += stride.padded_blockbytes();
count = count.wrapping_add(BLOCKBYTES as Count);
}
words
}
// For various loop lengths and finalization parameters, make sure that the
// implementation gives the same answer as the portable implementation does
// when invoked one block at a time. (So even the portable implementation
// itself is being tested here, to make sure its loop is correct.) Note
// that this doesn't include any fixed test vectors; those are taken from
// the blake2-kat.json file (copied from upstream) and tested elsewhere.
fn exercise_compress1_loop(implementation: Implementation) {
let mut input = [0; 100 * BLOCKBYTES];
paint_test_input(&mut input);
exercise_cases(|stride, length, last_node, finalize, count| {
let reference_words =
reference_compression(&input[..length], stride, last_node, finalize, count, 0);
let mut test_words = initial_test_words(0);
implementation.compress1_loop(
&input[..length],
&mut test_words,
count,
last_node,
finalize,
stride,
);
assert_eq!(reference_words, test_words);
});
}
#[test]
fn test_compress1_loop_portable() {
exercise_compress1_loop(Implementation::portable());
}
#[test]
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn test_compress1_loop_sse41() {
// Currently this just falls back to portable, but we test it anyway.
if let Some(imp) = Implementation::sse41_if_supported() {
exercise_compress1_loop(imp);
}
}
#[test]
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn test_compress1_loop_avx2() {
if let Some(imp) = Implementation::avx2_if_supported() {
exercise_compress1_loop(imp);
}
}
// I use ArrayVec everywhere in here becuase currently these tests pass
// under no_std. I might decide that's not worth maintaining at some point,
// since really all we care about with no_std is that the library builds,
// but for now it's here. Everything is keyed off of this N constant so
// that it's easy to copy the code to exercise_compress4_loop.
fn exercise_compress2_loop(implementation: Implementation) {
const N: usize = 2;
let mut input_buffer = [0; 100 * BLOCKBYTES];
paint_test_input(&mut input_buffer);
let mut inputs = ArrayVec::<[_; N]>::new();
for i in 0..N {
inputs.push(&input_buffer[i..]);
}
exercise_cases(|stride, length, last_node, finalize, count| {
let mut reference_words = ArrayVec::<[_; N]>::new();
for i in 0..N {
let words = reference_compression(
&inputs[i][..length],
stride,
last_node,
finalize,
count.wrapping_add((i * BLOCKBYTES) as Count),
i,
);
reference_words.push(words);
}
let mut test_words = ArrayVec::<[_; N]>::new();
for i in 0..N {
test_words.push(initial_test_words(i));
}
let mut jobs = ArrayVec::<[_; N]>::new();
for (i, words) in test_words.iter_mut().enumerate() {
jobs.push(Job {
input: &inputs[i][..length],
words,
count: count.wrapping_add((i * BLOCKBYTES) as Count),
last_node,
});
}
let mut jobs = jobs.into_inner().expect("full");
implementation.compress2_loop(&mut jobs, finalize, stride);
for i in 0..N {
assert_eq!(reference_words[i], test_words[i], "words {} unequal", i);
}
});
}
#[test]
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn test_compress2_loop_sse41() {
if let Some(imp) = Implementation::sse41_if_supported() {
exercise_compress2_loop(imp);
}
}
#[test]
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn test_compress2_loop_avx2() {
// Currently this just falls back to SSE4.1, but we test it anyway.
if let Some(imp) = Implementation::avx2_if_supported() {
exercise_compress2_loop(imp);
}
}
// Copied from exercise_compress2_loop, with a different value of N and an
// interior call to compress4_loop.
fn exercise_compress4_loop(implementation: Implementation) {
const N: usize = 4;
let mut input_buffer = [0; 100 * BLOCKBYTES];
paint_test_input(&mut input_buffer);
let mut inputs = ArrayVec::<[_; N]>::new();
for i in 0..N {
inputs.push(&input_buffer[i..]);
}
exercise_cases(|stride, length, last_node, finalize, count| {
let mut reference_words = ArrayVec::<[_; N]>::new();
for i in 0..N {
let words = reference_compression(
&inputs[i][..length],
stride,
last_node,
finalize,
count.wrapping_add((i * BLOCKBYTES) as Count),
i,
);
reference_words.push(words);
}
let mut test_words = ArrayVec::<[_; N]>::new();
for i in 0..N {
test_words.push(initial_test_words(i));
}
let mut jobs = ArrayVec::<[_; N]>::new();
for (i, words) in test_words.iter_mut().enumerate() {
jobs.push(Job {
input: &inputs[i][..length],
words,
count: count.wrapping_add((i * BLOCKBYTES) as Count),
last_node,
});
}
let mut jobs = jobs.into_inner().expect("full");
implementation.compress4_loop(&mut jobs, finalize, stride);
for i in 0..N {
assert_eq!(reference_words[i], test_words[i], "words {} unequal", i);
}
});
}
#[test]
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn test_compress4_loop_avx2() {
if let Some(imp) = Implementation::avx2_if_supported() {
exercise_compress4_loop(imp);
}
}
#[test]
fn sanity_check_count_size() {
assert_eq!(size_of::<Count>(), 2 * size_of::<Word>());
}
}

View File

@ -1,674 +0,0 @@
//! [![GitHub](https://img.shields.io/github/tag/oconnor663/blake2_simd.svg?label=GitHub)](https://github.com/oconnor663/blake2_simd) [![crates.io](https://img.shields.io/crates/v/blake2b_simd.svg)](https://crates.io/crates/blake2b_simd) [![Actions Status](https://github.com/oconnor663/blake2_simd/workflows/tests/badge.svg)](https://github.com/oconnor663/blake2_simd/actions)
//!
//! An implementation of the BLAKE2b and BLAKE2bp hash functions. See also
//! [`blake2s_simd`](https://docs.rs/blake2s_simd).
//!
//! This crate includes:
//!
//! - 100% stable Rust.
//! - SIMD implementations based on Samuel Neves' [`blake2-avx2`](https://github.com/sneves/blake2-avx2).
//! These are very fast. For benchmarks, see [the Performance section of the
//! README](https://github.com/oconnor663/blake2_simd#performance).
//! - Portable, safe implementations for other platforms.
//! - Dynamic CPU feature detection. Binaries include multiple implementations by default and
//! choose the fastest one the processor supports at runtime.
//! - All the features from the [the BLAKE2 spec](https://blake2.net/blake2.pdf), like adjustable
//! length, keying, and associated data for tree hashing.
//! - `no_std` support. The `std` Cargo feature is on by default, for CPU feature detection and
//! for implementing `std::io::Write`.
//! - Support for computing multiple BLAKE2b hashes in parallel, matching the efficiency of
//! BLAKE2bp. See the [`many`](many/index.html) module.
//!
//! # Example
//!
//! ```
//! use blake2b_simd::{blake2b, Params};
//!
//! let expected = "ca002330e69d3e6b84a46a56a6533fd79d51d97a3bb7cad6c2ff43b354185d6d\
//! c1e723fb3db4ae0737e120378424c714bb982d9dc5bbd7a0ab318240ddd18f8d";
//! let hash = blake2b(b"foo");
//! assert_eq!(expected, &hash.to_hex());
//!
//! let hash = Params::new()
//! .hash_length(16)
//! .key(b"The Magic Words are Squeamish Ossifrage")
//! .personal(b"L. P. Waterhouse")
//! .to_state()
//! .update(b"foo")
//! .update(b"bar")
//! .update(b"baz")
//! .finalize();
//! assert_eq!("ee8ff4e9be887297cf79348dc35dab56", &hash.to_hex());
//! ```
#![cfg_attr(not(feature = "std"), no_std)]
use arrayref::{array_refs, mut_array_refs};
use core::cmp;
use core::fmt;
use core::mem::size_of;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
mod avx2;
mod portable;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
mod sse41;
pub mod blake2bp;
mod guts;
pub mod many;
#[cfg(test)]
mod test;
type Word = u64;
type Count = u128;
/// The max hash length.
pub const OUTBYTES: usize = 8 * size_of::<Word>();
/// The max key length.
pub const KEYBYTES: usize = 8 * size_of::<Word>();
/// The max salt length.
pub const SALTBYTES: usize = 2 * size_of::<Word>();
/// The max personalization length.
pub const PERSONALBYTES: usize = 2 * size_of::<Word>();
/// The number input bytes passed to each call to the compression function. Small benchmarks need
/// to use an even multiple of `BLOCKBYTES`, or else their apparent throughput will be low.
pub const BLOCKBYTES: usize = 16 * size_of::<Word>();
const IV: [Word; 8] = [
0x6A09E667F3BCC908,
0xBB67AE8584CAA73B,
0x3C6EF372FE94F82B,
0xA54FF53A5F1D36F1,
0x510E527FADE682D1,
0x9B05688C2B3E6C1F,
0x1F83D9ABFB41BD6B,
0x5BE0CD19137E2179,
];
const SIGMA: [[u8; 16]; 12] = [
[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],
[13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
[6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
[10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
[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],
];
/// Compute the BLAKE2b hash of a slice of bytes all at once, using default
/// parameters.
///
/// # Example
///
/// ```
/// # use blake2b_simd::{blake2b, Params};
/// let expected = "ca002330e69d3e6b84a46a56a6533fd79d51d97a3bb7cad6c2ff43b354185d6d\
/// c1e723fb3db4ae0737e120378424c714bb982d9dc5bbd7a0ab318240ddd18f8d";
/// let hash = blake2b(b"foo");
/// assert_eq!(expected, &hash.to_hex());
/// ```
pub fn blake2b(input: &[u8]) -> Hash {
Params::new().hash(input)
}
/// A parameter builder that exposes all the non-default BLAKE2 features.
///
/// Apart from `hash_length`, which controls the length of the final `Hash`,
/// all of these parameters are just associated data that gets mixed with the
/// input. For more details, see [the BLAKE2 spec](https://blake2.net/blake2.pdf).
///
/// Several of the parameters have a valid range defined in the spec and
/// documented below. Trying to set an invalid parameter will panic.
///
/// # Example
///
/// ```
/// # use blake2b_simd::Params;
/// // Create a Params object with a secret key and a non-default length.
/// let mut params = Params::new();
/// params.key(b"my secret key");
/// params.hash_length(16);
///
/// // Use those params to hash an input all at once.
/// let hash = params.hash(b"my input");
///
/// // Or use those params to build an incremental State.
/// let mut state = params.to_state();
/// ```
#[derive(Clone)]
pub struct Params {
hash_length: u8,
key_length: u8,
key_block: [u8; BLOCKBYTES],
salt: [u8; SALTBYTES],
personal: [u8; PERSONALBYTES],
fanout: u8,
max_depth: u8,
max_leaf_length: u32,
node_offset: u64,
node_depth: u8,
inner_hash_length: u8,
last_node: guts::LastNode,
implementation: guts::Implementation,
}
impl Params {
/// Equivalent to `Params::default()`.
#[inline]
pub fn new() -> Self {
Self {
hash_length: OUTBYTES as u8,
key_length: 0,
key_block: [0; BLOCKBYTES],
salt: [0; SALTBYTES],
personal: [0; PERSONALBYTES],
// NOTE: fanout and max_depth don't default to zero!
fanout: 1,
max_depth: 1,
max_leaf_length: 0,
node_offset: 0,
node_depth: 0,
inner_hash_length: 0,
last_node: guts::LastNode::No,
implementation: guts::Implementation::detect(),
}
}
#[inline(always)]
fn to_words(&self) -> [Word; 8] {
let (salt_left, salt_right) = array_refs!(&self.salt, SALTBYTES / 2, SALTBYTES / 2);
let (personal_left, personal_right) =
array_refs!(&self.personal, PERSONALBYTES / 2, PERSONALBYTES / 2);
[
IV[0]
^ self.hash_length as u64
^ (self.key_length as u64) << 8
^ (self.fanout as u64) << 16
^ (self.max_depth as u64) << 24
^ (self.max_leaf_length as u64) << 32,
IV[1] ^ self.node_offset,
IV[2] ^ self.node_depth as u64 ^ (self.inner_hash_length as u64) << 8,
IV[3],
IV[4] ^ Word::from_le_bytes(*salt_left),
IV[5] ^ Word::from_le_bytes(*salt_right),
IV[6] ^ Word::from_le_bytes(*personal_left),
IV[7] ^ Word::from_le_bytes(*personal_right),
]
}
/// Hash an input all at once with these parameters.
#[inline]
pub fn hash(&self, input: &[u8]) -> Hash {
// If there's a key, just fall back to using the State.
if self.key_length > 0 {
return self.to_state().update(input).finalize();
}
let mut words = self.to_words();
self.implementation.compress1_loop(
input,
&mut words,
0,
self.last_node,
guts::Finalize::Yes,
guts::Stride::Serial,
);
Hash {
bytes: state_words_to_bytes(&words),
len: self.hash_length,
}
}
/// Construct a `State` object based on these parameters, for hashing input
/// incrementally.
pub fn to_state(&self) -> State {
State::with_params(self)
}
/// Set the length of the final hash in bytes, from 1 to `OUTBYTES` (64). Apart from
/// controlling the length of the final `Hash`, this is also associated data, and changing it
/// will result in a totally different hash.
#[inline]
pub fn hash_length(&mut self, length: usize) -> &mut Self {
assert!(
1 <= length && length <= OUTBYTES,
"Bad hash length: {}",
length
);
self.hash_length = length as u8;
self
}
/// Use a secret key, so that BLAKE2 acts as a MAC. The maximum key length is `KEYBYTES` (64).
/// An empty key is equivalent to having no key at all.
#[inline]
pub fn key(&mut self, key: &[u8]) -> &mut Self {
assert!(key.len() <= KEYBYTES, "Bad key length: {}", key.len());
self.key_length = key.len() as u8;
self.key_block = [0; BLOCKBYTES];
self.key_block[..key.len()].copy_from_slice(key);
self
}
/// At most `SALTBYTES` (16). Shorter salts are padded with null bytes. An empty salt is
/// equivalent to having no salt at all.
#[inline]
pub fn salt(&mut self, salt: &[u8]) -> &mut Self {
assert!(salt.len() <= SALTBYTES, "Bad salt length: {}", salt.len());
self.salt = [0; SALTBYTES];
self.salt[..salt.len()].copy_from_slice(salt);
self
}
/// At most `PERSONALBYTES` (16). Shorter personalizations are padded with null bytes. An empty
/// personalization is equivalent to having no personalization at all.
#[inline]
pub fn personal(&mut self, personalization: &[u8]) -> &mut Self {
assert!(
personalization.len() <= PERSONALBYTES,
"Bad personalization length: {}",
personalization.len()
);
self.personal = [0; PERSONALBYTES];
self.personal[..personalization.len()].copy_from_slice(personalization);
self
}
/// From 0 (meaning unlimited) to 255. The default is 1 (meaning sequential).
#[inline]
pub fn fanout(&mut self, fanout: u8) -> &mut Self {
self.fanout = fanout;
self
}
/// From 0 (meaning BLAKE2X B2 hashes), through 1 (the default, meaning sequential) to 255 (meaning unlimited).
#[inline]
pub fn max_depth(&mut self, depth: u8) -> &mut Self {
self.max_depth = depth;
self
}
/// From 0 (the default, meaning unlimited or sequential) to `2^32 - 1`.
#[inline]
pub fn max_leaf_length(&mut self, length: u32) -> &mut Self {
self.max_leaf_length = length;
self
}
/// From 0 (the default, meaning first, leftmost, leaf, or sequential) to `2^64 - 1`.
#[inline]
pub fn node_offset(&mut self, offset: u64) -> &mut Self {
self.node_offset = offset;
self
}
/// From 0 (the default, meaning leaf or sequential) to 255.
#[inline]
pub fn node_depth(&mut self, depth: u8) -> &mut Self {
self.node_depth = depth;
self
}
/// From 0 (the default, meaning sequential) to `OUTBYTES` (64).
#[inline]
pub fn inner_hash_length(&mut self, length: usize) -> &mut Self {
assert!(length <= OUTBYTES, "Bad inner hash length: {}", length);
self.inner_hash_length = length as u8;
self
}
/// Indicates the rightmost node in a row. This can also be changed on the
/// `State` object, potentially after hashing has begun. See
/// [`State::set_last_node`].
///
/// [`State::set_last_node`]: struct.State.html#method.set_last_node
#[inline]
pub fn last_node(&mut self, last_node: bool) -> &mut Self {
self.last_node = if last_node {
guts::LastNode::Yes
} else {
guts::LastNode::No
};
self
}
}
impl Default for Params {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for Params {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Params {{ hash_length: {}, key_length: {}, salt: {:?}, personal: {:?}, fanout: {}, \
max_depth: {}, max_leaf_length: {}, node_offset: {}, node_depth: {}, \
inner_hash_length: {}, last_node: {} }}",
self.hash_length,
// NB: Don't print the key itself. Debug shouldn't leak secrets.
self.key_length,
&self.salt,
&self.personal,
self.fanout,
self.max_depth,
self.max_leaf_length,
self.node_offset,
self.node_depth,
self.inner_hash_length,
self.last_node.yes(),
)
}
}
/// An incremental hasher for BLAKE2b.
///
/// To construct a `State` with non-default parameters, see `Params::to_state`.
///
/// # Example
///
/// ```
/// use blake2b_simd::{State, blake2b};
///
/// let mut state = blake2b_simd::State::new();
///
/// state.update(b"foo");
/// assert_eq!(blake2b(b"foo"), state.finalize());
///
/// state.update(b"bar");
/// assert_eq!(blake2b(b"foobar"), state.finalize());
/// ```
#[derive(Clone)]
pub struct State {
words: [Word; 8],
count: Count,
buf: [u8; BLOCKBYTES],
buflen: u8,
last_node: guts::LastNode,
hash_length: u8,
implementation: guts::Implementation,
is_keyed: bool,
}
impl State {
/// Equivalent to `State::default()` or `Params::default().to_state()`.
pub fn new() -> Self {
Self::with_params(&Params::default())
}
fn with_params(params: &Params) -> Self {
let mut state = Self {
words: params.to_words(),
count: 0,
buf: [0; BLOCKBYTES],
buflen: 0,
last_node: params.last_node,
hash_length: params.hash_length,
implementation: params.implementation,
is_keyed: params.key_length > 0,
};
if state.is_keyed {
state.buf = params.key_block;
state.buflen = state.buf.len() as u8;
}
state
}
fn fill_buf(&mut self, input: &mut &[u8]) {
let take = cmp::min(BLOCKBYTES - self.buflen as usize, input.len());
self.buf[self.buflen as usize..self.buflen as usize + take].copy_from_slice(&input[..take]);
self.buflen += take as u8;
*input = &input[take..];
}
// If the state already has some input in its buffer, try to fill the buffer and perform a
// compression. However, only do the compression if there's more input coming, otherwise it
// will give the wrong hash it the caller finalizes immediately after.
fn compress_buffer_if_possible(&mut self, input: &mut &[u8]) {
if self.buflen > 0 {
self.fill_buf(input);
if !input.is_empty() {
self.implementation.compress1_loop(
&self.buf,
&mut self.words,
self.count,
self.last_node,
guts::Finalize::No,
guts::Stride::Serial,
);
self.count = self.count.wrapping_add(BLOCKBYTES as Count);
self.buflen = 0;
}
}
}
/// Add input to the hash. You can call `update` any number of times.
pub fn update(&mut self, mut input: &[u8]) -> &mut Self {
// If we have a partial buffer, try to complete it.
self.compress_buffer_if_possible(&mut input);
// While there's more than a block of input left (which also means we cleared the buffer
// above), compress blocks directly without copying.
let mut end = input.len().saturating_sub(1);
end -= end % BLOCKBYTES;
if end > 0 {
self.implementation.compress1_loop(
&input[..end],
&mut self.words,
self.count,
self.last_node,
guts::Finalize::No,
guts::Stride::Serial,
);
self.count = self.count.wrapping_add(end as Count);
input = &input[end..];
}
// Buffer any remaining input, to be either compressed or finalized in a subsequent call.
// Note that this represents some copying overhead, which in theory we could avoid in
// all-at-once setting. A function hardcoded for exactly BLOCKSIZE input bytes is about 10%
// faster than using this implementation for the same input.
self.fill_buf(&mut input);
self
}
/// Finalize the state and return a `Hash`. This method is idempotent, and calling it multiple
/// times will give the same result. It's also possible to `update` with more input in between.
pub fn finalize(&self) -> Hash {
let mut words_copy = self.words;
self.implementation.compress1_loop(
&self.buf[..self.buflen as usize],
&mut words_copy,
self.count,
self.last_node,
guts::Finalize::Yes,
guts::Stride::Serial,
);
Hash {
bytes: state_words_to_bytes(&words_copy),
len: self.hash_length,
}
}
/// Set a flag indicating that this is the last node of its level in a tree hash. This is
/// equivalent to [`Params::last_node`], except that it can be set at any time before calling
/// `finalize`. That allows callers to begin hashing a node without knowing ahead of time
/// whether it's the last in its level. For more details about the intended use of this flag
/// [the BLAKE2 spec].
///
/// [`Params::last_node`]: struct.Params.html#method.last_node
/// [the BLAKE2 spec]: https://blake2.net/blake2.pdf
pub fn set_last_node(&mut self, last_node: bool) -> &mut Self {
self.last_node = if last_node {
guts::LastNode::Yes
} else {
guts::LastNode::No
};
self
}
/// Return the total number of bytes input so far.
///
/// Note that `count` doesn't include the bytes of the key block, if any.
/// It's exactly the total number of input bytes fed to `update`.
pub fn count(&self) -> Count {
let mut ret = self.count.wrapping_add(self.buflen as Count);
if self.is_keyed {
ret -= BLOCKBYTES as Count;
}
ret
}
}
#[inline(always)]
fn state_words_to_bytes(state_words: &[Word; 8]) -> [u8; OUTBYTES] {
let mut bytes = [0; OUTBYTES];
{
const W: usize = size_of::<Word>();
let refs = mut_array_refs!(&mut bytes, W, W, W, W, W, W, W, W);
*refs.0 = state_words[0].to_le_bytes();
*refs.1 = state_words[1].to_le_bytes();
*refs.2 = state_words[2].to_le_bytes();
*refs.3 = state_words[3].to_le_bytes();
*refs.4 = state_words[4].to_le_bytes();
*refs.5 = state_words[5].to_le_bytes();
*refs.6 = state_words[6].to_le_bytes();
*refs.7 = state_words[7].to_le_bytes();
}
bytes
}
#[cfg(feature = "std")]
impl std::io::Write for State {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.update(buf);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
impl fmt::Debug for State {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// NB: Don't print the words. Leaking them would allow length extension.
write!(
f,
"State {{ count: {}, hash_length: {}, last_node: {} }}",
self.count(),
self.hash_length,
self.last_node.yes(),
)
}
}
impl Default for State {
fn default() -> Self {
Self::with_params(&Params::default())
}
}
type HexString = arrayvec::ArrayString<[u8; 2 * OUTBYTES]>;
/// A finalized BLAKE2 hash, with constant-time equality.
#[derive(Clone, Copy)]
pub struct Hash {
bytes: [u8; OUTBYTES],
len: u8,
}
impl Hash {
/// Convert the hash to a byte slice. Note that if you're using BLAKE2 as a MAC, you need
/// constant time equality, which `&[u8]` doesn't provide.
pub fn as_bytes(&self) -> &[u8] {
&self.bytes[..self.len as usize]
}
/// Convert the hash to a byte array. Note that if you're using BLAKE2 as a
/// MAC, you need constant time equality, which arrays don't provide. This
/// panics in debug mode if the length of the hash isn't `OUTBYTES`.
#[inline]
pub fn as_array(&self) -> &[u8; OUTBYTES] {
debug_assert_eq!(self.len as usize, OUTBYTES);
&self.bytes
}
/// Convert the hash to a lowercase hexadecimal
/// [`ArrayString`](https://docs.rs/arrayvec/0.4/arrayvec/struct.ArrayString.html).
pub fn to_hex(&self) -> HexString {
bytes_to_hex(self.as_bytes())
}
}
fn bytes_to_hex(bytes: &[u8]) -> HexString {
let mut s = arrayvec::ArrayString::new();
let table = b"0123456789abcdef";
for &b in bytes {
s.push(table[(b >> 4) as usize] as char);
s.push(table[(b & 0xf) as usize] as char);
}
s
}
/// This implementation is constant time, if the two hashes are the same length.
impl PartialEq for Hash {
fn eq(&self, other: &Hash) -> bool {
constant_time_eq::constant_time_eq(&self.as_bytes(), &other.as_bytes())
}
}
/// This implementation is constant time, if the slice is the same length as the hash.
impl PartialEq<[u8]> for Hash {
fn eq(&self, other: &[u8]) -> bool {
constant_time_eq::constant_time_eq(&self.as_bytes(), other)
}
}
impl Eq for Hash {}
impl AsRef<[u8]> for Hash {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl fmt::Debug for Hash {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Hash(0x{})", self.to_hex())
}
}
// Paint a byte pattern that won't repeat, so that we don't accidentally miss
// buffer offset bugs. This is the same as what Bao uses in its tests.
#[cfg(test)]
fn paint_test_input(buf: &mut [u8]) {
let mut offset = 0;
let mut counter: u32 = 1;
while offset < buf.len() {
let bytes = counter.to_le_bytes();
let take = cmp::min(bytes.len(), buf.len() - offset);
buf[offset..][..take].copy_from_slice(&bytes[..take]);
counter += 1;
offset += take;
}
}
// This module is pub for internal benchmarks only. Please don't use it.
#[doc(hidden)]
pub mod benchmarks {
use super::*;
pub fn force_portable(params: &mut Params) {
params.implementation = guts::Implementation::portable();
}
pub fn force_portable_blake2bp(params: &mut blake2bp::Params) {
blake2bp::force_portable(params);
}
}

View File

@ -1,529 +0,0 @@
//! Interfaces for hashing multiple inputs at once, using SIMD more
//! efficiently.
//!
//! The throughput of these interfaces is comparable to BLAKE2bp, about twice
//! the throughput of regular BLAKE2b when AVX2 is available.
//!
//! These interfaces can accept any number of inputs, and the implementation
//! does its best to parallelize them. In general, the more inputs you can pass
//! in at once the better. If you need to batch your inputs in smaller groups,
//! see the [`degree`](fn.degree.html) function for a good batch size.
//!
//! The implementation keeps working in parallel even when inputs are of
//! different lengths, by managing a working set of jobs whose input isn't yet
//! exhausted. However, if one or two inputs are much longer than the others,
//! and they're encountered only at the end, there might not be any remaining
//! work to parallelize them with. In this case, sorting the inputs
//! longest-first can improve parallelism.
//!
//! # Example
//!
//! ```
//! use blake2b_simd::{blake2b, State, many::update_many};
//!
//! let mut states = [
//! State::new(),
//! State::new(),
//! State::new(),
//! State::new(),
//! ];
//!
//! let inputs = [
//! &b"foo"[..],
//! &b"bar"[..],
//! &b"baz"[..],
//! &b"bing"[..],
//! ];
//!
//! update_many(states.iter_mut().zip(inputs.iter()));
//!
//! for (state, input) in states.iter_mut().zip(inputs.iter()) {
//! assert_eq!(blake2b(input), state.finalize());
//! }
//! ```
use crate::guts::{self, Finalize, Implementation, Job, LastNode, Stride};
use crate::state_words_to_bytes;
use crate::Count;
use crate::Hash;
use crate::Params;
use crate::State;
use crate::Word;
use crate::BLOCKBYTES;
use arrayref::array_mut_ref;
use arrayvec::ArrayVec;
use core::fmt;
/// The largest possible value of [`degree`](fn.degree.html) on the target
/// platform.
///
/// Note that this constant reflects the parallelism degree supported by this
/// crate, so it will change over time as support is added or removed. For
/// example, when Rust stabilizes AVX-512 support and this crate adds an
/// AVX-512 implementation, this constant will double on x86 targets. If that
/// implementation is an optional feature (e.g. because it's nightly-only), the
/// value of this constant will depend on that optional feature also.
pub const MAX_DEGREE: usize = guts::MAX_DEGREE;
/// The parallelism degree of the implementation, detected at runtime. If you
/// hash your inputs in small batches, making the batch size a multiple of
/// `degree` will generally give good performance.
///
/// For example, an x86 processor that supports AVX2 can compute four BLAKE2b
/// hashes in parallel, so `degree` returns 4 on that machine. If you call
/// [`hash_many`] with only three inputs, that's not enough to use the AVX2
/// implementation, and your average throughput will be lower. Likewise if you
/// call it with five inputs of equal length, the first four will be hashed in
/// parallel with AVX2, but the last one will have to be hashed by itself, and
/// again your average throughput will be lower.
///
/// As noted in the module level docs, performance is more complicated if your
/// inputs are of different lengths. When parallelizing long and short inputs
/// together, the longer ones will have bytes left over, and the implementation
/// will try to parallelize those leftover bytes with subsequent inputs. The
/// more inputs available in that case, the more the implementation will be
/// able to parallelize.
///
/// If you need a constant batch size, for example to collect inputs in an
/// array, see [`MAX_DEGREE`].
///
/// [`hash_many`]: fn.hash_many.html
/// [`MAX_DEGREE`]: constant.MAX_DEGREE.html
pub fn degree() -> usize {
guts::Implementation::detect().degree()
}
type JobsVec<'a, 'b> = ArrayVec<[Job<'a, 'b>; guts::MAX_DEGREE]>;
#[inline(always)]
fn fill_jobs_vec<'a, 'b>(
jobs_iter: &mut impl Iterator<Item = Job<'a, 'b>>,
vec: &mut JobsVec<'a, 'b>,
target_len: usize,
) {
while vec.len() < target_len {
if let Some(job) = jobs_iter.next() {
vec.push(job);
} else {
break;
}
}
}
#[inline(always)]
fn evict_finished<'a, 'b>(vec: &mut JobsVec<'a, 'b>, num_jobs: usize) {
// Iterate backwards so that removal doesn't cause an out-of-bounds panic.
for i in (0..num_jobs).rev() {
// Note that is_empty() is only valid because we know all these jobs
// have been run at least once. Otherwise we could confuse the empty
// input for a finished job, which would be incorrect.
//
// Avoid a panic branch here in release mode.
debug_assert!(vec.len() > i);
if vec.len() > i && vec[i].input.is_empty() {
// Note that calling pop_at() repeatedly has some overhead, because
// later elements need to be shifted up. However, the JobsVec is
// small, and this approach guarantees that jobs are encountered in
// order.
vec.pop_at(i);
}
}
}
pub(crate) fn compress_many<'a, 'b, I>(
jobs: I,
imp: Implementation,
finalize: Finalize,
stride: Stride,
) where
I: IntoIterator<Item = Job<'a, 'b>>,
{
// Fuse is important for correctness, since each of these blocks tries to
// advance the iterator, even if a previous block emptied it.
let mut jobs_iter = jobs.into_iter().fuse();
let mut jobs_vec = JobsVec::new();
if imp.degree() >= 4 {
loop {
fill_jobs_vec(&mut jobs_iter, &mut jobs_vec, 4);
if jobs_vec.len() < 4 {
break;
}
let jobs_array = array_mut_ref!(jobs_vec, 0, 4);
imp.compress4_loop(jobs_array, finalize, stride);
evict_finished(&mut jobs_vec, 4);
}
}
if imp.degree() >= 2 {
loop {
fill_jobs_vec(&mut jobs_iter, &mut jobs_vec, 2);
if jobs_vec.len() < 2 {
break;
}
let jobs_array = array_mut_ref!(jobs_vec, 0, 2);
imp.compress2_loop(jobs_array, finalize, stride);
evict_finished(&mut jobs_vec, 2);
}
}
for job in jobs_vec.into_iter().chain(jobs_iter) {
let Job {
input,
words,
count,
last_node,
} = job;
imp.compress1_loop(input, words, count, last_node, finalize, stride);
}
}
/// Update any number of `State` objects at once.
///
/// # Example
///
/// ```
/// use blake2b_simd::{blake2b, State, many::update_many};
///
/// let mut states = [
/// State::new(),
/// State::new(),
/// State::new(),
/// State::new(),
/// ];
///
/// let inputs = [
/// &b"foo"[..],
/// &b"bar"[..],
/// &b"baz"[..],
/// &b"bing"[..],
/// ];
///
/// update_many(states.iter_mut().zip(inputs.iter()));
///
/// for (state, input) in states.iter_mut().zip(inputs.iter()) {
/// assert_eq!(blake2b(input), state.finalize());
/// }
/// ```
pub fn update_many<'a, 'b, I, T>(pairs: I)
where
I: IntoIterator<Item = (&'a mut State, &'b T)>,
T: 'b + AsRef<[u8]> + ?Sized,
{
// Get the guts::Implementation from the first state, if any.
let mut peekable_pairs = pairs.into_iter().peekable();
let implementation = if let Some((state, _)) = peekable_pairs.peek() {
state.implementation
} else {
// No work items, just short circuit.
return;
};
// Adapt the pairs iterator into a Jobs iterator, but skip over the Jobs
// where there's not actually any work to do (e.g. because there's not much
// input and it's all just going in the State buffer).
let jobs = peekable_pairs.flat_map(|(state, input_t)| {
let mut input = input_t.as_ref();
// For each pair, if the State has some input in its buffer, try to
// finish that buffer. If there wasn't enough input to do that --
// or if the input was empty to begin with -- skip this pair.
state.compress_buffer_if_possible(&mut input);
if input.is_empty() {
return None;
}
// Now we know the buffer is empty and there's more input. Make sure we
// buffer the final block, because update() doesn't finalize.
let mut last_block_start = input.len() - 1;
last_block_start -= last_block_start % BLOCKBYTES;
let (blocks, last_block) = input.split_at(last_block_start);
state.buf[..last_block.len()].copy_from_slice(last_block);
state.buflen = last_block.len() as u8;
// Finally, if the full blocks slice is non-empty, prepare that job for
// compression, and bump the State count.
if blocks.is_empty() {
None
} else {
let count = state.count;
state.count = state.count.wrapping_add(blocks.len() as Count);
Some(Job {
input: blocks,
words: &mut state.words,
count,
last_node: state.last_node,
})
}
});
// Run all the Jobs in the iterator.
compress_many(jobs, implementation, Finalize::No, Stride::Serial);
}
/// A job for the [`hash_many`] function. After calling [`hash_many`] on a
/// collection of `HashManyJob` objects, you can call [`to_hash`] on each job
/// to get the result.
///
/// [`hash_many`]: fn.hash_many.html
/// [`to_hash`]: struct.HashManyJob.html#method.to_hash
#[derive(Clone)]
pub struct HashManyJob<'a> {
words: [Word; 8],
count: Count,
last_node: LastNode,
hash_length: u8,
input: &'a [u8],
finished: bool,
implementation: guts::Implementation,
}
impl<'a> HashManyJob<'a> {
/// Construct a new `HashManyJob` from a set of hashing parameters and an
/// input.
#[inline]
pub fn new(params: &Params, input: &'a [u8]) -> Self {
let mut words = params.to_words();
let mut count = 0;
let mut finished = false;
// If we have key bytes, compress them into the state words. If there's
// no additional input, this compression needs to finalize and set
// finished=true.
if params.key_length > 0 {
let mut finalization = Finalize::No;
if input.is_empty() {
finalization = Finalize::Yes;
finished = true;
}
params.implementation.compress1_loop(
&params.key_block,
&mut words,
0,
params.last_node,
finalization,
Stride::Serial,
);
count = BLOCKBYTES as Count;
}
Self {
words,
count,
last_node: params.last_node,
hash_length: params.hash_length,
input,
finished,
implementation: params.implementation,
}
}
/// Get the hash from a finished job. If you call this before calling
/// [`hash_many`], it will panic in debug mode.
///
/// [`hash_many`]: fn.hash_many.html
#[inline]
pub fn to_hash(&self) -> Hash {
debug_assert!(self.finished, "job hasn't been run yet");
Hash {
bytes: state_words_to_bytes(&self.words),
len: self.hash_length,
}
}
}
impl<'a> fmt::Debug for HashManyJob<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// NB: Don't print the words. Leaking them would allow length extension.
write!(
f,
"HashManyJob {{ count: {}, hash_length: {}, last_node: {}, input_len: {} }}",
self.count,
self.hash_length,
self.last_node.yes(),
self.input.len(),
)
}
}
/// Hash any number of complete inputs all at once.
///
/// This is slightly more efficient than using `update_many` with `State`
/// objects, because it doesn't need to do any buffering.
///
/// Running `hash_many` on the same `HashManyJob` object more than once has no
/// effect.
///
/// # Example
///
/// ```
/// use blake2b_simd::{blake2b, Params, many::{HashManyJob, hash_many}};
///
/// let inputs = [
/// &b"foo"[..],
/// &b"bar"[..],
/// &b"baz"[..],
/// &b"bing"[..],
/// ];
///
/// let mut params = Params::new();
/// params.hash_length(16);
///
/// let mut jobs = [
/// HashManyJob::new(&params, inputs[0]),
/// HashManyJob::new(&params, inputs[1]),
/// HashManyJob::new(&params, inputs[2]),
/// HashManyJob::new(&params, inputs[3]),
/// ];
///
/// hash_many(jobs.iter_mut());
///
/// for (input, job) in inputs.iter().zip(jobs.iter()) {
/// let expected = params.hash(input);
/// assert_eq!(expected, job.to_hash());
/// }
/// ```
pub fn hash_many<'a, 'b, I>(hash_many_jobs: I)
where
'b: 'a,
I: IntoIterator<Item = &'a mut HashManyJob<'b>>,
{
// Get the guts::Implementation from the first job, if any.
let mut peekable_jobs = hash_many_jobs.into_iter().peekable();
let implementation = if let Some(job) = peekable_jobs.peek() {
job.implementation
} else {
// No work items, just short circuit.
return;
};
// In the jobs iterator, skip HashManyJobs that have already been run. This
// is less because we actually expect callers to call hash_many twice
// (though they're allowed to if they want), and more because
// HashManyJob::new might need to finalize if there are key bytes but no
// input. Tying the job lifetime to the Params reference is an alternative,
// but I've found it too constraining in practice. We could also put key
// bytes in every HashManyJob, but that would add unnecessary storage and
// zeroing for all callers.
let unfinished_jobs = peekable_jobs.into_iter().filter(|j| !j.finished);
let jobs = unfinished_jobs.map(|j| {
j.finished = true;
Job {
input: j.input,
words: &mut j.words,
count: j.count,
last_node: j.last_node,
}
});
compress_many(jobs, implementation, Finalize::Yes, Stride::Serial);
}
#[cfg(test)]
mod test {
use super::*;
use crate::guts;
use crate::paint_test_input;
use crate::BLOCKBYTES;
use arrayvec::ArrayVec;
#[test]
fn test_degree() {
assert!(degree() <= MAX_DEGREE);
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[cfg(feature = "std")]
{
if is_x86_feature_detected!("avx2") {
assert!(degree() >= 4);
}
if is_x86_feature_detected!("sse4.1") {
assert!(degree() >= 2);
}
}
}
#[test]
fn test_hash_many() {
// Use a length of inputs that will exercise all of the power-of-two loops.
const LEN: usize = 2 * guts::MAX_DEGREE - 1;
// Rerun LEN inputs LEN different times, with the empty input starting in a
// different spot each time.
let mut input = [0; LEN * BLOCKBYTES];
paint_test_input(&mut input);
for start_offset in 0..LEN {
let mut inputs: [&[u8]; LEN] = [&[]; LEN];
for i in 0..LEN {
let chunks = (i + start_offset) % LEN;
inputs[i] = &input[..chunks * BLOCKBYTES];
}
let mut params: ArrayVec<[Params; LEN]> = ArrayVec::new();
for i in 0..LEN {
let mut p = Params::new();
p.node_offset(i as u64);
if i % 2 == 1 {
p.last_node(true);
p.key(b"foo");
}
params.push(p);
}
let mut jobs: ArrayVec<[HashManyJob; LEN]> = ArrayVec::new();
for i in 0..LEN {
jobs.push(HashManyJob::new(&params[i], inputs[i]));
}
hash_many(&mut jobs);
// Check the outputs.
for i in 0..LEN {
let expected = params[i].hash(inputs[i]);
assert_eq!(expected, jobs[i].to_hash());
}
}
}
#[test]
fn test_update_many() {
// Use a length of inputs that will exercise all of the power-of-two loops.
const LEN: usize = 2 * guts::MAX_DEGREE - 1;
// Rerun LEN inputs LEN different times, with the empty input starting in a
// different spot each time.
let mut input = [0; LEN * BLOCKBYTES];
paint_test_input(&mut input);
for start_offset in 0..LEN {
let mut inputs: [&[u8]; LEN] = [&[]; LEN];
for i in 0..LEN {
let chunks = (i + start_offset) % LEN;
inputs[i] = &input[..chunks * BLOCKBYTES];
}
let mut params: ArrayVec<[Params; LEN]> = ArrayVec::new();
for i in 0..LEN {
let mut p = Params::new();
p.node_offset(i as u64);
if i % 2 == 1 {
p.last_node(true);
p.key(b"foo");
}
params.push(p);
}
let mut states: ArrayVec<[State; LEN]> = ArrayVec::new();
for i in 0..LEN {
states.push(params[i].to_state());
}
// Run each input twice through, to exercise buffering.
update_many(states.iter_mut().zip(inputs.iter()));
update_many(states.iter_mut().zip(inputs.iter()));
// Check the outputs.
for i in 0..LEN {
let mut reference_state = params[i].to_state();
// Again, run the input twice.
reference_state.update(inputs[i]);
reference_state.update(inputs[i]);
assert_eq!(reference_state.finalize(), states[i].finalize());
assert_eq!(2 * inputs[i].len() as Count, states[i].count());
}
}
}
}

View File

@ -1,168 +0,0 @@
use arrayref::{array_ref, array_refs};
use super::*;
use crate::guts::{
count_high, count_low, final_block, flag_word, input_debug_asserts, Finalize, LastNode, Stride,
};
// G is the mixing function, called eight times per round in the compression
// function. V is the 16-word state vector of the compression function, usually
// described as a 4x4 matrix. A, B, C, and D are the mixing indices, set by the
// caller first to the four columns of V, and then to its four diagonals. X and
// Y are words of input, chosen by the caller according to the message
// schedule, SIGMA.
#[inline(always)]
fn g(v: &mut [Word; 16], a: usize, b: usize, c: usize, d: usize, x: Word, y: Word) {
v[a] = v[a].wrapping_add(v[b]).wrapping_add(x);
v[d] = (v[d] ^ v[a]).rotate_right(32);
v[c] = v[c].wrapping_add(v[d]);
v[b] = (v[b] ^ v[c]).rotate_right(24);
v[a] = v[a].wrapping_add(v[b]).wrapping_add(y);
v[d] = (v[d] ^ v[a]).rotate_right(16);
v[c] = v[c].wrapping_add(v[d]);
v[b] = (v[b] ^ v[c]).rotate_right(63);
}
// This is too much inlining for some small chips like ARM Cortex-M0, so the
// uninline_portable feature is provided to disable it.
#[cfg_attr(not(feature = "uninline_portable"), inline(always))]
fn round(r: usize, m: &[Word; 16], v: &mut [Word; 16]) {
// Select the message schedule based on the round.
let s = SIGMA[r];
// Mix the columns.
g(v, 0, 4, 8, 12, m[s[0] as usize], m[s[1] as usize]);
g(v, 1, 5, 9, 13, m[s[2] as usize], m[s[3] as usize]);
g(v, 2, 6, 10, 14, m[s[4] as usize], m[s[5] as usize]);
g(v, 3, 7, 11, 15, m[s[6] as usize], m[s[7] as usize]);
// Mix the rows.
g(v, 0, 5, 10, 15, m[s[8] as usize], m[s[9] as usize]);
g(v, 1, 6, 11, 12, m[s[10] as usize], m[s[11] as usize]);
g(v, 2, 7, 8, 13, m[s[12] as usize], m[s[13] as usize]);
g(v, 3, 4, 9, 14, m[s[14] as usize], m[s[15] as usize]);
}
#[inline(always)]
fn compress_block(
block: &[u8; BLOCKBYTES],
words: &mut [Word; 8],
count: Count,
last_block: Word,
last_node: Word,
) {
// Initialize the compression state.
let mut v = [
words[0],
words[1],
words[2],
words[3],
words[4],
words[5],
words[6],
words[7],
IV[0],
IV[1],
IV[2],
IV[3],
IV[4] ^ count_low(count),
IV[5] ^ count_high(count),
IV[6] ^ last_block,
IV[7] ^ last_node,
];
// Parse the message bytes as ints in little endian order.
const W: usize = size_of::<Word>();
let msg_refs = array_refs!(block, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W);
let m = [
Word::from_le_bytes(*msg_refs.0),
Word::from_le_bytes(*msg_refs.1),
Word::from_le_bytes(*msg_refs.2),
Word::from_le_bytes(*msg_refs.3),
Word::from_le_bytes(*msg_refs.4),
Word::from_le_bytes(*msg_refs.5),
Word::from_le_bytes(*msg_refs.6),
Word::from_le_bytes(*msg_refs.7),
Word::from_le_bytes(*msg_refs.8),
Word::from_le_bytes(*msg_refs.9),
Word::from_le_bytes(*msg_refs.10),
Word::from_le_bytes(*msg_refs.11),
Word::from_le_bytes(*msg_refs.12),
Word::from_le_bytes(*msg_refs.13),
Word::from_le_bytes(*msg_refs.14),
Word::from_le_bytes(*msg_refs.15),
];
round(0, &m, &mut v);
round(1, &m, &mut v);
round(2, &m, &mut v);
round(3, &m, &mut v);
round(4, &m, &mut v);
round(5, &m, &mut v);
round(6, &m, &mut v);
round(7, &m, &mut v);
round(8, &m, &mut v);
round(9, &m, &mut v);
round(10, &m, &mut v);
round(11, &m, &mut v);
words[0] ^= v[0] ^ v[8];
words[1] ^= v[1] ^ v[9];
words[2] ^= v[2] ^ v[10];
words[3] ^= v[3] ^ v[11];
words[4] ^= v[4] ^ v[12];
words[5] ^= v[5] ^ v[13];
words[6] ^= v[6] ^ v[14];
words[7] ^= v[7] ^ v[15];
}
pub fn compress1_loop(
input: &[u8],
words: &mut [Word; 8],
mut count: Count,
last_node: LastNode,
finalize: Finalize,
stride: Stride,
) {
input_debug_asserts(input, finalize);
let mut local_words = *words;
let mut fin_offset = input.len().saturating_sub(1);
fin_offset -= fin_offset % stride.padded_blockbytes();
let mut buf = [0; BLOCKBYTES];
let (fin_block, fin_len, _) = final_block(input, fin_offset, &mut buf, stride);
let fin_last_block = flag_word(finalize.yes());
let fin_last_node = flag_word(finalize.yes() && last_node.yes());
let mut offset = 0;
loop {
let block;
let count_delta;
let last_block;
let last_node;
if offset == fin_offset {
block = fin_block;
count_delta = fin_len;
last_block = fin_last_block;
last_node = fin_last_node;
} else {
block = array_ref!(input, offset, BLOCKBYTES);
count_delta = BLOCKBYTES;
last_block = flag_word(false);
last_node = flag_word(false);
};
count = count.wrapping_add(count_delta as Count);
compress_block(block, &mut local_words, count, last_block, last_node);
// Check for termination before bumping the offset, to avoid overflow.
if offset == fin_offset {
break;
}
offset += stride.padded_blockbytes();
}
*words = local_words;
}

View File

@ -1,461 +0,0 @@
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
use crate::guts::{
assemble_count, count_high, count_low, final_block, flag_word, input_debug_asserts, Finalize,
Job, Stride,
};
use crate::{Word, BLOCKBYTES, IV, SIGMA};
use arrayref::{array_refs, mut_array_refs};
use core::cmp;
use core::mem;
pub const DEGREE: usize = 2;
#[inline(always)]
unsafe fn loadu(src: *const [Word; DEGREE]) -> __m128i {
// This is an unaligned load, so the pointer cast is allowed.
_mm_loadu_si128(src as *const __m128i)
}
#[inline(always)]
unsafe fn storeu(src: __m128i, dest: *mut [Word; DEGREE]) {
// This is an unaligned store, so the pointer cast is allowed.
_mm_storeu_si128(dest as *mut __m128i, src)
}
#[inline(always)]
unsafe fn add(a: __m128i, b: __m128i) -> __m128i {
_mm_add_epi64(a, b)
}
#[inline(always)]
unsafe fn eq(a: __m128i, b: __m128i) -> __m128i {
_mm_cmpeq_epi64(a, b)
}
#[inline(always)]
unsafe fn and(a: __m128i, b: __m128i) -> __m128i {
_mm_and_si128(a, b)
}
#[inline(always)]
unsafe fn negate_and(a: __m128i, b: __m128i) -> __m128i {
// Note that "and not" implies the reverse of the actual arg order.
_mm_andnot_si128(a, b)
}
#[inline(always)]
unsafe fn xor(a: __m128i, b: __m128i) -> __m128i {
_mm_xor_si128(a, b)
}
#[inline(always)]
unsafe fn set1(x: u64) -> __m128i {
_mm_set1_epi64x(x as i64)
}
#[inline(always)]
unsafe fn set2(a: u64, b: u64) -> __m128i {
// There's no _mm_setr_epi64x, so note the arg order is backwards.
_mm_set_epi64x(b as i64, a as i64)
}
// Adapted from https://github.com/rust-lang-nursery/stdsimd/pull/479.
macro_rules! _MM_SHUFFLE {
($z:expr, $y:expr, $x:expr, $w:expr) => {
($z << 6) | ($y << 4) | ($x << 2) | $w
};
}
// These rotations are the "simple version". For the "complicated version", see
// https://github.com/sneves/blake2-avx2/blob/b3723921f668df09ece52dcd225a36d4a4eea1d9/blake2b-common.h#L43-L46.
// For a discussion of the tradeoffs, see
// https://github.com/sneves/blake2-avx2/pull/5. In short:
// - Due to an LLVM bug (https://bugs.llvm.org/show_bug.cgi?id=44379), this
// version performs better on recent x86 chips.
// - LLVM is able to optimize this version to AVX-512 rotation instructions
// when those are enabled.
#[inline(always)]
unsafe fn rot32(x: __m128i) -> __m128i {
_mm_or_si128(_mm_srli_epi64(x, 32), _mm_slli_epi64(x, 64 - 32))
}
#[inline(always)]
unsafe fn rot24(x: __m128i) -> __m128i {
_mm_or_si128(_mm_srli_epi64(x, 24), _mm_slli_epi64(x, 64 - 24))
}
#[inline(always)]
unsafe fn rot16(x: __m128i) -> __m128i {
_mm_or_si128(_mm_srli_epi64(x, 16), _mm_slli_epi64(x, 64 - 16))
}
#[inline(always)]
unsafe fn rot63(x: __m128i) -> __m128i {
_mm_or_si128(_mm_srli_epi64(x, 63), _mm_slli_epi64(x, 64 - 63))
}
#[inline(always)]
unsafe fn round(v: &mut [__m128i; 16], m: &[__m128i; 16], r: usize) {
v[0] = add(v[0], m[SIGMA[r][0] as usize]);
v[1] = add(v[1], m[SIGMA[r][2] as usize]);
v[2] = add(v[2], m[SIGMA[r][4] as usize]);
v[3] = add(v[3], m[SIGMA[r][6] as usize]);
v[0] = add(v[0], v[4]);
v[1] = add(v[1], v[5]);
v[2] = add(v[2], v[6]);
v[3] = add(v[3], v[7]);
v[12] = xor(v[12], v[0]);
v[13] = xor(v[13], v[1]);
v[14] = xor(v[14], v[2]);
v[15] = xor(v[15], v[3]);
v[12] = rot32(v[12]);
v[13] = rot32(v[13]);
v[14] = rot32(v[14]);
v[15] = rot32(v[15]);
v[8] = add(v[8], v[12]);
v[9] = add(v[9], v[13]);
v[10] = add(v[10], v[14]);
v[11] = add(v[11], v[15]);
v[4] = xor(v[4], v[8]);
v[5] = xor(v[5], v[9]);
v[6] = xor(v[6], v[10]);
v[7] = xor(v[7], v[11]);
v[4] = rot24(v[4]);
v[5] = rot24(v[5]);
v[6] = rot24(v[6]);
v[7] = rot24(v[7]);
v[0] = add(v[0], m[SIGMA[r][1] as usize]);
v[1] = add(v[1], m[SIGMA[r][3] as usize]);
v[2] = add(v[2], m[SIGMA[r][5] as usize]);
v[3] = add(v[3], m[SIGMA[r][7] as usize]);
v[0] = add(v[0], v[4]);
v[1] = add(v[1], v[5]);
v[2] = add(v[2], v[6]);
v[3] = add(v[3], v[7]);
v[12] = xor(v[12], v[0]);
v[13] = xor(v[13], v[1]);
v[14] = xor(v[14], v[2]);
v[15] = xor(v[15], v[3]);
v[12] = rot16(v[12]);
v[13] = rot16(v[13]);
v[14] = rot16(v[14]);
v[15] = rot16(v[15]);
v[8] = add(v[8], v[12]);
v[9] = add(v[9], v[13]);
v[10] = add(v[10], v[14]);
v[11] = add(v[11], v[15]);
v[4] = xor(v[4], v[8]);
v[5] = xor(v[5], v[9]);
v[6] = xor(v[6], v[10]);
v[7] = xor(v[7], v[11]);
v[4] = rot63(v[4]);
v[5] = rot63(v[5]);
v[6] = rot63(v[6]);
v[7] = rot63(v[7]);
v[0] = add(v[0], m[SIGMA[r][8] as usize]);
v[1] = add(v[1], m[SIGMA[r][10] as usize]);
v[2] = add(v[2], m[SIGMA[r][12] as usize]);
v[3] = add(v[3], m[SIGMA[r][14] as usize]);
v[0] = add(v[0], v[5]);
v[1] = add(v[1], v[6]);
v[2] = add(v[2], v[7]);
v[3] = add(v[3], v[4]);
v[15] = xor(v[15], v[0]);
v[12] = xor(v[12], v[1]);
v[13] = xor(v[13], v[2]);
v[14] = xor(v[14], v[3]);
v[15] = rot32(v[15]);
v[12] = rot32(v[12]);
v[13] = rot32(v[13]);
v[14] = rot32(v[14]);
v[10] = add(v[10], v[15]);
v[11] = add(v[11], v[12]);
v[8] = add(v[8], v[13]);
v[9] = add(v[9], v[14]);
v[5] = xor(v[5], v[10]);
v[6] = xor(v[6], v[11]);
v[7] = xor(v[7], v[8]);
v[4] = xor(v[4], v[9]);
v[5] = rot24(v[5]);
v[6] = rot24(v[6]);
v[7] = rot24(v[7]);
v[4] = rot24(v[4]);
v[0] = add(v[0], m[SIGMA[r][9] as usize]);
v[1] = add(v[1], m[SIGMA[r][11] as usize]);
v[2] = add(v[2], m[SIGMA[r][13] as usize]);
v[3] = add(v[3], m[SIGMA[r][15] as usize]);
v[0] = add(v[0], v[5]);
v[1] = add(v[1], v[6]);
v[2] = add(v[2], v[7]);
v[3] = add(v[3], v[4]);
v[15] = xor(v[15], v[0]);
v[12] = xor(v[12], v[1]);
v[13] = xor(v[13], v[2]);
v[14] = xor(v[14], v[3]);
v[15] = rot16(v[15]);
v[12] = rot16(v[12]);
v[13] = rot16(v[13]);
v[14] = rot16(v[14]);
v[10] = add(v[10], v[15]);
v[11] = add(v[11], v[12]);
v[8] = add(v[8], v[13]);
v[9] = add(v[9], v[14]);
v[5] = xor(v[5], v[10]);
v[6] = xor(v[6], v[11]);
v[7] = xor(v[7], v[8]);
v[4] = xor(v[4], v[9]);
v[5] = rot63(v[5]);
v[6] = rot63(v[6]);
v[7] = rot63(v[7]);
v[4] = rot63(v[4]);
}
// We'd rather make this a regular function with #[inline(always)], but for
// some reason that blows up compile times by about 10 seconds, at least in
// some cases (BLAKE2b avx2.rs). This macro seems to get the same performance
// result, without the compile time issue.
macro_rules! compress2_transposed {
(
$h_vecs:expr,
$msg_vecs:expr,
$count_low:expr,
$count_high:expr,
$lastblock:expr,
$lastnode:expr,
) => {
let h_vecs: &mut [__m128i; 8] = $h_vecs;
let msg_vecs: &[__m128i; 16] = $msg_vecs;
let count_low: __m128i = $count_low;
let count_high: __m128i = $count_high;
let lastblock: __m128i = $lastblock;
let lastnode: __m128i = $lastnode;
let mut v = [
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(IV[0]),
set1(IV[1]),
set1(IV[2]),
set1(IV[3]),
xor(set1(IV[4]), count_low),
xor(set1(IV[5]), count_high),
xor(set1(IV[6]), lastblock),
xor(set1(IV[7]), lastnode),
];
round(&mut v, &msg_vecs, 0);
round(&mut v, &msg_vecs, 1);
round(&mut v, &msg_vecs, 2);
round(&mut v, &msg_vecs, 3);
round(&mut v, &msg_vecs, 4);
round(&mut v, &msg_vecs, 5);
round(&mut v, &msg_vecs, 6);
round(&mut v, &msg_vecs, 7);
round(&mut v, &msg_vecs, 8);
round(&mut v, &msg_vecs, 9);
round(&mut v, &msg_vecs, 10);
round(&mut v, &msg_vecs, 11);
h_vecs[0] = xor(xor(h_vecs[0], v[0]), v[8]);
h_vecs[1] = xor(xor(h_vecs[1], v[1]), v[9]);
h_vecs[2] = xor(xor(h_vecs[2], v[2]), v[10]);
h_vecs[3] = xor(xor(h_vecs[3], v[3]), v[11]);
h_vecs[4] = xor(xor(h_vecs[4], v[4]), v[12]);
h_vecs[5] = xor(xor(h_vecs[5], v[5]), v[13]);
h_vecs[6] = xor(xor(h_vecs[6], v[6]), v[14]);
h_vecs[7] = xor(xor(h_vecs[7], v[7]), v[15]);
};
}
#[inline(always)]
unsafe fn transpose_vecs(a: __m128i, b: __m128i) -> [__m128i; DEGREE] {
let a_words: [Word; DEGREE] = mem::transmute(a);
let b_words: [Word; DEGREE] = mem::transmute(b);
[set2(a_words[0], b_words[0]), set2(a_words[1], b_words[1])]
}
#[inline(always)]
unsafe fn transpose_state_vecs(jobs: &[Job; DEGREE]) -> [__m128i; 8] {
// Load all the state words into transposed vectors, where the first vector
// has the first word of each state, etc. Transposing once at the beginning
// and once at the end is more efficient that repeating it for each block.
let words0 = array_refs!(&jobs[0].words, DEGREE, DEGREE, DEGREE, DEGREE);
let words1 = array_refs!(&jobs[1].words, DEGREE, DEGREE, DEGREE, DEGREE);
let [h0, h1] = transpose_vecs(loadu(words0.0), loadu(words1.0));
let [h2, h3] = transpose_vecs(loadu(words0.1), loadu(words1.1));
let [h4, h5] = transpose_vecs(loadu(words0.2), loadu(words1.2));
let [h6, h7] = transpose_vecs(loadu(words0.3), loadu(words1.3));
[h0, h1, h2, h3, h4, h5, h6, h7]
}
#[inline(always)]
unsafe fn untranspose_state_vecs(h_vecs: &[__m128i; 8], jobs: &mut [Job; DEGREE]) {
// Un-transpose the updated state vectors back into the caller's arrays.
let [job0, job1] = jobs;
let words0 = mut_array_refs!(&mut job0.words, DEGREE, DEGREE, DEGREE, DEGREE);
let words1 = mut_array_refs!(&mut job1.words, DEGREE, DEGREE, DEGREE, DEGREE);
let out = transpose_vecs(h_vecs[0], h_vecs[1]);
storeu(out[0], words0.0);
storeu(out[1], words1.0);
let out = transpose_vecs(h_vecs[2], h_vecs[3]);
storeu(out[0], words0.1);
storeu(out[1], words1.1);
let out = transpose_vecs(h_vecs[4], h_vecs[5]);
storeu(out[0], words0.2);
storeu(out[1], words1.2);
let out = transpose_vecs(h_vecs[6], h_vecs[7]);
storeu(out[0], words0.3);
storeu(out[1], words1.3);
}
#[inline(always)]
unsafe fn transpose_msg_vecs(blocks: [*const [u8; BLOCKBYTES]; DEGREE]) -> [__m128i; 16] {
// These input arrays have no particular alignment, so we use unaligned
// loads to read from them.
let block0 = blocks[0] as *const [Word; DEGREE];
let block1 = blocks[1] as *const [Word; DEGREE];
let [m0, m1] = transpose_vecs(loadu(block0.add(0)), loadu(block1.add(0)));
let [m2, m3] = transpose_vecs(loadu(block0.add(1)), loadu(block1.add(1)));
let [m4, m5] = transpose_vecs(loadu(block0.add(2)), loadu(block1.add(2)));
let [m6, m7] = transpose_vecs(loadu(block0.add(3)), loadu(block1.add(3)));
let [m8, m9] = transpose_vecs(loadu(block0.add(4)), loadu(block1.add(4)));
let [m10, m11] = transpose_vecs(loadu(block0.add(5)), loadu(block1.add(5)));
let [m12, m13] = transpose_vecs(loadu(block0.add(6)), loadu(block1.add(6)));
let [m14, m15] = transpose_vecs(loadu(block0.add(7)), loadu(block1.add(7)));
[
m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15,
]
}
#[inline(always)]
unsafe fn load_counts(jobs: &[Job; DEGREE]) -> (__m128i, __m128i) {
(
set2(count_low(jobs[0].count), count_low(jobs[1].count)),
set2(count_high(jobs[0].count), count_high(jobs[1].count)),
)
}
#[inline(always)]
unsafe fn store_counts(jobs: &mut [Job; DEGREE], low: __m128i, high: __m128i) {
let low_ints: [Word; DEGREE] = mem::transmute(low);
let high_ints: [Word; DEGREE] = mem::transmute(high);
for i in 0..DEGREE {
jobs[i].count = assemble_count(low_ints[i], high_ints[i]);
}
}
#[inline(always)]
unsafe fn add_to_counts(lo: &mut __m128i, hi: &mut __m128i, delta: __m128i) {
// If the low counts reach zero, that means they wrapped, unless the delta
// was also zero.
*lo = add(*lo, delta);
let lo_reached_zero = eq(*lo, set1(0));
let delta_was_zero = eq(delta, set1(0));
let hi_inc = and(set1(1), negate_and(delta_was_zero, lo_reached_zero));
*hi = add(*hi, hi_inc);
}
#[inline(always)]
unsafe fn flags_vec(flags: [bool; DEGREE]) -> __m128i {
set2(flag_word(flags[0]), flag_word(flags[1]))
}
#[target_feature(enable = "sse4.1")]
pub unsafe fn compress2_loop(jobs: &mut [Job; DEGREE], finalize: Finalize, stride: Stride) {
// If we're not finalizing, there can't be a partial block at the end.
for job in jobs.iter() {
input_debug_asserts(job.input, finalize);
}
let msg_ptrs = [jobs[0].input.as_ptr(), jobs[1].input.as_ptr()];
let mut h_vecs = transpose_state_vecs(&jobs);
let (mut counts_lo, mut counts_hi) = load_counts(&jobs);
// Prepare the final blocks (note, which could be empty if the input is
// empty). Do all this before entering the main loop.
let min_len = jobs.iter().map(|job| job.input.len()).min().unwrap();
let mut fin_offset = min_len.saturating_sub(1);
fin_offset -= fin_offset % stride.padded_blockbytes();
// Performance note, making these buffers mem::uninitialized() seems to
// cause problems in the optimizer.
let mut buf0: [u8; BLOCKBYTES] = [0; BLOCKBYTES];
let mut buf1: [u8; BLOCKBYTES] = [0; BLOCKBYTES];
let (block0, len0, finalize0) = final_block(jobs[0].input, fin_offset, &mut buf0, stride);
let (block1, len1, finalize1) = final_block(jobs[1].input, fin_offset, &mut buf1, stride);
let fin_blocks: [*const [u8; BLOCKBYTES]; DEGREE] = [block0, block1];
let fin_counts_delta = set2(len0 as Word, len1 as Word);
let fin_last_block;
let fin_last_node;
if finalize.yes() {
fin_last_block = flags_vec([finalize0, finalize1]);
fin_last_node = flags_vec([
finalize0 && jobs[0].last_node.yes(),
finalize1 && jobs[1].last_node.yes(),
]);
} else {
fin_last_block = set1(0);
fin_last_node = set1(0);
}
// The main loop.
let mut offset = 0;
loop {
let blocks;
let counts_delta;
let last_block;
let last_node;
if offset == fin_offset {
blocks = fin_blocks;
counts_delta = fin_counts_delta;
last_block = fin_last_block;
last_node = fin_last_node;
} else {
blocks = [
msg_ptrs[0].add(offset) as *const [u8; BLOCKBYTES],
msg_ptrs[1].add(offset) as *const [u8; BLOCKBYTES],
];
counts_delta = set1(BLOCKBYTES as Word);
last_block = set1(0);
last_node = set1(0);
};
let m_vecs = transpose_msg_vecs(blocks);
add_to_counts(&mut counts_lo, &mut counts_hi, counts_delta);
compress2_transposed!(
&mut h_vecs,
&m_vecs,
counts_lo,
counts_hi,
last_block,
last_node,
);
// Check for termination before bumping the offset, to avoid overflow.
if offset == fin_offset {
break;
}
offset += stride.padded_blockbytes();
}
// Write out the results.
untranspose_state_vecs(&h_vecs, &mut *jobs);
store_counts(&mut *jobs, counts_lo, counts_hi);
let max_consumed = offset.saturating_add(stride.padded_blockbytes());
for job in jobs.iter_mut() {
let consumed = cmp::min(max_consumed, job.input.len());
job.input = &job.input[consumed..];
}
}

View File

@ -1,201 +0,0 @@
use super::*;
const EMPTY_HASH: &str = "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419\
d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce";
const ABC_HASH: &str = "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1\
7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923";
const ONE_BLOCK_HASH: &str = "865939e120e6805438478841afb739ae4250cf372653078a065cdcfffca4caf7\
98e6d462b65d658fc165782640eded70963449ae1500fb0f24981d7727e22c41";
const THOUSAND_HASH: &str = "1ee4e51ecab5210a518f26150e882627ec839967f19d763e1508b12cfefed148\
58f6a1c9d1f969bc224dc9440f5a6955277e755b9c513f9ba4421c5e50c8d787";
#[test]
fn test_update_state() {
let io = &[
(&b""[..], EMPTY_HASH),
(&b"abc"[..], ABC_HASH),
(&[0; BLOCKBYTES], ONE_BLOCK_HASH),
(&[0; 1000], THOUSAND_HASH),
];
// Test each input all at once.
for &(input, output) in io {
let hash = blake2b(input);
assert_eq!(&hash.to_hex(), output, "hash mismatch");
}
// Now in two chunks. This is especially important for the ONE_BLOCK case, because it would be
// a mistake for update() to call compress, even though the buffer is full.
for &(input, output) in io {
let mut state = State::new();
let split = input.len() / 2;
state.update(&input[..split]);
assert_eq!(split as Count, state.count());
state.update(&input[split..]);
assert_eq!(input.len() as Count, state.count());
let hash = state.finalize();
assert_eq!(&hash.to_hex(), output, "hash mismatch");
}
// Now one byte at a time.
for &(input, output) in io {
let mut state = State::new();
let mut count = 0;
for &b in input {
state.update(&[b]);
count += 1;
assert_eq!(count, state.count());
}
let hash = state.finalize();
assert_eq!(&hash.to_hex(), output, "hash mismatch");
}
}
#[test]
fn test_multiple_finalizes() {
let mut state = State::new();
assert_eq!(&state.finalize().to_hex(), EMPTY_HASH, "hash mismatch");
assert_eq!(&state.finalize().to_hex(), EMPTY_HASH, "hash mismatch");
assert_eq!(&state.finalize().to_hex(), EMPTY_HASH, "hash mismatch");
state.update(b"abc");
assert_eq!(&state.finalize().to_hex(), ABC_HASH, "hash mismatch");
assert_eq!(&state.finalize().to_hex(), ABC_HASH, "hash mismatch");
assert_eq!(&state.finalize().to_hex(), ABC_HASH, "hash mismatch");
}
#[cfg(feature = "std")]
#[test]
fn test_write() {
use std::io::prelude::*;
let mut state = State::new();
state.write_all(&[0; 1000]).unwrap();
let hash = state.finalize();
assert_eq!(&hash.to_hex(), THOUSAND_HASH, "hash mismatch");
}
// You can check this case against the equivalent Python:
//
// import hashlib
// hashlib.blake2b(
// b'foo',
// digest_size=18,
// key=b"bar",
// salt=b"bazbazbazbazbazb",
// person=b"bing bing bing b",
// fanout=2,
// depth=3,
// leaf_size=0x04050607,
// node_offset=0x08090a0b0c0d0e0f,
// node_depth=16,
// inner_size=17,
// last_node=True,
// ).hexdigest()
#[test]
fn test_all_parameters() {
let mut params = Params::new();
params
.hash_length(18)
// Make sure a shorter key properly overwrites a longer one.
.key(b"not the real key")
.key(b"bar")
.salt(b"bazbazbazbazbazb")
.personal(b"bing bing bing b")
.fanout(2)
.max_depth(3)
.max_leaf_length(0x04050607)
.node_offset(0x08090a0b0c0d0e0f)
.node_depth(16)
.inner_hash_length(17)
.last_node(true);
// Check the State API.
assert_eq!(
"ec0f59cb65f92e7fcca1280ba859a6925ded",
&params.to_state().update(b"foo").finalize().to_hex()
);
// Check the all-at-once API.
assert_eq!(
"ec0f59cb65f92e7fcca1280ba859a6925ded",
&params.hash(b"foo").to_hex()
);
}
#[test]
fn test_all_parameters_blake2bp() {
let mut params = blake2bp::Params::new();
params
.hash_length(18)
// Make sure a shorter key properly overwrites a longer one.
.key(b"not the real key")
.key(b"bar");
// Check the State API.
assert_eq!(
"8c54e888a8a01c63da6585c058fe54ea81df",
&params.to_state().update(b"foo").finalize().to_hex()
);
// Check the all-at-once API.
assert_eq!(
"8c54e888a8a01c63da6585c058fe54ea81df",
&params.hash(b"foo").to_hex()
);
}
#[test]
#[should_panic]
fn test_short_hash_length_panics() {
Params::new().hash_length(0);
}
#[test]
#[should_panic]
fn test_long_hash_length_panics() {
Params::new().hash_length(OUTBYTES + 1);
}
#[test]
#[should_panic]
fn test_long_key_panics() {
Params::new().key(&[0; KEYBYTES + 1]);
}
#[test]
#[should_panic]
fn test_long_salt_panics() {
Params::new().salt(&[0; SALTBYTES + 1]);
}
#[test]
#[should_panic]
fn test_long_personal_panics() {
Params::new().personal(&[0; PERSONALBYTES + 1]);
}
#[test]
fn test_zero_max_depth_supported() {
Params::new().max_depth(0);
}
#[test]
#[should_panic]
fn test_long_inner_hash_length_panics() {
Params::new().inner_hash_length(OUTBYTES + 1);
}
#[test]
#[should_panic]
fn test_blake2bp_short_hash_length_panics() {
blake2bp::Params::new().hash_length(0);
}
#[test]
#[should_panic]
fn test_blake2bp_long_hash_length_panics() {
blake2bp::Params::new().hash_length(OUTBYTES + 1);
}
#[test]
#[should_panic]
fn test_blake2bp_long_key_panics() {
blake2bp::Params::new().key(&[0; KEYBYTES + 1]);
}

View File

@ -1 +0,0 @@
{"files":{"Cargo.toml":"5039cc861ab84d85162dc1b3acf268a98d63d299a99b897ab21d390a9e9fb0f3","LICENSE.txt":"a2010f343487d3f7618affe54f789f5487602331c0a8d03f49e9a7c547cf0499","README":"4f0deec2ec32eeabaa065fef2ddd7816a32550b8395da5c47fc458bd45143bea","benches/bench.rs":"ffc599703d114cc4943db322f433b0819787db0c1ff41d7be3efc5c0940e0001","src/lib.rs":"ab735430ea1eaea0673a9a15d9437d591a718bc061c70f3499e42b65e4eb8ba4"},"package":"245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"}

View File

@ -1,25 +0,0 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies
#
# If you believe there's an error in this file please file an
# issue against the rust-lang/cargo repository. If you're
# editing this file be aware that the upstream Cargo.toml
# will likely look very different (and much more reasonable)
[package]
name = "constant_time_eq"
version = "0.1.5"
authors = ["Cesar Eduardo Barros <cesarb@cesarb.eti.br>"]
description = "Compares two equal-sized byte strings in constant time."
documentation = "https://docs.rs/constant_time_eq"
readme = "README"
keywords = ["constant_time"]
categories = ["cryptography", "no-std"]
license = "CC0-1.0"
repository = "https://github.com/cesarb/constant_time_eq"
[badges.travis-ci]
repository = "cesarb/constant_time_eq"

View File

@ -1,121 +0,0 @@
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.

View File

@ -1,3 +0,0 @@
Compares two equal-sized byte strings in constant time.
Inspired by the Linux kernel's crypto_memneq.

View File

@ -1,29 +0,0 @@
#![feature(test)]
extern crate constant_time_eq;
extern crate test;
use constant_time_eq::constant_time_eq;
use test::{Bencher, black_box};
fn bench(b: &mut Bencher, left: &[u8], right: &[u8]) {
b.bytes = (left.len() + right.len()) as u64;
b.iter(|| {
constant_time_eq(black_box(left), black_box(right))
})
}
#[bench]
fn bench_16(b: &mut Bencher) {
bench(b, &[0; 16], &[0; 16])
}
#[bench]
fn bench_4096(b: &mut Bencher) {
bench(b, &[0; 4096], &[0; 4096])
}
#[bench]
fn bench_65536(b: &mut Bencher) {
bench(b, &[0; 65536], &[0; 65536])
}

View File

@ -1,105 +0,0 @@
#![no_std]
// This function is non-inline to prevent the optimizer from looking inside it.
#[inline(never)]
fn constant_time_ne(a: &[u8], b: &[u8]) -> u8 {
assert!(a.len() == b.len());
// These useless slices make the optimizer elide the bounds checks.
// See the comment in clone_from_slice() added on Rust commit 6a7bc47.
let len = a.len();
let a = &a[..len];
let b = &b[..len];
let mut tmp = 0;
for i in 0..len {
tmp |= a[i] ^ b[i];
}
tmp // The compare with 0 must happen outside this function.
}
/// Compares two equal-sized byte strings in constant time.
///
/// # Examples
///
/// ```
/// use constant_time_eq::constant_time_eq;
///
/// assert!(constant_time_eq(b"foo", b"foo"));
/// assert!(!constant_time_eq(b"foo", b"bar"));
/// assert!(!constant_time_eq(b"bar", b"baz"));
/// # assert!(constant_time_eq(b"", b""));
///
/// // Not equal-sized, so won't take constant time.
/// assert!(!constant_time_eq(b"foo", b""));
/// assert!(!constant_time_eq(b"foo", b"quux"));
/// ```
#[inline]
pub fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
a.len() == b.len() && constant_time_ne(a, b) == 0
}
// Fixed-size variants for the most common sizes.
macro_rules! constant_time_ne_n {
($ne:ident, $n:expr) => {
// This function is non-inline to prevent the optimizer from looking inside it.
#[inline(never)]
fn $ne(a: &[u8; $n], b: &[u8; $n]) -> u8 {
let mut tmp = 0;
for i in 0..$n {
tmp |= a[i] ^ b[i];
}
tmp // The compare with 0 must happen outside this function.
}
};
}
constant_time_ne_n!(constant_time_ne_16, 16);
constant_time_ne_n!(constant_time_ne_32, 32);
constant_time_ne_n!(constant_time_ne_64, 64);
/// Compares two 128-bit byte strings in constant time.
///
/// # Examples
///
/// ```
/// use constant_time_eq::constant_time_eq_16;
///
/// assert!(constant_time_eq_16(&[3; 16], &[3; 16]));
/// assert!(!constant_time_eq_16(&[3; 16], &[7; 16]));
/// ```
#[inline]
pub fn constant_time_eq_16(a: &[u8; 16], b: &[u8; 16]) -> bool {
constant_time_ne_16(a, b) == 0
}
/// Compares two 256-bit byte strings in constant time.
///
/// # Examples
///
/// ```
/// use constant_time_eq::constant_time_eq_32;
///
/// assert!(constant_time_eq_32(&[3; 32], &[3; 32]));
/// assert!(!constant_time_eq_32(&[3; 32], &[7; 32]));
/// ```
#[inline]
pub fn constant_time_eq_32(a: &[u8; 32], b: &[u8; 32]) -> bool {
constant_time_ne_32(a, b) == 0
}
/// Compares two 512-bit byte strings in constant time.
///
/// # Examples
///
/// ```
/// use constant_time_eq::constant_time_eq_64;
///
/// assert!(constant_time_eq_64(&[3; 64], &[3; 64]));
/// assert!(!constant_time_eq_64(&[3; 64], &[7; 64]));
/// ```
#[inline]
pub fn constant_time_eq_64(a: &[u8; 64], b: &[u8; 64]) -> bool {
constant_time_ne_64(a, b) == 0
}

View File

@ -1 +0,0 @@
{"files":{"Cargo.toml":"1c598c990a0957aa81b038c1ece1d6b0c6d606c3cdd6420eb3ef5845510a4178","LICENSE":"e6a8ae2d796083783efc94b1e66271aa2929dc4dfb231d34239aa9c7db8396db","README.md":"9c3a6fd2a798bd1e105c5ee72f7b475471e3a9abec89b5be2b5263d654085d22","src/lib.rs":"d9d82538b9c6a3467407525e0db2c588981f2491fb40fbe237af925bc13a7551","tests/etc/group":"175d89e8d03e2976d3f37110050372163fcafd07a20c899ade3a9690b2cb4526","tests/etc/passwd":"aee0d4bd2abf55846683cc2e5daaa03641636a262519623d59b1ab8e1eb1db32","tests/etc/shadow":"ca7c1a6f96eaef3bd26da4faeae55e78cad04822c191e3263b539ee687de4d0a"},"package":"de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d"}

View File

@ -1,35 +0,0 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies
#
# If you believe there's an error in this file please file an
# issue against the rust-lang/cargo repository. If you're
# editing this file be aware that the upstream Cargo.toml
# will likely look very different (and much more reasonable)
[package]
name = "redox_users"
version = "0.3.5"
authors = ["Jose Narvaez <goyox86@gmail.com>", "Wesley Hershberger <mggmugginsmc@gmail.com>"]
description = "A Rust library to access Redox users and groups functionality"
documentation = "https://docs.rs/redox_users"
readme = "README.md"
keywords = ["redox", "auth"]
license = "MIT"
repository = "https://gitlab.redox-os.org/redox-os/users"
[dependencies.getrandom]
version = "0.1"
[dependencies.redox_syscall]
version = "0.1"
[dependencies.rust-argon2]
version = "0.8"
optional = true
[features]
auth = ["rust-argon2"]
default = ["auth"]

View File

@ -1,22 +0,0 @@
The MIT License (MIT)
Copyright (c) 2017 Jose Narvaez
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,23 +0,0 @@
# redox_users <a href="https://crates.io/crates/redox_users"><img src="https://img.shields.io/crates/v/redox_users.svg"></a>
Redox OS APIs for accessing users and groups information. [Documentation](https://docs.rs/redox_users/0.1.0/redox_users/)
High level APIs for:
- Getting the current process effective user ID.
- Getting the current process user ID.
- Getting the current process effective group ID.
- Getting the current process group ID.
- Manipulating User and Group information (including adding, removing, and modifying groups and users, in addition to other functionality, see docs)
We recommend to use these APIs instead of directly manipulating the
`/etc/group` and `/etc/passwd` as this is an implementation detail and
might change in the future.
Note that redox_users is an API designed only for use on Redox. It compiles on other platforms (for testing), but it will not work and might produce unexpected behavior.
## Hashing
redox_users uses the Argon2 hashing algorithm. The default hashing parameters are as follows:
```Rust
Argon2::new(10, 1, 4096, Variant::Argon2i)
```

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +0,0 @@
root;0;root
user;1000;user
wheel;1;user,root
li;1007;li

View File

@ -1,3 +0,0 @@
root;0;0;root;file:/root;file:/bin/ion
user;1000;1000;user;file:/home/user;file:/bin/ion
li;1007;1007;Lorem;file:/home/lorem;file:/bin/ion

View File

@ -1,3 +0,0 @@
root;$argon2i$m=4096,t=10,p=1$Tnc4UVV0N00$ML9LIOujd3nmAfkAwEcSTMPqakWUF0OUiLWrIy0nGLk
user;
li;!

View File

@ -1 +0,0 @@
{"files":{"CHANGELOG.md":"5ec3f32d09a22757e0f36f39dbd57db8bb382e274446364eb0604b84ed9664e7","Cargo.toml":"847d8033f0dec82bee18fb3dc76f685f34431b7b0af813de9a18fd8a36b374d4","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"26b997d1a1496df49cb1ad2cbe024714cd9126543c18e1bc6c7bbca1b3dc42b6","README.md":"2e5715166ab7b99342d7be096db244709d96c3dc2fbe393eb8afe7de198ecd37","src/argon2.rs":"c7ee856d807a6f3eb72efacd0af96d71f1075ae8e754831485f3b80c072caaab","src/block.rs":"08968700128c9d5de01a60b070fbe20fee8c5bf7428979515d3598ddc4268a6f","src/common.rs":"ff3d031e058e65cc03247008c61ba702e08d2d2545536c86aa208bbc05e7e8e2","src/config.rs":"60b2fcfca173d613e78edebb8827aacec4007675f64bdad5a038c7c7f8b1456c","src/context.rs":"3e774b14e97f336c0935b0eacec44e11daeda6d2039eefb93ed3568b4e48ba02","src/core.rs":"273633c52892dae6da3d4e4c9cd116d59f4235ab65c57a6dd0d31042657d27fa","src/decoded.rs":"0e7b4a64a1848809a9e0dd95618eb32851f13a5ac73f95e4547cfbb71d0dfdbb","src/encoding.rs":"532089c7233908b2ce7082f920f4c95b8d568160b9fd0b1a8f4d2a962623a681","src/error.rs":"e8fd91c80830d97af6d132dc8c7e9d0dcaf690ca7c1daf8b661b1ad5cb15443c","src/lib.rs":"22c795c286035f2dd5f5ea668b67cca2f25e46ba7f68a084edae91b65ef197e6","src/memory.rs":"bee7a02c738024c4a0fbfd2f4ae6515fb8ec36cb3922dd2ff0683f7864b3b713","src/result.rs":"54afe0972853b6e16c8a99118ddf4564112b8446ba4592131c4c66afd55822dd","src/thread_mode.rs":"21814ede00af01d43c0b04392bcfa0dc6fa24d0bedcef13248a100b2e9b290a8","src/variant.rs":"8ec67987dded48b1851085df99b1f25aa4c99b749acc60476db77f6d780672e5","src/version.rs":"6cdbb34892e1c09983eb81aec39be93bb5ea09ad001aa9f7c387f631be7feda4","tests/integration_test.rs":"25fbc41a35595223d074091844f934b816c75fbd535153e998470479fb1b7478"},"package":"4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb"}

View File

@ -1,88 +0,0 @@
ChangeLog for rust-argon2
=========================
This documents all notable changes to
[rust-argon2](https://github.com/sru-systems/rust-argon2).
## 0.8.3
- Replace transmute with to_le_bytes.
- Update base64 to version 0.13.
- Update crossbeam-utils to version 0.8.
- Update hex to version 0.4.
- Derive Clone for Error struct.
- Add optional serde support for Error struct.
## 0.8.2
- Change base64 to latest version (0.12).
## 0.8.1
- Fix issue with verifying multi-lane hashes with parallelism disabled (#27)
## 0.8.0
- Make parallelism optional via feature flag.
## 0.7.0
- Update crossbeam-utils dependency to 0.7.
## 0.6.1
- Use constant time equals to compare hashes.
## 0.6.0
- Use 2018 edition or Rust
- Use &dyn error::Error instead of &error::Error
- Fix clippy lints
- Allow callers of encode_string to pass any &[u8]
- Update base64 dependency.
## 0.5.1
- Use crossbeam utils 0.6 instead of crossbeam 0.5
## 0.5.0
- Replace blake2-rfc with blake2b_simd.
## 0.4.0
- Replace rustc-serialize dependency with base64 and hex.
- Update base64 dependency.
- Update crossbeam dependency.
- Update hex dependency.
- Allow updating to minor versions of blake2-rfc.
## 0.3.0
- Embed Config struct in Context struct.
## 0.2.0
- Use ThreadMode enum instead of explicit thread number.
- Use a Config struct instead of explicit configuration arguments.
- Use references instead of vectors for byte data in the Context struct.
- Deprecate the following functions:
- hash_encoded_defaults
- hash_encoded_old
- hash_encoded_std
- hash_raw_defaults
- hash_raw_old
- hash_raw_std
- verify_raw_old
- verify_raw_std

View File

@ -1,49 +0,0 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies
#
# If you believe there's an error in this file please file an
# issue against the rust-lang/cargo repository. If you're
# editing this file be aware that the upstream Cargo.toml
# will likely look very different (and much more reasonable)
[package]
edition = "2018"
name = "rust-argon2"
version = "0.8.3"
authors = ["Martijn Rijkeboer <mrr@sru-systems.com>"]
description = "Rust implementation of the Argon2 password hashing function."
homepage = "https://github.com/sru-systems/rust-argon2"
documentation = "https://docs.sru-systems.com/rust-argon2/0.8.0/argon2/"
readme = "README.md"
keywords = ["argon2", "argon2d", "argon2i", "hash", "password"]
license = "MIT/Apache-2.0"
repository = "https://github.com/sru-systems/rust-argon2"
[lib]
name = "argon2"
[dependencies.base64]
version = "0.13"
[dependencies.blake2b_simd]
version = "0.5"
[dependencies.constant_time_eq]
version = "0.1.4"
[dependencies.crossbeam-utils]
version = "0.8"
optional = true
[dependencies.serde]
version = "1.0.116"
features = ["derive"]
optional = true
[dev-dependencies.hex]
version = "0.4"
[features]
default = ["crossbeam-utils"]

View File

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,22 +0,0 @@
The MIT license.
Copyright (c) 2017 Martijn Rijkeboer <mrr@sru-systems.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,85 +0,0 @@
# Rust-argon2
Rust library for hashing passwords using
[Argon2](https://github.com/P-H-C/phc-winner-argon2), the password-hashing
function that won the
[Password Hashing Competition (PHC)](https://password-hashing.net).
## Usage
To use `rust-argon2`, add the following to your Cargo.toml:
```toml
[dependencies]
rust-argon2 = "0.8"
```
And the following to your crate root:
```rust
extern crate argon2;
```
## Examples
Create a password hash using the defaults and verify it:
```rust
use argon2::{self, Config};
let password = b"password";
let salt = b"randomsalt";
let config = Config::default();
let hash = argon2::hash_encoded(password, salt, &config).unwrap();
let matches = argon2::verify_encoded(&hash, password).unwrap();
assert!(matches);
```
Create a password hash with custom settings and verify it:
```rust
use argon2::{self, Config, ThreadMode, Variant, Version};
let password = b"password";
let salt = b"othersalt";
let config = Config {
variant: Variant::Argon2i,
version: Version::Version13,
mem_cost: 65536,
time_cost: 10,
lanes: 4,
thread_mode: ThreadMode::Parallel,
secret: &[],
ad: &[],
hash_length: 32
};
let hash = argon2::hash_encoded(password, salt, &config).unwrap();
let matches = argon2::verify_encoded(&hash, password).unwrap();
assert!(matches);
```
## Limitations
This crate has the same limitation as the `blake2-rfc` crate that it uses.
It does not attempt to clear potentially sensitive data from its work
memory. To do so correctly without a heavy performance penalty would
require help from the compiler. It's better to not attempt to do so than to
present a false assurance.
This version uses the standard implementation and does not yet implement
optimizations. Therefore, it is not the fastest implementation available.
## License
Rust-argon2 is dual licensed under the [MIT](LICENSE-MIT) and
[Apache 2.0](LICENSE-APACHE) licenses, the same licenses as the Rust compiler.
## Contributions
Contributions are welcome. By submitting a pull request you are agreeing to
make you work available under the license terms of the Rust-argon2 project.

View File

@ -1,760 +0,0 @@
// Copyright (c) 2017 Martijn Rijkeboer <mrr@sru-systems.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::config::Config;
use crate::context::Context;
use crate::core;
use crate::encoding;
use crate::memory::Memory;
use crate::result::Result;
use crate::thread_mode::ThreadMode;
use crate::variant::Variant;
use crate::version::Version;
use constant_time_eq::constant_time_eq;
/// Returns the length of the encoded string.
///
/// # Remarks
///
/// The length is **one** less that the original C version, since no null
/// terminator is used.
///
/// # Examples
///
/// ```rust
/// use argon2::{self, Variant};
///
/// let variant = Variant::Argon2i;
/// let mem = 4096;
/// let time = 10;
/// let parallelism = 10;
/// let salt_len = 8;
/// let hash_len = 32;
/// let enc_len = argon2::encoded_len(variant, mem, time, parallelism, salt_len, hash_len);
/// assert_eq!(enc_len, 86);
/// ```
#[rustfmt::skip]
pub fn encoded_len(
variant: Variant,
mem_cost: u32,
time_cost: u32,
parallelism: u32,
salt_len: u32,
hash_len: u32
) -> u32 {
("$$v=$m=,t=,p=$$".len() as u32) +
(variant.as_lowercase_str().len() as u32) +
encoding::num_len(Version::default().as_u32()) +
encoding::num_len(mem_cost) +
encoding::num_len(time_cost) +
encoding::num_len(parallelism) +
encoding::base64_len(salt_len) +
encoding::base64_len(hash_len)
}
/// Hashes the password and returns the encoded hash.
///
/// # Examples
///
/// Create an encoded hash with the default configuration:
///
/// ```
/// use argon2::{self, Config};
///
/// let pwd = b"password";
/// let salt = b"somesalt";
/// let config = Config::default();
/// let encoded = argon2::hash_encoded(pwd, salt, &config).unwrap();
/// ```
///
///
/// Create an Argon2d encoded hash with 4 lanes and parallel execution:
///
/// ```
/// use argon2::{self, Config, ThreadMode, Variant};
///
/// let pwd = b"password";
/// let salt = b"somesalt";
/// let mut config = Config::default();
/// config.variant = Variant::Argon2d;
#[cfg_attr(feature = "crossbeam-utils", doc = "config.lanes = 4;")]
#[cfg_attr(
feature = "crossbeam-utils",
doc = "config.thread_mode = ThreadMode::Parallel;"
)]
#[cfg_attr(not(feature = "crossbeam-utils"), doc = "config.lanes = 1;")]
#[cfg_attr(
not(feature = "crossbeam-utils"),
doc = "config.thread_mode = ThreadMode::Sequential;"
)]
/// let encoded = argon2::hash_encoded(pwd, salt, &config).unwrap();
/// ```
pub fn hash_encoded(pwd: &[u8], salt: &[u8], config: &Config) -> Result<String> {
let context = Context::new(config.clone(), pwd, salt)?;
let hash = run(&context);
let encoded = encoding::encode_string(&context, &hash);
Ok(encoded)
}
/// Hashes the password using default settings and returns the encoded hash.
///
/// # Examples
///
/// ```
/// use argon2;
///
/// let pwd = b"password";
/// let salt = b"somesalt";
/// let encoded = argon2::hash_encoded_defaults(pwd, salt).unwrap();
/// ```
///
/// The above rewritten using `hash_encoded`:
///
/// ```
/// use argon2::{self, Config};
///
/// let pwd = b"password";
/// let salt = b"somesalt";
/// let config = Config::default();
/// let encoded = argon2::hash_encoded(pwd, salt, &config).unwrap();
/// ```
#[deprecated(since = "0.2.0", note = "please use `hash_encoded` instead")]
pub fn hash_encoded_defaults(pwd: &[u8], salt: &[u8]) -> Result<String> {
hash_encoded(pwd, salt, &Config::default())
}
/// Hashes the password and returns the encoded hash (pre 0.2.0 `hash_encoded`).
///
/// # Examples
///
///
/// ```
/// use argon2::{self, Variant, Version};
///
/// let pwd = b"password";
/// let salt = b"somesalt";
/// let mem_cost = 4096;
/// let time_cost = 10;
/// let lanes = 1;
/// let threads = 1;
/// let secret = b"secret value";
/// let ad = b"associated data";
/// let hash_len = 32;
/// let encoded = argon2::hash_encoded_old(Variant::Argon2i,
/// Version::Version13,
/// mem_cost,
/// time_cost,
/// lanes,
/// threads,
/// pwd,
/// salt,
/// secret,
/// ad,
/// hash_len).unwrap();
/// ```
///
/// The above rewritten using the new `hash_encoded`:
///
/// ```
/// use argon2::{self, Config, ThreadMode, Variant, Version};
///
/// let pwd = b"password";
/// let salt = b"somesalt";
/// let config = Config {
/// variant: Variant::Argon2i,
/// version: Version::Version13,
/// mem_cost: 4096,
/// time_cost: 10,
/// lanes: 1,
/// thread_mode: ThreadMode::Sequential,
/// secret: b"secret value",
/// ad: b"associated data",
/// hash_length: 32,
/// };
/// let encoded = argon2::hash_encoded(pwd, salt, &config).unwrap();
/// ```
#[deprecated(since = "0.2.0", note = "please use new `hash_encoded` instead")]
pub fn hash_encoded_old(
variant: Variant,
version: Version,
mem_cost: u32,
time_cost: u32,
lanes: u32,
threads: u32,
pwd: &[u8],
salt: &[u8],
secret: &[u8],
ad: &[u8],
hash_len: u32,
) -> Result<String> {
let threads = if cfg!(feature = "crossbeam-utils") { threads } else { 1 };
let config = Config {
variant,
version,
mem_cost,
time_cost,
lanes,
thread_mode: ThreadMode::from_threads(threads),
secret,
ad,
hash_length: hash_len,
};
hash_encoded(pwd, salt, &config)
}
/// Hashes the password and returns the encoded hash (standard).
///
/// # Examples
///
///
/// ```
/// use argon2::{self, Variant, Version};
///
/// let pwd = b"password";
/// let salt = b"somesalt";
/// let mem_cost = 4096;
/// let time_cost = 10;
/// let parallelism = 1;
/// let hash_len = 32;
/// let encoded = argon2::hash_encoded_std(Variant::Argon2i,
/// Version::Version13,
/// mem_cost,
/// time_cost,
/// parallelism,
/// pwd,
/// salt,
/// hash_len).unwrap();
/// ```
///
/// The above rewritten using `hash_encoded`:
///
/// ```
/// use argon2::{self, Config, ThreadMode, Variant, Version};
///
/// let pwd = b"password";
/// let salt = b"somesalt";
/// let config = Config {
/// variant: Variant::Argon2i,
/// version: Version::Version13,
/// mem_cost: 4096,
/// time_cost: 10,
/// lanes: 1,
/// thread_mode: ThreadMode::Sequential,
/// secret: &[],
/// ad: &[],
/// hash_length: 32,
/// };
/// let encoded = argon2::hash_encoded(pwd, salt, &config).unwrap();
/// ```
#[deprecated(since = "0.2.0", note = "please use `hash_encoded` instead")]
pub fn hash_encoded_std(
variant: Variant,
version: Version,
mem_cost: u32,
time_cost: u32,
parallelism: u32,
pwd: &[u8],
salt: &[u8],
hash_len: u32,
) -> Result<String> {
let threads = if cfg!(feature = "crossbeam-utils") { parallelism } else { 1 };
let config = Config {
variant,
version,
mem_cost,
time_cost,
lanes: parallelism,
thread_mode: ThreadMode::from_threads(threads),
secret: &[],
ad: &[],
hash_length: hash_len,
};
hash_encoded(pwd, salt, &config)
}
/// Hashes the password and returns the hash as a vector.
///
/// # Examples
///
/// Create a hash with the default configuration:
///
/// ```
/// use argon2::{self, Config};
///
/// let pwd = b"password";
/// let salt = b"somesalt";
/// let config = Config::default();
/// let vec = argon2::hash_raw(pwd, salt, &config).unwrap();
/// ```
///
///
/// Create an Argon2d hash with 4 lanes and parallel execution:
///
/// ```
/// use argon2::{self, Config, ThreadMode, Variant};
///
/// let pwd = b"password";
/// let salt = b"somesalt";
/// let mut config = Config::default();
/// config.variant = Variant::Argon2d;
#[cfg_attr(feature = "crossbeam-utils", doc = "config.lanes = 4;")]
#[cfg_attr(
feature = "crossbeam-utils",
doc = "config.thread_mode = ThreadMode::Parallel;"
)]
#[cfg_attr(not(feature = "crossbeam-utils"), doc = "config.lanes = 1;")]
#[cfg_attr(
not(feature = "crossbeam-utils"),
doc = "config.thread_mode = ThreadMode::Sequential;"
)]
/// let vec = argon2::hash_raw(pwd, salt, &config).unwrap();
/// ```
pub fn hash_raw(pwd: &[u8], salt: &[u8], config: &Config) -> Result<Vec<u8>> {
let context = Context::new(config.clone(), pwd, salt)?;
let hash = run(&context);
Ok(hash)
}
/// Hashes the password using default settings and returns the hash as a vector.
///
/// # Examples
///
/// ```
/// use argon2;
///
/// let pwd = b"password";
/// let salt = b"somesalt";
/// let vec = argon2::hash_raw_defaults(pwd, salt).unwrap();
/// ```
///
/// The above rewritten using `hash_raw`:
///
/// ```
/// use argon2::{self, Config};
///
/// let pwd = b"password";
/// let salt = b"somesalt";
/// let config = Config::default();
/// let vec = argon2::hash_raw(pwd, salt, &config).unwrap();
/// ```
#[deprecated(since = "0.2.0", note = "please use `hash_raw` instead")]
pub fn hash_raw_defaults(pwd: &[u8], salt: &[u8]) -> Result<Vec<u8>> {
hash_raw(pwd, salt, &Config::default())
}
/// Hashes the password and returns the hash as a vector (pre 0.2.0 `hash_raw`).
///
/// # Examples
///
///
/// ```
/// use argon2::{self, Variant, Version};
///
/// let mem_cost = 4096;
/// let time_cost = 10;
/// let lanes = 1;
/// let threads = 1;
/// let pwd = b"password";
/// let salt = b"somesalt";
/// let secret = b"secret value";
/// let ad = b"associated data";
/// let hash_len = 32;
/// let vec = argon2::hash_raw_old(Variant::Argon2i,
/// Version::Version13,
/// mem_cost,
/// time_cost,
/// lanes,
/// threads,
/// pwd,
/// salt,
/// secret,
/// ad,
/// hash_len).unwrap();
/// ```
///
/// The above rewritten using the new `hash_raw`:
///
/// ```
/// use argon2::{self, Config, ThreadMode, Variant, Version};
///
/// let pwd = b"password";
/// let salt = b"somesalt";
/// let config = Config {
/// variant: Variant::Argon2i,
/// version: Version::Version13,
/// mem_cost: 4096,
/// time_cost: 10,
/// lanes: 1,
/// thread_mode: ThreadMode::Sequential,
/// secret: b"secret value",
/// ad: b"associated data",
/// hash_length: 32,
/// };
/// let vec = argon2::hash_raw(pwd, salt, &config);
/// ```
#[deprecated(since = "0.2.0", note = "please use new `hash_raw` instead")]
pub fn hash_raw_old(
variant: Variant,
version: Version,
mem_cost: u32,
time_cost: u32,
lanes: u32,
threads: u32,
pwd: &[u8],
salt: &[u8],
secret: &[u8],
ad: &[u8],
hash_len: u32,
) -> Result<Vec<u8>> {
let threads = if cfg!(feature = "crossbeam-utils") { threads } else { 1 };
let config = Config {
variant,
version,
mem_cost,
time_cost,
lanes,
thread_mode: ThreadMode::from_threads(threads),
secret,
ad,
hash_length: hash_len,
};
hash_raw(pwd, salt, &config)
}
/// Hashes the password and returns the hash as a vector (standard).
///
/// # Examples
///
///
/// ```
/// use argon2::{self, Variant, Version};
///
/// let pwd = b"password";
/// let salt = b"somesalt";
/// let mem_cost = 4096;
/// let time_cost = 10;
/// let parallelism = 1;
/// let hash_len = 32;
/// let vec = argon2::hash_raw_std(Variant::Argon2i,
/// Version::Version13,
/// mem_cost,
/// time_cost,
/// parallelism,
/// pwd,
/// salt,
/// hash_len).unwrap();
/// ```
///
/// The above rewritten using `hash_raw`:
///
/// ```
/// use argon2::{self, Config, ThreadMode, Variant, Version};
///
/// let pwd = b"password";
/// let salt = b"somesalt";
/// let config = Config {
/// variant: Variant::Argon2i,
/// version: Version::Version13,
/// mem_cost: 4096,
/// time_cost: 10,
/// lanes: 1,
/// thread_mode: ThreadMode::Sequential,
/// secret: &[],
/// ad: &[],
/// hash_length: 32,
/// };
/// let vec = argon2::hash_raw(pwd, salt, &config);
/// ```
#[deprecated(since = "0.2.0", note = "please use `hash_raw` instead")]
pub fn hash_raw_std(
variant: Variant,
version: Version,
mem_cost: u32,
time_cost: u32,
parallelism: u32,
pwd: &[u8],
salt: &[u8],
hash_len: u32,
) -> Result<Vec<u8>> {
let threads = if cfg!(feature = "crossbeam-utils") { parallelism } else { 1 };
let config = Config {
variant,
version,
mem_cost,
time_cost,
lanes: parallelism,
thread_mode: ThreadMode::from_threads(threads),
secret: &[],
ad: &[],
hash_length: hash_len,
};
hash_raw(pwd, salt, &config)
}
/// Verifies the password with the encoded hash.
///
/// # Examples
///
/// ```
/// use argon2;
///
/// let enc = "$argon2i$v=19$m=4096,t=3,p=1$c29tZXNhbHQ\
/// $iWh06vD8Fy27wf9npn6FXWiCX4K6pW6Ue1Bnzz07Z8A";
/// let pwd = b"password";
/// let res = argon2::verify_encoded(enc, pwd).unwrap();
/// assert!(res);
/// ```
pub fn verify_encoded(encoded: &str, pwd: &[u8]) -> Result<bool> {
verify_encoded_ext(encoded, pwd, &[], &[])
}
/// Verifies the password with the encoded hash, secret and associated data.
///
/// # Examples
///
/// ```
/// use argon2;
///
/// let enc = "$argon2i$v=19$m=4096,t=3,p=1$c29tZXNhbHQ\
/// $OlcSvlN20Lz43sK3jhCJ9K04oejhiY0AmI+ck6nuETo";
/// let pwd = b"password";
/// let secret = b"secret";
/// let ad = b"ad";
/// let res = argon2::verify_encoded_ext(enc, pwd, secret, ad).unwrap();
/// assert!(res);
/// ```
pub fn verify_encoded_ext(encoded: &str, pwd: &[u8], secret: &[u8], ad: &[u8]) -> Result<bool> {
let decoded = encoding::decode_string(encoded)?;
let threads = if cfg!(feature = "crossbeam-utils") { decoded.parallelism } else { 1 };
let config = Config {
variant: decoded.variant,
version: decoded.version,
mem_cost: decoded.mem_cost,
time_cost: decoded.time_cost,
lanes: decoded.parallelism,
thread_mode: ThreadMode::from_threads(threads),
secret,
ad,
hash_length: decoded.hash.len() as u32,
};
verify_raw(pwd, &decoded.salt, &decoded.hash, &config)
}
/// Verifies the password with the supplied configuration.
///
/// # Examples
///
///
/// ```
/// use argon2::{self, Config};
///
/// let pwd = b"password";
/// let salt = b"somesalt";
/// let hash = &[137, 104, 116, 234, 240, 252, 23, 45, 187, 193, 255, 103, 166,
/// 126, 133, 93, 104, 130, 95, 130, 186, 165, 110, 148, 123, 80,
/// 103, 207, 61, 59, 103, 192];
/// let config = Config::default();
/// let res = argon2::verify_raw(pwd, salt, hash, &config).unwrap();
/// assert!(res);
/// ```
pub fn verify_raw(pwd: &[u8], salt: &[u8], hash: &[u8], config: &Config) -> Result<bool> {
let config = Config {
hash_length: hash.len() as u32,
..config.clone()
};
let context = Context::new(config, pwd, salt)?;
let calculated_hash = run(&context);
Ok(constant_time_eq(hash, &calculated_hash))
}
/// Verifies the password with the supplied settings (pre 0.2.0 `verify_raw`).
///
/// # Examples
///
/// ```
/// use argon2::{self, Variant, Version};
///
/// let pwd = b"password";
/// let salt = b"somesalt";
/// let hash = &[137, 104, 116, 234, 240, 252, 23, 45, 187, 193, 255, 103, 166,
/// 126, 133, 93, 104, 130, 95, 130, 186, 165, 110, 148, 123, 80,
/// 103, 207, 61, 59, 103, 192];
/// let mem_cost = 4096;
/// let time_cost = 3;
/// let lanes = 1;
/// let threads = 1;
/// let secret = &[];
/// let ad = &[];
/// let res = argon2::verify_raw_old(Variant::Argon2i,
/// Version::Version13,
/// mem_cost,
/// time_cost,
/// lanes,
/// threads,
/// pwd,
/// salt,
/// secret,
/// ad,
/// hash).unwrap();
/// assert!(res);
/// ```
///
/// The above rewritten using the new `verify_raw`:
///
/// ```
/// use argon2::{self, Config, ThreadMode, Variant, Version};
///
/// let pwd = b"password";
/// let salt = b"somesalt";
/// let hash = &[137, 104, 116, 234, 240, 252, 23, 45, 187, 193, 255, 103, 166,
/// 126, 133, 93, 104, 130, 95, 130, 186, 165, 110, 148, 123, 80,
/// 103, 207, 61, 59, 103, 192];
/// let config = Config {
/// variant: Variant::Argon2i,
/// version: Version::Version13,
/// mem_cost: 4096,
/// time_cost: 3,
/// lanes: 1,
/// thread_mode: ThreadMode::Sequential,
/// secret: &[],
/// ad: &[],
/// hash_length: hash.len() as u32,
/// };
/// let res = argon2::verify_raw(pwd, salt, hash, &config).unwrap();
/// assert!(res);
/// ```
#[deprecated(since = "0.2.0", note = "please use new `verify_raw` instead")]
pub fn verify_raw_old(
variant: Variant,
version: Version,
mem_cost: u32,
time_cost: u32,
lanes: u32,
threads: u32,
pwd: &[u8],
salt: &[u8],
secret: &[u8],
ad: &[u8],
hash: &[u8],
) -> Result<bool> {
let threads = if cfg!(feature = "crossbeam-utils") { threads } else { 1 };
let config = Config {
variant,
version,
mem_cost,
time_cost,
lanes,
thread_mode: ThreadMode::from_threads(threads),
secret,
ad,
hash_length: hash.len() as u32,
};
verify_raw(pwd, salt, hash, &config)
}
/// Verifies the password with the supplied settings (standard).
///
/// # Examples
///
///
/// ```
/// use argon2::{self, Variant, Version};
///
/// let pwd = b"password";
/// let salt = b"somesalt";
/// let hash = &[137, 104, 116, 234, 240, 252, 23, 45, 187, 193, 255, 103, 166,
/// 126, 133, 93, 104, 130, 95, 130, 186, 165, 110, 148, 123, 80,
/// 103, 207, 61, 59, 103, 192];
/// let mem_cost = 4096;
/// let time_cost = 3;
/// let parallelism = 1;
/// let res = argon2::verify_raw_std(Variant::Argon2i,
/// Version::Version13,
/// mem_cost,
/// time_cost,
/// parallelism,
/// pwd,
/// salt,
/// hash).unwrap();
/// assert!(res);
/// ```
///
/// The above rewritten using `verify_raw`:
///
/// ```
/// use argon2::{self, Config, ThreadMode, Variant, Version};
///
/// let pwd = b"password";
/// let salt = b"somesalt";
/// let hash = &[137, 104, 116, 234, 240, 252, 23, 45, 187, 193, 255, 103, 166,
/// 126, 133, 93, 104, 130, 95, 130, 186, 165, 110, 148, 123, 80,
/// 103, 207, 61, 59, 103, 192];
/// let config = Config {
/// variant: Variant::Argon2i,
/// version: Version::Version13,
/// mem_cost: 4096,
/// time_cost: 3,
/// lanes: 1,
/// thread_mode: ThreadMode::Sequential,
/// secret: &[],
/// ad: &[],
/// hash_length: hash.len() as u32,
/// };
/// let res = argon2::verify_raw(pwd, salt, hash, &config).unwrap();
/// assert!(res);
/// ```
#[deprecated(since = "0.2.0", note = "please use `verify_raw` instead")]
pub fn verify_raw_std(
variant: Variant,
version: Version,
mem_cost: u32,
time_cost: u32,
parallelism: u32,
pwd: &[u8],
salt: &[u8],
hash: &[u8],
) -> Result<bool> {
let threads = if cfg!(feature = "crossbeam-utils") { parallelism } else { 1 };
let config = Config {
variant,
version,
mem_cost,
time_cost,
lanes: parallelism,
thread_mode: ThreadMode::from_threads(threads),
secret: &[],
ad: &[],
hash_length: hash.len() as u32,
};
verify_raw(pwd, salt, hash, &config)
}
fn run(context: &Context) -> Vec<u8> {
let mut memory = Memory::new(context.config.lanes, context.lane_length);
core::initialize(context, &mut memory);
core::fill_memory_blocks(context, &mut memory);
core::finalize(context, &memory)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn single_thread_verification_multi_lane_hash() {
/*
let hash = hash_encoded(b"foo", b"abcdefghijklmnopqrstuvwxyz", &Config {
lanes: 4, thread_mode: ThreadMode::Parallel,
..Config::default()
});
*/
let hash = "$argon2i$v=19$m=4096,t=3,p=4$YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo$BvBk2OaSofBHfbrUW61nHrWB/43xgfs/QJJ5DkMAd8I";
verify_encoded(hash, b"foo").unwrap();
}
}

View File

@ -1,146 +0,0 @@
// Copyright (c) 2017 Martijn Rijkeboer <mrr@sru-systems.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::common;
use std::fmt;
use std::fmt::Debug;
use std::ops::{BitXorAssign, Index, IndexMut};
/// Structure for the (1KB) memory block implemented as 128 64-bit words.
pub struct Block([u64; common::QWORDS_IN_BLOCK]);
impl Block {
/// Gets the byte slice representation of the block.
pub fn as_u8(&self) -> &[u8] {
let bytes: &[u8; common::BLOCK_SIZE] = unsafe {
&*(&self.0 as *const [u64; common::QWORDS_IN_BLOCK] as *const [u8; common::BLOCK_SIZE])
};
bytes
}
/// Gets the mutable byte slice representation of the block.
pub fn as_u8_mut(&mut self) -> &mut [u8] {
let bytes: &mut [u8; common::BLOCK_SIZE] = unsafe {
&mut *(&mut self.0 as *mut [u64; common::QWORDS_IN_BLOCK]
as *mut [u8; common::BLOCK_SIZE])
};
bytes
}
/// Copies self to destination.
pub fn copy_to(&self, dst: &mut Block) {
for (d, s) in dst.0.iter_mut().zip(self.0.iter()) {
*d = *s
}
}
/// Creates a new block filled with zeros.
pub fn zero() -> Block {
Block([0u64; common::QWORDS_IN_BLOCK])
}
}
impl<'a> BitXorAssign<&'a Block> for Block {
fn bitxor_assign(&mut self, rhs: &Block) {
for (s, r) in self.0.iter_mut().zip(rhs.0.iter()) {
*s ^= *r
}
}
}
impl Clone for Block {
fn clone(&self) -> Block {
Block(self.0)
}
}
impl Debug for Block {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_list().entries(self.0.iter()).finish()
}
}
impl Eq for Block {}
impl Index<usize> for Block {
type Output = u64;
fn index(&self, index: usize) -> &u64 {
&self.0[index]
}
}
impl IndexMut<usize> for Block {
fn index_mut(&mut self, index: usize) -> &mut u64 {
&mut self.0[index]
}
}
impl PartialEq for Block {
fn eq(&self, other: &Block) -> bool {
let mut equal = true;
for (s, o) in self.0.iter().zip(other.0.iter()) {
if s != o {
equal = false;
}
}
equal
}
}
#[cfg(test)]
mod tests {
use crate::block::Block;
use crate::common;
#[test]
fn as_u8_returns_correct_slice() {
let block = Block::zero();
let expected = vec![0u8; 1024];
let actual = block.as_u8();
assert_eq!(actual, expected.as_slice());
}
#[test]
fn as_u8_mut_returns_correct_slice() {
let mut block = Block::zero();
let mut expected = vec![0u8; 1024];
let actual = block.as_u8_mut();
assert_eq!(actual, expected.as_mut_slice());
}
#[test]
fn bitxor_assign_updates_lhs() {
let mut lhs = Block([0u64; common::QWORDS_IN_BLOCK]);
let rhs = Block([1u64; common::QWORDS_IN_BLOCK]);
lhs ^= &rhs;
assert_eq!(lhs, rhs);
}
#[test]
fn copy_to_copies_block() {
let src = Block([1u64; common::QWORDS_IN_BLOCK]);
let mut dst = Block([0u64; common::QWORDS_IN_BLOCK]);
src.copy_to(&mut dst);
assert_eq!(dst, src);
}
#[test]
fn clone_clones_block() {
let orig = Block([1u64; common::QWORDS_IN_BLOCK]);
let copy = orig.clone();
assert_eq!(copy, orig);
}
#[test]
fn zero_creates_block_will_all_zeros() {
let expected = Block([0u64; common::QWORDS_IN_BLOCK]);
let actual = Block::zero();
assert_eq!(actual, expected);
}
}

View File

@ -1,92 +0,0 @@
// Copyright (c) 2017 Martijn Rijkeboer <mrr@sru-systems.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/// Default number of lanes (degree of parallelism).
pub const DEF_LANES: u32 = 1;
/// Minimum number of lanes (degree of parallelism).
pub const MIN_LANES: u32 = 1;
/// Maximum number of lanes (degree of parallelism).
pub const MAX_LANES: u32 = 0x00FF_FFFF;
/// Number of synchronization points between lanes per pass.
pub const SYNC_POINTS: u32 = 4;
/// Default digest size in bytes.
pub const DEF_HASH_LENGTH: u32 = 32;
/// Minimum digest size in bytes.
pub const MIN_HASH_LENGTH: u32 = 4;
/// Maximum digest size in bytes.
pub const MAX_HASH_LENGTH: u32 = 0xFFFF_FFFF;
/// Default number of memory blocks (2^12).
pub const DEF_MEMORY: u32 = 4096;
/// Minimum number of memory blocks (each of BLOCK_SIZE bytes).
pub const MIN_MEMORY: u32 = 2 * SYNC_POINTS;
/// Maximum number of memory blocks (each of BLOCK_SIZE bytes).
#[cfg(target_pointer_width = "32")]
pub const MAX_MEMORY: u32 = 0x200000;
#[cfg(target_pointer_width = "64")]
pub const MAX_MEMORY: u32 = 0xFFFF_FFFF;
/// Default number of passes.
pub const DEF_TIME: u32 = 3;
/// Minimum number of passes
pub const MIN_TIME: u32 = 1;
/// Maximum number of passes.
pub const MAX_TIME: u32 = 0xFFFF_FFFF;
/// Minimum password length in bytes.
pub const MIN_PWD_LENGTH: u32 = 0;
/// Maximum password length in bytes.
pub const MAX_PWD_LENGTH: u32 = 0xFFFF_FFFF;
/// Minimum associated data length in bytes.
pub const MIN_AD_LENGTH: u32 = 0;
/// Maximum associated data length in bytes.
pub const MAX_AD_LENGTH: u32 = 0xFFFF_FFFF;
/// Minimum salt length in bytes.
pub const MIN_SALT_LENGTH: u32 = 8;
/// Maximum salt length in bytes.
pub const MAX_SALT_LENGTH: u32 = 0xFFFF_FFFF;
/// Minimum key length in bytes.
pub const MIN_SECRET_LENGTH: u32 = 0;
/// Maximum key length in bytes.
pub const MAX_SECRET_LENGTH: u32 = 0xFFFF_FFFF;
/// Memory block size in bytes.
pub const BLOCK_SIZE: usize = 1024;
/// Number of quad words in a block.
pub const QWORDS_IN_BLOCK: usize = BLOCK_SIZE / 8;
/// Number of pseudo-random values generated by one call to Blake in Argon2i
/// to generate reference block positions.
pub const ADDRESSES_IN_BLOCK: u32 = 128;
/// Pre-hashing digest length.
pub const PREHASH_DIGEST_LENGTH: usize = 64;
/// Pre-hashing digest length with extension.
pub const PREHASH_SEED_LENGTH: usize = 72;
/// Blake2b output length in bytes.
pub const BLAKE2B_OUT_LENGTH: usize = 64;

View File

@ -1,105 +0,0 @@
// Copyright (c) 2017 Martijn Rijkeboer <mrr@sru-systems.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::common;
use crate::thread_mode::ThreadMode;
use crate::variant::Variant;
use crate::version::Version;
/// Structure containing configuration settings.
///
/// # Examples
///
/// ```
/// use argon2::{Config, ThreadMode, Variant, Version};
///
/// let config = Config::default();
/// assert_eq!(config.ad, &[]);
/// assert_eq!(config.hash_length, 32);
/// assert_eq!(config.lanes, 1);
/// assert_eq!(config.mem_cost, 4096);
/// assert_eq!(config.secret, &[]);
/// assert_eq!(config.thread_mode, ThreadMode::Sequential);
/// assert_eq!(config.time_cost, 3);
/// assert_eq!(config.variant, Variant::Argon2i);
/// assert_eq!(config.version, Version::Version13);
/// ```
#[derive(Clone, Debug, PartialEq)]
pub struct Config<'a> {
/// The associated data.
pub ad: &'a [u8],
/// The length of the resulting hash.
pub hash_length: u32,
/// The number of lanes.
pub lanes: u32,
/// The amount of memory requested (KB).
pub mem_cost: u32,
/// The key.
pub secret: &'a [u8],
/// The thread mode.
pub thread_mode: ThreadMode,
/// The number of passes.
pub time_cost: u32,
/// The variant.
pub variant: Variant,
/// The version number.
pub version: Version,
}
impl<'a> Config<'a> {
pub fn uses_sequential(&self) -> bool {
self.thread_mode == ThreadMode::Sequential || self.lanes == 1
}
}
impl<'a> Default for Config<'a> {
fn default() -> Config<'a> {
Config {
ad: &[],
hash_length: common::DEF_HASH_LENGTH,
lanes: common::DEF_LANES,
mem_cost: common::DEF_MEMORY,
secret: &[],
thread_mode: ThreadMode::default(),
time_cost: common::DEF_TIME,
variant: Variant::default(),
version: Version::default(),
}
}
}
#[cfg(test)]
mod tests {
use crate::config::Config;
use crate::thread_mode::ThreadMode;
use crate::variant::Variant;
use crate::version::Version;
#[test]
fn default_returns_correct_instance() {
let config = Config::default();
assert_eq!(config.ad, &[]);
assert_eq!(config.hash_length, 32);
assert_eq!(config.lanes, 1);
assert_eq!(config.mem_cost, 4096);
assert_eq!(config.secret, &[]);
assert_eq!(config.thread_mode, ThreadMode::Sequential);
assert_eq!(config.time_cost, 3);
assert_eq!(config.variant, Variant::Argon2i);
assert_eq!(config.version, Version::Version13);
}
}

View File

@ -1,234 +0,0 @@
// Copyright (c) 2017 Martijn Rijkeboer <mrr@sru-systems.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::common;
use crate::config::Config;
use crate::error::Error;
use crate::result::Result;
/// Structure containing settings for the Argon2 algorithm. A combination of
/// the original argon2_context and argon2_instance_t.
#[derive(Debug, PartialEq)]
pub struct Context<'a> {
/// The config for this context.
pub config: Config<'a>,
/// The length of a lane.
pub lane_length: u32,
/// The number of memory blocks.
pub memory_blocks: u32,
/// The password.
pub pwd: &'a [u8],
/// The salt.
pub salt: &'a [u8],
/// The length of a segment.
pub segment_length: u32,
}
impl<'a> Context<'a> {
/// Attempts to create a new context.
pub fn new(config: Config<'a>, pwd: &'a [u8], salt: &'a [u8]) -> Result<Context<'a>> {
if config.lanes < common::MIN_LANES {
return Err(Error::LanesTooFew);
} else if config.lanes > common::MAX_LANES {
return Err(Error::LanesTooMany);
}
let lanes = config.lanes;
if config.mem_cost < common::MIN_MEMORY {
return Err(Error::MemoryTooLittle);
} else if config.mem_cost > common::MAX_MEMORY {
return Err(Error::MemoryTooMuch);
} else if config.mem_cost < 8 * lanes {
return Err(Error::MemoryTooLittle);
}
if config.time_cost < common::MIN_TIME {
return Err(Error::TimeTooSmall);
} else if config.time_cost > common::MAX_TIME {
return Err(Error::TimeTooLarge);
}
let pwd_len = pwd.len();
if pwd_len < common::MIN_PWD_LENGTH as usize {
return Err(Error::PwdTooShort);
} else if pwd_len > common::MAX_PWD_LENGTH as usize {
return Err(Error::PwdTooLong);
}
let salt_len = salt.len();
if salt_len < common::MIN_SALT_LENGTH as usize {
return Err(Error::SaltTooShort);
} else if salt_len > common::MAX_SALT_LENGTH as usize {
return Err(Error::SaltTooLong);
}
let secret_len = config.secret.len();
if secret_len < common::MIN_SECRET_LENGTH as usize {
return Err(Error::SecretTooShort);
} else if secret_len > common::MAX_SECRET_LENGTH as usize {
return Err(Error::SecretTooLong);
}
let ad_len = config.ad.len();
if ad_len < common::MIN_AD_LENGTH as usize {
return Err(Error::AdTooShort);
} else if ad_len > common::MAX_AD_LENGTH as usize {
return Err(Error::AdTooLong);
}
if config.hash_length < common::MIN_HASH_LENGTH {
return Err(Error::OutputTooShort);
} else if config.hash_length > common::MAX_HASH_LENGTH {
return Err(Error::OutputTooLong);
}
let mut memory_blocks = config.mem_cost;
if memory_blocks < 2 * common::SYNC_POINTS * lanes {
memory_blocks = 2 * common::SYNC_POINTS * lanes;
}
let segment_length = memory_blocks / (lanes * common::SYNC_POINTS);
let memory_blocks = segment_length * (lanes * common::SYNC_POINTS);
let lane_length = segment_length * common::SYNC_POINTS;
Ok(Context {
config,
lane_length,
memory_blocks,
pwd,
salt,
segment_length,
})
}
}
#[cfg(test)]
mod tests {
use crate::config::Config;
use crate::context::Context;
use crate::error::Error;
use crate::thread_mode::ThreadMode;
use crate::variant::Variant;
use crate::version::Version;
#[test]
fn new_returns_correct_instance() {
let config = Config {
ad: b"additionaldata",
hash_length: 32,
lanes: 4,
mem_cost: 4096,
secret: b"secret",
thread_mode: ThreadMode::Sequential,
time_cost: 3,
variant: Variant::Argon2i,
version: Version::Version13,
};
let pwd = b"password";
let salt = b"somesalt";
let result = Context::new(config.clone(), pwd, salt);
assert!(result.is_ok());
let context = result.unwrap();
assert_eq!(context.config, config);
assert_eq!(context.pwd, pwd);
assert_eq!(context.salt, salt);
assert_eq!(context.memory_blocks, 4096);
assert_eq!(context.segment_length, 256);
assert_eq!(context.lane_length, 1024);
}
#[test]
fn new_with_too_little_mem_cost_returns_correct_error() {
let config = Config {
mem_cost: 7,
..Default::default()
};
assert_eq!(
Context::new(config, &[0u8; 8], &[0u8; 8]),
Err(Error::MemoryTooLittle)
);
}
#[test]
fn new_with_less_than_8_x_lanes_mem_cost_returns_correct_error() {
let config = Config {
lanes: 4,
mem_cost: 31,
..Default::default()
};
assert_eq!(
Context::new(config, &[0u8; 8], &[0u8; 8]),
Err(Error::MemoryTooLittle)
);
}
#[test]
fn new_with_too_small_time_cost_returns_correct_error() {
let config = Config {
time_cost: 0,
..Default::default()
};
assert_eq!(
Context::new(config, &[0u8; 8], &[0u8; 8]),
Err(Error::TimeTooSmall)
);
}
#[test]
fn new_with_too_few_lanes_returns_correct_error() {
let config = Config {
lanes: 0,
..Default::default()
};
assert_eq!(
Context::new(config, &[0u8; 8], &[0u8; 8]),
Err(Error::LanesTooFew)
);
}
#[test]
fn new_with_too_many_lanes_returns_correct_error() {
let config = Config {
lanes: 1 << 24,
..Default::default()
};
assert_eq!(
Context::new(config, &[0u8; 8], &[0u8; 8]),
Err(Error::LanesTooMany)
);
}
#[test]
fn new_with_too_short_salt_returns_correct_error() {
let config = Default::default();
let salt = [0u8; 7];
assert_eq!(
Context::new(config, &[0u8; 8], &salt),
Err(Error::SaltTooShort)
);
}
#[test]
fn new_with_too_short_hash_length_returns_correct_error() {
let config = Config {
hash_length: 3,
..Default::default()
};
assert_eq!(
Context::new(config, &[0u8; 8], &[0u8; 8]),
Err(Error::OutputTooShort)
);
}
}

View File

@ -1,467 +0,0 @@
// Copyright (c) 2017 Martijn Rijkeboer <mrr@sru-systems.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::block::Block;
use crate::common;
use crate::context::Context;
use crate::memory::Memory;
use crate::variant::Variant;
use crate::version::Version;
use blake2b_simd::Params;
#[cfg(feature = "crossbeam-utils")]
use crossbeam_utils::thread::scope;
/// Position of the block currently being operated on.
#[derive(Clone, Debug)]
struct Position {
pass: u32,
lane: u32,
slice: u32,
index: u32,
}
/// Initializes the memory.
pub fn initialize(context: &Context, memory: &mut Memory) {
fill_first_blocks(context, memory, &mut h0(context));
}
/// Fills all the memory blocks.
pub fn fill_memory_blocks(context: &Context, memory: &mut Memory) {
if context.config.uses_sequential() {
fill_memory_blocks_st(context, memory);
} else {
fill_memory_blocks_mt(context, memory);
}
}
/// Calculates the final hash and returns it.
pub fn finalize(context: &Context, memory: &Memory) -> Vec<u8> {
let mut blockhash = memory[context.lane_length - 1].clone();
for l in 1..context.config.lanes {
let last_block_in_lane = l * context.lane_length + (context.lane_length - 1);
blockhash ^= &memory[last_block_in_lane];
}
let mut hash = vec![0u8; context.config.hash_length as usize];
hprime(hash.as_mut_slice(), blockhash.as_u8());
hash
}
fn blake2b(out: &mut [u8], input: &[&[u8]]) {
let mut blake = Params::new().hash_length(out.len()).to_state();
for slice in input {
blake.update(slice);
}
out.copy_from_slice(blake.finalize().as_bytes());
}
fn f_bla_mka(x: u64, y: u64) -> u64 {
let m = 0xFFFF_FFFFu64;
let xy = (x & m) * (y & m);
x.wrapping_add(y.wrapping_add(xy.wrapping_add(xy)))
}
fn fill_block(prev_block: &Block, ref_block: &Block, next_block: &mut Block, with_xor: bool) {
let mut block_r = ref_block.clone();
block_r ^= prev_block;
let mut block_tmp = block_r.clone();
// Now block_r = ref_block + prev_block and block_tmp = ref_block + prev_block
if with_xor {
// Saving the next block contents for XOR over
block_tmp ^= next_block;
// Now block_r = ref_block + prev_block and
// block_tmp = ref_block + prev_block + next_block
}
// Apply Blake2 on columns of 64-bit words: (0,1,...,15) , then
// (16,17,..31)... finally (112,113,...127)
for i in 0..8 {
let mut v0 = block_r[16 * i];
let mut v1 = block_r[16 * i + 1];
let mut v2 = block_r[16 * i + 2];
let mut v3 = block_r[16 * i + 3];
let mut v4 = block_r[16 * i + 4];
let mut v5 = block_r[16 * i + 5];
let mut v6 = block_r[16 * i + 6];
let mut v7 = block_r[16 * i + 7];
let mut v8 = block_r[16 * i + 8];
let mut v9 = block_r[16 * i + 9];
let mut v10 = block_r[16 * i + 10];
let mut v11 = block_r[16 * i + 11];
let mut v12 = block_r[16 * i + 12];
let mut v13 = block_r[16 * i + 13];
let mut v14 = block_r[16 * i + 14];
let mut v15 = block_r[16 * i + 15];
p(
&mut v0, &mut v1, &mut v2, &mut v3, &mut v4, &mut v5, &mut v6, &mut v7, &mut v8,
&mut v9, &mut v10, &mut v11, &mut v12, &mut v13, &mut v14, &mut v15,
);
block_r[16 * i] = v0;
block_r[16 * i + 1] = v1;
block_r[16 * i + 2] = v2;
block_r[16 * i + 3] = v3;
block_r[16 * i + 4] = v4;
block_r[16 * i + 5] = v5;
block_r[16 * i + 6] = v6;
block_r[16 * i + 7] = v7;
block_r[16 * i + 8] = v8;
block_r[16 * i + 9] = v9;
block_r[16 * i + 10] = v10;
block_r[16 * i + 11] = v11;
block_r[16 * i + 12] = v12;
block_r[16 * i + 13] = v13;
block_r[16 * i + 14] = v14;
block_r[16 * i + 15] = v15;
}
// Apply Blake2 on rows of 64-bit words: (0,1,16,17,...112,113), then
// (2,3,18,19,...,114,115).. finally (14,15,30,31,...,126,127)
for i in 0..8 {
let mut v0 = block_r[2 * i];
let mut v1 = block_r[2 * i + 1];
let mut v2 = block_r[2 * i + 16];
let mut v3 = block_r[2 * i + 17];
let mut v4 = block_r[2 * i + 32];
let mut v5 = block_r[2 * i + 33];
let mut v6 = block_r[2 * i + 48];
let mut v7 = block_r[2 * i + 49];
let mut v8 = block_r[2 * i + 64];
let mut v9 = block_r[2 * i + 65];
let mut v10 = block_r[2 * i + 80];
let mut v11 = block_r[2 * i + 81];
let mut v12 = block_r[2 * i + 96];
let mut v13 = block_r[2 * i + 97];
let mut v14 = block_r[2 * i + 112];
let mut v15 = block_r[2 * i + 113];
p(
&mut v0, &mut v1, &mut v2, &mut v3, &mut v4, &mut v5, &mut v6, &mut v7, &mut v8,
&mut v9, &mut v10, &mut v11, &mut v12, &mut v13, &mut v14, &mut v15,
);
block_r[2 * i] = v0;
block_r[2 * i + 1] = v1;
block_r[2 * i + 16] = v2;
block_r[2 * i + 17] = v3;
block_r[2 * i + 32] = v4;
block_r[2 * i + 33] = v5;
block_r[2 * i + 48] = v6;
block_r[2 * i + 49] = v7;
block_r[2 * i + 64] = v8;
block_r[2 * i + 65] = v9;
block_r[2 * i + 80] = v10;
block_r[2 * i + 81] = v11;
block_r[2 * i + 96] = v12;
block_r[2 * i + 97] = v13;
block_r[2 * i + 112] = v14;
block_r[2 * i + 113] = v15;
}
block_tmp.copy_to(next_block);
*next_block ^= &block_r;
}
fn fill_first_blocks(context: &Context, memory: &mut Memory, h0: &mut [u8]) {
for lane in 0..context.config.lanes {
let start = common::PREHASH_DIGEST_LENGTH;
// H'(H0||0||i)
h0[start..(start + 4)].clone_from_slice(&u32::to_le_bytes(0));
h0[(start + 4)..(start + 8)].clone_from_slice(&u32::to_le_bytes(lane));
hprime(memory[(lane, 0)].as_u8_mut(), &h0);
// H'(H0||1||i)
h0[start..(start + 4)].clone_from_slice(&u32::to_le_bytes(1));
hprime(memory[(lane, 1)].as_u8_mut(), &h0);
}
}
#[cfg(feature = "crossbeam-utils")]
fn fill_memory_blocks_mt(context: &Context, memory: &mut Memory) {
for p in 0..context.config.time_cost {
for s in 0..common::SYNC_POINTS {
let _ = scope(|scoped| {
for (l, mem) in (0..context.config.lanes).zip(memory.as_lanes_mut()) {
let position = Position {
pass: p,
lane: l,
slice: s,
index: 0,
};
scoped.spawn(move |_| {
fill_segment(context, &position, mem);
});
}
});
}
}
}
#[cfg(not(feature = "crossbeam-utils"))]
fn fill_memory_blocks_mt(_: &Context, _: &mut Memory) {
unimplemented!()
}
fn fill_memory_blocks_st(context: &Context, memory: &mut Memory) {
for p in 0..context.config.time_cost {
for s in 0..common::SYNC_POINTS {
for l in 0..context.config.lanes {
let position = Position {
pass: p,
lane: l,
slice: s,
index: 0,
};
fill_segment(context, &position, memory);
}
}
}
}
fn fill_segment(context: &Context, position: &Position, memory: &mut Memory) {
let mut position = position.clone();
let data_independent_addressing = (context.config.variant == Variant::Argon2i)
|| (context.config.variant == Variant::Argon2id && position.pass == 0)
&& (position.slice < (common::SYNC_POINTS / 2));
let zero_block = Block::zero();
let mut input_block = Block::zero();
let mut address_block = Block::zero();
if data_independent_addressing {
input_block[0] = position.pass as u64;
input_block[1] = position.lane as u64;
input_block[2] = position.slice as u64;
input_block[3] = context.memory_blocks as u64;
input_block[4] = context.config.time_cost as u64;
input_block[5] = context.config.variant.as_u64();
}
let mut starting_index = 0u32;
if position.pass == 0 && position.slice == 0 {
starting_index = 2;
// Don't forget to generate the first block of addresses:
if data_independent_addressing {
next_addresses(&mut address_block, &mut input_block, &zero_block);
}
}
let mut curr_offset = (position.lane * context.lane_length)
+ (position.slice * context.segment_length)
+ starting_index;
let mut prev_offset = if curr_offset % context.lane_length == 0 {
// Last block in this lane
curr_offset + context.lane_length - 1
} else {
curr_offset - 1
};
let mut pseudo_rand;
for i in starting_index..context.segment_length {
// 1.1 Rotating prev_offset if needed
if curr_offset % context.lane_length == 1 {
prev_offset = curr_offset - 1;
}
// 1.2 Computing the index of the reference block
// 1.2.1 Taking pseudo-random value from the previous block
if data_independent_addressing {
if i % common::ADDRESSES_IN_BLOCK == 0 {
next_addresses(&mut address_block, &mut input_block, &zero_block);
}
pseudo_rand = address_block[(i % common::ADDRESSES_IN_BLOCK) as usize];
} else {
pseudo_rand = memory[(prev_offset)][0];
}
// 1.2.2 Computing the lane of the reference block
// If (position.pass == 0) && (position.slice == 0): can not reference other lanes yet
let ref_lane = if (position.pass == 0) && (position.slice == 0) {
position.lane as u64
} else {
(pseudo_rand >> 32) % context.config.lanes as u64
};
// 1.2.3 Computing the number of possible reference block within the lane.
position.index = i;
let pseudo_rand_u32 = (pseudo_rand & 0xFFFF_FFFF) as u32;
let same_lane = ref_lane == (position.lane as u64);
let ref_index = index_alpha(context, &position, pseudo_rand_u32, same_lane);
// 2 Creating a new block
let index = context.lane_length as u64 * ref_lane + ref_index as u64;
let mut curr_block = memory[curr_offset].clone();
{
let prev_block = &memory[prev_offset];
let ref_block = &memory[index];
if context.config.version == Version::Version10 || position.pass == 0 {
fill_block(prev_block, ref_block, &mut curr_block, false);
} else {
fill_block(prev_block, ref_block, &mut curr_block, true);
}
}
memory[curr_offset] = curr_block;
curr_offset += 1;
prev_offset += 1;
}
}
fn g(a: &mut u64, b: &mut u64, c: &mut u64, d: &mut u64) {
*a = f_bla_mka(*a, *b);
*d = rotr64(*d ^ *a, 32);
*c = f_bla_mka(*c, *d);
*b = rotr64(*b ^ *c, 24);
*a = f_bla_mka(*a, *b);
*d = rotr64(*d ^ *a, 16);
*c = f_bla_mka(*c, *d);
*b = rotr64(*b ^ *c, 63);
}
fn h0(context: &Context) -> [u8; common::PREHASH_SEED_LENGTH] {
let input = [
&u32::to_le_bytes(context.config.lanes),
&u32::to_le_bytes(context.config.hash_length),
&u32::to_le_bytes(context.config.mem_cost),
&u32::to_le_bytes(context.config.time_cost),
&u32::to_le_bytes(context.config.version.as_u32()),
&u32::to_le_bytes(context.config.variant.as_u32()),
&len_as_32le(context.pwd),
context.pwd,
&len_as_32le(context.salt),
context.salt,
&len_as_32le(context.config.secret),
context.config.secret,
&len_as_32le(context.config.ad),
context.config.ad,
];
let mut out = [0u8; common::PREHASH_SEED_LENGTH];
blake2b(&mut out[0..common::PREHASH_DIGEST_LENGTH], &input);
out
}
fn hprime(out: &mut [u8], input: &[u8]) {
let out_len = out.len();
if out_len <= common::BLAKE2B_OUT_LENGTH {
blake2b(out, &[&u32::to_le_bytes(out_len as u32), input]);
} else {
let ai_len = 32;
let mut out_buffer = [0u8; common::BLAKE2B_OUT_LENGTH];
let mut in_buffer = [0u8; common::BLAKE2B_OUT_LENGTH];
blake2b(&mut out_buffer, &[&u32::to_le_bytes(out_len as u32), input]);
out[0..ai_len].clone_from_slice(&out_buffer[0..ai_len]);
let mut out_pos = ai_len;
let mut to_produce = out_len - ai_len;
while to_produce > common::BLAKE2B_OUT_LENGTH {
in_buffer.clone_from_slice(&out_buffer);
blake2b(&mut out_buffer, &[&in_buffer]);
out[out_pos..out_pos + ai_len].clone_from_slice(&out_buffer[0..ai_len]);
out_pos += ai_len;
to_produce -= ai_len;
}
blake2b(&mut out[out_pos..out_len], &[&out_buffer]);
}
}
fn index_alpha(context: &Context, position: &Position, pseudo_rand: u32, same_lane: bool) -> u32 {
// Pass 0:
// - This lane: all already finished segments plus already constructed blocks in this segment
// - Other lanes: all already finished segments
// Pass 1+:
// - This lane: (SYNC_POINTS - 1) last segments plus already constructed blocks in this segment
// - Other lanes : (SYNC_POINTS - 1) last segments
let reference_area_size: u32 = if position.pass == 0 {
// First pass
if position.slice == 0 {
// First slice
position.index - 1
} else if same_lane {
// The same lane => add current segment
position.slice * context.segment_length + position.index - 1
} else if position.index == 0 {
position.slice * context.segment_length - 1
} else {
position.slice * context.segment_length
}
} else {
// Second pass
if same_lane {
context.lane_length - context.segment_length + position.index - 1
} else if position.index == 0 {
context.lane_length - context.segment_length - 1
} else {
context.lane_length - context.segment_length
}
};
let reference_area_size = reference_area_size as u64;
let mut relative_position = pseudo_rand as u64;
relative_position = (relative_position * relative_position) >> 32;
relative_position = reference_area_size - 1 - ((reference_area_size * relative_position) >> 32);
// 1.2.5 Computing starting position
let start_position: u32 = if position.pass != 0 {
if position.slice == common::SYNC_POINTS - 1 {
0u32
} else {
(position.slice + 1) * context.segment_length
}
} else {
0u32
};
let start_position = start_position as u64;
// 1.2.6. Computing absolute position
((start_position + relative_position) % context.lane_length as u64) as u32
}
fn len_as_32le(slice: &[u8]) -> [u8; 4] {
u32::to_le_bytes(slice.len() as u32)
}
fn next_addresses(address_block: &mut Block, input_block: &mut Block, zero_block: &Block) {
input_block[6] += 1;
fill_block(zero_block, input_block, address_block, false);
fill_block(zero_block, &address_block.clone(), address_block, false);
}
fn p(
v0: &mut u64,
v1: &mut u64,
v2: &mut u64,
v3: &mut u64,
v4: &mut u64,
v5: &mut u64,
v6: &mut u64,
v7: &mut u64,
v8: &mut u64,
v9: &mut u64,
v10: &mut u64,
v11: &mut u64,
v12: &mut u64,
v13: &mut u64,
v14: &mut u64,
v15: &mut u64,
) {
g(v0, v4, v8, v12);
g(v1, v5, v9, v13);
g(v2, v6, v10, v14);
g(v3, v7, v11, v15);
g(v0, v5, v10, v15);
g(v1, v6, v11, v12);
g(v2, v7, v8, v13);
g(v3, v4, v9, v14);
}
fn rotr64(w: u64, c: u32) -> u64 {
(w >> c) | (w << (64 - c))
}

View File

@ -1,35 +0,0 @@
// Copyright (c) 2017 Martijn Rijkeboer <mrr@sru-systems.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::variant::Variant;
use crate::version::Version;
/// Structure that contains the decoded data.
#[derive(Debug, Eq, PartialEq)]
pub struct Decoded {
/// The variant.
pub variant: Variant,
/// The version.
pub version: Version,
/// The amount of memory requested (KiB).
pub mem_cost: u32,
/// The number of passes.
pub time_cost: u32,
/// The parallelism.
pub parallelism: u32,
/// The salt.
pub salt: Vec<u8>,
/// The hash.
pub hash: Vec<u8>,
}

View File

@ -1,405 +0,0 @@
// Copyright (c) 2017 Martijn Rijkeboer <mrr@sru-systems.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::context::Context;
use crate::decoded::Decoded;
use crate::error::Error;
use crate::result::Result;
use crate::variant::Variant;
use crate::version::Version;
use base64;
/// Structure containing the options.
struct Options {
mem_cost: u32,
time_cost: u32,
parallelism: u32,
}
/// Gets the base64 encoded length of a byte slice with the specified length.
pub fn base64_len(length: u32) -> u32 {
let olen = (length / 3) << 2;
match length % 3 {
2 => olen + 3,
1 => olen + 2,
_ => olen,
}
}
/// Attempts to decode the encoded string slice.
pub fn decode_string(encoded: &str) -> Result<Decoded> {
let items: Vec<&str> = encoded.split('$').collect();
if items.len() == 6 {
decode_empty(items[0])?;
let variant = decode_variant(items[1])?;
let version = decode_version(items[2])?;
let options = decode_options(items[3])?;
let salt = base64::decode(items[4])?;
let hash = base64::decode(items[5])?;
Ok(Decoded {
variant,
version,
mem_cost: options.mem_cost,
time_cost: options.time_cost,
parallelism: options.parallelism,
salt,
hash,
})
} else if items.len() == 5 {
decode_empty(items[0])?;
let variant = decode_variant(items[1])?;
let options = decode_options(items[2])?;
let salt = base64::decode(items[3])?;
let hash = base64::decode(items[4])?;
Ok(Decoded {
variant,
version: Version::Version10,
mem_cost: options.mem_cost,
time_cost: options.time_cost,
parallelism: options.parallelism,
salt,
hash,
})
} else {
Err(Error::DecodingFail)
}
}
fn decode_empty(str: &str) -> Result<()> {
if str == "" {
Ok(())
} else {
Err(Error::DecodingFail)
}
}
fn decode_options(str: &str) -> Result<Options> {
let items: Vec<&str> = str.split(',').collect();
if items.len() == 3 {
Ok(Options {
mem_cost: decode_option(items[0], "m")?,
time_cost: decode_option(items[1], "t")?,
parallelism: decode_option(items[2], "p")?,
})
} else {
Err(Error::DecodingFail)
}
}
fn decode_option(str: &str, name: &str) -> Result<u32> {
let items: Vec<&str> = str.split('=').collect();
if items.len() == 2 {
if items[0] == name {
decode_u32(items[1])
} else {
Err(Error::DecodingFail)
}
} else {
Err(Error::DecodingFail)
}
}
fn decode_u32(str: &str) -> Result<u32> {
match str.parse() {
Ok(i) => Ok(i),
Err(_) => Err(Error::DecodingFail),
}
}
fn decode_variant(str: &str) -> Result<Variant> {
Variant::from_str(str)
}
fn decode_version(str: &str) -> Result<Version> {
let items: Vec<&str> = str.split('=').collect();
if items.len() == 2 {
if items[0] == "v" {
Version::from_str(items[1])
} else {
Err(Error::DecodingFail)
}
} else {
Err(Error::DecodingFail)
}
}
/// Encodes the hash and context.
pub fn encode_string(context: &Context, hash: &[u8]) -> String {
format!(
"${}$v={}$m={},t={},p={}${}${}",
context.config.variant,
context.config.version,
context.config.mem_cost,
context.config.time_cost,
context.config.lanes,
base64::encode_config(context.salt, base64::STANDARD_NO_PAD),
base64::encode_config(hash, base64::STANDARD_NO_PAD),
)
}
/// Gets the string length of the specified number.
pub fn num_len(number: u32) -> u32 {
let mut len = 1;
let mut num = number;
while num >= 10 {
len += 1;
num /= 10;
}
len
}
#[cfg(test)]
mod tests {
#[cfg(feature = "crossbeam-utils")]
use crate::config::Config;
#[cfg(feature = "crossbeam-utils")]
use crate::context::Context;
use crate::decoded::Decoded;
#[cfg(feature = "crossbeam-utils")]
use crate::encoding::encode_string;
use crate::encoding::{base64_len, decode_string, num_len};
use crate::error::Error;
#[cfg(feature = "crossbeam-utils")]
use crate::thread_mode::ThreadMode;
use crate::variant::Variant;
use crate::version::Version;
#[test]
fn base64_len_returns_correct_length() {
let tests = vec![
(1, 2),
(2, 3),
(3, 4),
(4, 6),
(5, 7),
(6, 8),
(7, 10),
(8, 11),
(9, 12),
(10, 14),
];
for (len, expected) in tests {
let actual = base64_len(len);
assert_eq!(actual, expected);
}
}
#[test]
fn decode_string_with_version10_returns_correct_result() {
let encoded = "$argon2i$v=16$m=4096,t=3,p=1\
$c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
let expected = Decoded {
variant: Variant::Argon2i,
version: Version::Version10,
mem_cost: 4096,
time_cost: 3,
parallelism: 1,
salt: b"salt1234".to_vec(),
hash: b"12345678901234567890123456789012".to_vec(),
};
let actual = decode_string(encoded).unwrap();
assert_eq!(actual, expected);
}
#[test]
fn decode_string_with_version13_returns_correct_result() {
let encoded = "$argon2i$v=19$m=4096,t=3,p=1\
$c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
let expected = Decoded {
variant: Variant::Argon2i,
version: Version::Version13,
mem_cost: 4096,
time_cost: 3,
parallelism: 1,
salt: b"salt1234".to_vec(),
hash: b"12345678901234567890123456789012".to_vec(),
};
let actual = decode_string(encoded).unwrap();
assert_eq!(actual, expected);
}
#[test]
fn decode_string_without_version_returns_correct_result() {
let encoded = "$argon2i$m=4096,t=3,p=1\
$c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
let expected = Decoded {
variant: Variant::Argon2i,
version: Version::Version10,
mem_cost: 4096,
time_cost: 3,
parallelism: 1,
salt: b"salt1234".to_vec(),
hash: b"12345678901234567890123456789012".to_vec(),
};
let actual = decode_string(encoded).unwrap();
assert_eq!(actual, expected);
}
#[test]
fn decode_string_without_variant_returns_error_result() {
let encoded = "$m=4096,t=3,p=1\
$c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
let result = decode_string(encoded);
assert_eq!(result, Err(Error::DecodingFail));
}
#[test]
fn decode_string_with_empty_variant_returns_error_result() {
let encoded = "$$m=4096,t=3,p=1\
$c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
let result = decode_string(encoded);
assert_eq!(result, Err(Error::DecodingFail));
}
#[test]
fn decode_string_with_invalid_variant_returns_error_result() {
let encoded = "$argon$m=4096,t=3,p=1\
$c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
let result = decode_string(encoded);
assert_eq!(result, Err(Error::DecodingFail));
}
#[test]
fn decode_string_without_mem_cost_returns_error_result() {
let encoded = "$argon2i$t=3,p=1\
$c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
let result = decode_string(encoded);
assert_eq!(result, Err(Error::DecodingFail));
}
#[test]
fn decode_string_with_empty_mem_cost_returns_error_result() {
let encoded = "$argon2i$m=,t=3,p=1\
$c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
let result = decode_string(encoded);
assert_eq!(result, Err(Error::DecodingFail));
}
#[test]
fn decode_string_with_non_numeric_mem_cost_returns_error_result() {
let encoded = "$argon2i$m=a,t=3,p=1\
$c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
let result = decode_string(encoded);
assert_eq!(result, Err(Error::DecodingFail));
}
#[test]
fn decode_string_without_time_cost_returns_error_result() {
let encoded = "$argon2i$m=4096,p=1\
$c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
let result = decode_string(encoded);
assert_eq!(result, Err(Error::DecodingFail));
}
#[test]
fn decode_string_with_empty_time_cost_returns_error_result() {
let encoded = "$argon2i$m=4096,t=,p=1\
$c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
let result = decode_string(encoded);
assert_eq!(result, Err(Error::DecodingFail));
}
#[test]
fn decode_string_with_non_numeric_time_cost_returns_error_result() {
let encoded = "$argon2i$m=4096,t=a,p=1\
$c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
let result = decode_string(encoded);
assert_eq!(result, Err(Error::DecodingFail));
}
#[test]
fn decode_string_without_parallelism_returns_error_result() {
let encoded = "$argon2i$m=4096,t=3\
$c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
let result = decode_string(encoded);
assert_eq!(result, Err(Error::DecodingFail));
}
#[test]
fn decode_string_with_empty_parallelism_returns_error_result() {
let encoded = "$argon2i$m=4096,t=3,p=\
$c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
let result = decode_string(encoded);
assert_eq!(result, Err(Error::DecodingFail));
}
#[test]
fn decode_string_with_non_numeric_parallelism_returns_error_result() {
let encoded = "$argon2i$m=4096,t=3,p=a\
$c2FsdDEyMzQ=$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
let result = decode_string(encoded);
assert_eq!(result, Err(Error::DecodingFail));
}
#[test]
fn decode_string_without_salt_returns_error_result() {
let encoded = "$argon2i$m=4096,t=3,p=1\
$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=";
let result = decode_string(encoded);
assert_eq!(result, Err(Error::DecodingFail));
}
#[test]
fn decode_string_without_hash_returns_error_result() {
let encoded = "$argon2i$m=4096,t=3,p=a\
$c2FsdDEyMzQ=";
let result = decode_string(encoded);
assert_eq!(result, Err(Error::DecodingFail));
}
#[test]
fn decode_string_with_empty_hash_returns_error_result() {
let encoded = "$argon2i$m=4096,t=3,p=a\
$c2FsdDEyMzQ=$";
let result = decode_string(encoded);
assert_eq!(result, Err(Error::DecodingFail));
}
#[cfg(feature = "crossbeam-utils")]
#[test]
fn encode_string_returns_correct_string() {
let hash = b"12345678901234567890123456789012".to_vec();
let config = Config {
ad: &[],
hash_length: hash.len() as u32,
lanes: 1,
mem_cost: 4096,
secret: &[],
thread_mode: ThreadMode::Parallel,
time_cost: 3,
variant: Variant::Argon2i,
version: Version::Version13,
};
let pwd = b"password".to_vec();
let salt = b"salt1234".to_vec();
let context = Context::new(config, &pwd, &salt).unwrap();
let expected = "$argon2i$v=19$m=4096,t=3,p=1\
$c2FsdDEyMzQ$MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI";
let actual = encode_string(&context, &hash);
assert_eq!(actual, expected);
}
#[test]
fn num_len_returns_correct_length() {
let tests = vec![
(1, 1),
(10, 2),
(110, 3),
(1230, 4),
(12340, 5),
(123457, 6),
];
for (num, expected) in tests {
let actual = num_len(num);
assert_eq!(actual, expected);
}
}
}

View File

@ -1,123 +0,0 @@
// Copyright (c) 2017 Martijn Rijkeboer <mrr@sru-systems.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::{error, fmt};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// Error type for Argon2 errors.
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum Error {
/// The output (hash) is too short (minimum is 4).
OutputTooShort,
/// The output (hash) is too long (maximum is 2^32 - 1).
OutputTooLong,
/// The password is too short (minimum is 0).
PwdTooShort,
/// The password is too long (maximum is 2^32 - 1).
PwdTooLong,
/// The salt is too short (minimum is 8).
SaltTooShort,
/// The salt is too long (maximum is 2^32 - 1).
SaltTooLong,
/// The associated data is too short (minimum is 0).
AdTooShort,
/// The associated data is too long (maximum is 2^32 - 1).
AdTooLong,
/// The secret value is too short (minimum is 0).
SecretTooShort,
/// The secret value is too long (maximum is 2^32 - 1).
SecretTooLong,
/// The time cost (passes) is too small (minimum is 1).
TimeTooSmall,
/// The time cost (passes) is too large (maximum is 2^32 - 1).
TimeTooLarge,
/// The memory cost is too small (minimum is 8 x parallelism).
MemoryTooLittle,
/// The memory cost is too large (maximum 2GiB on 32-bit or 4TiB on 64-bit).
MemoryTooMuch,
/// The number of lanes (parallelism) is too small (minimum is 1).
LanesTooFew,
/// The number of lanes (parallelism) is too large (maximum is 2^24 - 1).
LanesTooMany,
/// Incorrect Argon2 variant.
IncorrectType,
/// Incorrect Argon2 version.
IncorrectVersion,
/// The decoding of the encoded data has failed.
DecodingFail,
}
impl Error {
fn msg(&self) -> &str {
match *self {
Error::OutputTooShort => "Output is too short",
Error::OutputTooLong => "Output is too long",
Error::PwdTooShort => "Password is too short",
Error::PwdTooLong => "Password is too long",
Error::SaltTooShort => "Salt is too short",
Error::SaltTooLong => "Salt is too long",
Error::AdTooShort => "Associated data is too short",
Error::AdTooLong => "Associated data is too long",
Error::SecretTooShort => "Secret is too short",
Error::SecretTooLong => "Secret is too long",
Error::TimeTooSmall => "Time cost is too small",
Error::TimeTooLarge => "Time cost is too large",
Error::MemoryTooLittle => "Memory cost is too small",
Error::MemoryTooMuch => "Memory cost is too large",
Error::LanesTooFew => "Too few lanes",
Error::LanesTooMany => "Too many lanes",
Error::IncorrectType => "There is no such type of Argon2",
Error::IncorrectVersion => "There is no such version of Argon2",
Error::DecodingFail => "Decoding failed",
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.msg())
}
}
impl error::Error for Error {
fn description(&self) -> &str {
self.msg()
}
fn cause(&self) -> Option<&dyn error::Error> {
None
}
}
impl From<base64::DecodeError> for Error {
fn from(_: base64::DecodeError) -> Self {
Error::DecodingFail
}
}

View File

@ -1,107 +0,0 @@
// Copyright (c) 2017 Martijn Rijkeboer <mrr@sru-systems.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Library for hashing passwords using
//! [Argon2](https://github.com/P-H-C/phc-winner-argon2), the password-hashing
//! function that won the
//! [Password Hashing Competition (PHC)](https://password-hashing.net).
//!
//! # Usage
//!
//! To use this crate, add the following to your Cargo.toml:
//!
//! ```toml
//! [dependencies]
//! rust-argon2 = "0.8"
//! ```
//!
//! And the following to your crate root:
//!
//! ```rust
//! extern crate argon2;
//! ```
//!
//! # Examples
//!
//! Create a password hash using the defaults and verify it:
//!
//! ```rust
//! use argon2::{self, Config};
//!
//! let password = b"password";
//! let salt = b"randomsalt";
//! let config = Config::default();
//! let hash = argon2::hash_encoded(password, salt, &config).unwrap();
//! let matches = argon2::verify_encoded(&hash, password).unwrap();
//! assert!(matches);
//! ```
//!
//! Create a password hash with custom settings and verify it:
//!
//! ```rust
//! use argon2::{self, Config, ThreadMode, Variant, Version};
//!
//! let password = b"password";
//! let salt = b"othersalt";
//! let config = Config {
//! variant: Variant::Argon2i,
//! version: Version::Version13,
//! mem_cost: 65536,
//! time_cost: 10,
#![cfg_attr(feature = "crossbeam-utils", doc = " lanes: 4,")]
#![cfg_attr(
feature = "crossbeam-utils",
doc = " thread_mode: ThreadMode::Parallel,"
)]
#![cfg_attr(not(feature = "crossbeam-utils"), doc = " lanes: 1,")]
#![cfg_attr(
not(feature = "crossbeam-utils"),
doc = " thread_mode: ThreadMode::Sequential,"
)]
//! secret: &[],
//! ad: &[],
//! hash_length: 32
//! };
//! let hash = argon2::hash_encoded(password, salt, &config).unwrap();
//! let matches = argon2::verify_encoded(&hash, password).unwrap();
//! assert!(matches);
//! ```
//!
//! # Limitations
//!
//! This crate has the same limitation as the `blake2-rfc` crate that it uses.
//! It does not attempt to clear potentially sensitive data from its work
//! memory. To do so correctly without a heavy performance penalty would
//! require help from the compiler. It's better to not attempt to do so than to
//! present a false assurance.
//!
//! This version uses the standard implementation and does not yet implement
//! optimizations. Therefore, it is not the fastest implementation available.
mod argon2;
mod block;
mod common;
mod config;
mod context;
mod core;
mod decoded;
mod encoding;
mod error;
mod memory;
mod result;
mod thread_mode;
mod variant;
mod version;
pub use crate::argon2::*;
pub use crate::config::Config;
pub use crate::error::Error;
pub use crate::result::Result;
pub use crate::thread_mode::ThreadMode;
pub use crate::variant::Variant;
pub use crate::version::Version;

View File

@ -1,117 +0,0 @@
// Copyright (c) 2017 Martijn Rijkeboer <mrr@sru-systems.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::block::Block;
use std::fmt;
use std::fmt::Debug;
use std::ops::{Index, IndexMut};
/// Structure representing the memory matrix.
pub struct Memory {
/// The number of rows.
rows: usize,
/// The number of columns.
cols: usize,
/// The flat array of blocks representing the memory matrix.
blocks: Box<[Block]>,
}
impl Memory {
/// Creates a new memory matrix.
pub fn new(lanes: u32, lane_length: u32) -> Memory {
let rows = lanes as usize;
let cols = lane_length as usize;
let total = rows * cols;
let blocks = vec![Block::zero(); total].into_boxed_slice();
Memory { rows, cols, blocks }
}
#[cfg(feature = "crossbeam-utils")]
/// Gets the mutable lanes representation of the memory matrix.
pub fn as_lanes_mut(&mut self) -> Vec<&mut Memory> {
let ptr: *mut Memory = self;
let mut vec = Vec::with_capacity(self.rows);
for _ in 0..self.rows {
vec.push(unsafe { &mut (*ptr) });
}
vec
}
}
impl Debug for Memory {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Memory {{ rows: {}, cols: {} }}", self.rows, self.cols)
}
}
impl Index<u32> for Memory {
type Output = Block;
fn index(&self, index: u32) -> &Block {
&self.blocks[index as usize]
}
}
impl Index<u64> for Memory {
type Output = Block;
fn index(&self, index: u64) -> &Block {
&self.blocks[index as usize]
}
}
impl Index<(u32, u32)> for Memory {
type Output = Block;
fn index(&self, index: (u32, u32)) -> &Block {
let pos = ((index.0 as usize) * self.cols) + (index.1 as usize);
&self.blocks[pos]
}
}
impl IndexMut<u32> for Memory {
fn index_mut(&mut self, index: u32) -> &mut Block {
&mut self.blocks[index as usize]
}
}
impl IndexMut<u64> for Memory {
fn index_mut(&mut self, index: u64) -> &mut Block {
&mut self.blocks[index as usize]
}
}
impl IndexMut<(u32, u32)> for Memory {
fn index_mut(&mut self, index: (u32, u32)) -> &mut Block {
let pos = ((index.0 as usize) * self.cols) + (index.1 as usize);
&mut self.blocks[pos]
}
}
#[cfg(test)]
mod tests {
use crate::memory::Memory;
#[test]
fn new_returns_correct_instance() {
let lanes = 4;
let lane_length = 128;
let memory = Memory::new(lanes, lane_length);
assert_eq!(memory.rows, lanes as usize);
assert_eq!(memory.cols, lane_length as usize);
assert_eq!(memory.blocks.len(), 512);
}
#[cfg(feature = "crossbeam-utils")]
#[test]
fn as_lanes_mut_returns_correct_vec() {
let mut memory = Memory::new(4, 128);
let lanes = memory.as_lanes_mut();
assert_eq!(lanes.len(), 4);
}
}

View File

@ -1,13 +0,0 @@
// Copyright (c) 2017 Martijn Rijkeboer <mrr@sru-systems.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::error::Error;
use std::result;
/// A specialized result type for Argon2 operations.
pub type Result<T> = result::Result<T, Error>;

View File

@ -1,64 +0,0 @@
// Copyright (c) 2017 Xidorn Quan <me@upsuper.org>
// Copyright (c) 2017 Martijn Rijkeboer <mrr@sru-systems.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/// The thread mode used to perform the hashing.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ThreadMode {
/// Run in one thread.
Sequential,
#[cfg(feature = "crossbeam-utils")]
/// Run in the same number of threads as the number of lanes.
Parallel,
}
impl ThreadMode {
#[cfg(feature = "crossbeam-utils")]
/// Create a thread mode from the threads count.
pub fn from_threads(threads: u32) -> ThreadMode {
if threads > 1 {
ThreadMode::Parallel
} else {
ThreadMode::Sequential
}
}
#[cfg(not(feature = "crossbeam-utils"))]
pub fn from_threads(threads: u32) -> ThreadMode {
assert_eq!(threads, 1);
Self::default()
}
}
impl Default for ThreadMode {
fn default() -> ThreadMode {
ThreadMode::Sequential
}
}
#[cfg(test)]
mod tests {
use crate::thread_mode::ThreadMode;
#[test]
fn default_returns_correct_thread_mode() {
assert_eq!(ThreadMode::default(), ThreadMode::Sequential);
}
#[cfg(feature = "crossbeam-utils")]
#[test]
fn from_threads_returns_correct_thread_mode() {
assert_eq!(ThreadMode::from_threads(0), ThreadMode::Sequential);
assert_eq!(ThreadMode::from_threads(1), ThreadMode::Sequential);
assert_eq!(ThreadMode::from_threads(2), ThreadMode::Parallel);
assert_eq!(ThreadMode::from_threads(10), ThreadMode::Parallel);
assert_eq!(ThreadMode::from_threads(100), ThreadMode::Parallel);
}
}

View File

@ -1,158 +0,0 @@
// Copyright (c) 2017 Martijn Rijkeboer <mrr@sru-systems.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::error::Error;
use crate::result::Result;
use std::fmt;
/// The Argon2 variant.
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum Variant {
/// Argon2 using data-dependent memory access to thwart tradeoff attacks.
/// Recommended for cryptocurrencies and backend servers.
Argon2d = 0,
/// Argon2 using data-independent memory access to thwart side-channel
/// attacks. Recommended for password hashing and password-based key
/// derivation.
Argon2i = 1,
/// Argon2 using hybrid construction.
Argon2id = 2,
}
impl Variant {
/// Gets the lowercase string slice representation of the variant.
pub fn as_lowercase_str(&self) -> &'static str {
match *self {
Variant::Argon2d => "argon2d",
Variant::Argon2i => "argon2i",
Variant::Argon2id => "argon2id",
}
}
/// Gets the u32 representation of the variant.
pub fn as_u32(&self) -> u32 {
*self as u32
}
/// Gets the u64 representation of the variant.
pub fn as_u64(&self) -> u64 {
*self as u64
}
/// Gets the uppercase string slice representation of the variant.
pub fn as_uppercase_str(&self) -> &'static str {
match *self {
Variant::Argon2d => "Argon2d",
Variant::Argon2i => "Argon2i",
Variant::Argon2id => "Argon2id",
}
}
/// Attempts to create a variant from a string slice.
pub fn from_str(str: &str) -> Result<Variant> {
match str {
"Argon2d" => Ok(Variant::Argon2d),
"Argon2i" => Ok(Variant::Argon2i),
"Argon2id" => Ok(Variant::Argon2id),
"argon2d" => Ok(Variant::Argon2d),
"argon2i" => Ok(Variant::Argon2i),
"argon2id" => Ok(Variant::Argon2id),
_ => Err(Error::DecodingFail),
}
}
/// Attempts to create a variant from an u32.
pub fn from_u32(val: u32) -> Result<Variant> {
match val {
0 => Ok(Variant::Argon2d),
1 => Ok(Variant::Argon2i),
2 => Ok(Variant::Argon2id),
_ => Err(Error::IncorrectType),
}
}
}
impl Default for Variant {
fn default() -> Variant {
Variant::Argon2i
}
}
impl fmt::Display for Variant {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.as_lowercase_str())
}
}
#[cfg(test)]
mod tests {
use crate::error::Error;
use crate::variant::Variant;
#[test]
fn as_lowercase_str_returns_correct_str() {
assert_eq!(Variant::Argon2d.as_lowercase_str(), "argon2d");
assert_eq!(Variant::Argon2i.as_lowercase_str(), "argon2i");
assert_eq!(Variant::Argon2id.as_lowercase_str(), "argon2id");
}
#[test]
fn as_u32_returns_correct_u32() {
assert_eq!(Variant::Argon2d.as_u32(), 0);
assert_eq!(Variant::Argon2i.as_u32(), 1);
assert_eq!(Variant::Argon2id.as_u32(), 2);
}
#[test]
fn as_u64_returns_correct_u64() {
assert_eq!(Variant::Argon2d.as_u64(), 0);
assert_eq!(Variant::Argon2i.as_u64(), 1);
assert_eq!(Variant::Argon2id.as_u64(), 2);
}
#[test]
fn as_uppercase_str_returns_correct_str() {
assert_eq!(Variant::Argon2d.as_uppercase_str(), "Argon2d");
assert_eq!(Variant::Argon2i.as_uppercase_str(), "Argon2i");
assert_eq!(Variant::Argon2id.as_uppercase_str(), "Argon2id");
}
#[test]
fn default_returns_correct_variant() {
assert_eq!(Variant::default(), Variant::Argon2i);
}
#[test]
fn display_returns_correct_string() {
assert_eq!(format!("{}", Variant::Argon2d), "argon2d");
assert_eq!(format!("{}", Variant::Argon2i), "argon2i");
assert_eq!(format!("{}", Variant::Argon2id), "argon2id");
}
#[test]
fn from_str_returns_correct_result() {
assert_eq!(Variant::from_str("Argon2d"), Ok(Variant::Argon2d));
assert_eq!(Variant::from_str("Argon2i"), Ok(Variant::Argon2i));
assert_eq!(Variant::from_str("Argon2id"), Ok(Variant::Argon2id));
assert_eq!(Variant::from_str("argon2d"), Ok(Variant::Argon2d));
assert_eq!(Variant::from_str("argon2i"), Ok(Variant::Argon2i));
assert_eq!(Variant::from_str("argon2id"), Ok(Variant::Argon2id));
assert_eq!(Variant::from_str("foobar"), Err(Error::DecodingFail));
}
#[test]
fn from_u32_returns_correct_result() {
assert_eq!(Variant::from_u32(0), Ok(Variant::Argon2d));
assert_eq!(Variant::from_u32(1), Ok(Variant::Argon2i));
assert_eq!(Variant::from_u32(2), Ok(Variant::Argon2id));
assert_eq!(Variant::from_u32(3), Err(Error::IncorrectType));
}
}

View File

@ -1,96 +0,0 @@
// Copyright (c) 2017 Martijn Rijkeboer <mrr@sru-systems.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::error::Error;
use crate::result::Result;
use std::fmt;
/// The Argon2 version.
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum Version {
/// Version 0x10.
Version10 = 0x10,
/// Version 0x13 (Recommended).
Version13 = 0x13,
}
impl Version {
/// Gets the u32 representation of the version.
pub fn as_u32(&self) -> u32 {
*self as u32
}
/// Attempts to create a version from a string slice.
pub fn from_str(str: &str) -> Result<Version> {
match str {
"16" => Ok(Version::Version10),
"19" => Ok(Version::Version13),
_ => Err(Error::DecodingFail),
}
}
/// Attempts to create a version from an u32.
pub fn from_u32(val: u32) -> Result<Version> {
match val {
0x10 => Ok(Version::Version10),
0x13 => Ok(Version::Version13),
_ => Err(Error::IncorrectVersion),
}
}
}
impl Default for Version {
fn default() -> Version {
Version::Version13
}
}
impl fmt::Display for Version {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.as_u32())
}
}
#[cfg(test)]
mod tests {
use crate::error::Error;
use crate::version::Version;
#[test]
fn as_u32_returns_correct_u32() {
assert_eq!(Version::Version10.as_u32(), 0x10);
assert_eq!(Version::Version13.as_u32(), 0x13);
}
#[test]
fn default_returns_correct_version() {
assert_eq!(Version::default(), Version::Version13);
}
#[test]
fn display_returns_correct_string() {
assert_eq!(format!("{}", Version::Version10), "16");
assert_eq!(format!("{}", Version::Version13), "19");
}
#[test]
fn from_str_returns_correct_result() {
assert_eq!(Version::from_str("16"), Ok(Version::Version10));
assert_eq!(Version::from_str("19"), Ok(Version::Version13));
assert_eq!(Version::from_str("11"), Err(Error::DecodingFail));
}
#[test]
fn from_u32_returns_correct_result() {
assert_eq!(Version::from_u32(0x10), Ok(Version::Version10));
assert_eq!(Version::from_u32(0x13), Ok(Version::Version13));
assert_eq!(Version::from_u32(0), Err(Error::IncorrectVersion));
}
}

File diff suppressed because it is too large Load Diff