Source file src/crypto/internal/boring/ecdh.go

     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 := make([]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 := make([]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  

View as plain text