1// Copyright 2013 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package ed25519 implements the Ed25519 signature algorithm. See
6// http://ed25519.cr.yp.to/.
7package ed25519
8
9// This code is a port of the public domain, "ref10" implementation of ed25519
10// from SUPERCOP.
11
12import (
13	"crypto/sha512"
14	"crypto/subtle"
15	"io"
16
17	"github.com/agl/ed25519/edwards25519"
18)
19
20const (
21	PublicKeySize  = 32
22	PrivateKeySize = 64
23	SignatureSize  = 64
24)
25
26// GenerateKey generates a public/private key pair using randomness from rand.
27func GenerateKey(rand io.Reader) (publicKey *[PublicKeySize]byte, privateKey *[PrivateKeySize]byte, err error) {
28	privateKey = new([64]byte)
29	publicKey = new([32]byte)
30	_, err = io.ReadFull(rand, privateKey[:32])
31	if err != nil {
32		return nil, nil, err
33	}
34
35	h := sha512.New()
36	h.Write(privateKey[:32])
37	digest := h.Sum(nil)
38
39	digest[0] &= 248
40	digest[31] &= 127
41	digest[31] |= 64
42
43	var A edwards25519.ExtendedGroupElement
44	var hBytes [32]byte
45	copy(hBytes[:], digest)
46	edwards25519.GeScalarMultBase(&A, &hBytes)
47	A.ToBytes(publicKey)
48
49	copy(privateKey[32:], publicKey[:])
50	return
51}
52
53// Sign signs the message with privateKey and returns a signature.
54func Sign(privateKey *[PrivateKeySize]byte, message []byte) *[SignatureSize]byte {
55	h := sha512.New()
56	h.Write(privateKey[:32])
57
58	var digest1, messageDigest, hramDigest [64]byte
59	var expandedSecretKey [32]byte
60	h.Sum(digest1[:0])
61	copy(expandedSecretKey[:], digest1[:])
62	expandedSecretKey[0] &= 248
63	expandedSecretKey[31] &= 63
64	expandedSecretKey[31] |= 64
65
66	h.Reset()
67	h.Write(digest1[32:])
68	h.Write(message)
69	h.Sum(messageDigest[:0])
70
71	var messageDigestReduced [32]byte
72	edwards25519.ScReduce(&messageDigestReduced, &messageDigest)
73	var R edwards25519.ExtendedGroupElement
74	edwards25519.GeScalarMultBase(&R, &messageDigestReduced)
75
76	var encodedR [32]byte
77	R.ToBytes(&encodedR)
78
79	h.Reset()
80	h.Write(encodedR[:])
81	h.Write(privateKey[32:])
82	h.Write(message)
83	h.Sum(hramDigest[:0])
84	var hramDigestReduced [32]byte
85	edwards25519.ScReduce(&hramDigestReduced, &hramDigest)
86
87	var s [32]byte
88	edwards25519.ScMulAdd(&s, &hramDigestReduced, &expandedSecretKey, &messageDigestReduced)
89
90	signature := new([64]byte)
91	copy(signature[:], encodedR[:])
92	copy(signature[32:], s[:])
93	return signature
94}
95
96// Verify returns true iff sig is a valid signature of message by publicKey.
97func Verify(publicKey *[PublicKeySize]byte, message []byte, sig *[SignatureSize]byte) bool {
98	if sig[63]&224 != 0 {
99		return false
100	}
101
102	var A edwards25519.ExtendedGroupElement
103	if !A.FromBytes(publicKey) {
104		return false
105	}
106	edwards25519.FeNeg(&A.X, &A.X)
107	edwards25519.FeNeg(&A.T, &A.T)
108
109	h := sha512.New()
110	h.Write(sig[:32])
111	h.Write(publicKey[:])
112	h.Write(message)
113	var digest [64]byte
114	h.Sum(digest[:0])
115
116	var hReduced [32]byte
117	edwards25519.ScReduce(&hReduced, &digest)
118
119	var R edwards25519.ProjectiveGroupElement
120	var b [32]byte
121	copy(b[:], sig[32:])
122	edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &b)
123
124	var checkR [32]byte
125	R.ToBytes(&checkR)
126	return subtle.ConstantTimeCompare(sig[:32], checkR[:]) == 1
127}
128