1182 lines
36 KiB
Rust
1182 lines
36 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::super::rng256::Rng256;
|
|
use alloc::vec::Vec;
|
|
use byteorder::{BigEndian, ByteOrder};
|
|
use core::ops::{Add, AddAssign, Sub, SubAssign};
|
|
use subtle::{self, Choice, ConditionallySelectable, ConstantTimeEq};
|
|
|
|
const BITS_PER_DIGIT: usize = 32;
|
|
const BYTES_PER_DIGIT: usize = BITS_PER_DIGIT >> 3;
|
|
const NDIGITS: usize = 8;
|
|
pub const NBYTES: usize = NDIGITS * BYTES_PER_DIGIT;
|
|
|
|
pub type Digit = u32;
|
|
type DoubleDigit = u64;
|
|
type SignedDoubleDigit = i64;
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
|
// TODO: remove this Default once https://github.com/dalek-cryptography/subtle/issues/63 is
|
|
// resolved.
|
|
#[derive(Default)]
|
|
pub struct Int256 {
|
|
digits: [Digit; NDIGITS],
|
|
}
|
|
|
|
impl ConditionallySelectable for Int256 {
|
|
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
|
let mut digits = [0; NDIGITS];
|
|
for (i, digit) in digits.iter_mut().enumerate() {
|
|
*digit = Digit::conditional_select(&a.digits[i], &b.digits[i], choice);
|
|
}
|
|
Self { digits }
|
|
}
|
|
}
|
|
|
|
/** Arithmetic operations on the secp256r1 field, where elements are represented as 8 digits of
|
|
* 32 bits. **/
|
|
#[allow(clippy::unreadable_literal)]
|
|
impl Int256 {
|
|
/** Constants for the secp256r1 curve. **/
|
|
// Curve order (prime)
|
|
pub const N: Int256 = Int256 {
|
|
digits: [
|
|
0xfc632551, 0xf3b9cac2, 0xa7179e84, 0xbce6faad, 0xffffffff, 0xffffffff, 0x00000000,
|
|
0xffffffff,
|
|
],
|
|
};
|
|
// Curve order - 2
|
|
pub const N_MIN_2: Int256 = Int256 {
|
|
digits: [
|
|
0xfc63254f, 0xf3b9cac2, 0xa7179e84, 0xbce6faad, 0xffffffff, 0xffffffff, 0x00000000,
|
|
0xffffffff,
|
|
],
|
|
};
|
|
// Curve field size
|
|
pub const P: Int256 = Int256 {
|
|
digits: [
|
|
0xffffffff, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
|
|
0xffffffff,
|
|
],
|
|
};
|
|
// Curve b
|
|
pub const B: Int256 = Int256 {
|
|
digits: [
|
|
0x27d2604b, 0x3bce3c3e, 0xcc53b0f6, 0x651d06b0, 0x769886bc, 0xb3ebbd55, 0xaa3a93e7,
|
|
0x5ac635d8,
|
|
],
|
|
};
|
|
// 2^257 mod P
|
|
pub const R: Int256 = Int256 {
|
|
digits: [
|
|
0x00000002, 0x00000000, 0x00000000, 0xfffffffe, 0xffffffff, 0xffffffff, 0xfffffffd,
|
|
0x00000001,
|
|
],
|
|
};
|
|
// 1 / 2^257 mod P
|
|
pub const R_INV: Int256 = Int256 {
|
|
digits: [
|
|
0x80000000, 0x00000001, 0xffffffff, 0x00000000, 0x80000001, 0xfffffffe, 0x00000001,
|
|
0x7fffffff,
|
|
],
|
|
};
|
|
|
|
pub const ZERO: Int256 = Int256 { digits: [0; 8] };
|
|
pub const ONE: Int256 = Int256 {
|
|
digits: [1, 0, 0, 0, 0, 0, 0, 0],
|
|
};
|
|
|
|
#[cfg(test)]
|
|
pub const fn new(digits: [Digit; NDIGITS]) -> Int256 {
|
|
Int256 { digits }
|
|
}
|
|
|
|
#[cfg(test)]
|
|
pub fn digits(self) -> [Digit; NDIGITS] {
|
|
self.digits
|
|
}
|
|
|
|
#[cfg(test)]
|
|
fn hamming_weight(&self) -> u32 {
|
|
self.digits.iter().map(|d| d.count_ones()).sum()
|
|
}
|
|
|
|
/** RNG **/
|
|
// Generates a uniformly distributed integer 0 <= x < 2^256
|
|
pub fn gen_uniform_256<R>(r: &mut R) -> Int256
|
|
where
|
|
R: Rng256,
|
|
{
|
|
Int256 {
|
|
digits: r.gen_uniform_u32x8(),
|
|
}
|
|
}
|
|
|
|
/** Serialization **/
|
|
pub fn from_bin(src: &[u8; NBYTES]) -> Int256 {
|
|
let mut digits = [0; NDIGITS];
|
|
for i in 0..NDIGITS {
|
|
digits[NDIGITS - 1 - i] = BigEndian::read_u32(array_ref![src, 4 * i, 4]);
|
|
}
|
|
Int256 { digits }
|
|
}
|
|
|
|
pub fn to_bin(&self, dst: &mut [u8; NBYTES]) {
|
|
for i in 0..NDIGITS {
|
|
BigEndian::write_u32(array_mut_ref![dst, 4 * i, 4], self.digits[NDIGITS - 1 - i]);
|
|
}
|
|
}
|
|
|
|
pub fn to_minimal_encoding(self) -> Vec<u8> {
|
|
let mut bytes_buffer = [0; NBYTES];
|
|
self.to_bin(&mut bytes_buffer);
|
|
match bytes_buffer.iter().position(|x| *x != 0) {
|
|
Some(pos) => {
|
|
let mut encoding = vec![];
|
|
if bytes_buffer[pos] & 0x80 == 0x80 {
|
|
encoding.push(0x00);
|
|
}
|
|
encoding.extend_from_slice(&bytes_buffer[pos..]);
|
|
encoding
|
|
}
|
|
None => vec![0x00],
|
|
}
|
|
}
|
|
|
|
/** Useful getters **/
|
|
#[inline(always)]
|
|
pub fn digit(&self, i: usize) -> Digit {
|
|
self.digits[i]
|
|
}
|
|
|
|
pub fn bit(&self, i: usize) -> Digit {
|
|
let digit = i / BITS_PER_DIGIT;
|
|
let bit = i & (BITS_PER_DIGIT - 1);
|
|
(self.digits[digit] >> bit) & 1
|
|
}
|
|
|
|
pub fn is_zero(&self) -> subtle::Choice {
|
|
// Best effort constant-time comparison, assuming the compiler doesn't optimize that.
|
|
Choice::from(
|
|
self.digits
|
|
.iter()
|
|
.fold(1u8, |acc, x| acc & x.ct_eq(&0).unwrap_u8()),
|
|
)
|
|
}
|
|
|
|
// Helper function to implement variable-time modular inverse.
|
|
#[cfg(test)]
|
|
fn is_even(&self) -> bool {
|
|
self.digits[0] & 1 == 0
|
|
}
|
|
|
|
#[cfg(test)]
|
|
fn count_ones(&self) -> u32 {
|
|
self.digits.iter().map(|x| x.count_ones()).sum()
|
|
}
|
|
|
|
/** Arithmetic operations: bit shifts **/
|
|
// Shift left by n bits, and return the result as well as the top digit that was shifted out.
|
|
// This is valid only for 0 < n < BITS_PER_DIGIT
|
|
pub fn shl(&self, n: usize) -> (Int256, Digit) {
|
|
let mut digits = [0; NDIGITS];
|
|
digits[0] = self.digits[0] << n;
|
|
#[allow(clippy::needless_range_loop)]
|
|
for i in 1..NDIGITS {
|
|
digits[i] = (self.digits[i] << n) | (self.digits[i - 1] >> (BITS_PER_DIGIT - n));
|
|
}
|
|
|
|
(
|
|
Int256 { digits },
|
|
self.digits[NDIGITS - 1] >> (BITS_PER_DIGIT - n),
|
|
)
|
|
}
|
|
|
|
// Shift right by n bits.
|
|
// This is valid only for 0 < n < BITS_PER_DIGIT
|
|
pub fn shr(&self, n: usize) -> Int256 {
|
|
let mut digits = [0; NDIGITS];
|
|
#[allow(clippy::needless_range_loop)]
|
|
for i in 0..(NDIGITS - 1) {
|
|
digits[i] = (self.digits[i] >> n) | (self.digits[i + 1] << (BITS_PER_DIGIT - n));
|
|
}
|
|
digits[NDIGITS - 1] = self.digits[NDIGITS - 1] >> n;
|
|
|
|
Int256 { digits }
|
|
}
|
|
|
|
// Helper function to implement variable-time modular inverse.
|
|
// Shift right by 1 bit, pushing highbit at the top.
|
|
#[cfg(test)]
|
|
fn shr1(&self, highbit: Digit) -> Int256 {
|
|
let mut digits = [0; NDIGITS];
|
|
for i in 0..(NDIGITS - 1) {
|
|
digits[i] = (self.digits[i] >> 1) | (self.digits[i + 1] << (BITS_PER_DIGIT - 1));
|
|
}
|
|
digits[NDIGITS - 1] = (self.digits[NDIGITS - 1] >> 1) | (highbit << (BITS_PER_DIGIT - 1));
|
|
|
|
Int256 { digits }
|
|
}
|
|
|
|
/** Arithmetic operations: addition/substraction **/
|
|
// Reduction modulo modd.
|
|
pub fn modd(&self, modd: &Int256) -> Int256 {
|
|
let mut digits = self.digits;
|
|
let choice = Int256::sub_conditional(&mut digits, modd, 0, Choice::from(1u8));
|
|
Int256::add_conditional(&mut digits, modd, 0, choice);
|
|
Int256 { digits }
|
|
}
|
|
|
|
// Computes: dst[], top += if choice { mod[] } else { 0 }
|
|
// Returns: new top digit
|
|
fn add_conditional(
|
|
dst: &mut [Digit; NDIGITS],
|
|
modd: &Int256,
|
|
top: Digit,
|
|
choice: Choice,
|
|
) -> Digit {
|
|
let mut carry: DoubleDigit = 0;
|
|
|
|
for (i, digit) in dst.iter_mut().enumerate() {
|
|
carry += *digit as DoubleDigit;
|
|
carry += u32::conditional_select(&0, &modd.digits[i], choice) as DoubleDigit;
|
|
*digit = carry as Digit;
|
|
carry >>= BITS_PER_DIGIT;
|
|
}
|
|
|
|
(carry as Digit) + top
|
|
}
|
|
|
|
// Computes: dst[], top -= if choice { mod[] } else { 0 }
|
|
// Returns: new top digit
|
|
fn sub_conditional(
|
|
dst: &mut [Digit; NDIGITS],
|
|
modd: &Int256,
|
|
top: Digit,
|
|
choice: Choice,
|
|
) -> Choice {
|
|
let mut borrow: SignedDoubleDigit = 0;
|
|
|
|
for (i, digit) in dst.iter_mut().enumerate() {
|
|
borrow += *digit as SignedDoubleDigit;
|
|
borrow -= u32::conditional_select(&0, &modd.digits[i], choice) as SignedDoubleDigit;
|
|
*digit = borrow as Digit;
|
|
borrow >>= BITS_PER_DIGIT;
|
|
}
|
|
|
|
((borrow + (top as SignedDoubleDigit)) as Digit).ct_eq(&!0)
|
|
}
|
|
|
|
/** Modular arithmetic operations **/
|
|
// Modular addition.
|
|
pub fn modadd_vartime(&self, other: &Int256, modd: &Int256) -> Int256 {
|
|
let (sum, carry) = (self as &Int256) + other;
|
|
let tmp = if carry != 0 { (&sum - modd).0 } else { sum };
|
|
|
|
// At this point, the sum can be >= modd, even without carry.
|
|
// We substract modd to handle this case.
|
|
tmp.modsub_vartime(modd, modd)
|
|
}
|
|
|
|
// Modular substraction.
|
|
pub fn modsub_vartime(&self, other: &Int256, modd: &Int256) -> Int256 {
|
|
let (diff, borrow) = (self as &Int256) - other;
|
|
if borrow != 0 {
|
|
(&diff + modd).0
|
|
} else {
|
|
diff
|
|
}
|
|
}
|
|
|
|
// Requires: the most-significant word of the modulus is 0xffffffff.
|
|
// Computes: a * b modulo modd.
|
|
pub fn modmul(a: &Int256, b: &Int256, modd: &Int256) -> Int256 {
|
|
Int256::modmul_top(a, b, 0, modd)
|
|
}
|
|
|
|
// Requires: the most-significant word of the modulus is 0xffffffff.
|
|
// Computes: a * (b, top_b) modulo modd.
|
|
pub fn modmul_top(a: &Int256, b: &Int256, top_b: Digit, modd: &Int256) -> Int256 {
|
|
let mut tmp = [0; NDIGITS * 2 + 1];
|
|
let mut top = 0;
|
|
|
|
// Multiply/add into tmp.
|
|
for i in 0..NDIGITS {
|
|
if i != 0 {
|
|
tmp[i + NDIGITS - 1] = top;
|
|
}
|
|
top = Int256::mul_add(array_mut_ref![tmp, i, NDIGITS], a, b.digits[i]);
|
|
}
|
|
|
|
tmp[2 * NDIGITS - 1] = top;
|
|
top = Int256::mul_add(array_mut_ref![tmp, NDIGITS, NDIGITS], a, top_b);
|
|
|
|
// Reduce tmp, digit by digit.
|
|
for j in 0..=NDIGITS {
|
|
let i = NDIGITS - j;
|
|
|
|
// Estimate the reducer as top * modd, because the most significant word of modd is
|
|
// 0xffffffff.
|
|
let mut reducer = Int256::ZERO;
|
|
let top_reducer = Int256::mul_add(&mut reducer.digits, modd, top);
|
|
top = Int256::sub_top(array_mut_ref![tmp, i, NDIGITS], &reducer, top, top_reducer);
|
|
|
|
#[cfg(test)]
|
|
assert!(top <= 1);
|
|
|
|
let _top =
|
|
Int256::sub_conditional(array_mut_ref![tmp, i, NDIGITS], modd, top, top.ct_eq(&1));
|
|
|
|
#[cfg(test)]
|
|
assert_eq!(bool::from(_top), false);
|
|
|
|
top = tmp[i + NDIGITS - 1];
|
|
}
|
|
|
|
let choice =
|
|
Int256::sub_conditional(array_mut_ref![tmp, 0, NDIGITS], modd, 0, Choice::from(1u8));
|
|
Int256::add_conditional(array_mut_ref![tmp, 0, NDIGITS], modd, 0, choice);
|
|
|
|
Int256 {
|
|
digits: *array_ref![tmp, 0, NDIGITS],
|
|
}
|
|
}
|
|
|
|
// Helper function to implement modular multiplication.
|
|
// Computes: dst[] += src[] * factor
|
|
// Returns: carry digit
|
|
fn mul_add(dst: &mut [Digit; NDIGITS], src: &Int256, factor: Digit) -> Digit {
|
|
let mut carry: DoubleDigit = 0;
|
|
|
|
for (i, digit) in dst.iter_mut().enumerate() {
|
|
carry += *digit as DoubleDigit;
|
|
carry += (src.digits[i] as DoubleDigit) * (factor as DoubleDigit);
|
|
*digit = carry as Digit;
|
|
carry >>= BITS_PER_DIGIT;
|
|
}
|
|
|
|
carry as Digit
|
|
}
|
|
|
|
// Helper function to implement modular multiplication.
|
|
// Computes: dst[], top -= src[], src_top
|
|
// Returns: borrow digit (new top)
|
|
fn sub_top(dst: &mut [Digit; NDIGITS], src: &Int256, top: Digit, src_top: Digit) -> Digit {
|
|
let mut borrow: SignedDoubleDigit = 0;
|
|
|
|
for (i, digit) in dst.iter_mut().enumerate() {
|
|
borrow += *digit as SignedDoubleDigit;
|
|
borrow -= src.digits[i] as SignedDoubleDigit;
|
|
*digit = borrow as Digit;
|
|
borrow >>= BITS_PER_DIGIT;
|
|
}
|
|
|
|
borrow += top as SignedDoubleDigit;
|
|
borrow -= src_top as SignedDoubleDigit;
|
|
|
|
#[cfg(test)]
|
|
assert_eq!(borrow >> BITS_PER_DIGIT, 0);
|
|
|
|
borrow as Digit
|
|
}
|
|
|
|
/** Constant-time helpers **/
|
|
// Helper function to implement constant-time modular inverse.
|
|
// Best-effort constant time function that computes:
|
|
// if idx == 0 {
|
|
// *tbl0 = Int256::ONE
|
|
// } else {
|
|
// *tbl0 = tbl[idx - 1]
|
|
// }
|
|
fn set_zero_to_idx(tbl0: &mut Int256, tbl: &[Int256; 15], idx: u32) {
|
|
*tbl0 = Int256::ONE;
|
|
for i in 1u32..16 {
|
|
tbl0.conditional_assign(&tbl[(i - 1) as usize], i.ct_eq(&idx));
|
|
}
|
|
}
|
|
|
|
/** Arithmetic operations: modular exponentiation **/
|
|
pub fn modpow(&self, power: &Int256, modd: &Int256) -> Int256 {
|
|
let mut tbl0 = Int256::ZERO;
|
|
let mut tbl = [Int256::ZERO; 15];
|
|
// tbl[i-1] = self^i
|
|
tbl[0] = *self;
|
|
for i in 1..15 {
|
|
tbl[i] = Int256::modmul(&tbl[i - 1], self, modd);
|
|
}
|
|
|
|
let mut result = Int256::ONE;
|
|
for j in (0..256).step_by(4) {
|
|
let i = 256 - j;
|
|
result = Int256::modmul(&result, &result, modd);
|
|
result = Int256::modmul(&result, &result, modd);
|
|
result = Int256::modmul(&result, &result, modd);
|
|
result = Int256::modmul(&result, &result, modd);
|
|
|
|
let idx = power.bit(i - 1) << 3
|
|
| power.bit(i - 2) << 2
|
|
| power.bit(i - 3) << 1
|
|
| power.bit(i - 4);
|
|
|
|
Int256::set_zero_to_idx(&mut tbl0, &tbl, idx); // tbl0 = tbl[idx-1];
|
|
tbl0 = Int256::modmul(&tbl0, &result, modd);
|
|
result.conditional_assign(&tbl0, !idx.ct_eq(&0));
|
|
}
|
|
|
|
result
|
|
}
|
|
|
|
/** Arithmetic operations: modular inverse **/
|
|
// Variable time function to compute modular inverse. This uses Euclid's theorem.
|
|
#[cfg(test)]
|
|
#[allow(clippy::many_single_char_names)]
|
|
pub fn modinv_vartime(&self, modd: &Int256) -> Int256 {
|
|
let mut r = Int256::ZERO;
|
|
let mut s = Int256::ONE;
|
|
let mut u = *modd;
|
|
let mut v = *self;
|
|
|
|
loop {
|
|
if u.is_even() {
|
|
u = u.shr1(0);
|
|
if r.is_even() {
|
|
r = r.shr1(0);
|
|
} else {
|
|
let (rr, highbit) = &r + modd;
|
|
r = rr.shr1(highbit);
|
|
}
|
|
} else if v.is_even() {
|
|
v = v.shr1(0);
|
|
if s.is_even() {
|
|
s = s.shr1(0);
|
|
} else {
|
|
let (ss, highbit) = &s + modd;
|
|
s = ss.shr1(highbit);
|
|
}
|
|
} else {
|
|
let (w, borrow) = &v - &u;
|
|
if borrow == 0 {
|
|
v = w;
|
|
let (ss, borrow) = &s - &r;
|
|
s = if borrow != 0 { (&ss + modd).0 } else { ss };
|
|
if bool::from(v.is_zero()) {
|
|
break;
|
|
}
|
|
} else {
|
|
u = (&u - &v).0;
|
|
let (rr, borrow) = &r - &s;
|
|
r = if borrow != 0 { (&rr + modd).0 } else { rr };
|
|
}
|
|
}
|
|
}
|
|
|
|
r.modd(modd)
|
|
}
|
|
|
|
/** Comparison between field elements. **/
|
|
// Best-effort constant-time less-than operation.
|
|
// FIXME: This code is currently required because subtle only supports constant-time equality
|
|
// comparisons. This should be removed once
|
|
// https://github.com/dalek-cryptography/subtle/issues/61 is fixed
|
|
pub fn ct_lt(&self, other: &Int256) -> Choice {
|
|
let mut borrow: SignedDoubleDigit = 0;
|
|
|
|
for i in 0..NDIGITS {
|
|
// The following statement updates the borrow according to this table.
|
|
// +-------------------------------------+----------------+------------------+
|
|
// | self.digits[i].cmp(other.digits[i]) | borrow += ? | resulting borrow |
|
|
// +-------------------------------------+----------------+------------------+
|
|
// | Less | ffffffff_xx... | ffffffff_yy... |
|
|
// | Equal | 0 | unchanged |
|
|
// | Greater | 00000000_xx... | 00000000_yy... |
|
|
// +-------------------------------------+----------------+------------------+
|
|
borrow +=
|
|
(self.digits[i] as SignedDoubleDigit) - (other.digits[i] as SignedDoubleDigit);
|
|
// This is a signed shift. After this operation, the borrow can take two values:
|
|
// - 00...00 (so far, self >= other)
|
|
// - ff...ff (so far, self < other)
|
|
borrow >>= BITS_PER_DIGIT;
|
|
}
|
|
|
|
Choice::from((borrow & 1) as u8)
|
|
}
|
|
|
|
// Best-effort constant time comparison.
|
|
// * 0 = equal
|
|
// * 1 = self > other
|
|
// * -1 = self < other
|
|
#[cfg(test)]
|
|
pub fn compare(&self, other: &Int256) -> u32 {
|
|
let mut borrow: SignedDoubleDigit = 0;
|
|
let mut notzero: Digit = 0;
|
|
|
|
for i in 0..NDIGITS {
|
|
borrow +=
|
|
(self.digits[i] as SignedDoubleDigit) - (other.digits[i] as SignedDoubleDigit);
|
|
notzero |= (borrow as Digit != 0) as Digit;
|
|
borrow >>= BITS_PER_DIGIT;
|
|
}
|
|
|
|
(borrow as Digit) | notzero
|
|
}
|
|
|
|
#[cfg(test)]
|
|
fn compare_vartime(&self, other: &Int256) -> u32 {
|
|
use core::cmp::Ordering;
|
|
|
|
for i in 0..NDIGITS {
|
|
match self.digits[NDIGITS - i - 1].cmp(&other.digits[NDIGITS - i - 1]) {
|
|
Ordering::Equal => continue,
|
|
Ordering::Greater => return 1,
|
|
Ordering::Less => return 0xffffffff,
|
|
}
|
|
}
|
|
0
|
|
}
|
|
}
|
|
|
|
/** Addition with carry **/
|
|
impl Add for &Int256 {
|
|
type Output = (Int256, Digit);
|
|
|
|
// Returns sum and carry (0 or 1).
|
|
fn add(self, other: &Int256) -> (Int256, Digit) {
|
|
let mut digits = [0; NDIGITS];
|
|
let mut carry: DoubleDigit = 0;
|
|
|
|
for (i, digit) in digits.iter_mut().enumerate() {
|
|
carry += (self.digits[i] as DoubleDigit) + (other.digits[i] as DoubleDigit);
|
|
*digit = carry as Digit;
|
|
carry >>= BITS_PER_DIGIT;
|
|
}
|
|
|
|
(Int256 { digits }, carry as Digit)
|
|
}
|
|
}
|
|
|
|
impl AddAssign<&Int256> for Int256 {
|
|
// Adds to self, ignoring carry.
|
|
fn add_assign(&mut self, other: &Int256) {
|
|
let mut carry: DoubleDigit = 0;
|
|
for i in 0..NDIGITS {
|
|
carry += (self.digits[i] as DoubleDigit) + (other.digits[i] as DoubleDigit);
|
|
self.digits[i] = carry as Digit;
|
|
carry >>= BITS_PER_DIGIT;
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Add<Digit> for &Int256 {
|
|
type Output = (Int256, Digit);
|
|
|
|
// Returns sum and carry (0 or 1).
|
|
fn add(self, digit: Digit) -> (Int256, Digit) {
|
|
let mut digits = [0; NDIGITS];
|
|
let mut carry = digit as DoubleDigit;
|
|
|
|
for (i, digit) in digits.iter_mut().enumerate() {
|
|
carry += self.digits[i] as DoubleDigit;
|
|
*digit = carry as Digit;
|
|
carry >>= BITS_PER_DIGIT;
|
|
}
|
|
|
|
(Int256 { digits }, carry as Digit)
|
|
}
|
|
}
|
|
|
|
/** Substraction with borrow **/
|
|
impl Sub for &Int256 {
|
|
type Output = (Int256, Digit);
|
|
|
|
// Returns difference and borrow (0 or -1).
|
|
fn sub(self, other: &Int256) -> (Int256, Digit) {
|
|
let mut digits = [0; NDIGITS];
|
|
let mut borrow: SignedDoubleDigit = 0;
|
|
|
|
for (i, digit) in digits.iter_mut().enumerate() {
|
|
borrow +=
|
|
(self.digits[i] as SignedDoubleDigit) - (other.digits[i] as SignedDoubleDigit);
|
|
*digit = borrow as Digit;
|
|
borrow >>= BITS_PER_DIGIT;
|
|
}
|
|
|
|
(Int256 { digits }, borrow as Digit)
|
|
}
|
|
}
|
|
|
|
impl SubAssign<&Int256> for Int256 {
|
|
// Substract from self, ignoring carry.
|
|
fn sub_assign(&mut self, other: &Int256) {
|
|
let mut borrow: SignedDoubleDigit = 0;
|
|
for i in 0..NDIGITS {
|
|
borrow +=
|
|
(self.digits[i] as SignedDoubleDigit) - (other.digits[i] as SignedDoubleDigit);
|
|
self.digits[i] = borrow as Digit;
|
|
borrow >>= BITS_PER_DIGIT;
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "derive_debug")]
|
|
impl core::fmt::Debug for Int256 {
|
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
|
write!(f, "Int256 {{ digits: {:08x?} }}", self.digits)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
pub mod test {
|
|
use super::super::montgomery::Montgomery;
|
|
use super::*;
|
|
|
|
/** Extra constants for tests **/
|
|
const TWO: Int256 = Int256 {
|
|
digits: [2, 0, 0, 0, 0, 0, 0, 0],
|
|
};
|
|
const P_MIN_1: Int256 = Int256 {
|
|
digits: [
|
|
0xfffffffe, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
|
|
0xffffffff,
|
|
],
|
|
};
|
|
const P_MIN_2: Int256 = Int256 {
|
|
digits: [
|
|
0xfffffffd, 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, 0x00000001,
|
|
0xffffffff,
|
|
],
|
|
};
|
|
|
|
// Generate all 256-bit integers that have exactly one bit set to 1.
|
|
pub fn get_1bit_one_test_values() -> Vec<Int256> {
|
|
let mut values = Vec::new();
|
|
for &byte in &[0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80] {
|
|
for &int in &[byte, byte << 8, byte << 16, byte << 24] {
|
|
values.push(Int256 {
|
|
digits: [int, 0, 0, 0, 0, 0, 0, 0],
|
|
});
|
|
values.push(Int256 {
|
|
digits: [0, int, 0, 0, 0, 0, 0, 0],
|
|
});
|
|
values.push(Int256 {
|
|
digits: [0, 0, int, 0, 0, 0, 0, 0],
|
|
});
|
|
values.push(Int256 {
|
|
digits: [0, 0, 0, int, 0, 0, 0, 0],
|
|
});
|
|
values.push(Int256 {
|
|
digits: [0, 0, 0, 0, int, 0, 0, 0],
|
|
});
|
|
values.push(Int256 {
|
|
digits: [0, 0, 0, 0, 0, int, 0, 0],
|
|
});
|
|
values.push(Int256 {
|
|
digits: [0, 0, 0, 0, 0, 0, int, 0],
|
|
});
|
|
values.push(Int256 {
|
|
digits: [0, 0, 0, 0, 0, 0, 0, int],
|
|
});
|
|
}
|
|
}
|
|
values
|
|
}
|
|
|
|
// Generate all 256-bit integers that have exactly one bit set to 0.
|
|
pub fn get_1bit_zero_test_values() -> Vec<Int256> {
|
|
let values: Vec<Int256> = get_1bit_one_test_values()
|
|
.iter()
|
|
.map(|x| {
|
|
let mut digits = [Default::default(); NDIGITS];
|
|
for i in 0..NDIGITS {
|
|
digits[i] = !x.digits[i];
|
|
}
|
|
Int256 { digits }
|
|
})
|
|
.collect();
|
|
values
|
|
}
|
|
|
|
pub fn get_nonzero_test_values() -> Vec<Int256> {
|
|
let mut values: Vec<Int256> = Montgomery::PRECOMPUTED
|
|
.iter()
|
|
.flatten()
|
|
.flatten()
|
|
.map(|x| x.montgomery_to_field().to_int())
|
|
.collect();
|
|
values.append(&mut get_1bit_one_test_values());
|
|
values.append(&mut get_1bit_zero_test_values());
|
|
values.push(Int256::B);
|
|
values.push(P_MIN_1);
|
|
values.push(P_MIN_2);
|
|
values
|
|
}
|
|
|
|
fn get_test_values() -> Vec<Int256> {
|
|
let mut values = get_nonzero_test_values();
|
|
values.push(Int256::ZERO);
|
|
values
|
|
}
|
|
|
|
#[test]
|
|
fn test_1bit_one() {
|
|
let values = get_1bit_one_test_values();
|
|
assert_eq!(values.len(), 256);
|
|
for x in &values {
|
|
assert_eq!(x.hamming_weight(), 1);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_1bit_zero() {
|
|
let values = get_1bit_zero_test_values();
|
|
assert_eq!(values.len(), 256);
|
|
for x in &values {
|
|
assert_eq!(x.hamming_weight(), 255);
|
|
}
|
|
}
|
|
|
|
/** Serialization **/
|
|
#[test]
|
|
fn test_to_bin_from_bin() {
|
|
for &x in &get_test_values() {
|
|
let mut buf = [Default::default(); NBYTES];
|
|
x.to_bin(&mut buf);
|
|
assert_eq!(Int256::from_bin(&buf), x);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_minimal_encoding_zero() {
|
|
let test_int = Int256::ZERO;
|
|
let expected_encoding = vec![0x00];
|
|
|
|
assert_eq!(test_int.to_minimal_encoding(), expected_encoding);
|
|
}
|
|
|
|
#[test]
|
|
fn test_minimal_encoding_one() {
|
|
let test_int = Int256::ONE;
|
|
let expected_encoding = vec![0x01];
|
|
|
|
assert_eq!(test_int.to_minimal_encoding(), expected_encoding);
|
|
}
|
|
|
|
#[test]
|
|
fn test_minimal_encoding_one_full_byte() {
|
|
let bytes = [
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0xFF,
|
|
];
|
|
let test_int = Int256::from_bin(&bytes);
|
|
let expected_encoding = vec![0x00, 0xFF];
|
|
|
|
assert_eq!(test_int.to_minimal_encoding(), expected_encoding);
|
|
}
|
|
|
|
#[test]
|
|
fn test_minimal_encoding_most_bytes_full() {
|
|
let bytes = [
|
|
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF,
|
|
];
|
|
let test_int = Int256::from_bin(&bytes);
|
|
let expected_encoding = bytes.to_vec();
|
|
|
|
assert_eq!(test_int.to_minimal_encoding(), expected_encoding);
|
|
}
|
|
|
|
#[test]
|
|
fn test_minimal_encoding_no_leading_byte() {
|
|
let bytes = [
|
|
0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF,
|
|
];
|
|
let test_int = Int256::from_bin(&bytes);
|
|
let expected_encoding = bytes.to_vec();
|
|
|
|
assert_eq!(test_int.to_minimal_encoding(), expected_encoding);
|
|
}
|
|
|
|
#[test]
|
|
fn test_minimal_encoding_with_leading_byte() {
|
|
let bytes = [
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF,
|
|
];
|
|
let test_int = Int256::from_bin(&bytes);
|
|
let mut expected_encoding = vec![0x00];
|
|
expected_encoding.extend(&bytes);
|
|
|
|
assert_eq!(test_int.to_minimal_encoding(), expected_encoding);
|
|
}
|
|
|
|
#[test]
|
|
fn test_from_bin_is_big_endian_bits_with_little_endian_words() {
|
|
let buf = b"\x01\x23\x45\x67\x89\xab\xcd\xef\
|
|
\x12\x34\x56\x78\x9a\xbc\xde\xf0\
|
|
\x23\x45\x67\x89\xab\xcd\xef\x01\
|
|
\x34\x56\x78\x9a\xbc\xde\xf0\x12";
|
|
assert_eq!(
|
|
Int256::from_bin(&buf),
|
|
Int256 {
|
|
digits: [
|
|
0xbcdef012, 0x3456789a, 0xabcdef01, 0x23456789, 0x9abcdef0, 0x12345678,
|
|
0x89abcdef, 0x01234567,
|
|
]
|
|
}
|
|
);
|
|
}
|
|
|
|
/** Useful getters **/
|
|
#[test]
|
|
fn test_is_zero() {
|
|
assert!(bool::from(Int256::ZERO.is_zero()));
|
|
for x in get_nonzero_test_values() {
|
|
assert!(!bool::from(x.is_zero()));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_is_even() {
|
|
assert!(Int256::ZERO.is_even());
|
|
assert!(!Int256::ONE.is_even());
|
|
assert!(TWO.is_even());
|
|
assert!(!Int256::N.is_even());
|
|
assert!(!Int256::P.is_even());
|
|
assert!(!Int256::B.is_even());
|
|
}
|
|
|
|
/** Arithmetic operations: bit shifts **/
|
|
#[test]
|
|
fn test_shift_zero() {
|
|
for i in 1..BITS_PER_DIGIT {
|
|
assert_eq!(Int256::ZERO.shl(i), (Int256::ZERO, 0));
|
|
}
|
|
for i in 1..BITS_PER_DIGIT {
|
|
assert_eq!(Int256::ZERO.shr(i), Int256::ZERO);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_shifts() {
|
|
let mut a = Int256::ONE;
|
|
|
|
// Shift left.
|
|
for i in 0..255 {
|
|
assert_eq!(a.bit(i), 1);
|
|
assert!(!bool::from(a.is_zero()));
|
|
let (shifted, carry) = a.shl(1);
|
|
assert_eq!(carry, 0);
|
|
a = shifted;
|
|
assert_eq!(a.bit(i), 0);
|
|
assert_eq!(a.count_ones(), 1);
|
|
}
|
|
|
|
assert_eq!(a.bit(255), 1);
|
|
assert!(!bool::from(a.is_zero()));
|
|
let (shifted, carry) = a.shl(1);
|
|
assert_eq!(carry, 1);
|
|
assert_eq!(shifted.bit(255), 0);
|
|
assert!(bool::from(shifted.is_zero()));
|
|
|
|
// Shift right.
|
|
for i in (1..256).rev() {
|
|
assert_eq!(a.bit(i), 1);
|
|
assert!(!bool::from(a.is_zero()));
|
|
a = a.shr(1);
|
|
assert_eq!(a.bit(i), 0);
|
|
assert_eq!(a.count_ones(), 1);
|
|
}
|
|
|
|
assert_eq!(a.bit(0), 1);
|
|
assert!(!bool::from(a.is_zero()));
|
|
a = a.shr(1);
|
|
assert_eq!(a.bit(0), 0);
|
|
assert!(bool::from(a.is_zero()));
|
|
}
|
|
|
|
#[test]
|
|
fn test_shl_shr1() {
|
|
for x in &get_test_values() {
|
|
let (shifted, carry) = x.shl(1);
|
|
assert_eq!(&shifted.shr1(carry), x);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_shr1_is_shr_one() {
|
|
for x in &get_test_values() {
|
|
assert_eq!(x.shr(1), x.shr1(0));
|
|
}
|
|
for x in &get_test_values() {
|
|
let mut y = *x;
|
|
for i in 1..BITS_PER_DIGIT {
|
|
y = y.shr1(0);
|
|
assert_eq!(x.shr(i), y);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Constant-time helpers **/
|
|
#[test]
|
|
fn test_set_zero_to_idx() {
|
|
let mut tbl = [Int256::ZERO; 15];
|
|
for (i, x) in tbl.iter_mut().enumerate() {
|
|
*x = Int256 {
|
|
digits: [i as u32; NDIGITS],
|
|
};
|
|
}
|
|
|
|
for i in 0..16 {
|
|
let mut tbl0 = Int256::ZERO;
|
|
Int256::set_zero_to_idx(&mut tbl0, &tbl, i as u32);
|
|
if i == 0 {
|
|
assert_eq!(tbl0, Int256::ONE);
|
|
} else {
|
|
assert_eq!(tbl0, tbl[i - 1]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Arithmetic: constant-time conditional addition/substraction **/
|
|
#[test]
|
|
fn test_add_conditional() {
|
|
for x in &get_test_values() {
|
|
for y in &get_test_values() {
|
|
let mut z = *x;
|
|
let carry = Int256::add_conditional(&mut z.digits, y, 0, Choice::from(0u8));
|
|
assert_eq!(carry, 0);
|
|
assert_eq!(z, *x);
|
|
let carry = Int256::add_conditional(&mut z.digits, y, 0, Choice::from(1u8));
|
|
assert_eq!((z, carry), x + y);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_sub_conditional() {
|
|
for x in &get_test_values() {
|
|
for y in &get_test_values() {
|
|
let mut z = *x;
|
|
let borrow = Int256::sub_conditional(&mut z.digits, y, 0, Choice::from(0u8));
|
|
assert_eq!(bool::from(borrow), false);
|
|
assert_eq!(z, *x);
|
|
let borrow = Int256::sub_conditional(&mut z.digits, y, 0, Choice::from(1u8));
|
|
assert_eq!((z, Digit::conditional_select(&0, &!0, borrow)), x - y);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Arithmetic operators **/
|
|
#[test]
|
|
fn test_add_sub() {
|
|
for x in &get_test_values() {
|
|
for y in &get_test_values() {
|
|
let (sum, carry) = x + y;
|
|
let (diff, borrow) = &sum - y;
|
|
assert_eq!(diff, *x);
|
|
assert_eq!(carry.wrapping_add(borrow), 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_sub_add() {
|
|
for x in &get_test_values() {
|
|
for y in &get_test_values() {
|
|
let (diff, borrow) = x - y;
|
|
let (sum, carry) = &diff + y;
|
|
assert_eq!(sum, *x);
|
|
assert_eq!(carry.wrapping_add(borrow), 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Arithmetic: modular exponentiation **/
|
|
#[test]
|
|
fn test_modpow() {
|
|
const MODULUS: Int256 = Int256::P;
|
|
for x in &get_test_values() {
|
|
let mut result = Int256::ONE;
|
|
let mut power = Int256::ZERO;
|
|
|
|
// This test is super slow with debug assertions enabled.
|
|
#[cfg(not(debug_assertions))]
|
|
const ITERATIONS: u32 = 100;
|
|
#[cfg(debug_assertions)]
|
|
const ITERATIONS: u32 = 5;
|
|
|
|
for _ in 0..ITERATIONS {
|
|
assert_eq!(x.modpow(&power, &MODULUS), result);
|
|
result = Int256::modmul(&result, x, &MODULUS);
|
|
power += &Int256::ONE;
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_self_times_modinv_is_one() {
|
|
const MODULUS: Int256 = Int256::P;
|
|
for x in &get_nonzero_test_values() {
|
|
let inv = x.modinv_vartime(&MODULUS);
|
|
let product = Int256::modmul(&x, &inv, &MODULUS);
|
|
assert_eq!(product, Int256::ONE);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_modinv_modinv() {
|
|
const MODULUS: Int256 = Int256::P;
|
|
for &x in &get_nonzero_test_values() {
|
|
// By construction, this test only works if x is less than the modulus.
|
|
if x.compare(&MODULUS) != 0xffffffff {
|
|
continue;
|
|
}
|
|
assert_eq!(x.modinv_vartime(&MODULUS).modinv_vartime(&MODULUS), x);
|
|
}
|
|
}
|
|
|
|
/** Other arithmetic **/
|
|
#[test]
|
|
fn test_add_digit() {
|
|
for x in &get_test_values() {
|
|
for y in &get_test_values() {
|
|
for &digit in &y.digits {
|
|
assert_eq!(
|
|
x + digit,
|
|
x + &Int256 {
|
|
digits: [digit, 0, 0, 0, 0, 0, 0, 0]
|
|
}
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_add_assign() {
|
|
for x in &get_test_values() {
|
|
for y in &get_test_values() {
|
|
let mut z = *x;
|
|
z += y;
|
|
assert_eq!(z, (x + y).0);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_sub_assign() {
|
|
for x in &get_test_values() {
|
|
for y in &get_test_values() {
|
|
let mut z = *x;
|
|
z -= y;
|
|
assert_eq!(z, (x - y).0);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_mul_add() {
|
|
for x in &get_test_values() {
|
|
for y in &get_test_values() {
|
|
let mut result = *x;
|
|
let mut carries = 0;
|
|
|
|
// This test is super slow with debug assertions enabled.
|
|
#[cfg(not(debug_assertions))]
|
|
const ITERATIONS: u32 = 1000;
|
|
#[cfg(debug_assertions)]
|
|
const ITERATIONS: u32 = 5;
|
|
|
|
for factor in 0..ITERATIONS {
|
|
let mut z = *x;
|
|
let ma_carry = Int256::mul_add(&mut z.digits, y, factor);
|
|
assert_eq!(ma_carry, carries);
|
|
assert_eq!(z, result);
|
|
|
|
let (sum, carry) = &result + y;
|
|
result = sum;
|
|
carries += carry;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Comparison between field elements. **/
|
|
#[test]
|
|
fn test_compare() {
|
|
for x in &get_test_values() {
|
|
for y in &get_test_values() {
|
|
let cmp = x.compare(y);
|
|
assert!(cmp == 0 || cmp == 1 || cmp == 0xffffffff);
|
|
assert_eq!(cmp, x.compare_vartime(y));
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_compare_is_reflexive() {
|
|
for x in &get_test_values() {
|
|
assert_eq!(x.compare(x), 0);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_compare_is_antisymetric() {
|
|
for x in &get_test_values() {
|
|
for y in &get_test_values() {
|
|
let a = x.compare(y);
|
|
let b = y.compare(x);
|
|
assert_eq!(a.wrapping_add(b), 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_lt() {
|
|
for x in &get_test_values() {
|
|
for y in &get_test_values() {
|
|
let ct_lt = bool::from(x.ct_lt(y));
|
|
let lt = x.compare_vartime(y) == 0xffffffff;
|
|
assert_eq!(ct_lt, lt);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_lt_is_antireflexive() {
|
|
for x in &get_test_values() {
|
|
assert!(!bool::from(x.ct_lt(x)));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_lt_is_antisymetric() {
|
|
for x in &get_test_values() {
|
|
for y in &get_test_values() {
|
|
let a = x.ct_lt(y).unwrap_u8();
|
|
let b = y.ct_lt(x).unwrap_u8();
|
|
let c = (x == y) as u8;
|
|
assert_eq!(a + b + c, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: more tests
|
|
}
|