From 93d1460cc7b8e1497cb61db155031356cdcef396 Mon Sep 17 00:00:00 2001 From: Cesar Eduardo Barros Date: Sun, 24 May 2015 20:14:36 -0300 Subject: [PATCH] Initial commit --- .gitignore | 2 + Cargo.toml | 15 +++ LICENSE | 25 ++++ README.md | 44 +++++++ src/as_mut_bytes.rs | 50 +++++++ src/blake2.rs | 315 ++++++++++++++++++++++++++++++++++++++++++++ src/blake2b.rs | 114 ++++++++++++++++ src/blake2s.rs | 112 ++++++++++++++++ src/bytes.rs | 42 ++++++ src/lib.rs | 44 +++++++ 10 files changed, 763 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 src/as_mut_bytes.rs create mode 100644 src/blake2.rs create mode 100644 src/blake2b.rs create mode 100644 src/blake2s.rs create mode 100644 src/bytes.rs create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a9d37c5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..dc9f05f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "blake2-rfc" +version = "0.1.0" +authors = ["Cesar Eduardo Barros "] +description = "A pure Rust implementation of BLAKE2 based on the draft RFC." +repository = "https://github.com/cesarb/blake2-rfc" +readme = "README.md" +keywords = ["blake2", "blake2b", "blake2s", "hash", "hashing", "crypto", "cryptography"] +license = "MIT" + +[dependencies] +constant_time_eq = "0.1" + +[dev-dependencies] +rustc-serialize = "0.3" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..811df6f --- /dev/null +++ b/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2015 Cesar Eduardo Barros + +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..0bc37a0 --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +# blake2-rfc + +This is a pure Rust implementation of BLAKE2 based on the [draft RFC] +for the BLAKE2 hash functions. + +[draft RFC]: https://tools.ietf.org/html/draft-saarinen-blake2 + +## Design + +This crate follow the common API design for streaming hash functions, +which has one state/context struct and three associated functions: one +to initialize the struct, one which is called repeatedly to process the +incoming data, and one to do the final processing and return the hash. +For the case where the full data is already in memory, there is a +convenience function which does these three steps in a single call. + +This basic design was slightly adapted to make a better use of Rust's +characteristics: the finalization function consumes the struct, doing a +move instead of a borrow, so the struct cannot be accidentally used +after its internal state has been overwritten by the finalization. + +To prevent timing attacks, it's important that the comparison of hash +values takes constant time. To make it easier to do the right thing, the +finalization function returns the result wrapped in a struct which does +a constant-time comparison by default. If a constant-time comparison is +not necessary, the hash result can easily be extracted from this struct. + +## Limitations + +This crate is limited to the features described in the draft RFC: only +the "digest length" and "key length" parameters can be used. Other +advanced BLAKE2 features have not been implemented. + +A single BLAKE2b hash is limited to 16 exabytes, lower than its +theorical limit (but identical to the BLAKE2s theorical limit), due to +the use of a `u64` as the byte counter. This limit can be increased, if +necessary, after either the `extprim` crate (with its `u128` type) or +the `OverflowingOps` trait become usable with the "stable" Rust release. + +This crate does not attempt to clear potentially sensitive data from its +work memory (which includes the state context, the stack, and processor +registers). 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. diff --git a/src/as_mut_bytes.rs b/src/as_mut_bytes.rs new file mode 100644 index 0000000..c299192 --- /dev/null +++ b/src/as_mut_bytes.rs @@ -0,0 +1,50 @@ +// Copyright (c) 2015 Cesar Eduardo Barros +// +// 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. + +use std::mem; +use std::slice; + +pub trait AsMutBytes { + fn as_mut_bytes(&mut self) -> &mut [u8]; +} + +macro_rules! as_mut_bytes_impl { + ($t:ty) => { + impl AsMutBytes for $t { + #[inline] + fn as_mut_bytes(&mut self) -> &mut [u8] { + unsafe { + slice::from_raw_parts_mut( + self.as_mut_ptr() as *mut u8, + mem::size_of::()) + } + } + } + } +} + +as_mut_bytes_impl!([u32; 16]); +as_mut_bytes_impl!([u64; 16]); diff --git a/src/blake2.rs b/src/blake2.rs new file mode 100644 index 0000000..785593e --- /dev/null +++ b/src/blake2.rs @@ -0,0 +1,315 @@ +// Copyright (c) 2015 Cesar Eduardo Barros +// +// 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. + +#[inline(always)] +pub const SIGMA: [[usize; 16]; 10] = [ + [ 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], +]; + +macro_rules! blake2_impl { + ($state:ident, $result:ident, $func:ident, $word:ident, + $bytes:expr, $R1:expr, $R2:expr, $R3:expr, $R4:expr, $IV:expr) => { + use std::cmp; + use std::fmt::{self, Debug}; + use std::mem; + use std::$word; + + use $crate::as_mut_bytes::AsMutBytes; + use $crate::bytes::{MutableByteVector, copy_memory}; + use $crate::constant_time_eq::constant_time_eq; + + /// Container for a hash result. + /// + /// This container uses a constant-time comparison for equality. + /// If a constant-time comparison is not necessary, the hash + /// result can be extracted with the `as_bytes` method. + #[derive(Copy, Eq)] + pub struct $result { + h: [u8; $bytes], + nn: usize, + } + + impl $result { + /// Returns the contained hash result as a byte string. + #[inline] + pub fn as_bytes(&self) -> &[u8] { &self.h[..self.nn] } + + /// Returns the length of the hash result. + /// + /// This is the same value that was used to create the hash + /// context. + #[inline] + pub fn len(&self) -> usize { self.nn } + } + + impl AsRef<[u8]> for $result { + #[inline] + fn as_ref(&self) -> &[u8] { self.as_bytes() } + } + + impl Clone for $result { + #[inline] + fn clone(&self) -> Self { *self } + } + + impl Debug for $result { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Debug::fmt(self.as_bytes(), f) + } + } + + impl PartialEq for $result { + #[inline] + fn eq(&self, other: &Self) -> bool { + constant_time_eq(self.as_bytes(), other.as_bytes()) + } + } + + impl<'a> PartialEq<&'a [u8]> for $result { + #[inline] + fn eq(&self, other: &&[u8]) -> bool { + constant_time_eq(self.as_bytes(), other) + } + } + + /// State context. + #[derive(Clone)] + pub struct $state { + m: [$word; 16], + h: [$word; 8], + t: u64, + nn: usize, + } + + const IV: [$word; 8] = $IV; + + /// Convenience function for all-in-one computation. + pub fn $func(nn: usize, k: &[u8], data: &[u8]) -> $result { + let mut state = $state::with_key(nn, k); + state.update(data); + state.finalize() + } + + impl $state { + /// Creates a new hashing context without a key. + pub fn new(nn: usize) -> Self { Self::with_key(nn, &[]) } + + /// Creates a new hashing context with a key. + pub fn with_key(nn: usize, k: &[u8]) -> Self { + let kk = k.len(); + assert!(nn >= 1 && nn <= $bytes && kk <= $bytes); + + let mut h = IV; + h[0] ^= 0x01010000 ^ ((kk as $word) << 8) ^ (nn as $word); + + let mut state = $state { + m: [0; 16], + h: h, + t: 0, + nn: nn, + }; + + if kk > 0 { + copy_memory(k, state.m.as_mut_bytes()); + state.t = $bytes * 2; + } + state + } + + /// Updates the hashing context with more data. + pub fn update(&mut self, data: &[u8]) { + let mut rest = data; + + let off = (self.t % ($bytes * 2)) as usize; + if off != 0 || self.t == 0 { + let len = cmp::min(($bytes * 2) - off, rest.len()); + + let part = &rest[..len]; + rest = &rest[part.len()..]; + + copy_memory(part, &mut self.m.as_mut_bytes()[off..]); + self.t = self.t.checked_add(part.len() as u64).unwrap(); + } + + while rest.len() >= $bytes * 2 { + self.compress(0); + + let part = &rest[..($bytes * 2)]; + rest = &rest[part.len()..]; + + copy_memory(part, self.m.as_mut_bytes()); + self.t = self.t.checked_add(part.len() as u64).unwrap(); + } + + if rest.len() > 0 { + self.compress(0); + + copy_memory(rest, self.m.as_mut_bytes()); + self.t = self.t.checked_add(rest.len() as u64).unwrap(); + } + } + + /// Consumes the hashing context and returns the resulting hash. + pub fn finalize(mut self) -> $result { + let off = (self.t % ($bytes * 2)) as usize; + if off != 0 { + self.m.as_mut_bytes()[off..].set_memory(0); + } + + self.compress(!0); + + for i in 0..self.h.len() { + self.h[i] = self.h[i].to_le(); + } + $result { + h: unsafe { mem::transmute(self.h) }, + nn: self.nn, + } + } + + #[inline(always)] + fn mix(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($word::from_le(x)); + v[d] = (v[d] ^ v[a]).rotate_right($R1); + v[c] = v[c].wrapping_add(v[d]); + v[b] = (v[b] ^ v[c]).rotate_right($R2); + v[a] = v[a].wrapping_add(v[b]).wrapping_add($word::from_le(y)); + v[d] = (v[d] ^ v[a]).rotate_right($R3); + v[c] = v[c].wrapping_add(v[d]); + v[b] = (v[b] ^ v[c]).rotate_right($R4); + } + + #[inline(always)] + fn round(v: &mut [$word; 16], m: &[$word; 16], s: &[usize; 16]) { + $state::mix(v, 0, 4, 8, 12, m[s[ 0]], m[s[ 1]]); + $state::mix(v, 1, 5, 9, 13, m[s[ 2]], m[s[ 3]]); + $state::mix(v, 2, 6, 10, 14, m[s[ 4]], m[s[ 5]]); + $state::mix(v, 3, 7, 11, 15, m[s[ 6]], m[s[ 7]]); + + $state::mix(v, 0, 5, 10, 15, m[s[ 8]], m[s[ 9]]); + $state::mix(v, 1, 6, 11, 12, m[s[10]], m[s[11]]); + $state::mix(v, 2, 7, 8, 13, m[s[12]], m[s[13]]); + $state::mix(v, 3, 4, 9, 14, m[s[14]], m[s[15]]); + } + + fn compress(&mut self, f0: $word) { + use $crate::blake2::SIGMA; + + let m = &self.m; + let h = &mut self.h; + + let t0 = (self.t & ($word::MAX as u64)) as $word; + let t1 = match $bytes { + 64 => 0, + 32 => (self.t >> 32) as $word, + _ => unreachable!(), + }; + + let mut v = [ + h[0], h[1], h[2], h[3], + h[4], h[5], h[6], h[7], + IV[0], IV[1], IV[2], IV[3], + IV[4] ^ t0, IV[5] ^ t1, IV[6] ^ f0, IV[7], + ]; + + $state::round(&mut v, m, &SIGMA[0]); + $state::round(&mut v, m, &SIGMA[1]); + $state::round(&mut v, m, &SIGMA[2]); + $state::round(&mut v, m, &SIGMA[3]); + $state::round(&mut v, m, &SIGMA[4]); + $state::round(&mut v, m, &SIGMA[5]); + $state::round(&mut v, m, &SIGMA[6]); + $state::round(&mut v, m, &SIGMA[7]); + $state::round(&mut v, m, &SIGMA[8]); + $state::round(&mut v, m, &SIGMA[9]); + if $bytes > 32 { + $state::round(&mut v, m, &SIGMA[0]); + $state::round(&mut v, m, &SIGMA[1]); + } + + for i in 0..8 { + h[i] ^= v[i] ^ v[i + 8]; + } + } + } + } +} + +pub fn selftest_seq(len: usize) -> Vec { + use std::num::Wrapping; + + let seed = Wrapping(len as u32); + let mut out = Vec::with_capacity(len); + + let mut a = Wrapping(0xDEAD4BAD) * seed; + let mut b = Wrapping(1); + + for _ in 0..len { + let t = a + b; + a = b; + b = t; + out.push((t >> 24).0 as u8); + } + out +} + +macro_rules! blake2_selftest_impl { + ($state:ident, $func:ident, $res:expr, $md_len:expr, $in_len:expr) => { + /// Runs the self-test for this hash function. + pub fn selftest() { + use $crate::blake2::selftest_seq; + + const BLAKE2_RES: [u8; 32] = $res; + const B2_MD_LEN: [usize; 4] = $md_len; + const B2_IN_LEN: [usize; 6] = $in_len; + + let mut state = $state::new(32); + + for &outlen in B2_MD_LEN.iter() { + for &inlen in B2_IN_LEN.iter() { + let data = selftest_seq(inlen); + let md = $func(outlen, &[], &data); + state.update(md.as_bytes()); + + let key = selftest_seq(outlen); + let md = $func(outlen, &key, &data); + state.update(md.as_bytes()); + } + } + + assert_eq!(state.finalize(), BLAKE2_RES.as_ref()); + } + } +} diff --git a/src/blake2b.rs b/src/blake2b.rs new file mode 100644 index 0000000..b539efa --- /dev/null +++ b/src/blake2b.rs @@ -0,0 +1,114 @@ +// Copyright (c) 2015 Cesar Eduardo Barros +// +// 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. + +//! The BLAKE2b hash function. +//! +//! # Examples +//! +//! ``` +//! use blake2_rfc::blake2b::{Blake2b, blake2b}; +//! +//! // Using the convenience function. +//! let hash = blake2b(64, &[], b"The quick brown fox jumps over the lazy dog"); +//! +//! // Using the state context. +//! let mut context = Blake2b::new(64); +//! context.update(b"The quick brown fox jumps over the lazy dog"); +//! let hash = context.finalize(); +//! +//! // Using the convenience function, with a key. +//! let hash = blake2b(64, b"key", b"The quick brown fox jumps over the lazy dog"); +//! +//! // Using the state context, with a key. +//! let mut context = Blake2b::with_key(64, b"key"); +//! context.update(b"The quick brown fox jumps over the lazy dog"); +//! let hash = context.finalize(); +//! ``` +//! +//! The returned hash is a `Blake2bResult`, which can be compared with +//! a byte string (the comparison will take constant time), or converted +//! into a byte string. + +blake2_impl!(Blake2b, Blake2bResult, blake2b, u64, 64, 32, 24, 16, 63, [ + 0x6A09E667F3BCC908, 0xBB67AE8584CAA73B, + 0x3C6EF372FE94F82B, 0xA54FF53A5F1D36F1, + 0x510E527FADE682D1, 0x9B05688C2B3E6C1F, + 0x1F83D9ABFB41BD6B, 0x5BE0CD19137E2179, +]); + +blake2_selftest_impl!(Blake2b, blake2b, [ + 0xC2, 0x3A, 0x78, 0x00, 0xD9, 0x81, 0x23, 0xBD, + 0x10, 0xF5, 0x06, 0xC6, 0x1E, 0x29, 0xDA, 0x56, + 0x03, 0xD7, 0x63, 0xB8, 0xBB, 0xAD, 0x2E, 0x73, + 0x7F, 0x5E, 0x76, 0x5A, 0x7B, 0xCC, 0xD4, 0x75, +], [ 20, 32, 48, 64 ], [ 0, 3, 128, 129, 255, 1024 ]); + +#[cfg(test)] +mod tests { + extern crate rustc_serialize as serialize; + use self::serialize::hex::FromHex; + + use blake2::selftest_seq; + use super::{Blake2b, blake2b}; + + #[test] + fn test_empty() { + assert_eq!(blake2b(64, &[], b""), + "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce" + .from_hex().unwrap().as_ref()); + } + + #[test] + fn selftest() { + super::selftest(); + } + + #[test] + fn test_split() { + let data = selftest_seq(512); + + let mut ctx = Blake2b::new(64); + ctx.update(&data[..32]); + ctx.update(&data[32..64]); + ctx.update(&data[64..448]); + ctx.update(&data[448..]); + + assert_eq!(ctx.finalize(), blake2b(64, &[], &data)); + } + + #[test] + fn test_4g() { + const ZEROS: [u8; 4096] = [0; 4096]; + + let mut state = Blake2b::new(64); + for _ in 0..1048576 { + state.update(&ZEROS); + } + assert_eq!(state.finalize(), + "645572ca5756f9104329ed543735fc11904f0c18c4df8adf930f22d07f3094919a519ff34fd240ae3f5d5b4c8042225c109fb951036fdc99e7d2cd0c1d36b267" + .from_hex().unwrap().as_ref()); + } +} diff --git a/src/blake2s.rs b/src/blake2s.rs new file mode 100644 index 0000000..f5277c7 --- /dev/null +++ b/src/blake2s.rs @@ -0,0 +1,112 @@ +// Copyright (c) 2015 Cesar Eduardo Barros +// +// 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. + +//! The BLAKE2s hash function. +//! +//! # Examples +//! +//! ``` +//! use blake2_rfc::blake2s::{Blake2s, blake2s}; +//! +//! // Using the convenience function. +//! let hash = blake2s(32, &[], b"The quick brown fox jumps over the lazy dog"); +//! +//! // Using the state context. +//! let mut context = Blake2s::new(32); +//! context.update(b"The quick brown fox jumps over the lazy dog"); +//! let hash = context.finalize(); +//! +//! // Using the convenience function, with a key. +//! let hash = blake2s(32, b"key", b"The quick brown fox jumps over the lazy dog"); +//! +//! // Using the state context, with a key. +//! let mut context = Blake2s::with_key(32, b"key"); +//! context.update(b"The quick brown fox jumps over the lazy dog"); +//! let hash = context.finalize(); +//! ``` +//! +//! The returned hash is a `Blake2sResult`, which can be compared with +//! a byte string (the comparison will take constant time), or converted +//! into a byte string. + +blake2_impl!(Blake2s, Blake2sResult, blake2s, u32, 32, 16, 12, 8, 7, [ + 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, + 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19, +]); + +blake2_selftest_impl!(Blake2s, blake2s, [ + 0x6A, 0x41, 0x1F, 0x08, 0xCE, 0x25, 0xAD, 0xCD, + 0xFB, 0x02, 0xAB, 0xA6, 0x41, 0x45, 0x1C, 0xEC, + 0x53, 0xC5, 0x98, 0xB2, 0x4F, 0x4F, 0xC7, 0x87, + 0xFB, 0xDC, 0x88, 0x79, 0x7F, 0x4C, 0x1D, 0xFE, +], [ 16, 20, 28, 32 ], [ 0, 3, 64, 65, 255, 1024 ]); + +#[cfg(test)] +mod tests { + extern crate rustc_serialize as serialize; + use self::serialize::hex::FromHex; + + use blake2::selftest_seq; + use super::{Blake2s, blake2s}; + + #[test] + fn test_empty() { + assert_eq!(blake2s(32, &[], b""), + "69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9" + .from_hex().unwrap().as_ref()); + } + + #[test] + fn selftest() { + super::selftest(); + } + + #[test] + fn test_split() { + let data = selftest_seq(256); + + let mut ctx = Blake2s::new(32); + ctx.update(&data[..16]); + ctx.update(&data[16..32]); + ctx.update(&data[32..224]); + ctx.update(&data[224..]); + + assert_eq!(ctx.finalize(), blake2s(32, &[], &data)); + } + + #[test] + fn test_4g() { + const ZEROS: [u8; 4096] = [0; 4096]; + + let mut state = Blake2s::new(32); + for _ in 0..1048576 { + state.update(&ZEROS); + } + assert_eq!(state.finalize(), + "2a8e26830310da3ef7f7032b7b1af11b989aba44a3713a22f539f69bd2ce4a87" + .from_hex().unwrap().as_ref()); + } +} diff --git a/src/bytes.rs b/src/bytes.rs new file mode 100644 index 0000000..88b835e --- /dev/null +++ b/src/bytes.rs @@ -0,0 +1,42 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of the Rust distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A local copy of the unstable std::slice::bytes module. + +use std::ptr; + +/// A trait for operations on mutable `[u8]`s. +pub trait MutableByteVector { + /// Sets all bytes of the receiver to the given value. + fn set_memory(&mut self, value: u8); +} + +impl MutableByteVector for [u8] { + #[inline] + fn set_memory(&mut self, value: u8) { + unsafe { ptr::write_bytes(self.as_mut_ptr(), value, self.len()) }; + } +} + +/// Copies data from `src` to `dst` +/// +/// Panics if the length of `dst` is less than the length of `src`. +#[inline] +pub fn copy_memory(src: &[u8], dst: &mut [u8]) { + let len_src = src.len(); + assert!(dst.len() >= len_src); + // `dst` is unaliasable, so we know statically it doesn't overlap + // with `src`. + unsafe { + ptr::copy_nonoverlapping(src.as_ptr(), + dst.as_mut_ptr(), + len_src); + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..e662db5 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,44 @@ +// Copyright (c) 2015 Cesar Eduardo Barros +// +// 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. + +//! A pure Rust implementation of BLAKE2 based on the draft RFC. + +extern crate constant_time_eq; + +mod as_mut_bytes; +mod bytes; + +#[macro_use] +mod blake2; + +pub mod blake2b; +pub mod blake2s; + +/// Runs the self-test for both BLAKE2b and BLAKE2s. +pub fn selftest() { + blake2b::selftest(); + blake2s::selftest(); +}