ecdh.mx raw
1 // Copyright 2022 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 //go:build boringcrypto && linux && (amd64 || arm64) && !android && !msan
6
7 package boring
8
9 // #include "goboringcrypto.h"
10 import "C"
11 import (
12 "errors"
13 "runtime"
14 "unsafe"
15 )
16
17 type PublicKeyECDH struct {
18 curve string
19 key *C.GO_EC_POINT
20 bytes []byte
21 }
22
23 func (k *PublicKeyECDH) finalize() {
24 C._goboringcrypto_EC_POINT_free(k.key)
25 }
26
27 type PrivateKeyECDH struct {
28 curve string
29 key *C.GO_EC_KEY
30 }
31
32 func (k *PrivateKeyECDH) finalize() {
33 C._goboringcrypto_EC_KEY_free(k.key)
34 }
35
36 func NewPublicKeyECDH(curve string, bytes []byte) (*PublicKeyECDH, error) {
37 if len(bytes) != 1+2*curveSize(curve) {
38 return nil, errors.New("NewPublicKeyECDH: wrong key length")
39 }
40
41 nid, err := curveNID(curve)
42 if err != nil {
43 return nil, err
44 }
45
46 group := C._goboringcrypto_EC_GROUP_new_by_curve_name(nid)
47 if group == nil {
48 return nil, fail("EC_GROUP_new_by_curve_name")
49 }
50 defer C._goboringcrypto_EC_GROUP_free(group)
51 key := C._goboringcrypto_EC_POINT_new(group)
52 if key == nil {
53 return nil, fail("EC_POINT_new")
54 }
55 ok := C._goboringcrypto_EC_POINT_oct2point(group, key, (*C.uint8_t)(unsafe.Pointer(&bytes[0])), C.size_t(len(bytes)), nil) != 0
56 if !ok {
57 C._goboringcrypto_EC_POINT_free(key)
58 return nil, errors.New("point not on curve")
59 }
60
61 k := &PublicKeyECDH{curve, key, append([]byte(nil), bytes...)}
62 // Note: Because of the finalizer, any time k.key is passed to cgo,
63 // that call must be followed by a call to runtime.KeepAlive(k),
64 // to make sure k is not collected (and finalized) before the cgo
65 // call returns.
66 runtime.SetFinalizer(k, (*PublicKeyECDH).finalize)
67 return k, nil
68 }
69
70 func (k *PublicKeyECDH) Bytes() []byte { return k.bytes }
71
72 func NewPrivateKeyECDH(curve string, bytes []byte) (*PrivateKeyECDH, error) {
73 if len(bytes) != curveSize(curve) {
74 return nil, errors.New("NewPrivateKeyECDH: wrong key length")
75 }
76
77 nid, err := curveNID(curve)
78 if err != nil {
79 return nil, err
80 }
81 key := C._goboringcrypto_EC_KEY_new_by_curve_name(nid)
82 if key == nil {
83 return nil, fail("EC_KEY_new_by_curve_name")
84 }
85 b := bytesToBN(bytes)
86 ok := b != nil && C._goboringcrypto_EC_KEY_set_private_key(key, b) != 0
87 if b != nil {
88 C._goboringcrypto_BN_free(b)
89 }
90 if !ok {
91 C._goboringcrypto_EC_KEY_free(key)
92 return nil, fail("EC_KEY_set_private_key")
93 }
94 k := &PrivateKeyECDH{curve, key}
95 // Note: Same as in NewPublicKeyECDH regarding finalizer and KeepAlive.
96 runtime.SetFinalizer(k, (*PrivateKeyECDH).finalize)
97 return k, nil
98 }
99
100 func (k *PrivateKeyECDH) PublicKey() (*PublicKeyECDH, error) {
101 defer runtime.KeepAlive(k)
102
103 group := C._goboringcrypto_EC_KEY_get0_group(k.key)
104 if group == nil {
105 return nil, fail("EC_KEY_get0_group")
106 }
107 kbig := C._goboringcrypto_EC_KEY_get0_private_key(k.key)
108 if kbig == nil {
109 return nil, fail("EC_KEY_get0_private_key")
110 }
111 pt := C._goboringcrypto_EC_POINT_new(group)
112 if pt == nil {
113 return nil, fail("EC_POINT_new")
114 }
115 if C._goboringcrypto_EC_POINT_mul(group, pt, kbig, nil, nil, nil) == 0 {
116 C._goboringcrypto_EC_POINT_free(pt)
117 return nil, fail("EC_POINT_mul")
118 }
119 bytes, err := pointBytesECDH(k.curve, group, pt)
120 if err != nil {
121 C._goboringcrypto_EC_POINT_free(pt)
122 return nil, err
123 }
124 pub := &PublicKeyECDH{k.curve, pt, bytes}
125 // Note: Same as in NewPublicKeyECDH regarding finalizer and KeepAlive.
126 runtime.SetFinalizer(pub, (*PublicKeyECDH).finalize)
127 return pub, nil
128 }
129
130 func pointBytesECDH(curve string, group *C.GO_EC_GROUP, pt *C.GO_EC_POINT) ([]byte, error) {
131 out := []byte{:1+2*curveSize(curve)}
132 n := C._goboringcrypto_EC_POINT_point2oct(group, pt, C.GO_POINT_CONVERSION_UNCOMPRESSED, (*C.uint8_t)(unsafe.Pointer(&out[0])), C.size_t(len(out)), nil)
133 if int(n) != len(out) {
134 return nil, fail("EC_POINT_point2oct")
135 }
136 return out, nil
137 }
138
139 func ECDH(priv *PrivateKeyECDH, pub *PublicKeyECDH) ([]byte, error) {
140 // Make sure priv and pub are not garbage collected while we are in a cgo
141 // call.
142 //
143 // The call to xCoordBytesECDH should prevent priv from being collected, but
144 // include this in case the code is reordered and there is a subsequent call
145 // cgo call after that point.
146 defer runtime.KeepAlive(priv)
147 defer runtime.KeepAlive(pub)
148
149 group := C._goboringcrypto_EC_KEY_get0_group(priv.key)
150 if group == nil {
151 return nil, fail("EC_KEY_get0_group")
152 }
153 privBig := C._goboringcrypto_EC_KEY_get0_private_key(priv.key)
154 if privBig == nil {
155 return nil, fail("EC_KEY_get0_private_key")
156 }
157 pt := C._goboringcrypto_EC_POINT_new(group)
158 if pt == nil {
159 return nil, fail("EC_POINT_new")
160 }
161 defer C._goboringcrypto_EC_POINT_free(pt)
162 if C._goboringcrypto_EC_POINT_mul(group, pt, nil, pub.key, privBig, nil) == 0 {
163 return nil, fail("EC_POINT_mul")
164 }
165 out, err := xCoordBytesECDH(priv.curve, group, pt)
166 if err != nil {
167 return nil, err
168 }
169 return out, nil
170 }
171
172 func xCoordBytesECDH(curve string, group *C.GO_EC_GROUP, pt *C.GO_EC_POINT) ([]byte, error) {
173 big := C._goboringcrypto_BN_new()
174 defer C._goboringcrypto_BN_free(big)
175 if C._goboringcrypto_EC_POINT_get_affine_coordinates_GFp(group, pt, big, nil, nil) == 0 {
176 return nil, fail("EC_POINT_get_affine_coordinates_GFp")
177 }
178 return bigBytesECDH(curve, big)
179 }
180
181 func bigBytesECDH(curve string, big *C.GO_BIGNUM) ([]byte, error) {
182 out := []byte{:curveSize(curve)}
183 if C._goboringcrypto_BN_bn2bin_padded((*C.uint8_t)(&out[0]), C.size_t(len(out)), big) == 0 {
184 return nil, fail("BN_bn2bin_padded")
185 }
186 return out, nil
187 }
188
189 func curveSize(curve string) int {
190 switch curve {
191 default:
192 panic("crypto/internal/boring: unknown curve " + curve)
193 case "P-256":
194 return 256 / 8
195 case "P-384":
196 return 384 / 8
197 case "P-521":
198 return (521 + 7) / 8
199 }
200 }
201
202 func GenerateKeyECDH(curve string) (*PrivateKeyECDH, []byte, error) {
203 nid, err := curveNID(curve)
204 if err != nil {
205 return nil, nil, err
206 }
207 key := C._goboringcrypto_EC_KEY_new_by_curve_name(nid)
208 if key == nil {
209 return nil, nil, fail("EC_KEY_new_by_curve_name")
210 }
211 if C._goboringcrypto_EC_KEY_generate_key_fips(key) == 0 {
212 C._goboringcrypto_EC_KEY_free(key)
213 return nil, nil, fail("EC_KEY_generate_key_fips")
214 }
215
216 group := C._goboringcrypto_EC_KEY_get0_group(key)
217 if group == nil {
218 C._goboringcrypto_EC_KEY_free(key)
219 return nil, nil, fail("EC_KEY_get0_group")
220 }
221 b := C._goboringcrypto_EC_KEY_get0_private_key(key)
222 if b == nil {
223 C._goboringcrypto_EC_KEY_free(key)
224 return nil, nil, fail("EC_KEY_get0_private_key")
225 }
226 bytes, err := bigBytesECDH(curve, b)
227 if err != nil {
228 C._goboringcrypto_EC_KEY_free(key)
229 return nil, nil, err
230 }
231
232 k := &PrivateKeyECDH{curve, key}
233 // Note: Same as in NewPublicKeyECDH regarding finalizer and KeepAlive.
234 runtime.SetFinalizer(k, (*PrivateKeyECDH).finalize)
235 return k, bytes, nil
236 }
237