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