#![cfg_attr(not(feature = "use_heap"), allow(dead_code))]
use {polyfill, c, error, untrusted};
#[cfg(target_pointer_width = "64")] pub type Limb = u64;
#[cfg(target_pointer_width = "32")] pub type Limb = u32;
#[cfg(target_pointer_width = "64")] pub const LIMB_BITS: usize = 64;
#[cfg(target_pointer_width = "32")] pub const LIMB_BITS: usize = 32;
#[cfg(target_pointer_width = "64")]
#[allow(trivial_numeric_casts)] #[derive(Debug, PartialEq)]
#[repr(u64)]
pub enum LimbMask {
True = 0xffff_ffff_ffff_ffff,
False = 0,
}
#[cfg(target_pointer_width = "32")]
#[allow(trivial_numeric_casts)] #[derive(Debug, PartialEq)]
#[repr(u32)]
pub enum LimbMask {
True = 0xffff_ffff,
False = 0,
}
pub const LIMB_BYTES: usize = (LIMB_BITS + 7) / 8;
#[cfg(all(any(test, feature = "rsa_signing"), target_pointer_width = "64"))]
#[inline]
pub fn limbs_as_bytes<'a>(src: &'a [Limb]) -> &'a [u8] {
polyfill::slice::u64_as_u8(src)
}
#[cfg(all(feature = "rsa_signing", target_pointer_width = "64"))]
#[inline]
pub fn limbs_as_bytes_mut<'a>(src: &'a mut [Limb]) -> &'a mut [u8] {
polyfill::slice::u64_as_u8_mut(src)
}
#[cfg(all(any(test, feature = "rsa_signing"), target_pointer_width = "32"))]
#[inline]
pub fn limbs_as_bytes<'a>(src: &'a [Limb]) -> &'a [u8] {
polyfill::slice::u32_as_u8(src)
}
#[cfg(all(feature = "rsa_signing", target_pointer_width = "32"))]
#[inline]
pub fn limbs_as_bytes_mut<'a>(src: &'a mut [Limb]) -> &'a mut [u8] {
polyfill::slice::u32_as_u8_mut(src)
}
#[inline]
pub fn limbs_less_than_limbs_consttime(a: &[Limb], b: &[Limb]) -> LimbMask {
assert_eq!(a.len(), b.len());
unsafe { LIMBS_less_than(a.as_ptr(), b.as_ptr(), b.len()) }
}
#[inline]
pub fn limbs_less_than_limbs_vartime(a: &[Limb], b: &[Limb]) -> bool {
limbs_less_than_limbs_consttime(a, b) == LimbMask::True
}
#[inline]
pub fn limbs_are_zero_constant_time(limbs: &[Limb]) -> LimbMask {
unsafe { LIMBS_are_zero(limbs.as_ptr(), limbs.len()) }
}
#[inline]
pub fn limbs_reduce_once_constant_time(r: &mut [Limb], m: &[Limb]) {
assert_eq!(r.len(), m.len());
unsafe { LIMBS_reduce_once(r.as_mut_ptr(), m.as_ptr(), m.len()) };
}
#[derive(Clone, Copy, PartialEq)]
pub enum AllowZero {
No,
Yes
}
pub fn parse_big_endian_in_range_partially_reduced_and_pad_consttime(
input: untrusted::Input, allow_zero: AllowZero, m: &[Limb],
result: &mut [Limb]) -> Result<(), error::Unspecified> {
try!(parse_big_endian_and_pad_consttime(input, result));
limbs_reduce_once_constant_time(result, m);
if allow_zero != AllowZero::Yes {
if limbs_are_zero_constant_time(&result) != LimbMask::False {
return Err(error::Unspecified);
}
}
Ok(())
}
pub fn parse_big_endian_in_range_and_pad_consttime(
input: untrusted::Input, allow_zero: AllowZero, max_exclusive: &[Limb],
result: &mut [Limb]) -> Result<(), error::Unspecified> {
try!(parse_big_endian_and_pad_consttime(input, result));
if limbs_less_than_limbs_consttime(&result, max_exclusive) !=
LimbMask::True {
return Err(error::Unspecified);
}
if allow_zero != AllowZero::Yes {
if limbs_are_zero_constant_time(&result) != LimbMask::False {
return Err(error::Unspecified);
}
}
Ok(())
}
pub fn parse_big_endian_and_pad_consttime(
input: untrusted::Input, result: &mut [Limb])
-> Result<(), error::Unspecified> {
if input.is_empty() {
return Err(error::Unspecified);
}
let mut bytes_in_current_limb = input.len() % LIMB_BYTES;
if bytes_in_current_limb == 0 {
bytes_in_current_limb = LIMB_BYTES;
}
let num_encoded_limbs =
(input.len() / LIMB_BYTES) +
(if bytes_in_current_limb == LIMB_BYTES { 0 } else { 1 });
if num_encoded_limbs > result.len() {
return Err(error::Unspecified);
}
for r in &mut result[..] {
*r = 0;
}
try!(input.read_all(error::Unspecified, |input| {
for i in 0..num_encoded_limbs {
let mut limb: Limb = 0;
for _ in 0..bytes_in_current_limb {
let b = try!(input.read_byte());
limb = (limb << 8) | (b as Limb);
}
result[num_encoded_limbs - i - 1] = limb;
bytes_in_current_limb = LIMB_BYTES;
}
Ok(())
}));
Ok(())
}
pub fn big_endian_from_limbs_padded(limbs: &[Limb], out: &mut [u8]) {
let num_limbs = limbs.len();
let (dest, to_zero) =
out.split_at_mut(num_limbs * LIMB_BYTES); for i in 0..num_limbs {
let mut limb = limbs[i];
for j in 0..LIMB_BYTES {
dest[((num_limbs - i - 1) * LIMB_BYTES) + (LIMB_BYTES - j - 1)] =
(limb & 0xff) as u8;
limb >>= 8;
}
}
polyfill::slice::fill(to_zero, 0);
}
extern {
fn LIMBS_are_zero(a: *const Limb, num_limbs: c::size_t) -> LimbMask;
fn LIMBS_less_than(a: *const Limb, b: *const Limb, num_limbs: c::size_t)
-> LimbMask;
fn LIMBS_reduce_once(r: *mut Limb, m: *const Limb, num_limbs: c::size_t);
}
#[cfg(test)]
mod tests {
use untrusted;
use super::*;
#[test]
fn test_parse_big_endian_and_pad_consttime() {
const LIMBS: usize = 4;
{
let inp = untrusted::Input::from(&[]);
let mut result = [0; LIMBS];
assert!(parse_big_endian_and_pad_consttime(inp, &mut result)
.is_err());
}
{
let inp = [1, 2, 3, 4, 5, 6, 7, 8, 9];
let inp = untrusted::Input::from(&inp);
let mut result = [0; 8 / LIMB_BYTES];
assert!(parse_big_endian_and_pad_consttime(inp, &mut result[..])
.is_err());
}
{
let inp = [0xfe];
let inp = untrusted::Input::from(&inp);
let mut result = [0; LIMBS];
assert_eq!(Ok(()),
parse_big_endian_and_pad_consttime(inp, &mut result[..]));
assert_eq!(&[0xfe, 0, 0, 0], &result);
}
{
let inp = [0xbe, 0xef, 0xf0, 0x0d];
let inp = untrusted::Input::from(&inp);
let mut result = [0; LIMBS];
assert_eq!(Ok(()),
parse_big_endian_and_pad_consttime(inp, &mut result));
assert_eq!(&[0xbeeff00d, 0, 0, 0], &result);
}
}
}