Skip to content

Commit efd6eaf

Browse files
committed
auto merge of #8174 : DaGenix/rust/digest-improvements, r=brson
Same content as #8097, but bors had an issue with that pull request. Opening a new one.
2 parents 3ddc72f + 1252472 commit efd6eaf

File tree

6 files changed

+912
-850
lines changed

6 files changed

+912
-850
lines changed

src/libextra/crypto/cryptoutil.rs

+327
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use std::num::One;
12+
use std::vec::bytes::{MutableByteVector, copy_memory};
13+
14+
15+
/// Write a u64 into a vector, which must be 8 bytes long. The value is written in big-endian
16+
/// format.
17+
pub fn write_u64_be(dst: &mut[u8], input: u64) {
18+
use std::cast::transmute;
19+
use std::unstable::intrinsics::to_be64;
20+
assert!(dst.len() == 8);
21+
unsafe {
22+
let x: *mut i64 = transmute(dst.unsafe_mut_ref(0));
23+
*x = to_be64(input as i64);
24+
}
25+
}
26+
27+
/// Write a u32 into a vector, which must be 4 bytes long. The value is written in big-endian
28+
/// format.
29+
pub fn write_u32_be(dst: &mut[u8], input: u32) {
30+
use std::cast::transmute;
31+
use std::unstable::intrinsics::to_be32;
32+
assert!(dst.len() == 4);
33+
unsafe {
34+
let x: *mut i32 = transmute(dst.unsafe_mut_ref(0));
35+
*x = to_be32(input as i32);
36+
}
37+
}
38+
39+
/// Read a vector of bytes into a vector of u64s. The values are read in big-endian format.
40+
pub fn read_u64v_be(dst: &mut[u64], input: &[u8]) {
41+
use std::cast::transmute;
42+
use std::unstable::intrinsics::to_be64;
43+
assert!(dst.len() * 8 == input.len());
44+
unsafe {
45+
let mut x: *mut i64 = transmute(dst.unsafe_mut_ref(0));
46+
let mut y: *i64 = transmute(input.unsafe_ref(0));
47+
do dst.len().times() {
48+
*x = to_be64(*y);
49+
x = x.offset(1);
50+
y = y.offset(1);
51+
}
52+
}
53+
}
54+
55+
/// Read a vector of bytes into a vector of u32s. The values are read in big-endian format.
56+
pub fn read_u32v_be(dst: &mut[u32], input: &[u8]) {
57+
use std::cast::transmute;
58+
use std::unstable::intrinsics::to_be32;
59+
assert!(dst.len() * 4 == input.len());
60+
unsafe {
61+
let mut x: *mut i32 = transmute(dst.unsafe_mut_ref(0));
62+
let mut y: *i32 = transmute(input.unsafe_ref(0));
63+
do dst.len().times() {
64+
*x = to_be32(*y);
65+
x = x.offset(1);
66+
y = y.offset(1);
67+
}
68+
}
69+
}
70+
71+
72+
/// Returns true if adding the two parameters will result in integer overflow
73+
pub fn will_add_overflow<T: Int + Unsigned>(x: T, y: T) -> bool {
74+
// This doesn't handle negative values! Don't copy this code elsewhere without considering if
75+
// negative values are important to you!
76+
let max: T = Bounded::max_value();
77+
return x > max - y;
78+
}
79+
80+
/// Shifts the second parameter and then adds it to the first. fails!() if there would be unsigned
81+
/// integer overflow.
82+
pub fn shift_add_check_overflow<T: Int + Unsigned + Clone>(x: T, mut y: T, shift: T) -> T {
83+
if y.leading_zeros() < shift {
84+
fail!("Could not add values - integer overflow.");
85+
}
86+
y = y << shift;
87+
88+
if will_add_overflow(x.clone(), y.clone()) {
89+
fail!("Could not add values - integer overflow.");
90+
}
91+
92+
return x + y;
93+
}
94+
95+
/// Shifts the second parameter and then adds it to the first, which is a tuple where the first
96+
/// element is the high order value. fails!() if there would be unsigned integer overflow.
97+
pub fn shift_add_check_overflow_tuple
98+
<T: Int + Unsigned + Clone>
99+
(x: (T, T), mut y: T, shift: T) -> (T, T) {
100+
if y.leading_zeros() < shift {
101+
fail!("Could not add values - integer overflow.");
102+
}
103+
y = y << shift;
104+
105+
match x {
106+
(hi, low) => {
107+
let one: T = One::one();
108+
if will_add_overflow(low.clone(), y.clone()) {
109+
if will_add_overflow(hi.clone(), one.clone()) {
110+
fail!("Could not add values - integer overflow.");
111+
} else {
112+
return (hi + one, low + y);
113+
}
114+
} else {
115+
return (hi, low + y);
116+
}
117+
}
118+
}
119+
}
120+
121+
122+
/// A FixedBuffer, likes its name implies, is a fixed size buffer. When the buffer becomes full, it
123+
/// must be processed. The input() method takes care of processing and then clearing the buffer
124+
/// automatically. However, other methods do not and require the caller to process the buffer. Any
125+
/// method that modifies the buffer directory or provides the caller with bytes that can be modifies
126+
/// results in those bytes being marked as used by the buffer.
127+
pub trait FixedBuffer {
128+
/// Input a vector of bytes. If the buffer becomes full, proccess it with the provided
129+
/// function and then clear the buffer.
130+
fn input(&mut self, input: &[u8], func: &fn(&[u8]));
131+
132+
/// Reset the buffer.
133+
fn reset(&mut self);
134+
135+
/// Zero the buffer up until the specified index. The buffer position currently must not be
136+
/// greater than that index.
137+
fn zero_until(&mut self, idx: uint);
138+
139+
/// Get a slice of the buffer of the specified size. There must be at least that many bytes
140+
/// remaining in the buffer.
141+
fn next<'s>(&'s mut self, len: uint) -> &'s mut [u8];
142+
143+
/// Get the current buffer. The buffer must already be full. This clears the buffer as well.
144+
fn full_buffer<'s>(&'s mut self) -> &'s [u8];
145+
146+
/// Get the current position of the buffer.
147+
fn position(&self) -> uint;
148+
149+
/// Get the number of bytes remaining in the buffer until it is full.
150+
fn remaining(&self) -> uint;
151+
152+
/// Get the size of the buffer
153+
fn size(&self) -> uint;
154+
}
155+
156+
macro_rules! impl_fixed_buffer( ($name:ident, $size:expr) => (
157+
impl FixedBuffer for $name {
158+
fn input(&mut self, input: &[u8], func: &fn(&[u8])) {
159+
let mut i = 0;
160+
161+
// FIXME: #6304 - This local variable shouldn't be necessary.
162+
let size = $size;
163+
164+
// If there is already data in the buffer, copy as much as we can into it and process
165+
// the data if the buffer becomes full.
166+
if self.buffer_idx != 0 {
167+
let buffer_remaining = size - self.buffer_idx;
168+
if input.len() >= buffer_remaining {
169+
copy_memory(
170+
self.buffer.mut_slice(self.buffer_idx, size),
171+
input.slice_to(buffer_remaining),
172+
buffer_remaining);
173+
self.buffer_idx = 0;
174+
func(self.buffer);
175+
i += buffer_remaining;
176+
} else {
177+
copy_memory(
178+
self.buffer.mut_slice(self.buffer_idx, self.buffer_idx + input.len()),
179+
input,
180+
input.len());
181+
self.buffer_idx += input.len();
182+
return;
183+
}
184+
}
185+
186+
// While we have at least a full buffer size chunks's worth of data, process that data
187+
// without copying it into the buffer
188+
while input.len() - i >= size {
189+
func(input.slice(i, i + size));
190+
i += size;
191+
}
192+
193+
// Copy any input data into the buffer. At this point in the method, the ammount of
194+
// data left in the input vector will be less than the buffer size and the buffer will
195+
// be empty.
196+
let input_remaining = input.len() - i;
197+
copy_memory(
198+
self.buffer.mut_slice(0, input_remaining),
199+
input.slice_from(i),
200+
input.len() - i);
201+
self.buffer_idx += input_remaining;
202+
}
203+
204+
fn reset(&mut self) {
205+
self.buffer_idx = 0;
206+
}
207+
208+
fn zero_until(&mut self, idx: uint) {
209+
assert!(idx >= self.buffer_idx);
210+
self.buffer.mut_slice(self.buffer_idx, idx).set_memory(0);
211+
self.buffer_idx = idx;
212+
}
213+
214+
fn next<'s>(&'s mut self, len: uint) -> &'s mut [u8] {
215+
self.buffer_idx += len;
216+
return self.buffer.mut_slice(self.buffer_idx - len, self.buffer_idx);
217+
}
218+
219+
fn full_buffer<'s>(&'s mut self) -> &'s [u8] {
220+
assert!(self.buffer_idx == $size);
221+
self.buffer_idx = 0;
222+
return self.buffer.slice_to($size);
223+
}
224+
225+
fn position(&self) -> uint { self.buffer_idx }
226+
227+
fn remaining(&self) -> uint { $size - self.buffer_idx }
228+
229+
fn size(&self) -> uint { $size }
230+
}
231+
))
232+
233+
234+
/// A fixed size buffer of 64 bytes useful for cryptographic operations.
235+
pub struct FixedBuffer64 {
236+
priv buffer: [u8, ..64],
237+
priv buffer_idx: uint,
238+
}
239+
240+
impl FixedBuffer64 {
241+
/// Create a new buffer
242+
pub fn new() -> FixedBuffer64 {
243+
return FixedBuffer64 {
244+
buffer: [0u8, ..64],
245+
buffer_idx: 0
246+
};
247+
}
248+
}
249+
250+
impl_fixed_buffer!(FixedBuffer64, 64)
251+
252+
/// A fixed size buffer of 128 bytes useful for cryptographic operations.
253+
pub struct FixedBuffer128 {
254+
priv buffer: [u8, ..128],
255+
priv buffer_idx: uint,
256+
}
257+
258+
impl FixedBuffer128 {
259+
/// Create a new buffer
260+
pub fn new() -> FixedBuffer128 {
261+
return FixedBuffer128 {
262+
buffer: [0u8, ..128],
263+
buffer_idx: 0
264+
};
265+
}
266+
}
267+
268+
impl_fixed_buffer!(FixedBuffer128, 128)
269+
270+
271+
/// The StandardPadding trait adds a method useful for various hash algorithms to a FixedBuffer
272+
/// struct.
273+
pub trait StandardPadding {
274+
/// Add standard padding to the buffer. The buffer must not be full when this method is called
275+
/// and is guaranteed to have exactly rem remaining bytes when it returns. If there are not at
276+
/// least rem bytes available, the buffer will be zero padded, processed, cleared, and then
277+
/// filled with zeros again until only rem bytes are remaining.
278+
fn standard_padding(&mut self, rem: uint, func: &fn(&[u8]));
279+
}
280+
281+
impl <T: FixedBuffer> StandardPadding for T {
282+
fn standard_padding(&mut self, rem: uint, func: &fn(&[u8])) {
283+
let size = self.size();
284+
285+
self.next(1)[0] = 128;
286+
287+
if self.remaining() < rem {
288+
self.zero_until(size);
289+
func(self.full_buffer());
290+
}
291+
292+
self.zero_until(size - rem);
293+
}
294+
}
295+
296+
297+
#[cfg(test)]
298+
mod test {
299+
use std::rand::IsaacRng;
300+
use std::rand::RngUtil;
301+
use std::vec;
302+
303+
use digest::Digest;
304+
305+
/// Feed 1,000,000 'a's into the digest with varying input sizes and check that the result is
306+
/// correct.
307+
pub fn test_digest_1million_random<D: Digest>(digest: &mut D, blocksize: uint, expected: &str) {
308+
let total_size = 1000000;
309+
let buffer = vec::from_elem(blocksize * 2, 'a' as u8);
310+
let mut rng = IsaacRng::new_unseeded();
311+
let mut count = 0;
312+
313+
digest.reset();
314+
315+
while count < total_size {
316+
let next: uint = rng.gen_uint_range(0, 2 * blocksize + 1);
317+
let remaining = total_size - count;
318+
let size = if next > remaining { remaining } else { next };
319+
digest.input(buffer.slice_to(size));
320+
count += size;
321+
}
322+
323+
let result_str = digest.result_str();
324+
325+
assert!(expected == result_str);
326+
}
327+
}

0 commit comments

Comments
 (0)