// EncodedBytesToBigIntPoint converts a 32 byte representation of a point // on the elliptical curve into a big integer point. It returns an error // if the point does not fall on the curve. func (curve *TwistedEdwardsCurve) EncodedBytesToBigIntPoint(s *[32]byte) (*big.Int, *big.Int, error) { sCopy := new([32]byte) for i := 0; i < fieldIntSize; i++ { sCopy[i] = s[i] } xIsNegBytes := sCopy[31]>>7 == 1 p := new(edwards25519.ExtendedGroupElement) if p.FromBytes(sCopy) == false { return nil, nil, fmt.Errorf("point not on curve") } // Normalize the X and Y coordinates in affine space. x, y, isNegative := curve.extendedToBigAffine(&p.X, &p.Y, &p.Z) // We got the wrong sign; flip the bit and recalculate. if xIsNegBytes != isNegative { x.Sub(curve.P, x) } // This should hopefully never happen, since the // library itself should never let us create a bad // point. if !curve.IsOnCurve(x, y) { return nil, nil, fmt.Errorf("point not on curve") } return x, y, nil }
// Verify returns true iff sig is a valid signature of message by publicKey. func Verify(publicKey *[PublicKeySize]byte, message []byte, sig *[SignatureSize]byte) bool { if sig[63]&224 != 0 { return false } var A edwards25519.ExtendedGroupElement if !A.FromBytes(publicKey) { return false } edwards25519.FeNeg(&A.X, &A.X) edwards25519.FeNeg(&A.T, &A.T) h := sha512.New() h.Write(sig[:32]) h.Write(publicKey[:]) h.Write(message) var digest [64]byte h.Sum(digest[:0]) var hReduced [32]byte edwards25519.ScReduce(&hReduced, &digest) var R edwards25519.ProjectiveGroupElement var b [32]byte copy(b[:], sig[32:]) edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &b) var checkR [32]byte R.ToBytes(&checkR) return subtle.ConstantTimeCompare(sig[:32], checkR[:]) == 1 }
// GenerateKey generates a public/private key pair using randomness from rand. func GenerateKey(rand io.Reader) (publicKey *[PublicKeySize]byte, privateKey *[PrivateKeySize]byte, err error) { privateKey = new([64]byte) publicKey = new([32]byte) _, err = io.ReadFull(rand, privateKey[:32]) if err != nil { return nil, nil, err } h := sha512.New() h.Write(privateKey[:32]) digest := h.Sum(nil) digest[0] &= 248 digest[31] &= 127 digest[31] |= 64 var A edwards25519.ExtendedGroupElement var hBytes [32]byte copy(hBytes[:], digest) edwards25519.GeScalarMultBase(&A, &hBytes) A.ToBytes(publicKey) copy(privateKey[32:], publicKey[:]) return }
// PublicKeyToCurve25519 converts an Ed25519 public key into the curve25519 // public key that would be generated from the same private key. func PublicKeyToCurve25519(curve25519Public *[32]byte, publicKey *[32]byte) bool { var A edwards25519.ExtendedGroupElement if !A.FromBytes(publicKey) { return false } // A.Z = 1 as a postcondition of FromBytes. var x edwards25519.FieldElement edwardsToMontgomeryX(&x, &A.Y) edwards25519.FeToBytes(curve25519Public, &x) return true }
func TestUnmarshalMarshal(t *testing.T) { pub, _, _ := GenerateKey(rand.Reader) var A edwards25519.ExtendedGroupElement if !A.FromBytes(pub) { t.Fatalf("ExtendedGroupElement.FromBytes failed") } var pub2 [32]byte A.ToBytes(&pub2) if *pub != pub2 { t.Errorf("FromBytes(%v)->ToBytes does not round-trip, got %x\n", *pub, pub2) } }
// Sign signs the message with privateKey and returns a signature. func Sign(privateKey *[PrivateKeySize]byte, message []byte) *[SignatureSize]byte { h := sha512.New() h.Write(privateKey[:32]) var digest1, messageDigest, hramDigest [64]byte var expandedSecretKey [32]byte h.Sum(digest1[:0]) copy(expandedSecretKey[:], digest1[:]) expandedSecretKey[0] &= 248 expandedSecretKey[31] &= 63 expandedSecretKey[31] |= 64 h.Reset() h.Write(digest1[32:]) h.Write(message) h.Sum(messageDigest[:0]) var messageDigestReduced [32]byte edwards25519.ScReduce(&messageDigestReduced, &messageDigest) var R edwards25519.ExtendedGroupElement edwards25519.GeScalarMultBase(&R, &messageDigestReduced) var encodedR [32]byte R.ToBytes(&encodedR) h.Reset() h.Write(encodedR[:]) h.Write(privateKey[32:]) h.Write(message) h.Sum(hramDigest[:0]) var hramDigestReduced [32]byte edwards25519.ScReduce(&hramDigestReduced, &hramDigest) var s [32]byte edwards25519.ScMulAdd(&s, &hramDigestReduced, &expandedSecretKey, &messageDigestReduced) signature := new([64]byte) copy(signature[:], encodedR[:]) copy(signature[32:], s[:]) return signature }
// Add adds two points represented by pairs of big integers on the elliptical // curve. func (curve *TwistedEdwardsCurve) Add(x1, y1, x2, y2 *big.Int) (x, y *big.Int) { // Convert to extended from affine. a := BigIntPointToEncodedBytes(x1, y1) aEGE := new(edwards25519.ExtendedGroupElement) aEGE.FromBytes(a) b := BigIntPointToEncodedBytes(x2, y2) bEGE := new(edwards25519.ExtendedGroupElement) bEGE.FromBytes(b) // Cache b for use in group element addition. bCached := new(cachedGroupElement) toCached(bCached, bEGE) p := aEGE q := bCached // geAdd(r*CompletedGroupElement, p*ExtendedGroupElement, // q*CachedGroupElement) // r is the result. r := new(edwards25519.CompletedGroupElement) var t0 edwards25519.FieldElement edwards25519.FeAdd(&r.X, &p.Y, &p.X) edwards25519.FeSub(&r.Y, &p.Y, &p.X) edwards25519.FeMul(&r.Z, &r.X, &q.yPlusX) edwards25519.FeMul(&r.Y, &r.Y, &q.yMinusX) edwards25519.FeMul(&r.T, &q.T2d, &p.T) edwards25519.FeMul(&r.X, &p.Z, &q.Z) edwards25519.FeAdd(&t0, &r.X, &r.X) edwards25519.FeSub(&r.X, &r.Z, &r.Y) edwards25519.FeAdd(&r.Y, &r.Z, &r.Y) edwards25519.FeAdd(&r.Z, &t0, &r.T) edwards25519.FeSub(&r.T, &t0, &r.T) rEGE := new(edwards25519.ExtendedGroupElement) r.ToExtended(rEGE) s := new([32]byte) rEGE.ToBytes(s) x, y, _ = curve.EncodedBytesToBigIntPoint(s) return }
// Double adds the same pair of big integer coordinates to itself on the // elliptical curve. func (curve *TwistedEdwardsCurve) Double(x1, y1 *big.Int) (x, y *big.Int) { // Convert to extended projective coordinates. a := BigIntPointToEncodedBytes(x1, y1) aEGE := new(edwards25519.ExtendedGroupElement) aEGE.FromBytes(a) r := new(edwards25519.CompletedGroupElement) aEGE.Double(r) rEGE := new(edwards25519.ExtendedGroupElement) r.ToExtended(rEGE) s := new([32]byte) rEGE.ToBytes(s) x, y, _ = curve.EncodedBytesToBigIntPoint(s) return }
// SignFromScalar signs a message 'hash' using the given private scalar priv. // It uses RFC6979 to generate a deterministic nonce. Considered experimental. // r = kG, where k is the RFC6979 nonce // s = r + hash512(k || A || M) * a func SignFromScalar(curve *TwistedEdwardsCurve, priv *PrivateKey, nonce []byte, hash []byte) (r, s *big.Int, err error) { publicKey := new([PubKeyBytesLen]byte) var A edwards25519.ExtendedGroupElement privateScalar := copyBytes(priv.Serialize()) reverse(privateScalar) // BE --> LE edwards25519.GeScalarMultBase(&A, privateScalar) A.ToBytes(publicKey) // For signing from a scalar, r = nonce. nonceLE := copyBytes(nonce) reverse(nonceLE) var R edwards25519.ExtendedGroupElement edwards25519.GeScalarMultBase(&R, nonceLE) var encodedR [32]byte R.ToBytes(&encodedR) // h = hash512(k || A || M) h := sha512.New() h.Reset() h.Write(encodedR[:]) h.Write(publicKey[:]) h.Write(hash) // s = r + h * a var hramDigest [64]byte h.Sum(hramDigest[:0]) var hramDigestReduced [32]byte edwards25519.ScReduce(&hramDigestReduced, &hramDigest) var localS [32]byte edwards25519.ScMulAdd(&localS, &hramDigestReduced, privateScalar, nonceLE) signature := new([64]byte) copy(signature[:], encodedR[:]) copy(signature[32:], localS[:]) sigEd, err := ParseSignature(curve, signature[:]) if err != nil { return nil, nil, err } return sigEd.GetR(), sigEd.GetS(), nil }
// ScalarMult returns k*(Bx,By) where k is a number in big-endian form. This // uses the repeated doubling method, which is variable time. // TODO use a constant time method to prevent side channel attacks. func (curve *TwistedEdwardsCurve) ScalarMult(x1, y1 *big.Int, k []byte) (x, y *big.Int) { // Convert the scalar to a big int. s := new(big.Int).SetBytes(k) // Get a new group element to do cached doubling // calculations in. dEGE := new(edwards25519.ExtendedGroupElement) dEGE.Zero() // Use the doubling method for the multiplication. // p := given point // q := point(zero) // for each bit in the scalar, descending: // double(q) // if bit == 1: // add(q, p) // return q // // Note that the addition is skipped for zero bits, // making this variable time and thus vulnerable to // side channel attack vectors. for i := s.BitLen() - 1; i >= 0; i-- { dCGE := new(edwards25519.CompletedGroupElement) dEGE.Double(dCGE) dCGE.ToExtended(dEGE) if s.Bit(i) == 1 { ss := new([32]byte) dEGE.ToBytes(ss) var err error xi, yi, err := curve.EncodedBytesToBigIntPoint(ss) if err != nil { return nil, nil } xAdd, yAdd := curve.Add(xi, yi, x1, y1) dTempBytes := BigIntPointToEncodedBytes(xAdd, yAdd) dEGE.FromBytes(dTempBytes) } } finalBytes := new([32]byte) dEGE.ToBytes(finalBytes) var err error x, y, err = curve.EncodedBytesToBigIntPoint(finalBytes) if err != nil { return nil, nil } return }