use {rand, error, rsa};
use limb::*;
pub fn set_to_rand_mod(out: &mut [Limb], max_exclusive: &[Limb],
rng: &rand::SecureRandom)
-> Result<(), error::Unspecified> {
assert_eq!(out.len(), max_exclusive.len());
assert!(out.len() >= 1);
assert!(out.len() <= rsa::PRIVATE_KEY_PUBLIC_MODULUS_MAX_LIMBS);
assert!(max_exclusive.len() >= 1);
assert!(max_exclusive.len() <= rsa::PRIVATE_KEY_PUBLIC_MODULUS_MAX_LIMBS);
let sampling_params = select_sampling_params(max_exclusive);
let mut tmp_out = [0; rsa::PRIVATE_KEY_PUBLIC_MODULUS_MAX_LIMBS + 1];
tmp_out[..out.len()].copy_from_slice(&out);
let mut tmp_max = [0; rsa::PRIVATE_KEY_PUBLIC_MODULUS_MAX_LIMBS + 1];
tmp_max[..max_exclusive.len()].copy_from_slice(&max_exclusive);
let extra_limb = if sampling_params.extend_limbs_by_one { 1 } else { 0 };
let range = Range {
max_exclusive: &tmp_max[..(max_exclusive.len() + extra_limb)],
sampling_params: &sampling_params,
};
range.sample_into_limbs(&mut tmp_out[..out.len() + extra_limb], rng)?;
let dest_len = out.len();
out.copy_from_slice(&tmp_out[..dest_len]);
Ok(())
}
struct Range<'a> {
max_exclusive: &'a [Limb],
sampling_params: &'a SamplingParams,
}
impl <'a> Range<'a> {
fn are_limbs_within(&self, limbs: &[Limb]) -> bool {
assert_eq!(self.max_exclusive.len(), limbs.len());
if limbs_less_than_limbs_consttime(limbs, self.max_exclusive) !=
LimbMask::True {
return false;
}
limbs_are_zero_constant_time(limbs) == LimbMask::False
}
fn sample_into_limbs(&self, out: &mut [Limb], rng: &rand::SecureRandom)
-> Result<(), error::Unspecified> {
assert_eq!(self.max_exclusive.len(), out.len());
for _ in 0..100 {
{
let mut dest_as_bytes = limbs_as_bytes_mut(out);
rng.fill(&mut dest_as_bytes)?;
}
let mask = self.sampling_params.most_sig_limb_mask;
out[self.max_exclusive.len() - 1] &= mask;
if self.are_limbs_within(&out) {
return Ok(());
}
if self.sampling_params.reduce_when_over_bound {
limbs_reduce_once_constant_time(out, self.max_exclusive);
if self.are_limbs_within(&out) {
return Ok(());
}
limbs_reduce_once_constant_time(out, self.max_exclusive);
if self.are_limbs_within(&out) {
return Ok(());
}
}
}
Err(error::Unspecified)
}
}
struct SamplingParams {
most_sig_limb_mask: Limb,
reduce_when_over_bound: bool,
extend_limbs_by_one: bool,
}
fn select_sampling_params(max_exclusive: &[Limb]) -> SamplingParams {
let most_sig = max_exclusive.last().unwrap();
if most_sig >> (LIMB_BITS - 3) == 0b100 {
SamplingParams {
most_sig_limb_mask: 1,
reduce_when_over_bound: true,
extend_limbs_by_one: true,
}
} else if most_sig >> (LIMB_BITS - 4) == 0b0100 {
SamplingParams {
most_sig_limb_mask: Limb::max_value(),
reduce_when_over_bound: true,
extend_limbs_by_one: false,
}
} else {
SamplingParams {
most_sig_limb_mask: Limb::max_value() >> most_sig.leading_zeros(),
reduce_when_over_bound: false,
extend_limbs_by_one: false,
}
}
}
#[cfg(test)]
mod tests {
use {core, rand, test};
use limb::*;
#[test]
fn test_select_sampling_params() {
use super::select_sampling_params;
let starting_with_0b100 = &[
1 << (LIMB_BITS - 1),
1 << (LIMB_BITS - 1) | 1,
(1 << (LIMB_BITS - 1)) | (1 << LIMB_BITS - 4),
(1 << (LIMB_BITS - 1)) | (Limb::max_value() >> 3),
];
for l in starting_with_0b100 {
for x in [
&[*l][..],
&[0, *l][..],
&[Limb::max_value(), *l][..],
].iter() {
let p = select_sampling_params(x);
assert!(p.extend_limbs_by_one);
assert!(p.reduce_when_over_bound);
assert_eq!(1, p.most_sig_limb_mask);
}
}
let starting_with_0b0100 = &[
1 << (LIMB_BITS - 2),
1 << (LIMB_BITS - 2) | 1,
(1 << (LIMB_BITS - 2)) | (1 << LIMB_BITS - 5),
(1 << (LIMB_BITS - 2)) | (Limb::max_value() >> 4),
];
for l in starting_with_0b0100 {
for x in [
&[*l][..],
&[0, *l][..],
&[Limb::max_value(), *l][..],
].iter() {
let p = select_sampling_params(x);
assert!(!p.extend_limbs_by_one);
assert!(p.reduce_when_over_bound);
assert_eq!(Limb::max_value(), p.most_sig_limb_mask);
}
}
macro_rules! assert_normal {
($i:expr, $l:expr) => {
{
let x = [$l];
let p = select_sampling_params(&x[..]);
let mask = Limb::max_value() >> (LIMB_BITS - 1 - $i);
assert!(!p.extend_limbs_by_one);
assert!(!p.reduce_when_over_bound);
assert_eq!(mask, p.most_sig_limb_mask);
}
}
}
for i in 0..(LIMB_BITS - 2) {
let l = 1 << i;
assert_normal!(i, l);
}
for i in 0..LIMB_BITS {
let l = 1 << i;
assert_normal!(i, l | l >> 1);
assert_normal!(i, l | l >> 2);
}
}
#[test]
fn test_limbs_in_range() {
use super::{SamplingParams,Range};
let params = SamplingParams {
most_sig_limb_mask: Limb::max_value(),
reduce_when_over_bound: false,
extend_limbs_by_one: false,
};
let limbs = &[Limb::max_value(), Limb::max_value()];
let range = Range { max_exclusive: limbs, sampling_params: ¶ms };
assert!(!range.are_limbs_within(&[Limb::max_value(),
Limb::max_value()]));
assert!(range.are_limbs_within(&[Limb::max_value(),
Limb::max_value() - 1]));
assert!(range.are_limbs_within(&[Limb::max_value() - 1,
Limb::max_value()]));
assert!(!range.are_limbs_within(&[0, 0]));
assert!(range.are_limbs_within(&[1, 0]));
assert!(range.are_limbs_within(&[0, 1]));
let limbs = &[0x12345678, 0xdeadbeef];
let range = Range { max_exclusive: limbs, sampling_params: ¶ms };
assert!(!range.are_limbs_within(&[0x12345678, 0xdeadbeef]));
assert!(range.are_limbs_within(&[0x12345678 - 1, 0xdeadbeef]));
assert!(range.are_limbs_within(&[0x12345678, 0xdeadbeef - 1]));
assert!(!range.are_limbs_within(&[0x12345678 + 0x10, 0xdeadbeef]));
assert!(!range.are_limbs_within(&[0x12345678, 0xdeadbeef + 0x10]));
let limbs = &[0, 1];
let range = Range { max_exclusive: limbs, sampling_params: ¶ms };
assert!(!range.are_limbs_within(&[0, 0]));
assert!(range.are_limbs_within(&[1, 0]));
assert!(!range.are_limbs_within(&[0, 1]));
assert!(range.are_limbs_within(&[Limb::max_value(), 0]));
let limbs = &[2];
let range = Range { max_exclusive: limbs, sampling_params: ¶ms };
assert!(!range.are_limbs_within(&[0]));
assert!(range.are_limbs_within(&[1]));
assert!(!range.are_limbs_within(&[2]));
}
#[test]
fn test_set_to_rand_mod() {
use super::set_to_rand_mod;
let rng = rand::SystemRandom::new();
macro_rules! generate_and_assert_success {
($limbs:expr, $num_limbs:expr) => { {
let limbs: [Limb; $num_limbs] = $limbs;
let mut out: [Limb; $num_limbs] = [0; $num_limbs];
assert!(set_to_rand_mod(&mut out, &limbs, &rng).is_ok());
assert!(out.iter().any( |b| *b > 0 ));
out
} }
};
let _ = generate_and_assert_success!([0xdeadbeef, 0xdeadbeef], 2);
let out = generate_and_assert_success!([2], 1);
assert_eq!([1], out);
let _ = generate_and_assert_success!([1 << (LIMB_BITS - 1)], 1);
let _ = generate_and_assert_success!([Limb::max_value()], 1);
let out = generate_and_assert_success!([0, 1], 2);
assert_eq!(0, out[1]);
let _ = generate_and_assert_success!([1, 1], 2);
let _ = generate_and_assert_success!([1 << (LIMB_BITS - 1), 1], 2);
let _ = generate_and_assert_success!([Limb::max_value(), 1], 2);
let _ = generate_and_assert_success!([0, 1 << (LIMB_BITS - 1)], 2);
let _ = generate_and_assert_success!([1, 1 << (LIMB_BITS - 1)], 2);
let _ = generate_and_assert_success!(
[1 << (LIMB_BITS - 1), 1 << (LIMB_BITS - 1)], 2);
let _ = generate_and_assert_success!(
[Limb::max_value(), 1 << (LIMB_BITS - 1)], 2);
let _ = generate_and_assert_success!([0, Limb::max_value()], 2);
let _ = generate_and_assert_success!([1, Limb::max_value()], 2);
let _ = generate_and_assert_success!(
[1 << (LIMB_BITS - 1), Limb::max_value()], 2);
let _ = generate_and_assert_success!(
[Limb::max_value(), Limb::max_value()], 2);
}
#[test]
fn test_random_generation_retries() {
use super::{SamplingParams, Range};
let random_00 = test::rand::FixedByteRandom { byte: 0x00 };
let random_ff = test::rand::FixedByteRandom { byte: 0xff };
let max_exclusive = [Limb::max_value(), Limb::max_value() >> 1];
let sampling_params = SamplingParams {
most_sig_limb_mask: Limb::max_value(),
reduce_when_over_bound: false,
extend_limbs_by_one: false,
};
let range = Range {
max_exclusive: &max_exclusive,
sampling_params: &sampling_params,
};
{
let mut result = [0, 0];
assert!(range.sample_into_limbs(&mut result, &random_00).is_err());
}
{
let mut result = [0, 0];
assert!(range.sample_into_limbs(&mut result, &random_ff).is_err());
}
let max_exclusive_bytes = limbs_as_bytes(&max_exclusive);
{
let rng = test::rand::FixedSliceRandom {
bytes: &max_exclusive_bytes
};
let mut result = [0, 0];
assert!(range.sample_into_limbs(&mut result, &rng).is_err());
}
let max_exclusive_minus_1 = [max_exclusive[0] - 1, max_exclusive[1]];
let max_exclusive_minus_1_bytes =
limbs_as_bytes(&max_exclusive_minus_1);
{
let rng = test::rand::FixedSliceRandom {
bytes: max_exclusive_minus_1_bytes
};
let mut result = [0, 0];
range.sample_into_limbs(&mut result, &rng).unwrap();
assert_eq!(&max_exclusive_minus_1, &result);
}
{
let bytes = [
&max_exclusive_bytes[..],
&[0u8; 2 * LIMB_BYTES],
&max_exclusive_minus_1_bytes[..],
];
let rng = test::rand::FixedSliceSequenceRandom {
bytes: &bytes,
current: core::cell::UnsafeCell::new(0),
};
let mut result = [0, 0];
range.sample_into_limbs(&mut result, &rng).unwrap();
assert_eq!(&max_exclusive_minus_1, &result);
}
}
}
#[cfg(feature = "internal_benches")]
mod bench {
use {bench, rand, rsa};
use limb::*;
use super::{Range, SamplingParams};
const MAX_LIMBS: usize = rsa::RSA_PUBLIC_KEY_MODULUS_MAX_LIMBS;
#[bench]
fn bench_sample_into_limbs_no_reduce(b: &mut bench::Bencher) {
let mut out: [Limb; MAX_LIMBS] = [0; MAX_LIMBS];
let rng = rand::SystemRandom::new();
let params = SamplingParams {
most_sig_limb_mask: Limb::max_value(),
reduce_when_over_bound: false,
extend_limbs_by_one: false,
};
let range = Range {
max_exclusive: &max_sized_0b100_bound(),
sampling_params: ¶ms,
};
b.iter(|| {
range.sample_into_limbs(&mut out, &rng)
});
}
#[bench]
fn bench_sample_into_limbs_with_reduce(b: &mut bench::Bencher) {
let mut out: [Limb; MAX_LIMBS] = [0; MAX_LIMBS];
let rng = rand::SystemRandom::new();
let params = SamplingParams {
most_sig_limb_mask: Limb::max_value(),
reduce_when_over_bound: true,
extend_limbs_by_one: false,
};
let range = Range {
max_exclusive: &max_sized_0b100_bound(),
sampling_params: ¶ms,
};
b.iter(|| {
range.sample_into_limbs(&mut out, &rng)
});
}
fn max_sized_0b100_bound() -> [Limb; MAX_LIMBS] {
let mut max: [Limb; MAX_LIMBS] = [0; MAX_LIMBS];
max[MAX_LIMBS - 1] = 1 << (LIMB_BITS - 1);
assert_eq!(max[MAX_LIMBS - 1] >> (LIMB_BITS - 3), 0b100);
max
}
}