github.com/consensys/gnark-crypto@v0.14.0/ecc/bw6-756/internal/fptower/e6.go (about) 1 // Copyright 2020 ConsenSys Software Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package fptower 16 17 import ( 18 "errors" 19 "math/big" 20 "sync" 21 22 "github.com/consensys/gnark-crypto/ecc" 23 "github.com/consensys/gnark-crypto/ecc/bw6-756/fp" 24 "github.com/consensys/gnark-crypto/ecc/bw6-756/fr" 25 ) 26 27 var bigIntPool = sync.Pool{ 28 New: func() interface{} { 29 return new(big.Int) 30 }, 31 } 32 33 // E6 is a degree two finite field extension of fp3 34 type E6 struct { 35 B0, B1 E3 36 } 37 38 // Equal returns true if z equals x, false otherwise 39 func (z *E6) Equal(x *E6) bool { 40 return z.B0.Equal(&x.B0) && z.B1.Equal(&x.B1) 41 } 42 43 // String puts E6 in string form 44 func (z *E6) String() string { 45 return (z.B0.String() + "+(" + z.B1.String() + ")*v") 46 } 47 48 // SetString sets a E6 from string 49 func (z *E6) SetString(s0, s1, s2, s3, s4, s5 string) *E6 { 50 z.B0.SetString(s0, s1, s2) 51 z.B1.SetString(s3, s4, s5) 52 return z 53 } 54 55 // Set copies x into z and returns z 56 func (z *E6) Set(x *E6) *E6 { 57 *z = *x 58 return z 59 } 60 61 // SetOne sets z to 1 in Montgomery form and returns z 62 func (z *E6) SetOne() *E6 { 63 *z = E6{} 64 z.B0.A0.SetOne() 65 return z 66 } 67 68 // Add sets z=x+y in E6 and returns z 69 func (z *E6) Add(x, y *E6) *E6 { 70 z.B0.Add(&x.B0, &y.B0) 71 z.B1.Add(&x.B1, &y.B1) 72 return z 73 } 74 75 // Sub sets z to x-y and returns z 76 func (z *E6) Sub(x, y *E6) *E6 { 77 z.B0.Sub(&x.B0, &y.B0) 78 z.B1.Sub(&x.B1, &y.B1) 79 return z 80 } 81 82 // Double sets z=2*x and returns z 83 func (z *E6) Double(x *E6) *E6 { 84 z.B0.Double(&x.B0) 85 z.B1.Double(&x.B1) 86 return z 87 } 88 89 // SetRandom used only in tests 90 func (z *E6) SetRandom() (*E6, error) { 91 if _, err := z.B0.SetRandom(); err != nil { 92 return nil, err 93 } 94 if _, err := z.B1.SetRandom(); err != nil { 95 return nil, err 96 } 97 return z, nil 98 } 99 100 // IsZero returns true if z is zero, false otherwise 101 func (z *E6) IsZero() bool { 102 return z.B0.IsZero() && z.B1.IsZero() 103 } 104 105 // IsOne returns true if z is one, false otherwise 106 func (z *E6) IsOne() bool { 107 return z.B0.IsOne() && z.B1.IsZero() 108 } 109 110 // Mul sets z=x*y in E6 and returns z 111 func (z *E6) Mul(x, y *E6) *E6 { 112 var a, b, c E3 113 a.Add(&x.B0, &x.B1) 114 b.Add(&y.B0, &y.B1) 115 a.Mul(&a, &b) 116 b.Mul(&x.B0, &y.B0) 117 c.Mul(&x.B1, &y.B1) 118 z.B1.Sub(&a, &b).Sub(&z.B1, &c) 119 z.B0.MulByNonResidue(&c).Add(&z.B0, &b) 120 return z 121 } 122 123 // Square sets z=x*x in E6 and returns z 124 func (z *E6) Square(x *E6) *E6 { 125 126 //Algorithm 22 from https://eprint.iacr.org/2010/354.pdf 127 var c0, c2, c3 E3 128 c0.Sub(&x.B0, &x.B1) 129 c3.MulByNonResidue(&x.B1).Neg(&c3).Add(&x.B0, &c3) 130 c2.Mul(&x.B0, &x.B1) 131 c0.Mul(&c0, &c3).Add(&c0, &c2) 132 z.B1.Double(&c2) 133 c2.MulByNonResidue(&c2) 134 z.B0.Add(&c0, &c2) 135 136 return z 137 } 138 139 // Karabina's compressed cyclotomic square 140 // https://eprint.iacr.org/2010/542.pdf 141 // Th. 3.2 with minor modifications to fit our tower 142 func (z *E6) CyclotomicSquareCompressed(x *E6) *E6 { 143 144 var t [7]fp.Element 145 146 // t0 = g1² 147 t[0].Square(&x.B0.A1) 148 // t1 = g5² 149 t[1].Square(&x.B1.A2) 150 // t5 = g1 + g5 151 t[5].Add(&x.B0.A1, &x.B1.A2) 152 // t2 = (g1 + g5)² 153 t[2].Square(&t[5]) 154 155 // t3 = g1² + g5² 156 t[3].Add(&t[0], &t[1]) 157 // t5 = 2 * g1 * g5 158 t[5].Sub(&t[2], &t[3]) 159 160 // t6 = g3 + g2 161 t[6].Add(&x.B1.A0, &x.B0.A2) 162 // t3 = (g3 + g2)² 163 t[3].Square(&t[6]) 164 // t2 = g3² 165 t[2].Square(&x.B1.A0) 166 167 // t6 = 2 * nr * g1 * g5 168 t[6].MulByNonResidue(&t[5]) 169 // t5 = 4 * nr * g1 * g5 + 2 * g3 170 t[5].Add(&t[6], &x.B1.A0). 171 Double(&t[5]) 172 // z3 = 6 * nr * g1 * g5 + 2 * g3 173 z.B1.A0.Add(&t[5], &t[6]) 174 175 // t4 = nr * g5² 176 t[4].MulByNonResidue(&t[1]) 177 // t5 = nr * g5² + g1² 178 t[5].Add(&t[0], &t[4]) 179 // t6 = nr * g5² + g1² - g2 180 t[6].Sub(&t[5], &x.B0.A2) 181 182 // t1 = g2² 183 t[1].Square(&x.B0.A2) 184 185 // t6 = 2 * nr * g5² + 2 * g1² - 2*g2 186 t[6].Double(&t[6]) 187 // z2 = 3 * nr * g5² + 3 * g1² - 2*g2 188 z.B0.A2.Add(&t[6], &t[5]) 189 190 // t4 = nr * g2² 191 t[4].MulByNonResidue(&t[1]) 192 // t5 = g3² + nr * g2² 193 t[5].Add(&t[2], &t[4]) 194 // t6 = g3² + nr * g2² - g1 195 t[6].Sub(&t[5], &x.B0.A1) 196 // t6 = 2 * g3² + 2 * nr * g2² - 2 * g1 197 t[6].Double(&t[6]) 198 // z1 = 3 * g3² + 3 * nr * g2² - 2 * g1 199 z.B0.A1.Add(&t[6], &t[5]) 200 201 // t0 = g2² + g3² 202 t[0].Add(&t[2], &t[1]) 203 // t5 = 2 * g3 * g2 204 t[5].Sub(&t[3], &t[0]) 205 // t6 = 2 * g3 * g2 + g5 206 t[6].Add(&t[5], &x.B1.A2) 207 // t6 = 4 * g3 * g2 + 2 * g5 208 t[6].Double(&t[6]) 209 // z5 = 6 * g3 * g2 + 2 * g5 210 z.B1.A2.Add(&t[5], &t[6]) 211 212 return z 213 } 214 215 // DecompressKarabina Karabina's cyclotomic square result 216 // if g3 != 0 217 // 218 // g4 = (E * g5² + 3 * g1² - 2 * g2)/4g3 219 // 220 // if g3 == 0 221 // 222 // g4 = 2g1g5/g2 223 // 224 // if g3=g2=0 then g4=g5=g1=0 and g0=1 (x=1) 225 // Theorem 3.1 is well-defined for all x in Gϕₙ\{1} 226 func (z *E6) DecompressKarabina(x *E6) *E6 { 227 228 var t [3]fp.Element 229 var one fp.Element 230 one.SetOne() 231 232 if x.B1.A0.IsZero() /* g3 == 0 */ { 233 t[0].Mul(&x.B0.A1, &x.B1.A2). 234 Double(&t[0]) 235 // t1 = g2 236 t[1].Set(&x.B0.A2) 237 238 // g3 != 0 239 240 if t[1].IsZero() /* g2 == g3 == 0 */ { 241 return z.SetOne() 242 } 243 } else /* g3 != 0 */ { 244 // t0 = g1² 245 t[0].Square(&x.B0.A1) 246 // t1 = 3 * g1² - 2 * g2 247 t[1].Sub(&t[0], &x.B0.A2). 248 Double(&t[1]). 249 Add(&t[1], &t[0]) 250 // t0 = E * g5² + t1 251 t[2].Square(&x.B1.A2) 252 t[0].MulByNonResidue(&t[2]). 253 Add(&t[0], &t[1]) 254 // t1 = 1/(4 * g3) 255 t[1].Double(&x.B1.A0). 256 Double(&t[1]) 257 } 258 259 // z4 = g4 260 z.B1.A1.Div(&t[0], &t[1]) // costly 261 262 // t1 = g2 * g1 263 t[1].Mul(&x.B0.A2, &x.B0.A1) 264 // t2 = 2 * g4² - 3 * g2 * g1 265 t[2].Square(&x.B1.A1). 266 Sub(&t[2], &t[1]). 267 Double(&t[2]). 268 Sub(&t[2], &t[1]) 269 // t1 = g3 * g5 (g3 can be 0) 270 t[1].Mul(&x.B1.A0, &x.B1.A2) 271 // c₀ = E * (2 * g4² + g3 * g5 - 3 * g2 * g1) + 1 272 t[2].Add(&t[2], &t[1]) 273 z.B0.A0.MulByNonResidue(&t[2]). 274 Add(&z.B0.A0, &one) 275 276 z.B0.A1.Set(&x.B0.A1) 277 z.B0.A2.Set(&x.B0.A2) 278 z.B1.A0.Set(&x.B1.A0) 279 z.B1.A2.Set(&x.B1.A2) 280 281 return z 282 } 283 284 // Granger-Scott's cyclotomic square 285 // https://eprint.iacr.org/2009/565.pdf, 3.2 286 func (z *E6) CyclotomicSquare(x *E6) *E6 { 287 // x=(x0,x1,x2,x3,x4,x5,x6,x7) in E3⁶ 288 // cyclosquare(x)=(3*x4²*u + 3*x0² - 2*x0, 289 // 3*x2²*u + 3*x3² - 2*x1, 290 // 3*x5²*u + 3*x1² - 2*x2, 291 // 6*x1*x5*u + 2*x3, 292 // 6*x0*x4 + 2*x4, 293 // 6*x2*x3 + 2*x5) 294 295 var t [9]fp.Element 296 297 t[0].Square(&x.B1.A1) 298 t[1].Square(&x.B0.A0) 299 t[6].Add(&x.B1.A1, &x.B0.A0).Square(&t[6]).Sub(&t[6], &t[0]).Sub(&t[6], &t[1]) // 2*x4*x0 300 t[2].Square(&x.B0.A2) 301 t[3].Square(&x.B1.A0) 302 t[7].Add(&x.B0.A2, &x.B1.A0).Square(&t[7]).Sub(&t[7], &t[2]).Sub(&t[7], &t[3]) // 2*x2*x3 303 t[4].Square(&x.B1.A2) 304 t[5].Square(&x.B0.A1) 305 t[8].Add(&x.B1.A2, &x.B0.A1).Square(&t[8]).Sub(&t[8], &t[4]).Sub(&t[8], &t[5]).MulByNonResidue(&t[8]) // 2*x5*x1*u 306 307 t[0].MulByNonResidue(&t[0]).Add(&t[0], &t[1]) // x4²*u + x0² 308 t[2].MulByNonResidue(&t[2]).Add(&t[2], &t[3]) // x2²*u + x3² 309 t[4].MulByNonResidue(&t[4]).Add(&t[4], &t[5]) // x5²*u + x1² 310 311 z.B0.A0.Sub(&t[0], &x.B0.A0).Double(&z.B0.A0).Add(&z.B0.A0, &t[0]) 312 z.B0.A1.Sub(&t[2], &x.B0.A1).Double(&z.B0.A1).Add(&z.B0.A1, &t[2]) 313 z.B0.A2.Sub(&t[4], &x.B0.A2).Double(&z.B0.A2).Add(&z.B0.A2, &t[4]) 314 315 z.B1.A0.Add(&t[8], &x.B1.A0).Double(&z.B1.A0).Add(&z.B1.A0, &t[8]) 316 z.B1.A1.Add(&t[6], &x.B1.A1).Double(&z.B1.A1).Add(&z.B1.A1, &t[6]) 317 z.B1.A2.Add(&t[7], &x.B1.A2).Double(&z.B1.A2).Add(&z.B1.A2, &t[7]) 318 319 return z 320 } 321 322 // Inverse sets z to the inverse of x in E6 and returns z 323 // 324 // if x == 0, sets and returns z = x 325 func (z *E6) Inverse(x *E6) *E6 { 326 // Algorithm 23 from https://eprint.iacr.org/2010/354.pdf 327 328 var t0, t1, tmp E3 329 t0.Square(&x.B0) 330 t1.Square(&x.B1) 331 tmp.MulByNonResidue(&t1) 332 t0.Sub(&t0, &tmp) 333 t1.Inverse(&t0) 334 z.B0.Mul(&x.B0, &t1) 335 z.B1.Mul(&x.B1, &t1).Neg(&z.B1) 336 337 return z 338 } 339 340 // BatchInvertE6 returns a new slice with every element in a inverted. 341 // It uses Montgomery batch inversion trick. 342 // 343 // if a[i] == 0, returns result[i] = a[i] 344 func BatchInvertE6(a []E6) []E6 { 345 res := make([]E6, len(a)) 346 if len(a) == 0 { 347 return res 348 } 349 350 zeroes := make([]bool, len(a)) 351 var accumulator E6 352 accumulator.SetOne() 353 354 for i := 0; i < len(a); i++ { 355 if a[i].IsZero() { 356 zeroes[i] = true 357 continue 358 } 359 res[i].Set(&accumulator) 360 accumulator.Mul(&accumulator, &a[i]) 361 } 362 363 accumulator.Inverse(&accumulator) 364 365 for i := len(a) - 1; i >= 0; i-- { 366 if zeroes[i] { 367 continue 368 } 369 res[i].Mul(&res[i], &accumulator) 370 accumulator.Mul(&accumulator, &a[i]) 371 } 372 373 return res 374 } 375 376 // Exp sets z=xᵏ (mod q⁶) and returns it 377 // uses 2-bits windowed method 378 func (z *E6) Exp(x E6, k *big.Int) *E6 { 379 if k.IsUint64() && k.Uint64() == 0 { 380 return z.SetOne() 381 } 382 383 e := k 384 if k.Sign() == -1 { 385 // negative k, we invert 386 // if k < 0: xᵏ (mod q⁶) == (x⁻¹)ᵏ (mod q⁶) 387 x.Inverse(&x) 388 389 // we negate k in a temp big.Int since 390 // Int.Bit(_) of k and -k is different 391 e = bigIntPool.Get().(*big.Int) 392 defer bigIntPool.Put(e) 393 e.Neg(k) 394 } 395 396 var res E6 397 var ops [3]E6 398 399 res.SetOne() 400 ops[0].Set(&x) 401 ops[1].Square(&ops[0]) 402 ops[2].Set(&ops[0]).Mul(&ops[2], &ops[1]) 403 404 b := e.Bytes() 405 for i := range b { 406 w := b[i] 407 mask := byte(0xc0) 408 for j := 0; j < 4; j++ { 409 res.Square(&res).Square(&res) 410 c := (w & mask) >> (6 - 2*j) 411 if c != 0 { 412 res.Mul(&res, &ops[c-1]) 413 } 414 mask = mask >> 2 415 } 416 } 417 z.Set(&res) 418 419 return z 420 } 421 422 // CyclotomicExp sets z=xᵏ (mod q⁶) and returns it 423 // uses 2-NAF decomposition 424 // x must be in the cyclotomic subgroup 425 // TODO: use a windowed method 426 func (z *E6) CyclotomicExp(x E6, k *big.Int) *E6 { 427 if k.IsUint64() && k.Uint64() == 0 { 428 return z.SetOne() 429 } 430 431 e := k 432 if k.Sign() == -1 { 433 // negative k, we invert (=conjugate) 434 // if k < 0: xᵏ (mod q⁶) == (x⁻¹)ᵏ (mod q⁶) 435 x.Conjugate(&x) 436 437 // we negate k in a temp big.Int since 438 // Int.Bit(_) of k and -k is different 439 e = bigIntPool.Get().(*big.Int) 440 defer bigIntPool.Put(e) 441 e.Neg(k) 442 } 443 444 var res, xInv E6 445 xInv.InverseUnitary(&x) 446 res.SetOne() 447 eNAF := make([]int8, e.BitLen()+3) 448 n := ecc.NafDecomposition(e, eNAF[:]) 449 for i := n - 1; i >= 0; i-- { 450 res.CyclotomicSquare(&res) 451 if eNAF[i] == 1 { 452 res.Mul(&res, &x) 453 } else if eNAF[i] == -1 { 454 res.Mul(&res, &xInv) 455 } 456 } 457 z.Set(&res) 458 return z 459 } 460 461 // ExpGLV sets z=xᵏ (q⁶) and returns it 462 // uses 2-dimensional GLV with 2-bits windowed method 463 // x must be in GT 464 // TODO: use 2-NAF 465 // TODO: use higher dimensional decomposition 466 func (z *E6) ExpGLV(x E6, k *big.Int) *E6 { 467 if k.IsUint64() && k.Uint64() == 0 { 468 return z.SetOne() 469 } 470 471 e := k 472 if k.Sign() == -1 { 473 // negative k, we invert 474 // if k < 0: xᵏ (mod q⁶) == (x⁻¹)ᵏ (mod q⁶) 475 x.Conjugate(&x) 476 477 // we negate k in a temp big.Int since 478 // Int.Bit(_) of k and -k is different 479 e = bigIntPool.Get().(*big.Int) 480 defer bigIntPool.Put(e) 481 e.Neg(k) 482 } 483 484 var table [15]E6 485 var res E6 486 var s1, s2 fr.Element 487 488 res.SetOne() 489 490 // table[b3b2b1b0-1] = b3b2*Frobinius(x) + b1b0*x 491 table[0].Set(&x) 492 table[3].Frobenius(&x) 493 494 // split the scalar, modifies ±x, Frob(x) accordingly 495 s := ecc.SplitScalar(e, &glvBasis) 496 497 if s[0].Sign() == -1 { 498 s[0].Neg(&s[0]) 499 table[0].InverseUnitary(&table[0]) 500 } 501 if s[1].Sign() == -1 { 502 s[1].Neg(&s[1]) 503 table[3].InverseUnitary(&table[3]) 504 } 505 506 // precompute table (2 bits sliding window) 507 // table[b3b2b1b0-1] = b3b2*Frobenius(x) + b1b0*x if b3b2b1b0 != 0 508 table[1].CyclotomicSquare(&table[0]) 509 table[2].Mul(&table[1], &table[0]) 510 table[4].Mul(&table[3], &table[0]) 511 table[5].Mul(&table[3], &table[1]) 512 table[6].Mul(&table[3], &table[2]) 513 table[7].CyclotomicSquare(&table[3]) 514 table[8].Mul(&table[7], &table[0]) 515 table[9].Mul(&table[7], &table[1]) 516 table[10].Mul(&table[7], &table[2]) 517 table[11].Mul(&table[7], &table[3]) 518 table[12].Mul(&table[11], &table[0]) 519 table[13].Mul(&table[11], &table[1]) 520 table[14].Mul(&table[11], &table[2]) 521 522 // bounds on the lattice base vectors guarantee that s1, s2 are len(r)/2 bits long max 523 s1 = s1.SetBigInt(&s[0]).Bits() 524 s2 = s2.SetBigInt(&s[1]).Bits() 525 526 maxBit := s1.BitLen() 527 if s2.BitLen() > maxBit { 528 maxBit = s2.BitLen() 529 } 530 hiWordIndex := (maxBit - 1) / 64 531 532 // loop starts from len(s1)/2 due to the bounds 533 for i := hiWordIndex; i >= 0; i-- { 534 mask := uint64(3) << 62 535 for j := 0; j < 32; j++ { 536 res.CyclotomicSquare(&res).CyclotomicSquare(&res) 537 b1 := (s1[i] & mask) >> (62 - 2*j) 538 b2 := (s2[i] & mask) >> (62 - 2*j) 539 if b1|b2 != 0 { 540 s := (b2<<2 | b1) 541 res.Mul(&res, &table[s-1]) 542 } 543 mask = mask >> 2 544 } 545 } 546 547 z.Set(&res) 548 return z 549 } 550 551 // InverseUnitary inverses a unitary element 552 func (z *E6) InverseUnitary(x *E6) *E6 { 553 return z.Conjugate(x) 554 } 555 556 // Conjugate sets z to x conjugated and returns z 557 func (z *E6) Conjugate(x *E6) *E6 { 558 *z = *x 559 z.B1.Neg(&z.B1) 560 return z 561 } 562 563 // SizeOfGT represents the size in bytes that a GT element need in binary form 564 const SizeOfGT = fp.Bytes * 6 565 566 // Bytes returns the regular (non montgomery) value 567 // of z as a big-endian byte array. 568 // z.C1.B2.A1 | z.C1.B2.A0 | z.C1.B1.A1 | ... 569 func (z *E6) Bytes() (r [SizeOfGT]byte) { 570 571 offset := 0 572 var buf [fp.Bytes]byte 573 574 buf = z.B1.A2.Bytes() 575 copy(r[offset:offset+fp.Bytes], buf[:]) 576 offset += fp.Bytes 577 578 buf = z.B1.A1.Bytes() 579 copy(r[offset:offset+fp.Bytes], buf[:]) 580 offset += fp.Bytes 581 582 buf = z.B1.A0.Bytes() 583 copy(r[offset:offset+fp.Bytes], buf[:]) 584 offset += fp.Bytes 585 586 buf = z.B0.A2.Bytes() 587 copy(r[offset:offset+fp.Bytes], buf[:]) 588 offset += fp.Bytes 589 590 buf = z.B0.A1.Bytes() 591 copy(r[offset:offset+fp.Bytes], buf[:]) 592 offset += fp.Bytes 593 594 buf = z.B0.A0.Bytes() 595 copy(r[offset:offset+fp.Bytes], buf[:]) 596 597 return 598 } 599 600 // SetBytes interprets e as the bytes of a big-endian GT 601 // sets z to that value (in Montgomery form), and returns z. 602 // z.C1.B2.A1 | z.C1.B2.A0 | z.C1.B1.A1 | ... 603 func (z *E6) SetBytes(e []byte) error { 604 if len(e) != SizeOfGT { 605 return errors.New("invalid buffer size") 606 } 607 offset := 0 608 z.B1.A2.SetBytes(e[offset : offset+fp.Bytes]) 609 offset += fp.Bytes 610 z.B1.A1.SetBytes(e[offset : offset+fp.Bytes]) 611 offset += fp.Bytes 612 z.B1.A0.SetBytes(e[offset : offset+fp.Bytes]) 613 offset += fp.Bytes 614 z.B0.A2.SetBytes(e[offset : offset+fp.Bytes]) 615 offset += fp.Bytes 616 z.B0.A1.SetBytes(e[offset : offset+fp.Bytes]) 617 offset += fp.Bytes 618 z.B0.A0.SetBytes(e[offset : offset+fp.Bytes]) 619 620 return nil 621 } 622 623 // IsInSubGroup ensures GT/E6 is in correct subgroup 624 func (z *E6) IsInSubGroup() bool { 625 var tmp, a, _a, b E6 626 var t [6]E6 627 628 // check z^(phi_k(p)) == 1 629 a.Frobenius(z) 630 b.Frobenius(&a).Mul(&b, z) 631 632 if !a.Equal(&b) { 633 return false 634 } 635 636 // check z^(p+1-t) == 1 637 _a.Frobenius(z) 638 a.CyclotomicSquare(&_a).Mul(&a, &_a) // z^(3p) 639 640 // t(x)-1 = (-x⁶ + 5x⁵ - 9x⁴ + 7x³ - 4x + 5)/3 641 t[0].CyclotomicSquare(z). 642 CyclotomicSquare(&t[0]) // z^4 643 t[1].Mul(&t[0], z) //z^5* 644 t[2].Expt(&t[0]). 645 Conjugate(&t[2]) // z^(-4u)* 646 tmp.CyclotomicSquare(&t[2]). 647 Expt(&tmp). 648 Expt(&tmp) // z^(-8u^3) 649 t[4].Expt(z). 650 Expt(&t[4]). 651 Expt(&t[4]) // z^(u^3) 652 t[3].Mul(&t[4], &tmp). 653 Conjugate(&t[3]) // z^(7u^3)* 654 t[4].Conjugate(&t[4]). 655 Mul(&t[4], &tmp). 656 Expt(&t[4]) // z^(-9u^4)* 657 t[5].Expt(&tmp). 658 Conjugate(&t[5]). 659 Mul(&t[5], &t[4]). 660 Expt(&t[5]) // z^(-u^5) 661 t[0].Expt(&t[5]) // z^(-u^6)* 662 tmp.Expt(&t[4]) // z^(-9u^5) 663 t[5].CyclotomicSquare(&t[5]). 664 CyclotomicSquare(&t[5]). 665 Conjugate(&t[5]). 666 Mul(&t[5], &tmp). 667 Conjugate(&t[5]) // z^(5u^5)* 668 669 b.Mul(&t[1], &t[2]). 670 Mul(&b, &t[3]). 671 Mul(&b, &t[4]). 672 Mul(&b, &t[5]). 673 Mul(&b, &t[0]) // z^(3(t-1)) 674 675 return a.Equal(&b) 676 } 677 678 // CompressTorus GT/E6 element to half its size 679 // z must be in the cyclotomic subgroup 680 // i.e. z^(p⁴-p²+1)=1 681 // e.g. GT 682 // "COMPRESSION IN FINITE FIELDS AND TORUS-BASED CRYPTOGRAPHY", K. RUBIN AND A. SILVERBERG 683 // z.B1 == 0 only when z ∈ {-1,1} 684 func (z *E6) CompressTorus() (E3, error) { 685 686 if z.B1.IsZero() { 687 return E3{}, errors.New("invalid input") 688 } 689 690 var res, tmp, one E3 691 one.SetOne() 692 tmp.Inverse(&z.B1) 693 res.Add(&z.B0, &one). 694 Mul(&res, &tmp) 695 696 return res, nil 697 } 698 699 // BatchCompressTorus GT/E6 elements to half their size 700 // using a batch inversion 701 func BatchCompressTorus(x []E6) ([]E3, error) { 702 703 n := len(x) 704 if n == 0 { 705 return []E3{}, errors.New("invalid input size") 706 } 707 708 var one E3 709 one.SetOne() 710 res := make([]E3, n) 711 712 for i := 0; i < n; i++ { 713 res[i].Set(&x[i].B1) 714 // throw an error if any of the x[i].C1 is 0 715 if res[i].IsZero() { 716 return []E3{}, errors.New("invalid input") 717 } 718 } 719 720 t := BatchInvertE3(res) // costs 1 inverse 721 722 for i := 0; i < n; i++ { 723 res[i].Add(&x[i].B0, &one). 724 Mul(&res[i], &t[i]) 725 } 726 727 return res, nil 728 } 729 730 // DecompressTorus GT/E6 a compressed element 731 // element must be in the cyclotomic subgroup 732 // "COMPRESSION IN FINITE FIELDS AND TORUS-BASED CRYPTOGRAPHY", K. RUBIN AND A. SILVERBERG 733 func (z *E3) DecompressTorus() E6 { 734 735 var res, num, denum E6 736 num.B0.Set(z) 737 num.B1.SetOne() 738 denum.B0.Set(z) 739 denum.B1.SetOne().Neg(&denum.B1) 740 res.Inverse(&denum). 741 Mul(&res, &num) 742 743 return res 744 } 745 746 // BatchDecompressTorus GT/E6 compressed elements 747 // using a batch inversion 748 func BatchDecompressTorus(x []E3) ([]E6, error) { 749 750 n := len(x) 751 if n == 0 { 752 return []E6{}, errors.New("invalid input size") 753 } 754 755 res := make([]E6, n) 756 num := make([]E6, n) 757 denum := make([]E6, n) 758 759 for i := 0; i < n; i++ { 760 num[i].B0.Set(&x[i]) 761 num[i].B1.SetOne() 762 denum[i].B0.Set(&x[i]) 763 denum[i].B1.SetOne().Neg(&denum[i].B1) 764 } 765 766 denum = BatchInvertE6(denum) // costs 1 inverse 767 768 for i := 0; i < n; i++ { 769 res[i].Mul(&num[i], &denum[i]) 770 } 771 772 return res, nil 773 }