diff --git a/curve/ecgfp5/point.go b/curve/ecgfp5/point.go index 9cfcf92..04b9a7d 100644 --- a/curve/ecgfp5/point.go +++ b/curve/ecgfp5/point.go @@ -1,6 +1,8 @@ package ecgfp5 import ( + "fmt" + g "github.com/elliottech/poseidon_crypto/field/goldilocks" gFp5 "github.com/elliottech/poseidon_crypto/field/goldilocks_quintic_extension" ) @@ -65,6 +67,73 @@ var ( u: gFp5.FP5_ONE, t: gFp5.Element{4, 0, 0, 0, 0}, } + + GeneratorWindowAffine = []AffinePoint{ + { + x: gFp5.Element{12883135586176881569, 4356519642755055268, 5248930565894896907, 2165973894480315022, 2448410071095648785}, + u: gFp5.Element{13835058052060938241, 18446744069414584321, 18446744069414584321, 18446744069414584321, 18446744069414584321}, + }, + { + x: gFp5.Element{16517537419581740386, 6962630169123120981, 12147752690379666704, 16637325971742264607, 2335078582315237010}, + u: gFp5.Element{8457587110646932172, 138591869800252458, 3187444967472352324, 18179149801168653736, 9453003655195557048}, + }, + { + x: gFp5.Element{4546139357324501584, 1393728687664685160, 15208040286522119521, 7903224051455420834, 12463930627278381774}, + u: gFp5.Element{16373828487211693378, 5899455736915524900, 17616512450102495476, 17643201028570366669, 2833280130550676525}, + }, + { + x: gFp5.Element{4341836049185169731, 9111482874850194930, 7798994609726992878, 12619124383509403661, 13047834166950680886}, + u: gFp5.Element{3584786391427904733, 1717626083626375072, 16549008311909030594, 17550175197111849143, 18374971670674568416}, + }, + { + x: gFp5.Element{18121072711119258927, 3394315639035318724, 2648370499809919556, 13348924736921714137, 3428166646246873447}, + u: gFp5.Element{9264305576790077869, 7426254234280836405, 5107777768036114824, 9390769538758625122, 9788182195111344062}, + }, + { + x: gFp5.Element{11080635543643017332, 3122290570793204485, 16632474826839786439, 14883711538614796285, 10396852362099782295}, + u: gFp5.Element{14253916706639980511, 15728038457561632290, 3947138785484546318, 4740958322851071718, 17384736114265519442}, + }, + { + x: gFp5.Element{4763058716218401568, 17879823368956058516, 13578954599286698938, 8634670560943921567, 13706660844700767685}, + u: gFp5.Element{3354778288360932917, 13842278303693121409, 4717821645259836467, 7978743897613094276, 10118963888992569394}, + }, + { + x: gFp5.Element{4026958896735257282, 13595990041314210204, 11499471878438064392, 10019455879458851233, 11986847968355927330}, + u: gFp5.Element{14532821659997761913, 9582789969382797985, 3082219099923033594, 2859656980617778370, 3746047816071136016}, + }, + { + x: gFp5.Element{15935900828168308224, 8668680449802005535, 491315506768012688, 6584881037682113026, 12386385009372860460}, + u: gFp5.Element{13217832923050551864, 51671271962049328, 15400792709153778477, 6752203529649104660, 2855313280735340066}, + }, + { + x: gFp5.Element{8473506523195244465, 2446964921175324878, 17962771942831363202, 6949608686158330138, 9315492999547366751}, + u: gFp5.Element{5171814696081600409, 3025466154945175207, 453302446979841822, 14135305892339872079, 2556388051049291052}, + }, + { + x: gFp5.Element{3960231187580500028, 3695840168764199059, 2914577777792670911, 9249939676680902688, 17553522813502241416}, + u: gFp5.Element{3015152305907361949, 10730034543155667220, 3314242046485170944, 1984395553885795852, 13781645774758249860}, + }, + { + x: gFp5.Element{11575997426281090678, 1534495174840625570, 7539338128385981583, 10393042019577161985, 10667466219175771157}, + u: gFp5.Element{16681365912970185037, 11287896019745355117, 11069899752345274504, 15487604769605237513, 13467978440572613228}, + }, + { + x: gFp5.Element{11192179397773394280, 3555953455665397909, 5346523552109387121, 4514445299325204396, 3932728981135688453}, + u: gFp5.Element{5421638117266109845, 204299445119713184, 6067390115784997081, 16191134954342419157, 4139938600224417293}, + }, + { + x: gFp5.Element{13189785832536261642, 8777097377506996162, 17497140949916325738, 15140279769427597032, 15517274717131999881}, + u: gFp5.Element{1040464435413162742, 9262701069034606854, 2990438819650713743, 18129195737333990255, 12490074042478236606}, + }, + { + x: gFp5.Element{17716508479149156535, 14351380558651795729, 3644546258883003807, 5171318241596472386, 294806796132518330}, + u: gFp5.Element{7535225611936271281, 14682077054502188499, 784215514926156349, 5280586574139275596, 14407528916988559545}, + }, + { + x: gFp5.Element{8681294642569802563, 7751765660802747503, 16382129702876313971, 7447155060842833278, 6859908403876474879}, + u: gFp5.Element{9674486254207846385, 5248970165164951259, 3611784478790504991, 18437168019170350173, 3537959913875671086}, + }, + } ) func (p ECgFp5Point) Equals(rhs ECgFp5Point) bool { @@ -435,6 +504,26 @@ func BatchToAffine(src []ECgFp5Point) []AffinePoint { return res } +// To pre-compute G, 2G, 3G...15G +func GenerateG() { + p := GENERATOR_ECgFp5Point + tmp := make([]ECgFp5Point, WIN_SIZE) + tmp[0] = p + for i := 1; i < WIN_SIZE; i++ { + if (i & 1) == 0 { + tmp[i] = tmp[i-1].Add(p) + } else { + tmp[i] = tmp[i>>1].Double() + } + } + res := BatchToAffine(tmp) + for i := 0; i < 16; i++ { + elx := fmt.Sprintf("gFp5.Element{%d, %d, %d, %d, %d}", res[i].x[0], res[i].x[1], res[i].x[2], res[i].x[3], res[i].x[4]) + elu := fmt.Sprintf("gFp5.Element{%d, %d, %d, %d, %d}", res[i].u[0], res[i].u[1], res[i].u[2], res[i].u[3], res[i].u[4]) + fmt.Printf("AffinePoint{\n x : %s,\n u : %s,\n},\n", elx, elu) + } +} + func (p ECgFp5Point) MakeWindowAffine() []AffinePoint { tmp := make([]ECgFp5Point, WIN_SIZE) tmp[0] = p @@ -466,3 +555,27 @@ func (r ECgFp5Point) Mul(s ECgFp5Scalar) ECgFp5Point { return p } + +// Returns G*scalarA + b*scalarB +func MulAddG(b ECgFp5Point, scalarA, scalarB ECgFp5Scalar) ECgFp5Point { + winA := GeneratorWindowAffine + winB := b.MakeWindowAffine() + + digitsA := make([]int32, (319+WINDOW)/WINDOW) + scalarA.RecodeSigned(digitsA, int32(WINDOW)) + digitsB := make([]int32, (319+WINDOW)/WINDOW) + scalarB.RecodeSigned(digitsB, int32(WINDOW)) + + n := len(digitsA) // == len(digitsB) + + // Top window digit of each scalar, combined into the running point. + p := LookupVarTime(winA, digitsA[n-1]).ToPoint() + p = p.AddAffine(LookupVarTime(winB, digitsB[n-1])) + + for i := n - 2; i >= 0; i-- { + p.SetMDouble(uint32(WINDOW)) // ×2^WINDOW, shared by both scalars + p = p.AddAffine(LookupVarTime(winA, digitsA[i])) + p = p.AddAffine(LookupVarTime(winB, digitsB[i])) + } + return p +} diff --git a/field/goldilocks_quintic_extension/goldilocks_quintic_extension.go b/field/goldilocks_quintic_extension/goldilocks_quintic_extension.go index f4b2907..2ba3c86 100644 --- a/field/goldilocks_quintic_extension/goldilocks_quintic_extension.go +++ b/field/goldilocks_quintic_extension/goldilocks_quintic_extension.go @@ -2,6 +2,7 @@ package goldilocks_quintic_extension import ( "errors" + "math/bits" g "github.com/elliottech/poseidon_crypto/field/goldilocks" ) @@ -96,52 +97,103 @@ func Sub(a, b Element) Element { } } +// reduce128 reduces hi*2^64 + lo modulo the Goldilocks prime. +func reduce128(lo, hi uint64) uint64 { + x_hi_hi := hi >> 32 + x_hi_lo := hi & g.EPSILON + + t0, borrow := bits.Sub64(lo, x_hi_hi, 0) + t0 -= (g.EPSILON & -borrow) + + t1 := x_hi_lo * g.EPSILON + + resWrapped, carry := bits.Add64(t0, t1, 0) + t2 := resWrapped + (g.EPSILON & -carry) + return t2 +} + +// acc192 is a 192-bit accumulator of unreduced products +type acc192 struct{ lo, mid, hi uint64 } + +func (s acc192) addProduct(a, b uint64) (res acc192) { // s += a*b (no reduction) + res = s + hi, lo := bits.Mul64(a, b) + var c uint64 + res.lo, c = bits.Add64(s.lo, lo, 0) + res.mid, c = bits.Add64(s.mid, hi, c) + res.hi += c + return +} + +func (s acc192) addProduct3(a, b uint64) (res acc192) { // s += 3*a*b (the X^5 = 3 fold) + res = s + hi, lo := bits.Mul64(a, b) + d0 := lo << 1 // 3*(hi:lo) = (hi:lo)<<1 + (hi:lo) + d1 := (hi << 1) | (lo >> 63) + d2 := hi >> 63 + var c uint64 + d0, c = bits.Add64(d0, lo, 0) + d1, c = bits.Add64(d1, hi, c) + d2 += c + res.lo, c = bits.Add64(s.lo, d0, 0) + res.mid, c = bits.Add64(s.mid, d1, c) + res.hi += d2 + c + return +} + +func (s acc192) reduce() uint64 { + // value = (lo + mid*2^64) + hi*2^128 ; 2^128 ≡ -2^32 (mod p) + rLow := reduce128(s.lo, s.mid) + r2 := reduce128(s.hi<<32, s.hi>>32) // = hi*2^32 mod p + res, borrow := bits.Sub64(rLow, r2, 0) + return res - (g.EPSILON & -borrow) +} + +// Mul multiplies two F_{p^5} elements with lazy reduction func Mul(a, b Element) Element { - w := FP5_W + a0, a1, a2, a3, a4 := uint64(a[0]), uint64(a[1]), uint64(a[2]), uint64(a[3]), uint64(a[4]) + b0, b1, b2, b3, b4 := uint64(b[0]), uint64(b[1]), uint64(b[2]), uint64(b[3]), uint64(b[4]) + + var s0, s1, s2, s3, s4 acc192 + + // c0 = a0b0 + 3(a1b4 + a2b3 + a3b2 + a4b1) + s0 = s0.addProduct(a0, b0) + s0 = s0.addProduct3(a1, b4) + s0 = s0.addProduct3(a2, b3) + s0 = s0.addProduct3(a3, b2) + s0 = s0.addProduct3(a4, b1) + // c1 = a0b1 + a1b0 + 3(a2b4 + a3b3 + a4b2) + s1 = s1.addProduct(a0, b1) + s1 = s1.addProduct(a1, b0) + s1 = s1.addProduct3(a2, b4) + s1 = s1.addProduct3(a3, b3) + s1 = s1.addProduct3(a4, b2) + // c2 = a0b2 + a1b1 + a2b0 + 3(a3b4 + a4b3) + s2 = s2.addProduct(a0, b2) + s2 = s2.addProduct(a1, b1) + s2 = s2.addProduct(a2, b0) + s2 = s2.addProduct3(a3, b4) + s2 = s2.addProduct3(a4, b3) + // c3 = a0b3 + a1b2 + a2b1 + a3b0 + 3 a4b4 + s3 = s3.addProduct(a0, b3) + s3 = s3.addProduct(a1, b2) + s3 = s3.addProduct(a2, b1) + s3 = s3.addProduct(a3, b0) + s3 = s3.addProduct3(a4, b4) + // c4 = a0b4 + a1b3 + a2b2 + a3b1 + a4b0 + s4 = s4.addProduct(a0, b4) + s4 = s4.addProduct(a1, b3) + s4 = s4.addProduct(a2, b2) + s4 = s4.addProduct(a3, b1) + s4 = s4.addProduct(a4, b0) - a0b0 := g.MulF(a[0], b[0]) - a1b4 := g.MulF(a[1], b[4]) - a2b3 := g.MulF(a[2], b[3]) - a3b2 := g.MulF(a[3], b[2]) - a4b1 := g.MulF(a[4], b[1]) - added := g.AddF(g.AddF(a1b4, a2b3), g.AddF(a3b2, a4b1)) - muld := g.MulF(w, added) - c0 := g.AddF(a0b0, muld) - - a0b1 := g.MulF(a[0], b[1]) - a1b0 := g.MulF(a[1], b[0]) - a2b4 := g.MulF(a[2], b[4]) - a3b3 := g.MulF(a[3], b[3]) - a4b2 := g.MulF(a[4], b[2]) - added = g.AddF(g.AddF(a2b4, a3b3), a4b2) - muld = g.MulF(w, added) - c1 := g.AddF(g.AddF(a0b1, a1b0), muld) - - a0b2 := g.MulF(a[0], b[2]) - a1b1 := g.MulF(a[1], b[1]) - a2b0 := g.MulF(a[2], b[0]) - a3b4 := g.MulF(a[3], b[4]) - a4b3 := g.MulF(a[4], b[3]) - added = g.AddF(a3b4, a4b3) - muld = g.MulF(w, added) - c2 := g.AddF(g.AddF(a0b2, a1b1), g.AddF(a2b0, muld)) - - a0b3 := g.MulF(a[0], b[3]) - a1b2 := g.MulF(a[1], b[2]) - a2b1 := g.MulF(a[2], b[1]) - a3b0 := g.MulF(a[3], b[0]) - a4b4 := g.MulF(a[4], b[4]) - muld = g.MulF(w, a4b4) - c3 := g.AddF(g.AddF(g.AddF(a0b3, a1b2), g.AddF(a2b1, a3b0)), muld) - - a0b4 := g.MulF(a[0], b[4]) - a1b3 := g.MulF(a[1], b[3]) - a2b2 := g.MulF(a[2], b[2]) - a3b1 := g.MulF(a[3], b[1]) - a4b0 := g.MulF(a[4], b[0]) - c4 := g.AddF(g.AddF(g.AddF(a0b4, a1b3), g.AddF(a2b2, a3b1)), a4b0) - - return Element{c0, c1, c2, c3, c4} + return Element{ + g.GoldilocksField(s0.reduce()), + g.GoldilocksField(s1.reduce()), + g.GoldilocksField(s2.reduce()), + g.GoldilocksField(s3.reduce()), + g.GoldilocksField(s4.reduce()), + } } // Returns a / b. Panics if b == 0. @@ -162,40 +214,65 @@ func ExpPowerOf2(x Element, power int) Element { return res } +// add3 adds the 3-limb value v2:v1:v0 into the accumulator. +func (s acc192) add3(v0, v1, v2 uint64) (res acc192) { + res = s + var c uint64 + res.lo, c = bits.Add64(s.lo, v0, 0) + res.mid, c = bits.Add64(s.mid, v1, c) + res.hi += v2 + c + return +} + +func (s acc192) addProduct2(a, b uint64) acc192 { // s += 2*a*b (shift the product left 1) + hi, lo := bits.Mul64(a, b) + return s.add3(lo<<1, (hi<<1)|(lo>>63), hi>>63) +} + +func (s acc192) addProduct6(a, b uint64) acc192 { // s += 6*a*b = 4*ab + 2*ab + hi, lo := bits.Mul64(a, b) + q0, q1, q2 := lo<<2, (hi<<2)|(lo>>62), hi>>62 // 4x + r0, r1, r2 := lo<<1, (hi<<1)|(lo>>63), hi>>63 // 2x + var c uint64 + q0, c = bits.Add64(q0, r0, 0) + q1, c = bits.Add64(q1, r1, c) + q2 += r2 + c + return s.add3(q0, q1, q2) +} + +// Square squares an F_{p^5} element with lazy reduction func Square(a Element) Element { - w := FP5_W - double_w := g.AddF(w, w) - - a0s := g.MulF(a[0], a[0]) - a1a4 := g.MulF(a[1], a[4]) - a2a3 := g.MulF(a[2], a[3]) - added := g.AddF(a1a4, a2a3) - muld := g.MulF(double_w, added) - c0 := g.AddF(a0s, muld) - - a0Double := g.AddF(a[0], a[0]) - a0Doublea1 := g.MulF(a0Double, a[1]) - a2a4DoubleW := g.MulF(g.MulF(a[2], a[4]), double_w) - a3a3w := g.MulF(g.MulF(a[3], a[3]), w) - c1 := g.AddF(g.AddF(a0Doublea1, a2a4DoubleW), a3a3w) - - a0Doublea2 := g.MulF(a0Double, a[2]) - a1Square := g.MulF(a[1], a[1]) - a4a3DoubleW := g.MulF(g.MulF(a[4], a[3]), double_w) - c2 := g.AddF(g.AddF(a0Doublea2, a1Square), a4a3DoubleW) - - a1Double := g.AddF(a[1], a[1]) - a0Doublea3 := g.MulF(a0Double, a[3]) - a1Doublea2 := g.MulF(a1Double, a[2]) - a4SquareW := g.MulF(g.MulF(a[4], a[4]), w) - c3 := g.AddF(g.AddF(a0Doublea3, a1Doublea2), a4SquareW) - - a0Doublea4 := g.MulF(a0Double, a[4]) - a1Doublea3 := g.MulF(a1Double, a[3]) - a2Square := g.MulF(a[2], a[2]) - c4 := g.AddF(g.AddF(a0Doublea4, a1Doublea3), a2Square) - - return Element{c0, c1, c2, c3, c4} + a0, a1, a2, a3, a4 := uint64(a[0]), uint64(a[1]), uint64(a[2]), uint64(a[3]), uint64(a[4]) + var s0, s1, s2, s3, s4 acc192 + + // c0 = a0^2 + 6 a1a4 + 6 a2a3 + s0 = s0.addProduct(a0, a0) + s0 = s0.addProduct6(a1, a4) + s0 = s0.addProduct6(a2, a3) + // c1 = 2 a0a1 + 6 a2a4 + 3 a3^2 + s1 = s1.addProduct2(a0, a1) + s1 = s1.addProduct6(a2, a4) + s1 = s1.addProduct3(a3, a3) + // c2 = 2 a0a2 + a1^2 + 6 a3a4 + s2 = s2.addProduct2(a0, a2) + s2 = s2.addProduct(a1, a1) + s2 = s2.addProduct6(a3, a4) + // c3 = 2 a0a3 + 2 a1a2 + 3 a4^2 + s3 = s3.addProduct2(a0, a3) + s3 = s3.addProduct2(a1, a2) + s3 = s3.addProduct3(a4, a4) + // c4 = 2 a0a4 + 2 a1a3 + a2^2 + s4 = s4.addProduct2(a0, a4) + s4 = s4.addProduct2(a1, a3) + s4 = s4.addProduct(a2, a2) + + return Element{ + g.GoldilocksField(s0.reduce()), + g.GoldilocksField(s1.reduce()), + g.GoldilocksField(s2.reduce()), + g.GoldilocksField(s3.reduce()), + g.GoldilocksField(s4.reduce()), + } } func Triple(a Element) Element { diff --git a/signature/schnorr/schnorr.go b/signature/schnorr/schnorr.go index 25e463c..b18b345 100644 --- a/signature/schnorr/schnorr.go +++ b/signature/schnorr/schnorr.go @@ -192,12 +192,12 @@ func IsSchnorrSignatureValid(pubKey, hashedMsg gFp5.Element, sig Signature) bool // Decode public key (canonical decoding automatically ensures valid group element) // No subgroup check needed due to prime order! - pubKeyWs, ok := curve.DecodeFp5AsWeierstrass(pubKey) + pubKeyWs, ok := curve.Decode(pubKey) if !ok { return false } - rV := curve.MulAdd2(curve.GENERATOR_WEIERSTRASS, pubKeyWs, sig.S, sig.E).Encode() // r_v = s*G + e*pk + rV := curve.MulAddG(pubKeyWs, sig.S, sig.E).Encode() // r_v = s*G + e*pk preImage := make([]g.GoldilocksField, 5+5) copy(preImage[:5], rV[:])