From 5afcdb2b94391ced811e3f8a50258778e28bb6a4 Mon Sep 17 00:00:00 2001
From: Palmer Cox
Date: Fri, 13 Sep 2013 21:34:09 -0400
Subject: [PATCH 1/9] Implement CheckedNumCast trait for integer types.
---
src/libstd/num/num.rs | 165 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 165 insertions(+)
diff --git a/src/libstd/num/num.rs b/src/libstd/num/num.rs
index 135124b04c143..a738dbca1ca61 100644
--- a/src/libstd/num/num.rs
+++ b/src/libstd/num/num.rs
@@ -430,6 +430,156 @@ impl_num_cast!(f32, to_f32)
impl_num_cast!(f64, to_f64)
impl_num_cast!(float, to_float)
+
+/// Cast from one machine scalar to another, checking that the value to be cast fits into the result
+/// type.
+#[inline]
+pub fn checked_cast(n: T) -> Option {
+ CheckedNumCast::checked_from(n)
+}
+
+pub trait CheckedNumCast {
+ fn checked_from(n: T) -> Option;
+
+ fn checked_to_u8(&self) -> Option;
+ fn checked_to_u16(&self) -> Option;
+ fn checked_to_u32(&self) -> Option;
+ fn checked_to_u64(&self) -> Option;
+ fn checked_to_uint(&self) -> Option;
+
+ fn checked_to_i8(&self) -> Option;
+ fn checked_to_i16(&self) -> Option;
+ fn checked_to_i32(&self) -> Option;
+ fn checked_to_i64(&self) -> Option;
+ fn checked_to_int(&self) -> Option;
+}
+
+fn checked_cast_u_to_u(input: T)
+ -> Option {
+ use sys::size_of;
+ if size_of::() <= size_of::() {
+ Some(cast(input))
+ } else {
+ let out_max: O = Bounded::max_value();
+ if input <= cast(out_max) {
+ Some(cast(input))
+ } else {
+ None
+ }
+ }
+}
+
+fn checked_cast_i_to_u(input: T)
+ -> Option {
+ use sys::size_of;
+ if input < Zero::zero() {
+ None
+ } else if size_of::() <= size_of::() {
+ Some(cast(input))
+ } else {
+ let out_max: O = Bounded::max_value();
+ if input <= cast(out_max) {
+ Some(cast(input))
+ } else {
+ None
+ }
+ }
+}
+
+fn checked_cast_u_to_i(input: T)
+ -> Option {
+ use sys::size_of;
+ if size_of::() < size_of::() {
+ Some(cast(input))
+ } else {
+ let out_max: O = Bounded::max_value();
+ if input <= cast(out_max) {
+ Some(cast(input))
+ } else {
+ None
+ }
+ }
+}
+
+fn checked_cast_i_to_i(input: T)
+ -> Option {
+ use sys::size_of;
+ if size_of::() <= size_of::() {
+ Some(cast(input))
+ } else {
+ let out_max: O = Bounded::max_value();
+ let out_min: O = Bounded::min_value();
+ if input >= cast(out_min) && input <= cast(out_max) {
+ Some(cast(input))
+ } else {
+ None
+ }
+ }
+}
+
+macro_rules! impl_checked_num_cast_u_to_x(
+ ($T:ty, $conv:ident) => (
+ impl CheckedNumCast for $T {
+ #[inline]
+ fn checked_from(n: N) -> Option<$T> {
+ // `$conv` could be generated using `concat_idents!`, but that
+ // macro seems to be broken at the moment
+ n.$conv()
+ }
+
+ #[inline] fn checked_to_u8(&self) -> Option { checked_cast_u_to_u(*self) }
+ #[inline] fn checked_to_u16(&self) -> Option { checked_cast_u_to_u(*self) }
+ #[inline] fn checked_to_u32(&self) -> Option { checked_cast_u_to_u(*self) }
+ #[inline] fn checked_to_u64(&self) -> Option { checked_cast_u_to_u(*self) }
+ #[inline] fn checked_to_uint(&self) -> Option { checked_cast_u_to_u(*self) }
+
+ #[inline] fn checked_to_i8(&self) -> Option { checked_cast_u_to_i(*self) }
+ #[inline] fn checked_to_i16(&self) -> Option { checked_cast_u_to_i(*self) }
+ #[inline] fn checked_to_i32(&self) -> Option { checked_cast_u_to_i(*self) }
+ #[inline] fn checked_to_i64(&self) -> Option { checked_cast_u_to_i(*self) }
+ #[inline] fn checked_to_int(&self) -> Option { checked_cast_u_to_i(*self) }
+ }
+ )
+)
+
+macro_rules! impl_checked_num_cast_i_to_x(
+ ($T:ty, $conv:ident) => (
+ impl CheckedNumCast for $T {
+ #[inline]
+ fn checked_from(n: N) -> Option<$T> {
+ // `$conv` could be generated using `concat_idents!`, but that
+ // macro seems to be broken at the moment
+ n.$conv()
+ }
+
+ #[inline] fn checked_to_u8(&self) -> Option { checked_cast_i_to_u(*self) }
+ #[inline] fn checked_to_u16(&self) -> Option { checked_cast_i_to_u(*self) }
+ #[inline] fn checked_to_u32(&self) -> Option { checked_cast_i_to_u(*self) }
+ #[inline] fn checked_to_u64(&self) -> Option { checked_cast_i_to_u(*self) }
+ #[inline] fn checked_to_uint(&self) -> Option { checked_cast_i_to_u(*self) }
+
+ #[inline] fn checked_to_i8(&self) -> Option { checked_cast_i_to_i(*self) }
+ #[inline] fn checked_to_i16(&self) -> Option { checked_cast_i_to_i(*self) }
+ #[inline] fn checked_to_i32(&self) -> Option { checked_cast_i_to_i(*self) }
+ #[inline] fn checked_to_i64(&self) -> Option { checked_cast_i_to_i(*self) }
+ #[inline] fn checked_to_int(&self) -> Option { checked_cast_i_to_i(*self) }
+ }
+ )
+)
+
+impl_checked_num_cast_u_to_x!(u8, checked_to_u8)
+impl_checked_num_cast_u_to_x!(u16, checked_to_u16)
+impl_checked_num_cast_u_to_x!(u32, checked_to_u32)
+impl_checked_num_cast_u_to_x!(u64, checked_to_u64)
+impl_checked_num_cast_u_to_x!(uint, checked_to_uint)
+
+impl_checked_num_cast_i_to_x!(i8, checked_to_i8)
+impl_checked_num_cast_i_to_x!(i16, checked_to_i16)
+impl_checked_num_cast_i_to_x!(i32, checked_to_i32)
+impl_checked_num_cast_i_to_x!(i64, checked_to_i64)
+impl_checked_num_cast_i_to_x!(int, checked_to_int)
+
+
pub trait ToStrRadix {
fn to_str_radix(&self, radix: uint) -> ~str;
}
@@ -697,4 +847,19 @@ mod tests {
assert_eq!(third.checked_mul(&3), Some(third * 3));
assert_eq!(third.checked_mul(&4), None);
}
+
+ #[test]
+ fn test_checked_cast() {
+ assert_eq!(checked_cast(255u16), Some(255u8));
+ assert!(256u16.checked_to_u8().is_none());
+
+ assert_eq!(checked_cast(127u8), Some(127i8));
+ assert!(128u8.checked_to_i8().is_none());
+
+ assert_eq!(checked_cast(127i8), Some(127u8));
+ assert!((-1i8).checked_to_u8().is_none());
+
+ assert_eq!(checked_cast(-128i16), Some(-128i8));
+ assert!((-129i16).checked_to_i8().is_none());
+ }
}
From 0b3dd204bf24183c1d84500fa8ca16a129a359a0 Mon Sep 17 00:00:00 2001
From: Palmer Cox
Date: Fri, 13 Sep 2013 22:53:36 -0400
Subject: [PATCH 2/9] Implement mut_chunk_iter for mutable vectors.
---
src/libstd/vec.rs | 73 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 73 insertions(+)
diff --git a/src/libstd/vec.rs b/src/libstd/vec.rs
index 1ff5835188650..9fff4177e25ec 100644
--- a/src/libstd/vec.rs
+++ b/src/libstd/vec.rs
@@ -584,6 +584,43 @@ impl<'self, T> Iterator<&'self [T]> for ChunkIter<'self, T> {
}
}
+/// An iterator over a vector in (non-overlapping) mutable chunks (`size`
+/// elements at a time).
+///
+/// When the vector len is not evenly divided by the chunk size,
+/// the last slice of the iteration will be the remainder.
+pub struct MutChunkIter<'self, T> {
+ priv v: &'self mut [T],
+ priv len: uint,
+ priv size: uint,
+ priv pos: uint
+}
+
+impl<'self, T> Iterator<&'self mut [T]> for MutChunkIter<'self, T> {
+ #[inline]
+ fn next(&mut self) -> Option<&'self mut [T]> {
+ if self.pos >= self.len {
+ None
+ } else {
+ let chunksz = cmp::min(self.len - self.pos, self.size);
+ let out = self.v.mut_slice(self.pos, self.pos + chunksz);
+ self.pos += chunksz;
+ Some(out)
+ }
+ }
+
+ #[inline]
+ fn size_hint(&self) -> (uint, Option) {
+ if self.len == 0 {
+ (0, Some(0))
+ } else {
+ let (n, rem) = self.len.div_rem(&self.size);
+ let n = if rem > 0 { n + 1 } else { n };
+ (n, Some(n))
+ }
+ }
+}
+
impl<'self, T> DoubleEndedIterator<&'self [T]> for ChunkIter<'self, T> {
#[inline]
fn next_back(&mut self) -> Option<&'self [T]> {
@@ -1824,6 +1861,14 @@ pub trait MutableVector<'self, T> {
fn mut_iter(self) -> VecMutIterator<'self, T>;
fn mut_rev_iter(self) -> MutRevIterator<'self, T>;
+ /**
+ * Returns an iterator over `size` elements of the vector at a
+ * time. The chunks do not overlap. If `size` does not divide the
+ * length of the vector, then the last chunk will not have length
+ * `size`. The chunk are mutable.
+ */
+ fn mut_chunk_iter(self, size: uint) -> MutChunkIter<'self, T>;
+
fn swap(self, a: uint, b: uint);
/**
@@ -1925,6 +1970,13 @@ impl<'self,T> MutableVector<'self, T> for &'self mut [T] {
self.mut_iter().invert()
}
+ #[inline]
+ fn mut_chunk_iter(self, size: uint) -> MutChunkIter<'self, T> {
+ assert!(size != 0);
+ let len = self.len();
+ MutChunkIter { v: self, len: len, size: size, pos: 0 }
+ }
+
/**
* Swaps two elements in a vector
*
@@ -3622,6 +3674,27 @@ mod tests {
v.push(2);
}
+ #[test]
+ fn test_mut_chunk_iterator() {
+ let mut v = [0u8, 1, 2, 3, 4, 5];
+
+ for (i, chunk) in v.mut_chunk_iter(3).enumerate() {
+ chunk[0] = i as u8;
+ chunk[1] = i as u8;
+ chunk[2] = i as u8;
+ }
+
+ let result = [0u8, 0, 0, 1, 1, 1];
+ assert_eq!(v, result);
+ }
+
+ #[test]
+ #[should_fail]
+ fn test_mut_chunk_iterator_0() {
+ let mut v = [1, 2, 3, 4];
+ let _it = v.mut_chunk_iter(0);
+ }
+
#[test]
fn test_mut_split() {
let mut values = [1u8,2,3,4,5];
From 7eb8658dd3c7d98449003d58c179c9b354fa8236 Mon Sep 17 00:00:00 2001
From: Palmer Cox
Date: Tue, 27 Aug 2013 23:47:16 -0400
Subject: [PATCH 3/9] Digest: Add the block_size() method to the Digest trait.
---
src/libextra/crypto/digest.rs | 5 +++++
src/libextra/crypto/md5.rs | 2 ++
src/libextra/crypto/sha1.rs | 1 +
src/libextra/crypto/sha2.rs | 12 ++++++++++++
4 files changed, 20 insertions(+)
diff --git a/src/libextra/crypto/digest.rs b/src/libextra/crypto/digest.rs
index d2d6b540cff4c..a5a20c873f134 100644
--- a/src/libextra/crypto/digest.rs
+++ b/src/libextra/crypto/digest.rs
@@ -45,6 +45,11 @@ pub trait Digest {
*/
fn output_bits(&self) -> uint;
+ /**
+ * Get the block size in bytes.
+ */
+ fn block_size(&self) -> uint;
+
/**
* Convenience function that feeds a string into a digest.
*
diff --git a/src/libextra/crypto/md5.rs b/src/libextra/crypto/md5.rs
index 8e8b752da8075..8a60001874b23 100644
--- a/src/libextra/crypto/md5.rs
+++ b/src/libextra/crypto/md5.rs
@@ -211,6 +211,8 @@ impl Digest for Md5 {
}
fn output_bits(&self) -> uint { 128 }
+
+ fn block_size(&self) -> uint { 64 }
}
diff --git a/src/libextra/crypto/sha1.rs b/src/libextra/crypto/sha1.rs
index 4d4d47feee817..e7d9181d5baf0 100644
--- a/src/libextra/crypto/sha1.rs
+++ b/src/libextra/crypto/sha1.rs
@@ -172,6 +172,7 @@ impl Digest for Sha1 {
fn input(&mut self, msg: &[u8]) { add_input(self, msg); }
fn result(&mut self, out: &mut [u8]) { return mk_result(self, out); }
fn output_bits(&self) -> uint { 160 }
+ fn block_size(&self) -> uint { 64 }
}
#[cfg(test)]
diff --git a/src/libextra/crypto/sha2.rs b/src/libextra/crypto/sha2.rs
index 49bbddca1dbb7..6285a409cfb8d 100644
--- a/src/libextra/crypto/sha2.rs
+++ b/src/libextra/crypto/sha2.rs
@@ -270,6 +270,8 @@ impl Digest for Sha512 {
}
fn output_bits(&self) -> uint { 512 }
+
+ fn block_size(&self) -> uint { 128 }
}
static H512: [u64, ..8] = [
@@ -320,6 +322,8 @@ impl Digest for Sha384 {
}
fn output_bits(&self) -> uint { 384 }
+
+ fn block_size(&self) -> uint { 128 }
}
static H384: [u64, ..8] = [
@@ -368,6 +372,8 @@ impl Digest for Sha512Trunc256 {
}
fn output_bits(&self) -> uint { 256 }
+
+ fn block_size(&self) -> uint { 128 }
}
static H512_TRUNC_256: [u64, ..8] = [
@@ -416,6 +422,8 @@ impl Digest for Sha512Trunc224 {
}
fn output_bits(&self) -> uint { 224 }
+
+ fn block_size(&self) -> uint { 128 }
}
static H512_TRUNC_224: [u64, ..8] = [
@@ -677,6 +685,8 @@ impl Digest for Sha256 {
}
fn output_bits(&self) -> uint { 256 }
+
+ fn block_size(&self) -> uint { 64 }
}
static H256: [u32, ..8] = [
@@ -727,6 +737,8 @@ impl Digest for Sha224 {
}
fn output_bits(&self) -> uint { 224 }
+
+ fn block_size(&self) -> uint { 64 }
}
static H224: [u32, ..8] = [
From 9435061dff298d8edea3062695821f82b0e337c8 Mon Sep 17 00:00:00 2001
From: Palmer Cox
Date: Sat, 31 Aug 2013 14:26:08 -0400
Subject: [PATCH 4/9] Digest: Add the output_bytes() method to the Digest
trait.
---
src/libextra/crypto/digest.rs | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/libextra/crypto/digest.rs b/src/libextra/crypto/digest.rs
index a5a20c873f134..534ca7ee7dec8 100644
--- a/src/libextra/crypto/digest.rs
+++ b/src/libextra/crypto/digest.rs
@@ -45,6 +45,13 @@ pub trait Digest {
*/
fn output_bits(&self) -> uint;
+ /**
+ * Get the output size in bytes.
+ */
+ fn output_bytes(&self) -> uint {
+ return (self.output_bits() + 7) / 8;
+ }
+
/**
* Get the block size in bytes.
*/
From 72dcb6e431ff7d76dd3cf3a0fcdc1575762123ab Mon Sep 17 00:00:00 2001
From: Palmer Cox
Date: Tue, 27 Aug 2013 23:51:50 -0400
Subject: [PATCH 5/9] Crypto: Implement fixed_time_eq function.
This function compares two vectors using a fixed number of operations. This
method should be used wherever an adversary might time how long a comparison
takes and derive useful information from that.
---
src/libextra/crypto/cryptoutil.rs | 106 +++++++++++++++++++++++++++++-
1 file changed, 105 insertions(+), 1 deletion(-)
diff --git a/src/libextra/crypto/cryptoutil.rs b/src/libextra/crypto/cryptoutil.rs
index 9516517d9f7be..2f44d3119e0f3 100644
--- a/src/libextra/crypto/cryptoutil.rs
+++ b/src/libextra/crypto/cryptoutil.rs
@@ -97,6 +97,90 @@ pub fn read_u32v_le(dst: &mut[u32], input: &[u8]) {
}
+#[cfg(target_arch = "x86")]
+#[cfg(target_arch = "x86_64")]
+#[inline(never)]
+unsafe fn fixed_time_eq_asm(mut lhsp: *u8, mut rhsp: *u8, mut count: uint) -> bool {
+ use std::unstable::intrinsics::uninit;
+
+ let mut result: u8 = 0;
+ let mut tmp: u8 = uninit();
+
+ asm!(
+ "
+ fixed_time_eq_loop:
+
+ mov ($1), $4
+ xor ($2), $4
+ or $4, $0
+
+ inc $1
+ inc $2
+ dec $3
+ jnz fixed_time_eq_loop
+ "
+ : "=&r" (result), "=&r" (lhsp), "=&r" (rhsp), "=&r" (count), "=&r" (tmp) // output
+ : "0" (result), "1" (lhsp), "2" (rhsp), "3" (count), "4" (tmp) // input
+ : "cc" // clobbers
+ : // flags
+ );
+
+ return result == 0;
+}
+
+#[cfg(target_arch = "arm")]
+#[inline(never)]
+unsafe fn fixed_time_eq_asm(mut lhsp: *u8, mut rhsp: *u8, mut count: uint) -> bool {
+ use std::unstable::intrinsics::uninit;
+
+ let mut result: u8 = 0;
+ let mut tmp1: u8 = uninit();
+ let mut tmp2: u8 = uninit();
+
+ asm!(
+ "
+ fixed_time_eq_loop:
+
+ ldrb $4, [$1]
+ ldrb $5, [$2]
+ eor $4, $4, $5
+ orr $0, $0, $4
+
+ add $1, $1, #1
+ add $2, $2, #1
+ subs $3, $3, #1
+ bne fixed_time_eq_loop
+ "
+ // output
+ : "=&r" (result), "=&r" (lhsp), "=&r" (rhsp), "=&r" (count), "=&r" (tmp1), "=&r" (tmp2)
+ : "0" (result), "1" (lhsp), "2" (rhsp), "3" (count), "4" (tmp1), "5" (tmp2) // input
+ : "cc" // clobbers
+ : // flags
+ );
+
+ return result == 0;
+}
+
+/// Compare two vectors using a fixed number of operations. If the two vectors are not of equal
+/// length, the function returns false immediately.
+pub fn fixed_time_eq(lhs: &[u8], rhs: &[u8]) -> bool {
+ if lhs.len() != rhs.len() {
+ return false;
+ }
+ if lhs.len() == 0 {
+ return true;
+ }
+
+ let count = lhs.len();
+
+ unsafe {
+ let lhsp = lhs.unsafe_ref(0);
+ let rhsp = rhs.unsafe_ref(0);
+ return fixed_time_eq_asm(lhsp, rhsp, count);
+ }
+}
+
+
trait ToBits {
/// Convert the value in bytes to the number of bits, a tuple where the 1st item is the
/// high-order value and the 2nd item is the low order value.
@@ -351,7 +435,7 @@ mod test {
use std::rand::RngUtil;
use std::vec;
- use cryptoutil::{add_bytes_to_bits, add_bytes_to_bits_tuple};
+ use cryptoutil::{add_bytes_to_bits, add_bytes_to_bits_tuple, fixed_time_eq};
use digest::Digest;
/// Feed 1,000,000 'a's into the digest with varying input sizes and check that the result is
@@ -423,4 +507,24 @@ mod test {
let value: u64 = Bounded::max_value();
add_bytes_to_bits_tuple::((value - 1, 0), 0x8000000000000000);
}
+
+ #[test]
+ pub fn test_fixed_time_eq() {
+ let a = [0, 1, 2];
+ let b = [0, 1, 2];
+ let c = [0, 1, 9];
+ let d = [9, 1, 2];
+ let e = [2, 1, 0];
+ let f = [2, 2, 2];
+ let g = [0, 0, 0];
+
+ assert!(fixed_time_eq(a, a));
+ assert!(fixed_time_eq(a, b));
+
+ assert!(!fixed_time_eq(a, c));
+ assert!(!fixed_time_eq(a, d));
+ assert!(!fixed_time_eq(a, e));
+ assert!(!fixed_time_eq(a, f));
+ assert!(!fixed_time_eq(a, g));
+ }
}
From ea73aa6690485b22b6360f4ae02cfda6d4153231 Mon Sep 17 00:00:00 2001
From: Palmer Cox
Date: Mon, 2 Sep 2013 12:11:10 -0400
Subject: [PATCH 6/9] Crypto: Implement the read_u32_le and read_u32_be
functions.
---
src/libextra/crypto/cryptoutil.rs | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/src/libextra/crypto/cryptoutil.rs b/src/libextra/crypto/cryptoutil.rs
index 2f44d3119e0f3..2d5cb397a5b49 100644
--- a/src/libextra/crypto/cryptoutil.rs
+++ b/src/libextra/crypto/cryptoutil.rs
@@ -96,6 +96,28 @@ pub fn read_u32v_le(dst: &mut[u32], input: &[u8]) {
}
}
+/// Read the value of a vector of bytes as a u32 value in little-endian format.
+pub fn read_u32_le(input: &[u8]) -> u32 {
+ use std::cast::transmute;
+ use std::unstable::intrinsics::to_le32;
+ assert!(input.len() == 4);
+ unsafe {
+ let tmp: *i32 = transmute(input.unsafe_ref(0));
+ return to_le32(*tmp) as u32;
+ }
+}
+
+/// Read the value of a vector of bytes as a u32 value in big-endian format.
+pub fn read_u32_be(input: &[u8]) -> u32 {
+ use std::cast::transmute;
+ use std::unstable::intrinsics::to_be32;
+ assert!(input.len() == 4);
+ unsafe {
+ let tmp: *i32 = transmute(input.unsafe_ref(0));
+ return to_be32(*tmp) as u32;
+ }
+}
+
#[cfg(target_arch = "x86")]
#[cfg(target_arch = "x86_64")]
From 055adbc88496a4d6e07023161065429387c66013 Mon Sep 17 00:00:00 2001
From: Palmer Cox
Date: Wed, 28 Aug 2013 00:22:24 -0400
Subject: [PATCH 7/9] Hmac: Implement the Mac trait and the Hmac function.
---
src/libextra/crypto/hmac.rs | 203 ++++++++++++++++++++++++++++++++++++
src/libextra/crypto/mac.rs | 95 +++++++++++++++++
src/libextra/extra.rs | 4 +
3 files changed, 302 insertions(+)
create mode 100644 src/libextra/crypto/hmac.rs
create mode 100644 src/libextra/crypto/mac.rs
diff --git a/src/libextra/crypto/hmac.rs b/src/libextra/crypto/hmac.rs
new file mode 100644
index 0000000000000..e3ba916afaf80
--- /dev/null
+++ b/src/libextra/crypto/hmac.rs
@@ -0,0 +1,203 @@
+// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this 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.
+
+/*!
+ * This module implements the Hmac function - a Message Authentication Code using a Digest.
+ */
+
+use std::vec;
+
+use digest::Digest;
+use mac::{Mac, MacResult};
+
+/**
+ * The Hmac struct represents an Hmac function - a Message Authentication Code using a Digest.
+ */
+pub struct Hmac {
+ priv digest: D,
+ priv i_key: ~[u8],
+ priv o_key: ~[u8],
+ priv finished: bool
+}
+
+fn derive_key(key: &mut [u8], mask: u8) {
+ for elem in key.mut_iter() {
+ *elem ^= mask;
+ }
+}
+
+// The key that Hmac processes must be the same as the block size of the underlying Digest. If the
+// provided key is smaller than that, we just pad it with zeros. If its larger, we hash it and then
+// pad it with zeros.
+fn expand_key(digest: &mut D, key: &[u8]) -> ~[u8] {
+ let bs = digest.block_size();
+ let mut expanded_key = vec::from_elem(bs, 0u8);
+ if key.len() <= bs {
+ vec::bytes::copy_memory(expanded_key, key, key.len());
+ for elem in expanded_key.mut_slice_from(key.len()).mut_iter() {
+ *elem = 0;
+ }
+ } else {
+ let output_size = digest.output_bytes();
+ digest.input(key);
+ digest.result(expanded_key.mut_slice_to(output_size));
+ digest.reset();
+ for elem in expanded_key.mut_slice_from(output_size).mut_iter() {
+ *elem = 0;
+ }
+ }
+ return expanded_key;
+}
+
+// Hmac uses two keys derived from the provided key - one by xoring every byte with 0x36 and another
+// with 0x5c.
+fn create_keys(digest: &mut D, key: &[u8]) -> (~[u8], ~[u8]) {
+ let mut i_key = expand_key(digest, key);
+ let mut o_key = i_key.clone();
+ derive_key(i_key, 0x36);
+ derive_key(o_key, 0x5c);
+ return (i_key, o_key);
+}
+
+impl Hmac {
+ /**
+ * Create a new Hmac instance.
+ *
+ * # Arguments
+ * * digest - The Digest to use.
+ * * key - The key to use.
+ *
+ */
+ #[experimental="EXPERIMENTAL. USE AT YOUR OWN RISK."]
+ pub fn new(mut digest: D, key: &[u8]) -> Hmac {
+ let (i_key, o_key) = create_keys(&mut digest, key);
+ digest.input(i_key);
+ return Hmac {
+ digest: digest,
+ i_key: i_key,
+ o_key: o_key,
+ finished: false
+ }
+ }
+}
+
+impl Mac for Hmac {
+ fn input(&mut self, data: &[u8]) {
+ assert!(!self.finished);
+ self.digest.input(data);
+ }
+
+ fn reset(&mut self) {
+ self.digest.reset();
+ self.digest.input(self.i_key);
+ self.finished = false;
+ }
+
+ fn result(&mut self) -> MacResult {
+ let output_size = self.digest.output_bytes();
+ let mut code = vec::from_elem(output_size, 0u8);
+
+ self.raw_result(code);
+
+ return MacResult::new_from_owned(code);
+ }
+
+ fn raw_result(&mut self, output: &mut [u8]) {
+ if !self.finished {
+ self.digest.result(output);
+
+ self.digest.reset();
+ self.digest.input(self.o_key);
+ self.digest.input(output);
+
+ self.finished = true;
+ }
+
+ self.digest.result(output);
+ }
+
+ fn output_bytes(&self) -> uint { self.digest.output_bytes() }
+}
+
+#[cfg(test)]
+mod test {
+ use mac::{Mac, MacResult};
+ use hmac::Hmac;
+ use digest::Digest;
+ use md5::Md5;
+
+ struct Test {
+ key: ~[u8],
+ data: ~[u8],
+ expected: ~[u8]
+ }
+
+ // Test vectors from: http://tools.ietf.org/html/rfc2104
+
+ fn tests() -> ~[Test] {
+ return ~[
+ Test {
+ key: ~[0x0b, ..16],
+ data: "Hi There".as_bytes().to_owned(),
+ expected: ~[
+ 0x92, 0x94, 0x72, 0x7a, 0x36, 0x38, 0xbb, 0x1c,
+ 0x13, 0xf4, 0x8e, 0xf8, 0x15, 0x8b, 0xfc, 0x9d ]
+ },
+ Test {
+ key: "Jefe".as_bytes().to_owned(),
+ data: "what do ya want for nothing?".as_bytes().to_owned(),
+ expected: ~[
+ 0x75, 0x0c, 0x78, 0x3e, 0x6a, 0xb0, 0xb5, 0x03,
+ 0xea, 0xa8, 0x6e, 0x31, 0x0a, 0x5d, 0xb7, 0x38 ]
+ },
+ Test {
+ key: ~[0xaa, ..16],
+ data: ~[0xdd, ..50],
+ expected: ~[
+ 0x56, 0xbe, 0x34, 0x52, 0x1d, 0x14, 0x4c, 0x88,
+ 0xdb, 0xb8, 0xc7, 0x33, 0xf0, 0xe8, 0xb3, 0xf6 ]
+ }
+ ];
+ }
+
+ #[test]
+ fn test_hmac_md5() {
+ let tests = tests();
+ for t in tests.iter() {
+ let mut hmac = Hmac::new(Md5::new(), t.key);
+
+ hmac.input(t.data);
+ let result = hmac.result();
+ let expected = MacResult::new(t.expected);
+ assert!(result == expected);
+
+ hmac.reset();
+
+ hmac.input(t.data);
+ let result2 = hmac.result();
+ let expected2 = MacResult::new(t.expected);
+ assert!(result2 == expected2);
+ }
+ }
+
+ #[test]
+ fn test_hmac_md5_incremental() {
+ let tests = tests();
+ for t in tests.iter() {
+ let mut hmac = Hmac::new(Md5::new(), t.key);
+ for i in range(0, t.data.len()) {
+ hmac.input(t.data.slice(i, i + 1));
+ }
+ let result = hmac.result();
+ let expected = MacResult::new(t.expected);
+ assert!(result == expected);
+ }
+ }
+}
diff --git a/src/libextra/crypto/mac.rs b/src/libextra/crypto/mac.rs
new file mode 100644
index 0000000000000..2c12561327424
--- /dev/null
+++ b/src/libextra/crypto/mac.rs
@@ -0,0 +1,95 @@
+// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this 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.
+
+/*!
+ * The mac module defines the Message Authentication Code (Mac) trait.
+ */
+
+use cryptoutil::fixed_time_eq;
+
+/**
+ * The Mac trait defines methods for a Message Authentication function.
+ */
+pub trait Mac {
+ /**
+ * Process input data.
+ *
+ * # Arguments
+ * * data - The input data to process.
+ *
+ */
+ fn input(&mut self, data: &[u8]);
+
+ /**
+ * Reset the Mac state to begin processing another input stream.
+ */
+ fn reset(&mut self);
+
+ /**
+ * Obtain the result of a Mac computation as a MacResult.
+ */
+ fn result(&mut self) -> MacResult;
+
+ /**
+ * Obtain the result of a Mac computation as [u8]. This method should be used very carefully
+ * since incorrect use of the Mac code could result in permitting a timing attack which defeats
+ * the security provided by a Mac function.
+ */
+ fn raw_result(&mut self, output: &mut [u8]);
+
+ /**
+ * Get the size of the Mac code, in bytes.
+ */
+ fn output_bytes(&self) -> uint;
+}
+
+/**
+ * A MacResult wraps a Mac code and provides a safe Eq implementation that runs in fixed time.
+ */
+pub struct MacResult {
+ priv code: ~[u8]
+}
+
+impl MacResult {
+ /**
+ * Create a new MacResult.
+ */
+ #[experimental="EXPERIMENTAL. USE AT YOUR OWN RISK."]
+ pub fn new(code: &[u8]) -> MacResult {
+ return MacResult {
+ code: code.to_owned()
+ };
+ }
+
+ /**
+ * Create a new MacResult taking ownership of the specified code value.
+ */
+ pub fn new_from_owned(code: ~[u8]) -> MacResult {
+ return MacResult {
+ code: code
+ };
+ }
+
+ /**
+ * Get the code value. Be very careful using this method, since incorrect use of the code value
+ * may permit timing attacks which defeat the security provided by the Mac function.
+ */
+ pub fn code<'s>(&'s self) -> &'s [u8] {
+ return self.code.as_slice();
+ }
+}
+
+impl Eq for MacResult {
+ fn eq(&self, x: &MacResult) -> bool {
+ let lhs = self.code();
+ let rhs = x.code();
+ return fixed_time_eq(lhs, rhs);
+ }
+}
diff --git a/src/libextra/extra.rs b/src/libextra/extra.rs
index 9c3c8636d8907..64c2d2b363cfd 100644
--- a/src/libextra/extra.rs
+++ b/src/libextra/extra.rs
@@ -70,6 +70,10 @@ pub mod treemap;
mod cryptoutil;
#[path="crypto/digest.rs"]
pub mod digest;
+#[path="crypto/hmac.rs"]
+pub mod hmac;
+#[path="crypto/mac.rs"]
+pub mod mac;
#[path="crypto/md5.rs"]
pub mod md5;
#[path="crypto/sha1.rs"]
From 490468e350ec0b8198c1caab0e107d00d90016ea Mon Sep 17 00:00:00 2001
From: Palmer Cox
Date: Sat, 31 Aug 2013 15:37:41 -0400
Subject: [PATCH 8/9] PBKDF2: Implement the PBKDF2 Key Derivation function.
---
src/libextra/crypto/pbkdf2.rs | 363 ++++++++++++++++++++++++++++++++++
src/libextra/extra.rs | 2 +
2 files changed, 365 insertions(+)
create mode 100644 src/libextra/crypto/pbkdf2.rs
diff --git a/src/libextra/crypto/pbkdf2.rs b/src/libextra/crypto/pbkdf2.rs
new file mode 100644
index 0000000000000..6e897fd01e61e
--- /dev/null
+++ b/src/libextra/crypto/pbkdf2.rs
@@ -0,0 +1,363 @@
+// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this 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.
+
+/*!
+ * This module implements the PBKDF2 Key Derivation Function as specified by
+ * http://tools.ietf.org/html/rfc2898.
+ */
+
+use std::rand::{IsaacRng, RngUtil};
+use std::vec;
+use std::vec::MutableCloneableVector;
+
+use base64;
+use base64::{FromBase64, ToBase64};
+use cryptoutil::{read_u32_be, write_u32_be, fixed_time_eq};
+use hmac::Hmac;
+use mac::Mac;
+use sha2::Sha256;
+
+// Calculate a block of the output of size equal to the output_bytes of the underlying Mac function
+// mac - The Mac function to use
+// salt - the salt value to use
+// c - the iteration count
+// idx - the 1 based index of the block
+// scratch - a temporary variable the same length as the block
+// block - the block of the output to calculate
+fn calculate_block(
+ mac: &mut M,
+ salt: &[u8],
+ c: u32,
+ idx: u32,
+ scratch: &mut [u8],
+ block: &mut [u8]) {
+ // Perform the 1st iteration. The output goes directly into block
+ mac.input(salt);
+ let mut idx_buf = [0u8, ..4];
+ write_u32_be(idx_buf, idx);
+ mac.input(idx_buf);
+ mac.raw_result(block);
+ mac.reset();
+
+ // Perform the 2nd iteration. The input comes from block and is output into scratch. scratch is
+ // then exclusive-or added into block. After all this, the input to the next step is now in
+ // scratch and block is left to just accumulate the exclusive-of sum of remaining iterations.
+ if c > 1 {
+ mac.input(block);
+ mac.raw_result(scratch);
+ mac.reset();
+ for (output, &input) in block.mut_iter().zip(scratch.iter()) {
+ *output ^= input;
+ }
+ }
+
+ // Perform all remaining iterations
+ for _ in range(2, c) {
+ mac.input(scratch);
+ mac.raw_result(scratch);
+ mac.reset();
+ for (output, &input) in block.mut_iter().zip(scratch.iter()) {
+ *output ^= input;
+ }
+ }
+}
+
+/**
+ * Execute the PBKDF2 Key Derivation Function. The Scrypt Key Derivation Function generally provides
+ * better security, so, applications that do not have a requirement to use PBKDF2 specifically
+ * should consider using that function instead.
+ *
+ * # Arguments
+ * * mac - The Pseudo Random Function to use.
+ * * salt - The salt value to use.
+ * * c - The iteration count. Users should carefully determine this value as it is the primary
+ * factor in determining the security of the derived key.
+ * * output - The output buffer to fill with the derived key value.
+ *
+ */
+#[experimental="EXPERIMENTAL. USE AT YOUR OWN RISK."]
+pub fn pbkdf2(mac: &mut M, salt: &[u8], c: u32, output: &mut [u8]) {
+ assert!(c > 0);
+
+ let os = mac.output_bytes();
+
+ // A temporary storage array needed by calculate_block. This is really only necessary if c > 1.
+ // Most users of pbkdf2 should use a value much larger than 1, so, this allocation should almost
+ // always be necessary. A big exception is Scrypt. However, this allocation is unlikely to be
+ // the bottleneck in Scrypt performance.
+ let mut scratch = vec::from_elem(os, 0u8);
+
+ let mut idx: u32 = 0;
+
+ for chunk in output.mut_chunk_iter(os) {
+ if idx == Bounded::max_value() {
+ fail!("PBKDF2 size limit exceeded.");
+ } else {
+ // The block index starts at 1. So, this is supposed to run on the first execution.
+ idx += 1;
+ }
+ if chunk.len() == os {
+ calculate_block(mac, salt, c, idx, scratch, chunk);
+ } else {
+ let mut tmp = vec::from_elem(os, 0u8);
+ calculate_block(mac, salt, c, idx, scratch, tmp);
+ chunk.copy_from(tmp);
+ }
+ }
+}
+
+/**
+ * pbkdf2_simple is a helper function that should be sufficient for the majority of cases where
+ * an application needs to use PBKDF2 to hash a password for storage. The result is a ~str that
+ * contains the parameters used as part of its encoding. The pbkdf2_check function may be used on
+ * a password to check if it is equal to a hashed value.
+ *
+ * # Format
+ *
+ * The format of the output is a modified version of the Modular Crypt Format that encodes algorithm
+ * used and iteration count. The format is indicated as "rpbkdf2" which is short for "Rust PBKF2
+ * format."
+ *
+ * $rpbkdf2$0$$$$
+ *
+ * # Arguments
+ *
+ * * password - The password to process as a str
+ * * c - The iteration count
+ *
+ */
+#[experimental="EXPERIMENTAL. USE AT YOUR OWN RISK."]
+pub fn pbkdf2_simple(password: &str, c: u32) -> ~str {
+ let mut rng = IsaacRng::new();
+
+ // 128-bit salt
+ let salt = rng.gen_bytes(16);
+
+ // 256-bit derived key
+ let mut dk = [0u8, ..32];
+
+ let mut mac = Hmac::new(Sha256::new(), password.as_bytes());
+
+ pbkdf2(&mut mac, salt, c, dk);
+
+ let mut result = ~"$rpbkdf2$0$";
+ let mut tmp = [0u8, ..4];
+ write_u32_be(tmp, c);
+ result.push_str(tmp.to_base64(base64::STANDARD));
+ result.push_char('$');
+ result.push_str(salt.to_base64(base64::STANDARD));
+ result.push_char('$');
+ result.push_str(dk.to_base64(base64::STANDARD));
+ result.push_char('$');
+
+ return result;
+}
+
+/**
+ * pbkdf2_check compares a password against the result of a previous call to pbkdf2_simple and
+ * returns true if the passed in password hashes to the same value.
+ *
+ * # Arguments
+ *
+ * * password - The password to process as a str
+ * * hashed_value - A string representing a hashed password returned by pbkdf2_simple()
+ *
+ */
+#[experimental="EXPERIMENTAL. USE AT YOUR OWN RISK."]
+pub fn pbkdf2_check(password: &str, hashed_value: &str) -> Result {
+ static ERR_STR: &'static str = "Hash is not in Rust PBKDF2 format.";
+
+ let mut iter = hashed_value.split_iter('$');
+
+ // Check that there are no characters before the first "$"
+ match iter.next() {
+ Some(x) => if x != "" { return Err(ERR_STR); },
+ None => return Err(ERR_STR)
+ }
+
+ // Check the name
+ match iter.next() {
+ Some(t) => if t != "rpbkdf2" { return Err(ERR_STR); },
+ None => return Err(ERR_STR)
+ }
+
+ // Parse format - currenlty only version 0 is supported
+ match iter.next() {
+ Some(fstr) => {
+ match fstr {
+ "0" => { }
+ _ => return Err(ERR_STR)
+ }
+ }
+ None => return Err(ERR_STR)
+ }
+
+ // Parse the iteration count
+ let c = match iter.next() {
+ Some(pstr) => match pstr.from_base64() {
+ Ok(pvec) => {
+ if pvec.len() != 4 { return Err(ERR_STR); }
+ read_u32_be(pvec)
+ }
+ Err(_) => return Err(ERR_STR)
+ },
+ None => return Err(ERR_STR)
+ };
+
+ // Salt
+ let salt = match iter.next() {
+ Some(sstr) => match sstr.from_base64() {
+ Ok(salt) => salt,
+ Err(_) => return Err(ERR_STR)
+ },
+ None => return Err(ERR_STR)
+ };
+
+ // Hashed value
+ let hash = match iter.next() {
+ Some(hstr) => match hstr.from_base64() {
+ Ok(hash) => hash,
+ Err(_) => return Err(ERR_STR)
+ },
+ None => return Err(ERR_STR)
+ };
+
+ // Make sure that the input ends with a "$"
+ match iter.next() {
+ Some(x) => if x != "" { return Err(ERR_STR); },
+ None => return Err(ERR_STR)
+ }
+
+ // Make sure there is no trailing data after the final "$"
+ match iter.next() {
+ Some(_) => return Err(ERR_STR),
+ None => { }
+ }
+
+ let mut mac = Hmac::new(Sha256::new(), password.as_bytes());
+
+ let mut output = vec::from_elem(hash.len(), 0u8);
+ pbkdf2(&mut mac, salt, c, output);
+
+ // Be careful here - its important that the comparison be done using a fixed time equality
+ // check. Otherwise an adversary that can measure how long this step takes can learn about the
+ // hashed value which would allow them to mount an offline brute force attack against the
+ // hashed password.
+ return Ok(fixed_time_eq(output, hash));
+}
+
+#[cfg(test)]
+mod test {
+ use std::vec;
+
+ use pbkdf2::{pbkdf2, pbkdf2_simple, pbkdf2_check};
+ use hmac::Hmac;
+ use sha1::Sha1;
+
+ struct Test {
+ password: ~[u8],
+ salt: ~[u8],
+ c: u32,
+ expected: ~[u8]
+ }
+
+ // Test vectors from http://tools.ietf.org/html/rfc6070. The 4th test vector is omitted because
+ // it takes too long to run.
+
+ fn tests() -> ~[Test] {
+ return ~[
+ Test {
+ password: "password".as_bytes().to_owned(),
+ salt: "salt".as_bytes().to_owned(),
+ c: 1,
+ expected: ~[
+ 0x0c, 0x60, 0xc8, 0x0f, 0x96, 0x1f, 0x0e, 0x71,
+ 0xf3, 0xa9, 0xb5, 0x24, 0xaf, 0x60, 0x12, 0x06,
+ 0x2f, 0xe0, 0x37, 0xa6 ]
+ },
+ Test {
+ password: "password".as_bytes().to_owned(),
+ salt: "salt".as_bytes().to_owned(),
+ c: 2,
+ expected: ~[
+ 0xea, 0x6c, 0x01, 0x4d, 0xc7, 0x2d, 0x6f, 0x8c,
+ 0xcd, 0x1e, 0xd9, 0x2a, 0xce, 0x1d, 0x41, 0xf0,
+ 0xd8, 0xde, 0x89, 0x57 ]
+ },
+ Test {
+ password: "password".as_bytes().to_owned(),
+ salt: "salt".as_bytes().to_owned(),
+ c: 4096,
+ expected: ~[
+ 0x4b, 0x00, 0x79, 0x01, 0xb7, 0x65, 0x48, 0x9a,
+ 0xbe, 0xad, 0x49, 0xd9, 0x26, 0xf7, 0x21, 0xd0,
+ 0x65, 0xa4, 0x29, 0xc1 ]
+ },
+ Test {
+ password: "passwordPASSWORDpassword".as_bytes().to_owned(),
+ salt: "saltSALTsaltSALTsaltSALTsaltSALTsalt".as_bytes().to_owned(),
+ c: 4096,
+ expected: ~[
+ 0x3d, 0x2e, 0xec, 0x4f, 0xe4, 0x1c, 0x84, 0x9b,
+ 0x80, 0xc8, 0xd8, 0x36, 0x62, 0xc0, 0xe4, 0x4a,
+ 0x8b, 0x29, 0x1a, 0x96, 0x4c, 0xf2, 0xf0, 0x70, 0x38 ]
+ },
+ Test {
+ password: ~[112, 97, 115, 115, 0, 119, 111, 114, 100],
+ salt: ~[115, 97, 0, 108, 116],
+ c: 4096,
+ expected: ~[
+ 0x56, 0xfa, 0x6a, 0xa7, 0x55, 0x48, 0x09, 0x9d,
+ 0xcc, 0x37, 0xd7, 0xf0, 0x34, 0x25, 0xe0, 0xc3 ]
+ }
+ ];
+ }
+
+ #[test]
+ fn test_pbkdf2() {
+ let tests = tests();
+ for t in tests.iter() {
+ let mut mac = Hmac::new(Sha1::new(), t.password);
+ let mut result = vec::from_elem(t.expected.len(), 0u8);
+ pbkdf2(&mut mac, t.salt, t.c, result);
+ assert!(result == t.expected);
+ }
+ }
+
+ #[test]
+ fn test_pbkdf2_simple() {
+ let password = "password";
+
+ let out1 = pbkdf2_simple(password, 1024);
+ let out2 = pbkdf2_simple(password, 1024);
+
+ // This just makes sure that a salt is being applied. It doesn't verify that that salt is
+ // cryptographically strong, however.
+ assert!(out1 != out2);
+
+ match pbkdf2_check(password, out1) {
+ Ok(r) => assert!(r),
+ Err(_) => fail!()
+ }
+ match pbkdf2_check(password, out2) {
+ Ok(r) => assert!(r),
+ Err(_) => fail!()
+ }
+
+ match pbkdf2_check("wrong", out1) {
+ Ok(r) => assert!(!r),
+ Err(_) => fail!()
+ }
+ match pbkdf2_check("wrong", out2) {
+ Ok(r) => assert!(!r),
+ Err(_) => fail!()
+ }
+ }
+}
diff --git a/src/libextra/extra.rs b/src/libextra/extra.rs
index 64c2d2b363cfd..00ebc06e02de5 100644
--- a/src/libextra/extra.rs
+++ b/src/libextra/extra.rs
@@ -76,6 +76,8 @@ pub mod hmac;
pub mod mac;
#[path="crypto/md5.rs"]
pub mod md5;
+#[path="crypto/pbkdf2.rs"]
+pub mod pbkdf2;
#[path="crypto/sha1.rs"]
pub mod sha1;
#[path="crypto/sha2.rs"]
From a70eced31f4094dec4a7f3cecbe71d26b741f927 Mon Sep 17 00:00:00 2001
From: Palmer Cox
Date: Mon, 2 Sep 2013 01:53:19 -0400
Subject: [PATCH 9/9] Scrypt: Implement the Scrypt Key Derivation function.
---
src/libextra/crypto/scrypt.rs | 542 ++++++++++++++++++++++++++++++++++
src/libextra/extra.rs | 2 +
2 files changed, 544 insertions(+)
create mode 100644 src/libextra/crypto/scrypt.rs
diff --git a/src/libextra/crypto/scrypt.rs b/src/libextra/crypto/scrypt.rs
new file mode 100644
index 0000000000000..bdb343ef397ce
--- /dev/null
+++ b/src/libextra/crypto/scrypt.rs
@@ -0,0 +1,542 @@
+// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this 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.
+
+/*!
+ * This module implements the Scrypt key derivation function as specified in [1].
+ *
+ * # References
+ * [1] - C. Percival. Stronger Key Derivation Via Sequential Memory-Hard Functions.
+ * http://www.tarsnap.com/scrypt/scrypt.pdf
+ */
+
+use std::num::CheckedNumCast;
+use std::rand::{IsaacRng, RngUtil};
+use std::sys::size_of;
+use std::vec;
+use std::vec::MutableCloneableVector;
+
+use base64;
+use base64::{FromBase64, ToBase64};
+use cryptoutil::{fixed_time_eq, read_u32_le, read_u32v_le, write_u32_le};
+use hmac::Hmac;
+use pbkdf2::pbkdf2;
+use sha2::Sha256;
+
+// The salsa20/8 core function.
+fn salsa20_8(input: &[u8], output: &mut [u8]) {
+ fn rot(a: u32, b: uint) -> u32 {
+ return (a << b) | (a >> (32 - b));
+ }
+
+ let mut x = [0u32, ..16];
+ read_u32v_le(x, input);
+
+ let rounds = 8;
+
+ macro_rules! run_round (
+ ($($set_idx:expr, $idx_a:expr, $idx_b:expr, $rot:expr);*) => { {
+ $( x[$set_idx] ^= rot(x[$idx_a] + x[$idx_b], $rot); )*
+ } }
+ )
+
+ do (rounds / 2).times() {
+ run_round!(
+ 0x4, 0x0, 0xc, 7;
+ 0x8, 0x4, 0x0, 9;
+ 0xc, 0x8, 0x4, 13;
+ 0x0, 0xc, 0x8, 18;
+ 0x9, 0x5, 0x1, 7;
+ 0xd, 0x9, 0x5, 9;
+ 0x1, 0xd, 0x9, 13;
+ 0x5, 0x1, 0xd, 18;
+ 0xe, 0xa, 0x6, 7;
+ 0x2, 0xe, 0xa, 9;
+ 0x6, 0x2, 0xe, 13;
+ 0xa, 0x6, 0x2, 18;
+ 0x3, 0xf, 0xb, 7;
+ 0x7, 0x3, 0xf, 9;
+ 0xb, 0x7, 0x3, 13;
+ 0xf, 0xb, 0x7, 18;
+ 0x1, 0x0, 0x3, 7;
+ 0x2, 0x1, 0x0, 9;
+ 0x3, 0x2, 0x1, 13;
+ 0x0, 0x3, 0x2, 18;
+ 0x6, 0x5, 0x4, 7;
+ 0x7, 0x6, 0x5, 9;
+ 0x4, 0x7, 0x6, 13;
+ 0x5, 0x4, 0x7, 18;
+ 0xb, 0xa, 0x9, 7;
+ 0x8, 0xb, 0xa, 9;
+ 0x9, 0x8, 0xb, 13;
+ 0xa, 0x9, 0x8, 18;
+ 0xc, 0xf, 0xe, 7;
+ 0xd, 0xc, 0xf, 9;
+ 0xe, 0xd, 0xc, 13;
+ 0xf, 0xe, 0xd, 18
+ )
+ }
+
+ for i in range(0u, 16) {
+ write_u32_le(
+ output.mut_slice(i * 4, (i + 1) * 4),
+ x[i] + read_u32_le(input.slice(i * 4, (i + 1) * 4)));
+ }
+}
+
+fn xor(x: &[u8], y: &[u8], output: &mut [u8]) {
+ for ((out, &x_i), &y_i) in output.mut_iter().zip(x.iter()).zip(y.iter()) {
+ *out = x_i ^ y_i;
+ }
+}
+
+// Execute the BlockMix operation
+// input - the input vector. The length must be a multiple of 128.
+// output - the output vector. Must be the same length as input.
+fn scrypt_block_mix(input: &[u8], output: &mut [u8]) {
+ let mut x = [0u8, ..64];
+ x.copy_from(input.slice_from(input.len() - 64));
+
+ let mut t = [0u8, ..64];
+
+
+ for (i, chunk) in input.chunk_iter(64).enumerate() {
+ xor(x, chunk, t);
+ salsa20_8(t, x);
+ let pos = if i % 2 == 0 { (i / 2) * 64 } else { (i / 2) * 64 + input.len() / 2 };
+ output.mut_slice(pos, pos + 64).copy_from(x);
+ }
+}
+
+// Execute the ROMix operation in-place.
+// b - the data to operate on
+// v - a temporary variable to store the vector V
+// t - a temporary variable to store the result of the xor
+// n - the scrypt parameter N
+fn scrypt_ro_mix(b: &mut [u8], v: &mut [u8], t: &mut [u8], n: uint) {
+ fn integerify(x: &[u8], n: uint) -> uint {
+ // n is a power of 2, so n - 1 gives us a bitmask that we can use to perform a calculation
+ // mod n using a simple bitwise and.
+ let mask = n - 1;
+ // This cast is safe since we're going to get the value mod n (which is a power of 2), so we
+ // don't have to care about truncating any of the high bits off
+ let result = (read_u32_le(x.slice(x.len() - 64, x.len() - 60)) as uint) & mask;
+ return result;
+ }
+
+ let len = b.len();
+
+ for chunk in v.mut_chunk_iter(len) {
+ chunk.copy_from(b);
+ scrypt_block_mix(chunk, b);
+ }
+
+ do n.times() {
+ let j = integerify(b, n);
+ xor(b, v.slice(j * len, (j + 1) * len), t);
+ scrypt_block_mix(t, b);
+ }
+}
+
+/**
+ * The Scrypt parameter values.
+ */
+#[deriving(Clone)]
+pub struct ScryptParams {
+ priv log_n: u8,
+ priv r: u32,
+ priv p: u32
+}
+
+impl ScryptParams {
+ /**
+ * Create a new instance of ScryptParams.
+ *
+ * # Arguments
+ *
+ * * log_n - The log2 of the Scrypt parameter N
+ * * r - The Scrypt parameter r
+ * * p - The Scrypt parameter p
+ *
+ */
+ pub fn new(log_n: u8, r: u32, p: u32) -> ScryptParams {
+ assert!(r > 0);
+ assert!(p > 0);
+ assert!(log_n > 0);
+ assert!((log_n as uint) < size_of::() * 8);
+
+ let n = (1u32 << log_n);
+
+ let rp = match r.checked_mul(&p) {
+ Some(x) => x,
+ None => fail!("Invalid Scrypt parameters.")
+ };
+
+ let rp128 = match rp.checked_mul(&128) {
+ Some(x) => x,
+ None => fail!("Invalid Scrypt parameters.")
+ };
+
+ let nr128 = match n.checked_mul(&r) {
+ Some(x) => match x.checked_mul(&128) {
+ Some(y) => y,
+ None => fail!("Invalid Scrypt parameters.")
+ },
+ None => fail!("Invalid Scrypt parameters.")
+ };
+
+ // Check that we won't attempt to allocate too much memory or get an integer overflow.
+ // These checks guarantee that we can cast these values safely to uints and perform all the
+ // math that we need to on them. This guarantees that the values r and p can both fit within
+ // a uint as well.
+ assert!(rp128.checked_to_uint().is_some());
+ assert!(nr128.checked_to_uint().is_some());
+
+ // This check required by Scrypt:
+ // check: n < 2^(128 * r / 8)
+ // r * 16 won't overflow since rp128 didn't above
+ assert!((log_n as u32) < r * 16);
+
+ // This check required by Scrypt:
+ // check: p <= ((2^32-1) * 32) / (128 * r)
+ // It takes a bit of re-arranging to get the check above into this form, but, it is indeed
+ // the same.
+ assert!(rp < 0x40000000);
+
+ return ScryptParams {
+ log_n: log_n,
+ r: r,
+ p: p
+ };
+ }
+}
+
+/**
+ * The scrypt key derivation function.
+ *
+ * # Arguments
+ *
+ * * password - The password to process as a byte vector
+ * * salt - The salt value to use as a byte vector
+ * * params - The ScryptParams to use
+ * * output - The resulting derived key is returned in this byte vector.
+ *
+ */
+#[experimental="EXPERIMENTAL. USE AT YOUR OWN RISK."]
+pub fn scrypt(password: &[u8], salt: &[u8], params: &ScryptParams, output: &mut [u8]) {
+ // This check required by Scrypt:
+ // check output.len() > 0 && output.len() <= (2^32 - 1) * 32
+ assert!(output.len() > 0);
+ assert!(output.len() / 32 <= 0xffffffff);
+
+ // The checks in the ScryptParams constructor guarantee that the following is safe:
+ let n = 1u << params.log_n;
+ let r = params.r as uint;
+ let p = params.p as uint;
+
+ let mut mac = Hmac::new(Sha256::new(), password);
+
+ let mut b = vec::from_elem(p * r * 128, 0u8);
+ pbkdf2(&mut mac, salt, 1, b);
+
+ let mut v = vec::from_elem(n * r * 128, 0u8);
+ let mut t = vec::from_elem(r * 128, 0u8);
+
+ for chunk in b.mut_chunk_iter(r * 128) {
+ scrypt_ro_mix(chunk, v, t, n);
+ }
+
+ pbkdf2(&mut mac, b, 1, output);
+}
+
+/**
+ * scrypt_simple is a helper function that should be sufficient for the majority of cases where
+ * an application needs to use Scrypt to hash a password for storage. The result is a ~str that
+ * contains the parameters used as part of its encoding. The scrypt_check function may be used on
+ * a password to check if it is equal to a hashed value.
+ *
+ * # Format
+ *
+ * The format of the output is a modified version of the Modular Crypt Format that encodes algorithm
+ * used and the parameter values. If all parameter values can each fit within a single byte, a
+ * compact format is used (format 0). However, if any value cannot, an expanded format where the r
+ * and p parameters are encoded using 4 bytes (format 1) is used. Both formats use a 128-bit salt
+ * and a 256-bit hash. The format is indicated as "rscrypt" which is short for "Rust Scrypt format."
+ *
+ * $rscrypt$$$$$
+ *
+ * # Arguments
+ *
+ * * password - The password to process as a str
+ * * params - The ScryptParams to use
+ *
+ */
+#[experimental="EXPERIMENTAL. USE AT YOUR OWN RISK."]
+pub fn scrypt_simple(password: &str, params: &ScryptParams) -> ~str {
+ let mut rng = IsaacRng::new();
+
+ // 128-bit salt
+ let salt = rng.gen_bytes(16);
+
+ // 256-bit derived key
+ let mut dk = [0u8, ..32];
+
+ scrypt(password.as_bytes(), salt, params, dk);
+
+ let mut result = ~"$rscrypt$";
+ if params.r < 256 && params.p < 256 {
+ result.push_str("0$");
+ let mut tmp = [0u8, ..3];
+ tmp[0] = params.log_n;
+ tmp[1] = params.r as u8;
+ tmp[2] = params.p as u8;
+ result.push_str(tmp.to_base64(base64::STANDARD));
+ } else {
+ result.push_str("1$");
+ let mut tmp = [0u8, ..9];
+ tmp[0] = params.log_n;
+ write_u32_le(tmp.mut_slice(1, 5), params.r);
+ write_u32_le(tmp.mut_slice(5, 9), params.p);
+ result.push_str(tmp.to_base64(base64::STANDARD));
+ }
+ result.push_char('$');
+ result.push_str(salt.to_base64(base64::STANDARD));
+ result.push_char('$');
+ result.push_str(dk.to_base64(base64::STANDARD));
+ result.push_char('$');
+
+ return result;
+}
+
+/**
+ * scrypt_check compares a password against the result of a previous call to scrypt_simple and
+ * returns true if the passed in password hashes to the same value.
+ *
+ * # Arguments
+ *
+ * * password - The password to process as a str
+ * * hashed_value - A string representing a hashed password returned by scrypt_simple()
+ *
+ */
+#[experimental="EXPERIMENTAL. USE AT YOUR OWN RISK."]
+pub fn scrypt_check(password: &str, hashed_value: &str) -> Result {
+ static ERR_STR: &'static str = "Hash is not in Rust Scrypt format.";
+
+ let mut iter = hashed_value.split_iter('$');
+
+ // Check that there are no characters before the first "$"
+ match iter.next() {
+ Some(x) => if x != "" { return Err(ERR_STR); },
+ None => return Err(ERR_STR)
+ }
+
+ // Check the name
+ match iter.next() {
+ Some(t) => if t != "rscrypt" { return Err(ERR_STR); },
+ None => return Err(ERR_STR)
+ }
+
+ // Parse format - currenlty only version 0 (compact) and 1 (expanded) are supported
+ let params: ScryptParams;
+ match iter.next() {
+ Some(fstr) => {
+ // Parse the parameters - the size of them depends on the if we are using the compact or
+ // expanded format
+ let pvec = match iter.next() {
+ Some(pstr) => match pstr.from_base64() {
+ Ok(x) => x,
+ Err(_) => return Err(ERR_STR)
+ },
+ None => return Err(ERR_STR)
+ };
+ match fstr {
+ "0" => {
+ if pvec.len() != 3 { return Err(ERR_STR); }
+ let log_n = pvec[0] as u8;
+ let r = pvec[1] as u32;
+ let p = pvec[2] as u32;
+ params = ScryptParams::new(log_n, r, p);
+ }
+ "1" => {
+ if pvec.len() != 9 { return Err(ERR_STR); }
+ let log_n = pvec[0];
+ let mut pval = [0u32, ..2];
+ read_u32v_le(pval, pvec.slice(1, 9));
+ params = ScryptParams::new(log_n, pval[0], pval[1]);
+ }
+ _ => return Err(ERR_STR)
+ }
+ }
+ None => return Err(ERR_STR)
+ }
+
+ // Salt
+ let salt = match iter.next() {
+ Some(sstr) => match sstr.from_base64() {
+ Ok(salt) => salt,
+ Err(_) => return Err(ERR_STR)
+ },
+ None => return Err(ERR_STR)
+ };
+
+ // Hashed value
+ let hash = match iter.next() {
+ Some(hstr) => match hstr.from_base64() {
+ Ok(hash) => hash,
+ Err(_) => return Err(ERR_STR)
+ },
+ None => return Err(ERR_STR)
+ };
+
+ // Make sure that the input ends with a "$"
+ match iter.next() {
+ Some(x) => if x != "" { return Err(ERR_STR); },
+ None => return Err(ERR_STR)
+ }
+
+ // Make sure there is no trailing data after the final "$"
+ match iter.next() {
+ Some(_) => return Err(ERR_STR),
+ None => { }
+ }
+
+ let mut output = vec::from_elem(hash.len(), 0u8);
+ scrypt(password.as_bytes(), salt, ¶ms, output);
+
+ // Be careful here - its important that the comparison be done using a fixed time equality
+ // check. Otherwise an adversary that can measure how long this step takes can learn about the
+ // hashed value which would allow them to mount an offline brute force attack against the
+ // hashed password.
+ return Ok(fixed_time_eq(output, hash));
+}
+
+#[cfg(test)]
+#[cfg(test)]
+mod test {
+ use std::vec;
+
+ use scrypt::{scrypt, scrypt_simple, scrypt_check, ScryptParams};
+
+ struct Test {
+ password: ~str,
+ salt: ~str,
+ log_n: u8,
+ r: u32,
+ p: u32,
+ expected: ~[u8]
+ }
+
+ // Test vectors from [1]. The last test vector is omitted because it takes too long to run.
+
+ fn tests() -> ~[Test] {
+ return ~[
+ Test {
+ password: ~"",
+ salt: ~"",
+ log_n: 4,
+ r: 1,
+ p: 1,
+ expected: ~[
+ 0x77, 0xd6, 0x57, 0x62, 0x38, 0x65, 0x7b, 0x20,
+ 0x3b, 0x19, 0xca, 0x42, 0xc1, 0x8a, 0x04, 0x97,
+ 0xf1, 0x6b, 0x48, 0x44, 0xe3, 0x07, 0x4a, 0xe8,
+ 0xdf, 0xdf, 0xfa, 0x3f, 0xed, 0xe2, 0x14, 0x42,
+ 0xfc, 0xd0, 0x06, 0x9d, 0xed, 0x09, 0x48, 0xf8,
+ 0x32, 0x6a, 0x75, 0x3a, 0x0f, 0xc8, 0x1f, 0x17,
+ 0xe8, 0xd3, 0xe0, 0xfb, 0x2e, 0x0d, 0x36, 0x28,
+ 0xcf, 0x35, 0xe2, 0x0c, 0x38, 0xd1, 0x89, 0x06 ]
+ },
+ Test {
+ password: ~"password",
+ salt: ~"NaCl",
+ log_n: 10,
+ r: 8,
+ p: 16,
+ expected: ~[
+ 0xfd, 0xba, 0xbe, 0x1c, 0x9d, 0x34, 0x72, 0x00,
+ 0x78, 0x56, 0xe7, 0x19, 0x0d, 0x01, 0xe9, 0xfe,
+ 0x7c, 0x6a, 0xd7, 0xcb, 0xc8, 0x23, 0x78, 0x30,
+ 0xe7, 0x73, 0x76, 0x63, 0x4b, 0x37, 0x31, 0x62,
+ 0x2e, 0xaf, 0x30, 0xd9, 0x2e, 0x22, 0xa3, 0x88,
+ 0x6f, 0xf1, 0x09, 0x27, 0x9d, 0x98, 0x30, 0xda,
+ 0xc7, 0x27, 0xaf, 0xb9, 0x4a, 0x83, 0xee, 0x6d,
+ 0x83, 0x60, 0xcb, 0xdf, 0xa2, 0xcc, 0x06, 0x40 ]
+ },
+ Test {
+ password: ~"pleaseletmein",
+ salt: ~"SodiumChloride",
+ log_n: 14,
+ r: 8,
+ p: 1,
+ expected: ~[
+ 0x70, 0x23, 0xbd, 0xcb, 0x3a, 0xfd, 0x73, 0x48,
+ 0x46, 0x1c, 0x06, 0xcd, 0x81, 0xfd, 0x38, 0xeb,
+ 0xfd, 0xa8, 0xfb, 0xba, 0x90, 0x4f, 0x8e, 0x3e,
+ 0xa9, 0xb5, 0x43, 0xf6, 0x54, 0x5d, 0xa1, 0xf2,
+ 0xd5, 0x43, 0x29, 0x55, 0x61, 0x3f, 0x0f, 0xcf,
+ 0x62, 0xd4, 0x97, 0x05, 0x24, 0x2a, 0x9a, 0xf9,
+ 0xe6, 0x1e, 0x85, 0xdc, 0x0d, 0x65, 0x1e, 0x40,
+ 0xdf, 0xcf, 0x01, 0x7b, 0x45, 0x57, 0x58, 0x87 ]
+ },
+ ];
+ }
+
+ #[test]
+ fn test_scrypt() {
+ let tests = tests();
+ for t in tests.iter() {
+ let mut result = vec::from_elem(t.expected.len(), 0u8);
+ let params = ScryptParams::new(t.log_n, t.r, t.p);
+ scrypt(t.password.as_bytes(), t.salt.as_bytes(), ¶ms, result);
+ assert!(result == t.expected);
+ }
+ }
+
+ fn test_scrypt_simple(log_n: u8, r: u32, p: u32) {
+ let password = "password";
+
+ let params = ScryptParams::new(log_n, r, p);
+ let out1 = scrypt_simple(password, ¶ms);
+ let out2 = scrypt_simple(password, ¶ms);
+
+ // This just makes sure that a salt is being applied. It doesn't verify that that salt is
+ // cryptographically strong, however.
+ assert!(out1 != out2);
+
+ match scrypt_check(password, out1) {
+ Ok(r) => assert!(r),
+ Err(_) => fail!()
+ }
+ match scrypt_check(password, out2) {
+ Ok(r) => assert!(r),
+ Err(_) => fail!()
+ }
+
+ match scrypt_check("wrong", out1) {
+ Ok(r) => assert!(!r),
+ Err(_) => fail!()
+ }
+ match scrypt_check("wrong", out2) {
+ Ok(r) => assert!(!r),
+ Err(_) => fail!()
+ }
+ }
+
+ #[test]
+ fn test_scrypt_simple_compact() {
+ // These parameters are intentionally very weak - the goal is to make the test run quickly!
+ test_scrypt_simple(7, 8, 1);
+ }
+
+ #[test]
+ fn test_scrypt_simple_expanded() {
+ // These parameters are intentionally very weak - the goal is to make the test run quickly!
+ test_scrypt_simple(3, 1, 256);
+ }
+}
diff --git a/src/libextra/extra.rs b/src/libextra/extra.rs
index 00ebc06e02de5..6a51a528bf76f 100644
--- a/src/libextra/extra.rs
+++ b/src/libextra/extra.rs
@@ -78,6 +78,8 @@ pub mod mac;
pub mod md5;
#[path="crypto/pbkdf2.rs"]
pub mod pbkdf2;
+#[path="crypto/scrypt.rs"]
+pub mod scrypt;
#[path="crypto/sha1.rs"]
pub mod sha1;
#[path="crypto/sha2.rs"]