// Elligator 1 reverse-map from point to uniform representative. // Returns nil if point has no uniform representative. // See section 3.3 of the Elligator paper. func (el *el1param) HideEncode(P point, rand cipher.Stream) []byte { ec := el.ec x, y := P.getXY() var a, b, etar, etarp1, X, z, u, t, t1 nist.Int // condition 1: a = y+1 is nonzero a.Add(y, &ec.one) if a.V.Sign() == 0 { return nil // y+1 = 0, no representative } // etar = r(y-1)/2(y+1) t1.Add(y, &ec.one).Add(&t1, &t1) // 2(y+1) etar.Sub(y, &ec.one).Mul(&etar, &el.r).Div(&etar, &t1) // condition 2: b = (1 + eta r)^2 - 1 is a square etarp1.Add(&ec.one, &etar) // etarp1 = (1 + eta r) b.Mul(&etarp1, &etarp1).Sub(&b, &ec.one) if math.Jacobi(&b.V, b.M) < 0 { return nil // b not a square, no representative } // condition 3: if etar = -2 then x=2s(c-1)Chi(c)/r if etar.Equal(&el.m2) && !x.Equal(&el.c3x) { return nil } // X = -(1+eta r)+((1+eta r)^2-1)^((q+1)/4) X.Exp(&b, &el.pp1d4).Sub(&X, &etarp1) // z = Chi((c-1)sX(1+X)x(X^2+1/c^2)) z.Mul(&el.cm1s, &X).Mul(&z, t.Add(&ec.one, &X)).Mul(&z, x) z.Mul(&z, t.Mul(&X, &X).Add(&t, &el.invc2)) chi(&z, &z) // u = zX u.Mul(&z, &X) // t = (1-u)/(1+u) t.Div(a.Sub(&ec.one, &u), b.Add(&ec.one, &u)) // 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, _ := t.MarshalBinary() padmask := el.padmask() if padmask != 0 { var pad [1]byte rand.XORKeyStream(pad[:], pad[:]) rep[0] |= pad[0] & padmask } return rep }
// Elligator 1 forward-map from representative to Edwards curve point. // Currently a straightforward, unoptimized implementation. // See section 3.2 of the Elligator paper. func (el *el1param) HideDecode(P point, rep []byte) { ec := el.ec var t, u, u2, v, Chiv, X, Y, x, y, t1, t2 nist.Int l := ec.PointLen() if len(rep) != l { panic("el1Map: wrong representative length") } // Take the appropriate number of bits from the representative. b := make([]byte, l) copy(b, rep) b[0] &^= el.padmask() // mask off the padding bits t.InitBytes(b, &ec.P) // u = (1-t)/(1+t) u.Div(t1.Sub(&ec.one, &t), t2.Add(&ec.one, &t)) // v = u^5 + (r^2-2)u^3 + u u2.Mul(&u, &u) // u2 = u^2 v.Mul(&u2, &u2) // v = u^4 v.Add(&v, t1.Mul(&el.r2m2, &u2)) // v = u^4 + (r^2-2)u^2 v.Add(&v, &ec.one).Mul(&v, &u) // v = u^5 + (r^2-2)u^3 + u // X = Chi(v)u chi(&Chiv, &v) X.Mul(&Chiv, &u) // Y = (Chi(v)v)^((q+1)/4) Chi(v) Chi(u^2+1/c^2) t1.Add(&u2, &el.invc2) chi(&t1, &t1) // t1 = Chi(u^2+1/c^2) Y.Mul(&Chiv, &v) Y.Exp(&Y, &el.pp1d4).Mul(&Y, &Chiv).Mul(&Y, &t1) // x = (c-1)sX(1+X)/Y x.Add(&ec.one, &X).Mul(&X, &x).Mul(&el.cm1s, &x).Div(&x, &Y) // y = (rX-(1+X)^2)/(rX+(1+X)^2) t1.Mul(&el.r, &X) // t1 = rX t2.Add(&ec.one, &X).Mul(&t2, &t2) // t2 = (1+X)^2 y.Div(u.Sub(&t1, &t2), v.Add(&t1, &t2)) // Sanity-check if !ec.onCurve(&x, &y) { panic("elligator1 produced invalid point") } P.initXY(&x.V, &y.V, ec.self) }
// 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) }