From a5a6f57c5abcf47387d1c9e1ea2ee8ab01925734 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Wed, 12 Aug 2020 18:25:52 +0100 Subject: [PATCH] Squashed 'bls12_381/' changes from 1a2e9f3..d0ea5d4 d0ea5d4 Merge pull request #32 from narodnik/sum 24aa1a4 Merge pull request #31 from zkcrypto/release-0.1.1 fb7c4cb add cargo fmt for sum traits (code we added) ccef392 add sum iterator implementations 82e14ed Release 0.1.1 a3608d4 Put endo optimizations behind endo crate feature. e32494e Merge pull request #18 from mmaker/master 948b199 Fix typo in comment. b3d1fe1 Merge pull request #27 from rex4539/fix-typos 253f681 Merge pull request #25 from mmaker/fix/sage-script c55f88f Fix typos 14b5e16 No need to define a polynomial ring in notes/design.rs. c9d17f6 Make sage script in notes/design.rs work with sage 3.9. af9ec4d Minor changes to comments documenting `clear_cofactor` 7dc6f31 Add clear_cofactor. git-subtree-dir: bls12_381 git-subtree-split: d0ea5d4958cae999dea1800207704171aa07a9ef --- .github/workflows/ci.yml | 12 +- Cargo.toml | 5 +- README.md | 5 +- RELEASES.md | 9 ++ src/g1.rs | 106 +++++++++++++ src/g2.rs | 325 ++++++++++++++++++++++++++++++++++++++- src/notes/design.rs | 9 +- src/scalar.rs | 2 +- 8 files changed, 462 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 39066dbfd..1388989f0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,8 +43,18 @@ jobs: uses: actions-rs/cargo@v1 with: command: build - args: --verbose --release --tests + args: --verbose --release --tests --features endo - name: Run tests + uses: actions-rs/cargo@v1 + with: + command: test + args: --verbose --release --features endo + - name: Build tests (no endomorphism) + uses: actions-rs/cargo@v1 + with: + command: build + args: --verbose --release --tests + - name: Run tests (no endomorphism) uses: actions-rs/cargo@v1 with: command: test diff --git a/Cargo.toml b/Cargo.toml index 0bfb0d443..b4354ef8f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ homepage = "https://github.com/zkcrypto/bls12_381" license = "MIT/Apache-2.0" name = "bls12_381" repository = "https://github.com/zkcrypto/bls12_381" -version = "0.1.0" +version = "0.1.1" edition = "2018" [package.metadata.docs.rs] @@ -30,3 +30,6 @@ groups = [] pairings = ["groups"] alloc = [] nightly = ["subtle/nightly"] + +# GLV patents US7110538B2 and US7995752B2 expire in September 2020. +endo = [] diff --git a/README.md b/README.md index ba61f300b..70a238e50 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ This crate provides an implementation of the BLS12-381 pairing-friendly elliptic * `pairings` (on by default): Enables some APIs for performing pairings. * `alloc` (on by default): Enables APIs that require an allocator; these include pairing optimizations. * `nightly`: Enables `subtle/nightly` which tries to prevent compiler optimizations that could jeopardize constant time operations. Requires the nightly Rust compiler. +* `endo`: Enables optimizations that leverage curve endomorphisms, which may run foul of patents US7110538B2 and US7995752B2 set to expire in September 2020. ## [Documentation](https://docs.rs/bls12_381) @@ -26,7 +27,7 @@ BLS12-381 is a pairing-friendly elliptic curve construction from the [BLS family * q = z4 - z2 + 1 * = `0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001` -... yielding two **source groups** G1 and G2, each of 255-bit prime order `q`, such that an efficiently computable non-degenerate bilinear pairing function `e` exists into a third **target group** GT. Specifically, G1 is the `q`-order subgroup of E(Fp) : y2 = x3 + 4 and G2 is the `q`-order subgroup of E'(Fp2) : y2 = x3 + 4(u + 1) where the extention field Fp2 is defined as Fp(u) / (u2 + 1). +... yielding two **source groups** G1 and G2, each of 255-bit prime order `q`, such that an efficiently computable non-degenerate bilinear pairing function `e` exists into a third **target group** GT. Specifically, G1 is the `q`-order subgroup of E(Fp) : y2 = x3 + 4 and G2 is the `q`-order subgroup of E'(Fp2) : y2 = x3 + 4(u + 1) where the extension field Fp2 is defined as Fp(u) / (u2 + 1). BLS12-381 is chosen so that `z` has small Hamming weight (to improve pairing performance) and also so that `GF(q)` has a large 232 primitive root of unity for performing radix-2 fast Fourier transforms for efficient multi-point evaluation and interpolation. It is also chosen so that it exists in a particularly efficient and rigid subfamily of BLS12 curves. @@ -34,7 +35,7 @@ BLS12-381 is chosen so that `z` has small Hamming weight (to improve pairing per Pairing-friendly elliptic curve constructions are (necessarily) less secure than conventional elliptic curves due to their small "embedding degree". Given a small enough embedding degree, the pairing function itself would allow for a break in DLP hardness if it projected into a weak target group, as weaknesses in this target group are immediately translated into weaknesses in the source group. -In order to achieve reasonable security without an unreasonably expensive pairing function, a careful choice of embedding degree, base field characteristic and prime subgroup order must be made. BLS12-381 uses an embedding degree of 12 to ensure fast pairing performance but a choice of a 381-bit base field characteristic to yeild a 255-bit subgroup order (for protection against [Pollard's rho algorithm](https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm)) while reaching close to a 128-bit security level. +In order to achieve reasonable security without an unreasonably expensive pairing function, a careful choice of embedding degree, base field characteristic and prime subgroup order must be made. BLS12-381 uses an embedding degree of 12 to ensure fast pairing performance but a choice of a 381-bit base field characteristic to yield a 255-bit subgroup order (for protection against [Pollard's rho algorithm](https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm)) while reaching close to a 128-bit security level. There are [known optimizations](https://ellipticnews.wordpress.com/2016/05/02/kim-barbulescu-variant-of-the-number-field-sieve-to-compute-discrete-logarithms-in-finite-fields/) of the [Number Field Sieve algorithm](https://en.wikipedia.org/wiki/General_number_field_sieve) which could be used to weaken DLP security in the target group by taking advantage of its structure, as it is a multiplicative subgroup of a low-degree extension field. However, these attacks require an (as of yet unknown) efficient algorithm for scanning a large space of polynomials. Even if the attack were practical it would only reduce security to roughly 117 to 120 bits. (This contrasts with 254-bit BN curves which usually have less than 100 bits of security in the same situation.) diff --git a/RELEASES.md b/RELEASES.md index 69afd5204..85fcd4ae5 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,12 @@ +# 0.1.1 + +Added `clear_cofactor` methods to `G1Projective` and `G2Projective`. If the crate feature `endo` +is enabled the G2 cofactor clearing will use the curve endomorphism technique described by +[Budroni-Pintore](https://ia.cr/2017/419). If the crate feature `endo` is _not_ enabled then +the code will simulate the effects of the Budroni-Pintore cofactor clearing in order to keep +the API consistent. In September 2020, when patents US7110538B2 and US7995752B2 expire, the +endo feature will be made default. However, for now it must be explicitly enabled. + # 0.1.0 Initial release. diff --git a/src/g1.rs b/src/g1.rs index aa90dc16b..0ab1e1d01 100644 --- a/src/g1.rs +++ b/src/g1.rs @@ -1,5 +1,7 @@ //! This module provides an implementation of the $\mathbb{G}_1$ group of BLS12-381. +use core::borrow::Borrow; +use core::iter::Sum; use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; @@ -140,6 +142,18 @@ impl<'a, 'b> Sub<&'b G1Affine> for &'a G1Projective { } } +impl Sum for G1Projective +where + T: Borrow, +{ + fn sum(iter: I) -> Self + where + I: Iterator, + { + iter.fold(Self::identity(), |acc, item| acc + item.borrow()) + } +} + impl_binops_additive!(G1Projective, G1Affine); impl_binops_additive_specify_output!(G1Affine, G1Projective, G1Projective); @@ -735,6 +749,34 @@ impl G1Projective { acc } + /// Multiply `self` by `crate::BLS_X`, using double and add. + fn mul_by_x(&self) -> G1Projective { + let mut xself = G1Projective::identity(); + // NOTE: in BLS12-381 we can just skip the first bit. + let mut x = crate::BLS_X >> 1; + let mut tmp = *self; + while x != 0 { + tmp = tmp.double(); + + if x % 2 == 1 { + xself += tmp; + } + x >>= 1; + } + // finally, flip the sign + if crate::BLS_X_IS_NEGATIVE { + xself = -xself; + } + xself + } + + /// Multiplies by $(1 - z)$, where $z$ is the parameter of BLS12-381, which + /// [suffices to clear](https://ia.cr/2019/403) the cofactor and map + /// elliptic curve points to elements of $\mathbb{G}\_1$. + pub fn clear_cofactor(&self) -> G1Projective { + self - self.mul_by_x() + } + /// 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]) { @@ -1303,6 +1345,70 @@ fn test_is_torsion_free() { assert!(bool::from(G1Affine::generator().is_torsion_free())); } +#[test] +fn test_mul_by_x() { + // multiplying by `x` a point in G1 is the same as multiplying by + // the equivalent scalar. + let generator = G1Projective::generator(); + let x = if crate::BLS_X_IS_NEGATIVE { + -Scalar::from(crate::BLS_X) + } else { + Scalar::from(crate::BLS_X) + }; + assert_eq!(generator.mul_by_x(), generator * x); + + let point = G1Projective::generator() * Scalar::from(42); + assert_eq!(point.mul_by_x(), point * x); +} + +#[test] +fn test_clear_cofactor() { + // the generator (and the identity) are always on the curve, + // even after clearing the cofactor + let generator = G1Projective::generator(); + assert!(bool::from(generator.clear_cofactor().is_on_curve())); + let id = G1Projective::identity(); + assert!(bool::from(id.clear_cofactor().is_on_curve())); + + let point = G1Projective { + x: Fp::from_raw_unchecked([ + 0x48af5ff540c817f0, + 0xd73893acaf379d5a, + 0xe6c43584e18e023c, + 0x1eda39c30f188b3e, + 0xf618c6d3ccc0f8d8, + 0x0073542cd671e16c, + ]), + y: Fp::from_raw_unchecked([ + 0x57bf8be79461d0ba, + 0xfc61459cee3547c3, + 0x0d23567df1ef147b, + 0x0ee187bcce1d9b64, + 0xb0c8cfbe9dc8fdc1, + 0x1328661767ef368b, + ]), + z: Fp::from_raw_unchecked([ + 0x3d2d1c670671394e, + 0x0ee3a800a2f7c1ca, + 0x270f4f21da2e5050, + 0xe02840a53f1be768, + 0x55debeb597512690, + 0x08bd25353dc8f791, + ]), + }; + + 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())); + + // in BLS12-381 the cofactor in G1 can be + // cleared multiplying by (1-x) + let h_eff = Scalar::from(1) + Scalar::from(crate::BLS_X); + assert_eq!(point.clear_cofactor(), point * h_eff); +} + #[test] fn test_batch_normalize() { let a = G1Projective::generator().double(); diff --git a/src/g2.rs b/src/g2.rs index 136cd03db..8d85d72e9 100644 --- a/src/g2.rs +++ b/src/g2.rs @@ -1,5 +1,7 @@ //! This module provides an implementation of the $\mathbb{G}_2$ group of BLS12-381. +use core::borrow::Borrow; +use core::iter::Sum; use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; @@ -141,6 +143,18 @@ impl<'a, 'b> Sub<&'b G2Affine> for &'a G2Projective { } } +impl Sum for G2Projective +where + T: Borrow, +{ + fn sum(iter: I) -> Self + where + I: Iterator, + { + iter.fold(Self::identity(), |acc, item| acc + item.borrow()) + } +} + impl_binops_additive!(G2Projective, G2Affine); impl_binops_additive_specify_output!(G2Affine, G2Projective, G2Projective); @@ -805,7 +819,7 @@ impl G2Projective { G2Projective::conditional_select(&res, &tmp, (!f1) & (!f2) & (!f3)) } - fn multiply(&self, by: &[u8; 32]) -> G2Projective { + fn multiply(&self, by: &[u8]) -> G2Projective { let mut acc = G2Projective::identity(); // This is a simple double-and-add implementation of point @@ -827,6 +841,132 @@ impl G2Projective { acc } + #[cfg(feature = "endo")] + 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(), + } + } + + #[cfg(feature = "endo")] + 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, + } + } + + /// Multiply `self` by `crate::BLS_X`, using double and add. + #[cfg(feature = "endo")] + fn mul_by_x(&self) -> G2Projective { + let mut xself = G2Projective::identity(); + // NOTE: in BLS12-381 we can just skip the first bit. + let mut x = crate::BLS_X >> 1; + let mut acc = *self; + while x != 0 { + acc = acc.double(); + if x % 2 == 1 { + xself += acc; + } + x >>= 1; + } + // finally, flip the sign + if crate::BLS_X_IS_NEGATIVE { + xself = -xself; + } + xself + } + + /// Clears the cofactor, using [Budroni-Pintore](https://ia.cr/2017/419). + /// This is equivalent to multiplying by $h\_\textrm{eff} = 3(z^2 - 1) \cdot + /// h_2$, where $h_2$ is the cofactor of $\mathbb{G}\_2$ and $z$ is the + /// parameter of BLS12-381. + /// + /// The endomorphism is only actually used if the crate feature `endo` is + /// enabled, and it is disabled by default to mitigate potential patent + /// issues. + pub fn clear_cofactor(&self) -> G2Projective { + #[cfg(feature = "endo")] + fn clear_cofactor(this: &G2Projective) -> G2Projective { + let t1 = this.mul_by_x(); // [x] P + let t2 = this.psi(); // psi(P) + + this.double().psi2() // psi^2(2P) + + (t1 + t2).mul_by_x() // psi^2(2P) + [x^2] P + [x] psi(P) + - t1 // psi^2(2P) + [x^2 - x] P + [x] psi(P) + - t2 // psi^2(2P) + [x^2 - x] P + [x - 1] psi(P) + - this // psi^2(2P) + [x^2 - x - 1] P + [x - 1] psi(P) + } + + #[cfg(not(feature = "endo"))] + fn clear_cofactor(this: &G2Projective) -> G2Projective { + this.multiply(&[ + 0x51, 0x55, 0xa9, 0xaa, 0x5, 0x0, 0x2, 0xe8, 0xb4, 0xf6, 0xbb, 0xde, 0xa, 0x4c, + 0x89, 0x59, 0xa3, 0xf6, 0x89, 0x66, 0xc0, 0xcb, 0x54, 0xe9, 0x1a, 0x7c, 0x47, 0xd7, + 0x69, 0xec, 0xc0, 0x2e, 0xb0, 0x12, 0x12, 0x5d, 0x1, 0xbf, 0x82, 0x6d, 0x95, 0xdb, + 0x31, 0x87, 0x17, 0x2f, 0x9c, 0x32, 0xe1, 0xff, 0x8, 0x15, 0x3, 0xff, 0x86, 0x99, + 0x68, 0xd7, 0x5a, 0x14, 0xe9, 0xa8, 0xe2, 0x88, 0x28, 0x35, 0x1b, 0xa9, 0xe, 0x6a, + 0x4c, 0x58, 0xb3, 0x75, 0xee, 0xf2, 0x8, 0x9f, 0xc6, 0xb, + ]) + } + + clear_cofactor(self) + } + /// 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]) { @@ -1551,6 +1691,189 @@ fn test_is_torsion_free() { assert!(bool::from(G2Affine::generator().is_torsion_free())); } +#[cfg(feature = "endo")] +#[test] +fn test_mul_by_x() { + // multiplying by `x` a point in G2 is the same as multiplying by + // the equivalent scalar. + let generator = G2Projective::generator(); + let x = if crate::BLS_X_IS_NEGATIVE { + -Scalar::from(crate::BLS_X) + } else { + Scalar::from(crate::BLS_X) + }; + assert_eq!(generator.mul_by_x(), generator * x); + + let point = G2Projective::generator() * Scalar::from(42); + assert_eq!(point.mul_by_x(), point * x); +} + +#[cfg(feature = "endo")] +#[test] +fn test_psi() { + let generator = G2Projective::generator(); + + // `point` is a random point in the curve + let point = G2Projective { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xee4c8cb7c047eaf2, + 0x44ca22eee036b604, + 0x33b3affb2aefe101, + 0x15d3e45bbafaeb02, + 0x7bfc2154cd7419a4, + 0x0a2d0c2b756e5edc, + ]), + c1: Fp::from_raw_unchecked([ + 0xfc224361029a8777, + 0x4cbf2baab8740924, + 0xc5008c6ec6592c89, + 0xecc2c57b472a9c2d, + 0x8613eafd9d81ffb1, + 0x10fe54daa2d3d495, + ]), + }, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x7de7edc43953b75c, + 0x58be1d2de35e87dc, + 0x5731d30b0e337b40, + 0xbe93b60cfeaae4c9, + 0x8b22c203764bedca, + 0x01616c8d1033b771, + ]), + c1: Fp::from_raw_unchecked([ + 0xea126fe476b5733b, + 0x85cee68b5dae1652, + 0x98247779f7272b04, + 0xa649c8b468c6e808, + 0xb5b9a62dff0c4e45, + 0x1555b67fc7bbe73d, + ]), + }, + z: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x0ef2ddffab187c0a, + 0x2424522b7d5ecbfc, + 0xc6f341a3398054f4, + 0x5523ddf409502df0, + 0xd55c0b5a88e0dd97, + 0x066428d704923e52, + ]), + c1: Fp::from_raw_unchecked([ + 0x538bbe0c95b4878d, + 0xad04a50379522881, + 0x6d5c05bf5c12fb64, + 0x4ce4a069a2d34787, + 0x59ea6c8d0dffaeaf, + 0x0d42a083a75bd6f3, + ]), + }, + }; + assert!(bool::from(point.is_on_curve())); + + // psi2(P) = psi(psi(P)) + assert_eq!(generator.psi2(), generator.psi().psi()); + assert_eq!(point.psi2(), point.psi().psi()); + // psi(P) is a morphism + assert_eq!(generator.double().psi(), generator.psi().double()); + assert_eq!(point.psi() + generator.psi(), (point + generator).psi()); + // psi(P) behaves in the same way on the same projective point + let mut normalized_point = [G2Affine::identity()]; + G2Projective::batch_normalize(&[point], &mut normalized_point); + let normalized_point = G2Projective::from(normalized_point[0]); + assert_eq!(point.psi(), normalized_point.psi()); + assert_eq!(point.psi2(), normalized_point.psi2()); +} + +#[test] +fn test_clear_cofactor() { + // `point` is a random point in the curve + let point = G2Projective { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xee4c8cb7c047eaf2, + 0x44ca22eee036b604, + 0x33b3affb2aefe101, + 0x15d3e45bbafaeb02, + 0x7bfc2154cd7419a4, + 0x0a2d0c2b756e5edc, + ]), + c1: Fp::from_raw_unchecked([ + 0xfc224361029a8777, + 0x4cbf2baab8740924, + 0xc5008c6ec6592c89, + 0xecc2c57b472a9c2d, + 0x8613eafd9d81ffb1, + 0x10fe54daa2d3d495, + ]), + }, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x7de7edc43953b75c, + 0x58be1d2de35e87dc, + 0x5731d30b0e337b40, + 0xbe93b60cfeaae4c9, + 0x8b22c203764bedca, + 0x01616c8d1033b771, + ]), + c1: Fp::from_raw_unchecked([ + 0xea126fe476b5733b, + 0x85cee68b5dae1652, + 0x98247779f7272b04, + 0xa649c8b468c6e808, + 0xb5b9a62dff0c4e45, + 0x1555b67fc7bbe73d, + ]), + }, + z: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x0ef2ddffab187c0a, + 0x2424522b7d5ecbfc, + 0xc6f341a3398054f4, + 0x5523ddf409502df0, + 0xd55c0b5a88e0dd97, + 0x066428d704923e52, + ]), + c1: Fp::from_raw_unchecked([ + 0x538bbe0c95b4878d, + 0xad04a50379522881, + 0x6d5c05bf5c12fb64, + 0x4ce4a069a2d34787, + 0x59ea6c8d0dffaeaf, + 0x0d42a083a75bd6f3, + ]), + }, + }; + + 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())); + + // the generator (and the identity) are always on the curve, + // even after clearing the cofactor + let generator = G2Projective::generator(); + assert!(bool::from(generator.clear_cofactor().is_on_curve())); + let id = G2Projective::identity(); + assert!(bool::from(id.clear_cofactor().is_on_curve())); + + // test the effect on q-torsion points multiplying by h_eff modulo |Scalar| + // h_eff % q = 0x2b116900400069009a40200040001ffff + let h_eff_modq: [u8; 32] = [ + 0xff, 0xff, 0x01, 0x00, 0x04, 0x00, 0x02, 0xa4, 0x09, 0x90, 0x06, 0x00, 0x04, 0x90, 0x16, + 0xb1, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + ]; + assert_eq!(generator.clear_cofactor(), generator.multiply(&h_eff_modq)); + assert_eq!( + cleared_point.clear_cofactor(), + cleared_point.multiply(&h_eff_modq) + ); +} + #[test] fn test_batch_normalize() { let a = G2Projective::generator().double(); diff --git a/src/notes/design.rs b/src/notes/design.rs index d245260ef..0f8c7fafb 100644 --- a/src/notes/design.rs +++ b/src/notes/design.rs @@ -46,10 +46,9 @@ //! y = psqrt(rhs) //! p = ec(x, y) * g1_h(param) //! if (not p.is_zero()) and (p * r).is_zero(): -//! print "g1 generator: %s" % p +//! print("g1 generator: {}".format(p)) //! break -//! Fqx. = PolynomialRing(Fq, 'j') -//! Fq2. = GF(q^2, modulus=j^2 + 1) +//! Fq2. = GF(q^2, modulus=[1, 0, 1]) //! ec2 = EllipticCurve(Fq2, [0, (4 * (1 + i))]) //! assert(ec2.order() == (r * g2_h(param))) //! for x in range(0,100): @@ -57,7 +56,7 @@ //! if rhs.is_square(): //! y = psqrt(rhs) //! p = ec2(Fq2(x), y) * g2_h(param) -//! if (not p.is_zero()) and (p * r).is_zero(): -//! print "g2 generator: %s" % p +//! if not p.is_zero() and (p * r).is_zero(): +//! print("g2 generator: {}".format(p)) //! break //! ``` diff --git a/src/scalar.rs b/src/scalar.rs index d4a7ab2d2..b3140afdb 100644 --- a/src/scalar.rs +++ b/src/scalar.rs @@ -256,7 +256,7 @@ impl Scalar { // // and computing their sum in the field. It remains to see that arbitrary 256-bit // numbers can be placed into Montgomery form safely using the reduction. The - // reduction works so long as the product is less than R=2^256 multipled by + // reduction works so long as the product is less than R=2^256 multiplied by // the modulus. This holds because for any `c` smaller than the modulus, we have // that (2^256 - 1)*c is an acceptable product for the reduction. Therefore, the // reduction always works so long as `c` is in the field; in this case it is either the