diff --git a/benches/groups.rs b/benches/groups.rs index 3fc3df138..845b9bc0b 100644 --- a/benches/groups.rs +++ b/benches/groups.rs @@ -21,6 +21,9 @@ fn criterion_benchmark(c: &mut Criterion) { c.bench_function(&format!("{} scalar multiplication", name), move |b| { b.iter(|| black_box(a) * black_box(s)) }); + c.bench_function(&format!("{} subgroup check", name), move |b| { + b.iter(|| black_box(a).is_torsion_free()) + }); } // G1Projective @@ -66,6 +69,9 @@ fn criterion_benchmark(c: &mut Criterion) { c.bench_function(&format!("{} scalar multiplication", name), move |b| { b.iter(|| black_box(a) * black_box(s)) }); + c.bench_function(&format!("{} subgroup check", name), move |b| { + b.iter(|| black_box(a).is_torsion_free()) + }); } // G2Projective diff --git a/src/g1.rs b/src/g1.rs index 82a7cc49f..bd1ab03ca 100644 --- a/src/g1.rs +++ b/src/g1.rs @@ -186,6 +186,21 @@ impl G1Affine { self.infinity } + /// Returns true if this point is free of an $h$-torsion component, and so it + /// exists within the $q$-order subgroup $\mathbb{G}_1$. This should always return true + /// unless an "unchecked" API was used. + pub fn is_torsion_free(&self) -> Choice { + const FQ_MODULUS_BYTES: [u8; 32] = [ + 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115, + ]; + + // Clear the r-torsion from the point and check if it is the identity + G1Projective::from(*self) + .multiply(&FQ_MODULUS_BYTES) + .is_identity() + } + /// Returns true if this point is on the curve. This should always return /// true unless an "unchecked" API was used. pub fn is_on_curve(&self) -> Choice { @@ -1041,3 +1056,30 @@ fn test_affine_scalar_multiplication() { assert_eq!(G1Affine::from(g * a) * b, g * c); } + +#[test] +fn test_is_torsion_free() { + let a = G1Affine { + x: Fp::from_raw_unchecked([ + 0xabaf895b97e43c8, + 0xba4c6432eb9b61b0, + 0x12506f52adfe307f, + 0x75028c3439336b72, + 0x84744f05b8e9bd71, + 0x113d554fb09554f7, + ]), + y: Fp::from_raw_unchecked([ + 0x73e90e88f5cf01c0, + 0x37007b65dd3197e2, + 0x5cf9a1992f0d7c78, + 0x4f83c10b9eb3330d, + 0xf6a63f6f07f60961, + 0xc53b5b97e634df3, + ]), + infinity: Choice::from(0u8), + }; + assert!(!bool::from(a.is_torsion_free())); + + assert!(bool::from(G1Affine::identity().is_torsion_free())); + assert!(bool::from(G1Affine::generator().is_torsion_free())); +} diff --git a/src/g2.rs b/src/g2.rs index 067cde2a7..5890d1f2e 100644 --- a/src/g2.rs +++ b/src/g2.rs @@ -217,6 +217,21 @@ impl G2Affine { self.infinity } + /// Returns true if this point is free of an $h$-torsion component, and so it + /// exists within the $q$-order subgroup $\mathbb{G}_2$. This should always return true + /// unless an "unchecked" API was used. + pub fn is_torsion_free(&self) -> Choice { + const FQ_MODULUS_BYTES: [u8; 32] = [ + 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, + 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115, + ]; + + // Clear the r-torsion from the point and check if it is the identity + G2Projective::from(*self) + .multiply(&FQ_MODULUS_BYTES) + .is_identity() + } + /// Returns true if this point is on the curve. This should always return /// true unless an "unchecked" API was used. pub fn is_on_curve(&self) -> Choice { @@ -1228,3 +1243,50 @@ fn test_affine_scalar_multiplication() { assert_eq!(G2Affine::from(g * a) * b, g * c); } + +#[test] +fn test_is_torsion_free() { + let a = G2Affine { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x89f550c813db6431, + 0xa50be8c456cd8a1a, + 0xa45b374114cae851, + 0xbb6190f5bf7fff63, + 0x970ca02c3ba80bc7, + 0x2b85d24e840fbac, + ]), + c1: Fp::from_raw_unchecked([ + 0x6888bc53d70716dc, + 0x3dea6b4117682d70, + 0xd8f5f930500ca354, + 0x6b5ecb6556f5c155, + 0xc96bef0434778ab0, + 0x5081505515006ad, + ]), + }, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x3cf1ea0d434b0f40, + 0x1a0dc610e603e333, + 0x7f89956160c72fa0, + 0x25ee03decf6431c5, + 0xeee8e206ec0fe137, + 0x97592b226dfef28, + ]), + c1: Fp::from_raw_unchecked([ + 0x71e8bb5f29247367, + 0xa5fe049e211831ce, + 0xce6b354502a3896, + 0x93b012000997314e, + 0x6759f3b6aa5b42ac, + 0x156944c4dfe92bbb, + ]), + }, + infinity: Choice::from(0u8), + }; + assert!(!bool::from(a.is_torsion_free())); + + assert!(bool::from(G2Affine::identity().is_torsion_free())); + assert!(bool::from(G2Affine::generator().is_torsion_free())); +}