From: Russ Cox Date: Tue, 16 Aug 2022 14:37:49 +0000 (-0400) Subject: crypto/ecdh: add boringcrypto support X-Git-Tag: go1.20rc1~307 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=d62f8d5f2becb765c6c6947bfc1858ced43c1cbb;p=gostls13.git crypto/ecdh: add boringcrypto support Update crypto/ecdh to use boringcrypto when enabled. Change-Id: Idd0ce06a22b1a62289b383c46893800621c7d97b Reviewed-on: https://go-review.googlesource.com/c/go/+/423363 Run-TryBot: Russ Cox Auto-Submit: Russ Cox Reviewed-by: Cherry Mui Reviewed-by: Filippo Valsorda TryBot-Result: Gopher Robot --- diff --git a/src/crypto/ecdh/ecdh.go b/src/crypto/ecdh/ecdh.go index 74e198222c..73a5c68d50 100644 --- a/src/crypto/ecdh/ecdh.go +++ b/src/crypto/ecdh/ecdh.go @@ -8,6 +8,7 @@ package ecdh import ( "crypto" + "crypto/internal/boring" "crypto/subtle" "io" "sync" @@ -64,6 +65,7 @@ type Curve interface { type PublicKey struct { curve Curve publicKey []byte + boring *boring.PublicKeyECDH } // Bytes returns a copy of the encoding of the public key. @@ -98,6 +100,7 @@ func (k *PublicKey) Curve() Curve { type PrivateKey struct { curve Curve privateKey []byte + boring *boring.PrivateKeyECDH // publicKey is set under publicKeyOnce, to allow loading private keys with // NewPrivateKey without having to perform a scalar multiplication. publicKey *PublicKey @@ -134,7 +137,23 @@ func (k *PrivateKey) Curve() Curve { func (k *PrivateKey) PublicKey() *PublicKey { k.publicKeyOnce.Do(func() { - k.publicKey = k.curve.privateKeyToPublicKey(k) + if k.boring != nil { + // Because we already checked in NewPrivateKey that the key is valid, + // there should not be any possible errors from BoringCrypto, + // so we turn the error into a panic. + // (We can't return it anyhow.) + kpub, err := k.boring.PublicKey() + if err != nil { + panic("boringcrypto: " + err.Error()) + } + k.publicKey = &PublicKey{ + curve: k.curve, + publicKey: kpub.Bytes(), + boring: kpub, + } + } else { + k.publicKey = k.curve.privateKeyToPublicKey(k) + } }) return k.publicKey } diff --git a/src/crypto/ecdh/ecdh_test.go b/src/crypto/ecdh/ecdh_test.go index 947eef1ef1..0846268c45 100644 --- a/src/crypto/ecdh/ecdh_test.go +++ b/src/crypto/ecdh/ecdh_test.go @@ -10,6 +10,7 @@ import ( "crypto/cipher" "crypto/ecdh" "crypto/rand" + "crypto/sha256" "encoding/hex" "fmt" "internal/testenv" @@ -173,7 +174,8 @@ func TestVectors(t *testing.T) { t.Fatal(err) } if !bytes.Equal(secret, hexDecode(t, v.SharedSecret)) { - t.Error("shared secret does not match") + t.Errorf("shared secret does not match: %x %x %s %x", secret, sha256.Sum256(secret), v.SharedSecret, + sha256.Sum256(hexDecode(t, v.SharedSecret))) } }) } @@ -285,6 +287,8 @@ func TestNewPrivateKey(t *testing.T) { t.Errorf("unexpectedly accepted %q", input) } else if k != nil { t.Error("PrivateKey was not nil on error") + } else if strings.Contains(err.Error(), "boringcrypto") { + t.Errorf("boringcrypto error leaked out: %v", err) } } }) @@ -344,6 +348,8 @@ func TestNewPublicKey(t *testing.T) { t.Errorf("unexpectedly accepted %q", input) } else if k != nil { t.Error("PublicKey was not nil on error") + } else if strings.Contains(err.Error(), "boringcrypto") { + t.Errorf("boringcrypto error leaked out: %v", err) } } }) diff --git a/src/crypto/ecdh/nist.go b/src/crypto/ecdh/nist.go index c5d37b5fb2..6d30b7bbb2 100644 --- a/src/crypto/ecdh/nist.go +++ b/src/crypto/ecdh/nist.go @@ -5,6 +5,7 @@ package ecdh import ( + "crypto/internal/boring" "crypto/internal/nistec" "crypto/internal/randutil" "encoding/binary" @@ -35,6 +36,14 @@ func (c *nistCurve[Point]) String() string { var errInvalidPrivateKey = errors.New("crypto/ecdh: invalid private key") func (c *nistCurve[Point]) GenerateKey(rand io.Reader) (*PrivateKey, error) { + if boring.Enabled && rand == boring.RandReader { + key, bytes, err := boring.GenerateKeyECDH(c.name) + if err != nil { + return nil, err + } + return newBoringPrivateKey(c, key, bytes) + } + key := make([]byte, len(c.scalarOrder)) randutil.MaybeReadByte(rand) for { @@ -70,13 +79,31 @@ func (c *nistCurve[Point]) NewPrivateKey(key []byte) (*PrivateKey, error) { if isZero(key) || !isLess(key, c.scalarOrder) { return nil, errInvalidPrivateKey } - return &PrivateKey{ + if boring.Enabled { + bk, err := boring.NewPrivateKeyECDH(c.name, key) + if err != nil { + return nil, err + } + return newBoringPrivateKey(c, bk, key) + } + k := &PrivateKey{ curve: c, privateKey: append([]byte{}, key...), - }, nil + } + return k, nil +} + +func newBoringPrivateKey(c Curve, bk *boring.PrivateKeyECDH, privateKey []byte) (*PrivateKey, error) { + k := &PrivateKey{ + curve: c, + boring: bk, + privateKey: append([]byte(nil), privateKey...), + } + return k, nil } func (c *nistCurve[Point]) privateKeyToPublicKey(key *PrivateKey) *PublicKey { + boring.Unreachable() if key.curve != c { panic("crypto/ecdh: internal error: converting the wrong key type") } @@ -142,15 +169,23 @@ func (c *nistCurve[Point]) NewPublicKey(key []byte) (*PublicKey, error) { if len(key) == 0 || key[0] != 4 { return nil, errors.New("crypto/ecdh: invalid public key") } - // SetBytes also checks that the point is on the curve. - if _, err := c.newPoint().SetBytes(key); err != nil { - return nil, err - } - - return &PublicKey{ + k := &PublicKey{ curve: c, publicKey: append([]byte{}, key...), - }, nil + } + if boring.Enabled { + bk, err := boring.NewPublicKeyECDH(c.name, k.publicKey) + if err != nil { + return nil, err + } + k.boring = bk + } else { + // SetBytes also checks that the point is on the curve. + if _, err := c.newPoint().SetBytes(key); err != nil { + return nil, err + } + } + return k, nil } func (c *nistCurve[Point]) ECDH(local *PrivateKey, remote *PublicKey) ([]byte, error) { @@ -160,6 +195,12 @@ func (c *nistCurve[Point]) ECDH(local *PrivateKey, remote *PublicKey) ([]byte, e // at infinity, but in a prime order group such as the NIST curves that can // only be the result of a scalar multiplication if one of the inputs is the // zero scalar or the point at infinity. + + if boring.Enabled { + return boring.ECDH(local.boring, remote.boring) + } + + boring.Unreachable() p, err := c.newPoint().SetBytes(remote.publicKey) if err != nil { return nil, err diff --git a/src/crypto/internal/boring/boring.go b/src/crypto/internal/boring/boring.go index d6b8e37b72..7ea438d3a7 100644 --- a/src/crypto/internal/boring/boring.go +++ b/src/crypto/internal/boring/boring.go @@ -71,6 +71,10 @@ func bigToBN(x BigInt) *C.GO_BIGNUM { return C._goboringcrypto_BN_le2bn(wbase(x), C.size_t(len(x)*wordBytes), nil) } +func bytesToBN(x []byte) *C.GO_BIGNUM { + return C._goboringcrypto_BN_bin2bn((*C.uint8_t)(&x[0]), C.size_t(len(x)), nil) +} + func bnToBig(bn *C.GO_BIGNUM) BigInt { x := make(BigInt, (C._goboringcrypto_BN_num_bytes(bn)+wordBytes-1)/wordBytes) if C._goboringcrypto_BN_bn2le_padded(wbase(x), C.size_t(len(x)*wordBytes), bn) == 0 { diff --git a/src/crypto/internal/boring/build-goboring.sh b/src/crypto/internal/boring/build-goboring.sh index 5c0b74e074..4938b5eac3 100755 --- a/src/crypto/internal/boring/build-goboring.sh +++ b/src/crypto/internal/boring/build-goboring.sh @@ -55,7 +55,7 @@ BEGIN { /\/\*unchecked/ { next } # Check enum values. -!enum && $1 == "enum" && $NF == "{" { +!enum && ($1 == "enum" || $2 == "enum") && $NF == "{" { enum = 1 next } @@ -63,6 +63,10 @@ enum && $1 == "};" { enum = 0 next } +enum && /^}.*;$/ { + enum = 0 + next +} enum && NF == 3 && $2 == "=" { name = $1 sub(/^GO_/, "", name) @@ -111,7 +115,7 @@ EOF cat >boringh.awk <<'EOF' /^\/\/ #include/ {sub(/\/\//, ""); print > "goboringcrypto0.h"; next} /typedef struct|enum ([a-z_]+ )?{|^[ \t]/ {print >"goboringcrypto1.h";next} -{gsub(/GO_/, ""); gsub(/enum go_/, "enum "); print >"goboringcrypto1.h"} +{gsub(/GO_/, ""); gsub(/enum go_/, "enum "); gsub(/go_point_conv/, "point_conv"); print >"goboringcrypto1.h"} EOF awk -f boringx.awk goboringcrypto.h # writes goboringcrypto.x diff --git a/src/crypto/internal/boring/ecdh.go b/src/crypto/internal/boring/ecdh.go new file mode 100644 index 0000000000..8f46d8146f --- /dev/null +++ b/src/crypto/internal/boring/ecdh.go @@ -0,0 +1,224 @@ +// Copyright 2022 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. + +//go:build boringcrypto && linux && (amd64 || arm64) && !android && !cmd_go_bootstrap && !msan + +package boring + +// #include "goboringcrypto.h" +import "C" +import ( + "errors" + "runtime" + "unsafe" +) + +type PublicKeyECDH struct { + curve string + key *C.GO_EC_POINT + group *C.GO_EC_GROUP + bytes []byte +} + +func (k *PublicKeyECDH) finalize() { + C._goboringcrypto_EC_POINT_free(k.key) +} + +type PrivateKeyECDH struct { + curve string + key *C.GO_EC_KEY +} + +func (k *PrivateKeyECDH) finalize() { + C._goboringcrypto_EC_KEY_free(k.key) +} + +func NewPublicKeyECDH(curve string, bytes []byte) (*PublicKeyECDH, error) { + if len(bytes) < 1 { + return nil, errors.New("NewPublicKeyECDH: missing key") + } + + nid, err := curveNID(curve) + if err != nil { + return nil, err + } + + group := C._goboringcrypto_EC_GROUP_new_by_curve_name(nid) + if group == nil { + return nil, fail("EC_GROUP_new_by_curve_name") + } + defer C._goboringcrypto_EC_GROUP_free(group) + key := C._goboringcrypto_EC_POINT_new(group) + if key == nil { + return nil, fail("EC_POINT_new") + } + ok := C._goboringcrypto_EC_POINT_oct2point(group, key, (*C.uint8_t)(unsafe.Pointer(&bytes[0])), C.size_t(len(bytes)), nil) != 0 + if !ok { + C._goboringcrypto_EC_POINT_free(key) + return nil, errors.New("point not on curve") + } + + k := &PublicKeyECDH{curve, key, group, append([]byte(nil), bytes...)} + // 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, (*PublicKeyECDH).finalize) + return k, nil +} + +func (k *PublicKeyECDH) Bytes() []byte { return k.bytes } + +func NewPrivateKeyECDH(curve string, bytes []byte) (*PrivateKeyECDH, 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") + } + b := bytesToBN(bytes) + ok := b != nil && C._goboringcrypto_EC_KEY_set_private_key(key, b) != 0 + if b != nil { + C._goboringcrypto_BN_free(b) + } + if !ok { + C._goboringcrypto_EC_KEY_free(key) + return nil, fail("EC_KEY_set_private_key") + } + k := &PrivateKeyECDH{curve, key} + // Note: Same as in NewPublicKeyECDH regarding finalizer and KeepAlive. + runtime.SetFinalizer(k, (*PrivateKeyECDH).finalize) + return k, nil +} + +func (k *PrivateKeyECDH) PublicKey() (*PublicKeyECDH, error) { + defer runtime.KeepAlive(k) + + group := C._goboringcrypto_EC_KEY_get0_group(k.key) + if group == nil { + return nil, fail("EC_KEY_get0_group") + } + kbig := C._goboringcrypto_EC_KEY_get0_private_key(k.key) + if kbig == nil { + return nil, fail("EC_KEY_get0_private_key") + } + pt := C._goboringcrypto_EC_POINT_new(group) + if pt == nil { + return nil, fail("EC_POINT_new") + } + if C._goboringcrypto_EC_POINT_mul(group, pt, kbig, nil, nil, nil) == 0 { + C._goboringcrypto_EC_POINT_free(pt) + return nil, fail("EC_POINT_mul") + } + bytes, err := pointBytesECDH(k.curve, group, pt) + if err != nil { + C._goboringcrypto_EC_POINT_free(pt) + return nil, err + } + pub := &PublicKeyECDH{k.curve, pt, group, bytes} + // Note: Same as in NewPublicKeyECDH regarding finalizer and KeepAlive. + runtime.SetFinalizer(pub, (*PublicKeyECDH).finalize) + return pub, nil +} + +func pointBytesECDH(curve string, group *C.GO_EC_GROUP, pt *C.GO_EC_POINT) ([]byte, error) { + out := make([]byte, 1+2*curveSize(curve)) + 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) + if int(n) != len(out) { + return nil, fail("EC_POINT_point2oct") + } + return out, nil +} + +func ECDH(priv *PrivateKeyECDH, pub *PublicKeyECDH) ([]byte, error) { + group := C._goboringcrypto_EC_KEY_get0_group(priv.key) + if group == nil { + return nil, fail("EC_KEY_get0_group") + } + privBig := C._goboringcrypto_EC_KEY_get0_private_key(priv.key) + if privBig == nil { + return nil, fail("EC_KEY_get0_private_key") + } + pt := C._goboringcrypto_EC_POINT_new(group) + if pt == nil { + return nil, fail("EC_POINT_new") + } + defer C._goboringcrypto_EC_POINT_free(pt) + if C._goboringcrypto_EC_POINT_mul(group, pt, nil, pub.key, privBig, nil) == 0 { + return nil, fail("EC_POINT_mul") + } + out, err := xCoordBytesECDH(priv.curve, group, pt) + if err != nil { + return nil, err + } + return out, nil +} + +func xCoordBytesECDH(curve string, group *C.GO_EC_GROUP, pt *C.GO_EC_POINT) ([]byte, error) { + big := C._goboringcrypto_BN_new() + defer C._goboringcrypto_BN_free(big) + if C._goboringcrypto_EC_POINT_get_affine_coordinates_GFp(group, pt, big, nil, nil) == 0 { + return nil, fail("EC_POINT_get_affine_coordinates_GFp") + } + return bigBytesECDH(curve, big) +} + +func bigBytesECDH(curve string, big *C.GO_BIGNUM) ([]byte, error) { + out := make([]byte, curveSize(curve)) + if C._goboringcrypto_BN_bn2bin_padded((*C.uint8_t)(&out[0]), C.size_t(len(out)), big) == 0 { + return nil, fail("BN_bn2bin_padded") + } + return out, nil +} + +func curveSize(curve string) int { + switch curve { + default: + panic("crypto/internal/boring: unknown curve " + curve) + case "P-256": + return 256 / 8 + case "P-384": + return 384 / 8 + case "P-521": + return (521 + 7) / 8 + } +} + +func GenerateKeyECDH(curve string) (*PrivateKeyECDH, []byte, error) { + nid, err := curveNID(curve) + if err != nil { + return nil, nil, err + } + key := C._goboringcrypto_EC_KEY_new_by_curve_name(nid) + if key == nil { + return nil, nil, fail("EC_KEY_new_by_curve_name") + } + if C._goboringcrypto_EC_KEY_generate_key_fips(key) == 0 { + C._goboringcrypto_EC_KEY_free(key) + return nil, nil, fail("EC_KEY_generate_key_fips") + } + + group := C._goboringcrypto_EC_KEY_get0_group(key) + if group == nil { + C._goboringcrypto_EC_KEY_free(key) + return nil, nil, fail("EC_KEY_get0_group") + } + b := C._goboringcrypto_EC_KEY_get0_private_key(key) + if b == nil { + C._goboringcrypto_EC_KEY_free(key) + return nil, nil, fail("EC_KEY_get0_private_key") + } + bytes, err := bigBytesECDH(curve, b) + if err != nil { + C._goboringcrypto_EC_KEY_free(key) + return nil, nil, err + } + + k := &PrivateKeyECDH{curve, key} + // Note: Same as in NewPublicKeyECDH regarding finalizer and KeepAlive. + runtime.SetFinalizer(k, (*PrivateKeyECDH).finalize) + return k, bytes, nil +} diff --git a/src/crypto/internal/boring/goboringcrypto.h b/src/crypto/internal/boring/goboringcrypto.h index a5f4e0aac2..2b11049728 100644 --- a/src/crypto/internal/boring/goboringcrypto.h +++ b/src/crypto/internal/boring/goboringcrypto.h @@ -144,6 +144,7 @@ GO_BIGNUM* _goboringcrypto_BN_bin2bn(const uint8_t*, size_t, GO_BIGNUM*); GO_BIGNUM* _goboringcrypto_BN_le2bn(const uint8_t*, size_t, GO_BIGNUM*); size_t _goboringcrypto_BN_bn2bin(const GO_BIGNUM*, uint8_t*); int _goboringcrypto_BN_bn2le_padded(uint8_t*, size_t, const GO_BIGNUM*); +int _goboringcrypto_BN_bn2bin_padded(uint8_t*, size_t, const GO_BIGNUM*); // #include /*unchecked (opaque)*/ typedef struct GO_EC_GROUP { char data[1]; } GO_EC_GROUP; @@ -152,9 +153,21 @@ void _goboringcrypto_EC_GROUP_free(GO_EC_GROUP*); /*unchecked (opaque)*/ typedef struct GO_EC_POINT { char data[1]; } GO_EC_POINT; GO_EC_POINT* _goboringcrypto_EC_POINT_new(const GO_EC_GROUP*); +int _goboringcrypto_EC_POINT_mul(const GO_EC_GROUP*, GO_EC_POINT*, const GO_BIGNUM*, const GO_EC_POINT*, const GO_BIGNUM*, GO_BN_CTX*); void _goboringcrypto_EC_POINT_free(GO_EC_POINT*); int _goboringcrypto_EC_POINT_get_affine_coordinates_GFp(const GO_EC_GROUP*, const GO_EC_POINT*, GO_BIGNUM*, GO_BIGNUM*, GO_BN_CTX*); int _goboringcrypto_EC_POINT_set_affine_coordinates_GFp(const GO_EC_GROUP*, GO_EC_POINT*, const GO_BIGNUM*, const GO_BIGNUM*, GO_BN_CTX*); +int _goboringcrypto_EC_POINT_oct2point(const GO_EC_GROUP*, GO_EC_POINT*, const uint8_t*, size_t, GO_BN_CTX*); +GO_EC_POINT* _goboringcrypto_EC_POINT_dup(const GO_EC_POINT*, const GO_EC_GROUP*); +int _goboringcrypto_EC_POINT_is_on_curve(const GO_EC_GROUP*, const GO_EC_POINT*, GO_BN_CTX*); +#ifndef OPENSSL_HEADER_EC_H +typedef enum { + GO_POINT_CONVERSION_COMPRESSED = 2, + GO_POINT_CONVERSION_UNCOMPRESSED = 4, + GO_POINT_CONVERSION_HYBRID = 6, +} go_point_conversion_form_t; +#endif +size_t _goboringcrypto_EC_POINT_point2oct(const GO_EC_GROUP*, const GO_EC_POINT*, go_point_conversion_form_t, uint8_t*, size_t, GO_BN_CTX*); // #include /*unchecked (opaque)*/ typedef struct GO_EC_KEY { char data[1]; } GO_EC_KEY; @@ -170,6 +183,9 @@ const GO_BIGNUM* _goboringcrypto_EC_KEY_get0_private_key(const GO_EC_KEY*); const GO_EC_POINT* _goboringcrypto_EC_KEY_get0_public_key(const GO_EC_KEY*); // TODO: EC_KEY_check_fips? +// #include +int _goboringcrypto_ECDH_compute_key_fips(uint8_t*, size_t, const GO_EC_POINT*, const GO_EC_KEY*); + // #include typedef struct GO_ECDSA_SIG { char data[16]; } GO_ECDSA_SIG; GO_ECDSA_SIG* _goboringcrypto_ECDSA_SIG_new(void); diff --git a/src/crypto/internal/boring/notboring.go b/src/crypto/internal/boring/notboring.go index 2fa5eaf30f..1c5e4c742d 100644 --- a/src/crypto/internal/boring/notboring.go +++ b/src/crypto/internal/boring/notboring.go @@ -110,3 +110,13 @@ func VerifyRSAPKCS1v15(pub *PublicKeyRSA, h crypto.Hash, hashed, sig []byte) err func VerifyRSAPSS(pub *PublicKeyRSA, h crypto.Hash, hashed, sig []byte, saltLen int) error { panic("boringcrypto: not available") } + +type PublicKeyECDH struct{} +type PrivateKeyECDH struct{} + +func ECDH(*PrivateKeyECDH, *PublicKeyECDH) ([]byte, error) { panic("boringcrypto: not available") } +func GenerateKeyECDH(string) (*PrivateKeyECDH, []byte, error) { panic("boringcrypto: not available") } +func NewPrivateKeyECDH(string, []byte) (*PrivateKeyECDH, error) { panic("boringcrypto: not available") } +func NewPublicKeyECDH(string, []byte) (*PublicKeyECDH, error) { panic("boringcrypto: not available") } +func (*PublicKeyECDH) Bytes() []byte { panic("boringcrypto: not available") } +func (*PrivateKeyECDH) PublicKey() (*PublicKeyECDH, error) { panic("boringcrypto: not available") } diff --git a/src/crypto/internal/boring/syso/goboringcrypto_linux_amd64.syso b/src/crypto/internal/boring/syso/goboringcrypto_linux_amd64.syso index fd982bff9d..6cea789355 100644 Binary files a/src/crypto/internal/boring/syso/goboringcrypto_linux_amd64.syso and b/src/crypto/internal/boring/syso/goboringcrypto_linux_amd64.syso differ diff --git a/src/crypto/internal/boring/syso/goboringcrypto_linux_arm64.syso b/src/crypto/internal/boring/syso/goboringcrypto_linux_arm64.syso index 46520b7592..9659aa1a5e 100644 Binary files a/src/crypto/internal/boring/syso/goboringcrypto_linux_arm64.syso and b/src/crypto/internal/boring/syso/goboringcrypto_linux_arm64.syso differ diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index 25556ac04c..dea9935c12 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -395,19 +395,37 @@ var depsRules = ` < crypto < crypto/subtle < crypto/internal/alias - < crypto/internal/randutil - < crypto/internal/nistec/fiat - < crypto/internal/nistec - < crypto/internal/edwards25519/field - < crypto/internal/edwards25519, crypto/ecdh < crypto/cipher; crypto/cipher, crypto/internal/boring/bcache < crypto/internal/boring - < crypto/boring + < crypto/boring; + + crypto/internal/alias + < crypto/internal/randutil + < crypto/internal/nistec/fiat + < crypto/internal/nistec + < crypto/internal/edwards25519/field + < crypto/internal/edwards25519; + + crypto/boring < crypto/aes, crypto/des, crypto/hmac, crypto/md5, crypto/rc4, - crypto/sha1, crypto/sha256, crypto/sha512 + crypto/sha1, crypto/sha256, crypto/sha512; + + crypto/boring, crypto/internal/edwards25519/field + < crypto/ecdh; + + crypto/aes, + crypto/des, + crypto/ecdh, + crypto/hmac, + crypto/internal/edwards25519, + crypto/md5, + crypto/rc4, + crypto/sha1, + crypto/sha256, + crypto/sha512 < CRYPTO; CGO, fmt, net !< CRYPTO;