github.com/cloudflare/circl@v1.5.0/ecc/bls12381/g2.go (about) 1 package bls12381 2 3 import ( 4 "crypto" 5 "crypto/subtle" 6 "fmt" 7 8 "github.com/cloudflare/circl/ecc/bls12381/ff" 9 "github.com/cloudflare/circl/expander" 10 ) 11 12 // G2Size is the length in bytes of an element in G2 in uncompressed form.. 13 const G2Size = 2 * ff.Fp2Size 14 15 // G2SizeCompressed is the length in bytes of an element in G2 in compressed form. 16 const G2SizeCompressed = ff.Fp2Size 17 18 // G2 is a point in the twist of the BLS12 curve over Fp2. 19 type G2 struct{ x, y, z ff.Fp2 } 20 21 func (g G2) String() string { return fmt.Sprintf("x: %v\ny: %v\nz: %v", g.x, g.y, g.z) } 22 23 // Bytes serializes a G2 element in uncompressed form. 24 func (g G2) Bytes() []byte { return g.encodeBytes(false) } 25 26 // Bytes serializes a G2 element in compressed form. 27 func (g G2) BytesCompressed() []byte { return g.encodeBytes(true) } 28 29 // SetBytes sets g to the value in bytes, and returns a non-nil error if not in G2. 30 func (g *G2) SetBytes(b []byte) error { 31 if len(b) < G2SizeCompressed { 32 return errInputLength 33 } 34 35 // Check for invalid prefixes 36 switch b[0] & 0xE0 { 37 case 0x20, 0x60, 0xE0: 38 return errEncoding 39 } 40 41 isCompressed := int((b[0] >> 7) & 0x1) 42 isInfinity := int((b[0] >> 6) & 0x1) 43 isBigYCoord := int((b[0] >> 5) & 0x1) 44 45 if isInfinity == 1 { 46 l := G2Size 47 if isCompressed == 1 { 48 l = G2SizeCompressed 49 } 50 zeros := make([]byte, l-1) 51 if (b[0]&0x1F) != 0 || subtle.ConstantTimeCompare(b[1:l], zeros) != 1 { 52 return errEncoding 53 } 54 g.SetIdentity() 55 return nil 56 } 57 58 x := (&[ff.Fp2Size]byte{})[:] 59 copy(x, b) 60 x[0] &= 0x1F 61 if err := g.x.UnmarshalBinary(x); err != nil { 62 return err 63 } 64 65 if isCompressed == 1 { 66 x3b := &ff.Fp2{} 67 x3b.Sqr(&g.x) 68 x3b.Mul(x3b, &g.x) 69 x3b.Add(x3b, &g2Params.b) 70 if g.y.Sqrt(x3b) == 0 { 71 return errEncoding 72 } 73 if g.y.IsNegative() != isBigYCoord { 74 g.y.Neg() 75 } 76 } else { 77 if len(b) < G2Size { 78 return errInputLength 79 } 80 if err := g.y.UnmarshalBinary(b[ff.Fp2Size:G2Size]); err != nil { 81 return err 82 } 83 } 84 85 g.z.SetOne() 86 if !g.IsOnG2() { 87 return errEncoding 88 } 89 return nil 90 } 91 92 func (g G2) encodeBytes(compressed bool) []byte { 93 g.toAffine() 94 var isCompressed, isInfinity, isBigYCoord byte 95 if compressed { 96 isCompressed = 1 97 } 98 if g.z.IsZero() == 1 { 99 isInfinity = 1 100 } 101 if isCompressed == 1 && isInfinity == 0 { 102 isBigYCoord = byte(g.y.IsNegative()) 103 } 104 105 bytes, _ := g.x.MarshalBinary() 106 if isCompressed == 0 { 107 yBytes, _ := g.y.MarshalBinary() 108 bytes = append(bytes, yBytes...) 109 } 110 if isInfinity == 1 { 111 l := len(bytes) 112 for i := 0; i < l; i++ { 113 bytes[i] = 0 114 } 115 } 116 117 bytes[0] = bytes[0]&0x1F | headerEncoding(isCompressed, isInfinity, isBigYCoord) 118 119 return bytes 120 } 121 122 // Neg inverts g. 123 func (g *G2) Neg() { g.y.Neg() } 124 125 // SetIdentity assigns g to the identity element. 126 func (g *G2) SetIdentity() { g.x = ff.Fp2{}; g.y.SetOne(); g.z = ff.Fp2{} } 127 128 // isValidProjective returns true if the point is not a projective point. 129 func (g *G2) isValidProjective() bool { return (g.x.IsZero() & g.y.IsZero() & g.z.IsZero()) != 1 } 130 131 // IsOnG2 returns true if the point is in the group G2. 132 func (g *G2) IsOnG2() bool { return g.isValidProjective() && g.isOnCurve() && g.isRTorsion() } 133 134 // IsIdentity return true if the point is the identity of G2. 135 func (g *G2) IsIdentity() bool { return g.isValidProjective() && (g.z.IsZero() == 1) } 136 137 // cmov sets g to P if b == 1 138 func (g *G2) cmov(P *G2, b int) { 139 (&g.x).CMov(&g.x, &P.x, b) 140 (&g.y).CMov(&g.y, &P.y, b) 141 (&g.z).CMov(&g.z, &P.z, b) 142 } 143 144 // isRTorsion returns true if point is in the r-torsion subgroup. 145 func (g *G2) isRTorsion() bool { 146 // Bowe, "Faster Subgroup Checks for BLS12-381" (https://eprint.iacr.org/2019/814) 147 _z := bls12381.minusZ[:] 148 Q := *g 149 Q.psi() // Q = \psi(g) 150 Q.scalarMultShort(_z, &Q) // Q = -[z]\psi(g) 151 Q.Add(&Q, g) // Q = -[z]\psi(g)+g 152 Q.psi() // Q = -[z]\psi^2(g)+\psi(g) 153 Q.psi() // Q = -[z]\psi^3(g)+\psi^2(g) 154 155 return Q.IsEqual(g) // Equivalent to verification equation in paper 156 } 157 158 // psi is the Galbraith-Scott endomorphism. See https://eprint.iacr.org/2008/117. 159 func (g *G2) psi() { 160 g.x.Frob(&g.x) 161 g.y.Frob(&g.y) 162 g.z.Frob(&g.z) 163 g.x.Mul(&g2Psi.alpha, &g.x) 164 g.y.Mul(&g2Psi.beta, &g.y) 165 } 166 167 // clearCofactor maps g to a point in the r-torsion subgroup. 168 // 169 // This method multiplies g times a multiple of the cofactor as proposed by 170 // Fuentes-Knapp-Rodríguez at https://doi.org/10.1007/978-3-642-28496-0_25. 171 // 172 // The explicit formulas for BLS curves are in Section 4.1 of Budroni-Pintore 173 // "Efficient hash maps to G2 on BLS curves" at https://eprint.iacr.org/2017/419 174 // 175 // h(a)P = [x^2-x-1]P + [x-1]ψ(P) + ψ^2(2P) 176 func (g *G2) clearCofactor() { 177 x := bls12381.minusZ[:] 178 xP, psiP := &G2{}, &G2{} 179 _2P := *g 180 181 _2P.Double() // 2P 182 _2P.psi() // ψ(2P) 183 _2P.psi() // ψ^2(2P) 184 xP.scalarMultShort(x, g) // -xP 185 xP.Add(xP, g) // -xP + P = [-x+1]P 186 *psiP = *xP // 187 psiP.psi() // ψ(-xP + P) = [-x+1]ψ(P) 188 xP.scalarMultShort(x, xP) // x^2P - xP = [x^2-x]P 189 g.Add(g, psiP) // P + [-x+1]ψ(P) 190 g.Neg() // -P + [x-1]ψ(P) 191 g.Add(g, xP) // [x^2-x-1]P + [x-1]ψ(P) 192 g.Add(g, &_2P) // [x^2-x-1]P + [x-1]ψ(P) + 2ψ^2(P) 193 } 194 195 // Double updates g = 2g. 196 func (g *G2) Double() { doubleAndLine(g, nil) } 197 198 // Add updates g=P+Q. 199 func (g *G2) Add(P, Q *G2) { addAndLine(g, P, Q, nil) } 200 201 // ScalarMult calculates g = kP. 202 func (g *G2) ScalarMult(k *Scalar, P *G2) { b, _ := k.MarshalBinary(); g.scalarMult(b, P) } 203 204 // scalarMult calculates g = kP, where k is the scalar in big-endian order. 205 func (g *G2) scalarMult(k []byte, P *G2) { 206 var Q G2 207 Q.SetIdentity() 208 T := &G2{} 209 var mults [16]G2 210 mults[0].SetIdentity() 211 mults[1] = *P 212 for i := 1; i < 8; i++ { 213 mults[2*i] = mults[i] 214 mults[2*i].Double() 215 mults[2*i+1].Add(&mults[2*i], P) 216 } 217 N := 8 * len(k) 218 for i := 0; i < N; i += 4 { 219 Q.Double() 220 Q.Double() 221 Q.Double() 222 Q.Double() 223 idx := 0xf & (k[i/8] >> uint(4-i%8)) 224 for j := 0; j < 16; j++ { 225 T.cmov(&mults[j], subtle.ConstantTimeByteEq(idx, uint8(j))) 226 } 227 Q.Add(&Q, T) 228 } 229 *g = Q 230 } 231 232 // scalarMultShort multiplies by a short, constant scalar k, where k is the 233 // scalar in big-endian order. Runtime depends on the scalar. 234 func (g *G2) scalarMultShort(k []byte, P *G2) { 235 // Since the scalar is short and low Hamming weight not much helps. 236 var Q G2 237 Q.SetIdentity() 238 N := 8 * len(k) 239 for i := 0; i < N; i++ { 240 Q.Double() 241 bit := 0x1 & (k[i/8] >> uint(7-i%8)) 242 if bit != 0 { 243 Q.Add(&Q, P) 244 } 245 } 246 *g = Q 247 } 248 249 // IsEqual returns true if g and p are equivalent. 250 func (g *G2) IsEqual(p *G2) bool { 251 var lx, rx, ly, ry ff.Fp2 252 lx.Mul(&g.x, &p.z) // lx = x1*z2 253 rx.Mul(&p.x, &g.z) // rx = x2*z1 254 lx.Sub(&lx, &rx) // lx = lx-rx 255 ly.Mul(&g.y, &p.z) // ly = y1*z2 256 ry.Mul(&p.y, &g.z) // ry = y2*z1 257 ly.Sub(&ly, &ry) // ly = ly-ry 258 return lx.IsZero() == 1 && ly.IsZero() == 1 259 } 260 261 // EncodeToCurve is a non-uniform encoding from an input byte string (and 262 // an optional domain separation tag) to elements in G2. This function must not 263 // be used as a hash function, otherwise use G2.Hash instead. 264 func (g *G2) Encode(input, dst []byte) { 265 const L = 64 266 pseudo := expander.NewExpanderMD(crypto.SHA256, dst).Expand(input, 2*L) 267 268 var u ff.Fp2 269 u[0].SetBytes(pseudo[0*L : 1*L]) 270 u[1].SetBytes(pseudo[1*L : 2*L]) 271 272 var q isogG2Point 273 q.sswu(&u) 274 g.evalIsogG2(&q) 275 g.clearCofactor() 276 } 277 278 // Hash produces an element of G2 from the hash of an input byte string and 279 // an optional domain separation tag. This function is safe to use when a 280 // random oracle returning points in G2 be required. 281 func (g *G2) Hash(input, dst []byte) { 282 const L = 64 283 pseudo := expander.NewExpanderMD(crypto.SHA256, dst).Expand(input, 4*L) 284 285 var u0, u1 ff.Fp2 286 u0[0].SetBytes(pseudo[0*L : 1*L]) 287 u0[1].SetBytes(pseudo[1*L : 2*L]) 288 u1[0].SetBytes(pseudo[2*L : 3*L]) 289 u1[1].SetBytes(pseudo[3*L : 4*L]) 290 291 var q0, q1 isogG2Point 292 q0.sswu(&u0) 293 q1.sswu(&u1) 294 var p0, p1 G2 295 p0.evalIsogG2(&q0) 296 p1.evalIsogG2(&q1) 297 g.Add(&p0, &p1) 298 g.clearCofactor() 299 } 300 301 // isOnCurve returns true if g is a valid point on the curve. 302 func (g *G2) isOnCurve() bool { 303 var x3, z3, y2 ff.Fp2 304 y2.Sqr(&g.y) // y2 = y^2 305 y2.Mul(&y2, &g.z) // y2 = y^2*z 306 x3.Sqr(&g.x) // x3 = x^2 307 x3.Mul(&x3, &g.x) // x3 = x^3 308 z3.Sqr(&g.z) // z3 = z^2 309 z3.Mul(&z3, &g.z) // z3 = z^3 310 z3.Mul(&z3, &g2Params.b) // z3 = (4+4i)*z^3 311 x3.Add(&x3, &z3) // x3 = x^3 + (4+4i)*z^3 312 y2.Sub(&y2, &x3) // y2 = y^2*z - (x^3 + (4+4i)*z^3) 313 return y2.IsZero() == 1 314 } 315 316 // toAffine updates g with its affine representation. 317 func (g *G2) toAffine() { 318 if g.z.IsZero() != 1 { 319 var invZ ff.Fp2 320 invZ.Inv(&g.z) 321 g.x.Mul(&g.x, &invZ) 322 g.y.Mul(&g.y, &invZ) 323 g.z.SetOne() 324 } 325 } 326 327 // G2Generator returns the generator point of G2. 328 func G2Generator() *G2 { 329 var G G2 330 G.x = g2Params.genX 331 G.y = g2Params.genY 332 G.z.SetOne() 333 return &G 334 }