Skip to content

Commit 654c536

Browse files
author
Palmer Cox
committed
Sha2: Create cryptoutil.rs and re-write the Sha2 module to make use of it.
There are 2 main pieces of functionality in cryptoutil.rs: * A set of unsafe function for efficiently reading and writing u32 and u64 values. All of these functions are fairly easy to audit to confirm that they do what they are supposed to. * A FixedBuffer struct. This struct keeps track of input data until there is enough of it to execute the a function on it which expects a fixed block of data. The Sha2 module was rewritten to take advantage of the new functions in cryptoutil as well as FixedBuffer. The result is that the duplicate code for maintaining a buffer of input data is removed from the Sha512 and Sha256 implementation. Additionally, the FixedBuffer code is much more efficient than the previous code was.
1 parent 3cac628 commit 654c536

File tree

3 files changed

+551
-451
lines changed

3 files changed

+551
-451
lines changed

src/libextra/crypto/cryptoutil.rs

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
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::vec::bytes::{MutableByteVector, copy_memory};
12+
13+
14+
/// Write a u64 into a vector, which must be 8 bytes long. The value is written in big-endian
15+
/// format.
16+
pub fn write_u64_be(dst: &mut[u8], input: u64) {
17+
use std::cast::transmute;
18+
use std::unstable::intrinsics::to_be64;
19+
assert!(dst.len() == 8);
20+
unsafe {
21+
let x: *mut i64 = transmute(dst.unsafe_mut_ref(0));
22+
*x = to_be64(input as i64);
23+
}
24+
}
25+
26+
/// Write a u32 into a vector, which must be 4 bytes long. The value is written in big-endian
27+
/// format.
28+
pub fn write_u32_be(dst: &mut[u8], input: u32) {
29+
use std::cast::transmute;
30+
use std::unstable::intrinsics::to_be32;
31+
assert!(dst.len() == 4);
32+
unsafe {
33+
let x: *mut i32 = transmute(dst.unsafe_mut_ref(0));
34+
*x = to_be32(input as i32);
35+
}
36+
}
37+
38+
/// Read a vector of bytes into a vector of u64s. The values are read in big-endian format.
39+
pub fn read_u64v_be(dst: &mut[u64], input: &[u8]) {
40+
use std::cast::transmute;
41+
use std::unstable::intrinsics::to_be64;
42+
assert!(dst.len() * 8 == input.len());
43+
unsafe {
44+
let mut x: *mut i64 = transmute(dst.unsafe_mut_ref(0));
45+
let mut y: *i64 = transmute(input.unsafe_ref(0));
46+
do dst.len().times() {
47+
*x = to_be64(*y);
48+
x = x.offset(1);
49+
y = y.offset(1);
50+
}
51+
}
52+
}
53+
54+
/// Read a vector of bytes into a vector of u32s. The values are read in big-endian format.
55+
pub fn read_u32v_be(dst: &mut[u32], input: &[u8]) {
56+
use std::cast::transmute;
57+
use std::unstable::intrinsics::to_be32;
58+
assert!(dst.len() * 4 == input.len());
59+
unsafe {
60+
let mut x: *mut i32 = transmute(dst.unsafe_mut_ref(0));
61+
let mut y: *i32 = transmute(input.unsafe_ref(0));
62+
do dst.len().times() {
63+
*x = to_be32(*y);
64+
x = x.offset(1);
65+
y = y.offset(1);
66+
}
67+
}
68+
}
69+
70+
71+
/// A FixedBuffer, likes its name implies, is a fixed size buffer. When the buffer becomes full, it
72+
/// must be processed. The input() method takes care of processing and then clearing the buffer
73+
/// automatically. However, other methods do not and require the caller to process the buffer. Any
74+
/// method that modifies the buffer directory or provides the caller with bytes that can be modifies
75+
/// results in those bytes being marked as used by the buffer.
76+
pub trait FixedBuffer {
77+
/// Input a vector of bytes. If the buffer becomes full, proccess it with the provided
78+
/// function and then clear the buffer.
79+
fn input(&mut self, input: &[u8], func: &fn(&[u8]));
80+
81+
/// Reset the buffer.
82+
fn reset(&mut self);
83+
84+
/// Zero the buffer up until the specified index. The buffer position currently must not be
85+
/// greater than that index.
86+
fn zero_until(&mut self, idx: uint);
87+
88+
/// Get a slice of the buffer of the specified size. There must be at least that many bytes
89+
/// remaining in the buffer.
90+
fn next<'s>(&'s mut self, len: uint) -> &'s mut [u8];
91+
92+
/// Get the current buffer. The buffer must already be full. This clears the buffer as well.
93+
fn full_buffer<'s>(&'s mut self) -> &'s [u8];
94+
95+
/// Get the current position of the buffer.
96+
fn position(&self) -> uint;
97+
98+
/// Get the number of bytes remaining in the buffer until it is full.
99+
fn remaining(&self) -> uint;
100+
101+
/// Get the size of the buffer
102+
fn size(&self) -> uint;
103+
}
104+
105+
macro_rules! impl_fixed_buffer( ($name:ident, $size:expr) => (
106+
impl FixedBuffer for $name {
107+
fn input(&mut self, input: &[u8], func: &fn(&[u8])) {
108+
let mut i = 0;
109+
110+
// FIXME: #6304 - This local variable shouldn't be necessary.
111+
let size = $size;
112+
113+
// If there is already data in the buffer, copy as much as we can into it and process
114+
// the data if the buffer becomes full.
115+
if self.buffer_idx != 0 {
116+
let buffer_remaining = size - self.buffer_idx;
117+
if input.len() >= buffer_remaining {
118+
copy_memory(
119+
self.buffer.mut_slice(self.buffer_idx, size),
120+
input.slice_to(buffer_remaining),
121+
buffer_remaining);
122+
self.buffer_idx = 0;
123+
func(self.buffer);
124+
i += buffer_remaining;
125+
} else {
126+
copy_memory(
127+
self.buffer.mut_slice(self.buffer_idx, self.buffer_idx + input.len()),
128+
input,
129+
input.len());
130+
self.buffer_idx += input.len();
131+
return;
132+
}
133+
}
134+
135+
// While we have at least a full buffer size chunks's worth of data, process that data
136+
// without copying it into the buffer
137+
while input.len() - i >= size {
138+
func(input.slice(i, i + size));
139+
i += size;
140+
}
141+
142+
// Copy any input data into the buffer. At this point in the method, the ammount of
143+
// data left in the input vector will be less than the buffer size and the buffer will
144+
// be empty.
145+
let input_remaining = input.len() - i;
146+
copy_memory(
147+
self.buffer.mut_slice(0, input_remaining),
148+
input.slice_from(i),
149+
input.len() - i);
150+
self.buffer_idx += input_remaining;
151+
}
152+
153+
fn reset(&mut self) {
154+
self.buffer_idx = 0;
155+
}
156+
157+
fn zero_until(&mut self, idx: uint) {
158+
assert!(idx >= self.buffer_idx);
159+
self.buffer.mut_slice(self.buffer_idx, idx).set_memory(0);
160+
self.buffer_idx = idx;
161+
}
162+
163+
fn next<'s>(&'s mut self, len: uint) -> &'s mut [u8] {
164+
self.buffer_idx += len;
165+
return self.buffer.mut_slice(self.buffer_idx - len, self.buffer_idx);
166+
}
167+
168+
fn full_buffer<'s>(&'s mut self) -> &'s [u8] {
169+
assert!(self.buffer_idx == $size);
170+
self.buffer_idx = 0;
171+
return self.buffer.slice_to($size);
172+
}
173+
174+
fn position(&self) -> uint { self.buffer_idx }
175+
176+
fn remaining(&self) -> uint { $size - self.buffer_idx }
177+
178+
fn size(&self) -> uint { $size }
179+
}
180+
))
181+
182+
183+
/// A fixed size buffer of 64 bytes useful for cryptographic operations.
184+
pub struct FixedBuffer64 {
185+
priv buffer: [u8, ..64],
186+
priv buffer_idx: uint,
187+
}
188+
189+
impl FixedBuffer64 {
190+
/// Create a new buffer
191+
pub fn new() -> FixedBuffer64 {
192+
return FixedBuffer64 {
193+
buffer: [0u8, ..64],
194+
buffer_idx: 0
195+
};
196+
}
197+
}
198+
199+
impl_fixed_buffer!(FixedBuffer64, 64)
200+
201+
/// A fixed size buffer of 128 bytes useful for cryptographic operations.
202+
pub struct FixedBuffer128 {
203+
priv buffer: [u8, ..128],
204+
priv buffer_idx: uint,
205+
}
206+
207+
impl FixedBuffer128 {
208+
/// Create a new buffer
209+
pub fn new() -> FixedBuffer128 {
210+
return FixedBuffer128 {
211+
buffer: [0u8, ..128],
212+
buffer_idx: 0
213+
};
214+
}
215+
}
216+
217+
impl_fixed_buffer!(FixedBuffer128, 128)
218+
219+
220+
/// The StandardPadding trait adds a method useful for various hash algorithms to a FixedBuffer
221+
/// struct.
222+
pub trait StandardPadding {
223+
/// Add standard padding to the buffer. The buffer must not be full when this method is called
224+
/// and is guaranteed to have exactly rem remaining bytes when it returns. If there are not at
225+
/// least rem bytes available, the buffer will be zero padded, processed, cleared, and then
226+
/// filled with zeros again until only rem bytes are remaining.
227+
fn standard_padding(&mut self, rem: uint, func: &fn(&[u8]));
228+
}
229+
230+
impl <T: FixedBuffer> StandardPadding for T {
231+
fn standard_padding(&mut self, rem: uint, func: &fn(&[u8])) {
232+
let size = self.size();
233+
234+
self.next(1)[0] = 128;
235+
236+
if self.remaining() < rem {
237+
self.zero_until(size);
238+
func(self.full_buffer());
239+
}
240+
241+
self.zero_until(size - rem);
242+
}
243+
}

0 commit comments

Comments
 (0)