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 fipsSelfTest()
141 fips140.RecordApproved()
142
143
144
145 for {
146 key := make([]byte, len(c.N))
147 if err := drbg.ReadWithReader(rand, key); err != nil {
148 return nil, err
149 }
150
151
152
153 key[1] ^= 0x42
154
155
156
157 if c.curve == p521 && c.N[0]&0b1111_1110 == 0 {
158 key[0] &= 0b0000_0001
159 }
160
161 privateKey, err := NewPrivateKey(c, key)
162 if err != nil {
163 continue
164 }
165
166
167
168
169
170
171
172
173
174
175 fips140.PCT("ECDH PCT", func() error {
176 p1, err := c.newPoint().ScalarBaseMult(privateKey.d)
177 if err != nil {
178 return err
179 }
180 if !bytes.Equal(p1.Bytes(), privateKey.pub.q) {
181 return errors.New("crypto/ecdh: public key does not match private key")
182 }
183 return nil
184 })
185
186 return privateKey, nil
187 }
188 }
189
190 func NewPrivateKey[P Point[P]](c *Curve[P], key []byte) (*PrivateKey, error) {
191 fipsSelfTest()
192
193
194
195
196 if len(key) != len(c.N) || isZero(key) || !isLess(key, c.N) {
197 return nil, errors.New("crypto/ecdh: invalid private key")
198 }
199
200 p, err := c.newPoint().ScalarBaseMult(key)
201 if err != nil {
202
203
204 panic("crypto/ecdh: internal error: nistec ScalarBaseMult failed for a fixed-size input")
205 }
206
207 publicKey := p.Bytes()
208 if len(publicKey) == 1 {
209
210
211
212 panic("crypto/ecdh: internal error: public key is the identity element")
213 }
214
215 k := &PrivateKey{d: bytes.Clone(key), pub: PublicKey{curve: c.curve, q: publicKey}}
216 return k, nil
217 }
218
219 func NewPublicKey[P Point[P]](c *Curve[P], key []byte) (*PublicKey, error) {
220
221 if len(key) == 0 || key[0] != 4 {
222 return nil, errors.New("crypto/ecdh: invalid public key")
223 }
224
225
226
227
228
229 if _, err := c.newPoint().SetBytes(key); err != nil {
230 return nil, err
231 }
232
233 return &PublicKey{curve: c.curve, q: bytes.Clone(key)}, nil
234 }
235
236 func ECDH[P Point[P]](c *Curve[P], k *PrivateKey, peer *PublicKey) ([]byte, error) {
237 fipsSelfTest()
238 fips140.RecordApproved()
239 return ecdh(c, k, peer)
240 }
241
242 func ecdh[P Point[P]](c *Curve[P], k *PrivateKey, peer *PublicKey) ([]byte, error) {
243 if c.curve != k.pub.curve {
244 return nil, errors.New("crypto/ecdh: mismatched curves")
245 }
246 if k.pub.curve != peer.curve {
247 return nil, errors.New("crypto/ecdh: mismatched curves")
248 }
249
250
251
252
253
254 if len(k.pub.q) == 1 {
255 return nil, errors.New("crypto/ecdh: public key is the identity element")
256 }
257
258
259
260 p, err := c.newPoint().SetBytes(peer.q)
261 if err != nil {
262 return nil, err
263 }
264
265
266 if _, err := p.ScalarMult(p, k.d); err != nil {
267 return nil, err
268 }
269
270
271
272 return p.BytesX()
273 }
274
275
276 func isZero(x []byte) bool {
277 var acc byte
278 for _, b := range x {
279 acc |= b
280 }
281 return acc == 0
282 }
283
284
285
286 func isLess(a, b []byte) bool {
287 if len(a) != len(b) {
288 panic("crypto/ecdh: internal error: mismatched isLess inputs")
289 }
290
291
292
293
294 if len(a) > 72 {
295 panic("crypto/ecdh: internal error: isLess input too large")
296 }
297 bufA, bufB := make([]byte, 72), make([]byte, 72)
298 for i := range a {
299 bufA[i], bufB[i] = a[len(a)-i-1], b[len(b)-i-1]
300 }
301
302
303 var borrow uint64
304 for i := 0; i < len(bufA); i += 8 {
305 limbA, limbB := byteorder.LEUint64(bufA[i:]), byteorder.LEUint64(bufB[i:])
306 _, borrow = bits.Sub64(limbA, limbB, borrow)
307 }
308
309
310 return borrow == 1
311 }
312
View as plain text