Crypto: Add overflow checking addition functions.

Added functions to cryptoutil.rs that perform an addition after shifting
the 2nd parameter by a specified constant. These function fail!() if integer
overflow will result. Updated the Sha2 implementation to use these functions.
This commit is contained in:
Palmer Cox 2013-07-30 00:07:51 -04:00
parent 281b79525b
commit 4e7b0ee3cd
2 changed files with 71 additions and 55 deletions

View file

@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::num::One;
use std::vec::bytes::{MutableByteVector, copy_memory};
@ -68,6 +69,56 @@ pub fn read_u32v_be(dst: &mut[u32], input: &[u8]) {
}
/// Returns true if adding the two parameters will result in integer overflow
pub fn will_add_overflow<T: Int + Unsigned>(x: T, y: T) -> bool {
// This doesn't handle negative values! Don't copy this code elsewhere without considering if
// negative values are important to you!
let max: T = Bounded::max_value();
return x > max - y;
}
/// Shifts the second parameter and then adds it to the first. fails!() if there would be unsigned
/// integer overflow.
pub fn shift_add_check_overflow<T: Int + Unsigned + Clone>(x: T, mut y: T, shift: T) -> T {
if y.leading_zeros() < shift {
fail!("Could not add values - integer overflow.");
}
y = y << shift;
if will_add_overflow(x.clone(), y.clone()) {
fail!("Could not add values - integer overflow.");
}
return x + y;
}
/// Shifts the second parameter and then adds it to the first, which is a tuple where the first
/// element is the high order value. fails!() if there would be unsigned integer overflow.
pub fn shift_add_check_overflow_tuple
<T: Int + Unsigned + Clone>
(x: (T, T), mut y: T, shift: T) -> (T, T) {
if y.leading_zeros() < shift {
fail!("Could not add values - integer overflow.");
}
y = y << shift;
match x {
(hi, low) => {
let one: T = One::one();
if will_add_overflow(low.clone(), y.clone()) {
if will_add_overflow(hi.clone(), one.clone()) {
fail!("Could not add values - integer overflow.");
} else {
return (hi + one, low + y);
}
} else {
return (hi, low + y);
}
}
}
}
/// A FixedBuffer, likes its name implies, is a fixed size buffer. When the buffer becomes full, it
/// must be processed. The input() method takes care of processing and then clearing the buffer
/// automatically. However, other methods do not and require the caller to process the buffer. Any

View file

