diff --git a/src/g1.rs b/src/g1.rs index a7bd55878..fbd9f3ae5 100644 --- a/src/g1.rs +++ b/src/g1.rs @@ -1,7 +1,7 @@ //! This module provides an implementation of the $\mathbb{G}_1$ group of BLS12-381. use crate::fp::Fp; -use subtle::Choice; +use subtle::{Choice, ConstantTimeEq}; /// This is an element of $\mathbb{G}_1$ represented in the affine coordinate space. /// It is ideal to keep elements in this representation to reduce memory usage and @@ -16,6 +16,57 @@ pub struct G1Affine { infinity: Choice, } +const B: Fp = Fp::from_raw_unchecked([ + 0xaa270000000cfff3, + 0x53cc0032fc34000a, + 0x478fe97a6b0a807f, + 0xb1d37ebee6ba24d7, + 0x8ec9733bbf78ab2f, + 0x9d645513d83de7e, +]); + +impl G1Affine { + /// Returns the identity of the group: the point at infinity. + pub fn identity() -> G1Affine { + G1Affine { + x: Fp::zero(), + y: Fp::one(), + infinity: Choice::from(1u8), + } + } + + /// Returns a fixed generator of the group. See [`notes::design`](notes/design/index.html#fixed-generators) + /// for how this generator is chosen. + pub fn generator() -> G1Affine { + G1Affine { + x: Fp::from_raw_unchecked([ + 0x5cb38790fd530c16, + 0x7817fc679976fff5, + 0x154f95c7143ba1c1, + 0xf0ae6acdf3d0e747, + 0xedce6ecc21dbf440, + 0x120177419e0bfb75, + ]), + y: Fp::from_raw_unchecked([ + 0xbaac93d50ce72271, + 0x8c22631a7918fd8e, + 0xdd595f13570725ce, + 0x51ac582950405194, + 0xe1c8c3fad0059c0, + 0xbbc3efc5008a26a, + ]), + infinity: Choice::from(0u8), + } + } + + /// 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 { + // y^2 - x^3 ?= 4 + (self.y.square() - (self.x.square() * self.x)).ct_eq(&B) | self.infinity + } +} + /// This is an element of $\mathbb{G}_1$ represented in the projective coordinate space. #[derive(Copy, Clone, Debug)] pub struct G1Projective { @@ -23,3 +74,77 @@ pub struct G1Projective { y: Fp, z: Fp, } + +impl G1Projective { + /// Returns the identity of the group: the point at infinity. + pub fn identity() -> G1Projective { + G1Projective { + x: Fp::zero(), + y: Fp::one(), + z: Fp::zero(), + } + } + + /// Returns a fixed generator of the group. See [`notes::design`](notes/design/index.html#fixed-generators) + /// for how this generator is chosen. + pub fn generator() -> G1Projective { + G1Projective { + x: Fp::from_raw_unchecked([ + 0x5cb38790fd530c16, + 0x7817fc679976fff5, + 0x154f95c7143ba1c1, + 0xf0ae6acdf3d0e747, + 0xedce6ecc21dbf440, + 0x120177419e0bfb75, + ]), + y: Fp::from_raw_unchecked([ + 0xbaac93d50ce72271, + 0x8c22631a7918fd8e, + 0xdd595f13570725ce, + 0x51ac582950405194, + 0xe1c8c3fad0059c0, + 0xbbc3efc5008a26a, + ]), + z: Fp::one(), + } + } + + /// 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 { + // Y^2 - X^3 = 4(Z^6) + + (self.y.square() - (self.x.square() * self.x)) + .ct_eq(&((self.z.square() * self.z).square() * B)) + | self.z.is_zero() + } +} + +#[test] +fn test_is_on_curve() { + assert!(bool::from(G1Affine::identity().is_on_curve())); + assert!(bool::from(G1Affine::generator().is_on_curve())); + assert!(bool::from(G1Projective::identity().is_on_curve())); + assert!(bool::from(G1Projective::generator().is_on_curve())); + + let z = Fp::from_raw_unchecked([ + 0xba7afa1f9a6fe250, + 0xfa0f5b595eafe731, + 0x3bdc477694c306e7, + 0x2149be4b3949fa24, + 0x64aa6e0649b2078c, + 0x12b108ac33643c3e, + ]); + + let gen = G1Affine::generator(); + let mut test = G1Projective { + x: gen.x * (z.square()), + y: gen.y * (z.square() * z), + z, + }; + + assert!(bool::from(test.is_on_curve())); + + test.x = z; + assert!(!bool::from(test.is_on_curve())); +} diff --git a/src/g2.rs b/src/g2.rs index 4cad32f14..225f31ed6 100644 --- a/src/g2.rs +++ b/src/g2.rs @@ -1,7 +1,8 @@ //! This module provides an implementation of the $\mathbb{G}_2$ group of BLS12-381. +use crate::fp::Fp; use crate::fp2::Fp2; -use subtle::Choice; +use subtle::{Choice, ConstantTimeEq}; /// This is an element of $\mathbb{G}_2$ represented in the affine coordinate space. /// It is ideal to keep elements in this representation to reduce memory usage and @@ -16,6 +17,87 @@ pub struct G2Affine { infinity: Choice, } +const B: Fp2 = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xaa270000000cfff3, + 0x53cc0032fc34000a, + 0x478fe97a6b0a807f, + 0xb1d37ebee6ba24d7, + 0x8ec9733bbf78ab2f, + 0x9d645513d83de7e, + ]), + c1: Fp::from_raw_unchecked([ + 0xaa270000000cfff3, + 0x53cc0032fc34000a, + 0x478fe97a6b0a807f, + 0xb1d37ebee6ba24d7, + 0x8ec9733bbf78ab2f, + 0x9d645513d83de7e, + ]), +}; + +impl G2Affine { + /// Returns the identity of the group: the point at infinity. + pub fn identity() -> G2Affine { + G2Affine { + x: Fp2::zero(), + y: Fp2::one(), + infinity: Choice::from(1u8), + } + } + + /// Returns a fixed generator of the group. See [`notes::design`](notes/design/index.html#fixed-generators) + /// for how this generator is chosen. + pub fn generator() -> G2Affine { + G2Affine { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xf5f28fa202940a10, + 0xb3f5fb2687b4961a, + 0xa1a893b53e2ae580, + 0x9894999d1a3caee9, + 0x6f67b7631863366b, + 0x58191924350bcd7, + ]), + c1: Fp::from_raw_unchecked([ + 0xa5a9c0759e23f606, + 0xaaa0c59dbccd60c3, + 0x3bb17e18e2867806, + 0x1b1ab6cc8541b367, + 0xc2b6ed0ef2158547, + 0x11922a097360edf3, + ]), + }, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x4c730af860494c4a, + 0x597cfa1f5e369c5a, + 0xe7e6856caa0a635a, + 0xbbefb5e96e0d495f, + 0x7d3a975f0ef25a2, + 0x83fd8e7e80dae5, + ]), + c1: Fp::from_raw_unchecked([ + 0xadc0fc92df64b05d, + 0x18aa270a2b1461dc, + 0x86adac6a3be4eba0, + 0x79495c4ec93da33a, + 0xe7175850a43ccaed, + 0xb2bc2a163de1bf2, + ]), + }, + infinity: Choice::from(0u8), + } + } + + /// 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 { + // y^2 - x^3 ?= 4(u + 1) + (self.y.square() - (self.x.square() * self.x)).ct_eq(&B) | self.infinity + } +} + /// This is an element of $\mathbb{G}_2$ represented in the projective coordinate space. #[derive(Copy, Clone, Debug)] pub struct G2Projective { @@ -23,3 +105,107 @@ pub struct G2Projective { y: Fp2, z: Fp2, } + +impl G2Projective { + /// Returns the identity of the group: the point at infinity. + pub fn identity() -> G2Projective { + G2Projective { + x: Fp2::zero(), + y: Fp2::one(), + z: Fp2::zero(), + } + } + + /// Returns a fixed generator of the group. See [`notes::design`](notes/design/index.html#fixed-generators) + /// for how this generator is chosen. + pub fn generator() -> G2Projective { + G2Projective { + x: Fp2 { + c0: Fp::from_raw_unchecked([ + 0xf5f28fa202940a10, + 0xb3f5fb2687b4961a, + 0xa1a893b53e2ae580, + 0x9894999d1a3caee9, + 0x6f67b7631863366b, + 0x58191924350bcd7, + ]), + c1: Fp::from_raw_unchecked([ + 0xa5a9c0759e23f606, + 0xaaa0c59dbccd60c3, + 0x3bb17e18e2867806, + 0x1b1ab6cc8541b367, + 0xc2b6ed0ef2158547, + 0x11922a097360edf3, + ]), + }, + y: Fp2 { + c0: Fp::from_raw_unchecked([ + 0x4c730af860494c4a, + 0x597cfa1f5e369c5a, + 0xe7e6856caa0a635a, + 0xbbefb5e96e0d495f, + 0x7d3a975f0ef25a2, + 0x83fd8e7e80dae5, + ]), + c1: Fp::from_raw_unchecked([ + 0xadc0fc92df64b05d, + 0x18aa270a2b1461dc, + 0x86adac6a3be4eba0, + 0x79495c4ec93da33a, + 0xe7175850a43ccaed, + 0xb2bc2a163de1bf2, + ]), + }, + z: Fp2::one(), + } + } + + /// 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 { + // Y^2 - X^3 = 4(u + 1)(Z^6) + + (self.y.square() - (self.x.square() * self.x)) + .ct_eq(&((self.z.square() * self.z).square() * B)) + | self.z.is_zero() + } +} + +#[test] +fn test_is_on_curve() { + assert!(bool::from(G2Affine::identity().is_on_curve())); + assert!(bool::from(G2Affine::generator().is_on_curve())); + assert!(bool::from(G2Projective::identity().is_on_curve())); + assert!(bool::from(G2Projective::generator().is_on_curve())); + + let z = Fp2 { + c0: Fp::from_raw_unchecked([ + 0xba7afa1f9a6fe250, + 0xfa0f5b595eafe731, + 0x3bdc477694c306e7, + 0x2149be4b3949fa24, + 0x64aa6e0649b2078c, + 0x12b108ac33643c3e, + ]), + c1: Fp::from_raw_unchecked([ + 0x125325df3d35b5a8, + 0xdc469ef5555d7fe3, + 0x2d716d2443106a9, + 0x5a1db59a6ff37d0, + 0x7cf7784e5300bb8f, + 0x16a88922c7a5e844, + ]), + }; + + let gen = G2Affine::generator(); + let mut test = G2Projective { + x: gen.x * (z.square()), + y: gen.y * (z.square() * z), + z, + }; + + assert!(bool::from(test.is_on_curve())); + + test.x = z; + assert!(!bool::from(test.is_on_curve())); +} diff --git a/src/lib.rs b/src/lib.rs index 4a9a11da3..7ec5761b1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,6 +26,12 @@ extern crate std; #[macro_use] mod util; +/// Notes about how the BLS12-381 elliptic curve is designed, specified +/// and implemented by this library. +pub mod notes { + pub mod design; +} + mod scalar; pub use scalar::Scalar; diff --git a/src/notes/design.rs b/src/notes/design.rs new file mode 100644 index 000000000..d245260ef --- /dev/null +++ b/src/notes/design.rs @@ -0,0 +1,63 @@ +//! # Design of BLS12-381 +//! ## Fixed Generators +//! +//! Although any generator produced by hashing to $\mathbb{G}_1$ or $\mathbb{G}_2$ is +//! safe to use in a cryptographic protocol, we specify some simple, fixed generators. +//! +//! In order to derive these generators, we select the lexicographically smallest +//! valid $x$-coordinate and the lexicographically smallest corresponding $y$-coordinate, +//! and then scale the resulting point by the cofactor, such that the result is not the +//! identity. This results in the following fixed generators: +//! +//! 1. $\mathbb{G}_1$ +//! * $x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507$ +//! * $y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569$ +//! 2. $\mathbb{G}_2$ +//! * $x = 352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160 + 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758 u$ +//! * $y = 1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905 + 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582 u$ +//! +//! This can be derived using the following sage script: +//! +//! ```norun +//! param = -0xd201000000010000 +//! def r(x): +//! return (x**4) - (x**2) + 1 +//! def q(x): +//! return (((x - 1) ** 2) * ((x**4) - (x**2) + 1) // 3) + x +//! def g1_h(x): +//! return ((x-1)**2) // 3 +//! def g2_h(x): +//! return ((x**8) - (4 * (x**7)) + (5 * (x**6)) - (4 * (x**4)) + (6 * (x**3)) - (4 * (x**2)) - (4*x) + 13) // 9 +//! q = q(param) +//! r = r(param) +//! Fq = GF(q) +//! ec = EllipticCurve(Fq, [0, 4]) +//! def psqrt(v): +//! assert(not v.is_zero()) +//! a = sqrt(v) +//! b = -a +//! if a < b: +//! return a +//! else: +//! return b +//! for x in range(0,100): +//! rhs = Fq(x)^3 + 4 +//! if rhs.is_square(): +//! 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 +//! break +//! Fqx. = PolynomialRing(Fq, 'j') +//! Fq2. = GF(q^2, modulus=j^2 + 1) +//! ec2 = EllipticCurve(Fq2, [0, (4 * (1 + i))]) +//! assert(ec2.order() == (r * g2_h(param))) +//! for x in range(0,100): +//! rhs = (Fq2(x))^3 + (4 * (1 + i)) +//! 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 +//! break +//! ```