diff --git a/src/libextra/crypto/cryptoutil.rs b/src/libextra/crypto/cryptoutil.rs index 9516517d9f7be..2d5cb397a5b49 100644 --- a/src/libextra/crypto/cryptoutil.rs +++ b/src/libextra/crypto/cryptoutil.rs @@ -96,6 +96,112 @@ 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")] +#[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 @@ -351,7 +457,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 +529,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)); + } } diff --git a/src/libextra/crypto/digest.rs b/src/libextra/crypto/digest.rs index d2d6b540cff4c..534ca7ee7dec8 100644 --- a/src/libextra/crypto/digest.rs +++ b/src/libextra/crypto/digest.rs @@ -45,6 +45,18 @@ 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. + */ + fn block_size(&self) -> uint; + /** * Convenience function that feeds a string into a digest. * 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/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/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/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/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] = [ diff --git a/src/libextra/extra.rs b/src/libextra/extra.rs index 9c3c8636d8907..6a51a528bf76f 100644 --- a/src/libextra/extra.rs +++ b/src/libextra/extra.rs @@ -70,8 +70,16 @@ 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/pbkdf2.rs"] +pub mod pbkdf2; +#[path="crypto/scrypt.rs"] +pub mod scrypt; #[path="crypto/sha1.rs"] pub mod sha1; #[path="crypto/sha2.rs"] 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()); + } } 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];