Files
OpenSK/libraries/crypto/src/ec/point.rs
kaczmarczyck 1b360662ee Public Key plain byte encoding (#540)
* public key is encoded in bytes

* ECDSA pubkey in uncompressed format
2022-08-31 15:51:40 +02:00

1036 lines
31 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::exponent256::ExponentP256;
use super::gfp256::GFP256;
use super::int256::Int256;
use super::montgomery::Montgomery;
#[cfg(feature = "std")]
use arrayref::array_mut_ref;
use arrayref::array_ref;
use core::ops::Add;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
// A point on the elliptic curve is represented by two field elements.
// The "direct" representation with GFP256 (integer modulo p) is used for serialization of public
// keys.
#[derive(Clone, Copy)]
pub struct PointP256 {
x: GFP256,
y: GFP256,
}
impl PointP256 {
// The point at infinity.
// Although this point is not "valid" on the curve (as it doesn't have an order of N), it is
// useful for tests.
#[cfg(test)]
const INFINITY: PointP256 = PointP256 {
x: GFP256::ZERO,
y: GFP256::ZERO,
};
/** Serialization **/
// This uses uncompressed point format from "SEC 1: Elliptic Curve Cryptography" ("Standards for
// Efficient Cryptography").
pub fn from_bytes_uncompressed_vartime(bytes: &[u8]) -> Option<PointP256> {
if bytes.len() != 65 || bytes[0] != 0x04 {
None
} else {
PointP256::new_checked_vartime(
Int256::from_bin(array_ref![bytes, 1, 32]),
Int256::from_bin(array_ref![bytes, 33, 32]),
)
}
}
#[cfg(feature = "std")]
pub fn to_bytes_uncompressed(&self, bytes: &mut [u8; 65]) {
bytes[0] = 0x04;
self.x.to_int().to_bin(array_mut_ref![bytes, 1, 32]);
self.y.to_int().to_bin(array_mut_ref![bytes, 33, 32]);
}
/** Constructors **/
pub fn new_checked_vartime(x: Int256, y: Int256) -> Option<PointP256> {
let gfx = GFP256::from_int_checked(x)?;
let gfy = GFP256::from_int_checked(y)?;
if GFP256::is_valid_point_vartime(&gfx, &gfy) {
Some(PointP256 { x: gfx, y: gfy })
} else {
None
}
}
fn from_projective(point: &PointProjective) -> PointP256 {
PointP256::from_affine(&point.to_affine())
}
fn from_affine(affine: &PointAffine) -> PointP256 {
PointP256 {
x: affine.x.montgomery_to_field(),
y: affine.y.montgomery_to_field(),
}
}
fn to_affine(&self) -> PointAffine {
PointAffine {
x: Montgomery::field_to_montgomery(&self.x),
y: Montgomery::field_to_montgomery(&self.y),
}
}
/** Useful getters **/
#[cfg(test)]
pub fn is_valid_vartime(&self) -> bool {
GFP256::is_valid_point_vartime(&self.x, &self.y)
}
pub fn getx(self) -> GFP256 {
self.x
}
pub fn gety(self) -> GFP256 {
self.y
}
/** Arithmetic **/
pub fn base_point_mul(n: &ExponentP256) -> PointP256 {
let point = PointProjective::scalar_base_mul(n);
PointP256::from_projective(&point)
}
pub fn mul(&self, n: &ExponentP256) -> PointP256 {
let p = self.to_affine();
let point = p.scalar_mul(n);
PointP256::from_projective(&point)
}
// Computes n1*G + n2*self
pub fn points_mul(&self, n1: &ExponentP256, n2: &ExponentP256) -> PointP256 {
let p = self.to_affine();
let p1 = PointProjective::scalar_base_mul(n1);
let p2 = p.scalar_mul(n2);
let point = &p1 + &p2;
PointP256::from_projective(&point)
}
}
// A point on the elliptic curve in projective form.
// This uses Montgomery representation for field elements.
// This is in projective coordinates, i.e. it represents the point { x: x / z, y: y / z }.
// This representation is more convenient to implement complete formulas for elliptic curve
// arithmetic.
#[derive(Clone, Copy)]
pub struct PointProjective {
x: Montgomery,
y: Montgomery,
z: Montgomery,
}
impl ConditionallySelectable for PointProjective {
#[allow(clippy::many_single_char_names)]
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
let x = Montgomery::conditional_select(&a.x, &b.x, choice);
let y = Montgomery::conditional_select(&a.y, &b.y, choice);
let z = Montgomery::conditional_select(&a.z, &b.z, choice);
Self { x, y, z }
}
}
// Equivalent to PointProjective { x, y, z: 1 }
#[derive(Clone, Copy)]
pub struct PointAffine {
x: Montgomery,
y: Montgomery,
}
impl PointProjective {
#[cfg(test)]
pub const INFINITY: PointProjective = PointProjective {
x: Montgomery::ZERO,
y: Montgomery::ONE,
z: Montgomery::ZERO,
};
/** Constructors **/
pub fn from_affine(point: &PointAffine) -> PointProjective {
PointProjective {
x: point.x,
y: point.y,
z: Montgomery::ONE,
}
}
#[cfg(test)]
// Construct a point in projective coordinates, with a given z value.
// This point is equivalent to { x, y, z: 1 }
pub fn from_affine_shuffled(point: &PointAffine, z: Montgomery) -> PointProjective {
PointProjective {
x: &point.x * &z,
y: &point.y * &z,
z,
}
}
fn to_affine(&self) -> PointAffine {
let zinv = self.z.inv();
let x = &self.x * &zinv;
let y = &self.y * &zinv;
PointAffine { x, y }
}
/** Constant-time helpers **/
fn select_point(table: &[PointProjective; 15], index: u32) -> PointProjective {
let mut point = PointProjective {
x: Montgomery::ZERO,
y: Montgomery::ZERO,
z: Montgomery::ZERO,
};
for i in 0..15 {
let choice = (i + 1).ct_eq(&index);
point.conditional_assign(&table[i as usize], choice);
}
point
}
/** Arithmetic **/
// Complete formula from https://eprint.iacr.org/2015/1060.pdf, Algorithm 5.
fn add_mixed(&self, other: &PointAffine) -> PointProjective {
// Steps 1-2 (same as add).
let mut t0 = &self.x * &other.x;
let t1 = &self.y * &other.y;
let mut t2 = self.z;
// Steps 3-7 (same as add).
let t3 = &self.x + &self.y;
let t4 = &other.x + &other.y;
let t3 = &t3 * &t4;
let t4 = &t0 + &t1;
let t3 = &t3 - &t4;
// Steps 8-11 (add_mixed optimization).
let t4 = &other.y * &self.z;
let t4 = &t4 + &self.y;
let y = &other.x * &self.z;
let y = &y + &self.x;
// Steps 12-17 (same as add).
let z = &Montgomery::B * &t2;
let mut x = &y - &z;
x.mul_scalar3(); // 14-15
let z = &t1 - &x;
let x = &t1 + &x;
// Steps 18-22 (same as add).
let y = &Montgomery::B * &y;
t2.mul_scalar3(); // 19-20
let y = &y - &t2;
let mut y = &y - &t0;
// Steps 23-27 (same as add).
y.mul_scalar3(); // 23-24
t0.mul_scalar3(); // 25-26
let t0 = &t0 - &t2;
// Steps 28-36 (same as add).
let t1 = &t4 * &y;
let t2 = &t0 * &y;
let y = &x * &z;
let y = &y + &t2;
let x = &t3 * &x;
let x = &x - &t1;
let z = &t4 * &z;
let t1 = &t3 * &t0;
let z = &z + &t1;
PointProjective { x, y, z }
}
// Complete formula from https://eprint.iacr.org/2015/1060.pdf, Algorithm 6.
fn double(&self) -> PointProjective {
// Steps 1-3 (same as add).
let mut t0 = self.x.square();
let t1 = self.y.square();
let mut t2 = self.z.square();
// Steps 4-7.
let mut t3 = &self.x * &self.y;
t3.mul_scalar2();
let mut z = &self.x * &self.z;
z.mul_scalar2();
// Steps 8-13 (same as add).
let y = &Montgomery::B * &t2;
let mut y = &y - &z;
y.mul_scalar3(); // 10-11
let x = &t1 - &y;
let y = &t1 + &y;
// Steps 14-15.
let y = &x * &y;
let x = &x * &t3;
// Steps 16-20 (same as add).
t2.mul_scalar3(); // 16-17
let z = &Montgomery::B * &z;
let z = &z - &t2;
let mut z = &z - &t0;
// Steps 21-26 (same as add).
z.mul_scalar3(); // 21-22
t0.mul_scalar3(); // 23-24
let t0 = &t0 - &t2;
// Steps 27-34.
let t0 = &t0 * &z;
let y = &y + &t0;
let mut t0 = &self.y * &self.z;
t0.mul_scalar2();
let z = &t0 * &z;
let x = &x - &z;
let mut z = &t0 * &t1;
z.mul_scalar4(); // 33-34
PointProjective { x, y, z }
}
// Compute scalar*G
fn scalar_base_mul(scalar: &ExponentP256) -> PointProjective {
let mut n = PointProjective {
x: Montgomery::ZERO,
y: Montgomery::ZERO,
z: Montgomery::ZERO,
};
let mut choice_n_is_inf = Choice::from(1u8);
for i in 0..32 {
if i != 0 {
n = n.double();
}
for table_offset in 0..2 {
let j = 32 * table_offset;
let bit0 = scalar.bit(31 - i + j);
let bit1 = scalar.bit(95 - i + j);
let bit2 = scalar.bit(159 - i + j);
let bit3 = scalar.bit(223 - i + j);
let index = bit0 | (bit1 << 1) | (bit2 << 2) | (bit3 << 3);
let p = PointAffine::select_point(&Montgomery::PRECOMPUTED[table_offset], index);
let t = n.add_mixed(&p);
n.conditional_assign(&PointProjective::from_affine(&p), choice_n_is_inf);
let choice_p_is_inf = index.ct_eq(&0);
n.conditional_assign(&t, !(choice_p_is_inf | choice_n_is_inf));
choice_n_is_inf &= choice_p_is_inf;
}
}
n
}
// Complete formula from https://eprint.iacr.org/2015/1060.pdf, Algorithm 1.
#[cfg(test)]
fn add_complete_general(self, other: &PointProjective) -> PointProjective {
// Steps 1-3.
let t0 = &self.x * &other.x;
let t1 = &self.y * &other.y;
let t2 = &self.z * &other.z;
// Steps 4-8.
let t3 = &self.x + &self.y;
let t4 = &other.x + &other.y;
let t3 = &t3 * &t4;
let t4 = &t0 + &t1;
let t3 = &t3 - &t4;
// Steps 9-13.
let t4 = &self.x + &self.z;
let t5 = &other.x + &other.z;
let t4 = &t4 * &t5;
let t5 = &t0 + &t2;
let t4 = &t4 - &t5;
// Steps 14-18.
let t5 = &self.y + &self.z;
let x = &other.y + &other.z;
let t5 = &t5 * &x;
let x = &t1 + &t2;
let t5 = &t5 - &x;
// Steps 19-24.
let z = &Montgomery::A * &t4;
let x = &Montgomery::THREE_B * &t2;
let z = &x + &z;
let x = &t1 - &z;
let z = &t1 + &z;
let y = &x * &z;
// Steps 25-34.
let t1 = &t0 + &t0;
let t1 = &t1 + &t0;
let t2 = &Montgomery::A * &t2;
let t4 = &Montgomery::THREE_B * &t4;
let t1 = &t1 + &t2;
let t2 = &t0 - &t2;
let t2 = &Montgomery::A * &t2;
let t4 = &t4 + &t2;
let t0 = &t1 * &t4;
let y = &y + &t0;
// Steps 35-37.
let t0 = &t5 * &t4;
let x = &t3 * &x;
let x = &x - &t0;
// Steps 38-40.
let t0 = &t3 * &t1;
let z = &t5 * &z;
let z = &z + &t0;
PointProjective { x, y, z }
}
}
impl PointAffine {
/** Constant-time helpers **/
fn select_point(table: &[[Montgomery; 2]; 15], index: u32) -> PointAffine {
let mut x = Montgomery::ZERO;
let mut y = Montgomery::ZERO;
for i in 0..15 {
let choice = (i + 1).ct_eq(&index);
x.conditional_assign(&table[i as usize][0], choice);
y.conditional_assign(&table[i as usize][1], choice);
}
PointAffine { x, y }
}
/** Arithmetic **/
fn scalar_mul(&self, scalar: &ExponentP256) -> PointProjective {
let mut precomp = [PointProjective {
x: Montgomery::ZERO,
y: Montgomery::ZERO,
z: Montgomery::ZERO,
}; 15];
precomp[0] = PointProjective::from_affine(self);
for i in (1..15).step_by(2) {
precomp[i] = precomp[i >> 1].double();
precomp[i + 1] = precomp[i].add_mixed(self);
}
let mut n = PointProjective {
x: Montgomery::ZERO,
y: Montgomery::ZERO,
z: Montgomery::ZERO,
};
let mut choice_n_is_inf = Choice::from(1u8);
for i in (0..256).step_by(4) {
if i != 0 {
n = n.double();
n = n.double();
n = n.double();
n = n.double();
}
let index = scalar.bit(255 - i) << 3
| scalar.bit(255 - i - 1) << 2
| scalar.bit(255 - i - 2) << 1
| scalar.bit(255 - i - 3);
let p = PointProjective::select_point(&precomp, index);
let t = n.add(&p);
n.conditional_assign(&p, choice_n_is_inf);
let choice_p_is_inf = index.ct_eq(&0);
n.conditional_assign(&t, !(choice_p_is_inf | choice_n_is_inf));
choice_n_is_inf &= choice_p_is_inf;
}
n
}
}
/** Arithmetic operators **/
#[allow(clippy::suspicious_arithmetic_impl)]
impl Add for &PointProjective {
type Output = PointProjective;
// Complete formula from https://eprint.iacr.org/2015/1060.pdf, Algorithm 4.
fn add(self, other: &PointProjective) -> PointProjective {
// Steps 1-3.
let mut t0 = &self.x * &other.x;
let t1 = &self.y * &other.y;
let mut t2 = &self.z * &other.z;
// Steps 4-8.
let t3 = &self.x + &self.y;
let t4 = &other.x + &other.y;
let t3 = &t3 * &t4;
let t4 = &t0 + &t1;
let t3 = &t3 - &t4;
// Steps 9-13.
let t4 = &self.y + &self.z;
let x = &other.y + &other.z;
let t4 = &t4 * &x;
let x = &t1 + &t2;
let t4 = &t4 - &x;
// Steps 14-18.
let x = &self.x + &self.z;
let y = &other.x + &other.z;
let x = &x * &y;
let y = &t0 + &t2;
let y = &x - &y;
// Steps 19-24.
let z = &Montgomery::B * &t2;
let mut x = &y - &z;
x.mul_scalar3(); // 21-22
let z = &t1 - &x;
let x = &t1 + &x;
// Steps 25-29.
let y = &Montgomery::B * &y;
t2.mul_scalar3(); // 26-27
let y = &y - &t2;
let mut y = &y - &t0;
// Steps 30-34.
y.mul_scalar3(); // 30-31
t0.mul_scalar3(); // 32-33
let t0 = &t0 - &t2;
// Steps 35-43.
let t1 = &t4 * &y;
let t2 = &t0 * &y;
let y = &x * &z;
let y = &y + &t2;
let x = &t3 * &x;
let x = &x - &t1;
let z = &t4 * &z;
let t1 = &t3 * &t0;
let z = &z + &t1;
PointProjective { x, y, z }
}
}
impl core::fmt::Debug for PointP256 {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("PointP256")
.field("x", &self.x)
.field("y", &self.y)
.finish()
}
}
impl PartialEq for PointP256 {
fn eq(&self, other: &PointP256) -> bool {
self.x == other.x && self.y == other.y
}
}
#[cfg(test)]
pub mod test {
use super::*;
impl PartialEq for PointAffine {
fn eq(&self, other: &PointAffine) -> bool {
self.x == other.x && self.y == other.y
}
}
impl core::fmt::Debug for PointAffine {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("PointAffine")
.field("x", &self.x)
.field("y", &self.y)
.finish()
}
}
impl PartialEq for PointProjective {
fn eq(&self, other: &PointProjective) -> bool {
self.to_affine() == other.to_affine()
}
}
impl core::fmt::Debug for PointProjective {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("PointProjective")
.field("x", &self.x)
.field("y", &self.y)
.field("z", &self.z)
.finish()
}
}
pub fn precomputed(i: usize, j: usize) -> PointAffine {
PointAffine {
x: Montgomery::PRECOMPUTED[i][j][0],
y: Montgomery::PRECOMPUTED[i][j][1],
}
}
fn get_test_values_affine() -> Vec<PointAffine> {
let mut values = Vec::new();
for table in 0..2 {
for index in 0..15 {
values.push(precomputed(table, index));
}
}
values
}
fn get_test_values_projective() -> Vec<PointProjective> {
let mut values: Vec<_> = get_test_values_affine()
.iter()
.map(|p| PointProjective::from_affine(p))
.collect();
values.push(PointProjective::INFINITY);
values
}
fn get_test_values() -> Vec<PointP256> {
get_test_values_affine()
.iter()
.map(|p| PointP256::from_affine(p))
.collect()
}
/** Serialization **/
#[test]
fn test_to_bytes_from_bytes() {
for &x in &get_test_values() {
let mut buf = [Default::default(); 65];
x.to_bytes_uncompressed(&mut buf);
assert_eq!(PointP256::from_bytes_uncompressed_vartime(&buf), Some(x));
}
}
#[test]
fn test_from_bytes_infinity_is_invalid() {
let mut buf = [0; 65];
buf[0] = 0x04;
assert_eq!(PointP256::from_bytes_uncompressed_vartime(&buf), None);
}
/** Conversion between point types **/
#[test]
fn test_convert_p256_affine() {
for x in &get_test_values_affine() {
assert_eq!(PointP256::from_affine(x).to_affine(), *x);
}
}
#[test]
fn test_convert_projective_affine() {
for x in &get_test_values_affine() {
assert_eq!(PointProjective::from_affine(x).to_affine(), *x);
}
}
#[test]
fn test_projective_shuffle() {
for x in &get_test_values_affine() {
for &shuffle in &super::super::montgomery::test::get_nonzero_test_values() {
assert_eq!(
PointProjective::from_affine_shuffled(x, shuffle).to_affine(),
*x
);
}
}
}
/** Point validation **/
// Edge cases generated with the following Sage script.
//
// ```
// k = GF(2^256 - 2^224 + 2^192 + 2^96 - 1, 't');
// R = PolynomialRing(k, 'u');
// u = R.gen()
// b = 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b
//
// def print_point(x, y):
// print "x = 0x%x" % x
// print "y = 0x%x" % y
// print "x^3 - 3*x + b = 0x%x" % (x^3 - 3*x + b)
// print "y^2 = 0x%x" % y^2
//
// def find_point_at_x(x):
// f = x^3 - 3*x + b - u^2
// r = f.roots()
// if len(r) > 0:
// y = r[0][0]
// print_point(x, y)
//
// def find_point_at_y(y):
// f = u^3 - 3*u + b - y^2
// r = f.roots()
// if len(r) > 0:
// x = r[0][0]
// print_point(x, y)
//
// ITERATIONS = 16
//
// print "*" * 40
// print "Small x"
// print "*" * 40
// for i in range(ITERATIONS):
// x = k(i)
// find_point_at_x(x)
//
// print "*" * 40
// print "Small y"
// print "*" * 40
// for i in range(ITERATIONS):
// y = k(i)
// find_point_at_y(y)
//
// print "*" * 40
// print "High-weight x"
// print "*" * 40
// for i in range(ITERATIONS):
// x = k(2^255 - 1 - 2^i)
// find_point_at_x(x)
//
// print "*" * 40
// print "High-weight y"
// print "*" * 40
// for i in range(ITERATIONS):
// y = k(2^255 - 1 - 2^i)
// find_point_at_y(y)
// ```
#[rustfmt::skip]
const POINTS_SMALL_X: &[[[u32; 8]; 2]] = &[
[
[0x00000005, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000],
[0xcdb70433, 0xccbea3f7, 0x9b265c3a, 0xee35afc4, 0x667e8521, 0x016ec431, 0x55a7e7fa, 0xba6dbc45],
],
[
[0x00000006, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000],
[0x7d7ddc34, 0xddf2aed7, 0xf0183f16, 0x232efd48, 0xcc8dffb8, 0xb9967a1a, 0xabdaf53e, 0xc94db3d2],
],
[
[0x00000008, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000],
[0x636041b3, 0x2242d085, 0xd631ff69, 0x27249b16, 0x3f37f6a6, 0xd624d1d2, 0xca290db0, 0xb706288a],
],
[
[0x00000009, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000],
[0x8fe7b297, 0xdb1812e4, 0x3c63a432, 0xfbc52276, 0xd33315cb, 0xcaaa94f7, 0x2caf2e23, 0x8e14e843],
],
[
[0x0000000c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000],
[0xb2b74387, 0x6abbc3b4, 0x9de5be82, 0xc726c883, 0x6c4b6500, 0xc653e363, 0xcb0680c1, 0x93fbd29a],
],
];
#[rustfmt::skip]
const POINTS_SMALL_Y: &[[[u32; 8]; 2]] = &[
[
[0x0069d2c7, 0x875d877f, 0x7b70f611, 0x6375e8a9, 0x95dbac0d, 0x10db6dd0, 0xab9c6e9e, 0x8d0177eb],
[0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000],
],
[
[0x7607ce12, 0xc9fe1bd7, 0x8b283fbb, 0x53eb03e0, 0xddcaac96, 0xa62f56d3, 0xc6825c8a, 0xcfe9c22c],
[0x00000004, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000],
],
[
[0xde8de1d7, 0xb176f692, 0x841022ca, 0x4cffaf35, 0xeb345f84, 0x0a92738c, 0x46cd60d8, 0xd7325d76],
[0x00000005, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000],
],
[
[0x87914a78, 0x077d5a71, 0x8e3dc5b2, 0x979131e2, 0x97d7ab3f, 0x731dbdaf, 0x1da31d68, 0x9b21c2de],
[0x00000006, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000],
],
[
[0xd56ac453, 0xd7acceae, 0xc6693e4f, 0xcffa296d, 0xe4df51fc, 0x564d94b7, 0xbc9f7da8, 0xb2ed6eac],
[0x00000007, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000],
],
];
#[rustfmt::skip]
const POINTS_HIGH_WEIGHT: &[[[u32; 8]; 2]] = &[
// High-weight x coordinate.
[
[0xfffffffe, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xefffffff],
[0xf0e94c90, 0x722ff8d6, 0x66ebf289, 0x9b17896c, 0x334f0e43, 0xc4e1c5d1, 0x1ea63e81, 0xa120f8da],
],
[
[0xfffffffd, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xefffffff],
[0x5ad8aa4b, 0x717d6de4, 0x2af77820, 0x04ce8429, 0xefb80898, 0xd004a68e, 0xe4b30001, 0x887ce5d3],
],
// High-weight y coordinate.
[
[0x98619b11, 0xae2e447c, 0x02bcee26, 0x1fcb1b9b, 0xed3ee2d9, 0xefb6ff97, 0x77ee5948, 0xe063d049],
[0xfffffffd, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xefffffff],
],
[
[0xcaf4e99c, 0x494eac75, 0x3237de43, 0x695ba4d4, 0x68339d6f, 0xbca064f3, 0x4910c02a, 0xaefca662],
[0xfffffffb, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xefffffff],
],
];
#[test]
fn test_zero_x_point() {
// Even though this point verifies the equation y^2 = x^3 - 3x + b, none of the (x, y)
// coordinates is allowed to be zero.
#[rustfmt::skip]
let x = Int256::new(
[0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000]
);
#[rustfmt::skip]
let y = Int256::new(
[0xe8b06c0b, 0xd7407a95, 0xe25178e8, 0xabe3d50d, 0x7b5f9449, 0xdbcc42a2, 0xf1d07c29, 0x99b7a386]
);
assert!(PointP256::new_checked_vartime(x, y).is_none());
}
#[test]
fn test_small_x_points() {
for p in POINTS_SMALL_X {
let x = Int256::new(p[0]);
let y = Int256::new(p[1]);
// These points are valid.
assert!(PointP256::new_checked_vartime(x, y).is_some());
// Adding p to the x coordinate doesn't invalidate the equation y^2 = x^3 - 3x + b (as
// we work in GF(p)), however the coordinates must be serialized in a canonical form
// modulo p.
assert!(PointP256::new_checked_vartime((&x + &Int256::P).0, y).is_none());
}
}
#[test]
fn test_small_y_points() {
for p in POINTS_SMALL_Y {
let x = Int256::new(p[0]);
let y = Int256::new(p[1]);
// These points are valid.
assert!(PointP256::new_checked_vartime(x, y).is_some());
// Adding p to the y coordinate doesn't invalidate the equation y^2 = x^3 - 3x + b (as
// we work in GF(p)), however the coordinates must be serialized in a canonical form
// modulo p.
assert!(PointP256::new_checked_vartime(x, (&y + &Int256::P).0).is_none());
}
}
#[test]
fn test_high_weight_points() {
// These points are all valid, with one coordinate of high Hamming weight. This is a sanity
// check that arithmetic works on such high weight values.
for p in POINTS_HIGH_WEIGHT {
let x = Int256::new(p[0]);
let y = Int256::new(p[1]);
assert!(PointP256::new_checked_vartime(x, y).is_some());
}
}
#[test]
fn test_infinity_is_invalid() {
assert!(PointP256::new_checked_vartime(
PointP256::INFINITY.x.to_int(),
PointP256::INFINITY.y.to_int()
)
.is_none());
}
/** Constant-time helpers **/
#[test]
fn test_select_point_projective() {
let mut table = Vec::new();
for i in 0..15 {
table.push(PointProjective::from_affine(&precomputed(0, i)));
}
assert_eq!(
PointProjective::select_point(array_ref![table, 0, 15], 0),
PointProjective {
x: Montgomery::ZERO,
y: Montgomery::ZERO,
z: Montgomery::ZERO,
}
);
for index in 1..16 {
assert_eq!(
PointProjective::select_point(array_ref![table, 0, 15], index as u32),
table[index - 1]
);
}
}
#[test]
fn test_select_point_affine() {
let table = &Montgomery::PRECOMPUTED[0];
assert_eq!(
PointAffine::select_point(table, 0),
PointAffine {
x: Montgomery::ZERO,
y: Montgomery::ZERO,
}
);
for index in 1..16 {
assert_eq!(
PointAffine::select_point(table, index as u32),
precomputed(0, index - 1)
);
}
}
/** Arithmetic operators **/
#[test]
fn test_add_is_add_complete_general() {
for x in &get_test_values_projective() {
for y in &get_test_values_projective() {
let left = x.add_complete_general(y);
let right = x + y;
assert_eq!(left, right);
}
}
}
// Due to the 3 nested loops, this test is super slow with debug assertions enabled.
#[cfg(not(debug_assertions))]
#[test]
fn test_add_is_associative() {
for x in &get_test_values_projective() {
for y in &get_test_values_projective() {
for z in &get_test_values_projective() {
// (x + y) + z
let left = &(x + y) + z;
// x + (y + z)
let right = x + &(y + z);
assert_eq!(left, right);
}
}
}
}
#[test]
fn test_add_is_commutative() {
for x in &get_test_values_projective() {
for y in &get_test_values_projective() {
assert_eq!(x + y, y + x);
}
}
}
#[test]
fn test_add_mixed() {
for x in &get_test_values_projective() {
for y in &get_test_values_affine() {
assert_eq!(x.add_mixed(y), x + &PointProjective::from_affine(y));
}
}
}
#[test]
fn test_double() {
for x in &get_test_values_projective() {
println!("doubling {:?}", x);
assert_eq!(x.double(), x + x);
}
}
#[test]
fn test_add_infinity() {
for &x in &get_test_values_projective() {
assert_eq!(&x + &PointProjective::INFINITY, x);
}
}
#[test]
fn test_add_mixed_infinity() {
for x in &get_test_values_affine() {
assert_eq!(
PointProjective::INFINITY.add_mixed(x),
PointProjective::from_affine(x)
);
}
}
#[test]
fn test_double_infinity() {
assert_eq!(
PointProjective::INFINITY.double(),
PointProjective::INFINITY
);
}
#[test]
fn test_generator_is_valid_point() {
let gen = precomputed(0, 0);
assert!(PointP256::from_affine(&gen).is_valid_vartime());
}
#[test]
fn test_generator_has_correct_order() {
let gen = precomputed(0, 0);
// 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.
// In this test we check that N is indeed the curve order and therefore we need an unsafe
// block to construct an exponent of N.
let order = unsafe { ExponentP256::from_int_unchecked(Int256::N) };
assert_eq!(
PointP256::from_projective(&gen.scalar_mul(&order)),
PointP256::INFINITY
);
}
#[test]
fn test_scalar_base_mul_is_scalar_mul_generator() {
let gen = precomputed(0, 0);
// TODO: more scalars
for scalar in &super::super::exponent256::test::get_test_values() {
assert_eq!(
PointProjective::scalar_base_mul(scalar),
gen.scalar_mul(scalar)
);
}
}
#[test]
fn test_base_point_mul_is_mul_generator() {
let gen = precomputed(0, 0);
// TODO: more scalars
for scalar in &super::super::exponent256::test::get_test_values() {
assert_eq!(
PointP256::base_point_mul(scalar),
PointP256::from_affine(&gen).mul(scalar)
);
}
}
// Helper function to compute the point 2^power * p.
pub fn power_of_two(mut p: PointProjective, power: usize) -> PointProjective {
for _ in 0..power {
p = p.double();
}
p
}
// TODO: more tests
}