// Decode an Edwards curve point into the given x,y coordinates. // Returns an error if the input does not denote a valid curve point. // Note that this does NOT check if the point is in the prime-order subgroup: // an adversary could create an encoding denoting a point // on the twist of the curve, or in a larger subgroup. // However, the "safecurves" criteria (http://safecurves.cr.yp.to) // ensure that none of these other subgroups are small // other than the tiny ones represented by the cofactor; // hence Diffie-Hellman exchange can be done without subgroup checking // without exposing more than the least-significant bits of the scalar. func (c *curve) decodePoint(bb []byte, x, y *nist.Int) error { // Convert from little-endian //fmt.Printf("decoding:\n%s\n", hex.Dump(bb)) b := make([]byte, len(bb)) util.Reverse(b, bb) // Extract the sign of the x-coordinate xsign := uint(b[0] >> 7) b[0] &^= 0x80 // Extract the y-coordinate y.V.SetBytes(b) y.M = &c.P // Compute the corresponding x-coordinate if !c.solveForX(x, y) { return errors.New("invalid elliptic curve point") } if c.coordSign(x) != xsign { x.Neg(x) } return nil }
// Elligator 2 reverse-map from point to uniform representative. // Returns nil if point has no uniform representative. // See section 5.3 of the Elligator paper. func (el *el2param) HideEncode(P point, rand cipher.Stream) []byte { edx, edy := P.getXY() var x, y, r, xpA, t1 nist.Int // convert Edwards to Montgomery coordinates el.ed2mont(&x, &y, edx, edy) // condition 1: x != -A if x.Equal(&el.negA) { return nil // x = -A, no representative } // condition 2: if y=0, then x=0 if y.V.Sign() == 0 && x.V.Sign() != 0 { return nil // y=0 but x!=0, no representative } // condition 3: -ux(x+A) is a square xpA.Add(&x, &el.A) t1.Mul(&el.u, &x).Mul(&t1, &xpA).Neg(&t1) if math.Jacobi(&t1.V, t1.M) < 0 { return nil // not a square, no representative } if y.V.Cmp(&el.pm1d2) <= 0 { // y in image of sqrt function r.Mul(&xpA, &el.u).Div(&x, &r) } else { // y not in image of sqrt function r.Mul(&el.u, &x).Div(&xpA, &r) } r.Neg(&r) el.sqrt(&r, &r) // Sanity check on result if r.V.Cmp(&el.pm1d2) > 0 { panic("el2: r too big") } // Map representative to a byte-string by padding the upper byte. // This assumes that the prime c.P is close enough to a power of 2 // that the adversary will never notice the "missing" values; // this is true for the class of curves Elligator1 was designed for. rep, _ := r.MarshalBinary() padmask := el.padmask() if padmask != 0 { var pad [1]byte rand.XORKeyStream(pad[:], pad[:]) rep[0] |= pad[0] & padmask } return rep }
// Compute the square root function, // specified in section 5.5 of the Elligator paper. func (el *el2param) sqrt(r, a *nist.Int) { var b, b2 nist.Int b.Exp(a, &el.pp3d8) // b = a^((p+3)/8); b in {a,-a} b2.Mul(&b, &b) // b^2 = a? if !b2.Equal(a) { b.Mul(&b, &el.sqrtm1) // b*sqrt(-1) } if b.V.Cmp(&el.pm1d2) > 0 { // |b| b.Neg(&b) } r.Set(&b) }
// Elligator 2 forward-map from representative to Edwards curve point. // Currently a straightforward, unoptimized implementation. // See section 5.2 of the Elligator paper. func (el *el2param) HideDecode(P point, rep []byte) { ec := el.ec var r, v, x, y, t1, edx, edy nist.Int l := ec.PointLen() if len(rep) != l { panic("el2Map: wrong representative length") } // Take the appropriate number of bits from the representative. buf := make([]byte, l) copy(buf, rep) buf[0] &^= el.padmask() // mask off the padding bits r.InitBytes(buf, &ec.P) // v = -A/(1+ur^2) v.Mul(&r, &r).Mul(&el.u, &v).Add(&ec.one, &v).Div(&el.negA, &v) // e = Chi(v^3+Av^2+Bv), where B=1 because of ed2mont equivalence t1.Add(&v, &el.A).Mul(&t1, &v).Add(&t1, &ec.one).Mul(&t1, &v) e := math.Jacobi(&t1.V, t1.M) // x = ev-(1-e)A/2 if e == 1 { x.Set(&v) } else { x.Add(&v, &el.A).Neg(&x) } // y = -e sqrt(x^3+Ax^2+Bx), where B=1 y.Add(&x, &el.A).Mul(&y, &x).Add(&y, &ec.one).Mul(&y, &x) el.sqrt(&y, &y) if e == 1 { y.Neg(&y) // -e factor } // Convert Montgomery to Edwards coordinates el.mont2ed(&edx, &edy, &x, &y) // Sanity-check if !ec.onCurve(&edx, &edy) { panic("elligator2 produced invalid point") } P.initXY(&edx.V, &edy.V, ec.self) }
// Initialize a twisted Edwards curve with given parameters. // Caller passes pointers to null and base point prototypes to be initialized. func (c *curve) init(self abstract.Group, p *Param, fullGroup bool, null, base point) *curve { c.self = self c.Param = *p c.full = fullGroup c.null = null // Edwards curve parameters as ModInts for convenience c.a.Init(&p.A, &p.P) c.d.Init(&p.D, &p.P) // Cofactor c.cofact.Init64(int64(p.R), &c.P) // Determine the modulus for scalars on this curve. // Note that we do NOT initialize c.order with Init(), // as that would normalize to the modulus, resulting in zero. // Just to be sure it's never used, we leave c.order.M set to nil. // We want it to be in a ModInt so we can pass it to P.Mul(), // but the scalar's modulus isn't needed for point multiplication. if fullGroup { // Scalar modulus is prime-order times the ccofactor c.order.V.SetInt64(int64(p.R)).Mul(&c.order.V, &p.Q) } else { c.order.V.Set(&p.Q) // Prime-order subgroup } // Useful ModInt constants for this curve c.zero.Init64(0, &c.P) c.one.Init64(1, &c.P) // Identity element is (0,1) null.initXY(zero, one, self) // Base point B var bx, by *big.Int if !fullGroup { bx, by = &p.PBX, &p.PBY } else { bx, by = &p.FBX, &p.FBY base.initXY(&p.FBX, &p.FBY, self) } if by.Sign() == 0 { // No standard base point was defined, so pick one. // Find the lowest-numbered y-coordinate that works. //println("Picking base point:") var x, y nist.Int for y.Init64(2, &c.P); ; y.Add(&y, &c.one) { if !c.solveForX(&x, &y) { continue // try another y } if c.coordSign(&x) != 0 { x.Neg(&x) // try positive x first } base.initXY(&x.V, &y.V, self) if c.validPoint(base) { break // got one } x.Neg(&x) // try -bx if c.validPoint(base) { break // got one } } //println("BX: "+x.V.String()) //println("BY: "+y.V.String()) bx, by = &x.V, &y.V } base.initXY(bx, by, self) // Uniform representation encoding methods, // only useful when using the full group. // (Points taken from the subgroup would be trivially recognizable.) if fullGroup { if p.Elligator1s.Sign() != 0 { c.hide = new(el1param).init(c, &p.Elligator1s) } else if p.Elligator2u.Sign() != 0 { c.hide = new(el2param).init(c, &p.Elligator2u) } // XXX Elligator Squared } // Sanity checks if !c.validPoint(null) { panic("invalid identity point " + null.String()) } if !c.validPoint(base) { panic("invalid base point " + base.String()) } return c }
// Pick a [pseudo-]random curve point with optional embedded data, // filling in the point's x,y coordinates // and returning any remaining data not embedded. func (c *curve) pickPoint(P point, data []byte, rand cipher.Stream) []byte { // How much data to embed? dl := c.pickLen() if dl > len(data) { dl = len(data) } // Retry until we find a valid point var x, y nist.Int var Q abstract.Point for { // Get random bits the size of a compressed Point encoding, // in which the topmost bit is reserved for the x-coord sign. l := c.PointLen() b := make([]byte, l) rand.XORKeyStream(b, b) // Interpret as little-endian if data != nil { b[0] = byte(dl) // Encode length in low 8 bits copy(b[1:1+dl], data) // Copy in data to embed } util.Reverse(b, b) // Convert to big-endian form xsign := b[0] >> 7 // save x-coordinate sign bit b[0] &^= 0xff << uint(c.P.BitLen()&7) // clear high bits y.M = &c.P // set y-coordinate y.SetBytes(b) if !c.solveForX(&x, &y) { // Corresponding x-coordinate? continue // none, retry } // Pick a random sign for the x-coordinate if c.coordSign(&x) != uint(xsign) { x.Neg(&x) } // Initialize the point P.initXY(&x.V, &y.V, c.self) if c.full { // If we're using the full group, // we just need any point on the curve, so we're done. return data[dl:] } // We're using the prime-order subgroup, // so we need to make sure the point is in that subgroup. // If we're not trying to embed data, // we can convert our point into one in the subgroup // simply by multiplying it by the cofactor. if data == nil { P.Mul(P, &c.cofact) // multiply by cofactor if P.Equal(c.null) { continue // unlucky; try again } return data[dl:] } // Since we need the point's y-coordinate to make sense, // we must simply check if the point is in the subgroup // and retry point generation until it is. if Q == nil { Q = c.self.Point() } Q.Mul(P, &c.order) if Q.Equal(c.null) { return data[dl:] } // Keep trying... } }