1// Copyright 2017 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
7package boring
8
9// #include "goboringcrypto.h"
10import "C"
11import (
12	"errors"
13	"runtime"
14)
15
16type ecdsaSignature struct {
17	R, S BigInt
18}
19
20type PrivateKeyECDSA struct {
21	key *C.GO_EC_KEY
22}
23
24func (k *PrivateKeyECDSA) finalize() {
25	C._goboringcrypto_EC_KEY_free(k.key)
26}
27
28type PublicKeyECDSA struct {
29	key *C.GO_EC_KEY
30}
31
32func (k *PublicKeyECDSA) finalize() {
33	C._goboringcrypto_EC_KEY_free(k.key)
34}
35
36var errUnknownCurve = errors.New("boringcrypto: unknown elliptic curve")
37
38func curveNID(curve string) (C.int, error) {
39	switch curve {
40	case "P-224":
41		return C.GO_NID_secp224r1, nil
42	case "P-256":
43		return C.GO_NID_X9_62_prime256v1, nil
44	case "P-384":
45		return C.GO_NID_secp384r1, nil
46	case "P-521":
47		return C.GO_NID_secp521r1, nil
48	}
49	return 0, errUnknownCurve
50}
51
52func NewPublicKeyECDSA(curve string, X, Y BigInt) (*PublicKeyECDSA, error) {
53	key, err := newECKey(curve, X, Y)
54	if err != nil {
55		return nil, err
56	}
57	k := &PublicKeyECDSA{key}
58	// Note: Because of the finalizer, any time k.key is passed to cgo,
59	// that call must be followed by a call to runtime.KeepAlive(k),
60	// to make sure k is not collected (and finalized) before the cgo
61	// call returns.
62	runtime.SetFinalizer(k, (*PublicKeyECDSA).finalize)
63	return k, nil
64}
65
66func newECKey(curve string, X, Y BigInt) (*C.GO_EC_KEY, error) {
67	nid, err := curveNID(curve)
68	if err != nil {
69		return nil, err
70	}
71	key := C._goboringcrypto_EC_KEY_new_by_curve_name(nid)
72	if key == nil {
73		return nil, fail("EC_KEY_new_by_curve_name")
74	}
75	group := C._goboringcrypto_EC_KEY_get0_group(key)
76	pt := C._goboringcrypto_EC_POINT_new(group)
77	if pt == nil {
78		C._goboringcrypto_EC_KEY_free(key)
79		return nil, fail("EC_POINT_new")
80	}
81	bx := bigToBN(X)
82	by := bigToBN(Y)
83	ok := bx != nil && by != nil && C._goboringcrypto_EC_POINT_set_affine_coordinates_GFp(group, pt, bx, by, nil) != 0 &&
84		C._goboringcrypto_EC_KEY_set_public_key(key, pt) != 0
85	if bx != nil {
86		C._goboringcrypto_BN_free(bx)
87	}
88	if by != nil {
89		C._goboringcrypto_BN_free(by)
90	}
91	C._goboringcrypto_EC_POINT_free(pt)
92	if !ok {
93		C._goboringcrypto_EC_KEY_free(key)
94		return nil, fail("EC_POINT_set_affine_coordinates_GFp")
95	}
96	return key, nil
97}
98
99func NewPrivateKeyECDSA(curve string, X, Y BigInt, D BigInt) (*PrivateKeyECDSA, error) {
100	key, err := newECKey(curve, X, Y)
101	if err != nil {
102		return nil, err
103	}
104	bd := bigToBN(D)
105	ok := bd != nil && C._goboringcrypto_EC_KEY_set_private_key(key, bd) != 0
106	if bd != nil {
107		C._goboringcrypto_BN_free(bd)
108	}
109	if !ok {
110		C._goboringcrypto_EC_KEY_free(key)
111		return nil, fail("EC_KEY_set_private_key")
112	}
113	k := &PrivateKeyECDSA{key}
114	// Note: Because of the finalizer, any time k.key is passed to cgo,
115	// that call must be followed by a call to runtime.KeepAlive(k),
116	// to make sure k is not collected (and finalized) before the cgo
117	// call returns.
118	runtime.SetFinalizer(k, (*PrivateKeyECDSA).finalize)
119	return k, nil
120}
121
122func SignMarshalECDSA(priv *PrivateKeyECDSA, hash []byte) ([]byte, error) {
123	size := C._goboringcrypto_ECDSA_size(priv.key)
124	sig := make([]byte, size)
125	var sigLen C.uint
126	if C._goboringcrypto_ECDSA_sign(0, base(hash), C.size_t(len(hash)), base(sig), &sigLen, priv.key) == 0 {
127		return nil, fail("ECDSA_sign")
128	}
129	runtime.KeepAlive(priv)
130	return sig[:sigLen], nil
131}
132
133func VerifyECDSA(pub *PublicKeyECDSA, hash []byte, sig []byte) bool {
134	ok := C._goboringcrypto_ECDSA_verify(0, base(hash), C.size_t(len(hash)), base(sig), C.size_t(len(sig)), pub.key) != 0
135	runtime.KeepAlive(pub)
136	return ok
137}
138
139func GenerateKeyECDSA(curve string) (X, Y, D BigInt, err error) {
140	nid, err := curveNID(curve)
141	if err != nil {
142		return nil, nil, nil, err
143	}
144	key := C._goboringcrypto_EC_KEY_new_by_curve_name(nid)
145	if key == nil {
146		return nil, nil, nil, fail("EC_KEY_new_by_curve_name")
147	}
148	defer C._goboringcrypto_EC_KEY_free(key)
149	if C._goboringcrypto_EC_KEY_generate_key_fips(key) == 0 {
150		return nil, nil, nil, fail("EC_KEY_generate_key_fips")
151	}
152	group := C._goboringcrypto_EC_KEY_get0_group(key)
153	pt := C._goboringcrypto_EC_KEY_get0_public_key(key)
154	bd := C._goboringcrypto_EC_KEY_get0_private_key(key)
155	if pt == nil || bd == nil {
156		return nil, nil, nil, fail("EC_KEY_get0_private_key")
157	}
158	bx := C._goboringcrypto_BN_new()
159	if bx == nil {
160		return nil, nil, nil, fail("BN_new")
161	}
162	defer C._goboringcrypto_BN_free(bx)
163	by := C._goboringcrypto_BN_new()
164	if by == nil {
165		return nil, nil, nil, fail("BN_new")
166	}
167	defer C._goboringcrypto_BN_free(by)
168	if C._goboringcrypto_EC_POINT_get_affine_coordinates_GFp(group, pt, bx, by, nil) == 0 {
169		return nil, nil, nil, fail("EC_POINT_get_affine_coordinates_GFp")
170	}
171	return bnToBig(bx), bnToBig(by), bnToBig(bd), nil
172}
173