use super::field::*;
#[derive(Clone, Copy, Debug)]
pub struct JacobianPoint<const LIMBS: usize> {
pub x: FieldElement<LIMBS>,
pub y: FieldElement<LIMBS>,
pub z: FieldElement<LIMBS>,
}
impl<const LIMBS: usize> JacobianPoint<LIMBS> {
pub fn infinity() -> Self {
Self {
x: FieldElement::one(),
y: FieldElement::one(),
z: FieldElement::ZERO,
}
}
pub fn from_affine(x: FieldElement<LIMBS>, y: FieldElement<LIMBS>) -> Self {
Self {
x,
y,
z: FieldElement::one(),
}
}
pub fn is_infinity(&self) -> bool {
self.z.is_zero()
}
pub fn to_affine(&self, p: &[u64; LIMBS]) -> Option<(FieldElement<LIMBS>, FieldElement<LIMBS>)> {
if self.z.is_zero() {
return None;
}
let z_inv = field_inv(&self.z, p);
let z_inv2 = field_sqr(&z_inv, p);
let z_inv3 = field_mul(&z_inv2, &z_inv, p);
let x = field_mul(&self.x, &z_inv2, p);
let y = field_mul(&self.y, &z_inv3, p);
Some((x, y))
}
}
pub struct CurveParams<const LIMBS: usize> {
pub p: [u64; LIMBS],
pub a: FieldElement<LIMBS>,
pub b: FieldElement<LIMBS>,
pub gx: FieldElement<LIMBS>,
pub gy: FieldElement<LIMBS>,
pub n: [u64; LIMBS],
pub qlen_bits: usize,
pub felem_bytes: usize,
}
pub fn hex_to_fe<const LIMBS: usize>(hex: &str) -> FieldElement<LIMBS> {
let bytes: Vec<u8> = (0..hex.len())
.step_by(2)
.map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap())
.collect();
FieldElement::from_bytes_be(&bytes)
}
pub fn hex_to_limbs<const LIMBS: usize>(hex: &str) -> [u64; LIMBS] {
hex_to_fe::<LIMBS>(hex).limbs
}
fn hex_to_fe4(hex: &str) -> FieldElement<4> {
hex_to_fe::<4>(hex)
}
fn hex_to_fe6(hex: &str) -> FieldElement<6> {
hex_to_fe::<6>(hex)
}
pub fn p256_params() -> CurveParams<4> {
CurveParams {
p: P256_P,
a: hex_to_fe4("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC"),
b: hex_to_fe4("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B"),
gx: hex_to_fe4("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296"),
gy: hex_to_fe4("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5"),
n: P256_N,
qlen_bits: 256,
felem_bytes: 32,
}
}
pub fn p384_params() -> CurveParams<6> {
CurveParams {
p: P384_P,
a: hex_to_fe6(
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC",
),
b: hex_to_fe6(
"B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF",
),
gx: hex_to_fe6(
"AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7",
),
gy: hex_to_fe6(
"3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F",
),
n: P384_N,
qlen_bits: 384,
felem_bytes: 48,
}
}
pub fn secp256k1_params() -> CurveParams<4> {
CurveParams {
p: hex_to_limbs::<4>("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F"),
a: hex_to_fe::<4>("0000000000000000000000000000000000000000000000000000000000000000"),
b: hex_to_fe::<4>("0000000000000000000000000000000000000000000000000000000000000007"),
gx: hex_to_fe::<4>("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798"),
gy: hex_to_fe::<4>("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8"),
n: hex_to_limbs::<4>("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"),
qlen_bits: 256,
felem_bytes: 32,
}
}
pub fn brainpoolp256r1_params() -> CurveParams<4> {
CurveParams {
p: hex_to_limbs::<4>("A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377"),
a: hex_to_fe::<4>("7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9"),
b: hex_to_fe::<4>("26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6"),
gx: hex_to_fe::<4>("8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262"),
gy: hex_to_fe::<4>("547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997"),
n: hex_to_limbs::<4>("A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7"),
qlen_bits: 256,
felem_bytes: 32,
}
}
pub fn brainpoolp384r1_params() -> CurveParams<6> {
CurveParams {
p: hex_to_limbs::<6>(
"8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53",
),
a: hex_to_fe::<6>(
"7BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F90F8AA5814A503AD4EB04A8C7DD22CE2826",
),
b: hex_to_fe::<6>(
"04A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62D57CB4390295DBC9943AB78696FA504C11",
),
gx: hex_to_fe::<6>(
"1D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10E8E826E03436D646AAEF87B2E247D4AF1E",
),
gy: hex_to_fe::<6>(
"8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E4646217791811142820341263C5315",
),
n: hex_to_limbs::<6>(
"8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC3103B883202E9046565",
),
qlen_bits: 384,
felem_bytes: 48,
}
}
pub fn brainpoolp512r1_params() -> CurveParams<8> {
CurveParams {
p: hex_to_limbs::<8>(
"AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3",
),
a: hex_to_fe::<8>(
"7830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA",
),
b: hex_to_fe::<8>(
"3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F723",
),
gx: hex_to_fe::<8>(
"81AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D0098EFF3B1F78E2D0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F822",
),
gy: hex_to_fe::<8>(
"7DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892",
),
n: hex_to_limbs::<8>(
"AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069",
),
qlen_bits: 512,
felem_bytes: 64,
}
}
pub fn secp521r1_params() -> CurveParams<9> {
CurveParams {
p: hex_to_limbs::<9>(
"01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
),
a: hex_to_fe::<9>(
"01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC",
),
b: hex_to_fe::<9>(
"0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00",
),
gx: hex_to_fe::<9>(
"00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66",
),
gy: hex_to_fe::<9>(
"011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650",
),
n: hex_to_limbs::<9>(
"01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409",
),
qlen_bits: 521,
felem_bytes: 66,
}
}
pub fn is_on_curve<const LIMBS: usize>(
x: &FieldElement<LIMBS>,
y: &FieldElement<LIMBS>,
params: &CurveParams<LIMBS>,
) -> bool {
let p = ¶ms.p;
let y2 = field_sqr(y, p);
let x2 = field_sqr(x, p);
let x3 = field_mul(&x2, x, p);
let ax = field_mul(¶ms.a, x, p);
let rhs = field_add(&field_add(&x3, &ax, p), ¶ms.b, p);
y2 == rhs
}
pub fn point_double<const LIMBS: usize>(
pt: &JacobianPoint<LIMBS>,
params: &CurveParams<LIMBS>,
) -> JacobianPoint<LIMBS> {
let p = ¶ms.p;
let xx = field_sqr(&pt.x, p);
let yy = field_sqr(&pt.y, p);
let yyyy = field_sqr(&yy, p);
let zz = field_sqr(&pt.z, p);
let x_plus_yy = field_add(&pt.x, &yy, p);
let x_plus_yy_sq = field_sqr(&x_plus_yy, p);
let s_inner = field_sub(&field_sub(&x_plus_yy_sq, &xx, p), &yyyy, p);
let s = field_add(&s_inner, &s_inner, p);
let two_xx = field_add(&xx, &xx, p);
let three_xx = field_add(&two_xx, &xx, p);
let zz_sq = field_sqr(&zz, p);
let a_zz_sq = field_mul(¶ms.a, &zz_sq, p);
let m = field_add(&three_xx, &a_zz_sq, p);
let m_sq = field_sqr(&m, p);
let two_s = field_add(&s, &s, p);
let t = field_sub(&m_sq, &two_s, p);
let x3 = t;
let s_minus_t = field_sub(&s, &t, p);
let m_term = field_mul(&m, &s_minus_t, p);
let two_yyyy = field_add(&yyyy, &yyyy, p);
let four_yyyy = field_add(&two_yyyy, &two_yyyy, p);
let eight_yyyy = field_add(&four_yyyy, &four_yyyy, p);
let y3 = field_sub(&m_term, &eight_yyyy, p);
let y_plus_z = field_add(&pt.y, &pt.z, p);
let y_plus_z_sq = field_sqr(&y_plus_z, p);
let z3 = field_sub(&field_sub(&y_plus_z_sq, &yy, p), &zz, p);
JacobianPoint { x: x3, y: y3, z: z3 }
}
pub fn point_add<const LIMBS: usize>(
p_pt: &JacobianPoint<LIMBS>,
q_pt: &JacobianPoint<LIMBS>,
params: &CurveParams<LIMBS>,
) -> JacobianPoint<LIMBS> {
let p = ¶ms.p;
let p_inf = p_pt.z.is_zero();
let q_inf = q_pt.z.is_zero();
let z1z1 = field_sqr(&p_pt.z, p);
let z1z1z1 = field_mul(&z1z1, &p_pt.z, p);
let z2z2 = field_sqr(&q_pt.z, p);
let z2z2z2 = field_mul(&z2z2, &q_pt.z, p);
let u1 = field_mul(&p_pt.x, &z2z2, p);
let u2 = field_mul(&q_pt.x, &z1z1, p);
let s1 = field_mul(&p_pt.y, &z2z2z2, p);
let s2 = field_mul(&q_pt.y, &z1z1z1, p);
let h = field_sub(&u2, &u1, p);
let r = field_sub(&s2, &s1, p);
let h_zero = h.is_zero();
let r_zero = r.is_zero();
if !p_inf && !q_inf && h_zero && r_zero {
return point_double(p_pt, params);
}
if !p_inf && !q_inf && h_zero && !r_zero {
return JacobianPoint::infinity();
}
let h2 = field_sqr(&h, p);
let h3 = field_mul(&h2, &h, p);
let u1h2 = field_mul(&u1, &h2, p);
let r2 = field_sqr(&r, p);
let u1h2_2 = field_add(&u1h2, &u1h2, p);
let x3 = field_sub(&field_sub(&r2, &h3, p), &u1h2_2, p);
let u1h2_minus_x3 = field_sub(&u1h2, &x3, p);
let r_term = field_mul(&r, &u1h2_minus_x3, p);
let s1h3 = field_mul(&s1, &h3, p);
let y3 = field_sub(&r_term, &s1h3, p);
let z1z2 = field_mul(&p_pt.z, &q_pt.z, p);
let z3 = field_mul(&z1z2, &h, p);
let result = JacobianPoint { x: x3, y: y3, z: z3 };
if p_inf {
*q_pt
} else if q_inf {
*p_pt
} else {
result
}
}
fn point_add_ct<const LIMBS: usize>(
p_pt: &JacobianPoint<LIMBS>,
q_pt: &JacobianPoint<LIMBS>,
params: &CurveParams<LIMBS>,
) -> JacobianPoint<LIMBS> {
let p = ¶ms.p;
let p_inf = p_pt.z.is_zero();
let q_inf = q_pt.z.is_zero();
let z1z1 = field_sqr(&p_pt.z, p);
let z1z1z1 = field_mul(&z1z1, &p_pt.z, p);
let z2z2 = field_sqr(&q_pt.z, p);
let z2z2z2 = field_mul(&z2z2, &q_pt.z, p);
let u1 = field_mul(&p_pt.x, &z2z2, p);
let u2 = field_mul(&q_pt.x, &z1z1, p);
let s1 = field_mul(&p_pt.y, &z2z2z2, p);
let s2 = field_mul(&q_pt.y, &z1z1z1, p);
let h = field_sub(&u2, &u1, p);
let r = field_sub(&s2, &s1, p);
let h2 = field_sqr(&h, p);
let h3 = field_mul(&h2, &h, p);
let u1h2 = field_mul(&u1, &h2, p);
let r2 = field_sqr(&r, p);
let u1h2_2 = field_add(&u1h2, &u1h2, p);
let x3 = field_sub(&field_sub(&r2, &h3, p), &u1h2_2, p);
let u1h2_minus_x3 = field_sub(&u1h2, &x3, p);
let r_term = field_mul(&r, &u1h2_minus_x3, p);
let s1h3 = field_mul(&s1, &h3, p);
let y3 = field_sub(&r_term, &s1h3, p);
let z1z2 = field_mul(&p_pt.z, &q_pt.z, p);
let z3 = field_mul(&z1z2, &h, p);
let general = JacobianPoint { x: x3, y: y3, z: z3 };
let mut out = general;
out = ct_select_point(p_pt, &out, q_inf as u64);
out = ct_select_point(q_pt, &out, p_inf as u64);
out
}
fn ct_select_point<const LIMBS: usize>(
a: &JacobianPoint<LIMBS>,
b: &JacobianPoint<LIMBS>,
condition: u64,
) -> JacobianPoint<LIMBS> {
let mask = 0u64.wrapping_sub(condition);
let inv = !mask;
let mut out = JacobianPoint {
x: FieldElement { limbs: [0u64; LIMBS] },
y: FieldElement { limbs: [0u64; LIMBS] },
z: FieldElement { limbs: [0u64; LIMBS] },
};
for i in 0..LIMBS {
out.x.limbs[i] = (a.x.limbs[i] & mask) | (b.x.limbs[i] & inv);
out.y.limbs[i] = (a.y.limbs[i] & mask) | (b.y.limbs[i] & inv);
out.z.limbs[i] = (a.z.limbs[i] & mask) | (b.z.limbs[i] & inv);
}
out
}
pub fn scalar_mul_point<const LIMBS: usize>(
k: &FieldElement<LIMBS>,
point: &JacobianPoint<LIMBS>,
params: &CurveParams<LIMBS>,
) -> JacobianPoint<LIMBS> {
let mut r0 = JacobianPoint::infinity();
let mut r1 = *point;
let total_bits = LIMBS * 64;
for i in (0..total_bits).rev() {
let limb_idx = i / 64;
let bit_idx = i % 64;
let bit = (k.limbs[limb_idx] >> bit_idx) & 1;
ct_swap(&mut r0, &mut r1, bit);
r1 = point_add_ct(&r0, &r1, params);
r0 = point_double(&r0, params);
ct_swap(&mut r0, &mut r1, bit);
}
r0
}
fn ct_swap<const LIMBS: usize>(a: &mut JacobianPoint<LIMBS>, b: &mut JacobianPoint<LIMBS>, condition: u64) {
let mask = 0u64.wrapping_sub(condition);
for i in 0..LIMBS {
let t = mask & (a.x.limbs[i] ^ b.x.limbs[i]);
a.x.limbs[i] ^= t;
b.x.limbs[i] ^= t;
let t = mask & (a.y.limbs[i] ^ b.y.limbs[i]);
a.y.limbs[i] ^= t;
b.y.limbs[i] ^= t;
let t = mask & (a.z.limbs[i] ^ b.z.limbs[i]);
a.z.limbs[i] ^= t;
b.z.limbs[i] ^= t;
}
}
pub fn double_scalar_mul<const LIMBS: usize>(
k1: &FieldElement<LIMBS>,
g: &JacobianPoint<LIMBS>,
k2: &FieldElement<LIMBS>,
q: &JacobianPoint<LIMBS>,
params: &CurveParams<LIMBS>,
) -> JacobianPoint<LIMBS> {
let total_bits = LIMBS * 64;
let mut result = JacobianPoint::infinity();
let g_plus_q = point_add(g, q, params);
for i in (0..total_bits).rev() {
result = point_double(&result, params);
let limb_idx = i / 64;
let bit_idx = i % 64;
let b1 = (k1.limbs[limb_idx] >> bit_idx) & 1;
let b2 = (k2.limbs[limb_idx] >> bit_idx) & 1;
let to_add = match (b1, b2) {
(1, 1) => Some(&g_plus_q),
(1, 0) => Some(g),
(0, 1) => Some(q),
_ => None,
};
if let Some(pt) = to_add {
result = point_add(&result, pt, params);
}
}
result
}
#[cfg(test)]
mod tests {
use super::*;
fn hex_to_bytes(hex: &str) -> Vec<u8> {
(0..hex.len())
.step_by(2)
.map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap())
.collect()
}
#[test]
fn test_p256_generator_on_curve() {
let params = p256_params();
let p = ¶ms.p;
let x = ¶ms.gx;
let y = ¶ms.gy;
let y2 = field_sqr(y, p);
let x2 = field_sqr(x, p);
let x3 = field_mul(&x2, x, p);
let ax = field_mul(¶ms.a, x, p);
let rhs = field_add(&field_add(&x3, &ax, p), ¶ms.b, p);
assert_eq!(y2, rhs, "Generator point not on curve");
}
#[test]
fn test_p256_scalar_mul_generator() {
let params = p256_params();
let g = JacobianPoint::from_affine(params.gx, params.gy);
let one = FieldElement::<4>::one();
let result = scalar_mul_point(&one, &g, ¶ms);
let (rx, ry) = result.to_affine(¶ms.p).unwrap();
assert_eq!(rx, params.gx);
assert_eq!(ry, params.gy);
}
#[test]
fn test_p256_double_generator() {
let params = p256_params();
let g = JacobianPoint::from_affine(params.gx, params.gy);
let two = FieldElement::<4>::from_bytes_be(&[2]);
let result = scalar_mul_point(&two, &g, ¶ms);
let (rx, _ry) = result.to_affine(¶ms.p).unwrap();
let doubled = point_double(&g, ¶ms);
let (dx, _dy) = doubled.to_affine(¶ms.p).unwrap();
assert_eq!(rx, dx);
}
#[test]
fn test_p256_n_times_g_is_infinity() {
let params = p256_params();
let g = JacobianPoint::from_affine(params.gx, params.gy);
let n = FieldElement::<4> { limbs: params.n };
let result = scalar_mul_point(&n, &g, ¶ms);
assert!(result.is_infinity(), "n*G should be infinity");
}
fn check_generator_on_curve<const LIMBS: usize>(params: &CurveParams<LIMBS>) {
assert!(is_on_curve(¶ms.gx, ¶ms.gy, params), "Generator not on curve");
}
fn check_n_times_g_is_infinity<const LIMBS: usize>(params: &CurveParams<LIMBS>) {
let g = JacobianPoint::from_affine(params.gx, params.gy);
let n = FieldElement::<LIMBS> { limbs: params.n };
let result = scalar_mul_point(&n, &g, params);
assert!(result.is_infinity(), "n*G is not infinity");
}
#[test]
fn test_p384_generator_on_curve() {
check_generator_on_curve(&p384_params());
}
#[test]
fn test_p384_n_times_g_is_infinity() {
check_n_times_g_is_infinity(&p384_params());
}
#[test]
fn test_secp256k1_generator_on_curve() {
check_generator_on_curve(&secp256k1_params());
}
#[test]
fn test_secp256k1_n_times_g_is_infinity() {
check_n_times_g_is_infinity(&secp256k1_params());
}
#[test]
fn test_brainpoolp256r1_generator_on_curve() {
check_generator_on_curve(&brainpoolp256r1_params());
}
#[test]
fn test_brainpoolp256r1_n_times_g_is_infinity() {
check_n_times_g_is_infinity(&brainpoolp256r1_params());
}
#[test]
fn test_brainpoolp384r1_generator_on_curve() {
check_generator_on_curve(&brainpoolp384r1_params());
}
#[test]
fn test_brainpoolp384r1_n_times_g_is_infinity() {
check_n_times_g_is_infinity(&brainpoolp384r1_params());
}
#[test]
fn test_brainpoolp512r1_generator_on_curve() {
check_generator_on_curve(&brainpoolp512r1_params());
}
#[test]
fn test_brainpoolp512r1_n_times_g_is_infinity() {
check_n_times_g_is_infinity(&brainpoolp512r1_params());
}
#[test]
fn test_secp521r1_generator_on_curve() {
check_generator_on_curve(&secp521r1_params());
}
#[test]
fn test_secp521r1_n_times_g_is_infinity() {
check_n_times_g_is_infinity(&secp521r1_params());
}
#[test]
fn test_p256_known_2g() {
let params = p256_params();
let g = JacobianPoint::from_affine(params.gx, params.gy);
let two = FieldElement::<4>::from_bytes_be(&[2]);
let result = scalar_mul_point(&two, &g, ¶ms);
let (rx, ry) = result.to_affine(¶ms.p).unwrap();
let expected_x = hex_to_bytes("7CF27B188D034F7E8A52380304B51AC3C08969E277F21B35A60B48FC47669978");
let expected_y = hex_to_bytes("07775510DB8ED040293D9AC69F7430DBBA7DADE63CE982299E04B79D227873D1");
assert_eq!(rx.to_bytes_be(), expected_x);
assert_eq!(ry.to_bytes_be(), expected_y);
}
}