Skip to content

Commit 6386f88

Browse files
author
Palmer Cox
committed
Crypto: update checked addition functions to use CheckedAdd intrinsic.
The shift_add_check_overflow and shift_add_check_overflow_tuple functions are re-written to be more efficient and to make use of the CheckedAdd instrinsic instead of manually checking for integer overflow. * The invokation leading_zeros() is removed and replaced with simple integer comparison. The leading_zeros() method results in a ctpop LLVM instruction and it may not be efficient on all architectures; integer comparisons, however, are efficient on just about any architecture. * The methods lose the ability for the caller to specify a particular shift value - that functionality wasn't being used and removing it allows for the code to be simplified. * Finally, the methods are renamed to add_bytes_to_bits and add_bytes_to_bits_tuple to reflect their very specific purposes.
1 parent c707065 commit 6386f88

File tree

3 files changed

+65
-42
lines changed

3 files changed

+65
-42
lines changed

src/libextra/crypto/cryptoutil.rs

Lines changed: 59 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +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;
11+
use std::num::{One, Zero, CheckedAdd};
1212
use std::vec::bytes::{MutableByteVector, copy_memory};
1313

1414

@@ -97,50 +97,73 @@ pub fn read_u32v_le(dst: &mut[u32], input: &[u8]) {
9797
}
9898

9999

