Skip to content

Commit 0b95463

Browse files
author
Palmer Cox
committed
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.
1 parent 3dabbcb commit 0b95463

File tree

2 files changed

+71
-55
lines changed

2 files changed

+71
-55
lines changed

src/libextra/crypto/cryptoutil.rs

+51
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use std::num::One;
1112
use std::vec::bytes::{MutableByteVector, copy_memory};
1213

1314

@@ -68,6 +69,56 @@ pub fn read_u32v_be(dst: &mut[u32], in: &[u8]) {
6869
}
6970

7071

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+
71122
/// A FixedBuffer, likes its name implies, is a fixed size buffer. When the buffer becomes full, it
72123
/// must be processed. The input() method takes care of processing and then clearing the buffer
73124
/// automatically. However, other methods do not and require the caller to process the buffer. Any

src/libextra/crypto/sha2.rs

+20-55
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010

1111
use std::uint;
1212

13-
use cryptoutil::{write_u64_be, write_u32_be, read_u64v_be, read_u32v_be, FixedBuffer,
14-
FixedBuffer128, FixedBuffer64, StandardPadding};
13+
use cryptoutil::{write_u64_be, write_u32_be, read_u64v_be, read_u32v_be, shift_add_check_overflow,
14+
shift_add_check_overflow_tuple, FixedBuffer, FixedBuffer128, FixedBuffer64, StandardPadding};
1515
use digest::Digest;
1616

1717

@@ -34,47 +34,6 @@ macro_rules! sha2_round(
3434
)
3535

3636

37-
// BitCounter is a specialized structure intended simply for counting the
38-
// number of bits that have been processed by the SHA-2 512 family of functions.
39-
// It does very little overflow checking since such checking is not necessary
40-
// for how it is used. A more generic structure would have to do this checking.
41-
// So, don't copy this structure and use it elsewhere!
42-
struct BitCounter {
43-
high_bit_count: u64,
44-
low_byte_count: u64
45-
}
46-
47-
impl BitCounter {
48-
fn new() -> BitCounter {
49-
return BitCounter {
50-
high_bit_count: 0,
51-
low_byte_count: 0
52-
};
53-
}
54-
55-
fn add_bytes(&mut self, bytes: uint) {
56-
self.low_byte_count += bytes as u64;
57-
if(self.low_byte_count > 0x1fffffffffffffffu64) {
58-
self.high_bit_count += (self.low_byte_count >> 61);
59-
self.low_byte_count &= 0x1fffffffffffffffu64;
60-
}
61-
}
62-
63-
fn reset(&mut self) {
64-
self.low_byte_count = 0;
65-
self.high_bit_count = 0;
66-
}
67-
68-
fn get_low_bit_count(&self) -> u64 {
69-
self.low_byte_count << 3
70-
}
71-
72-
fn get_high_bit_count(&self) -> u64 {
73-
self.high_bit_count
74-
}
75-
}
76-
77-
7837
// A structure that represents that state of a digest computation for the SHA-2 512 family of digest
7938
// functions
8039
struct Engine512State {
@@ -223,7 +182,7 @@ static K64: [u64, ..80] = [
223182
// A structure that keeps track of the state of the Sha-512 operation and contains the logic
224183
// necessary to perform the final calculations.
225184
struct Engine512 {
226-
bit_counter: BitCounter,
185+
length_bits: (u64, u64),
227186
buffer: FixedBuffer128,
228187
state: Engine512State,
229188
finished: bool,
@@ -232,23 +191,24 @@ struct Engine512 {
232191
impl Engine512 {
233192
fn new(h: &[u64, ..8]) -> Engine512 {
234193
return Engine512 {
235-
bit_counter: BitCounter::new(),
194+
length_bits: (0, 0),
236195
buffer: FixedBuffer128::new(),
237196
state: Engine512State::new(h),
238197
finished: false
239198
}
240199
}
241200

242201
fn reset(&mut self, h: &[u64, ..8]) {
243-
self.bit_counter.reset();
202+
self.length_bits = (0, 0);
244203
self.buffer.reset();
245204
self.state.reset(h);
246205
self.finished = false;
247206
}
248207

249208
fn input(&mut self, in: &[u8]) {
250209
assert!(!self.finished)
251-
self.bit_counter.add_bytes(in.len());
210+
// Assumes that in.len() can be converted to u64 without overflow
211+
self.length_bits = shift_add_check_overflow_tuple(self.length_bits, in.len() as u64, 3);
252212
self.buffer.input(in, |in: &[u8]| { self.state.process_block(in) });
253213
}
254214

@@ -258,8 +218,12 @@ impl Engine512 {
258218
}
259219

260220
self.buffer.standard_padding(16, |in: &[u8]| { self.state.process_block(in) });
261-
write_u64_be(self.buffer.next(8), self.bit_counter.get_high_bit_count());
262-
write_u64_be(self.buffer.next(8), self.bit_counter.get_low_bit_count());
221+
match self.length_bits {
222+
(hi, low) => {
223+
write_u64_be(self.buffer.next(8), hi);
224+
write_u64_be(self.buffer.next(8), low);
225+
}
226+
}
263227
self.state.process_block(self.buffer.full_buffer());
264228

265229
self.finished = true;
@@ -608,7 +572,7 @@ static K32: [u32, ..64] = [
608572
// A structure that keeps track of the state of the Sha-256 operation and contains the logic
609573
// necessary to perform the final calculations.
610574
struct Engine256 {
611-
length: u64,
575+
length_bits: u64,
612576
buffer: FixedBuffer64,
613577
state: Engine256State,
614578
finished: bool,
@@ -617,23 +581,24 @@ struct Engine256 {
617581
impl Engine256 {
618582
fn new(h: &[u32, ..8]) -> Engine256 {
619583
return Engine256 {
620-
length: 0,
584+
length_bits: 0,
621585
buffer: FixedBuffer64::new(),
622586
state: Engine256State::new(h),
623587
finished: false
624588
}
625589
}
626590

627591
fn reset(&mut self, h: &[u32, ..8]) {
628-
self.length = 0;
592+
self.length_bits = 0;
629593
self.buffer.reset();
630594
self.state.reset(h);
631595
self.finished = false;
632596
}
633597

634598
fn input(&mut self, in: &[u8]) {
635599
assert!(!self.finished)
636-
self.length += in.len() as u64;
600+
// Assumes that in.len() can be converted to u64 without overflow
601+
self.length_bits = shift_add_check_overflow(self.length_bits, in.len() as u64, 3);
637602
self.buffer.input(in, |in: &[u8]| { self.state.process_block(in) });
638603
}
639604

@@ -643,8 +608,8 @@ impl Engine256 {
643608
}
644609

645610
self.buffer.standard_padding(8, |in: &[u8]| { self.state.process_block(in) });
646-
write_u32_be(self.buffer.next(4), (self.length >> 29) as u32 );
647-
write_u32_be(self.buffer.next(4), (self.length << 3) as u32);
611+
write_u32_be(self.buffer.next(4), (self.length_bits >> 32) as u32 );
612+
write_u32_be(self.buffer.next(4), self.length_bits as u32);
648613
self.state.process_block(self.buffer.full_buffer());
649614

650615
self.finished = true;

0 commit comments

Comments
 (0)