@ -10,8 +10,8 @@
use std::uint;
use cryptoutil::{write_u64_be, write_u32_be, read_u64v_be, read_u32v_be, FixedBuffer,
FixedBuffer128, FixedBuffer64, StandardPadding};
use cryptoutil::{write_u64_be, write_u32_be, read_u64v_be, read_u32v_be, shift_add_check_overflow,
shift_add_check_overflow_tuple, FixedBuffer, FixedBuffer128, FixedBuffer64, StandardPadding};
use digest::Digest;
@ -34,47 +34,6 @@ macro_rules! sha2_round(
)
// BitCounter is a specialized structure intended simply for counting the
// number of bits that have been processed by the SHA-2 512 family of functions.
// It does very little overflow checking since such checking is not necessary
// for how it is used. A more generic structure would have to do this checking.
// So, don't copy this structure and use it elsewhere!
struct BitCounter {
high_bit_count: u64,
low_byte_count: u64
}
impl BitCounter {
fn new() -> BitCounter {
return BitCounter {
high_bit_count: 0,
low_byte_count: 0
};
}
fn add_bytes(&mut self, bytes: uint) {
self.low_byte_count += bytes as u64;
if(self.low_byte_count > 0x1fffffffffffffffu64) {
self.high_bit_count += (self.low_byte_count >> 61);
self.low_byte_count &= 0x1fffffffffffffffu64;
}
}
fn reset(&mut self) {
self.low_byte_count = 0;
self.high_bit_count = 0;
}
fn get_low_bit_count(&self) -> u64 {
self.low_byte_count << 3
}
fn get_high_bit_count(&self) -> u64 {
self.high_bit_count
}
}
// A structure that represents that state of a digest computation for the SHA-2 512 family of digest
// functions
struct Engine512State {
@ -223,7 +182,7 @@ static K64: [u64, ..80] = [
// A structure that keeps track of the state of the Sha-512 operation and contains the logic
// necessary to perform the final calculations.
struct Engine512 {
bit_counter: BitCounter,
length_bits: (u64, u64),
buffer: FixedBuffer128,
state: Engine512State,
finished: bool,
@ -232,7 +191,7 @@ struct Engine512 {
impl Engine512 {
fn new(h: &[u64, ..8]) -> Engine512 {
return Engine512 {
bit_counter: BitCounter::new(),
length_bits: (0, 0),
buffer: FixedBuffer128::new(),
state: Engine512State::new(h),
finished: false
@ -240,7 +199,7 @@ impl Engine512 {
}
fn reset(&mut self, h: &[u64, ..8]) {
self.bit_counter.reset();
self.length_bits = (0, 0);
self.buffer.reset();
self.state.reset(h);
self.finished = false;
@ -248,7 +207,8 @@ impl Engine512 {
fn input(&mut self, input: &[u8]) {
assert!(!self.finished)
self.bit_counter.add_bytes(input.len());
// Assumes that input.len() can be converted to u64 without overflow
self.length_bits = shift_add_check_overflow_tuple(self.length_bits, input.len() as u64, 3);
self.buffer.input(input, |input: &[u8]| { self.state.process_block(input) });
}
@ -258,8 +218,12 @@ impl Engine512 {
}
self.buffer.standard_padding(16, |input: &[u8]| { self.state.process_block(input) });
write_u64_be(self.buffer.next(8), self.bit_counter.get_high_bit_count());
write_u64_be(self.buffer.next(8), self.bit_counter.get_low_bit_count());
match self.length_bits {
(hi, low) => {
write_u64_be(self.buffer.next(8), hi);
write_u64_be(self.buffer.next(8), low);
}
}
self.state.process_block(self.buffer.full_buffer());
self.finished = true;
@ -608,7 +572,7 @@ static K32: [u32, ..64] = [
// A structure that keeps track of the state of the Sha-256 operation and contains the logic
// necessary to perform the final calculations.
struct Engine256 {
length: u64,
length_bits: u64,
buffer: FixedBuffer64,
state: Engine256State,
finished: bool,
@ -617,7 +581,7 @@ struct Engine256 {
impl Engine256 {
fn new(h: &[u32, ..8]) -> Engine256 {
return Engine256 {
length: 0,
length_bits: 0,
buffer: FixedBuffer64::new(),
state: Engine256State::new(h),
finished: false
@ -625,7 +589,7 @@ impl Engine256 {
}
fn reset(&mut self, h: &[u32, ..8]) {
self.length = 0;
self.length_bits = 0;
self.buffer.reset();
self.state.reset(h);
self.finished = false;
@ -633,7 +597,8 @@ impl Engine256 {
fn input(&mut self, input: &[u8]) {
assert!(!self.finished)
self.length += input.len() as u64;
// Assumes that input.len() can be converted to u64 without overflow
self.length_bits = shift_add_check_overflow(self.length_bits, input.len() as u64, 3);
self.buffer.input(input, |input: &[u8]| { self.state.process_block(input) });
}
@ -643,8 +608,8 @@ impl Engine256 {
}
self.buffer.standard_padding(8, |input: &[u8]| { self.state.process_block(input) });
write_u32_be(self.buffer.next(4), (self.length >> 29) as u32 );
write_u32_be(self.buffer.next(4), (self.length << 3) as u32);
write_u32_be(self.buffer.next(4), (self.length_bits >> 32) as u32 );
write_u32_be(self.buffer.next(4), self.length_bits as u32);
self.state.process_block(self.buffer.full_buffer());
self.finished = true;