1
2
3
4
5 package ecdh
6
7 import (
8 "bytes"
9 "crypto/internal/fips140"
10 "crypto/internal/fips140/drbg"
11 "crypto/internal/fips140/nistec"
12 "crypto/internal/fips140deps/byteorder"
13 "errors"
14 "io"
15 "math/bits"
16 )
17
18
19
20
21
22
23
24
25 type PrivateKey struct {
26 pub PublicKey
27 d []byte
28 }
29
30 func (priv *PrivateKey) Bytes() []byte {
31 return priv.d
32 }
33
34 func (priv *PrivateKey) PublicKey() *PublicKey {
35 return &priv.pub
36 }
37
38 type PublicKey struct {
39 curve curveID
40 q []byte
41 }
42
43 func (pub *PublicKey) Bytes() []byte {
44 return pub.q
45 }
46
47 type curveID string
48
49 const (
50 p224 curveID = "P-224"
51 p256 curveID = "P-256"
52 p384 curveID = "P-384"
53 p521 curveID = "P-521"
54 )
55
56 type Curve[P Point[P]] struct {
57 curve curveID
58 newPoint func() P
59 N []byte
60 }
61
62
63 type Point[P any] interface {
64 *nistec.P224Point | *nistec.P256Point | *nistec.P384Point | *nistec.P521Point
65 Bytes() []byte
66 BytesX() ([]byte, error)
67 SetBytes([]byte) (P, error)
68 ScalarMult(P, []byte) (P, error)
69 ScalarBaseMult([]byte) (P, error)
70 }
71
72 func P224() *Curve[*nistec.P224Point] {
73 return &Curve[*nistec.P224Point]{
74 curve: p224,
75 newPoint: nistec.NewP224Point,
76 N: p224Order,
77 }
78 }
79
80 var p224Order = []byte{
81 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
82 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2,
83 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45,
84 0x5c, 0x5c, 0x2a, 0x3d,
85 }
86
87 func P256() *Curve[*nistec.P256Point] {
88 return &Curve[*nistec.P256Point]{
89 curve: p256,
90 newPoint: nistec.NewP256Point,
91 N: p256Order,
92 }
93 }
94
95 var p256Order = []byte{
96 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
97 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
98 0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17, 0x9e, 0x84,
99 0xf3, 0xb9, 0xca, 0xc2, 0xfc, 0x63, 0x25, 0x51,
100 }
101
102 func P384() *Curve[*nistec.P384Point] {
103 return &Curve[*nistec.P384Point]{
104 curve: p384,
105 newPoint: nistec.NewP384Point,
106 N: p384Order,
107 }
108 }
109
110 var p384Order = []byte{
111 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
112 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
113 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
114 0xc7, 0x63, 0x4d, 0x81, 0xf4, 0x37, 0x2d, 0xdf,
115 0x58, 0x1a, 0x0d, 0xb2, 0x48, 0xb0, 0xa7, 0x7a,
116 0xec, 0xec, 0x19, 0x6a, 0xcc, 0xc5, 0x29, 0x73,
117 }
118
119 func P521() *Curve[*nistec.P521Point] {
120 return &Curve[*nistec.P521Point]{
121 curve: p521,
122 newPoint: nistec.NewP521Point,
123 N: p521Order,
124 }
125 }
126
127 var p521Order = []byte{0x01, 0xff,
128 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
129 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
130 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
131 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfa,
132 0x51, 0x86, 0x87, 0x83, 0xbf, 0x2f, 0x96, 0x6b,
133 0x7f, 0xcc, 0x01, 0x48, 0xf7, 0x09, 0xa5, 0xd0,
134 0x3b, 0xb5, 0xc9, 0xb8, 0x89, 0x9c, 0x47, 0xae,
135 0xbb, 0x6f, 0xb7, 0x1e, 0x91, 0x38, 0x64, 0x09,
136 }
137
138
139 func GenerateKey[P Point[P]](c *Curve[P], rand io.Reader) (*PrivateKey, error) {
140 fips140.RecordApproved()
141
142
143
144 for {
145 key := make([]byte, len(c.N))
146 if err := drbg.ReadWithReader(rand, key); err != nil {
147 return nil, err
148 }
149
150
151
152 key[1] ^= 0x42
153
154
155
156 if c.curve == p521 && c.N[0]&0b1111_1110 == 0 {
157 key[0] &= 0b0000_0001
158 }
159
160 privateKey, err := NewPrivateKey(c, key)
161 if err != nil {
162 continue
163 }
164
165
166
167
168
169
170
171
172
173
174 fips140.PCT("ECDH PCT", func() error {
175 p1, err := c.newPoint().ScalarBaseMult(privateKey.d)
176 if err != nil {
177 return err
178 }
179 if !bytes.Equal(p1.Bytes(), privateKey.pub.q) {
180 return errors.New("crypto/ecdh: public key does not match private key")
181 }
182 return nil
183 })
184
185 return privateKey, nil
186 }
187 }
188
189 func NewPrivateKey[P Point[P]](c *Curve[P], key []byte) (*PrivateKey, error) {
190
191
192
193 if len(key) != len(c.N) || isZero(key) || !isLess(key, c.N) {
194 return nil, errors.New("crypto/ecdh: invalid private key")
195 }
196
197 p, err := c.newPoint().ScalarBaseMult(key)
198 if err != nil {
199
200
201 panic("crypto/ecdh: internal error: nistec ScalarBaseMult failed for a fixed-size input")
202 }
203
204 publicKey := p.Bytes()
205 if len(publicKey) == 1 {
206
207
208
209 panic("crypto/ecdh: internal error: public key is the identity element")
210 }
211
212 k := &PrivateKey{d: bytes.Clone(key), pub: PublicKey{curve: c.curve, q: publicKey}}
213 return k, nil
214 }
215
216 func NewPublicKey[P Point[P]](c *Curve[P], key []byte) (*PublicKey, error) {
217
218 if len(key) == 0 || key[0] != 4 {
219 return nil, errors.New("crypto/ecdh: invalid public key")
220 }
221
222
223
224
225
226 if _, err := c.newPoint().SetBytes(key); err != nil {
227 return nil, err
228 }
229
230 return &PublicKey{curve: c.curve, q: bytes.Clone(key)}, nil
231 }
232
233 func ECDH[P Point[P]](c *Curve[P], k *PrivateKey, peer *PublicKey) ([]byte, error) {
234 fipsSelfTest()
235 fips140.RecordApproved()
236 return ecdh(c, k, peer)
237 }
238
239 func ecdh[P Point[P]](c *Curve[P], k *PrivateKey, peer *PublicKey) ([]byte, error) {
240 if c.curve != k.pub.curve {
241 return nil, errors.New("crypto/ecdh: mismatched curves")
242 }
243 if k.pub.curve != peer.curve {
244 return nil, errors.New("crypto/ecdh: mismatched curves")
245 }
246
247
248
249
250
251 if len(k.pub.q) == 1 {
252 return nil, errors.New("crypto/ecdh: public key is the identity element")
253 }
254
255
256
257 p, err := c.newPoint().SetBytes(peer.q)
258 if err != nil {
259 return nil, err
260 }
261
262
263 if _, err := p.ScalarMult(p, k.d); err != nil {
264 return nil, err
265 }
266
267
268
269 return p.BytesX()
270 }
271
272
273 func isZero(x []byte) bool {
274 var acc byte
275 for _, b := range x {
276 acc |= b
277 }
278 return acc == 0
279 }
280
281
282
283 func isLess(a, b []byte) bool {
284 if len(a) != len(b) {
285 panic("crypto/ecdh: internal error: mismatched isLess inputs")
286 }
287
288
289
290
291 if len(a) > 72 {
292 panic("crypto/ecdh: internal error: isLess input too large")
293 }
294 bufA, bufB := make([]byte, 72), make([]byte, 72)
295 for i := range a {
296 bufA[i], bufB[i] = a[len(a)-i-1], b[len(b)-i-1]
297 }
298
299
300 var borrow uint64
301 for i := 0; i < len(bufA); i += 8 {
302 limbA, limbB := byteorder.LEUint64(bufA[i:]), byteorder.LEUint64(bufB[i:])
303 _, borrow = bits.Sub64(limbA, limbB, borrow)
304 }
305
306
307 return borrow == 1
308 }
309
View as plain text