blob: 4fcba4be7293cd69be61258ade0b45a4cebc8c5a [file] [log] [blame]
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build linux,amd64
// +build !android
// +build !cmd_go_bootstrap
// +build !msan
package boring
// #include "goboringcrypto.h"
import "C"
import (
"encoding/asn1"
"errors"
"math/big"
"runtime"
"unsafe"
)
type ecdsaSignature struct {
R, S *big.Int
}
type PrivateKeyECDSA struct {
key *C.GO_EC_KEY
}
func (k *PrivateKeyECDSA) finalize() {
C._goboringcrypto_EC_KEY_free(k.key)
}
type PublicKeyECDSA struct {
key *C.GO_EC_KEY
}
func (k *PublicKeyECDSA) finalize() {
C._goboringcrypto_EC_KEY_free(k.key)
}
var errUnknownCurve = errors.New("boringcrypto: unknown elliptic curve")
func curveNID(curve string) (C.int, error) {
switch curve {
case "P-224":
return C.GO_NID_secp224r1, nil
case "P-256":
return C.GO_NID_X9_62_prime256v1, nil
case "P-384":
return C.GO_NID_secp384r1, nil
case "P-521":
return C.GO_NID_secp521r1, nil
}
return 0, errUnknownCurve
}
func NewPublicKeyECDSA(curve string, X, Y *big.Int) (*PublicKeyECDSA, error) {
key, err := newECKey(curve, X, Y)
if err != nil {
return nil, err
}
k := &PublicKeyECDSA{key}
// Note: Because of the finalizer, any time k.key is passed to cgo,
// that call must be followed by a call to runtime.KeepAlive(k),
// to make sure k is not collected (and finalized) before the cgo
// call returns.
runtime.SetFinalizer(k, (*PublicKeyECDSA).finalize)
return k, nil
}
func newECKey(curve string, X, Y *big.Int) (*C.GO_EC_KEY, error) {
nid, err := curveNID(curve)
if err != nil {
return nil, err
}
key := C._goboringcrypto_EC_KEY_new_by_curve_name(nid)
if key == nil {
return nil, fail("EC_KEY_new_by_curve_name")
}
group := C._goboringcrypto_EC_KEY_get0_group(key)
pt := C._goboringcrypto_EC_POINT_new(group)
if pt == nil {
C._goboringcrypto_EC_KEY_free(key)
return nil, fail("EC_POINT_new")
}
bx := bigToBN(X)
by := bigToBN(Y)
ok := bx != nil && by != nil && C._goboringcrypto_EC_POINT_set_affine_coordinates_GFp(group, pt, bx, by, nil) != 0 &&
C._goboringcrypto_EC_KEY_set_public_key(key, pt) != 0
if bx != nil {
C._goboringcrypto_BN_free(bx)
}
if by != nil {
C._goboringcrypto_BN_free(by)
}
C._goboringcrypto_EC_POINT_free(pt)
if !ok {
C._goboringcrypto_EC_KEY_free(key)
return nil, fail("EC_POINT_set_affine_coordinates_GFp")
}
return key, nil
}
func NewPrivateKeyECDSA(curve string, X, Y *big.Int, D *big.Int) (*PrivateKeyECDSA, error) {
key, err := newECKey(curve, X, Y)
if err != nil {
return nil, err
}
bd := bigToBN(D)
ok := bd != nil && C._goboringcrypto_EC_KEY_set_private_key(key, bd) != 0
if bd != nil {
C._goboringcrypto_BN_free(bd)
}
if !ok {
C._goboringcrypto_EC_KEY_free(key)
return nil, fail("EC_KEY_set_private_key")
}
k := &PrivateKeyECDSA{key}
// Note: Because of the finalizer, any time k.key is passed to cgo,
// that call must be followed by a call to runtime.KeepAlive(k),
// to make sure k is not collected (and finalized) before the cgo
// call returns.
runtime.SetFinalizer(k, (*PrivateKeyECDSA).finalize)
return k, nil
}
func SignECDSA(priv *PrivateKeyECDSA, hash []byte) (r, s *big.Int, err error) {
// We could use ECDSA_do_sign instead but would need to convert
// the resulting BIGNUMs to *big.Int form. If we're going to do a
// conversion, converting the ASN.1 form is more convenient and
// likely not much more expensive.
sig, err := SignMarshalECDSA(priv, hash)
if err != nil {
return nil, nil, err
}
var esig ecdsaSignature
if _, err := asn1.Unmarshal(sig, &esig); err != nil {
return nil, nil, err
}
return esig.R, esig.S, nil
}
func SignMarshalECDSA(priv *PrivateKeyECDSA, hash []byte) ([]byte, error) {
size := C._goboringcrypto_ECDSA_size(priv.key)
sig := make([]byte, size)
var sigLen C.uint
if C._goboringcrypto_ECDSA_sign(0, base(hash), C.size_t(len(hash)), (*C.uint8_t)(unsafe.Pointer(&sig[0])), &sigLen, priv.key) == 0 {
return nil, fail("ECDSA_sign")
}
runtime.KeepAlive(priv)
return sig[:sigLen], nil
}
func VerifyECDSA(pub *PublicKeyECDSA, hash []byte, r, s *big.Int) bool {
// We could use ECDSA_do_verify instead but would need to convert
// r and s to BIGNUM form. If we're going to do a conversion, marshaling
// to ASN.1 is more convenient and likely not much more expensive.
sig, err := asn1.Marshal(ecdsaSignature{r, s})
if err != nil {
return false
}
ok := C._goboringcrypto_ECDSA_verify(0, base(hash), C.size_t(len(hash)), (*C.uint8_t)(unsafe.Pointer(&sig[0])), C.size_t(len(sig)), pub.key) != 0
runtime.KeepAlive(pub)
return ok
}
func GenerateKeyECDSA(curve string) (X, Y, D *big.Int, err error) {
nid, err := curveNID(curve)
if err != nil {
return nil, nil, nil, err
}
key := C._goboringcrypto_EC_KEY_new_by_curve_name(nid)
if key == nil {
return nil, nil, nil, fail("EC_KEY_new_by_curve_name")
}
defer C._goboringcrypto_EC_KEY_free(key)
if C._goboringcrypto_EC_KEY_generate_key_fips(key) == 0 {
return nil, nil, nil, fail("EC_KEY_generate_key_fips")
}
group := C._goboringcrypto_EC_KEY_get0_group(key)
pt := C._goboringcrypto_EC_KEY_get0_public_key(key)
bd := C._goboringcrypto_EC_KEY_get0_private_key(key)
if pt == nil || bd == nil {
return nil, nil, nil, fail("EC_KEY_get0_private_key")
}
bx := C._goboringcrypto_BN_new()
if bx == nil {
return nil, nil, nil, fail("BN_new")
}
defer C._goboringcrypto_BN_free(bx)
by := C._goboringcrypto_BN_new()
if by == nil {
return nil, nil, nil, fail("BN_new")
}
defer C._goboringcrypto_BN_free(by)
if C._goboringcrypto_EC_POINT_get_affine_coordinates_GFp(group, pt, bx, by, nil) == 0 {
return nil, nil, nil, fail("EC_POINT_get_affine_coordinates_GFp")
}
return bnToBig(bx), bnToBig(by), bnToBig(bd), nil
}