Skip to content

Commit

Permalink
Add clear_cofactor.
Browse files Browse the repository at this point in the history
In g1, simply multiply by the cofactor.
In g2, we use Budroni-Pintore.
  • Loading branch information
mmaker committed Nov 20, 2019
1 parent ea4374c commit 1f09a0b
Show file tree
Hide file tree
Showing 2 changed files with 197 additions and 0 deletions.
44 changes: 44 additions & 0 deletions src/g1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,14 @@ impl G1Projective {
acc
}

/// Clear the cofactor, multiplying by |G1|/|Scalar|.
/// In BLS curves, this is (x-1)^2/3.
pub fn clear_cofactor(&self) -> G1Projective {
// cofactor = (x - 1)^2 / 3 = 76329603384216526031706109802092473003
let cofactor = Scalar::from_raw([0x8c00aaab0000aaab, 0x396c8c005555e156, 0x0, 0x0]);
self * cofactor
}

/// Converts a batch of `G1Projective` elements into `G1Affine` elements. This
/// function will panic if `p.len() != q.len()`.
pub fn batch_normalize(p: &[Self], q: &mut [G1Affine]) {
Expand Down Expand Up @@ -1303,6 +1311,42 @@ fn test_is_torsion_free() {
assert!(bool::from(G1Affine::generator().is_torsion_free()));
}

#[test]
fn test_clear_cofactor() {
// the generator (and the identity) are always on the curve,
// even after clearning the cofactor
let a = G1Projective::generator();
assert!(bool::from(a.clear_cofactor().is_on_curve()));
let a = G1Projective::identity();
assert!(bool::from(a.clear_cofactor().is_on_curve()));

let point = G1Projective {
x: Fp::from_raw_unchecked([
0x39fe026102c62b0f,
0x34897857a2bb9fdd,
0x5d4b41e803880f57,
0x526a0545c64c3db2,
0x279ff7fd830e66dc,
0x08c780c2d4e82eaa,
]),
y: Fp::from_raw_unchecked([
0xa5e12b97de368eca,
0x95e6fb4eea1a99db,
0x663cd6052bb322f7,
0x4fd2fe2300c90ed2,
0x8a4d37b0f994fbf4,
0x12f8cab78adc3c67,
]),
z: Fp::one(),
};

assert!(bool::from(point.is_on_curve()));
assert!(!bool::from(G1Affine::from(point).is_torsion_free()));
let cleared_point = point.clear_cofactor();
assert!(bool::from(cleared_point.is_on_curve()));
assert!(bool::from(G1Affine::from(cleared_point).is_torsion_free()));
}

#[test]
fn test_batch_normalize() {
let a = G1Projective::generator().double();
Expand Down
153 changes: 153 additions & 0 deletions src/g2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,90 @@ impl G2Projective {
acc
}

fn psi(&self) -> G2Projective {
// 1 / ((u+1) ^ ((q-1)/3))
let psi_coeff_x = Fp2 {
c0: Fp::zero(),
c1: Fp::from_raw_unchecked([
0x890dc9e4867545c3,
0x2af322533285a5d5,
0x50880866309b7e2c,
0xa20d1b8c7e881024,
0x14e4f04fe2db9068,
0x14e56d3f1564853a,
]),
};
// 1 / ((u+1) ^ (p-1)/2)
let psi_coeff_y = Fp2 {
c0: Fp::from_raw_unchecked([
0x3e2f585da55c9ad1,
0x4294213d86c18183,
0x382844c88b623732,
0x92ad2afd19103e18,
0x1d794e4fac7cf0b9,
0x0bd592fc7d825ec8,
]),
c1: Fp::from_raw_unchecked([
0x7bcfa7a25aa30fda,
0xdc17dec12a927e7c,
0x2f088dd86b4ebef1,
0xd1ca2087da74d4a7,
0x2da2596696cebc1d,
0x0e2b7eedbbfd87d2,
]),
};

G2Projective {
// x = frobenius(x)/((u+1)^((p-1)/3))
x: self.x.frobenius_map() * psi_coeff_x,
// y = frobenius(y)/(u+1)^((p-1)/2)
y: self.y.frobenius_map() * psi_coeff_y,
// z = frobenius(z)
z: self.z.frobenius_map(),
}
}

fn psi2(&self) -> G2Projective {
// 1 / 2 ^ ((q-1)/3)
let psi2_coeff_x = Fp2 {
c0: Fp::from_raw_unchecked([
0xcd03c9e48671f071,
0x5dab22461fcda5d2,
0x587042afd3851b95,
0x8eb60ebe01bacb9e,
0x03f97d6e83d050d2,
0x18f0206554638741,
]),
c1: Fp::zero(),
};

G2Projective {
// x = frobenius^2(x)/2^((p-1)/3)
x: self.x.frobenius_map().frobenius_map() * psi2_coeff_x,
// y = -frobenius^2(y)
y: self.y.frobenius_map().frobenius_map().neg(),
// z = z
z: self.z,
}
}

/// Clears the cofactor, using Budroni-Pintore.
/// this is equivalent to multiplying by:
/// h_eff = 3 * (x^2 - 1) * cofactor, where
/// cofactor = |G2| / |Scalar|.
/// https://eprint.iacr.org/2017/419.pdf
pub fn clear_cofactor(&self) -> G2Projective {
// (x^2 - x - 1) = 0xac45a4010001a402d20100010000ffff
let const_coeff = Scalar::from_raw([0xd20100010000ffff, 0xac45a4010001a402, 0x0, 0x0]);

// (x - 1) = -0xd201000000010001
// nota bene: we negate the point in the next equation
let linear_coeff = Scalar::from(crate::BLS_X + 1);

// return 2 * psi(psi(self)) + linear_coeff * psi(self) * const_coeff * self
self.psi2().double() - self.psi() * linear_coeff + self * const_coeff
}

/// Converts a batch of `G2Projective` elements into `G2Affine` elements. This
/// function will panic if `p.len() != q.len()`.
pub fn batch_normalize(p: &[Self], q: &mut [G2Affine]) {
Expand Down Expand Up @@ -1551,6 +1635,75 @@ fn test_is_torsion_free() {
assert!(bool::from(G2Affine::generator().is_torsion_free()));
}

#[test]
fn test_psi() {
// psi2(P) = psi(psi(P))
let a = G2Projective::generator();
assert_eq!(a.psi2(), a.psi().psi());

// psi(P) is an endomorphism, so psi(2P) = 2psi(P)
let a = G2Projective::generator();
assert_eq!(a.double().psi(), a.psi().double());
}

#[test]
fn test_clear_cofactor() {
// the generator (and the identity) are always on the curve,
// even after clearning the cofactor
let a = G2Projective::generator();
assert!(bool::from(a.clear_cofactor().is_on_curve()));
let a = G2Projective::identity();
assert!(bool::from(a.clear_cofactor().is_on_curve()));

// `point` is a random point in the curve
let point = G2Projective {
x: Fp2 {
c0: Fp::from_raw_unchecked([
0x948f204331b6131c,
0xee5566746c718524,
0x745d37136fe4dc14,
0xa9b374978aa4548c,
0xa4de7aebe42d8f26,
0x0db1fb08a6da0500,
]),
c1: Fp::from_raw_unchecked([
0x255beaba2e7e12aa,
0x4233ffff75344a10,
0xd1ae7904d08a8961,
0x0c9c3ced8c65f89d,
0x2d3bcc73bd6e353f,
0x187cf7656536ccfb,
]),
},
y: Fp2 {
c0: Fp::from_raw_unchecked([
0x01e5a459c6549285,
0xea297a7d9a7d528d,
0x495a09d4f8f5c0bf,
0xc75239a28dfdaf44,
0x9a50d828c3e56bfe,
0x11336aee07d0c318,
]),
c1: Fp::from_raw_unchecked([
0xc1e17a97abc6aaa8,
0x9fb7712411acf191,
0xe04fd3be6b4bdedd,
0xf37b623b78e08acd,
0x8f4b270bd2eb12db,
0x1114bca06b7e4eab,
]),
},
z: Fp2::one(),
};

assert!(bool::from(point.is_on_curve()));
assert!(!bool::from(G2Affine::from(point).is_torsion_free()));
let cleared_point = point.clear_cofactor();

assert!(bool::from(cleared_point.is_on_curve()));
assert!(bool::from(G2Affine::from(cleared_point).is_torsion_free()));
}

#[test]
fn test_batch_normalize() {
let a = G2Projective::generator().double();
Expand Down

0 comments on commit 1f09a0b

Please sign in to comment.