]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | // Copyright 2016 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/. | |
7 | // | |
8 | // These functions are also compatible with the “Ed25519” function defined in | |
9 | // https://tools.ietf.org/html/draft-irtf-cfrg-eddsa-05. | |
10 | package ed25519 | |
11 | ||
12 | // This code is a port of the public domain, “ref10” implementation of ed25519 | |
13 | // from SUPERCOP. | |
14 | ||
15 | import ( | |
16 | "crypto" | |
17 | cryptorand "crypto/rand" | |
18 | "crypto/sha512" | |
19 | "crypto/subtle" | |
20 | "errors" | |
21 | "io" | |
22 | "strconv" | |
23 | ||
24 | "golang.org/x/crypto/ed25519/internal/edwards25519" | |
25 | ) | |
26 | ||
27 | const ( | |
28 | // PublicKeySize is the size, in bytes, of public keys as used in this package. | |
29 | PublicKeySize = 32 | |
30 | // PrivateKeySize is the size, in bytes, of private keys as used in this package. | |
31 | PrivateKeySize = 64 | |
32 | // SignatureSize is the size, in bytes, of signatures generated and verified by this package. | |
33 | SignatureSize = 64 | |
34 | ) | |
35 | ||
36 | // PublicKey is the type of Ed25519 public keys. | |
37 | type PublicKey []byte | |
38 | ||
39 | // PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer. | |
40 | type PrivateKey []byte | |
41 | ||
42 | // Public returns the PublicKey corresponding to priv. | |
43 | func (priv PrivateKey) Public() crypto.PublicKey { | |
44 | publicKey := make([]byte, PublicKeySize) | |
45 | copy(publicKey, priv[32:]) | |
46 | return PublicKey(publicKey) | |
47 | } | |
48 | ||
49 | // Sign signs the given message with priv. | |
50 | // Ed25519 performs two passes over messages to be signed and therefore cannot | |
51 | // handle pre-hashed messages. Thus opts.HashFunc() must return zero to | |
52 | // indicate the message hasn't been hashed. This can be achieved by passing | |
53 | // crypto.Hash(0) as the value for opts. | |
54 | func (priv PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) { | |
55 | if opts.HashFunc() != crypto.Hash(0) { | |
56 | return nil, errors.New("ed25519: cannot sign hashed message") | |
57 | } | |
58 | ||
59 | return Sign(priv, message), nil | |
60 | } | |
61 | ||
62 | // GenerateKey generates a public/private key pair using entropy from rand. | |
63 | // If rand is nil, crypto/rand.Reader will be used. | |
64 | func GenerateKey(rand io.Reader) (publicKey PublicKey, privateKey PrivateKey, err error) { | |
65 | if rand == nil { | |
66 | rand = cryptorand.Reader | |
67 | } | |
68 | ||
69 | privateKey = make([]byte, PrivateKeySize) | |
70 | publicKey = make([]byte, PublicKeySize) | |
71 | _, err = io.ReadFull(rand, privateKey[:32]) | |
72 | if err != nil { | |
73 | return nil, nil, err | |
74 | } | |
75 | ||
76 | digest := sha512.Sum512(privateKey[:32]) | |
77 | digest[0] &= 248 | |
78 | digest[31] &= 127 | |
79 | digest[31] |= 64 | |
80 | ||
81 | var A edwards25519.ExtendedGroupElement | |
82 | var hBytes [32]byte | |
83 | copy(hBytes[:], digest[:]) | |
84 | edwards25519.GeScalarMultBase(&A, &hBytes) | |
85 | var publicKeyBytes [32]byte | |
86 | A.ToBytes(&publicKeyBytes) | |
87 | ||
88 | copy(privateKey[32:], publicKeyBytes[:]) | |
89 | copy(publicKey, publicKeyBytes[:]) | |
90 | ||
91 | return publicKey, privateKey, nil | |
92 | } | |
93 | ||
94 | // Sign signs the message with privateKey and returns a signature. It will | |
95 | // panic if len(privateKey) is not PrivateKeySize. | |
96 | func Sign(privateKey PrivateKey, message []byte) []byte { | |
97 | if l := len(privateKey); l != PrivateKeySize { | |
98 | panic("ed25519: bad private key length: " + strconv.Itoa(l)) | |
99 | } | |
100 | ||
101 | h := sha512.New() | |
102 | h.Write(privateKey[:32]) | |
103 | ||
104 | var digest1, messageDigest, hramDigest [64]byte | |
105 | var expandedSecretKey [32]byte | |
106 | h.Sum(digest1[:0]) | |
107 | copy(expandedSecretKey[:], digest1[:]) | |
108 | expandedSecretKey[0] &= 248 | |
109 | expandedSecretKey[31] &= 63 | |
110 | expandedSecretKey[31] |= 64 | |
111 | ||
112 | h.Reset() | |
113 | h.Write(digest1[32:]) | |
114 | h.Write(message) | |
115 | h.Sum(messageDigest[:0]) | |
116 | ||
117 | var messageDigestReduced [32]byte | |
118 | edwards25519.ScReduce(&messageDigestReduced, &messageDigest) | |
119 | var R edwards25519.ExtendedGroupElement | |
120 | edwards25519.GeScalarMultBase(&R, &messageDigestReduced) | |
121 | ||
122 | var encodedR [32]byte | |
123 | R.ToBytes(&encodedR) | |
124 | ||
125 | h.Reset() | |
126 | h.Write(encodedR[:]) | |
127 | h.Write(privateKey[32:]) | |
128 | h.Write(message) | |
129 | h.Sum(hramDigest[:0]) | |
130 | var hramDigestReduced [32]byte | |
131 | edwards25519.ScReduce(&hramDigestReduced, &hramDigest) | |
132 | ||
133 | var s [32]byte | |
134 | edwards25519.ScMulAdd(&s, &hramDigestReduced, &expandedSecretKey, &messageDigestReduced) | |
135 | ||
136 | signature := make([]byte, SignatureSize) | |
137 | copy(signature[:], encodedR[:]) | |
138 | copy(signature[32:], s[:]) | |
139 | ||
140 | return signature | |
141 | } | |
142 | ||
143 | // Verify reports whether sig is a valid signature of message by publicKey. It | |
144 | // will panic if len(publicKey) is not PublicKeySize. | |
145 | func Verify(publicKey PublicKey, message, sig []byte) bool { | |
146 | if l := len(publicKey); l != PublicKeySize { | |
147 | panic("ed25519: bad public key length: " + strconv.Itoa(l)) | |
148 | } | |
149 | ||
150 | if len(sig) != SignatureSize || sig[63]&224 != 0 { | |
151 | return false | |
152 | } | |
153 | ||
154 | var A edwards25519.ExtendedGroupElement | |
155 | var publicKeyBytes [32]byte | |
156 | copy(publicKeyBytes[:], publicKey) | |
157 | if !A.FromBytes(&publicKeyBytes) { | |
158 | return false | |
159 | } | |
160 | edwards25519.FeNeg(&A.X, &A.X) | |
161 | edwards25519.FeNeg(&A.T, &A.T) | |
162 | ||
163 | h := sha512.New() | |
164 | h.Write(sig[:32]) | |
165 | h.Write(publicKey[:]) | |
166 | h.Write(message) | |
167 | var digest [64]byte | |
168 | h.Sum(digest[:0]) | |
169 | ||
170 | var hReduced [32]byte | |
171 | edwards25519.ScReduce(&hReduced, &digest) | |
172 | ||
173 | var R edwards25519.ProjectiveGroupElement | |
174 | var b [32]byte | |
175 | copy(b[:], sig[32:]) | |
176 | edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &b) | |
177 | ||
178 | var checkR [32]byte | |
179 | R.ToBytes(&checkR) | |
180 | return subtle.ConstantTimeCompare(sig[:32], checkR[:]) == 1 | |
181 | } |