Files
OpenSK/libraries/crypto/src/ec/exponent256.rs
kaczmarczyck 4782d7e186 Separate RNG library (#470)
* seperates the RNG library

* fixes crypto tests

* adds rng256 workflow

* fixes formatting
2022-04-28 11:36:43 +02:00

345 lines
9.8 KiB
Rust

// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::int256::{Digit, Int256};
use core::ops::Mul;
use rng256::Rng256;
use subtle::{self, Choice, ConditionallySelectable, CtOption};
// An exponent on the elliptic curve, that is an element modulo the curve order N.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
// TODO: remove this Default once https://github.com/dalek-cryptography/subtle/issues/63 is
// resolved.
#[derive(Default)]
pub struct ExponentP256 {
int: Int256,
}
impl ConditionallySelectable for ExponentP256 {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
Self {
int: Int256::conditional_select(&a.int, &b.int, choice),
}
}
}
impl ExponentP256 {
/** Constructors **/
pub fn from_int_checked(int: Int256) -> CtOption<ExponentP256> {
CtOption::new(ExponentP256 { int }, int.ct_lt(&Int256::N))
}
#[cfg(test)]
// Normally the ExponentP256 type guarantees that its values stay in [0, N[ because N is the
// curve order and therefore exponents >= N are equivalent to their reduction modulo N.
// This unsafe function is only used in tests to check that N is indeed the curve order.
pub unsafe fn from_int_unchecked(int: Int256) -> ExponentP256 {
ExponentP256 { int }
}
pub fn modn(int: Int256) -> ExponentP256 {
ExponentP256 {
int: int.modd(&Int256::N),
}
}
/** Helpful getters **/
pub fn bit(&self, i: usize) -> Digit {
self.int.bit(i)
}
pub fn to_int(self) -> Int256 {
self.int
}
pub fn is_zero(&self) -> subtle::Choice {
self.int.is_zero()
}
pub fn non_zero(self) -> CtOption<NonZeroExponentP256> {
CtOption::new(NonZeroExponentP256 { e: self }, !self.is_zero())
}
/** Arithmetic **/
pub fn mul_top(&self, other: &Int256, other_top: Digit) -> ExponentP256 {
ExponentP256 {
int: Int256::modmul_top(&self.int, other, other_top, &Int256::N),
}
}
}
/** Arithmetic operators **/
impl Mul for &ExponentP256 {
type Output = ExponentP256;
fn mul(self, other: &ExponentP256) -> ExponentP256 {
ExponentP256 {
int: Int256::modmul(&self.int, &other.int, &Int256::N),
}
}
}
// A non-zero exponent on the elliptic curve.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
// TODO: remove this Default once https://github.com/dalek-cryptography/subtle/issues/63 is
// resolved.
#[derive(Default)]
pub struct NonZeroExponentP256 {
e: ExponentP256,
}
impl ConditionallySelectable for NonZeroExponentP256 {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
Self {
e: ExponentP256::conditional_select(&a.e, &b.e, choice),
}
}
}
impl NonZeroExponentP256 {
/** RNG **/
// Generates a uniformly distributed element 0 < k < N
pub fn gen_uniform<R>(r: &mut R) -> NonZeroExponentP256
where
R: Rng256,
{
loop {
let x = Int256::gen_uniform_256(r);
if bool::from(Int256::N_MIN_2.ct_lt(&x)) {
continue;
}
// At this point, x <= n - 2.
// We add 1 so that 0 < result < n.
return NonZeroExponentP256 {
e: ExponentP256 { int: (&x + 1).0 },
};
}
}
/** Constructors **/
pub fn from_int_checked(int: Int256) -> CtOption<NonZeroExponentP256> {
ExponentP256::from_int_checked(int)
.and_then(|e| CtOption::new(NonZeroExponentP256 { e }, !e.is_zero()))
}
/** Helpful getters **/
pub fn to_int(self) -> Int256 {
self.e.to_int()
}
pub fn as_exponent(&self) -> &ExponentP256 {
&self.e
}
/** Arithmetic **/
// Compute the inverse modulo N. This uses Fermat's little theorem for constant-timeness.
pub fn inv(&self) -> NonZeroExponentP256 {
NonZeroExponentP256 {
e: ExponentP256 {
int: self.e.int.modpow(&Int256::N_MIN_2, &Int256::N),
},
}
}
#[cfg(test)]
fn inv_vartime(&self) -> NonZeroExponentP256 {
NonZeroExponentP256 {
e: ExponentP256 {
int: self.e.int.modinv_vartime(&Int256::N),
},
}
}
}
/** Arithmetic operators **/
impl Mul for &NonZeroExponentP256 {
type Output = NonZeroExponentP256;
// The product of two non-zero elements is also non-zero, because the curve order N is prime.
fn mul(self, other: &NonZeroExponentP256) -> NonZeroExponentP256 {
NonZeroExponentP256 {
e: &self.e * &other.e,
}
}
}
#[cfg(test)]
pub mod test {
use super::super::montgomery::Montgomery;
use super::*;
use crate::util::ToOption;
const ZERO: ExponentP256 = ExponentP256 { int: Int256::ZERO };
const ONE: NonZeroExponentP256 = NonZeroExponentP256 {
e: ExponentP256 { int: Int256::ONE },
};
const N_MIN_1_INT: Int256 = Int256::new([
0xfc632550, 0xf3b9cac2, 0xa7179e84, 0xbce6faad, 0xffffffff, 0xffffffff, 0x00000000,
0xffffffff,
]);
const N_MIN_1: NonZeroExponentP256 = NonZeroExponentP256 {
e: ExponentP256 { int: N_MIN_1_INT },
};
fn get_nonzero_test_values() -> Vec<NonZeroExponentP256> {
let mut values: Vec<NonZeroExponentP256> = Montgomery::PRECOMPUTED
.iter()
.flatten()
.flatten()
.map(|x| {
ExponentP256::modn(x.montgomery_to_field().to_int())
.non_zero()
.unwrap()
})
.collect();
values.extend(
super::super::int256::test::get_nonzero_test_values()
.iter()
.filter_map(|&x| {
let y = ExponentP256::modn(x).non_zero();
if bool::from(y.is_some()) {
Some(y.unwrap())
} else {
None
}
}),
);
values.push(ONE);
values
}
pub fn get_test_values() -> Vec<ExponentP256> {
let mut values: Vec<ExponentP256> = get_nonzero_test_values()
.iter()
.map(|x| *x.as_exponent())
.collect();
values.push(ZERO);
values
}
/** Constructors **/
#[test]
fn test_from_int_checked() {
assert_eq!(
ExponentP256::from_int_checked(Int256::ZERO).to_option(),
Some(ExponentP256 { int: Int256::ZERO })
);
assert_eq!(
ExponentP256::from_int_checked(Int256::ONE).to_option(),
Some(ExponentP256 { int: Int256::ONE })
);
assert_eq!(
ExponentP256::from_int_checked(N_MIN_1_INT).to_option(),
Some(ExponentP256 { int: N_MIN_1_INT })
);
assert_eq!(ExponentP256::from_int_checked(Int256::N).to_option(), None);
}
#[test]
fn test_modn() {
assert_eq!(
ExponentP256::modn(Int256::ZERO),
ExponentP256 { int: Int256::ZERO }
);
assert_eq!(
ExponentP256::modn(Int256::ONE),
ExponentP256 { int: Int256::ONE }
);
assert_eq!(
ExponentP256::modn(N_MIN_1_INT),
ExponentP256 { int: N_MIN_1_INT }
);
assert_eq!(
ExponentP256::modn(Int256::N),
ExponentP256 { int: Int256::ZERO }
);
}
/** Arithmetic operations: inverse **/
#[test]
fn test_inv_is_inv_vartime() {
for x in &get_nonzero_test_values() {
assert_eq!(x.inv(), x.inv_vartime());
}
}
#[test]
fn test_self_times_inv_is_one() {
for x in &get_nonzero_test_values() {
assert_eq!(x * &x.inv(), ONE);
}
}
#[test]
fn test_inv_inv() {
for x in get_nonzero_test_values() {
assert_eq!(x.inv().inv(), x);
}
}
#[test]
fn test_well_known_inverses() {
assert_eq!(ONE.inv(), ONE);
assert_eq!(N_MIN_1.inv(), N_MIN_1);
}
/** RNG **/
// Mock rng that samples through a list of values, then panics.
struct StressTestingRng {
values: Vec<Int256>,
index: usize,
}
impl StressTestingRng {
pub fn new(values: Vec<Int256>) -> StressTestingRng {
StressTestingRng { values, index: 0 }
}
}
impl Rng256 for StressTestingRng {
// This function is unused, as we redefine gen_uniform_u32x8.
fn gen_uniform_u8x32(&mut self) -> [u8; 32] {
unreachable!()
}
fn gen_uniform_u32x8(&mut self) -> [u32; 8] {
let result = self.values[self.index].digits();
self.index += 1;
result
}
}
#[test]
fn test_uniform_non_zero_is_below_n() {
let mut rng = StressTestingRng::new(vec![
Int256::new([
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff,
]),
Int256::N,
N_MIN_1.to_int(),
Int256::N_MIN_2,
]);
assert_eq!(NonZeroExponentP256::gen_uniform(&mut rng), N_MIN_1);
}
#[test]
fn test_uniform_n_is_above_zero() {
let mut rng = StressTestingRng::new(vec![Int256::ZERO]);
assert_eq!(NonZeroExponentP256::gen_uniform(&mut rng), ONE);
}
}