diff --git a/ecc/bls12-377/hash_to_g1.go b/ecc/bls12-377/hash_to_g1.go index 7f70f54102..d63191b36b 100644 --- a/ecc/bls12-377/hash_to_g1.go +++ b/ecc/bls12-377/hash_to_g1.go @@ -89,65 +89,58 @@ func g1Isogeny(p *G1Affine) { // g1SqrtRatio computes the square root of u/v and returns 0 iff u/v was indeed a quadratic residue // if not, we get sqrt(Z * u / v). Recall that Z is non-residue +// If v = 0, u/v is meaningless and the output is unspecified, without raising an error. // The main idea is that since the computation of the square root involves taking large powers of u/v, the inversion of v can be avoided func g1SqrtRatio(z *fp.Element, u *fp.Element, v *fp.Element) uint64 { - // Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ F.2.1.1. for any field + // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-sqrt_ratio-for-any-field tv1 := fp.Element{7563926049028936178, 2688164645460651601, 12112688591437172399, 3177973240564633687, 14764383749841851163, 52487407124055189} //tv1 = c6 var tv2, tv3, tv4, tv5 fp.Element var exp big.Int - // c4 = 70368744177663 = 2^46 - 1 + // c4 = 70368744177663 = 2⁴⁶ - 1 // q is odd so c1 is at least 1. exp.SetBytes([]byte{63, 255, 255, 255, 255, 255}) - tv2.Exp(*v, &exp) - tv3.Mul(&tv2, &tv2) - tv3.Mul(&tv3, v) - - // line 5 - tv5.Mul(u, &tv3) + tv2.Exp(*v, &exp) // 2. tv2 = vᶜ⁴ + tv3.Square(&tv2) // 3. tv3 = tv2² + tv3.Mul(&tv3, v) // 4. tv3 = tv3 * v + tv5.Mul(u, &tv3) // 5. tv5 = u * tv3 // c3 = 1837921289030710838195067919506396475074392872918698035817074744121558668640693829665401097909504529 exp.SetBytes([]byte{3, 92, 116, 140, 47, 138, 33, 213, 140, 118, 11, 128, 217, 66, 146, 118, 52, 69, 179, 230, 1, 234, 39, 30, 61, 230, 196, 95, 116, 18, 144, 0, 46, 22, 186, 136, 96, 0, 0, 1, 10, 17}) - tv5.Exp(tv5, &exp) - tv5.Mul(&tv5, &tv2) - tv2.Mul(&tv5, v) - tv3.Mul(&tv5, u) - // line 10 - tv4.Mul(&tv3, &tv2) + tv5.Exp(tv5, &exp) // 6. tv5 = tv5ᶜ³ + tv5.Mul(&tv5, &tv2) // 7. tv5 = tv5 * tv2 + tv2.Mul(&tv5, v) // 8. tv2 = tv5 * v + tv3.Mul(&tv5, u) // 9. tv3 = tv5 * u + tv4.Mul(&tv3, &tv2) // 10. tv4 = tv3 * tv2 // c5 = 35184372088832 exp.SetBytes([]byte{32, 0, 0, 0, 0, 0}) - tv5.Exp(tv4, &exp) - - isQNr := g1NotOne(&tv5) - - tv2.Mul(&tv3, &fp.Element{13262060633605929793, 16269117706405780335, 1787999441809606207, 11078968899094441280, 17534011895423012165, 96686002316065324}) - tv5.Mul(&tv4, &tv1) - - // line 15 - - tv3.Select(int(isQNr), &tv3, &tv2) - tv4.Select(int(isQNr), &tv4, &tv5) - - exp.Lsh(big.NewInt(1), 46-2) - - for i := 46; i >= 2; i-- { - //line 20 - tv5.Exp(tv4, &exp) - nE1 := g1NotOne(&tv5) - - tv2.Mul(&tv3, &tv1) - tv1.Mul(&tv1, &tv1) - tv5.Mul(&tv4, &tv1) - - tv3.Select(int(nE1), &tv3, &tv2) - tv4.Select(int(nE1), &tv4, &tv5) - - exp.Rsh(&exp, 1) + tv5.Exp(tv4, &exp) // 11. tv5 = tv4ᶜ⁵ + isQNr := g1NotOne(&tv5) // 12. isQR = tv5 == 1 + c7 := fp.Element{13262060633605929793, 16269117706405780335, 1787999441809606207, 11078968899094441280, 17534011895423012165, 96686002316065324} + tv2.Mul(&tv3, &c7) // 13. tv2 = tv3 * c7 + tv5.Mul(&tv4, &tv1) // 14. tv5 = tv4 * tv1 + tv3.Select(int(isQNr), &tv3, &tv2) // 15. tv3 = CMOV(tv2, tv3, isQR) + tv4.Select(int(isQNr), &tv4, &tv5) // 16. tv4 = CMOV(tv5, tv4, isQR) + exp.Lsh(big.NewInt(1), 46-2) // 18, 19: tv5 = 2ⁱ⁻² for i = c1 + + for i := 46; i >= 2; i-- { // 17. for i in (c1, c1 - 1, ..., 2): + + tv5.Exp(tv4, &exp) // 20. tv5 = tv4ᵗᵛ⁵ + nE1 := g1NotOne(&tv5) // 21. e1 = tv5 == 1 + tv2.Mul(&tv3, &tv1) // 22. tv2 = tv3 * tv1 + tv1.Mul(&tv1, &tv1) // 23. tv1 = tv1 * tv1 Why not write square? + tv5.Mul(&tv4, &tv1) // 24. tv5 = tv4 * tv1 + tv3.Select(int(nE1), &tv3, &tv2) // 25. tv3 = CMOV(tv2, tv3, e1) + tv4.Select(int(nE1), &tv4, &tv5) // 26. tv4 = CMOV(tv5, tv4, e1) + + if i > 2 { + exp.Rsh(&exp, 1) // 18, 19. tv5 = 2ⁱ⁻² + } } *z = tv3 @@ -173,30 +166,29 @@ func g1MulByZ(z *fp.Element, x *fp.Element) { *z = res } -//TODO: Define A,B here - -// From https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ Pg 80 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-simplified-swu-method // mapToCurve1 implements the SSWU map // No cofactor clearing or isogeny func mapToCurve1(u *fp.Element) G1Affine { + var sswuIsoCurveCoeffA = fp.Element{17252667382019449424, 8408110001211059699, 18415587021986261264, 10797086888535946954, 9462758283094809199, 54995354010328751} + var sswuIsoCurveCoeffB = fp.Element{11130294635325289193, 6502679372128844082, 15863297759487624914, 16270683149854112145, 3560014356538878812, 27923742146399959} + var tv1 fp.Element - tv1.Square(u) + tv1.Square(u) // 1. tv1 = u² //mul tv1 by Z - g1MulByZ(&tv1, &tv1) + g1MulByZ(&tv1, &tv1) // 2. tv1 = Z * tv1 var tv2 fp.Element - tv2.Square(&tv1) - tv2.Add(&tv2, &tv1) + tv2.Square(&tv1) // 3. tv2 = tv1² + tv2.Add(&tv2, &tv1) // 4. tv2 = tv2 + tv1 var tv3 fp.Element - //Standard doc line 5 var tv4 fp.Element tv4.SetOne() - tv3.Add(&tv2, &tv4) - //TODO: Use bCurveConf when no isogeny - tv3.Mul(&tv3, &fp.Element{11130294635325289193, 6502679372128844082, 15863297759487624914, 16270683149854112145, 3560014356538878812, 27923742146399959}) + tv3.Add(&tv2, &tv4) // 5. tv3 = tv2 + 1 + tv3.Mul(&tv3, &sswuIsoCurveCoeffB) // 6. tv3 = B * tv3 tv2NZero := g1NotZero(&tv2) @@ -204,48 +196,45 @@ func mapToCurve1(u *fp.Element) G1Affine { tv4 = fp.Element{9871116327010172167, 9167007004823125620, 18338974479346628539, 5649234265355377548, 13442091487463296847, 77904398905292312} tv2.Neg(&tv2) - tv4.Select(int(tv2NZero), &tv4, &tv2) - //TODO: When no isogeny use curve constants - tv2 = fp.Element{17252667382019449424, 8408110001211059699, 18415587021986261264, 10797086888535946954, 9462758283094809199, 54995354010328751} - tv4.Mul(&tv4, &tv2) + tv4.Select(int(tv2NZero), &tv4, &tv2) // 7. tv4 = CMOV(Z, -tv2, tv2 != 0) + tv4.Mul(&tv4, &sswuIsoCurveCoeffA) // 8. tv4 = A * tv4 - tv2.Square(&tv3) + tv2.Square(&tv3) // 9. tv2 = tv3² var tv6 fp.Element - //Standard doc line 10 - tv6.Square(&tv4) + tv6.Square(&tv4) // 10. tv6 = tv4² var tv5 fp.Element - tv5.Mul(&tv6, &fp.Element{17252667382019449424, 8408110001211059699, 18415587021986261264, 10797086888535946954, 9462758283094809199, 54995354010328751}) + tv5.Mul(&tv6, &sswuIsoCurveCoeffA) // 11. tv5 = A * tv6 - tv2.Add(&tv2, &tv5) - tv2.Mul(&tv2, &tv3) - tv6.Mul(&tv6, &tv4) + tv2.Add(&tv2, &tv5) // 12. tv2 = tv2 + tv5 + tv2.Mul(&tv2, &tv3) // 13. tv2 = tv2 * tv3 + tv6.Mul(&tv6, &tv4) // 14. tv6 = tv6 * tv4 - //Standards doc line 15 - tv5.Mul(&tv6, &fp.Element{11130294635325289193, 6502679372128844082, 15863297759487624914, 16270683149854112145, 3560014356538878812, 27923742146399959}) - tv2.Add(&tv2, &tv5) + tv5.Mul(&tv6, &sswuIsoCurveCoeffB) // 15. tv5 = B * tv6 + tv2.Add(&tv2, &tv5) // 16. tv2 = tv2 + tv5 var x fp.Element - x.Mul(&tv1, &tv3) + x.Mul(&tv1, &tv3) // 17. x = tv1 * tv3 var y1 fp.Element - gx1NSquare := g1SqrtRatio(&y1, &tv2, &tv6) + gx1NSquare := g1SqrtRatio(&y1, &tv2, &tv6) // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6) var y fp.Element - y.Mul(&tv1, u) + y.Mul(&tv1, u) // 19. y = tv1 * u - //Standards doc line 20 - y.Mul(&y, &y1) + y.Mul(&y, &y1) // 20. y = y * y1 - x.Select(int(gx1NSquare), &tv3, &x) - y.Select(int(gx1NSquare), &y1, &y) + x.Select(int(gx1NSquare), &tv3, &x) // 21. x = CMOV(x, tv3, is_gx1_square) + y.Select(int(gx1NSquare), &y1, &y) // 22. y = CMOV(y, y1, is_gx1_square) y1.Neg(&y) y.Select(int(g1Sgn0(u)^g1Sgn0(&y)), &y, &y1) - //Standards doc line 25 - x.Div(&x, &tv4) + // 23. e1 = sgn0(u) == sgn0(y) + // 24. y = CMOV(-y, y, e1) + + x.Div(&x, &tv4) // 25. x = x / tv4 return G1Affine{x, y} } @@ -288,13 +277,13 @@ func hashToFp(msg, dst []byte, count int) ([]fp.Element, error) { // g1Sgn0 is an algebraic substitute for the notion of sign in ordered fields // Namely, every non-zero quadratic residue in a finite field of characteristic =/= 2 has exactly two square roots, one of each sign -// Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/ section 4.1 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-the-sgn0-function // The sign of an element is not obviously related to that of its Montgomery form func g1Sgn0(z *fp.Element) uint64 { nonMont := *z nonMont.FromMont() - + // m == 1 return nonMont[0] % 2 } @@ -311,7 +300,7 @@ func MapToG1(u fp.Element) G1Affine { // EncodeToG1 hashes a message to a point on the G1 curve using the SSWU map. // It is faster than HashToG1, but the result is not uniformly distributed. Unsuitable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -//https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/#section-6.6.3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func EncodeToG1(msg, dst []byte) (G1Affine, error) { var res G1Affine @@ -331,7 +320,7 @@ func EncodeToG1(msg, dst []byte) (G1Affine, error) { // HashToG1 hashes a message to a point on the G1 curve using the SSWU map. // Slower than EncodeToG1, but usable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func HashToG1(msg, dst []byte) (G1Affine, error) { u, err := hashToFp(msg, dst, 2*1) if err != nil { @@ -341,7 +330,7 @@ func HashToG1(msg, dst []byte) (G1Affine, error) { Q0 := mapToCurve1(&u[0]) Q1 := mapToCurve1(&u[1]) - //TODO: Add in E' first, then apply isogeny + //TODO (perf): Add in E' first, then apply isogeny g1Isogeny(&Q0) g1Isogeny(&Q1) diff --git a/ecc/bls12-377/hash_to_g2.go b/ecc/bls12-377/hash_to_g2.go index 0dfdbe18f9..46e6c62ae3 100644 --- a/ecc/bls12-377/hash_to_g2.go +++ b/ecc/bls12-377/hash_to_g2.go @@ -530,10 +530,11 @@ func g2Isogeny(p *G2Affine) { // g2SqrtRatio computes the square root of u/v and returns 0 iff u/v was indeed a quadratic residue // if not, we get sqrt(Z * u / v). Recall that Z is non-residue +// If v = 0, u/v is meaningless and the output is unspecified, without raising an error. // The main idea is that since the computation of the square root involves taking large powers of u/v, the inversion of v can be avoided func g2SqrtRatio(z *fptower.E2, u *fptower.E2, v *fptower.E2) uint64 { - // Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ F.2.1.1. for any field + // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-sqrt_ratio-for-any-field tv1 := fptower.E2{ A0: fp.Element{0}, @@ -542,59 +543,51 @@ func g2SqrtRatio(z *fptower.E2, u *fptower.E2, v *fptower.E2) uint64 { var tv2, tv3, tv4, tv5 fptower.E2 var exp big.Int - // c4 = 140737488355327 = 2^47 - 1 + // c4 = 140737488355327 = 2⁴⁷ - 1 // q is odd so c1 is at least 1. exp.SetBytes([]byte{127, 255, 255, 255, 255, 255}) - tv2.Exp(*v, &exp) - tv3.Mul(&tv2, &tv2) - tv3.Mul(&tv3, v) - - // line 5 - tv5.Mul(u, &tv3) + tv2.Exp(*v, &exp) // 2. tv2 = vᶜ⁴ + tv3.Square(&tv2) // 3. tv3 = tv2² + tv3.Mul(&tv3, v) // 4. tv3 = tv3 * v + tv5.Mul(u, &tv3) // 5. tv5 = u * tv3 // c3 = 237702427642072544657662731610863241996908072983433720914596829155825880635712864411696995402952020023758239370111403151139377997388748144480691770738487127695940799749981367718443673617185911789718419957467908625 exp.SetBytes([]byte{2, 211, 7, 208, 187, 175, 251, 34, 86, 145, 59, 179, 97, 38, 60, 75, 184, 184, 125, 164, 174, 233, 63, 31, 94, 113, 65, 61, 218, 77, 92, 9, 208, 24, 175, 185, 6, 96, 205, 192, 20, 231, 18, 80, 42, 77, 108, 70, 10, 170, 170, 139, 183, 10, 224, 49, 131, 36, 185, 88, 99, 140, 157, 107, 203, 251, 210, 53, 241, 192, 154, 74, 218, 38, 143, 46, 27, 216, 0, 115, 56, 210, 84, 240, 0, 0, 1, 10, 17}) - tv5.Exp(tv5, &exp) - tv5.Mul(&tv5, &tv2) - tv2.Mul(&tv5, v) - tv3.Mul(&tv5, u) - // line 10 - tv4.Mul(&tv3, &tv2) + tv5.Exp(tv5, &exp) // 6. tv5 = tv5ᶜ³ + tv5.Mul(&tv5, &tv2) // 7. tv5 = tv5 * tv2 + tv2.Mul(&tv5, v) // 8. tv2 = tv5 * v + tv3.Mul(&tv5, u) // 9. tv3 = tv5 * u + tv4.Mul(&tv3, &tv2) // 10. tv4 = tv3 * tv2 // c5 = 70368744177664 exp.SetBytes([]byte{64, 0, 0, 0, 0, 0}) - tv5.Exp(tv4, &exp) - - isQNr := g2NotOne(&tv5) - - tv2.Mul(&tv3, &fptower.E2{ + tv5.Exp(tv4, &exp) // 11. tv5 = tv4ᶜ⁵ + isQNr := g2NotOne(&tv5) // 12. isQR = tv5 == 1 + c7 := fptower.E2{ A0: fp.Element{1479358275892676257, 2814264704614556731, 13691179386454739330, 7530671302001941842, 60362263363904715, 37906327945374822}, A1: fp.Element{5350190547200862053, 10822704806123199611, 5122684409451163826, 10616884767534481491, 1436196917100294910, 20226740120672211}, - }) - tv5.Mul(&tv4, &tv1) - - // line 15 - - tv3.Select(int(isQNr), &tv3, &tv2) - tv4.Select(int(isQNr), &tv4, &tv5) - - exp.Lsh(big.NewInt(1), 47-2) - - for i := 47; i >= 2; i-- { - //line 20 - tv5.Exp(tv4, &exp) - nE1 := g2NotOne(&tv5) - - tv2.Mul(&tv3, &tv1) - tv1.Mul(&tv1, &tv1) - tv5.Mul(&tv4, &tv1) - - tv3.Select(int(nE1), &tv3, &tv2) - tv4.Select(int(nE1), &tv4, &tv5) - - exp.Rsh(&exp, 1) + } + tv2.Mul(&tv3, &c7) // 13. tv2 = tv3 * c7 + tv5.Mul(&tv4, &tv1) // 14. tv5 = tv4 * tv1 + tv3.Select(int(isQNr), &tv3, &tv2) // 15. tv3 = CMOV(tv2, tv3, isQR) + tv4.Select(int(isQNr), &tv4, &tv5) // 16. tv4 = CMOV(tv5, tv4, isQR) + exp.Lsh(big.NewInt(1), 47-2) // 18, 19: tv5 = 2ⁱ⁻² for i = c1 + + for i := 47; i >= 2; i-- { // 17. for i in (c1, c1 - 1, ..., 2): + + tv5.Exp(tv4, &exp) // 20. tv5 = tv4ᵗᵛ⁵ + nE1 := g2NotOne(&tv5) // 21. e1 = tv5 == 1 + tv2.Mul(&tv3, &tv1) // 22. tv2 = tv3 * tv1 + tv1.Mul(&tv1, &tv1) // 23. tv1 = tv1 * tv1 Why not write square? + tv5.Mul(&tv4, &tv1) // 24. tv5 = tv4 * tv1 + tv3.Select(int(nE1), &tv3, &tv2) // 25. tv3 = CMOV(tv2, tv3, e1) + tv4.Select(int(nE1), &tv4, &tv5) // 26. tv4 = CMOV(tv5, tv4, e1) + + if i > 2 { + exp.Rsh(&exp, 1) // 18, 19. tv5 = 2ⁱ⁻² + } } *z = tv3 @@ -619,33 +612,35 @@ func g2MulByZ(z *fptower.E2, x *fptower.E2) { } -//TODO: Define A,B here - -// From https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ Pg 80 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-simplified-swu-method // mapToCurve2 implements the SSWU map // No cofactor clearing or isogeny func mapToCurve2(u *fptower.E2) G2Affine { + var sswuIsoCurveCoeffA = fptower.E2{ + A0: fp.Element{4274545572028848265, 14157081418478689358, 13123833976752631407, 4466041663276938746, 9062541850312583986, 90030181981586611}, + A1: fp.Element{4627353644986202063, 14941155654691983603, 14266958733709189881, 10264689865410103271, 10052798319587953375, 111844286035220969}, + } + var sswuIsoCurveCoeffB = fptower.E2{ + A0: fp.Element{10237434857876739089, 8476639787604822147, 6641637803208190023, 1721529389316620686, 8656544759275761743, 38999476160258021}, + A1: fp.Element{2360755569119276357, 10390833517265838837, 12467133771585386911, 8219721226907645480, 3130947551623757939, 83517800164149569}, + } + var tv1 fptower.E2 - tv1.Square(u) + tv1.Square(u) // 1. tv1 = u² //mul tv1 by Z - g2MulByZ(&tv1, &tv1) + g2MulByZ(&tv1, &tv1) // 2. tv1 = Z * tv1 var tv2 fptower.E2 - tv2.Square(&tv1) - tv2.Add(&tv2, &tv1) + tv2.Square(&tv1) // 3. tv2 = tv1² + tv2.Add(&tv2, &tv1) // 4. tv2 = tv2 + tv1 var tv3 fptower.E2 - //Standard doc line 5 var tv4 fptower.E2 tv4.SetOne() - tv3.Add(&tv2, &tv4) - //TODO: Use bCurveConf when no isogeny - tv3.Mul(&tv3, &fptower.E2{ - A0: fp.Element{10237434857876739089, 8476639787604822147, 6641637803208190023, 1721529389316620686, 8656544759275761743, 38999476160258021}, - A1: fp.Element{2360755569119276357, 10390833517265838837, 12467133771585386911, 8219721226907645480, 3130947551623757939, 83517800164149569}, - }) + tv3.Add(&tv2, &tv4) // 5. tv3 = tv2 + 1 + tv3.Mul(&tv3, &sswuIsoCurveCoeffB) // 6. tv3 = B * tv3 tv2NZero := g2NotZero(&tv2) @@ -656,57 +651,45 @@ func mapToCurve2(u *fptower.E2) G2Affine { } tv2.Neg(&tv2) - tv4.Select(int(tv2NZero), &tv4, &tv2) - //TODO: When no isogeny use curve constants - tv2 = fptower.E2{ - A0: fp.Element{4274545572028848265, 14157081418478689358, 13123833976752631407, 4466041663276938746, 9062541850312583986, 90030181981586611}, - A1: fp.Element{4627353644986202063, 14941155654691983603, 14266958733709189881, 10264689865410103271, 10052798319587953375, 111844286035220969}, - } - tv4.Mul(&tv4, &tv2) + tv4.Select(int(tv2NZero), &tv4, &tv2) // 7. tv4 = CMOV(Z, -tv2, tv2 != 0) + tv4.Mul(&tv4, &sswuIsoCurveCoeffA) // 8. tv4 = A * tv4 - tv2.Square(&tv3) + tv2.Square(&tv3) // 9. tv2 = tv3² var tv6 fptower.E2 - //Standard doc line 10 - tv6.Square(&tv4) + tv6.Square(&tv4) // 10. tv6 = tv4² var tv5 fptower.E2 - tv5.Mul(&tv6, &fptower.E2{ - A0: fp.Element{4274545572028848265, 14157081418478689358, 13123833976752631407, 4466041663276938746, 9062541850312583986, 90030181981586611}, - A1: fp.Element{4627353644986202063, 14941155654691983603, 14266958733709189881, 10264689865410103271, 10052798319587953375, 111844286035220969}, - }) + tv5.Mul(&tv6, &sswuIsoCurveCoeffA) // 11. tv5 = A * tv6 - tv2.Add(&tv2, &tv5) - tv2.Mul(&tv2, &tv3) - tv6.Mul(&tv6, &tv4) + tv2.Add(&tv2, &tv5) // 12. tv2 = tv2 + tv5 + tv2.Mul(&tv2, &tv3) // 13. tv2 = tv2 * tv3 + tv6.Mul(&tv6, &tv4) // 14. tv6 = tv6 * tv4 - //Standards doc line 15 - tv5.Mul(&tv6, &fptower.E2{ - A0: fp.Element{10237434857876739089, 8476639787604822147, 6641637803208190023, 1721529389316620686, 8656544759275761743, 38999476160258021}, - A1: fp.Element{2360755569119276357, 10390833517265838837, 12467133771585386911, 8219721226907645480, 3130947551623757939, 83517800164149569}, - }) - tv2.Add(&tv2, &tv5) + tv5.Mul(&tv6, &sswuIsoCurveCoeffB) // 15. tv5 = B * tv6 + tv2.Add(&tv2, &tv5) // 16. tv2 = tv2 + tv5 var x fptower.E2 - x.Mul(&tv1, &tv3) + x.Mul(&tv1, &tv3) // 17. x = tv1 * tv3 var y1 fptower.E2 - gx1NSquare := g2SqrtRatio(&y1, &tv2, &tv6) + gx1NSquare := g2SqrtRatio(&y1, &tv2, &tv6) // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6) var y fptower.E2 - y.Mul(&tv1, u) + y.Mul(&tv1, u) // 19. y = tv1 * u - //Standards doc line 20 - y.Mul(&y, &y1) + y.Mul(&y, &y1) // 20. y = y * y1 - x.Select(int(gx1NSquare), &tv3, &x) - y.Select(int(gx1NSquare), &y1, &y) + x.Select(int(gx1NSquare), &tv3, &x) // 21. x = CMOV(x, tv3, is_gx1_square) + y.Select(int(gx1NSquare), &y1, &y) // 22. y = CMOV(y, y1, is_gx1_square) y1.Neg(&y) y.Select(int(g2Sgn0(u)^g2Sgn0(&y)), &y, &y1) - //Standards doc line 25 - x.Div(&x, &tv4) + // 23. e1 = sgn0(u) == sgn0(y) + // 24. y = CMOV(-y, y, e1) + + x.Div(&x, &tv4) // 25. x = x / tv4 return G2Affine{x, y} } @@ -728,28 +711,29 @@ func g2EvalPolynomial(z *fptower.E2, monic bool, coefficients []fptower.E2, x *f // g2Sgn0 is an algebraic substitute for the notion of sign in ordered fields // Namely, every non-zero quadratic residue in a finite field of characteristic =/= 2 has exactly two square roots, one of each sign -// Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/ section 4.1 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-the-sgn0-function // The sign of an element is not obviously related to that of its Montgomery form func g2Sgn0(z *fptower.E2) uint64 { nonMont := *z nonMont.FromMont() - sign := uint64(0) - zero := uint64(1) + sign := uint64(0) // 1. sign = 0 + zero := uint64(1) // 2. zero = 1 var signI uint64 var zeroI uint64 - signI = nonMont.A0[0] % 2 - sign = sign | (zero & signI) - + // 3. i = 1 + signI = nonMont.A0[0] % 2 // 4. sign_i = x_i mod 2 zeroI = g1NotZero(&nonMont.A0) - zeroI = 1 ^ (zeroI|-zeroI)>>63 - zero = zero & zeroI - - signI = nonMont.A1[0] % 2 - sign = sign | (zero & signI) - + zeroI = 1 ^ (zeroI|-zeroI)>>63 // 5. zero_i = x_i == 0 + sign = sign | (zero & signI) // 6. sign = sign OR (zero AND sign_i) # Avoid short-circuit logic ops + zero = zero & zeroI // 7. zero = zero AND zero_i + // 3. i = 2 + signI = nonMont.A1[0] % 2 // 4. sign_i = x_i mod 2 + // 5. zero_i = x_i == 0 + sign = sign | (zero & signI) // 6. sign = sign OR (zero AND sign_i) # Avoid short-circuit logic ops + // 7. zero = zero AND zero_i return sign } @@ -766,7 +750,7 @@ func MapToG2(u fptower.E2) G2Affine { // EncodeToG2 hashes a message to a point on the G2 curve using the SSWU map. // It is faster than HashToG2, but the result is not uniformly distributed. Unsuitable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -//https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/#section-6.6.3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func EncodeToG2(msg, dst []byte) (G2Affine, error) { var res G2Affine @@ -789,7 +773,7 @@ func EncodeToG2(msg, dst []byte) (G2Affine, error) { // HashToG2 hashes a message to a point on the G2 curve using the SSWU map. // Slower than EncodeToG2, but usable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func HashToG2(msg, dst []byte) (G2Affine, error) { u, err := hashToFp(msg, dst, 2*2) if err != nil { @@ -805,7 +789,7 @@ func HashToG2(msg, dst []byte) (G2Affine, error) { A1: u[2+1], }) - //TODO: Add in E' first, then apply isogeny + //TODO (perf): Add in E' first, then apply isogeny g2Isogeny(&Q0) g2Isogeny(&Q1) diff --git a/ecc/bls12-378/hash_to_g1.go b/ecc/bls12-378/hash_to_g1.go index b4d9342f36..e67e9b40c2 100644 --- a/ecc/bls12-378/hash_to_g1.go +++ b/ecc/bls12-378/hash_to_g1.go @@ -89,65 +89,58 @@ func g1Isogeny(p *G1Affine) { // g1SqrtRatio computes the square root of u/v and returns 0 iff u/v was indeed a quadratic residue // if not, we get sqrt(Z * u / v). Recall that Z is non-residue +// If v = 0, u/v is meaningless and the output is unspecified, without raising an error. // The main idea is that since the computation of the square root involves taking large powers of u/v, the inversion of v can be avoided func g1SqrtRatio(z *fp.Element, u *fp.Element, v *fp.Element) uint64 { - // Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ F.2.1.1. for any field + // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-sqrt_ratio-for-any-field tv1 := fp.Element{3422016347327078217, 15952935974507985473, 10210560017327941857, 6195437588884472512, 1531492004832937820, 17090488542823369} //tv1 = c6 var tv2, tv3, tv4, tv5 fp.Element var exp big.Int - // c4 = 2199023255551 = 2^41 - 1 + // c4 = 2199023255551 = 2⁴¹ - 1 // q is odd so c1 is at least 1. exp.SetBytes([]byte{1, 255, 255, 255, 255, 255}) - tv2.Exp(*v, &exp) - tv3.Mul(&tv2, &tv2) - tv3.Mul(&tv3, v) - - // line 5 - tv5.Mul(u, &tv3) + tv2.Exp(*v, &exp) // 2. tv2 = vᶜ⁴ + tv3.Square(&tv2) // 3. tv3 = tv2² + tv3.Mul(&tv3, v) // 4. tv3 = tv3 * v + tv5.Mul(u, &tv3) // 5. tv5 = u * tv3 // c3 = 137617509170765099891752579783724504691201148437113468788429769127729045045134922651478473733013131816 exp.SetBytes([]byte{251, 172, 16, 89, 161, 52, 100, 20, 242, 215, 73, 3, 180, 65, 232, 161, 1, 103, 173, 145, 196, 8, 201, 166, 3, 112, 216, 52, 41, 39, 95, 243, 165, 253, 218, 160, 139, 0, 0, 38, 82, 40}) - tv5.Exp(tv5, &exp) - tv5.Mul(&tv5, &tv2) - tv2.Mul(&tv5, v) - tv3.Mul(&tv5, u) - // line 10 - tv4.Mul(&tv3, &tv2) + tv5.Exp(tv5, &exp) // 6. tv5 = tv5ᶜ³ + tv5.Mul(&tv5, &tv2) // 7. tv5 = tv5 * tv2 + tv2.Mul(&tv5, v) // 8. tv2 = tv5 * v + tv3.Mul(&tv5, u) // 9. tv3 = tv5 * u + tv4.Mul(&tv3, &tv2) // 10. tv4 = tv3 * tv2 // c5 = 1099511627776 exp.SetBytes([]byte{1, 0, 0, 0, 0, 0}) - tv5.Exp(tv4, &exp) - - isQNr := g1NotOne(&tv5) - - tv2.Mul(&tv3, &fp.Element{17614810958234635860, 11393801269165528284, 8781501035240632779, 8106712880529013806, 4971838157288047198, 122121039825317715}) - tv5.Mul(&tv4, &tv1) - - // line 15 - - tv3.Select(int(isQNr), &tv3, &tv2) - tv4.Select(int(isQNr), &tv4, &tv5) - - exp.Lsh(big.NewInt(1), 41-2) - - for i := 41; i >= 2; i-- { - //line 20 - tv5.Exp(tv4, &exp) - nE1 := g1NotOne(&tv5) - - tv2.Mul(&tv3, &tv1) - tv1.Mul(&tv1, &tv1) - tv5.Mul(&tv4, &tv1) - - tv3.Select(int(nE1), &tv3, &tv2) - tv4.Select(int(nE1), &tv4, &tv5) - - exp.Rsh(&exp, 1) + tv5.Exp(tv4, &exp) // 11. tv5 = tv4ᶜ⁵ + isQNr := g1NotOne(&tv5) // 12. isQR = tv5 == 1 + c7 := fp.Element{17614810958234635860, 11393801269165528284, 8781501035240632779, 8106712880529013806, 4971838157288047198, 122121039825317715} + tv2.Mul(&tv3, &c7) // 13. tv2 = tv3 * c7 + tv5.Mul(&tv4, &tv1) // 14. tv5 = tv4 * tv1 + tv3.Select(int(isQNr), &tv3, &tv2) // 15. tv3 = CMOV(tv2, tv3, isQR) + tv4.Select(int(isQNr), &tv4, &tv5) // 16. tv4 = CMOV(tv5, tv4, isQR) + exp.Lsh(big.NewInt(1), 41-2) // 18, 19: tv5 = 2ⁱ⁻² for i = c1 + + for i := 41; i >= 2; i-- { // 17. for i in (c1, c1 - 1, ..., 2): + + tv5.Exp(tv4, &exp) // 20. tv5 = tv4ᵗᵛ⁵ + nE1 := g1NotOne(&tv5) // 21. e1 = tv5 == 1 + tv2.Mul(&tv3, &tv1) // 22. tv2 = tv3 * tv1 + tv1.Mul(&tv1, &tv1) // 23. tv1 = tv1 * tv1 Why not write square? + tv5.Mul(&tv4, &tv1) // 24. tv5 = tv4 * tv1 + tv3.Select(int(nE1), &tv3, &tv2) // 25. tv3 = CMOV(tv2, tv3, e1) + tv4.Select(int(nE1), &tv4, &tv5) // 26. tv4 = CMOV(tv5, tv4, e1) + + if i > 2 { + exp.Rsh(&exp, 1) // 18, 19. tv5 = 2ⁱ⁻² + } } *z = tv3 @@ -175,30 +168,29 @@ func g1MulByZ(z *fp.Element, x *fp.Element) { *z = res } -//TODO: Define A,B here - -// From https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ Pg 80 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-simplified-swu-method // mapToCurve1 implements the SSWU map // No cofactor clearing or isogeny func mapToCurve1(u *fp.Element) G1Affine { + var sswuIsoCurveCoeffA = fp.Element{15314533651602404840, 3999629397495592995, 17991228730268553058, 13253234862282888158, 4784493033884022421, 276795783356562829} + var sswuIsoCurveCoeffB = fp.Element{10499526804702755432, 6768914877862902950, 8287496811509120276, 9263962031121981469, 5075273437274786541, 60255618913255595} + var tv1 fp.Element - tv1.Square(u) + tv1.Square(u) // 1. tv1 = u² //mul tv1 by Z - g1MulByZ(&tv1, &tv1) + g1MulByZ(&tv1, &tv1) // 2. tv1 = Z * tv1 var tv2 fp.Element - tv2.Square(&tv1) - tv2.Add(&tv2, &tv1) + tv2.Square(&tv1) // 3. tv2 = tv1² + tv2.Add(&tv2, &tv1) // 4. tv2 = tv2 + tv1 var tv3 fp.Element - //Standard doc line 5 var tv4 fp.Element tv4.SetOne() - tv3.Add(&tv2, &tv4) - //TODO: Use bCurveConf when no isogeny - tv3.Mul(&tv3, &fp.Element{10499526804702755432, 6768914877862902950, 8287496811509120276, 9263962031121981469, 5075273437274786541, 60255618913255595}) + tv3.Add(&tv2, &tv4) // 5. tv3 = tv2 + 1 + tv3.Mul(&tv3, &sswuIsoCurveCoeffB) // 6. tv3 = B * tv3 tv2NZero := g1NotZero(&tv2) @@ -206,48 +198,45 @@ func mapToCurve1(u *fp.Element) G1Affine { tv4 = fp.Element{5249763402351377716, 3384457438931451475, 13367120442609335946, 13855353052415766542, 11761008755492169078, 30127809456627797} tv2.Neg(&tv2) - tv4.Select(int(tv2NZero), &tv4, &tv2) - //TODO: When no isogeny use curve constants - tv2 = fp.Element{15314533651602404840, 3999629397495592995, 17991228730268553058, 13253234862282888158, 4784493033884022421, 276795783356562829} - tv4.Mul(&tv4, &tv2) + tv4.Select(int(tv2NZero), &tv4, &tv2) // 7. tv4 = CMOV(Z, -tv2, tv2 != 0) + tv4.Mul(&tv4, &sswuIsoCurveCoeffA) // 8. tv4 = A * tv4 - tv2.Square(&tv3) + tv2.Square(&tv3) // 9. tv2 = tv3² var tv6 fp.Element - //Standard doc line 10 - tv6.Square(&tv4) + tv6.Square(&tv4) // 10. tv6 = tv4² var tv5 fp.Element - tv5.Mul(&tv6, &fp.Element{15314533651602404840, 3999629397495592995, 17991228730268553058, 13253234862282888158, 4784493033884022421, 276795783356562829}) + tv5.Mul(&tv6, &sswuIsoCurveCoeffA) // 11. tv5 = A * tv6 - tv2.Add(&tv2, &tv5) - tv2.Mul(&tv2, &tv3) - tv6.Mul(&tv6, &tv4) + tv2.Add(&tv2, &tv5) // 12. tv2 = tv2 + tv5 + tv2.Mul(&tv2, &tv3) // 13. tv2 = tv2 * tv3 + tv6.Mul(&tv6, &tv4) // 14. tv6 = tv6 * tv4 - //Standards doc line 15 - tv5.Mul(&tv6, &fp.Element{10499526804702755432, 6768914877862902950, 8287496811509120276, 9263962031121981469, 5075273437274786541, 60255618913255595}) - tv2.Add(&tv2, &tv5) + tv5.Mul(&tv6, &sswuIsoCurveCoeffB) // 15. tv5 = B * tv6 + tv2.Add(&tv2, &tv5) // 16. tv2 = tv2 + tv5 var x fp.Element - x.Mul(&tv1, &tv3) + x.Mul(&tv1, &tv3) // 17. x = tv1 * tv3 var y1 fp.Element - gx1NSquare := g1SqrtRatio(&y1, &tv2, &tv6) + gx1NSquare := g1SqrtRatio(&y1, &tv2, &tv6) // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6) var y fp.Element - y.Mul(&tv1, u) + y.Mul(&tv1, u) // 19. y = tv1 * u - //Standards doc line 20 - y.Mul(&y, &y1) + y.Mul(&y, &y1) // 20. y = y * y1 - x.Select(int(gx1NSquare), &tv3, &x) - y.Select(int(gx1NSquare), &y1, &y) + x.Select(int(gx1NSquare), &tv3, &x) // 21. x = CMOV(x, tv3, is_gx1_square) + y.Select(int(gx1NSquare), &y1, &y) // 22. y = CMOV(y, y1, is_gx1_square) y1.Neg(&y) y.Select(int(g1Sgn0(u)^g1Sgn0(&y)), &y, &y1) - //Standards doc line 25 - x.Div(&x, &tv4) + // 23. e1 = sgn0(u) == sgn0(y) + // 24. y = CMOV(-y, y, e1) + + x.Div(&x, &tv4) // 25. x = x / tv4 return G1Affine{x, y} } @@ -290,13 +279,13 @@ func hashToFp(msg, dst []byte, count int) ([]fp.Element, error) { // g1Sgn0 is an algebraic substitute for the notion of sign in ordered fields // Namely, every non-zero quadratic residue in a finite field of characteristic =/= 2 has exactly two square roots, one of each sign -// Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/ section 4.1 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-the-sgn0-function // The sign of an element is not obviously related to that of its Montgomery form func g1Sgn0(z *fp.Element) uint64 { nonMont := *z nonMont.FromMont() - + // m == 1 return nonMont[0] % 2 } @@ -313,7 +302,7 @@ func MapToG1(u fp.Element) G1Affine { // EncodeToG1 hashes a message to a point on the G1 curve using the SSWU map. // It is faster than HashToG1, but the result is not uniformly distributed. Unsuitable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -//https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/#section-6.6.3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func EncodeToG1(msg, dst []byte) (G1Affine, error) { var res G1Affine @@ -333,7 +322,7 @@ func EncodeToG1(msg, dst []byte) (G1Affine, error) { // HashToG1 hashes a message to a point on the G1 curve using the SSWU map. // Slower than EncodeToG1, but usable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func HashToG1(msg, dst []byte) (G1Affine, error) { u, err := hashToFp(msg, dst, 2*1) if err != nil { @@ -343,7 +332,7 @@ func HashToG1(msg, dst []byte) (G1Affine, error) { Q0 := mapToCurve1(&u[0]) Q1 := mapToCurve1(&u[1]) - //TODO: Add in E' first, then apply isogeny + //TODO (perf): Add in E' first, then apply isogeny g1Isogeny(&Q0) g1Isogeny(&Q1) diff --git a/ecc/bls12-381/hash_to_g1.go b/ecc/bls12-381/hash_to_g1.go index 1e5bbbe4ec..23c108b946 100644 --- a/ecc/bls12-381/hash_to_g1.go +++ b/ecc/bls12-381/hash_to_g1.go @@ -131,34 +131,35 @@ func g1Isogeny(p *G1Affine) { // g1SqrtRatio computes the square root of u/v and returns 0 iff u/v was indeed a quadratic residue // if not, we get sqrt(Z * u / v). Recall that Z is non-residue +// If v = 0, u/v is meaningless and the output is unspecified, without raising an error. // The main idea is that since the computation of the square root involves taking large powers of u/v, the inversion of v can be avoided func g1SqrtRatio(z *fp.Element, u *fp.Element, v *fp.Element) uint64 { - // Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ F.2.1.2. q = 3 mod 4 + // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-optimized-sqrt_ratio-for-q- (3 mod 4) var tv1 fp.Element - tv1.Square(v) + tv1.Square(v) // 1. tv1 = v² var tv2 fp.Element - tv2.Mul(u, v) - tv1.Mul(&tv1, &tv2) + tv2.Mul(u, v) // 2. tv2 = u * v + tv1.Mul(&tv1, &tv2) // 3. tv1 = tv1 * tv2 var y1 fp.Element { var c1 big.Int // c1 = 1000602388805416848354447456433976039139220704984751971333014534031007912622709466110671907282253916009473568139946 - c1.SetBytes([]byte{6, 128, 68, 122, 142, 95, 249, 166, 146, 198, 233, 237, 144, 210, 235, 53, 217, 29, 210, 225, 60, 225, 68, 175, 217, 204, 52, 168, 61, 172, 61, 137, 7, 170, 255, 255, 172, 84, 255, 255, 238, 127, 191, 255, 255, 255, 234, 170}) - y1.Exp(tv1, &c1) + c1.SetBytes([]byte{6, 128, 68, 122, 142, 95, 249, 166, 146, 198, 233, 237, 144, 210, 235, 53, 217, 29, 210, 225, 60, 225, 68, 175, 217, 204, 52, 168, 61, 172, 61, 137, 7, 170, 255, 255, 172, 84, 255, 255, 238, 127, 191, 255, 255, 255, 234, 170}) // c1 = (q - 3) / 4 # Integer arithmetic + + y1.Exp(tv1, &c1) // 4. y1 = tv1ᶜ¹ } - y1.Mul(&y1, &tv2) + y1.Mul(&y1, &tv2) // 5. y1 = y1 * tv2 var y2 fp.Element - y2.Mul(&y1, &fp.Element{17544630987809824292, 17306709551153317753, 8299808889594647786, 5930295261504720397, 675038575008112577, 167386374569371918}) - - var tv3 fp.Element - tv3.Square(&y1) - tv3.Mul(&tv3, v) - - isQNr := tv3.NotEqual(u) - z.Select(int(isQNr), &y1, &y2) + // c2 = sqrt(-Z) + tv3 := fp.Element{17544630987809824292, 17306709551153317753, 8299808889594647786, 5930295261504720397, 675038575008112577, 167386374569371918} + y2.Mul(&y1, &tv3) // 6. y2 = y1 * c2 + tv3.Square(&y1) // 7. tv3 = y1² + tv3.Mul(&tv3, v) // 8. tv3 = tv3 * v + isQNr := tv3.NotEqual(u) // 9. isQR = tv3 == u + z.Select(int(isQNr), &y1, &y2) // 10. y = CMOV(y2, y1, isQR) return isQNr } @@ -176,30 +177,29 @@ func g1MulByZ(z *fp.Element, x *fp.Element) { *z = res } -//TODO: Define A,B here - -// From https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ Pg 80 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-simplified-swu-method // mapToCurve1 implements the SSWU map // No cofactor clearing or isogeny func mapToCurve1(u *fp.Element) G1Affine { + var sswuIsoCurveCoeffA = fp.Element{3415322872136444497, 9675504606121301699, 13284745414851768802, 2873609449387478652, 2897906769629812789, 1536947672689614213} + var sswuIsoCurveCoeffB = fp.Element{18129637713272545760, 11144507692959411567, 10108153527111632324, 9745270364868568433, 14587922135379007624, 469008097655535723} + var tv1 fp.Element - tv1.Square(u) + tv1.Square(u) // 1. tv1 = u² //mul tv1 by Z - g1MulByZ(&tv1, &tv1) + g1MulByZ(&tv1, &tv1) // 2. tv1 = Z * tv1 var tv2 fp.Element - tv2.Square(&tv1) - tv2.Add(&tv2, &tv1) + tv2.Square(&tv1) // 3. tv2 = tv1² + tv2.Add(&tv2, &tv1) // 4. tv2 = tv2 + tv1 var tv3 fp.Element - //Standard doc line 5 var tv4 fp.Element tv4.SetOne() - tv3.Add(&tv2, &tv4) - //TODO: Use bCurveConf when no isogeny - tv3.Mul(&tv3, &fp.Element{18129637713272545760, 11144507692959411567, 10108153527111632324, 9745270364868568433, 14587922135379007624, 469008097655535723}) + tv3.Add(&tv2, &tv4) // 5. tv3 = tv2 + 1 + tv3.Mul(&tv3, &sswuIsoCurveCoeffB) // 6. tv3 = B * tv3 tv2NZero := g1NotZero(&tv2) @@ -207,48 +207,45 @@ func mapToCurve1(u *fp.Element) G1Affine { tv4 = fp.Element{9830232086645309404, 1112389714365644829, 8603885298299447491, 11361495444721768256, 5788602283869803809, 543934104870762216} tv2.Neg(&tv2) - tv4.Select(int(tv2NZero), &tv4, &tv2) - //TODO: When no isogeny use curve constants - tv2 = fp.Element{3415322872136444497, 9675504606121301699, 13284745414851768802, 2873609449387478652, 2897906769629812789, 1536947672689614213} - tv4.Mul(&tv4, &tv2) + tv4.Select(int(tv2NZero), &tv4, &tv2) // 7. tv4 = CMOV(Z, -tv2, tv2 != 0) + tv4.Mul(&tv4, &sswuIsoCurveCoeffA) // 8. tv4 = A * tv4 - tv2.Square(&tv3) + tv2.Square(&tv3) // 9. tv2 = tv3² var tv6 fp.Element - //Standard doc line 10 - tv6.Square(&tv4) + tv6.Square(&tv4) // 10. tv6 = tv4² var tv5 fp.Element - tv5.Mul(&tv6, &fp.Element{3415322872136444497, 9675504606121301699, 13284745414851768802, 2873609449387478652, 2897906769629812789, 1536947672689614213}) + tv5.Mul(&tv6, &sswuIsoCurveCoeffA) // 11. tv5 = A * tv6 - tv2.Add(&tv2, &tv5) - tv2.Mul(&tv2, &tv3) - tv6.Mul(&tv6, &tv4) + tv2.Add(&tv2, &tv5) // 12. tv2 = tv2 + tv5 + tv2.Mul(&tv2, &tv3) // 13. tv2 = tv2 * tv3 + tv6.Mul(&tv6, &tv4) // 14. tv6 = tv6 * tv4 - //Standards doc line 15 - tv5.Mul(&tv6, &fp.Element{18129637713272545760, 11144507692959411567, 10108153527111632324, 9745270364868568433, 14587922135379007624, 469008097655535723}) - tv2.Add(&tv2, &tv5) + tv5.Mul(&tv6, &sswuIsoCurveCoeffB) // 15. tv5 = B * tv6 + tv2.Add(&tv2, &tv5) // 16. tv2 = tv2 + tv5 var x fp.Element - x.Mul(&tv1, &tv3) + x.Mul(&tv1, &tv3) // 17. x = tv1 * tv3 var y1 fp.Element - gx1NSquare := g1SqrtRatio(&y1, &tv2, &tv6) + gx1NSquare := g1SqrtRatio(&y1, &tv2, &tv6) // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6) var y fp.Element - y.Mul(&tv1, u) + y.Mul(&tv1, u) // 19. y = tv1 * u - //Standards doc line 20 - y.Mul(&y, &y1) + y.Mul(&y, &y1) // 20. y = y * y1 - x.Select(int(gx1NSquare), &tv3, &x) - y.Select(int(gx1NSquare), &y1, &y) + x.Select(int(gx1NSquare), &tv3, &x) // 21. x = CMOV(x, tv3, is_gx1_square) + y.Select(int(gx1NSquare), &y1, &y) // 22. y = CMOV(y, y1, is_gx1_square) y1.Neg(&y) y.Select(int(g1Sgn0(u)^g1Sgn0(&y)), &y, &y1) - //Standards doc line 25 - x.Div(&x, &tv4) + // 23. e1 = sgn0(u) == sgn0(y) + // 24. y = CMOV(-y, y, e1) + + x.Div(&x, &tv4) // 25. x = x / tv4 return G1Affine{x, y} } @@ -291,13 +288,13 @@ func hashToFp(msg, dst []byte, count int) ([]fp.Element, error) { // g1Sgn0 is an algebraic substitute for the notion of sign in ordered fields // Namely, every non-zero quadratic residue in a finite field of characteristic =/= 2 has exactly two square roots, one of each sign -// Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/ section 4.1 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-the-sgn0-function // The sign of an element is not obviously related to that of its Montgomery form func g1Sgn0(z *fp.Element) uint64 { nonMont := *z nonMont.FromMont() - + // m == 1 return nonMont[0] % 2 } @@ -314,7 +311,7 @@ func MapToG1(u fp.Element) G1Affine { // EncodeToG1 hashes a message to a point on the G1 curve using the SSWU map. // It is faster than HashToG1, but the result is not uniformly distributed. Unsuitable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -//https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/#section-6.6.3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func EncodeToG1(msg, dst []byte) (G1Affine, error) { var res G1Affine @@ -334,7 +331,7 @@ func EncodeToG1(msg, dst []byte) (G1Affine, error) { // HashToG1 hashes a message to a point on the G1 curve using the SSWU map. // Slower than EncodeToG1, but usable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func HashToG1(msg, dst []byte) (G1Affine, error) { u, err := hashToFp(msg, dst, 2*1) if err != nil { @@ -344,7 +341,7 @@ func HashToG1(msg, dst []byte) (G1Affine, error) { Q0 := mapToCurve1(&u[0]) Q1 := mapToCurve1(&u[1]) - //TODO: Add in E' first, then apply isogeny + //TODO (perf): Add in E' first, then apply isogeny g1Isogeny(&Q0) g1Isogeny(&Q1) diff --git a/ecc/bls12-381/hash_to_g2.go b/ecc/bls12-381/hash_to_g2.go index 428e631735..19f8fbfa8a 100644 --- a/ecc/bls12-381/hash_to_g2.go +++ b/ecc/bls12-381/hash_to_g2.go @@ -130,10 +130,11 @@ func g2Isogeny(p *G2Affine) { // g2SqrtRatio computes the square root of u/v and returns 0 iff u/v was indeed a quadratic residue // if not, we get sqrt(Z * u / v). Recall that Z is non-residue +// If v = 0, u/v is meaningless and the output is unspecified, without raising an error. // The main idea is that since the computation of the square root involves taking large powers of u/v, the inversion of v can be avoided func g2SqrtRatio(z *fptower.E2, u *fptower.E2, v *fptower.E2) uint64 { - // Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ F.2.1.1. for any field + // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-sqrt_ratio-for-any-field tv1 := fptower.E2{ A0: fp.Element{8921533702591418330, 15859389534032789116, 3389114680249073393, 15116930867080254631, 3288288975085550621, 1021049300055853010}, @@ -142,59 +143,51 @@ func g2SqrtRatio(z *fptower.E2, u *fptower.E2, v *fptower.E2) uint64 { var tv2, tv3, tv4, tv5 fptower.E2 var exp big.Int - // c4 = 7 = 2^3 - 1 + // c4 = 7 = 2³ - 1 // q is odd so c1 is at least 1. exp.SetBytes([]byte{7}) - tv2.Exp(*v, &exp) - tv3.Mul(&tv2, &tv2) - tv3.Mul(&tv3, v) - - // line 5 - tv5.Mul(u, &tv3) + tv2.Exp(*v, &exp) // 2. tv2 = vᶜ⁴ + tv3.Square(&tv2) // 3. tv3 = tv2² + tv3.Mul(&tv3, v) // 4. tv3 = tv3 * v + tv5.Mul(u, &tv3) // 5. tv5 = u * tv3 // c3 = 1001205140483106588246484290269935788605945006208159541241399033561623546780709821462541004956387089373434649096260670658193992783731681621012512651314777238193313314641988297376025498093520728838658813979860931248214124593092835 exp.SetBytes([]byte{42, 67, 122, 75, 140, 53, 252, 116, 189, 39, 142, 170, 34, 242, 94, 158, 45, 201, 14, 80, 231, 4, 107, 70, 110, 89, 228, 147, 73, 232, 189, 5, 10, 98, 207, 209, 109, 220, 166, 239, 83, 20, 147, 48, 151, 142, 240, 17, 214, 134, 25, 200, 97, 133, 199, 178, 146, 232, 90, 135, 9, 26, 4, 150, 107, 249, 30, 211, 231, 27, 116, 49, 98, 195, 56, 54, 33, 19, 207, 215, 206, 214, 177, 215, 99, 130, 234, 178, 106, 160, 0, 1, 199, 24, 227}) - tv5.Exp(tv5, &exp) - tv5.Mul(&tv5, &tv2) - tv2.Mul(&tv5, v) - tv3.Mul(&tv5, u) - // line 10 - tv4.Mul(&tv3, &tv2) + tv5.Exp(tv5, &exp) // 6. tv5 = tv5ᶜ³ + tv5.Mul(&tv5, &tv2) // 7. tv5 = tv5 * tv2 + tv2.Mul(&tv5, v) // 8. tv2 = tv5 * v + tv3.Mul(&tv5, u) // 9. tv3 = tv5 * u + tv4.Mul(&tv3, &tv2) // 10. tv4 = tv3 * tv2 // c5 = 4 exp.SetBytes([]byte{4}) - tv5.Exp(tv4, &exp) - - isQNr := g2NotOne(&tv5) - - tv2.Mul(&tv3, &fptower.E2{ + tv5.Exp(tv4, &exp) // 11. tv5 = tv4ᶜ⁵ + isQNr := g2NotOne(&tv5) // 12. isQR = tv5 == 1 + c7 := fptower.E2{ A0: fp.Element{1921729236329761493, 9193968980645934504, 9862280504246317678, 6861748847800817560, 10375788487011937166, 4460107375738415}, A1: fp.Element{16821121318233475459, 10183025025229892778, 1779012082459463630, 3442292649700377418, 1061500799026501234, 1352426537312017168}, - }) - tv5.Mul(&tv4, &tv1) - - // line 15 - - tv3.Select(int(isQNr), &tv3, &tv2) - tv4.Select(int(isQNr), &tv4, &tv5) - - exp.Lsh(big.NewInt(1), 3-2) - - for i := 3; i >= 2; i-- { - //line 20 - tv5.Exp(tv4, &exp) - nE1 := g2NotOne(&tv5) - - tv2.Mul(&tv3, &tv1) - tv1.Mul(&tv1, &tv1) - tv5.Mul(&tv4, &tv1) - - tv3.Select(int(nE1), &tv3, &tv2) - tv4.Select(int(nE1), &tv4, &tv5) - - exp.Rsh(&exp, 1) + } + tv2.Mul(&tv3, &c7) // 13. tv2 = tv3 * c7 + tv5.Mul(&tv4, &tv1) // 14. tv5 = tv4 * tv1 + tv3.Select(int(isQNr), &tv3, &tv2) // 15. tv3 = CMOV(tv2, tv3, isQR) + tv4.Select(int(isQNr), &tv4, &tv5) // 16. tv4 = CMOV(tv5, tv4, isQR) + exp.Lsh(big.NewInt(1), 3-2) // 18, 19: tv5 = 2ⁱ⁻² for i = c1 + + for i := 3; i >= 2; i-- { // 17. for i in (c1, c1 - 1, ..., 2): + + tv5.Exp(tv4, &exp) // 20. tv5 = tv4ᵗᵛ⁵ + nE1 := g2NotOne(&tv5) // 21. e1 = tv5 == 1 + tv2.Mul(&tv3, &tv1) // 22. tv2 = tv3 * tv1 + tv1.Mul(&tv1, &tv1) // 23. tv1 = tv1 * tv1 Why not write square? + tv5.Mul(&tv4, &tv1) // 24. tv5 = tv4 * tv1 + tv3.Select(int(nE1), &tv3, &tv2) // 25. tv3 = CMOV(tv2, tv3, e1) + tv4.Select(int(nE1), &tv4, &tv5) // 26. tv4 = CMOV(tv5, tv4, e1) + + if i > 2 { + exp.Rsh(&exp, 1) // 18, 19. tv5 = 2ⁱ⁻² + } } *z = tv3 @@ -219,33 +212,35 @@ func g2MulByZ(z *fptower.E2, x *fptower.E2) { } -//TODO: Define A,B here - -// From https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ Pg 80 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-simplified-swu-method // mapToCurve2 implements the SSWU map // No cofactor clearing or isogeny func mapToCurve2(u *fptower.E2) G2Affine { + var sswuIsoCurveCoeffA = fptower.E2{ + A0: fp.Element{0}, + A1: fp.Element{16517514583386313282, 74322656156451461, 16683759486841714365, 815493829203396097, 204518332920448171, 1306242806803223655}, + } + var sswuIsoCurveCoeffB = fptower.E2{ + A0: fp.Element{2515823342057463218, 7982686274772798116, 7934098172177393262, 8484566552980779962, 4455086327883106868, 1323173589274087377}, + A1: fp.Element{2515823342057463218, 7982686274772798116, 7934098172177393262, 8484566552980779962, 4455086327883106868, 1323173589274087377}, + } + var tv1 fptower.E2 - tv1.Square(u) + tv1.Square(u) // 1. tv1 = u² //mul tv1 by Z - g2MulByZ(&tv1, &tv1) + g2MulByZ(&tv1, &tv1) // 2. tv1 = Z * tv1 var tv2 fptower.E2 - tv2.Square(&tv1) - tv2.Add(&tv2, &tv1) + tv2.Square(&tv1) // 3. tv2 = tv1² + tv2.Add(&tv2, &tv1) // 4. tv2 = tv2 + tv1 var tv3 fptower.E2 - //Standard doc line 5 var tv4 fptower.E2 tv4.SetOne() - tv3.Add(&tv2, &tv4) - //TODO: Use bCurveConf when no isogeny - tv3.Mul(&tv3, &fptower.E2{ - A0: fp.Element{2515823342057463218, 7982686274772798116, 7934098172177393262, 8484566552980779962, 4455086327883106868, 1323173589274087377}, - A1: fp.Element{2515823342057463218, 7982686274772798116, 7934098172177393262, 8484566552980779962, 4455086327883106868, 1323173589274087377}, - }) + tv3.Add(&tv2, &tv4) // 5. tv3 = tv2 + 1 + tv3.Mul(&tv3, &sswuIsoCurveCoeffB) // 6. tv3 = B * tv3 tv2NZero := g2NotZero(&tv2) @@ -256,57 +251,45 @@ func mapToCurve2(u *fptower.E2) G2Affine { } tv2.Neg(&tv2) - tv4.Select(int(tv2NZero), &tv4, &tv2) - //TODO: When no isogeny use curve constants - tv2 = fptower.E2{ - A0: fp.Element{0}, - A1: fp.Element{16517514583386313282, 74322656156451461, 16683759486841714365, 815493829203396097, 204518332920448171, 1306242806803223655}, - } - tv4.Mul(&tv4, &tv2) + tv4.Select(int(tv2NZero), &tv4, &tv2) // 7. tv4 = CMOV(Z, -tv2, tv2 != 0) + tv4.Mul(&tv4, &sswuIsoCurveCoeffA) // 8. tv4 = A * tv4 - tv2.Square(&tv3) + tv2.Square(&tv3) // 9. tv2 = tv3² var tv6 fptower.E2 - //Standard doc line 10 - tv6.Square(&tv4) + tv6.Square(&tv4) // 10. tv6 = tv4² var tv5 fptower.E2 - tv5.Mul(&tv6, &fptower.E2{ - A0: fp.Element{0}, - A1: fp.Element{16517514583386313282, 74322656156451461, 16683759486841714365, 815493829203396097, 204518332920448171, 1306242806803223655}, - }) + tv5.Mul(&tv6, &sswuIsoCurveCoeffA) // 11. tv5 = A * tv6 - tv2.Add(&tv2, &tv5) - tv2.Mul(&tv2, &tv3) - tv6.Mul(&tv6, &tv4) + tv2.Add(&tv2, &tv5) // 12. tv2 = tv2 + tv5 + tv2.Mul(&tv2, &tv3) // 13. tv2 = tv2 * tv3 + tv6.Mul(&tv6, &tv4) // 14. tv6 = tv6 * tv4 - //Standards doc line 15 - tv5.Mul(&tv6, &fptower.E2{ - A0: fp.Element{2515823342057463218, 7982686274772798116, 7934098172177393262, 8484566552980779962, 4455086327883106868, 1323173589274087377}, - A1: fp.Element{2515823342057463218, 7982686274772798116, 7934098172177393262, 8484566552980779962, 4455086327883106868, 1323173589274087377}, - }) - tv2.Add(&tv2, &tv5) + tv5.Mul(&tv6, &sswuIsoCurveCoeffB) // 15. tv5 = B * tv6 + tv2.Add(&tv2, &tv5) // 16. tv2 = tv2 + tv5 var x fptower.E2 - x.Mul(&tv1, &tv3) + x.Mul(&tv1, &tv3) // 17. x = tv1 * tv3 var y1 fptower.E2 - gx1NSquare := g2SqrtRatio(&y1, &tv2, &tv6) + gx1NSquare := g2SqrtRatio(&y1, &tv2, &tv6) // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6) var y fptower.E2 - y.Mul(&tv1, u) + y.Mul(&tv1, u) // 19. y = tv1 * u - //Standards doc line 20 - y.Mul(&y, &y1) + y.Mul(&y, &y1) // 20. y = y * y1 - x.Select(int(gx1NSquare), &tv3, &x) - y.Select(int(gx1NSquare), &y1, &y) + x.Select(int(gx1NSquare), &tv3, &x) // 21. x = CMOV(x, tv3, is_gx1_square) + y.Select(int(gx1NSquare), &y1, &y) // 22. y = CMOV(y, y1, is_gx1_square) y1.Neg(&y) y.Select(int(g2Sgn0(u)^g2Sgn0(&y)), &y, &y1) - //Standards doc line 25 - x.Div(&x, &tv4) + // 23. e1 = sgn0(u) == sgn0(y) + // 24. y = CMOV(-y, y, e1) + + x.Div(&x, &tv4) // 25. x = x / tv4 return G2Affine{x, y} } @@ -328,28 +311,29 @@ func g2EvalPolynomial(z *fptower.E2, monic bool, coefficients []fptower.E2, x *f // g2Sgn0 is an algebraic substitute for the notion of sign in ordered fields // Namely, every non-zero quadratic residue in a finite field of characteristic =/= 2 has exactly two square roots, one of each sign -// Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/ section 4.1 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-the-sgn0-function // The sign of an element is not obviously related to that of its Montgomery form func g2Sgn0(z *fptower.E2) uint64 { nonMont := *z nonMont.FromMont() - sign := uint64(0) - zero := uint64(1) + sign := uint64(0) // 1. sign = 0 + zero := uint64(1) // 2. zero = 1 var signI uint64 var zeroI uint64 - signI = nonMont.A0[0] % 2 - sign = sign | (zero & signI) - + // 3. i = 1 + signI = nonMont.A0[0] % 2 // 4. sign_i = x_i mod 2 zeroI = g1NotZero(&nonMont.A0) - zeroI = 1 ^ (zeroI|-zeroI)>>63 - zero = zero & zeroI - - signI = nonMont.A1[0] % 2 - sign = sign | (zero & signI) - + zeroI = 1 ^ (zeroI|-zeroI)>>63 // 5. zero_i = x_i == 0 + sign = sign | (zero & signI) // 6. sign = sign OR (zero AND sign_i) # Avoid short-circuit logic ops + zero = zero & zeroI // 7. zero = zero AND zero_i + // 3. i = 2 + signI = nonMont.A1[0] % 2 // 4. sign_i = x_i mod 2 + // 5. zero_i = x_i == 0 + sign = sign | (zero & signI) // 6. sign = sign OR (zero AND sign_i) # Avoid short-circuit logic ops + // 7. zero = zero AND zero_i return sign } @@ -366,7 +350,7 @@ func MapToG2(u fptower.E2) G2Affine { // EncodeToG2 hashes a message to a point on the G2 curve using the SSWU map. // It is faster than HashToG2, but the result is not uniformly distributed. Unsuitable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -//https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/#section-6.6.3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func EncodeToG2(msg, dst []byte) (G2Affine, error) { var res G2Affine @@ -389,7 +373,7 @@ func EncodeToG2(msg, dst []byte) (G2Affine, error) { // HashToG2 hashes a message to a point on the G2 curve using the SSWU map. // Slower than EncodeToG2, but usable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func HashToG2(msg, dst []byte) (G2Affine, error) { u, err := hashToFp(msg, dst, 2*2) if err != nil { @@ -405,7 +389,7 @@ func HashToG2(msg, dst []byte) (G2Affine, error) { A1: u[2+1], }) - //TODO: Add in E' first, then apply isogeny + //TODO (perf): Add in E' first, then apply isogeny g2Isogeny(&Q0) g2Isogeny(&Q1) diff --git a/ecc/bls24-315/hash_to_g1.go b/ecc/bls24-315/hash_to_g1.go index a36076f752..faae02c7a7 100644 --- a/ecc/bls24-315/hash_to_g1.go +++ b/ecc/bls24-315/hash_to_g1.go @@ -89,65 +89,58 @@ func g1Isogeny(p *G1Affine) { // g1SqrtRatio computes the square root of u/v and returns 0 iff u/v was indeed a quadratic residue // if not, we get sqrt(Z * u / v). Recall that Z is non-residue +// If v = 0, u/v is meaningless and the output is unspecified, without raising an error. // The main idea is that since the computation of the square root involves taking large powers of u/v, the inversion of v can be avoided func g1SqrtRatio(z *fp.Element, u *fp.Element, v *fp.Element) uint64 { - // Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ F.2.1.1. for any field + // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-sqrt_ratio-for-any-field tv1 := fp.Element{11195128742969911322, 1359304652430195240, 15267589139354181340, 10518360976114966361, 300769513466036652} //tv1 = c6 var tv2, tv3, tv4, tv5 fp.Element var exp big.Int - // c4 = 1048575 = 2^20 - 1 + // c4 = 1048575 = 2²⁰ - 1 // q is odd so c1 is at least 1. exp.SetBytes([]byte{15, 255, 255}) - tv2.Exp(*v, &exp) - tv3.Mul(&tv2, &tv2) - tv3.Mul(&tv3, v) - - // line 5 - tv5.Mul(u, &tv3) + tv2.Exp(*v, &exp) // 2. tv2 = vᶜ⁴ + tv3.Square(&tv2) // 3. tv3 = tv2² + tv3.Mul(&tv3, v) // 4. tv3 = tv3 * v + tv5.Mul(u, &tv3) // 5. tv5 = u * tv3 // c3 = 18932887415653914611351818986134037849871398170907377879650252106493894621432467626129921 exp.SetBytes([]byte{38, 17, 208, 21, 172, 54, 178, 134, 159, 186, 76, 95, 75, 226, 245, 126, 246, 14, 128, 213, 19, 208, 215, 2, 16, 247, 46, 210, 149, 239, 40, 19, 127, 64, 23, 250, 1}) - tv5.Exp(tv5, &exp) - tv5.Mul(&tv5, &tv2) - tv2.Mul(&tv5, v) - tv3.Mul(&tv5, u) - // line 10 - tv4.Mul(&tv3, &tv2) + tv5.Exp(tv5, &exp) // 6. tv5 = tv5ᶜ³ + tv5.Mul(&tv5, &tv2) // 7. tv5 = tv5 * tv2 + tv2.Mul(&tv5, v) // 8. tv2 = tv5 * v + tv3.Mul(&tv5, u) // 9. tv3 = tv5 * u + tv4.Mul(&tv3, &tv2) // 10. tv4 = tv3 * tv2 // c5 = 524288 exp.SetBytes([]byte{8, 0, 0}) - tv5.Exp(tv4, &exp) - - isQNr := g1NotOne(&tv5) - - tv2.Mul(&tv3, &fp.Element{1141794007209116247, 256324699145650176, 2958838397954514392, 9976887947641032208, 153331829745922234}) - tv5.Mul(&tv4, &tv1) - - // line 15 - - tv3.Select(int(isQNr), &tv3, &tv2) - tv4.Select(int(isQNr), &tv4, &tv5) - - exp.Lsh(big.NewInt(1), 20-2) - - for i := 20; i >= 2; i-- { - //line 20 - tv5.Exp(tv4, &exp) - nE1 := g1NotOne(&tv5) - - tv2.Mul(&tv3, &tv1) - tv1.Mul(&tv1, &tv1) - tv5.Mul(&tv4, &tv1) - - tv3.Select(int(nE1), &tv3, &tv2) - tv4.Select(int(nE1), &tv4, &tv5) - - exp.Rsh(&exp, 1) + tv5.Exp(tv4, &exp) // 11. tv5 = tv4ᶜ⁵ + isQNr := g1NotOne(&tv5) // 12. isQR = tv5 == 1 + c7 := fp.Element{1141794007209116247, 256324699145650176, 2958838397954514392, 9976887947641032208, 153331829745922234} + tv2.Mul(&tv3, &c7) // 13. tv2 = tv3 * c7 + tv5.Mul(&tv4, &tv1) // 14. tv5 = tv4 * tv1 + tv3.Select(int(isQNr), &tv3, &tv2) // 15. tv3 = CMOV(tv2, tv3, isQR) + tv4.Select(int(isQNr), &tv4, &tv5) // 16. tv4 = CMOV(tv5, tv4, isQR) + exp.Lsh(big.NewInt(1), 20-2) // 18, 19: tv5 = 2ⁱ⁻² for i = c1 + + for i := 20; i >= 2; i-- { // 17. for i in (c1, c1 - 1, ..., 2): + + tv5.Exp(tv4, &exp) // 20. tv5 = tv4ᵗᵛ⁵ + nE1 := g1NotOne(&tv5) // 21. e1 = tv5 == 1 + tv2.Mul(&tv3, &tv1) // 22. tv2 = tv3 * tv1 + tv1.Mul(&tv1, &tv1) // 23. tv1 = tv1 * tv1 Why not write square? + tv5.Mul(&tv4, &tv1) // 24. tv5 = tv4 * tv1 + tv3.Select(int(nE1), &tv3, &tv2) // 25. tv3 = CMOV(tv2, tv3, e1) + tv4.Select(int(nE1), &tv4, &tv5) // 26. tv4 = CMOV(tv5, tv4, e1) + + if i > 2 { + exp.Rsh(&exp, 1) // 18, 19. tv5 = 2ⁱ⁻² + } } *z = tv3 @@ -175,30 +168,29 @@ func g1MulByZ(z *fp.Element, x *fp.Element) { *z = res } -//TODO: Define A,B here - -// From https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ Pg 80 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-simplified-swu-method // mapToCurve1 implements the SSWU map // No cofactor clearing or isogeny func mapToCurve1(u *fp.Element) G1Affine { + var sswuIsoCurveCoeffA = fp.Element{5402807948305211529, 9163880483319140034, 7646126700453841420, 11071466103913358468, 124200740526673728} + var sswuIsoCurveCoeffB = fp.Element{16058189711238232929, 8302337653269510588, 11411933349841587630, 8954038365926617417, 177308873523699836} + var tv1 fp.Element - tv1.Square(u) + tv1.Square(u) // 1. tv1 = u² //mul tv1 by Z - g1MulByZ(&tv1, &tv1) + g1MulByZ(&tv1, &tv1) // 2. tv1 = Z * tv1 var tv2 fp.Element - tv2.Square(&tv1) - tv2.Add(&tv2, &tv1) + tv2.Square(&tv1) // 3. tv2 = tv1² + tv2.Add(&tv2, &tv1) // 4. tv2 = tv2 + tv1 var tv3 fp.Element - //Standard doc line 5 var tv4 fp.Element tv4.SetOne() - tv3.Add(&tv2, &tv4) - //TODO: Use bCurveConf when no isogeny - tv3.Mul(&tv3, &fp.Element{16058189711238232929, 8302337653269510588, 11411933349841587630, 8954038365926617417, 177308873523699836}) + tv3.Add(&tv2, &tv4) // 5. tv3 = tv2 + 1 + tv3.Mul(&tv3, &sswuIsoCurveCoeffB) // 6. tv3 = B * tv3 tv2NZero := g1NotZero(&tv2) @@ -206,48 +198,45 @@ func mapToCurve1(u *fp.Element) G1Affine { tv4 = fp.Element{8178485296672800069, 8476448362227282520, 14180928431697993131, 4308307642551989706, 120359802761433421} tv2.Neg(&tv2) - tv4.Select(int(tv2NZero), &tv4, &tv2) - //TODO: When no isogeny use curve constants - tv2 = fp.Element{5402807948305211529, 9163880483319140034, 7646126700453841420, 11071466103913358468, 124200740526673728} - tv4.Mul(&tv4, &tv2) + tv4.Select(int(tv2NZero), &tv4, &tv2) // 7. tv4 = CMOV(Z, -tv2, tv2 != 0) + tv4.Mul(&tv4, &sswuIsoCurveCoeffA) // 8. tv4 = A * tv4 - tv2.Square(&tv3) + tv2.Square(&tv3) // 9. tv2 = tv3² var tv6 fp.Element - //Standard doc line 10 - tv6.Square(&tv4) + tv6.Square(&tv4) // 10. tv6 = tv4² var tv5 fp.Element - tv5.Mul(&tv6, &fp.Element{5402807948305211529, 9163880483319140034, 7646126700453841420, 11071466103913358468, 124200740526673728}) + tv5.Mul(&tv6, &sswuIsoCurveCoeffA) // 11. tv5 = A * tv6 - tv2.Add(&tv2, &tv5) - tv2.Mul(&tv2, &tv3) - tv6.Mul(&tv6, &tv4) + tv2.Add(&tv2, &tv5) // 12. tv2 = tv2 + tv5 + tv2.Mul(&tv2, &tv3) // 13. tv2 = tv2 * tv3 + tv6.Mul(&tv6, &tv4) // 14. tv6 = tv6 * tv4 - //Standards doc line 15 - tv5.Mul(&tv6, &fp.Element{16058189711238232929, 8302337653269510588, 11411933349841587630, 8954038365926617417, 177308873523699836}) - tv2.Add(&tv2, &tv5) + tv5.Mul(&tv6, &sswuIsoCurveCoeffB) // 15. tv5 = B * tv6 + tv2.Add(&tv2, &tv5) // 16. tv2 = tv2 + tv5 var x fp.Element - x.Mul(&tv1, &tv3) + x.Mul(&tv1, &tv3) // 17. x = tv1 * tv3 var y1 fp.Element - gx1NSquare := g1SqrtRatio(&y1, &tv2, &tv6) + gx1NSquare := g1SqrtRatio(&y1, &tv2, &tv6) // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6) var y fp.Element - y.Mul(&tv1, u) + y.Mul(&tv1, u) // 19. y = tv1 * u - //Standards doc line 20 - y.Mul(&y, &y1) + y.Mul(&y, &y1) // 20. y = y * y1 - x.Select(int(gx1NSquare), &tv3, &x) - y.Select(int(gx1NSquare), &y1, &y) + x.Select(int(gx1NSquare), &tv3, &x) // 21. x = CMOV(x, tv3, is_gx1_square) + y.Select(int(gx1NSquare), &y1, &y) // 22. y = CMOV(y, y1, is_gx1_square) y1.Neg(&y) y.Select(int(g1Sgn0(u)^g1Sgn0(&y)), &y, &y1) - //Standards doc line 25 - x.Div(&x, &tv4) + // 23. e1 = sgn0(u) == sgn0(y) + // 24. y = CMOV(-y, y, e1) + + x.Div(&x, &tv4) // 25. x = x / tv4 return G1Affine{x, y} } @@ -290,13 +279,13 @@ func hashToFp(msg, dst []byte, count int) ([]fp.Element, error) { // g1Sgn0 is an algebraic substitute for the notion of sign in ordered fields // Namely, every non-zero quadratic residue in a finite field of characteristic =/= 2 has exactly two square roots, one of each sign -// Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/ section 4.1 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-the-sgn0-function // The sign of an element is not obviously related to that of its Montgomery form func g1Sgn0(z *fp.Element) uint64 { nonMont := *z nonMont.FromMont() - + // m == 1 return nonMont[0] % 2 } @@ -313,7 +302,7 @@ func MapToG1(u fp.Element) G1Affine { // EncodeToG1 hashes a message to a point on the G1 curve using the SSWU map. // It is faster than HashToG1, but the result is not uniformly distributed. Unsuitable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -//https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/#section-6.6.3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func EncodeToG1(msg, dst []byte) (G1Affine, error) { var res G1Affine @@ -333,7 +322,7 @@ func EncodeToG1(msg, dst []byte) (G1Affine, error) { // HashToG1 hashes a message to a point on the G1 curve using the SSWU map. // Slower than EncodeToG1, but usable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func HashToG1(msg, dst []byte) (G1Affine, error) { u, err := hashToFp(msg, dst, 2*1) if err != nil { @@ -343,7 +332,7 @@ func HashToG1(msg, dst []byte) (G1Affine, error) { Q0 := mapToCurve1(&u[0]) Q1 := mapToCurve1(&u[1]) - //TODO: Add in E' first, then apply isogeny + //TODO (perf): Add in E' first, then apply isogeny g1Isogeny(&Q0) g1Isogeny(&Q1) diff --git a/ecc/bls24-317/hash_to_g1.go b/ecc/bls24-317/hash_to_g1.go index 94fb16f0dc..ab839928e1 100644 --- a/ecc/bls24-317/hash_to_g1.go +++ b/ecc/bls24-317/hash_to_g1.go @@ -101,34 +101,35 @@ func g1Isogeny(p *G1Affine) { // g1SqrtRatio computes the square root of u/v and returns 0 iff u/v was indeed a quadratic residue // if not, we get sqrt(Z * u / v). Recall that Z is non-residue +// If v = 0, u/v is meaningless and the output is unspecified, without raising an error. // The main idea is that since the computation of the square root involves taking large powers of u/v, the inversion of v can be avoided func g1SqrtRatio(z *fp.Element, u *fp.Element, v *fp.Element) uint64 { - // Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ F.2.1.2. q = 3 mod 4 + // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-optimized-sqrt_ratio-for-q- (3 mod 4) var tv1 fp.Element - tv1.Square(v) + tv1.Square(v) // 1. tv1 = v² var tv2 fp.Element - tv2.Mul(u, v) - tv1.Mul(&tv1, &tv2) + tv2.Mul(u, v) // 2. tv2 = u * v + tv1.Mul(&tv1, &tv2) // 3. tv1 = tv1 * tv2 var y1 fp.Element { var c1 big.Int // c1 = 34098267776073977878774941477068514265486278030354898494302534825976493299308006404506539182762 - c1.SetBytes([]byte{4, 22, 50, 136, 155, 216, 34, 75, 60, 163, 241, 104, 45, 254, 116, 14, 69, 166, 152, 121, 161, 49, 205, 17, 181, 188, 206, 121, 13, 9, 47, 223, 163, 84, 75, 149, 151, 106, 202, 170}) - y1.Exp(tv1, &c1) + c1.SetBytes([]byte{4, 22, 50, 136, 155, 216, 34, 75, 60, 163, 241, 104, 45, 254, 116, 14, 69, 166, 152, 121, 161, 49, 205, 17, 181, 188, 206, 121, 13, 9, 47, 223, 163, 84, 75, 149, 151, 106, 202, 170}) // c1 = (q - 3) / 4 # Integer arithmetic + + y1.Exp(tv1, &c1) // 4. y1 = tv1ᶜ¹ } - y1.Mul(&y1, &tv2) + y1.Mul(&y1, &tv2) // 5. y1 = y1 * tv2 var y2 fp.Element - y2.Mul(&y1, &fp.Element{10652859563586318787, 3643689439157831556, 9236201363192486412, 11781990169133948855, 1044489031832785863}) - - var tv3 fp.Element - tv3.Square(&y1) - tv3.Mul(&tv3, v) - - isQNr := tv3.NotEqual(u) - z.Select(int(isQNr), &y1, &y2) + // c2 = sqrt(-Z) + tv3 := fp.Element{10652859563586318787, 3643689439157831556, 9236201363192486412, 11781990169133948855, 1044489031832785863} + y2.Mul(&y1, &tv3) // 6. y2 = y1 * c2 + tv3.Square(&y1) // 7. tv3 = y1² + tv3.Mul(&tv3, v) // 8. tv3 = tv3 * v + isQNr := tv3.NotEqual(u) // 9. isQR = tv3 == u + z.Select(int(isQNr), &y1, &y2) // 10. y = CMOV(y2, y1, isQR) return isQNr } @@ -144,30 +145,29 @@ func g1MulByZ(z *fp.Element, x *fp.Element) { *z = res } -//TODO: Define A,B here - -// From https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ Pg 80 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-simplified-swu-method // mapToCurve1 implements the SSWU map // No cofactor clearing or isogeny func mapToCurve1(u *fp.Element) G1Affine { + var sswuIsoCurveCoeffA = fp.Element{2751493217506761890, 10508083672876982400, 9568653941102734201, 1934905759174260726, 590687129635764257} + var sswuIsoCurveCoeffB = fp.Element{14477170886729819615, 1154054877908840441, 13400991584556574205, 3277375072715511934, 979998381373634863} + var tv1 fp.Element - tv1.Square(u) + tv1.Square(u) // 1. tv1 = u² //mul tv1 by Z - g1MulByZ(&tv1, &tv1) + g1MulByZ(&tv1, &tv1) // 2. tv1 = Z * tv1 var tv2 fp.Element - tv2.Square(&tv1) - tv2.Add(&tv2, &tv1) + tv2.Square(&tv1) // 3. tv2 = tv1² + tv2.Add(&tv2, &tv1) // 4. tv2 = tv2 + tv1 var tv3 fp.Element - //Standard doc line 5 var tv4 fp.Element tv4.SetOne() - tv3.Add(&tv2, &tv4) - //TODO: Use bCurveConf when no isogeny - tv3.Mul(&tv3, &fp.Element{14477170886729819615, 1154054877908840441, 13400991584556574205, 3277375072715511934, 979998381373634863}) + tv3.Add(&tv2, &tv4) // 5. tv3 = tv2 + 1 + tv3.Mul(&tv3, &sswuIsoCurveCoeffB) // 6. tv3 = B * tv3 tv2NZero := g1NotZero(&tv2) @@ -175,48 +175,45 @@ func mapToCurve1(u *fp.Element) G1Affine { tv4 = fp.Element{18400687542797871745, 809728271075671860, 17770696641280178537, 10361798156408411167, 334758614216279309} tv2.Neg(&tv2) - tv4.Select(int(tv2NZero), &tv4, &tv2) - //TODO: When no isogeny use curve constants - tv2 = fp.Element{2751493217506761890, 10508083672876982400, 9568653941102734201, 1934905759174260726, 590687129635764257} - tv4.Mul(&tv4, &tv2) + tv4.Select(int(tv2NZero), &tv4, &tv2) // 7. tv4 = CMOV(Z, -tv2, tv2 != 0) + tv4.Mul(&tv4, &sswuIsoCurveCoeffA) // 8. tv4 = A * tv4 - tv2.Square(&tv3) + tv2.Square(&tv3) // 9. tv2 = tv3² var tv6 fp.Element - //Standard doc line 10 - tv6.Square(&tv4) + tv6.Square(&tv4) // 10. tv6 = tv4² var tv5 fp.Element - tv5.Mul(&tv6, &fp.Element{2751493217506761890, 10508083672876982400, 9568653941102734201, 1934905759174260726, 590687129635764257}) + tv5.Mul(&tv6, &sswuIsoCurveCoeffA) // 11. tv5 = A * tv6 - tv2.Add(&tv2, &tv5) - tv2.Mul(&tv2, &tv3) - tv6.Mul(&tv6, &tv4) + tv2.Add(&tv2, &tv5) // 12. tv2 = tv2 + tv5 + tv2.Mul(&tv2, &tv3) // 13. tv2 = tv2 * tv3 + tv6.Mul(&tv6, &tv4) // 14. tv6 = tv6 * tv4 - //Standards doc line 15 - tv5.Mul(&tv6, &fp.Element{14477170886729819615, 1154054877908840441, 13400991584556574205, 3277375072715511934, 979998381373634863}) - tv2.Add(&tv2, &tv5) + tv5.Mul(&tv6, &sswuIsoCurveCoeffB) // 15. tv5 = B * tv6 + tv2.Add(&tv2, &tv5) // 16. tv2 = tv2 + tv5 var x fp.Element - x.Mul(&tv1, &tv3) + x.Mul(&tv1, &tv3) // 17. x = tv1 * tv3 var y1 fp.Element - gx1NSquare := g1SqrtRatio(&y1, &tv2, &tv6) + gx1NSquare := g1SqrtRatio(&y1, &tv2, &tv6) // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6) var y fp.Element - y.Mul(&tv1, u) + y.Mul(&tv1, u) // 19. y = tv1 * u - //Standards doc line 20 - y.Mul(&y, &y1) + y.Mul(&y, &y1) // 20. y = y * y1 - x.Select(int(gx1NSquare), &tv3, &x) - y.Select(int(gx1NSquare), &y1, &y) + x.Select(int(gx1NSquare), &tv3, &x) // 21. x = CMOV(x, tv3, is_gx1_square) + y.Select(int(gx1NSquare), &y1, &y) // 22. y = CMOV(y, y1, is_gx1_square) y1.Neg(&y) y.Select(int(g1Sgn0(u)^g1Sgn0(&y)), &y, &y1) - //Standards doc line 25 - x.Div(&x, &tv4) + // 23. e1 = sgn0(u) == sgn0(y) + // 24. y = CMOV(-y, y, e1) + + x.Div(&x, &tv4) // 25. x = x / tv4 return G1Affine{x, y} } @@ -259,13 +256,13 @@ func hashToFp(msg, dst []byte, count int) ([]fp.Element, error) { // g1Sgn0 is an algebraic substitute for the notion of sign in ordered fields // Namely, every non-zero quadratic residue in a finite field of characteristic =/= 2 has exactly two square roots, one of each sign -// Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/ section 4.1 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-the-sgn0-function // The sign of an element is not obviously related to that of its Montgomery form func g1Sgn0(z *fp.Element) uint64 { nonMont := *z nonMont.FromMont() - + // m == 1 return nonMont[0] % 2 } @@ -282,7 +279,7 @@ func MapToG1(u fp.Element) G1Affine { // EncodeToG1 hashes a message to a point on the G1 curve using the SSWU map. // It is faster than HashToG1, but the result is not uniformly distributed. Unsuitable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -//https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/#section-6.6.3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func EncodeToG1(msg, dst []byte) (G1Affine, error) { var res G1Affine @@ -302,7 +299,7 @@ func EncodeToG1(msg, dst []byte) (G1Affine, error) { // HashToG1 hashes a message to a point on the G1 curve using the SSWU map. // Slower than EncodeToG1, but usable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func HashToG1(msg, dst []byte) (G1Affine, error) { u, err := hashToFp(msg, dst, 2*1) if err != nil { @@ -312,7 +309,7 @@ func HashToG1(msg, dst []byte) (G1Affine, error) { Q0 := mapToCurve1(&u[0]) Q1 := mapToCurve1(&u[1]) - //TODO: Add in E' first, then apply isogeny + //TODO (perf): Add in E' first, then apply isogeny g1Isogeny(&Q0) g1Isogeny(&Q1) diff --git a/ecc/bn254/hash_to_g1.go b/ecc/bn254/hash_to_g1.go index 47e35a7d48..9df840a530 100644 --- a/ecc/bn254/hash_to_g1.go +++ b/ecc/bn254/hash_to_g1.go @@ -23,7 +23,7 @@ import ( // mapToCurve1 implements the Shallue and van de Woestijne method, applicable to any elliptic curve in Weierstrass form // No cofactor clearing or isogeny -// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-14#appendix-F.1 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#straightline-svdw func mapToCurve1(u *fp.Element) G1Affine { var tv1, tv2, tv3, tv4 fp.Element var x1, x2, x3, gx1, gx2, gx, x, y fp.Element @@ -36,7 +36,6 @@ func mapToCurve1(u *fp.Element) G1Affine { //c3 = sqrt(-g(Z) * (3 * Z² + 4 * A)) # sgn0(c3) MUST equal 0 //c4 = -4 * g(Z) / (3 * Z² + 4 * A) - //TODO: Move outside function? Z := fp.Element{15230403791020821917, 754611498739239741, 7381016538464732716, 1011752739694698287} c1 := fp.Element{1248766071674976557, 10548065924188627562, 16242874202584236114, 560012691975822483} c2 := fp.Element{12997850613838968789, 14304628359724097447, 2950087706404981016, 1237622763554136189} @@ -58,8 +57,7 @@ func mapToCurve1(u *fp.Element) G1Affine { x1.Sub(&c2, &tv4) // 10. x1 = c2 - tv4 gx1.Square(&x1) // 11. gx1 = x1² - //TODO: Beware A ≠ 0 - //12. gx1 = gx1 + A + //12. gx1 = gx1 + A All curves in gnark-crypto have A=0 (j-invariant=0). It is crucial to include this step if the curve has nonzero A coefficient. gx1.Mul(&gx1, &x1) // 13. gx1 = gx1 * x1 gx1.Add(&gx1, &bCurveCoeff) // 14. gx1 = gx1 + B gx1NotSquare = gx1.Legendre() >> 1 // 15. e1 = is_square(gx1) @@ -67,7 +65,7 @@ func mapToCurve1(u *fp.Element) G1Affine { x2.Add(&c2, &tv4) // 16. x2 = c2 + tv4 gx2.Square(&x2) // 17. gx2 = x2² - // 18. gx2 = gx2 + A + // 18. gx2 = gx2 + A See line 12 gx2.Mul(&gx2, &x2) // 19. gx2 = gx2 * x2 gx2.Add(&gx2, &bCurveCoeff) // 20. gx2 = gx2 + B @@ -123,13 +121,13 @@ func hashToFp(msg, dst []byte, count int) ([]fp.Element, error) { // g1Sgn0 is an algebraic substitute for the notion of sign in ordered fields // Namely, every non-zero quadratic residue in a finite field of characteristic =/= 2 has exactly two square roots, one of each sign -// Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/ section 4.1 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-the-sgn0-function // The sign of an element is not obviously related to that of its Montgomery form func g1Sgn0(z *fp.Element) uint64 { nonMont := *z nonMont.FromMont() - + // m == 1 return nonMont[0] % 2 } @@ -143,7 +141,7 @@ func MapToG1(u fp.Element) G1Affine { // EncodeToG1 hashes a message to a point on the G1 curve using the SVDW map. // It is faster than HashToG1, but the result is not uniformly distributed. Unsuitable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -//https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/#section-6.6.3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func EncodeToG1(msg, dst []byte) (G1Affine, error) { var res G1Affine @@ -160,7 +158,7 @@ func EncodeToG1(msg, dst []byte) (G1Affine, error) { // HashToG1 hashes a message to a point on the G1 curve using the SVDW map. // Slower than EncodeToG1, but usable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func HashToG1(msg, dst []byte) (G1Affine, error) { u, err := hashToFp(msg, dst, 2*1) if err != nil { diff --git a/ecc/bn254/hash_to_g2.go b/ecc/bn254/hash_to_g2.go index 4f19f3db23..4395405049 100644 --- a/ecc/bn254/hash_to_g2.go +++ b/ecc/bn254/hash_to_g2.go @@ -23,7 +23,7 @@ import ( // mapToCurve2 implements the Shallue and van de Woestijne method, applicable to any elliptic curve in Weierstrass form // No cofactor clearing or isogeny -// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-14#appendix-F.1 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#straightline-svdw func mapToCurve2(u *fptower.E2) G2Affine { var tv1, tv2, tv3, tv4 fptower.E2 var x1, x2, x3, gx1, gx2, gx, x, y fptower.E2 @@ -36,7 +36,6 @@ func mapToCurve2(u *fptower.E2) G2Affine { //c3 = sqrt(-g(Z) * (3 * Z² + 4 * A)) # sgn0(c3) MUST equal 0 //c4 = -4 * g(Z) / (3 * Z² + 4 * A) - //TODO: Move outside function? Z := fptower.E2{ A0: fp.Element{15230403791020821917, 754611498739239741, 7381016538464732716, 1011752739694698287}, A1: fp.Element{0}, @@ -73,8 +72,7 @@ func mapToCurve2(u *fptower.E2) G2Affine { x1.Sub(&c2, &tv4) // 10. x1 = c2 - tv4 gx1.Square(&x1) // 11. gx1 = x1² - //TODO: Beware A ≠ 0 - //12. gx1 = gx1 + A + //12. gx1 = gx1 + A All curves in gnark-crypto have A=0 (j-invariant=0). It is crucial to include this step if the curve has nonzero A coefficient. gx1.Mul(&gx1, &x1) // 13. gx1 = gx1 * x1 gx1.Add(&gx1, &bTwistCurveCoeff) // 14. gx1 = gx1 + B gx1NotSquare = gx1.Legendre() >> 1 // 15. e1 = is_square(gx1) @@ -82,7 +80,7 @@ func mapToCurve2(u *fptower.E2) G2Affine { x2.Add(&c2, &tv4) // 16. x2 = c2 + tv4 gx2.Square(&x2) // 17. gx2 = x2² - // 18. gx2 = gx2 + A + // 18. gx2 = gx2 + A See line 12 gx2.Mul(&gx2, &x2) // 19. gx2 = gx2 * x2 gx2.Add(&gx2, &bTwistCurveCoeff) // 20. gx2 = gx2 + B @@ -117,28 +115,29 @@ func mapToCurve2(u *fptower.E2) G2Affine { // g2Sgn0 is an algebraic substitute for the notion of sign in ordered fields // Namely, every non-zero quadratic residue in a finite field of characteristic =/= 2 has exactly two square roots, one of each sign -// Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/ section 4.1 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-the-sgn0-function // The sign of an element is not obviously related to that of its Montgomery form func g2Sgn0(z *fptower.E2) uint64 { nonMont := *z nonMont.FromMont() - sign := uint64(0) - zero := uint64(1) + sign := uint64(0) // 1. sign = 0 + zero := uint64(1) // 2. zero = 1 var signI uint64 var zeroI uint64 - signI = nonMont.A0[0] % 2 - sign = sign | (zero & signI) - + // 3. i = 1 + signI = nonMont.A0[0] % 2 // 4. sign_i = x_i mod 2 zeroI = g1NotZero(&nonMont.A0) - zeroI = 1 ^ (zeroI|-zeroI)>>63 - zero = zero & zeroI - - signI = nonMont.A1[0] % 2 - sign = sign | (zero & signI) - + zeroI = 1 ^ (zeroI|-zeroI)>>63 // 5. zero_i = x_i == 0 + sign = sign | (zero & signI) // 6. sign = sign OR (zero AND sign_i) # Avoid short-circuit logic ops + zero = zero & zeroI // 7. zero = zero AND zero_i + // 3. i = 2 + signI = nonMont.A1[0] % 2 // 4. sign_i = x_i mod 2 + // 5. zero_i = x_i == 0 + sign = sign | (zero & signI) // 6. sign = sign OR (zero AND sign_i) # Avoid short-circuit logic ops + // 7. zero = zero AND zero_i return sign } @@ -153,7 +152,7 @@ func MapToG2(u fptower.E2) G2Affine { // EncodeToG2 hashes a message to a point on the G2 curve using the SVDW map. // It is faster than HashToG2, but the result is not uniformly distributed. Unsuitable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -//https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/#section-6.6.3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func EncodeToG2(msg, dst []byte) (G2Affine, error) { var res G2Affine @@ -174,7 +173,7 @@ func EncodeToG2(msg, dst []byte) (G2Affine, error) { // HashToG2 hashes a message to a point on the G2 curve using the SVDW map. // Slower than EncodeToG2, but usable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func HashToG2(msg, dst []byte) (G2Affine, error) { u, err := hashToFp(msg, dst, 2*2) if err != nil { diff --git a/ecc/bw6-633/hash_to_g1.go b/ecc/bw6-633/hash_to_g1.go index 41dd42345d..7e6d7f1a5d 100644 --- a/ecc/bw6-633/hash_to_g1.go +++ b/ecc/bw6-633/hash_to_g1.go @@ -111,52 +111,48 @@ func g1Isogeny(p *G1Affine) { // g1SqrtRatio computes the square root of u/v and returns 0 iff u/v was indeed a quadratic residue // if not, we get sqrt(Z * u / v). Recall that Z is non-residue +// If v = 0, u/v is meaningless and the output is unspecified, without raising an error. // The main idea is that since the computation of the square root involves taking large powers of u/v, the inversion of v can be avoided func g1SqrtRatio(z *fp.Element, u *fp.Element, v *fp.Element) uint64 { - // Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ F.2.1.3. q = 5 mod 8 - // TODO: Test correct use of Element.Select + // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-optimized-sqrt_ratio-for-q-5 (mod 8) var tv1, tv2 fp.Element - tv1.Square(v) - tv2.Mul(&tv1, v) - tv1.Square(&tv1) - tv2.Mul(&tv2, u) - tv1.Mul(&tv1, &tv2) + tv1.Square(v) // 1. tv1 = v² + tv2.Mul(&tv1, v) // 2. tv2 = tv1 * v + tv1.Square(&tv1) // 3. tv1 = tv1² + tv2.Mul(&tv2, u) // 4. tv2 = tv2 * u + tv1.Mul(&tv1, &tv2) // 5. tv1 = tv1 * tv2 var c1 big.Int - // c1 = 2561809830520971834851673423317370187208698865113597259441094318876502093964723972342941881295000173427447071280550850682906970629308555147846507041252715387332745928667496467633282424373249 + // c1 = (q - 5) / 8 = 2561809830520971834851673423317370187208698865113597259441094318876502093964723972342941881295000173427447071280550850682906970629308555147846507041252715387332745928667496467633282424373249 c1.SetBytes([]byte{36, 204, 103, 152, 30, 107, 236, 127, 131, 66, 233, 224, 58, 229, 86, 181, 31, 155, 24, 235, 175, 58, 88, 233, 203, 46, 211, 91, 55, 123, 69, 240, 42, 84, 216, 31, 91, 212, 146, 23, 27, 83, 235, 208, 126, 175, 137, 47, 193, 209, 10, 29, 183, 180, 128, 250, 246, 185, 207, 87, 7, 56, 68, 167, 166, 211, 122, 98, 40, 254, 231, 154, 233, 34, 221, 72, 174, 0, 1}) var y1 fp.Element - y1.Exp(tv1, &c1) - y1.Mul(&y1, &tv2) - tv1.Mul(&y1, &fp.Element{7899625277197386435, 5217716493391639390, 7472932469883704682, 7632350077606897049, 9296070723299766388, 14353472371414671016, 14644604696869838127, 11421353192299464576, 237964513547175570, 46667570639865841}) - tv2.Square(&tv1) - - //Line 10 in std doc - tv2.Mul(&tv2, v) - - y1.Select(int(tv2.NotEqual(u)), &tv1, &y1) - - tv2.Square(&y1) - tv2.Mul(&tv2, v) - - //Line 15 - isQNr := tv2.NotEqual(u) + y1.Exp(tv1, &c1) // 6. y1 = tv1ᶜ¹ + y1.Mul(&y1, &tv2) // 7. y1 = y1 * tv2 + // c2 = sqrt(-1) + c2 := fp.Element{7899625277197386435, 5217716493391639390, 7472932469883704682, 7632350077606897049, 9296070723299766388, 14353472371414671016, 14644604696869838127, 11421353192299464576, 237964513547175570, 46667570639865841} + tv1.Mul(&y1, &c2) // 8. tv1 = y1 * c2 + tv2.Square(&tv1) // 9. tv2 = tv1² + tv2.Mul(&tv2, v) // 10. tv2 = tv2 * v + // 11. e1 = tv2 == u + y1.Select(int(tv2.NotEqual(u)), &tv1, &y1) // 12. y1 = CMOV(y1, tv1, e1) + tv2.Square(&y1) // 13. tv2 = y1² + tv2.Mul(&tv2, v) // 14. tv2 = tv2 * v + isQNr := tv2.NotEqual(u) // 15. isQR = tv2 == u var y2 fp.Element - y2.Mul(&y1, &fp.Element{17779787117422825675, 4939796438941267298, 17917495165866445585, 10745325761140663972, 1801762923695319826, 15143865745144854318, 15159144602769454149, 1697730798702809869, 18119907780441139825, 48351383131100009}) - tv1.Mul(&y2, &fp.Element{7899625277197386435, 5217716493391639390, 7472932469883704682, 7632350077606897049, 9296070723299766388, 14353472371414671016, 14644604696869838127, 11421353192299464576, 237964513547175570, 46667570639865841}) - tv2.Square(&tv1) - tv2.Mul(&tv2, v) - + // c3 = sqrt(Z / c2) + y2 = fp.Element{17779787117422825675, 4939796438941267298, 17917495165866445585, 10745325761140663972, 1801762923695319826, 15143865745144854318, 15159144602769454149, 1697730798702809869, 18119907780441139825, 48351383131100009} + y2.Mul(&y1, &y2) // 16. y2 = y1 * c3 + tv1.Mul(&y2, &c2) // 17. tv1 = y2 * c2 + tv2.Square(&tv1) // 18. tv2 = tv1² + tv2.Mul(&tv2, v) // 19. tv2 = tv2 * v var tv3 fp.Element - //Line 20 // Z = [11] - g1MulByZ(&tv3, u) - - y2.Select(int(tv2.NotEqual(&tv3)), &tv1, &y2) - - z.Select(int(isQNr), &y1, &y2) + g1MulByZ(&tv3, u) // 20. tv3 = Z * u + // 21. e2 = tv2 == tv3 + y2.Select(int(tv2.NotEqual(&tv3)), &tv1, &y2) // 22. y2 = CMOV(y2, tv1, e2) + z.Select(int(isQNr), &y1, &y2) // 23. y = CMOV(y2, y1, isQR) return isQNr } @@ -174,30 +170,29 @@ func g1MulByZ(z *fp.Element, x *fp.Element) { *z = res } -//TODO: Define A,B here - -// From https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ Pg 80 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-simplified-swu-method // mapToCurve1 implements the SSWU map // No cofactor clearing or isogeny func mapToCurve1(u *fp.Element) G1Affine { + var sswuIsoCurveCoeffA = fp.Element{12925890271846221020, 6355149021182850637, 12305199997029221454, 3176370205483940054, 1111744716227392272, 1674946515969267914, 9082444721826297409, 17859522351279563418, 11442187008395780520, 4206825732020662} + var sswuIsoCurveCoeffB = fp.Element{1447342806075484185, 5642327672839545870, 16783436050687675045, 2630023864181351186, 5909133526915342434, 1057352115267779153, 1923190814798170064, 13280701548970829092, 3305076617946573429, 29606717104036842} + var tv1 fp.Element - tv1.Square(u) + tv1.Square(u) // 1. tv1 = u² //mul tv1 by Z - g1MulByZ(&tv1, &tv1) + g1MulByZ(&tv1, &tv1) // 2. tv1 = Z * tv1 var tv2 fp.Element - tv2.Square(&tv1) - tv2.Add(&tv2, &tv1) + tv2.Square(&tv1) // 3. tv2 = tv1² + tv2.Add(&tv2, &tv1) // 4. tv2 = tv2 + tv1 var tv3 fp.Element - //Standard doc line 5 var tv4 fp.Element tv4.SetOne() - tv3.Add(&tv2, &tv4) - //TODO: Use bCurveConf when no isogeny - tv3.Mul(&tv3, &fp.Element{1447342806075484185, 5642327672839545870, 16783436050687675045, 2630023864181351186, 5909133526915342434, 1057352115267779153, 1923190814798170064, 13280701548970829092, 3305076617946573429, 29606717104036842}) + tv3.Add(&tv2, &tv4) // 5. tv3 = tv2 + 1 + tv3.Mul(&tv3, &sswuIsoCurveCoeffB) // 6. tv3 = B * tv3 tv2NZero := g1NotZero(&tv2) @@ -205,48 +200,45 @@ func mapToCurve1(u *fp.Element) G1Affine { tv4 = fp.Element{6130771042861286320, 11947466704102345269, 5006184736040647654, 10738967583325648129, 6155303802163134778, 6459686480506411032, 14448065740527999419, 1019798761927372322, 5080373183861200608, 66158761009468389} tv2.Neg(&tv2) - tv4.Select(int(tv2NZero), &tv4, &tv2) - //TODO: When no isogeny use curve constants - tv2 = fp.Element{12925890271846221020, 6355149021182850637, 12305199997029221454, 3176370205483940054, 1111744716227392272, 1674946515969267914, 9082444721826297409, 17859522351279563418, 11442187008395780520, 4206825732020662} - tv4.Mul(&tv4, &tv2) + tv4.Select(int(tv2NZero), &tv4, &tv2) // 7. tv4 = CMOV(Z, -tv2, tv2 != 0) + tv4.Mul(&tv4, &sswuIsoCurveCoeffA) // 8. tv4 = A * tv4 - tv2.Square(&tv3) + tv2.Square(&tv3) // 9. tv2 = tv3² var tv6 fp.Element - //Standard doc line 10 - tv6.Square(&tv4) + tv6.Square(&tv4) // 10. tv6 = tv4² var tv5 fp.Element - tv5.Mul(&tv6, &fp.Element{12925890271846221020, 6355149021182850637, 12305199997029221454, 3176370205483940054, 1111744716227392272, 1674946515969267914, 9082444721826297409, 17859522351279563418, 11442187008395780520, 4206825732020662}) + tv5.Mul(&tv6, &sswuIsoCurveCoeffA) // 11. tv5 = A * tv6 - tv2.Add(&tv2, &tv5) - tv2.Mul(&tv2, &tv3) - tv6.Mul(&tv6, &tv4) + tv2.Add(&tv2, &tv5) // 12. tv2 = tv2 + tv5 + tv2.Mul(&tv2, &tv3) // 13. tv2 = tv2 * tv3 + tv6.Mul(&tv6, &tv4) // 14. tv6 = tv6 * tv4 - //Standards doc line 15 - tv5.Mul(&tv6, &fp.Element{1447342806075484185, 5642327672839545870, 16783436050687675045, 2630023864181351186, 5909133526915342434, 1057352115267779153, 1923190814798170064, 13280701548970829092, 3305076617946573429, 29606717104036842}) - tv2.Add(&tv2, &tv5) + tv5.Mul(&tv6, &sswuIsoCurveCoeffB) // 15. tv5 = B * tv6 + tv2.Add(&tv2, &tv5) // 16. tv2 = tv2 + tv5 var x fp.Element - x.Mul(&tv1, &tv3) + x.Mul(&tv1, &tv3) // 17. x = tv1 * tv3 var y1 fp.Element - gx1NSquare := g1SqrtRatio(&y1, &tv2, &tv6) + gx1NSquare := g1SqrtRatio(&y1, &tv2, &tv6) // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6) var y fp.Element - y.Mul(&tv1, u) + y.Mul(&tv1, u) // 19. y = tv1 * u - //Standards doc line 20 - y.Mul(&y, &y1) + y.Mul(&y, &y1) // 20. y = y * y1 - x.Select(int(gx1NSquare), &tv3, &x) - y.Select(int(gx1NSquare), &y1, &y) + x.Select(int(gx1NSquare), &tv3, &x) // 21. x = CMOV(x, tv3, is_gx1_square) + y.Select(int(gx1NSquare), &y1, &y) // 22. y = CMOV(y, y1, is_gx1_square) y1.Neg(&y) y.Select(int(g1Sgn0(u)^g1Sgn0(&y)), &y, &y1) - //Standards doc line 25 - x.Div(&x, &tv4) + // 23. e1 = sgn0(u) == sgn0(y) + // 24. y = CMOV(-y, y, e1) + + x.Div(&x, &tv4) // 25. x = x / tv4 return G1Affine{x, y} } @@ -289,13 +281,13 @@ func hashToFp(msg, dst []byte, count int) ([]fp.Element, error) { // g1Sgn0 is an algebraic substitute for the notion of sign in ordered fields // Namely, every non-zero quadratic residue in a finite field of characteristic =/= 2 has exactly two square roots, one of each sign -// Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/ section 4.1 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-the-sgn0-function // The sign of an element is not obviously related to that of its Montgomery form func g1Sgn0(z *fp.Element) uint64 { nonMont := *z nonMont.FromMont() - + // m == 1 return nonMont[0] % 2 } @@ -312,7 +304,7 @@ func MapToG1(u fp.Element) G1Affine { // EncodeToG1 hashes a message to a point on the G1 curve using the SSWU map. // It is faster than HashToG1, but the result is not uniformly distributed. Unsuitable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -//https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/#section-6.6.3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func EncodeToG1(msg, dst []byte) (G1Affine, error) { var res G1Affine @@ -332,7 +324,7 @@ func EncodeToG1(msg, dst []byte) (G1Affine, error) { // HashToG1 hashes a message to a point on the G1 curve using the SSWU map. // Slower than EncodeToG1, but usable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func HashToG1(msg, dst []byte) (G1Affine, error) { u, err := hashToFp(msg, dst, 2*1) if err != nil { @@ -342,7 +334,7 @@ func HashToG1(msg, dst []byte) (G1Affine, error) { Q0 := mapToCurve1(&u[0]) Q1 := mapToCurve1(&u[1]) - //TODO: Add in E' first, then apply isogeny + //TODO (perf): Add in E' first, then apply isogeny g1Isogeny(&Q0) g1Isogeny(&Q1) diff --git a/ecc/bw6-633/hash_to_g2.go b/ecc/bw6-633/hash_to_g2.go index 5ad54e8717..9b78724681 100644 --- a/ecc/bw6-633/hash_to_g2.go +++ b/ecc/bw6-633/hash_to_g2.go @@ -88,52 +88,48 @@ func g2Isogeny(p *G2Affine) { // g2SqrtRatio computes the square root of u/v and returns 0 iff u/v was indeed a quadratic residue // if not, we get sqrt(Z * u / v). Recall that Z is non-residue +// If v = 0, u/v is meaningless and the output is unspecified, without raising an error. // The main idea is that since the computation of the square root involves taking large powers of u/v, the inversion of v can be avoided func g2SqrtRatio(z *fp.Element, u *fp.Element, v *fp.Element) uint64 { - // Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ F.2.1.3. q = 5 mod 8 - // TODO: Test correct use of Element.Select + // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-optimized-sqrt_ratio-for-q-5 (mod 8) var tv1, tv2 fp.Element - tv1.Square(v) - tv2.Mul(&tv1, v) - tv1.Square(&tv1) - tv2.Mul(&tv2, u) - tv1.Mul(&tv1, &tv2) + tv1.Square(v) // 1. tv1 = v² + tv2.Mul(&tv1, v) // 2. tv2 = tv1 * v + tv1.Square(&tv1) // 3. tv1 = tv1² + tv2.Mul(&tv2, u) // 4. tv2 = tv2 * u + tv1.Mul(&tv1, &tv2) // 5. tv1 = tv1 * tv2 var c1 big.Int - // c1 = 2561809830520971834851673423317370187208698865113597259441094318876502093964723972342941881295000173427447071280550850682906970629308555147846507041252715387332745928667496467633282424373249 + // c1 = (q - 5) / 8 = 2561809830520971834851673423317370187208698865113597259441094318876502093964723972342941881295000173427447071280550850682906970629308555147846507041252715387332745928667496467633282424373249 c1.SetBytes([]byte{36, 204, 103, 152, 30, 107, 236, 127, 131, 66, 233, 224, 58, 229, 86, 181, 31, 155, 24, 235, 175, 58, 88, 233, 203, 46, 211, 91, 55, 123, 69, 240, 42, 84, 216, 31, 91, 212, 146, 23, 27, 83, 235, 208, 126, 175, 137, 47, 193, 209, 10, 29, 183, 180, 128, 250, 246, 185, 207, 87, 7, 56, 68, 167, 166, 211, 122, 98, 40, 254, 231, 154, 233, 34, 221, 72, 174, 0, 1}) var y1 fp.Element - y1.Exp(tv1, &c1) - y1.Mul(&y1, &tv2) - tv1.Mul(&y1, &fp.Element{7899625277197386435, 5217716493391639390, 7472932469883704682, 7632350077606897049, 9296070723299766388, 14353472371414671016, 14644604696869838127, 11421353192299464576, 237964513547175570, 46667570639865841}) - tv2.Square(&tv1) - - //Line 10 in std doc - tv2.Mul(&tv2, v) - - y1.Select(int(tv2.NotEqual(u)), &tv1, &y1) - - tv2.Square(&y1) - tv2.Mul(&tv2, v) - - //Line 15 - isQNr := tv2.NotEqual(u) + y1.Exp(tv1, &c1) // 6. y1 = tv1ᶜ¹ + y1.Mul(&y1, &tv2) // 7. y1 = y1 * tv2 + // c2 = sqrt(-1) + c2 := fp.Element{7899625277197386435, 5217716493391639390, 7472932469883704682, 7632350077606897049, 9296070723299766388, 14353472371414671016, 14644604696869838127, 11421353192299464576, 237964513547175570, 46667570639865841} + tv1.Mul(&y1, &c2) // 8. tv1 = y1 * c2 + tv2.Square(&tv1) // 9. tv2 = tv1² + tv2.Mul(&tv2, v) // 10. tv2 = tv2 * v + // 11. e1 = tv2 == u + y1.Select(int(tv2.NotEqual(u)), &tv1, &y1) // 12. y1 = CMOV(y1, tv1, e1) + tv2.Square(&y1) // 13. tv2 = y1² + tv2.Mul(&tv2, v) // 14. tv2 = tv2 * v + isQNr := tv2.NotEqual(u) // 15. isQR = tv2 == u var y2 fp.Element - y2.Mul(&y1, &fp.Element{16212120288951005687, 11690167560162600414, 9845362566212292170, 5006379754746321817, 3559960229467473872, 1378556217976105943, 4841104984578141598, 15436992508257808297, 6778583767067406308, 4544728946065242}) - tv1.Mul(&y2, &fp.Element{7899625277197386435, 5217716493391639390, 7472932469883704682, 7632350077606897049, 9296070723299766388, 14353472371414671016, 14644604696869838127, 11421353192299464576, 237964513547175570, 46667570639865841}) - tv2.Square(&tv1) - tv2.Mul(&tv2, v) - + // c3 = sqrt(Z / c2) + y2 = fp.Element{16212120288951005687, 11690167560162600414, 9845362566212292170, 5006379754746321817, 3559960229467473872, 1378556217976105943, 4841104984578141598, 15436992508257808297, 6778583767067406308, 4544728946065242} + y2.Mul(&y1, &y2) // 16. y2 = y1 * c3 + tv1.Mul(&y2, &c2) // 17. tv1 = y2 * c2 + tv2.Square(&tv1) // 18. tv2 = tv1² + tv2.Mul(&tv2, v) // 19. tv2 = tv2 * v var tv3 fp.Element - //Line 20 // Z = [2] - g2MulByZ(&tv3, u) - - y2.Select(int(tv2.NotEqual(&tv3)), &tv1, &y2) - - z.Select(int(isQNr), &y1, &y2) + g2MulByZ(&tv3, u) // 20. tv3 = Z * u + // 21. e2 = tv2 == tv3 + y2.Select(int(tv2.NotEqual(&tv3)), &tv1, &y2) // 22. y2 = CMOV(y2, tv1, e2) + z.Select(int(isQNr), &y1, &y2) // 23. y = CMOV(y2, y1, isQR) return isQNr } @@ -147,30 +143,29 @@ func g2MulByZ(z *fp.Element, x *fp.Element) { *z = res } -//TODO: Define A,B here - -// From https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ Pg 80 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-simplified-swu-method // mapToCurve2 implements the SSWU map // No cofactor clearing or isogeny func mapToCurve2(u *fp.Element) G2Affine { + var sswuIsoCurveCoeffA = fp.Element{13503940466125084703, 3000707982748310797, 1529397070312683242, 9240962296298654443, 4577258595340312235, 16046828875439788343, 7236093083337192433, 2860564553402019540, 5160479239841632821, 65394042426465165} + var sswuIsoCurveCoeffB = fp.Element{4170590011558214244, 9101648159034903675, 4256739633972552875, 7483080556638609334, 12430228215152656439, 9977400640742476476, 15847011074743951739, 17768582661138350292, 10869631430819016060, 64187107279947172} + var tv1 fp.Element - tv1.Square(u) + tv1.Square(u) // 1. tv1 = u² //mul tv1 by Z - g2MulByZ(&tv1, &tv1) + g2MulByZ(&tv1, &tv1) // 2. tv1 = Z * tv1 var tv2 fp.Element - tv2.Square(&tv1) - tv2.Add(&tv2, &tv1) + tv2.Square(&tv1) // 3. tv2 = tv1² + tv2.Add(&tv2, &tv1) // 4. tv2 = tv2 + tv1 var tv3 fp.Element - //Standard doc line 5 var tv4 fp.Element tv4.SetOne() - tv3.Add(&tv2, &tv4) - //TODO: Use bCurveConf when no isogeny - tv3.Mul(&tv3, &fp.Element{4170590011558214244, 9101648159034903675, 4256739633972552875, 7483080556638609334, 12430228215152656439, 9977400640742476476, 15847011074743951739, 17768582661138350292, 10869631430819016060, 64187107279947172}) + tv3.Add(&tv2, &tv4) // 5. tv3 = tv2 + 1 + tv3.Mul(&tv3, &sswuIsoCurveCoeffB) // 6. tv3 = B * tv3 tv2NZero := g2NotZero(&tv2) @@ -178,48 +173,45 @@ func mapToCurve2(u *fp.Element) G2Affine { tv4 = fp.Element{14263791471689722215, 10958139817512614717, 646289283071182148, 16194112285086178910, 12391927829343171647, 3698619178316197998, 14879001273850772332, 4646357410414107532, 14313982959885664825, 19561843432566578} tv2.Neg(&tv2) - tv4.Select(int(tv2NZero), &tv4, &tv2) - //TODO: When no isogeny use curve constants - tv2 = fp.Element{13503940466125084703, 3000707982748310797, 1529397070312683242, 9240962296298654443, 4577258595340312235, 16046828875439788343, 7236093083337192433, 2860564553402019540, 5160479239841632821, 65394042426465165} - tv4.Mul(&tv4, &tv2) + tv4.Select(int(tv2NZero), &tv4, &tv2) // 7. tv4 = CMOV(Z, -tv2, tv2 != 0) + tv4.Mul(&tv4, &sswuIsoCurveCoeffA) // 8. tv4 = A * tv4 - tv2.Square(&tv3) + tv2.Square(&tv3) // 9. tv2 = tv3² var tv6 fp.Element - //Standard doc line 10 - tv6.Square(&tv4) + tv6.Square(&tv4) // 10. tv6 = tv4² var tv5 fp.Element - tv5.Mul(&tv6, &fp.Element{13503940466125084703, 3000707982748310797, 1529397070312683242, 9240962296298654443, 4577258595340312235, 16046828875439788343, 7236093083337192433, 2860564553402019540, 5160479239841632821, 65394042426465165}) + tv5.Mul(&tv6, &sswuIsoCurveCoeffA) // 11. tv5 = A * tv6 - tv2.Add(&tv2, &tv5) - tv2.Mul(&tv2, &tv3) - tv6.Mul(&tv6, &tv4) + tv2.Add(&tv2, &tv5) // 12. tv2 = tv2 + tv5 + tv2.Mul(&tv2, &tv3) // 13. tv2 = tv2 * tv3 + tv6.Mul(&tv6, &tv4) // 14. tv6 = tv6 * tv4 - //Standards doc line 15 - tv5.Mul(&tv6, &fp.Element{4170590011558214244, 9101648159034903675, 4256739633972552875, 7483080556638609334, 12430228215152656439, 9977400640742476476, 15847011074743951739, 17768582661138350292, 10869631430819016060, 64187107279947172}) - tv2.Add(&tv2, &tv5) + tv5.Mul(&tv6, &sswuIsoCurveCoeffB) // 15. tv5 = B * tv6 + tv2.Add(&tv2, &tv5) // 16. tv2 = tv2 + tv5 var x fp.Element - x.Mul(&tv1, &tv3) + x.Mul(&tv1, &tv3) // 17. x = tv1 * tv3 var y1 fp.Element - gx1NSquare := g2SqrtRatio(&y1, &tv2, &tv6) + gx1NSquare := g2SqrtRatio(&y1, &tv2, &tv6) // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6) var y fp.Element - y.Mul(&tv1, u) + y.Mul(&tv1, u) // 19. y = tv1 * u - //Standards doc line 20 - y.Mul(&y, &y1) + y.Mul(&y, &y1) // 20. y = y * y1 - x.Select(int(gx1NSquare), &tv3, &x) - y.Select(int(gx1NSquare), &y1, &y) + x.Select(int(gx1NSquare), &tv3, &x) // 21. x = CMOV(x, tv3, is_gx1_square) + y.Select(int(gx1NSquare), &y1, &y) // 22. y = CMOV(y, y1, is_gx1_square) y1.Neg(&y) y.Select(int(g2Sgn0(u)^g2Sgn0(&y)), &y, &y1) - //Standards doc line 25 - x.Div(&x, &tv4) + // 23. e1 = sgn0(u) == sgn0(y) + // 24. y = CMOV(-y, y, e1) + + x.Div(&x, &tv4) // 25. x = x / tv4 return G2Affine{x, y} } @@ -241,13 +233,13 @@ func g2EvalPolynomial(z *fp.Element, monic bool, coefficients []fp.Element, x *f // g2Sgn0 is an algebraic substitute for the notion of sign in ordered fields // Namely, every non-zero quadratic residue in a finite field of characteristic =/= 2 has exactly two square roots, one of each sign -// Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/ section 4.1 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-the-sgn0-function // The sign of an element is not obviously related to that of its Montgomery form func g2Sgn0(z *fp.Element) uint64 { nonMont := *z nonMont.FromMont() - + // m == 1 return nonMont[0] % 2 } @@ -264,7 +256,7 @@ func MapToG2(u fp.Element) G2Affine { // EncodeToG2 hashes a message to a point on the G2 curve using the SSWU map. // It is faster than HashToG2, but the result is not uniformly distributed. Unsuitable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -//https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/#section-6.6.3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func EncodeToG2(msg, dst []byte) (G2Affine, error) { var res G2Affine @@ -284,7 +276,7 @@ func EncodeToG2(msg, dst []byte) (G2Affine, error) { // HashToG2 hashes a message to a point on the G2 curve using the SSWU map. // Slower than EncodeToG2, but usable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func HashToG2(msg, dst []byte) (G2Affine, error) { u, err := hashToFp(msg, dst, 2*1) if err != nil { @@ -294,7 +286,7 @@ func HashToG2(msg, dst []byte) (G2Affine, error) { Q0 := mapToCurve2(&u[0]) Q1 := mapToCurve2(&u[1]) - //TODO: Add in E' first, then apply isogeny + //TODO (perf): Add in E' first, then apply isogeny g2Isogeny(&Q0) g2Isogeny(&Q1) diff --git a/ecc/bw6-756/hash_to_g1.go b/ecc/bw6-756/hash_to_g1.go index ee2bc8ff5a..03ee750cf2 100644 --- a/ecc/bw6-756/hash_to_g1.go +++ b/ecc/bw6-756/hash_to_g1.go @@ -89,65 +89,58 @@ func g1Isogeny(p *G1Affine) { // g1SqrtRatio computes the square root of u/v and returns 0 iff u/v was indeed a quadratic residue // if not, we get sqrt(Z * u / v). Recall that Z is non-residue +// If v = 0, u/v is meaningless and the output is unspecified, without raising an error. // The main idea is that since the computation of the square root involves taking large powers of u/v, the inversion of v can be avoided func g1SqrtRatio(z *fp.Element, u *fp.Element, v *fp.Element) uint64 { - // Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ F.2.1.1. for any field + // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-sqrt_ratio-for-any-field tv1 := fp.Element{17302715199413996045, 15077845457253267709, 8842885729139027579, 12189878420705505575, 12380986790262239346, 585111498723936856, 4947215576903759546, 1186632482028566920, 14543050817583235372, 5644943604719368358, 9440830989708189862, 1039766423535362} //tv1 = c6 var tv2, tv3, tv4, tv5 fp.Element var exp big.Int - // c4 = 4835703278458516698824703 = 2^82 - 1 + // c4 = 4835703278458516698824703 = 2⁸² - 1 // q is odd so c1 is at least 1. exp.SetBytes([]byte{3, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}) - tv2.Exp(*v, &exp) - tv3.Mul(&tv2, &tv2) - tv3.Mul(&tv3, v) - - // line 5 - tv5.Mul(u, &tv3) + tv2.Exp(*v, &exp) // 2. tv2 = vᶜ⁴ + tv3.Square(&tv2) // 3. tv3 = tv2² + tv3.Mul(&tv3, v) // 4. tv3 = tv3 * v + tv5.Mul(u, &tv3) // 5. tv5 = u * tv3 // c3 = 37877157660731232732990269576663233239936484746509109593426423261538632780449313352717366389444912082695314931794809746268936574949192324351273838279701014606648452884726586254167471840902479876056412368 exp.SetBytes([]byte{1, 238, 213, 183, 107, 119, 49, 92, 85, 130, 79, 195, 198, 173, 25, 235, 146, 241, 154, 95, 88, 89, 209, 63, 126, 70, 68, 40, 170, 44, 116, 217, 152, 213, 206, 120, 133, 72, 219, 61, 96, 89, 2, 93, 64, 159, 85, 65, 79, 214, 57, 103, 160, 220, 200, 220, 82, 89, 162, 189, 182, 200, 212, 168, 96, 85, 71, 132, 177, 188, 251, 218, 22, 208, 189, 13, 10, 73, 216, 6, 120, 252, 199, 240, 208}) - tv5.Exp(tv5, &exp) - tv5.Mul(&tv5, &tv2) - tv2.Mul(&tv5, v) - tv3.Mul(&tv5, u) - // line 10 - tv4.Mul(&tv3, &tv2) + tv5.Exp(tv5, &exp) // 6. tv5 = tv5ᶜ³ + tv5.Mul(&tv5, &tv2) // 7. tv5 = tv5 * tv2 + tv2.Mul(&tv5, v) // 8. tv2 = tv5 * v + tv3.Mul(&tv5, u) // 9. tv3 = tv5 * u + tv4.Mul(&tv3, &tv2) // 10. tv4 = tv3 * tv2 // c5 = 2417851639229258349412352 exp.SetBytes([]byte{2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) - tv5.Exp(tv4, &exp) - - isQNr := g1NotOne(&tv5) - - tv2.Mul(&tv3, &fp.Element{13990906742184113945, 15879050380504523621, 13768460034940508157, 12337541071329853620, 6296858130192020747, 9289986178217863086, 18403114759403589657, 4546259071787184045, 5504643400205978814, 13830311104669138548, 96107744534255859, 1024735223965534}) - tv5.Mul(&tv4, &tv1) - - // line 15 - - tv3.Select(int(isQNr), &tv3, &tv2) - tv4.Select(int(isQNr), &tv4, &tv5) - - exp.Lsh(big.NewInt(1), 82-2) - - for i := 82; i >= 2; i-- { - //line 20 - tv5.Exp(tv4, &exp) - nE1 := g1NotOne(&tv5) - - tv2.Mul(&tv3, &tv1) - tv1.Mul(&tv1, &tv1) - tv5.Mul(&tv4, &tv1) - - tv3.Select(int(nE1), &tv3, &tv2) - tv4.Select(int(nE1), &tv4, &tv5) - - exp.Rsh(&exp, 1) + tv5.Exp(tv4, &exp) // 11. tv5 = tv4ᶜ⁵ + isQNr := g1NotOne(&tv5) // 12. isQR = tv5 == 1 + c7 := fp.Element{13990906742184113945, 15879050380504523621, 13768460034940508157, 12337541071329853620, 6296858130192020747, 9289986178217863086, 18403114759403589657, 4546259071787184045, 5504643400205978814, 13830311104669138548, 96107744534255859, 1024735223965534} + tv2.Mul(&tv3, &c7) // 13. tv2 = tv3 * c7 + tv5.Mul(&tv4, &tv1) // 14. tv5 = tv4 * tv1 + tv3.Select(int(isQNr), &tv3, &tv2) // 15. tv3 = CMOV(tv2, tv3, isQR) + tv4.Select(int(isQNr), &tv4, &tv5) // 16. tv4 = CMOV(tv5, tv4, isQR) + exp.Lsh(big.NewInt(1), 82-2) // 18, 19: tv5 = 2ⁱ⁻² for i = c1 + + for i := 82; i >= 2; i-- { // 17. for i in (c1, c1 - 1, ..., 2): + + tv5.Exp(tv4, &exp) // 20. tv5 = tv4ᵗᵛ⁵ + nE1 := g1NotOne(&tv5) // 21. e1 = tv5 == 1 + tv2.Mul(&tv3, &tv1) // 22. tv2 = tv3 * tv1 + tv1.Mul(&tv1, &tv1) // 23. tv1 = tv1 * tv1 Why not write square? + tv5.Mul(&tv4, &tv1) // 24. tv5 = tv4 * tv1 + tv3.Select(int(nE1), &tv3, &tv2) // 25. tv3 = CMOV(tv2, tv3, e1) + tv4.Select(int(nE1), &tv4, &tv5) // 26. tv4 = CMOV(tv5, tv4, e1) + + if i > 2 { + exp.Rsh(&exp, 1) // 18, 19. tv5 = 2ⁱ⁻² + } } *z = tv3 @@ -175,30 +168,29 @@ func g1MulByZ(z *fp.Element, x *fp.Element) { *z = res } -//TODO: Define A,B here - -// From https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ Pg 80 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-simplified-swu-method // mapToCurve1 implements the SSWU map // No cofactor clearing or isogeny func mapToCurve1(u *fp.Element) G1Affine { + var sswuIsoCurveCoeffA = fp.Element{6087387690755251612, 7643068232434215576, 6195945763281467660, 97569654519975969, 1505434147110560758, 12342644747290341982, 14059794106692380317, 15229664573794943703, 16908793757593141664, 1949816925291208189, 9451095697369482684, 234190359239853} + var sswuIsoCurveCoeffB = fp.Element{18446744073709458379, 881299893533802495, 4886355625346099349, 6225448195760991771, 6629400315996169345, 12607886696045185322, 7201730065066775519, 1932403901886200506, 8616600553259348813, 6369175937589644082, 7499857803942196586, 3773119276850162} + var tv1 fp.Element - tv1.Square(u) + tv1.Square(u) // 1. tv1 = u² //mul tv1 by Z - g1MulByZ(&tv1, &tv1) + g1MulByZ(&tv1, &tv1) // 2. tv1 = Z * tv1 var tv2 fp.Element - tv2.Square(&tv1) - tv2.Add(&tv2, &tv1) + tv2.Square(&tv1) // 3. tv2 = tv1² + tv2.Add(&tv2, &tv1) // 4. tv2 = tv2 + tv1 var tv3 fp.Element - //Standard doc line 5 var tv4 fp.Element tv4.SetOne() - tv3.Add(&tv2, &tv4) - //TODO: Use bCurveConf when no isogeny - tv3.Mul(&tv3, &fp.Element{18446744073709458379, 881299893533802495, 4886355625346099349, 6225448195760991771, 6629400315996169345, 12607886696045185322, 7201730065066775519, 1932403901886200506, 8616600553259348813, 6369175937589644082, 7499857803942196586, 3773119276850162}) + tv3.Add(&tv2, &tv4) // 5. tv3 = tv2 + 1 + tv3.Mul(&tv3, &sswuIsoCurveCoeffB) // 6. tv3 = B * tv3 tv2NZero := g1NotZero(&tv2) @@ -206,48 +198,45 @@ func mapToCurve1(u *fp.Element) G1Affine { tv4 = fp.Element{18446744073709504998, 11529623972028612607, 739483395258014634, 5527028560780200701, 11477868704616895891, 15905434021829949368, 2844651761892435780, 17567410508478669002, 4162242322955979641, 15743938111024983262, 11916654042695069468, 4062866236140222} tv2.Neg(&tv2) - tv4.Select(int(tv2NZero), &tv4, &tv2) - //TODO: When no isogeny use curve constants - tv2 = fp.Element{6087387690755251612, 7643068232434215576, 6195945763281467660, 97569654519975969, 1505434147110560758, 12342644747290341982, 14059794106692380317, 15229664573794943703, 16908793757593141664, 1949816925291208189, 9451095697369482684, 234190359239853} - tv4.Mul(&tv4, &tv2) + tv4.Select(int(tv2NZero), &tv4, &tv2) // 7. tv4 = CMOV(Z, -tv2, tv2 != 0) + tv4.Mul(&tv4, &sswuIsoCurveCoeffA) // 8. tv4 = A * tv4 - tv2.Square(&tv3) + tv2.Square(&tv3) // 9. tv2 = tv3² var tv6 fp.Element - //Standard doc line 10 - tv6.Square(&tv4) + tv6.Square(&tv4) // 10. tv6 = tv4² var tv5 fp.Element - tv5.Mul(&tv6, &fp.Element{6087387690755251612, 7643068232434215576, 6195945763281467660, 97569654519975969, 1505434147110560758, 12342644747290341982, 14059794106692380317, 15229664573794943703, 16908793757593141664, 1949816925291208189, 9451095697369482684, 234190359239853}) + tv5.Mul(&tv6, &sswuIsoCurveCoeffA) // 11. tv5 = A * tv6 - tv2.Add(&tv2, &tv5) - tv2.Mul(&tv2, &tv3) - tv6.Mul(&tv6, &tv4) + tv2.Add(&tv2, &tv5) // 12. tv2 = tv2 + tv5 + tv2.Mul(&tv2, &tv3) // 13. tv2 = tv2 * tv3 + tv6.Mul(&tv6, &tv4) // 14. tv6 = tv6 * tv4 - //Standards doc line 15 - tv5.Mul(&tv6, &fp.Element{18446744073709458379, 881299893533802495, 4886355625346099349, 6225448195760991771, 6629400315996169345, 12607886696045185322, 7201730065066775519, 1932403901886200506, 8616600553259348813, 6369175937589644082, 7499857803942196586, 3773119276850162}) - tv2.Add(&tv2, &tv5) + tv5.Mul(&tv6, &sswuIsoCurveCoeffB) // 15. tv5 = B * tv6 + tv2.Add(&tv2, &tv5) // 16. tv2 = tv2 + tv5 var x fp.Element - x.Mul(&tv1, &tv3) + x.Mul(&tv1, &tv3) // 17. x = tv1 * tv3 var y1 fp.Element - gx1NSquare := g1SqrtRatio(&y1, &tv2, &tv6) + gx1NSquare := g1SqrtRatio(&y1, &tv2, &tv6) // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6) var y fp.Element - y.Mul(&tv1, u) + y.Mul(&tv1, u) // 19. y = tv1 * u - //Standards doc line 20 - y.Mul(&y, &y1) + y.Mul(&y, &y1) // 20. y = y * y1 - x.Select(int(gx1NSquare), &tv3, &x) - y.Select(int(gx1NSquare), &y1, &y) + x.Select(int(gx1NSquare), &tv3, &x) // 21. x = CMOV(x, tv3, is_gx1_square) + y.Select(int(gx1NSquare), &y1, &y) // 22. y = CMOV(y, y1, is_gx1_square) y1.Neg(&y) y.Select(int(g1Sgn0(u)^g1Sgn0(&y)), &y, &y1) - //Standards doc line 25 - x.Div(&x, &tv4) + // 23. e1 = sgn0(u) == sgn0(y) + // 24. y = CMOV(-y, y, e1) + + x.Div(&x, &tv4) // 25. x = x / tv4 return G1Affine{x, y} } @@ -290,13 +279,13 @@ func hashToFp(msg, dst []byte, count int) ([]fp.Element, error) { // g1Sgn0 is an algebraic substitute for the notion of sign in ordered fields // Namely, every non-zero quadratic residue in a finite field of characteristic =/= 2 has exactly two square roots, one of each sign -// Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/ section 4.1 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-the-sgn0-function // The sign of an element is not obviously related to that of its Montgomery form func g1Sgn0(z *fp.Element) uint64 { nonMont := *z nonMont.FromMont() - + // m == 1 return nonMont[0] % 2 } @@ -313,7 +302,7 @@ func MapToG1(u fp.Element) G1Affine { // EncodeToG1 hashes a message to a point on the G1 curve using the SSWU map. // It is faster than HashToG1, but the result is not uniformly distributed. Unsuitable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -//https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/#section-6.6.3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func EncodeToG1(msg, dst []byte) (G1Affine, error) { var res G1Affine @@ -333,7 +322,7 @@ func EncodeToG1(msg, dst []byte) (G1Affine, error) { // HashToG1 hashes a message to a point on the G1 curve using the SSWU map. // Slower than EncodeToG1, but usable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func HashToG1(msg, dst []byte) (G1Affine, error) { u, err := hashToFp(msg, dst, 2*1) if err != nil { @@ -343,7 +332,7 @@ func HashToG1(msg, dst []byte) (G1Affine, error) { Q0 := mapToCurve1(&u[0]) Q1 := mapToCurve1(&u[1]) - //TODO: Add in E' first, then apply isogeny + //TODO (perf): Add in E' first, then apply isogeny g1Isogeny(&Q0) g1Isogeny(&Q1) diff --git a/ecc/bw6-756/hash_to_g2.go b/ecc/bw6-756/hash_to_g2.go index cfb975c2af..d84ae9b50c 100644 --- a/ecc/bw6-756/hash_to_g2.go +++ b/ecc/bw6-756/hash_to_g2.go @@ -160,65 +160,58 @@ func g2Isogeny(p *G2Affine) { // g2SqrtRatio computes the square root of u/v and returns 0 iff u/v was indeed a quadratic residue // if not, we get sqrt(Z * u / v). Recall that Z is non-residue +// If v = 0, u/v is meaningless and the output is unspecified, without raising an error. // The main idea is that since the computation of the square root involves taking large powers of u/v, the inversion of v can be avoided func g2SqrtRatio(z *fp.Element, u *fp.Element, v *fp.Element) uint64 { - // Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ F.2.1.1. for any field + // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-sqrt_ratio-for-any-field tv1 := fp.Element{17302715199413996045, 15077845457253267709, 8842885729139027579, 12189878420705505575, 12380986790262239346, 585111498723936856, 4947215576903759546, 1186632482028566920, 14543050817583235372, 5644943604719368358, 9440830989708189862, 1039766423535362} //tv1 = c6 var tv2, tv3, tv4, tv5 fp.Element var exp big.Int - // c4 = 4835703278458516698824703 = 2^82 - 1 + // c4 = 4835703278458516698824703 = 2⁸² - 1 // q is odd so c1 is at least 1. exp.SetBytes([]byte{3, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}) - tv2.Exp(*v, &exp) - tv3.Mul(&tv2, &tv2) - tv3.Mul(&tv3, v) - - // line 5 - tv5.Mul(u, &tv3) + tv2.Exp(*v, &exp) // 2. tv2 = vᶜ⁴ + tv3.Square(&tv2) // 3. tv3 = tv2² + tv3.Mul(&tv3, v) // 4. tv3 = tv3 * v + tv5.Mul(u, &tv3) // 5. tv5 = u * tv3 // c3 = 37877157660731232732990269576663233239936484746509109593426423261538632780449313352717366389444912082695314931794809746268936574949192324351273838279701014606648452884726586254167471840902479876056412368 exp.SetBytes([]byte{1, 238, 213, 183, 107, 119, 49, 92, 85, 130, 79, 195, 198, 173, 25, 235, 146, 241, 154, 95, 88, 89, 209, 63, 126, 70, 68, 40, 170, 44, 116, 217, 152, 213, 206, 120, 133, 72, 219, 61, 96, 89, 2, 93, 64, 159, 85, 65, 79, 214, 57, 103, 160, 220, 200, 220, 82, 89, 162, 189, 182, 200, 212, 168, 96, 85, 71, 132, 177, 188, 251, 218, 22, 208, 189, 13, 10, 73, 216, 6, 120, 252, 199, 240, 208}) - tv5.Exp(tv5, &exp) - tv5.Mul(&tv5, &tv2) - tv2.Mul(&tv5, v) - tv3.Mul(&tv5, u) - // line 10 - tv4.Mul(&tv3, &tv2) + tv5.Exp(tv5, &exp) // 6. tv5 = tv5ᶜ³ + tv5.Mul(&tv5, &tv2) // 7. tv5 = tv5 * tv2 + tv2.Mul(&tv5, v) // 8. tv2 = tv5 * v + tv3.Mul(&tv5, u) // 9. tv3 = tv5 * u + tv4.Mul(&tv3, &tv2) // 10. tv4 = tv3 * tv2 // c5 = 2417851639229258349412352 exp.SetBytes([]byte{2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) - tv5.Exp(tv4, &exp) - - isQNr := g2NotOne(&tv5) - - tv2.Mul(&tv3, &fp.Element{13990906742184113945, 15879050380504523621, 13768460034940508157, 12337541071329853620, 6296858130192020747, 9289986178217863086, 18403114759403589657, 4546259071787184045, 5504643400205978814, 13830311104669138548, 96107744534255859, 1024735223965534}) - tv5.Mul(&tv4, &tv1) - - // line 15 - - tv3.Select(int(isQNr), &tv3, &tv2) - tv4.Select(int(isQNr), &tv4, &tv5) - - exp.Lsh(big.NewInt(1), 82-2) - - for i := 82; i >= 2; i-- { - //line 20 - tv5.Exp(tv4, &exp) - nE1 := g2NotOne(&tv5) - - tv2.Mul(&tv3, &tv1) - tv1.Mul(&tv1, &tv1) - tv5.Mul(&tv4, &tv1) - - tv3.Select(int(nE1), &tv3, &tv2) - tv4.Select(int(nE1), &tv4, &tv5) - - exp.Rsh(&exp, 1) + tv5.Exp(tv4, &exp) // 11. tv5 = tv4ᶜ⁵ + isQNr := g2NotOne(&tv5) // 12. isQR = tv5 == 1 + c7 := fp.Element{13990906742184113945, 15879050380504523621, 13768460034940508157, 12337541071329853620, 6296858130192020747, 9289986178217863086, 18403114759403589657, 4546259071787184045, 5504643400205978814, 13830311104669138548, 96107744534255859, 1024735223965534} + tv2.Mul(&tv3, &c7) // 13. tv2 = tv3 * c7 + tv5.Mul(&tv4, &tv1) // 14. tv5 = tv4 * tv1 + tv3.Select(int(isQNr), &tv3, &tv2) // 15. tv3 = CMOV(tv2, tv3, isQR) + tv4.Select(int(isQNr), &tv4, &tv5) // 16. tv4 = CMOV(tv5, tv4, isQR) + exp.Lsh(big.NewInt(1), 82-2) // 18, 19: tv5 = 2ⁱ⁻² for i = c1 + + for i := 82; i >= 2; i-- { // 17. for i in (c1, c1 - 1, ..., 2): + + tv5.Exp(tv4, &exp) // 20. tv5 = tv4ᵗᵛ⁵ + nE1 := g2NotOne(&tv5) // 21. e1 = tv5 == 1 + tv2.Mul(&tv3, &tv1) // 22. tv2 = tv3 * tv1 + tv1.Mul(&tv1, &tv1) // 23. tv1 = tv1 * tv1 Why not write square? + tv5.Mul(&tv4, &tv1) // 24. tv5 = tv4 * tv1 + tv3.Select(int(nE1), &tv3, &tv2) // 25. tv3 = CMOV(tv2, tv3, e1) + tv4.Select(int(nE1), &tv4, &tv5) // 26. tv4 = CMOV(tv5, tv4, e1) + + if i > 2 { + exp.Rsh(&exp, 1) // 18, 19. tv5 = 2ⁱ⁻² + } } *z = tv3 @@ -246,30 +239,29 @@ func g2MulByZ(z *fp.Element, x *fp.Element) { *z = res } -//TODO: Define A,B here - -// From https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ Pg 80 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-simplified-swu-method // mapToCurve2 implements the SSWU map // No cofactor clearing or isogeny func mapToCurve2(u *fp.Element) G2Affine { + var sswuIsoCurveCoeffA = fp.Element{11188695195863236139, 18339800635248689929, 13644954250665578253, 16122525194076552550, 1985822167495960177, 11021218035968661748, 12951199075167016614, 18080500199774882647, 3065668365127963650, 1810223365641727596, 18249180996905802984, 4351293214471385} + var sswuIsoCurveCoeffB = fp.Element{3597427888115195847, 8485485194496420669, 9451115945982544412, 10217463679676360079, 3023875305953960937, 5866766270380139867, 15059909646037855295, 1065687373540957157, 12978541562777068958, 18112033168403904062, 11632286302244735111, 1469792042332206} + var tv1 fp.Element - tv1.Square(u) + tv1.Square(u) // 1. tv1 = u² //mul tv1 by Z - g2MulByZ(&tv1, &tv1) + g2MulByZ(&tv1, &tv1) // 2. tv1 = Z * tv1 var tv2 fp.Element - tv2.Square(&tv1) - tv2.Add(&tv2, &tv1) + tv2.Square(&tv1) // 3. tv2 = tv1² + tv2.Add(&tv2, &tv1) // 4. tv2 = tv2 + tv1 var tv3 fp.Element - //Standard doc line 5 var tv4 fp.Element tv4.SetOne() - tv3.Add(&tv2, &tv4) - //TODO: Use bCurveConf when no isogeny - tv3.Mul(&tv3, &fp.Element{3597427888115195847, 8485485194496420669, 9451115945982544412, 10217463679676360079, 3023875305953960937, 5866766270380139867, 15059909646037855295, 1065687373540957157, 12978541562777068958, 18112033168403904062, 11632286302244735111, 1469792042332206}) + tv3.Add(&tv2, &tv4) // 5. tv3 = tv2 + 1 + tv3.Mul(&tv3, &sswuIsoCurveCoeffB) // 6. tv3 = B * tv3 tv2NZero := g2NotZero(&tv2) @@ -277,48 +269,45 @@ func mapToCurve2(u *fp.Element) G2Affine { tv4 = fp.Element{18446744073709504998, 11529623972028612607, 739483395258014634, 5527028560780200701, 11477868704616895891, 15905434021829949368, 2844651761892435780, 17567410508478669002, 4162242322955979641, 15743938111024983262, 11916654042695069468, 4062866236140222} tv2.Neg(&tv2) - tv4.Select(int(tv2NZero), &tv4, &tv2) - //TODO: When no isogeny use curve constants - tv2 = fp.Element{11188695195863236139, 18339800635248689929, 13644954250665578253, 16122525194076552550, 1985822167495960177, 11021218035968661748, 12951199075167016614, 18080500199774882647, 3065668365127963650, 1810223365641727596, 18249180996905802984, 4351293214471385} - tv4.Mul(&tv4, &tv2) + tv4.Select(int(tv2NZero), &tv4, &tv2) // 7. tv4 = CMOV(Z, -tv2, tv2 != 0) + tv4.Mul(&tv4, &sswuIsoCurveCoeffA) // 8. tv4 = A * tv4 - tv2.Square(&tv3) + tv2.Square(&tv3) // 9. tv2 = tv3² var tv6 fp.Element - //Standard doc line 10 - tv6.Square(&tv4) + tv6.Square(&tv4) // 10. tv6 = tv4² var tv5 fp.Element - tv5.Mul(&tv6, &fp.Element{11188695195863236139, 18339800635248689929, 13644954250665578253, 16122525194076552550, 1985822167495960177, 11021218035968661748, 12951199075167016614, 18080500199774882647, 3065668365127963650, 1810223365641727596, 18249180996905802984, 4351293214471385}) + tv5.Mul(&tv6, &sswuIsoCurveCoeffA) // 11. tv5 = A * tv6 - tv2.Add(&tv2, &tv5) - tv2.Mul(&tv2, &tv3) - tv6.Mul(&tv6, &tv4) + tv2.Add(&tv2, &tv5) // 12. tv2 = tv2 + tv5 + tv2.Mul(&tv2, &tv3) // 13. tv2 = tv2 * tv3 + tv6.Mul(&tv6, &tv4) // 14. tv6 = tv6 * tv4 - //Standards doc line 15 - tv5.Mul(&tv6, &fp.Element{3597427888115195847, 8485485194496420669, 9451115945982544412, 10217463679676360079, 3023875305953960937, 5866766270380139867, 15059909646037855295, 1065687373540957157, 12978541562777068958, 18112033168403904062, 11632286302244735111, 1469792042332206}) - tv2.Add(&tv2, &tv5) + tv5.Mul(&tv6, &sswuIsoCurveCoeffB) // 15. tv5 = B * tv6 + tv2.Add(&tv2, &tv5) // 16. tv2 = tv2 + tv5 var x fp.Element - x.Mul(&tv1, &tv3) + x.Mul(&tv1, &tv3) // 17. x = tv1 * tv3 var y1 fp.Element - gx1NSquare := g2SqrtRatio(&y1, &tv2, &tv6) + gx1NSquare := g2SqrtRatio(&y1, &tv2, &tv6) // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6) var y fp.Element - y.Mul(&tv1, u) + y.Mul(&tv1, u) // 19. y = tv1 * u - //Standards doc line 20 - y.Mul(&y, &y1) + y.Mul(&y, &y1) // 20. y = y * y1 - x.Select(int(gx1NSquare), &tv3, &x) - y.Select(int(gx1NSquare), &y1, &y) + x.Select(int(gx1NSquare), &tv3, &x) // 21. x = CMOV(x, tv3, is_gx1_square) + y.Select(int(gx1NSquare), &y1, &y) // 22. y = CMOV(y, y1, is_gx1_square) y1.Neg(&y) y.Select(int(g2Sgn0(u)^g2Sgn0(&y)), &y, &y1) - //Standards doc line 25 - x.Div(&x, &tv4) + // 23. e1 = sgn0(u) == sgn0(y) + // 24. y = CMOV(-y, y, e1) + + x.Div(&x, &tv4) // 25. x = x / tv4 return G2Affine{x, y} } @@ -340,13 +329,13 @@ func g2EvalPolynomial(z *fp.Element, monic bool, coefficients []fp.Element, x *f // g2Sgn0 is an algebraic substitute for the notion of sign in ordered fields // Namely, every non-zero quadratic residue in a finite field of characteristic =/= 2 has exactly two square roots, one of each sign -// Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/ section 4.1 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-the-sgn0-function // The sign of an element is not obviously related to that of its Montgomery form func g2Sgn0(z *fp.Element) uint64 { nonMont := *z nonMont.FromMont() - + // m == 1 return nonMont[0] % 2 } @@ -363,7 +352,7 @@ func MapToG2(u fp.Element) G2Affine { // EncodeToG2 hashes a message to a point on the G2 curve using the SSWU map. // It is faster than HashToG2, but the result is not uniformly distributed. Unsuitable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -//https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/#section-6.6.3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func EncodeToG2(msg, dst []byte) (G2Affine, error) { var res G2Affine @@ -383,7 +372,7 @@ func EncodeToG2(msg, dst []byte) (G2Affine, error) { // HashToG2 hashes a message to a point on the G2 curve using the SSWU map. // Slower than EncodeToG2, but usable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func HashToG2(msg, dst []byte) (G2Affine, error) { u, err := hashToFp(msg, dst, 2*1) if err != nil { @@ -393,7 +382,7 @@ func HashToG2(msg, dst []byte) (G2Affine, error) { Q0 := mapToCurve2(&u[0]) Q1 := mapToCurve2(&u[1]) - //TODO: Add in E' first, then apply isogeny + //TODO (perf): Add in E' first, then apply isogeny g2Isogeny(&Q0) g2Isogeny(&Q1) diff --git a/ecc/bw6-761/hash_to_g1.go b/ecc/bw6-761/hash_to_g1.go index 9282d3fe31..716a516b96 100644 --- a/ecc/bw6-761/hash_to_g1.go +++ b/ecc/bw6-761/hash_to_g1.go @@ -89,34 +89,35 @@ func g1Isogeny(p *G1Affine) { // g1SqrtRatio computes the square root of u/v and returns 0 iff u/v was indeed a quadratic residue // if not, we get sqrt(Z * u / v). Recall that Z is non-residue +// If v = 0, u/v is meaningless and the output is unspecified, without raising an error. // The main idea is that since the computation of the square root involves taking large powers of u/v, the inversion of v can be avoided func g1SqrtRatio(z *fp.Element, u *fp.Element, v *fp.Element) uint64 { - // Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ F.2.1.2. q = 3 mod 4 + // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-optimized-sqrt_ratio-for-q- (3 mod 4) var tv1 fp.Element - tv1.Square(v) + tv1.Square(v) // 1. tv1 = v² var tv2 fp.Element - tv2.Mul(u, v) - tv1.Mul(&tv1, &tv2) + tv2.Mul(u, v) // 2. tv2 = u * v + tv1.Mul(&tv1, &tv2) // 3. tv1 = tv1 * tv2 var y1 fp.Element { var c1 big.Int // c1 = 1722862596078933134849197420568914385619917228134037527378447540052405855560872934021920795822352921910216141938446653362790439780138561939837377924781325399737901274844627212593135907855899198987974925107492278210691228279767074 - c1.SetBytes([]byte{72, 186, 9, 62, 224, 243, 130, 180, 97, 242, 80, 1, 62, 191, 207, 174, 73, 134, 26, 160, 116, 81, 162, 20, 160, 157, 123, 224, 33, 239, 144, 92, 30, 233, 142, 57, 97, 58, 70, 64, 243, 174, 191, 201, 109, 8, 193, 33, 162, 114, 59, 68, 190, 127, 100, 28, 119, 52, 247, 28, 250, 255, 203, 166, 40, 69, 176, 149, 153, 234, 62, 5, 131, 62, 43, 186, 188, 41, 13, 249, 164, 79, 154, 28, 0, 0, 32, 189, 39, 64, 0, 0, 0, 0, 34}) - y1.Exp(tv1, &c1) + c1.SetBytes([]byte{72, 186, 9, 62, 224, 243, 130, 180, 97, 242, 80, 1, 62, 191, 207, 174, 73, 134, 26, 160, 116, 81, 162, 20, 160, 157, 123, 224, 33, 239, 144, 92, 30, 233, 142, 57, 97, 58, 70, 64, 243, 174, 191, 201, 109, 8, 193, 33, 162, 114, 59, 68, 190, 127, 100, 28, 119, 52, 247, 28, 250, 255, 203, 166, 40, 69, 176, 149, 153, 234, 62, 5, 131, 62, 43, 186, 188, 41, 13, 249, 164, 79, 154, 28, 0, 0, 32, 189, 39, 64, 0, 0, 0, 0, 34}) // c1 = (q - 3) / 4 # Integer arithmetic + + y1.Exp(tv1, &c1) // 4. y1 = tv1ᶜ¹ } - y1.Mul(&y1, &tv2) + y1.Mul(&y1, &tv2) // 5. y1 = y1 * tv2 var y2 fp.Element - y2.Mul(&y1, &fp.Element{10289215067249928212, 13987875627487618797, 10154775028297877632, 5892581882377791321, 12835424790914788634, 14963278386355512102, 10283221901563449361, 9868336211540881409, 7345304935218488881, 6998778443322886180, 9453359982570584357, 56775348355244645}) - - var tv3 fp.Element - tv3.Square(&y1) - tv3.Mul(&tv3, v) - - isQNr := tv3.NotEqual(u) - z.Select(int(isQNr), &y1, &y2) + // c2 = sqrt(-Z) + tv3 := fp.Element{10289215067249928212, 13987875627487618797, 10154775028297877632, 5892581882377791321, 12835424790914788634, 14963278386355512102, 10283221901563449361, 9868336211540881409, 7345304935218488881, 6998778443322886180, 9453359982570584357, 56775348355244645} + y2.Mul(&y1, &tv3) // 6. y2 = y1 * c2 + tv3.Square(&y1) // 7. tv3 = y1² + tv3.Mul(&tv3, v) // 8. tv3 = tv3 * v + isQNr := tv3.NotEqual(u) // 9. isQR = tv3 == u + z.Select(int(isQNr), &y1, &y2) // 10. y = CMOV(y2, y1, isQR) return isQNr } @@ -130,30 +131,29 @@ func g1MulByZ(z *fp.Element, x *fp.Element) { *z = res } -//TODO: Define A,B here - -// From https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ Pg 80 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-simplified-swu-method // mapToCurve1 implements the SSWU map // No cofactor clearing or isogeny func mapToCurve1(u *fp.Element) G1Affine { + var sswuIsoCurveCoeffA = fp.Element{12169852093062392636, 3867460573998792965, 2540986171999662608, 3377838107874487171, 6313266756742099767, 5994530928773814047, 5007141583730923456, 2345996307867737670, 7096861766432061441, 10014420324597579745, 8416419844935780388, 63340978449966806} + var sswuIsoCurveCoeffB = fp.Element{9514135687797572479, 9972495974968977338, 17954535578332286571, 7437044986470910914, 13903267017721129281, 1871129682978723308, 13401268269932482209, 739043012311877982, 12116264695643437343, 1632209977726909861, 3621981106970059143, 65605772132525947} + var tv1 fp.Element - tv1.Square(u) + tv1.Square(u) // 1. tv1 = u² //mul tv1 by Z - g1MulByZ(&tv1, &tv1) + g1MulByZ(&tv1, &tv1) // 2. tv1 = Z * tv1 var tv2 fp.Element - tv2.Square(&tv1) - tv2.Add(&tv2, &tv1) + tv2.Square(&tv1) // 3. tv2 = tv1² + tv2.Add(&tv2, &tv1) // 4. tv2 = tv2 + tv1 var tv3 fp.Element - //Standard doc line 5 var tv4 fp.Element tv4.SetOne() - tv3.Add(&tv2, &tv4) - //TODO: Use bCurveConf when no isogeny - tv3.Mul(&tv3, &fp.Element{9514135687797572479, 9972495974968977338, 17954535578332286571, 7437044986470910914, 13903267017721129281, 1871129682978723308, 13401268269932482209, 739043012311877982, 12116264695643437343, 1632209977726909861, 3621981106970059143, 65605772132525947}) + tv3.Add(&tv2, &tv4) // 5. tv3 = tv2 + 1 + tv3.Mul(&tv3, &sswuIsoCurveCoeffB) // 6. tv3 = B * tv3 tv2NZero := g1NotZero(&tv2) @@ -161,48 +161,45 @@ func mapToCurve1(u *fp.Element) G1Affine { tv4 = fp.Element{289919226011913130, 13019990545710127566, 4409829457611675068, 13030600802816293865, 15696054586628993047, 9353078419867322391, 5664203968291172875, 5090703637405909511, 17774776443174359288, 10018561694451762270, 12632664537138156478, 46143195394855163} tv2.Neg(&tv2) - tv4.Select(int(tv2NZero), &tv4, &tv2) - //TODO: When no isogeny use curve constants - tv2 = fp.Element{12169852093062392636, 3867460573998792965, 2540986171999662608, 3377838107874487171, 6313266756742099767, 5994530928773814047, 5007141583730923456, 2345996307867737670, 7096861766432061441, 10014420324597579745, 8416419844935780388, 63340978449966806} - tv4.Mul(&tv4, &tv2) + tv4.Select(int(tv2NZero), &tv4, &tv2) // 7. tv4 = CMOV(Z, -tv2, tv2 != 0) + tv4.Mul(&tv4, &sswuIsoCurveCoeffA) // 8. tv4 = A * tv4 - tv2.Square(&tv3) + tv2.Square(&tv3) // 9. tv2 = tv3² var tv6 fp.Element - //Standard doc line 10 - tv6.Square(&tv4) + tv6.Square(&tv4) // 10. tv6 = tv4² var tv5 fp.Element - tv5.Mul(&tv6, &fp.Element{12169852093062392636, 3867460573998792965, 2540986171999662608, 3377838107874487171, 6313266756742099767, 5994530928773814047, 5007141583730923456, 2345996307867737670, 7096861766432061441, 10014420324597579745, 8416419844935780388, 63340978449966806}) + tv5.Mul(&tv6, &sswuIsoCurveCoeffA) // 11. tv5 = A * tv6 - tv2.Add(&tv2, &tv5) - tv2.Mul(&tv2, &tv3) - tv6.Mul(&tv6, &tv4) + tv2.Add(&tv2, &tv5) // 12. tv2 = tv2 + tv5 + tv2.Mul(&tv2, &tv3) // 13. tv2 = tv2 * tv3 + tv6.Mul(&tv6, &tv4) // 14. tv6 = tv6 * tv4 - //Standards doc line 15 - tv5.Mul(&tv6, &fp.Element{9514135687797572479, 9972495974968977338, 17954535578332286571, 7437044986470910914, 13903267017721129281, 1871129682978723308, 13401268269932482209, 739043012311877982, 12116264695643437343, 1632209977726909861, 3621981106970059143, 65605772132525947}) - tv2.Add(&tv2, &tv5) + tv5.Mul(&tv6, &sswuIsoCurveCoeffB) // 15. tv5 = B * tv6 + tv2.Add(&tv2, &tv5) // 16. tv2 = tv2 + tv5 var x fp.Element - x.Mul(&tv1, &tv3) + x.Mul(&tv1, &tv3) // 17. x = tv1 * tv3 var y1 fp.Element - gx1NSquare := g1SqrtRatio(&y1, &tv2, &tv6) + gx1NSquare := g1SqrtRatio(&y1, &tv2, &tv6) // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6) var y fp.Element - y.Mul(&tv1, u) + y.Mul(&tv1, u) // 19. y = tv1 * u - //Standards doc line 20 - y.Mul(&y, &y1) + y.Mul(&y, &y1) // 20. y = y * y1 - x.Select(int(gx1NSquare), &tv3, &x) - y.Select(int(gx1NSquare), &y1, &y) + x.Select(int(gx1NSquare), &tv3, &x) // 21. x = CMOV(x, tv3, is_gx1_square) + y.Select(int(gx1NSquare), &y1, &y) // 22. y = CMOV(y, y1, is_gx1_square) y1.Neg(&y) y.Select(int(g1Sgn0(u)^g1Sgn0(&y)), &y, &y1) - //Standards doc line 25 - x.Div(&x, &tv4) + // 23. e1 = sgn0(u) == sgn0(y) + // 24. y = CMOV(-y, y, e1) + + x.Div(&x, &tv4) // 25. x = x / tv4 return G1Affine{x, y} } @@ -245,13 +242,13 @@ func hashToFp(msg, dst []byte, count int) ([]fp.Element, error) { // g1Sgn0 is an algebraic substitute for the notion of sign in ordered fields // Namely, every non-zero quadratic residue in a finite field of characteristic =/= 2 has exactly two square roots, one of each sign -// Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/ section 4.1 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-the-sgn0-function // The sign of an element is not obviously related to that of its Montgomery form func g1Sgn0(z *fp.Element) uint64 { nonMont := *z nonMont.FromMont() - + // m == 1 return nonMont[0] % 2 } @@ -268,7 +265,7 @@ func MapToG1(u fp.Element) G1Affine { // EncodeToG1 hashes a message to a point on the G1 curve using the SSWU map. // It is faster than HashToG1, but the result is not uniformly distributed. Unsuitable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -//https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/#section-6.6.3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func EncodeToG1(msg, dst []byte) (G1Affine, error) { var res G1Affine @@ -288,7 +285,7 @@ func EncodeToG1(msg, dst []byte) (G1Affine, error) { // HashToG1 hashes a message to a point on the G1 curve using the SSWU map. // Slower than EncodeToG1, but usable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func HashToG1(msg, dst []byte) (G1Affine, error) { u, err := hashToFp(msg, dst, 2*1) if err != nil { @@ -298,7 +295,7 @@ func HashToG1(msg, dst []byte) (G1Affine, error) { Q0 := mapToCurve1(&u[0]) Q1 := mapToCurve1(&u[1]) - //TODO: Add in E' first, then apply isogeny + //TODO (perf): Add in E' first, then apply isogeny g1Isogeny(&Q0) g1Isogeny(&Q1) diff --git a/ecc/bw6-761/hash_to_g2.go b/ecc/bw6-761/hash_to_g2.go index 37bdede8f1..bacb36d661 100644 --- a/ecc/bw6-761/hash_to_g2.go +++ b/ecc/bw6-761/hash_to_g2.go @@ -260,34 +260,35 @@ func g2Isogeny(p *G2Affine) { // g2SqrtRatio computes the square root of u/v and returns 0 iff u/v was indeed a quadratic residue // if not, we get sqrt(Z * u / v). Recall that Z is non-residue +// If v = 0, u/v is meaningless and the output is unspecified, without raising an error. // The main idea is that since the computation of the square root involves taking large powers of u/v, the inversion of v can be avoided func g2SqrtRatio(z *fp.Element, u *fp.Element, v *fp.Element) uint64 { - // Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ F.2.1.2. q = 3 mod 4 + // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-optimized-sqrt_ratio-for-q- (3 mod 4) var tv1 fp.Element - tv1.Square(v) + tv1.Square(v) // 1. tv1 = v² var tv2 fp.Element - tv2.Mul(u, v) - tv1.Mul(&tv1, &tv2) + tv2.Mul(u, v) // 2. tv2 = u * v + tv1.Mul(&tv1, &tv2) // 3. tv1 = tv1 * tv2 var y1 fp.Element { var c1 big.Int // c1 = 1722862596078933134849197420568914385619917228134037527378447540052405855560872934021920795822352921910216141938446653362790439780138561939837377924781325399737901274844627212593135907855899198987974925107492278210691228279767074 - c1.SetBytes([]byte{72, 186, 9, 62, 224, 243, 130, 180, 97, 242, 80, 1, 62, 191, 207, 174, 73, 134, 26, 160, 116, 81, 162, 20, 160, 157, 123, 224, 33, 239, 144, 92, 30, 233, 142, 57, 97, 58, 70, 64, 243, 174, 191, 201, 109, 8, 193, 33, 162, 114, 59, 68, 190, 127, 100, 28, 119, 52, 247, 28, 250, 255, 203, 166, 40, 69, 176, 149, 153, 234, 62, 5, 131, 62, 43, 186, 188, 41, 13, 249, 164, 79, 154, 28, 0, 0, 32, 189, 39, 64, 0, 0, 0, 0, 34}) - y1.Exp(tv1, &c1) + c1.SetBytes([]byte{72, 186, 9, 62, 224, 243, 130, 180, 97, 242, 80, 1, 62, 191, 207, 174, 73, 134, 26, 160, 116, 81, 162, 20, 160, 157, 123, 224, 33, 239, 144, 92, 30, 233, 142, 57, 97, 58, 70, 64, 243, 174, 191, 201, 109, 8, 193, 33, 162, 114, 59, 68, 190, 127, 100, 28, 119, 52, 247, 28, 250, 255, 203, 166, 40, 69, 176, 149, 153, 234, 62, 5, 131, 62, 43, 186, 188, 41, 13, 249, 164, 79, 154, 28, 0, 0, 32, 189, 39, 64, 0, 0, 0, 0, 34}) // c1 = (q - 3) / 4 # Integer arithmetic + + y1.Exp(tv1, &c1) // 4. y1 = tv1ᶜ¹ } - y1.Mul(&y1, &tv2) + y1.Mul(&y1, &tv2) // 5. y1 = y1 * tv2 var y2 fp.Element - y2.Mul(&y1, &fp.Element{10751254254539175147, 9766094991671066833, 2332062865126794245, 278658983607570155, 16958907618386923993, 3828006658569666757, 13622760089646566549, 10723972911493525927, 12307000153990793747, 14543371959135298146, 6497680552248674778, 41056765998425020}) - - var tv3 fp.Element - tv3.Square(&y1) - tv3.Mul(&tv3, v) - - isQNr := tv3.NotEqual(u) - z.Select(int(isQNr), &y1, &y2) + // c2 = sqrt(-Z) + tv3 := fp.Element{10751254254539175147, 9766094991671066833, 2332062865126794245, 278658983607570155, 16958907618386923993, 3828006658569666757, 13622760089646566549, 10723972911493525927, 12307000153990793747, 14543371959135298146, 6497680552248674778, 41056765998425020} + y2.Mul(&y1, &tv3) // 6. y2 = y1 * c2 + tv3.Square(&y1) // 7. tv3 = y1² + tv3.Mul(&tv3, v) // 8. tv3 = tv3 * v + isQNr := tv3.NotEqual(u) // 9. isQR = tv3 == u + z.Select(int(isQNr), &y1, &y2) // 10. y = CMOV(y2, y1, isQR) return isQNr } @@ -305,30 +306,29 @@ func g2MulByZ(z *fp.Element, x *fp.Element) { *z = res } -//TODO: Define A,B here - -// From https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ Pg 80 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-simplified-swu-method // mapToCurve2 implements the SSWU map // No cofactor clearing or isogeny func mapToCurve2(u *fp.Element) G2Affine { + var sswuIsoCurveCoeffA = fp.Element{13704010396169241312, 14330175345318364589, 4449492585807198633, 9884564993510771995, 16507506367033405761, 12171409358426895620, 3759742122315801393, 6972450370136308820, 13649992927502603798, 15742083997009939515, 4062268800652448528, 42571325818609943} + var sswuIsoCurveCoeffB = fp.Element{17251063859315847117, 13422534455279952781, 15626212001505409941, 8548929388122544483, 12216093319907597521, 15761783579263790289, 10925761432004348632, 8228665107915194054, 13147767302058909808, 5735540302608306489, 5152863309501448410, 45595036249636616} + var tv1 fp.Element - tv1.Square(u) + tv1.Square(u) // 1. tv1 = u² //mul tv1 by Z - g2MulByZ(&tv1, &tv1) + g2MulByZ(&tv1, &tv1) // 2. tv1 = Z * tv1 var tv2 fp.Element - tv2.Square(&tv1) - tv2.Add(&tv2, &tv1) + tv2.Square(&tv1) // 3. tv2 = tv1² + tv2.Add(&tv2, &tv1) // 4. tv2 = tv2 + tv1 var tv3 fp.Element - //Standard doc line 5 var tv4 fp.Element tv4.SetOne() - tv3.Add(&tv2, &tv4) - //TODO: Use bCurveConf when no isogeny - tv3.Mul(&tv3, &fp.Element{17251063859315847117, 13422534455279952781, 15626212001505409941, 8548929388122544483, 12216093319907597521, 15761783579263790289, 10925761432004348632, 8228665107915194054, 13147767302058909808, 5735540302608306489, 5152863309501448410, 45595036249636616}) + tv3.Add(&tv2, &tv4) // 5. tv3 = tv2 + 1 + tv3.Mul(&tv3, &sswuIsoCurveCoeffB) // 6. tv3 = B * tv3 tv2NZero := g2NotZero(&tv2) @@ -336,48 +336,45 @@ func mapToCurve2(u *fp.Element) G2Affine { tv4 = fp.Element{4056054414400208518, 3320816571827031140, 10263935383895698150, 11003897938091601562, 15597443347325643510, 13135057492086854609, 2659919018052618801, 3683105852685266909, 6137961753831301777, 15077955943918945393, 14961510259660508891, 8138608324875079} tv2.Neg(&tv2) - tv4.Select(int(tv2NZero), &tv4, &tv2) - //TODO: When no isogeny use curve constants - tv2 = fp.Element{13704010396169241312, 14330175345318364589, 4449492585807198633, 9884564993510771995, 16507506367033405761, 12171409358426895620, 3759742122315801393, 6972450370136308820, 13649992927502603798, 15742083997009939515, 4062268800652448528, 42571325818609943} - tv4.Mul(&tv4, &tv2) + tv4.Select(int(tv2NZero), &tv4, &tv2) // 7. tv4 = CMOV(Z, -tv2, tv2 != 0) + tv4.Mul(&tv4, &sswuIsoCurveCoeffA) // 8. tv4 = A * tv4 - tv2.Square(&tv3) + tv2.Square(&tv3) // 9. tv2 = tv3² var tv6 fp.Element - //Standard doc line 10 - tv6.Square(&tv4) + tv6.Square(&tv4) // 10. tv6 = tv4² var tv5 fp.Element - tv5.Mul(&tv6, &fp.Element{13704010396169241312, 14330175345318364589, 4449492585807198633, 9884564993510771995, 16507506367033405761, 12171409358426895620, 3759742122315801393, 6972450370136308820, 13649992927502603798, 15742083997009939515, 4062268800652448528, 42571325818609943}) + tv5.Mul(&tv6, &sswuIsoCurveCoeffA) // 11. tv5 = A * tv6 - tv2.Add(&tv2, &tv5) - tv2.Mul(&tv2, &tv3) - tv6.Mul(&tv6, &tv4) + tv2.Add(&tv2, &tv5) // 12. tv2 = tv2 + tv5 + tv2.Mul(&tv2, &tv3) // 13. tv2 = tv2 * tv3 + tv6.Mul(&tv6, &tv4) // 14. tv6 = tv6 * tv4 - //Standards doc line 15 - tv5.Mul(&tv6, &fp.Element{17251063859315847117, 13422534455279952781, 15626212001505409941, 8548929388122544483, 12216093319907597521, 15761783579263790289, 10925761432004348632, 8228665107915194054, 13147767302058909808, 5735540302608306489, 5152863309501448410, 45595036249636616}) - tv2.Add(&tv2, &tv5) + tv5.Mul(&tv6, &sswuIsoCurveCoeffB) // 15. tv5 = B * tv6 + tv2.Add(&tv2, &tv5) // 16. tv2 = tv2 + tv5 var x fp.Element - x.Mul(&tv1, &tv3) + x.Mul(&tv1, &tv3) // 17. x = tv1 * tv3 var y1 fp.Element - gx1NSquare := g2SqrtRatio(&y1, &tv2, &tv6) + gx1NSquare := g2SqrtRatio(&y1, &tv2, &tv6) // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6) var y fp.Element - y.Mul(&tv1, u) + y.Mul(&tv1, u) // 19. y = tv1 * u - //Standards doc line 20 - y.Mul(&y, &y1) + y.Mul(&y, &y1) // 20. y = y * y1 - x.Select(int(gx1NSquare), &tv3, &x) - y.Select(int(gx1NSquare), &y1, &y) + x.Select(int(gx1NSquare), &tv3, &x) // 21. x = CMOV(x, tv3, is_gx1_square) + y.Select(int(gx1NSquare), &y1, &y) // 22. y = CMOV(y, y1, is_gx1_square) y1.Neg(&y) y.Select(int(g2Sgn0(u)^g2Sgn0(&y)), &y, &y1) - //Standards doc line 25 - x.Div(&x, &tv4) + // 23. e1 = sgn0(u) == sgn0(y) + // 24. y = CMOV(-y, y, e1) + + x.Div(&x, &tv4) // 25. x = x / tv4 return G2Affine{x, y} } @@ -399,13 +396,13 @@ func g2EvalPolynomial(z *fp.Element, monic bool, coefficients []fp.Element, x *f // g2Sgn0 is an algebraic substitute for the notion of sign in ordered fields // Namely, every non-zero quadratic residue in a finite field of characteristic =/= 2 has exactly two square roots, one of each sign -// Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/ section 4.1 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-the-sgn0-function // The sign of an element is not obviously related to that of its Montgomery form func g2Sgn0(z *fp.Element) uint64 { nonMont := *z nonMont.FromMont() - + // m == 1 return nonMont[0] % 2 } @@ -422,7 +419,7 @@ func MapToG2(u fp.Element) G2Affine { // EncodeToG2 hashes a message to a point on the G2 curve using the SSWU map. // It is faster than HashToG2, but the result is not uniformly distributed. Unsuitable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -//https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/#section-6.6.3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func EncodeToG2(msg, dst []byte) (G2Affine, error) { var res G2Affine @@ -442,7 +439,7 @@ func EncodeToG2(msg, dst []byte) (G2Affine, error) { // HashToG2 hashes a message to a point on the G2 curve using the SSWU map. // Slower than EncodeToG2, but usable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func HashToG2(msg, dst []byte) (G2Affine, error) { u, err := hashToFp(msg, dst, 2*1) if err != nil { @@ -452,7 +449,7 @@ func HashToG2(msg, dst []byte) (G2Affine, error) { Q0 := mapToCurve2(&u[0]) Q1 := mapToCurve2(&u[1]) - //TODO: Add in E' first, then apply isogeny + //TODO (perf): Add in E' first, then apply isogeny g2Isogeny(&Q0) g2Isogeny(&Q1) diff --git a/go.mod b/go.mod index e80ce91aae..583ea31428 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/consensys/gnark-crypto go 1.17 require ( - github.com/consensys/bavard v0.1.12 + github.com/consensys/bavard v0.1.13 github.com/leanovate/gopter v0.2.9 github.com/mmcloughlin/addchain v0.4.0 github.com/spf13/cobra v1.5.0 diff --git a/go.sum b/go.sum index 86c32f0518..24019a2212 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/consensys/bavard v0.1.12 h1:rApQlUvBg5FeW/fnigtVnAs0sBrgDN2pEuHNdWElSUE= github.com/consensys/bavard v0.1.12/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= diff --git a/internal/generator/ecc/template/hash_to_curve.go.tmpl b/internal/generator/ecc/template/hash_to_curve.go.tmpl index 7a6e87575f..d4e720bf57 100644 --- a/internal/generator/ecc/template/hash_to_curve.go.tmpl +++ b/internal/generator/ecc/template/hash_to_curve.go.tmpl @@ -50,28 +50,36 @@ func hashToFp(msg, dst []byte, count int) ([]fp.Element, error) { // {{$CurveName}}Sgn0 is an algebraic substitute for the notion of sign in ordered fields // Namely, every non-zero quadratic residue in a finite field of characteristic =/= 2 has exactly two square roots, one of each sign -// Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/ section 4.1 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-the-sgn0-function // The sign of an element is not obviously related to that of its Montgomery form func {{$CurveName}}Sgn0(z *{{$CoordType}}) uint64 { nonMont := *z nonMont.FromMont() - {{if eq $TowerDegree 1}} + {{if eq $TowerDegree 1}} // m == 1 return nonMont[0]%2 {{else}} - sign := uint64(0) - zero := uint64(1) + sign := uint64(0) // 1. sign = 0 + zero := uint64(1) // 2. zero = 1 var signI uint64 var zeroI uint64 - {{range $i := interval 0 $TowerDegree}} - signI = nonMont.{{$.FieldCoordName}}{{$i}}[0] % 2 - sign = sign | (zero & signI) - {{if not (eq $i (sub $TowerDegree 1))}} + {{ range $i := interval 0 $TowerDegree}} + // 3. i = {{add $i 1}} + signI = nonMont.{{$.FieldCoordName}}{{$i}}[0] % 2 // 4. sign_i = x_i mod 2 + {{- $notLast := not (eq $i (sub $TowerDegree 1))}} + {{- if $notLast}} zeroI = g1NotZero(&nonMont.{{$.FieldCoordName}}{{$i}}) - zeroI = 1 ^ (zeroI|-zeroI)>>63 - zero = zero & zeroI - {{end}} - {{end}} + zeroI = 1 ^ (zeroI|-zeroI)>>63 // 5. zero_i = x_i == 0 + {{- else}} + // 5. zero_i = x_i == 0 + {{- end}} + sign = sign | (zero & signI) // 6. sign = sign OR (zero AND sign_i) # Avoid short-circuit logic ops + {{- if $notLast}} + zero = zero & zeroI // 7. zero = zero AND zero_i + {{- else}} + // 7. zero = zero AND zero_i + {{- end}} + {{- end}} return sign {{end}} } @@ -93,7 +101,7 @@ func MapTo{{$CurveTitle}}(u {{$CoordType}}) {{$AffineType}} { // EncodeTo{{$CurveTitle}} hashes a message to a point on the {{$CurveTitle}} curve using the {{.MappingAlgorithm}} map. // It is faster than HashTo{{$CurveTitle}}, but the result is not uniformly distributed. Unsuitable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -//https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/#section-6.6.3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func EncodeTo{{$CurveTitle}}(msg, dst []byte) ({{$AffineType}}, error) { var res {{$AffineType}} @@ -123,7 +131,7 @@ func EncodeTo{{$CurveTitle}}(msg, dst []byte) ({{$AffineType}}, error) { // HashTo{{$CurveTitle}} hashes a message to a point on the {{$CurveTitle}} curve using the {{.MappingAlgorithm}} map. // Slower than EncodeTo{{$CurveTitle}}, but usable as a random oracle. // dst stands for "domain separation tag", a string unique to the construction using the hash function -// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-3 +//https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#roadmap func HashTo{{$CurveTitle}}(msg, dst []byte) ({{$AffineType}}, error) { u, err := hashToFp(msg, dst, 2 * {{$TowerDegree}}) if err != nil { @@ -143,7 +151,7 @@ func HashTo{{$CurveTitle}}(msg, dst []byte) ({{$AffineType}}, error) { {{end}} {{ if $isogenyNeeded }} - //TODO: Add in E' first, then apply isogeny + //TODO (perf): Add in E' first, then apply isogeny {{$CurveName}}Isogeny(&Q0) {{$CurveName}}Isogeny(&Q1) {{ end }} diff --git a/internal/generator/ecc/template/sswu.go.tmpl b/internal/generator/ecc/template/sswu.go.tmpl index 55d2fd151a..50afc2acf5 100644 --- a/internal/generator/ecc/template/sswu.go.tmpl +++ b/internal/generator/ecc/template/sswu.go.tmpl @@ -9,11 +9,10 @@ {{$AffineType := print $CurveTitle "Affine"}} {{$JacType := print $CurveTitle "Jac"}} {{$IsG1 := eq $CurveTitle "G1"}} -{{$CurveIndex := "2"}} -{{$package := "fptower"}} -{{if eq $TowerDegree 1}}{{$package = "fp"}}{{end}} -{{if $IsG1}}{{$CurveIndex = "1"}}{{end}} - +{{$CurveIndex := select $IsG1 "2" "1"}} +{{$package := select (eq $TowerDegree 1) "fptower" "fp"}} +{{$sswuCurveACoeff := select $isogenyNeeded "This is meant to produce an error. Since most likely A = 0, there is opportunity for optimizations that need to be looked at." "sswuIsoCurveCoeffA"}} +{{$sswuCurveBCoeff := select $isogenyNeeded "bCurveConf" "sswuIsoCurveCoeffB"}} //Note: This only works for simple extensions @@ -98,88 +97,84 @@ func {{$CurveName}}Isogeny(p *{{$AffineType}}) { // {{$CurveName}}SqrtRatio computes the square root of u/v and returns 0 iff u/v was indeed a quadratic residue // if not, we get sqrt(Z * u / v). Recall that Z is non-residue +// If v = 0, u/v is meaningless and the output is unspecified, without raising an error. // The main idea is that since the computation of the square root involves taking large powers of u/v, the inversion of v can be avoided func {{$CurveName}}SqrtRatio(z *{{$CoordType}}, u *{{$CoordType}}, v *{{$CoordType}}) uint64 { -{{ if eq (mod .FieldSizeMod256 4) 3 }} // Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ F.2.1.2. q = 3 mod 4 +{{ if eq (mod .FieldSizeMod256 4) 3 }} // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-optimized-sqrt_ratio-for-q- (3 mod 4) var tv1 {{$CoordType}} - tv1.Square(v) + tv1.Square(v) // 1. tv1 = v² var tv2 {{$CoordType}} - tv2.Mul(u, v) - tv1.Mul(&tv1, &tv2) + tv2.Mul(u, v) // 2. tv2 = u * v + tv1.Mul(&tv1, &tv2) // 3. tv1 = tv1 * tv2 var y1 {{$CoordType}} { var c1 big.Int // c1 = {{ $c1Int }} - c1.SetBytes([]byte{ {{ $c1IntBytes }} }) - y1.Exp(tv1, &c1) + c1.SetBytes([]byte{ {{ $c1IntBytes }} }) // c1 = (q - 3) / 4 # Integer arithmetic + + y1.Exp(tv1, &c1) // 4. y1 = tv1ᶜ¹ } - y1.Mul(&y1, &tv2) + y1.Mul(&y1, &tv2) // 5. y1 = y1 * tv2 var y2 {{$CoordType}} - y2.Mul(&y1, &{{$CoordType}} {{ asElement (index .PrecomputedParams 1)}}) - - var tv3 {{$CoordType}} - tv3.Square(&y1) - tv3.Mul(&tv3, v) - - isQNr := tv3.NotEqual(u) - z.Select(int(isQNr), &y1, &y2) + // c2 = sqrt(-Z) + tv3 := {{$CoordType}} {{ asElement (index .PrecomputedParams 1)}} + y2.Mul(&y1, &tv3) // 6. y2 = y1 * c2 + tv3.Square(&y1) // 7. tv3 = y1² + tv3.Mul(&tv3, v) // 8. tv3 = tv3 * v + isQNr := tv3.NotEqual(u) // 9. isQR = tv3 == u + z.Select(int(isQNr), &y1, &y2) // 10. y = CMOV(y2, y1, isQR) return isQNr } {{ end }} -{{ if eq (mod .FieldSizeMod256 8) 5 }} // Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ F.2.1.3. q = 5 mod 8 -// TODO: Test correct use of Element.Select +{{ if eq (mod .FieldSizeMod256 8) 5 }} // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-optimized-sqrt_ratio-for-q-5 (mod 8) var tv1, tv2 {{$CoordType}} - tv1.Square(v) - tv2.Mul(&tv1, v) - tv1.Square(&tv1) - tv2.Mul(&tv2, u) - tv1.Mul(&tv1, &tv2) + tv1.Square(v) // 1. tv1 = v² + tv2.Mul(&tv1, v) // 2. tv2 = tv1 * v + tv1.Square(&tv1) // 3. tv1 = tv1² + tv2.Mul(&tv2, u) // 4. tv2 = tv2 * u + tv1.Mul(&tv1, &tv2) // 5. tv1 = tv1 * tv2 var c1 big.Int - // c1 = {{ $c1Int }} + // c1 = (q - 5) / 8 = {{ $c1Int }} c1.SetBytes([]byte { {{ $c1IntBytes }} }) var y1 {{$CoordType}} - y1.Exp(tv1, &c1) - y1.Mul(&y1, &tv2) - tv1.Mul(&y1, &{{$CoordType}} {{asElement (index .PrecomputedParams 1)}} ) - tv2.Square(&tv1) - - //Line 10 in std doc - tv2.Mul(&tv2, v) - - y1.Select(int(tv2.NotEqual(u)), &tv1, &y1) - - tv2.Square(&y1) - tv2.Mul(&tv2, v) - - //Line 15 - isQNr := tv2.NotEqual(u) + y1.Exp(tv1, &c1) // 6. y1 = tv1ᶜ¹ + y1.Mul(&y1, &tv2) // 7. y1 = y1 * tv2 + // c2 = sqrt(-1) + c2 := {{$CoordType}} {{asElement (index .PrecomputedParams 1)}} + tv1.Mul(&y1, &c2) // 8. tv1 = y1 * c2 + tv2.Square(&tv1) // 9. tv2 = tv1² + tv2.Mul(&tv2, v) // 10. tv2 = tv2 * v + // 11. e1 = tv2 == u + y1.Select(int(tv2.NotEqual(u)), &tv1, &y1) // 12. y1 = CMOV(y1, tv1, e1) + tv2.Square(&y1) // 13. tv2 = y1² + tv2.Mul(&tv2, v) // 14. tv2 = tv2 * v + isQNr := tv2.NotEqual(u) // 15. isQR = tv2 == u var y2 {{$CoordType}} - y2.Mul(&y1, &{{$CoordType}} {{asElement (index .PrecomputedParams 2)}} ) - tv1.Mul(&y2, &{{$CoordType}} {{asElement (index .PrecomputedParams 1)}}) - tv2.Square(&tv1) - tv2.Mul(&tv2, v) - + // c3 = sqrt(Z / c2) + y2 = {{$CoordType}} {{asElement (index .PrecomputedParams 2)}} + y2.Mul(&y1, &y2) // 16. y2 = y1 * c3 + tv1.Mul(&y2, &c2) // 17. tv1 = y2 * c2 + tv2.Square(&tv1) // 18. tv2 = tv1² + tv2.Mul(&tv2, v) // 19. tv2 = tv2 * v var tv3 {{$CoordType}} - //Line 20 // Z = [{{printList .Z}}] - {{$CurveName}}MulByZ(&tv3, u) - - y2.Select(int(tv2.NotEqual(&tv3)), &tv1, &y2) - - z.Select(int(isQNr), &y1, &y2) + {{$CurveName}}MulByZ(&tv3, u) // 20. tv3 = Z * u + // 21. e2 = tv2 == tv3 + y2.Select(int(tv2.NotEqual(&tv3)), &tv1, &y2) // 22. y2 = CMOV(y2, tv1, e2) + z.Select(int(isQNr), &y1, &y2) // 23. y = CMOV(y2, y1, isQR) return isQNr } {{ end }} -{{ if eq (mod .FieldSizeMod256 8) 1 }}// Taken from https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ F.2.1.1. for any field +{{ if eq (mod .FieldSizeMod256 8) 1 }}// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-sqrt_ratio-for-any-field {{ $c2Int := index $cInts 1}} {{ $c2IntBytes := printList (bytes $c2Int ) }} @@ -195,58 +190,50 @@ func {{$CurveName}}SqrtRatio(z *{{$CoordType}}, u *{{$CoordType}}, v *{{$CoordTy tv1 := {{$CoordType}} {{asElement (index .PrecomputedParams 1) }} //tv1 = c6 - var tv2, tv3, tv4, tv5 {{$CoordType}} + var tv2, tv3, tv4, tv5 {{$CoordType}} var exp big.Int - // c4 = {{ $c4Int }} = 2^{{$c1Int}} - 1 + // c4 = {{ $c4Int }} = 2{{supScr $c1Int}} - 1 // q is odd so c1 is at least 1. exp.SetBytes([]byte { {{ $c4IntBytes }} }) - tv2.Exp(*v, &exp) - tv3.Mul(&tv2, &tv2) - tv3.Mul(&tv3, v) - - // line 5 - tv5.Mul(u, &tv3) + tv2.Exp(*v, &exp) // 2. tv2 = vᶜ⁴ + tv3.Square(&tv2) // 3. tv3 = tv2² + tv3.Mul(&tv3, v) // 4. tv3 = tv3 * v + tv5.Mul(u, &tv3) // 5. tv5 = u * tv3 // c3 = {{ $c3Int }} exp.SetBytes([]byte { {{ $c3IntBytes }} }) - tv5.Exp(tv5, &exp) - tv5.Mul(&tv5, &tv2) - tv2.Mul(&tv5, v) - tv3.Mul(&tv5, u) - // line 10 - tv4.Mul(&tv3, &tv2) + tv5.Exp(tv5, &exp) // 6. tv5 = tv5ᶜ³ + tv5.Mul(&tv5, &tv2) // 7. tv5 = tv5 * tv2 + tv2.Mul(&tv5, v) // 8. tv2 = tv5 * v + tv3.Mul(&tv5, u) // 9. tv3 = tv5 * u + tv4.Mul(&tv3, &tv2) // 10. tv4 = tv3 * tv2 // c5 = {{ $c5Int }} exp.SetBytes([]byte { {{ $c5IntBytes }} }) - tv5.Exp(tv4, &exp) - - isQNr := {{$CurveName}}NotOne(&tv5) - - tv2.Mul(&tv3, &{{$CoordType}} {{asElement (index .PrecomputedParams 2) }} ) - tv5.Mul(&tv4, &tv1) - - // line 15 - - tv3.Select(int(isQNr), &tv3, &tv2) - tv4.Select(int(isQNr), &tv4, &tv5) - - exp.Lsh( big.NewInt(1), {{ $c1Int }} - 2) - - for i := {{ $c1Int }}; i >= 2; i -- { - //line 20 - tv5.Exp(tv4, &exp) - nE1 := {{$CurveName}}NotOne(&tv5) - - tv2.Mul(&tv3, &tv1) - tv1.Mul(&tv1, &tv1) - tv5.Mul(&tv4, &tv1) - - tv3.Select(int(nE1), &tv3, &tv2) - tv4.Select(int(nE1), &tv4, &tv5) - - exp.Rsh(&exp,1) + tv5.Exp(tv4, &exp) // 11. tv5 = tv4ᶜ⁵ + isQNr := {{$CurveName}}NotOne(&tv5) // 12. isQR = tv5 == 1 + c7 := {{$CoordType}} {{asElement (index .PrecomputedParams 2) }} + tv2.Mul(&tv3, &c7) // 13. tv2 = tv3 * c7 + tv5.Mul(&tv4, &tv1) // 14. tv5 = tv4 * tv1 + tv3.Select(int(isQNr), &tv3, &tv2) // 15. tv3 = CMOV(tv2, tv3, isQR) + tv4.Select(int(isQNr), &tv4, &tv5) // 16. tv4 = CMOV(tv5, tv4, isQR) + exp.Lsh( big.NewInt(1), {{ $c1Int }} - 2) // 18, 19: tv5 = 2ⁱ⁻² for i = c1 + + for i := {{ $c1Int }}; i >= 2; i -- { // 17. for i in (c1, c1 - 1, ..., 2): + + tv5.Exp(tv4, &exp) // 20. tv5 = tv4ᵗᵛ⁵ + nE1 := {{$CurveName}}NotOne(&tv5) // 21. e1 = tv5 == 1 + tv2.Mul(&tv3, &tv1) // 22. tv2 = tv3 * tv1 + tv1.Mul(&tv1, &tv1) // 23. tv1 = tv1 * tv1 Why not write square? + tv5.Mul(&tv4, &tv1) // 24. tv5 = tv4 * tv1 + tv3.Select(int(nE1), &tv3, &tv2) // 25. tv3 = CMOV(tv2, tv3, e1) + tv4.Select(int(nE1), &tv4, &tv5) // 26. tv4 = CMOV(tv5, tv4, e1) + + if i > 2 { + exp.Rsh(&exp, 1) // 18, 19. tv5 = 2ⁱ⁻² + } } *z = tv3 @@ -304,30 +291,31 @@ func {{$CurveName}}MulByZ(z *{{$CoordType}}, x *{{$CoordType}}) { {{ end }}} -//TODO: Define A,B here - -// From https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/13/ Pg 80 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#name-simplified-swu-method // mapToCurve{{$CurveIndex}} implements the SSWU map // No cofactor clearing or isogeny func mapToCurve{{$CurveIndex}}(u *{{$CoordType}}) {{$AffineType}} { + {{if $isogenyNeeded}} + var {{$sswuCurveACoeff}} = {{$CoordType}} {{asElement .A}} + var {{$sswuCurveBCoeff}} = {{$CoordType}} {{asElement .B}} + {{end}} + var tv1 {{$CoordType}} - tv1.Square(u) + tv1.Square(u) // 1. tv1 = u² //mul tv1 by Z - {{$CurveName}}MulByZ(&tv1, &tv1) + {{$CurveName}}MulByZ(&tv1, &tv1) // 2. tv1 = Z * tv1 var tv2 {{$CoordType}} - tv2.Square(&tv1) - tv2.Add(&tv2, &tv1) + tv2.Square(&tv1) // 3. tv2 = tv1² + tv2.Add(&tv2, &tv1) // 4. tv2 = tv2 + tv1 var tv3 {{$CoordType}} - //Standard doc line 5 var tv4 {{$CoordType}} tv4.SetOne() - tv3.Add(&tv2, &tv4) - //TODO: Use bCurveConf when no isogeny - tv3.Mul(&tv3, &{{$CoordType}}{{ asElement .B }}) + tv3.Add(&tv2, &tv4) // 5. tv3 = tv2 + 1 + tv3.Mul(&tv3, &{{$sswuCurveBCoeff}}) // 6. tv3 = B * tv3 tv2NZero := {{$CurveName}}NotZero(&tv2) @@ -335,48 +323,45 @@ func mapToCurve{{$CurveIndex}}(u *{{$CoordType}}) {{$AffineType}} { tv4 = {{$CoordType}}{{ asElement .Z }} tv2.Neg(&tv2) - tv4.Select(int(tv2NZero), &tv4, &tv2) - //TODO: When no isogeny use curve constants - tv2 = {{$CoordType}}{{ asElement .A }} - tv4.Mul(&tv4, &tv2) + tv4.Select(int(tv2NZero), &tv4, &tv2) // 7. tv4 = CMOV(Z, -tv2, tv2 != 0) + tv4.Mul(&tv4, &{{$sswuCurveACoeff}}) // 8. tv4 = A * tv4 - tv2.Square(&tv3) + tv2.Square(&tv3) // 9. tv2 = tv3² var tv6 {{$CoordType}} - //Standard doc line 10 - tv6.Square(&tv4) + tv6.Square(&tv4) // 10. tv6 = tv4² var tv5 {{$CoordType}} - tv5.Mul(&tv6, &{{$CoordType}}{{ asElement .A }}) + tv5.Mul(&tv6, &{{$sswuCurveACoeff}}) // 11. tv5 = A * tv6 - tv2.Add(&tv2, &tv5) - tv2.Mul(&tv2, &tv3) - tv6.Mul(&tv6, &tv4) + tv2.Add(&tv2, &tv5) // 12. tv2 = tv2 + tv5 + tv2.Mul(&tv2, &tv3) // 13. tv2 = tv2 * tv3 + tv6.Mul(&tv6, &tv4) // 14. tv6 = tv6 * tv4 - //Standards doc line 15 - tv5.Mul(&tv6, &{{$CoordType}}{{asElement .B}}) - tv2.Add(&tv2, &tv5) + tv5.Mul(&tv6, &{{$sswuCurveBCoeff}}) // 15. tv5 = B * tv6 + tv2.Add(&tv2, &tv5) // 16. tv2 = tv2 + tv5 var x {{$CoordType}} - x.Mul(&tv1, &tv3) + x.Mul(&tv1, &tv3) // 17. x = tv1 * tv3 var y1 {{$CoordType}} - gx1NSquare := {{$CurveName}}SqrtRatio(&y1, &tv2, &tv6) + gx1NSquare := {{$CurveName}}SqrtRatio(&y1, &tv2, &tv6) // 18. (is_gx1_square, y1) = sqrt_ratio(tv2, tv6) var y {{$CoordType}} - y.Mul(&tv1, u) + y.Mul(&tv1, u) // 19. y = tv1 * u - //Standards doc line 20 - y.Mul(&y, &y1) + y.Mul(&y, &y1) // 20. y = y * y1 - x.Select(int(gx1NSquare), &tv3, &x) - y.Select(int(gx1NSquare), &y1, &y) + x.Select(int(gx1NSquare), &tv3, &x) // 21. x = CMOV(x, tv3, is_gx1_square) + y.Select(int(gx1NSquare), &y1, &y) // 22. y = CMOV(y, y1, is_gx1_square) y1.Neg(&y) y.Select(int({{$CurveName}}Sgn0(u)^{{$CurveName}}Sgn0(&y)), &y, &y1) - //Standards doc line 25 - x.Div(&x, &tv4) + // 23. e1 = sgn0(u) == sgn0(y) + // 24. y = CMOV(-y, y, e1) + + x.Div(&x, &tv4) // 25. x = x / tv4 return {{$AffineType}}{x, y} } diff --git a/internal/generator/ecc/template/svdw.go.tmpl b/internal/generator/ecc/template/svdw.go.tmpl index 111899008e..7056bf5ade 100644 --- a/internal/generator/ecc/template/svdw.go.tmpl +++ b/internal/generator/ecc/template/svdw.go.tmpl @@ -13,7 +13,7 @@ // mapToCurve{{$CurveIndex}} implements the Shallue and van de Woestijne method, applicable to any elliptic curve in Weierstrass form // No cofactor clearing or isogeny -// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-14#appendix-F.1 +// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-16.html#straightline-svdw func mapToCurve{{$CurveIndex}}(u *{{$CoordType}}) {{$AffineType}} { var tv1, tv2, tv3, tv4 {{$CoordType}} var x1, x2, x3, gx1, gx2, gx, x, y {{$CoordType}} @@ -26,7 +26,6 @@ func mapToCurve{{$CurveIndex}}(u *{{$CoordType}}) {{$AffineType}} { //c3 = sqrt(-g(Z) * (3 * Z² + 4 * A)) # sgn0(c3) MUST equal 0 //c4 = -4 * g(Z) / (3 * Z² + 4 * A) - //TODO: Move outside function? Z := {{$CoordType}}{{asElement (index $.PrecomputedParams 0)}} c1 := {{$CoordType}}{{asElement (index $.PrecomputedParams 1)}} c2 := {{$CoordType}}{{asElement (index $.PrecomputedParams 2)}} @@ -48,8 +47,7 @@ func mapToCurve{{$CurveIndex}}(u *{{$CoordType}}) {{$AffineType}} { x1.Sub(&c2, &tv4) // 10. x1 = c2 - tv4 gx1.Square(&x1) // 11. gx1 = x1² - //TODO: Beware A ≠ 0 - //12. gx1 = gx1 + A + //12. gx1 = gx1 + A All curves in gnark-crypto have A=0 (j-invariant=0). It is crucial to include this step if the curve has nonzero A coefficient. gx1.Mul(&gx1, &x1) // 13. gx1 = gx1 * x1 gx1.Add(&gx1, &{{$B}}) // 14. gx1 = gx1 + B gx1NotSquare = gx1.Legendre() >> 1 // 15. e1 = is_square(gx1) @@ -57,7 +55,7 @@ func mapToCurve{{$CurveIndex}}(u *{{$CoordType}}) {{$AffineType}} { x2.Add(&c2, &tv4) // 16. x2 = c2 + tv4 gx2.Square(&x2) // 17. gx2 = x2² - // 18. gx2 = gx2 + A + // 18. gx2 = gx2 + A See line 12 gx2.Mul(&gx2, &x2) // 19. gx2 = gx2 * x2 gx2.Add(&gx2, &{{$B}}) // 20. gx2 = gx2 + B