// 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 { 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 { 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 { 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 { 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 { 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 }