100-
/// Returns true if adding the two parameters will result in integer overflow
101-
pub fn will_add_overflow<T: Int + Unsigned>(x: T, y: T) -> bool {
102-
// This doesn't handle negative values! Don't copy this code elsewhere without considering if
103-
// negative values are important to you!
104-
let max: T = Bounded::max_value();
105-
return x > max - y;
100+
trait ToBits {
101+
/// Convert the value in bytes to the number of bits, a tuple where the 1st item is the
102+
/// high-order value and the 2nd item is the low order value.
103+
fn to_bits(self) -> (Self, Self);
106104
}
107105

108-
/// Shifts the second parameter and then adds it to the first. fails!() if there would be unsigned
109-
/// integer overflow.
110-
pub fn shift_add_check_overflow<T: Int + Unsigned + Clone>(x: T, mut y: T, shift: T) -> T {
111-
if y.leading_zeros() < shift {
112-
fail!("Could not add values - integer overflow.");
106+
impl ToBits for u64 {
107+
fn to_bits(self) -> (u64, u64) {
108+
return (self >> 61, self << 3);
113109
}
114-
y = y << shift;
110+
}
115111

116-
if will_add_overflow(x.clone(), y.clone()) {
117-
fail!("Could not add values - integer overflow.");
118-
}
112+
/// Adds the specified number of bytes to the bit count. fail!() if this would cause numeric
113+
/// overflow.
114+
pub fn add_bytes_to_bits<T: Int + CheckedAdd + ToBits>(bits: T, bytes: T) -> T {
115+
let (new_high_bits, new_low_bits) = bytes.to_bits();
119116

120-
return x + y;
121-
}
117+
if new_high_bits > Zero::zero() {
118+
fail!("Numeric overflow occured.")
119+
}
122120

123-
/// Shifts the second parameter and then adds it to the first, which is a tuple where the first
124-
/// element is the high order value. fails!() if there would be unsigned integer overflow.
125-
pub fn shift_add_check_overflow_tuple
126-
<T: Int + Unsigned + Clone>
127-
(x: (T, T), mut y: T, shift: T) -> (T, T) {
128-
if y.leading_zeros() < shift {
129-
fail!("Could not add values - integer overflow.");
121+
match bits.checked_add(&new_low_bits) {
122+
Some(x) => return x,
123+
None => fail!("Numeric overflow occured.")
130124
}
131-
y = y << shift;
125+
}
132126

133-
match x {
134-
(hi, low) => {
135-
let one: T = One::one();
136-
if will_add_overflow(low.clone(), y.clone()) {
137-
if will_add_overflow(hi.clone(), one.clone()) {
138-
fail!("Could not add values - integer overflow.");
139-
} else {
140-
return (hi + one, low + y);
141-
}
127+
/// Adds the specified number of bytes to the bit count, which is a tuple where the first element is
128+
/// the high order value. fail!() if this would cause numeric overflow.
129+
pub fn add_bytes_to_bits_tuple
130+
<T: Int + Unsigned + CheckedAdd + ToBits>
131+
(bits: (T, T), bytes: T) -> (T, T) {
132+
let (new_high_bits, new_low_bits) = bytes.to_bits();
133+
let (hi, low) = bits;
134+
135+
// Add the low order value - if there is no overflow, then add the high order values
136+
// If the addition of the low order values causes overflow, add one to the high order values
137+
// before adding them.
138+
match low.checked_add(&new_low_bits) {
139+
Some(x) => {
140+
if new_high_bits == Zero::zero() {
141+
// This is the fast path - every other alternative will rarely occur in practice
142+
// considering how large an input would need to be for those paths to be used.
143+
return (hi, x);
142144
} else {
143-
return (hi, low + y);
145+
match hi.checked_add(&new_high_bits) {
146+
Some(y) => return (y, x),
147+
None => fail!("Numeric overflow occured.")
148+
}
149+
}
150+
},
151+
None => {
152+
let one: T = One::one();
153+
let z = match new_high_bits.checked_add(&one) {
154+
Some(w) => w,
155+
None => fail!("Numeric overflow occured.")
156+
};
157+
match hi.checked_add(&z) {
158+
// This re-executes the addition that was already performed earlier when overflow
159+
// occured, this time allowing the overflow to happen. Technically, this could be
160+
// avoided by using the checked add intrinsic directly, but that involves using
161+
// unsafe code and is not really worthwhile considering how infrequently code will
162+
// run in practice. This is the reason that this function requires that the type T
163+
// be Unsigned - overflow is not defined for Signed types. This function could be
164+
// implemented for signed types as well if that were needed.
165+
Some(y) => return (y, low + new_low_bits),
166+
None => fail!("Numeric overflow occured.")
144167
}
145168
}
146169
}

src/libextra/crypto/sha1.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
*/
2424

2525

26-
use cryptoutil::{write_u32_be, read_u32v_be, shift_add_check_overflow, FixedBuffer, FixedBuffer64,
26+
use cryptoutil::{write_u32_be, read_u32v_be, add_bytes_to_bits, FixedBuffer, FixedBuffer64,
2727
StandardPadding};
2828
use digest::Digest;
2929

@@ -52,7 +52,7 @@ pub struct Sha1 {
5252
fn add_input(st: &mut Sha1, msg: &[u8]) {
5353
assert!((!st.computed));
5454
// Assumes that msg.len() can be converted to u64 without overflow
55-
st.length_bits = shift_add_check_overflow(st.length_bits, msg.len() as u64, 3);
55+
st.length_bits = add_bytes_to_bits(st.length_bits, msg.len() as u64);
5656
st.buffer.input(msg, |d: &[u8]| { process_msg_block(d, &mut st.h); });
5757
}
5858

src/libextra/crypto/sha2.rs

Lines changed: 4 additions & 4 deletions
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, shift_add_check_overflow,
14-
shift_add_check_overflow_tuple, FixedBuffer, FixedBuffer128, FixedBuffer64, StandardPadding};
13+
use cryptoutil::{write_u64_be, write_u32_be, read_u64v_be, read_u32v_be, add_bytes_to_bits,
14+
add_bytes_to_bits_tuple, FixedBuffer, FixedBuffer128, FixedBuffer64, StandardPadding};
1515
use digest::Digest;
1616

1717

@@ -210,7 +210,7 @@ impl Engine512 {
210210
fn input(&mut self, input: &[u8]) {
211211
assert!(!self.finished)
212212
// Assumes that input.len() can be converted to u64 without overflow
213-
self.length_bits = shift_add_check_overflow_tuple(self.length_bits, input.len() as u64, 3);
213+
self.length_bits = add_bytes_to_bits_tuple(self.length_bits, input.len() as u64);
214214
self.buffer.input(input, |input: &[u8]| { self.state.process_block(input) });
215215
}
216216

@@ -602,7 +602,7 @@ impl Engine256 {
602602
fn input(&mut self, input: &[u8]) {
603603
assert!(!self.finished)
604604
// Assumes that input.len() can be converted to u64 without overflow
605-
self.length_bits = shift_add_check_overflow(self.length_bits, input.len() as u64, 3);
605+
self.length_bits = add_bytes_to_bits(self.length_bits, input.len() as u64);
606606
self.buffer.input(input, |input: &[u8]| { self.state.process_block(input) });
607607
}
608608

0 commit comments

Comments
 (0)