Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[go1.20-support] Stop using deprecated EC functions for ECDSA #143

Merged
merged 3 commits into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 11 additions & 12 deletions openssl/ecdsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,13 @@ func NewPrivateKeyECDSA(curve string, X, Y BigInt, D BigInt) (*PrivateKeyECDSA,
func HashSignECDSA(priv *PrivateKeyECDSA, hash []byte, h crypto.Hash) (*big.Int, *big.Int, error) {
size := C._goboringcrypto_ECDSA_size(priv.key)
sig := make([]byte, size)
var sigLen C.size_t
var sigLen C.size_t = C.size_t(size)
md := cryptoHashToMD(h)
if md == nil {
panic("boring: invalid hash")
}
if C._goboringcrypto_ECDSA_sign(md, base(hash), C.size_t(len(hash)), (*C.uint8_t)(unsafe.Pointer(&sig[0])), &sigLen, priv.key) == 0 {
if C._goboringcrypto_ECDSA_sign(md, base(hash), C.size_t(len(hash)),
(*C.uint8_t)(unsafe.Pointer(&sig[0])), &sigLen, priv.key) == 0 {
return nil, nil, NewOpenSSLError("ECDSA_sign failed")
}
runtime.KeepAlive(priv)
Expand All @@ -151,18 +152,19 @@ func HashSignECDSA(priv *PrivateKeyECDSA, hash []byte, h crypto.Hash) (*big.Int,
func SignMarshalECDSA(priv *PrivateKeyECDSA, hash []byte) ([]byte, error) {
size := C._goboringcrypto_ECDSA_size(priv.key)
sig := make([]byte, size)
var sigLen C.uint
ok := C._goboringcrypto_internal_ECDSA_sign(0, base(hash), C.size_t(len(hash)), (*C.uint8_t)(unsafe.Pointer(&sig[0])), &sigLen, priv.key) > 0
if !ok {
return nil, NewOpenSSLError(("ECDSA_sign failed"))
var sigLen C.size_t = C.size_t(size)
if C._goboringcrypto_ECDSA_sign_raw(nil, base(hash), C.size_t(len(hash)),
(*C.uint8_t)(unsafe.Pointer(&sig[0])), &sigLen, priv.key) == 0 {
return nil, NewOpenSSLError("ECDSA_sign failed")
}

runtime.KeepAlive(priv)
return sig[:sigLen], nil
}

func VerifyECDSA(pub *PublicKeyECDSA, hash []byte, sig []byte) bool {
ok := C._goboringcrypto_internal_ECDSA_verify(0, base(hash), C.size_t(len(hash)), (*C.uint8_t)(unsafe.Pointer(&sig[0])), C.uint(len(sig)), pub.key) > 0
ok := C._goboringcrypto_ECDSA_verify_raw(nil, base(hash), C.size_t(len(hash)),
(*C.uint8_t)(unsafe.Pointer(&sig[0])), C.uint(len(sig)), pub.key) > 0
runtime.KeepAlive(pub)
return ok
}
Expand All @@ -186,14 +188,11 @@ func GenerateKeyECDSA(curve string) (X, Y, D BigInt, err error) {
if err != nil {
return nil, nil, nil, err
}
key := C._goboringcrypto_EC_KEY_new_by_curve_name(nid)
key := C._goboringcrypto_EC_KEY_generate_key_fips(nid)
if key == nil {
return nil, nil, nil, NewOpenSSLError("EC_KEY_new_by_curve_name failed")
return nil, nil, nil, NewOpenSSLError("EC_KEY_generate_key_fips failed")
}
defer C._goboringcrypto_EC_KEY_free(key)
if C._goboringcrypto_EC_KEY_generate_key(key) == 0 {
return nil, nil, nil, NewOpenSSLError("EC_KEY_generate_key failed")
}
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)
Expand Down
100 changes: 100 additions & 0 deletions openssl/ecdsa_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//go:build linux && !android
// +build linux,!android

package openssl_test

import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"testing"

"github.com/golang-fips/openssl-fips/openssl"
"github.com/golang-fips/openssl-fips/openssl/bbig"
)

func testAllCurves(t *testing.T, f func(*testing.T, elliptic.Curve)) {
tests := []struct {
name string
curve elliptic.Curve
}{
{"P256", elliptic.P256()},
{"P384", elliptic.P384()},
{"P521", elliptic.P521()},
}
for _, test := range tests {
curve := test.curve
t.Run(test.name, func(t *testing.T) {
t.Parallel()
f(t, curve)
})
}
}

func TestECDSAKeyGeneration(t *testing.T) {
testAllCurves(t, testECDSAKeyGeneration)
}

func testECDSAKeyGeneration(t *testing.T, c elliptic.Curve) {
priv, err := generateKeyForCurve(c)
if err != nil {
t.Fatal(err)
}
if !c.IsOnCurve(priv.PublicKey.X, priv.PublicKey.Y) {
t.Errorf("public key invalid: %s", err)
}
}

func TestECDSASignAndVerify(t *testing.T) {
testAllCurves(t, testECDSASignAndVerify)
}

func testECDSASignAndVerify(t *testing.T, c elliptic.Curve) {
key, err := generateKeyForCurve(c)
if err != nil {
t.Fatal(err)
}
msg := []byte("hi!")
hashed := openssl.SHA256(msg)

priv, err := openssl.NewPrivateKeyECDSA(key.Params().Name, bbig.Enc(key.X), bbig.Enc(key.Y), bbig.Enc(key.D))
if err != nil {
t.Fatal(err)
}
pub, err := openssl.NewPublicKeyECDSA(key.Params().Name, bbig.Enc(key.X), bbig.Enc(key.Y))
if err != nil {
t.Fatal(err)
}
signed, err := openssl.SignMarshalECDSA(priv, hashed[:])
if err != nil {
t.Fatal(err)
}
if !openssl.VerifyECDSA(pub, hashed[:], signed) {
t.Errorf("Verify failed")
}
signed[0] ^= 0xff
if openssl.VerifyECDSA(pub, hashed[:], signed) {
t.Errorf("Verify succeeded despite intentionally invalid hash!")
}
r, s, err := openssl.HashSignECDSA(priv, msg, crypto.SHA256)
if err != nil {
t.Fatal(err)
}
if !openssl.HashVerifyECDSA(pub, msg, r, s, crypto.SHA256) {
t.Errorf("Verify failed")
}
rb := r.Bytes()
rb[0] ^= 0xff
r.SetBytes(rb)
if openssl.HashVerifyECDSA(pub, msg, r, s, crypto.SHA256) {
t.Errorf("Verify succeeded on modified signature!")
}
}

func generateKeyForCurve(c elliptic.Curve) (*ecdsa.PrivateKey, error) {
x, y, d, err := openssl.GenerateKeyECDSA(c.Params().Name)
if err != nil {
return nil, err
}
return &ecdsa.PrivateKey{PublicKey: ecdsa.PublicKey{Curve: c, X: bbig.Dec(x), Y: bbig.Dec(y)}, D: bbig.Dec(d)}, nil
}
26 changes: 10 additions & 16 deletions openssl/goopenssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -459,12 +459,12 @@ DEFINEFUNC(int, EC_POINT_set_affine_coordinates_GFp,

typedef EC_KEY GO_EC_KEY;

GO_EC_KEY *_goboringcrypto_EC_KEY_generate_key_fips(int nid);

DEFINEFUNC(GO_EC_KEY *, EC_KEY_new, (void), ())
DEFINEFUNC(GO_EC_KEY *, EC_KEY_new_by_curve_name, (int arg0), (arg0))
DEFINEFUNC(void, EC_KEY_free, (GO_EC_KEY * arg0), (arg0))
DEFINEFUNC(const GO_EC_GROUP *, EC_KEY_get0_group, (const GO_EC_KEY *arg0), (arg0))
DEFINEFUNC(int, EC_KEY_set_group, (GO_EC_KEY *arg0, const EC_GROUP *arg1), (arg0, arg1))
DEFINEFUNC(int, EC_KEY_generate_key, (GO_EC_KEY * arg0), (arg0))
DEFINEFUNC(int, EC_KEY_set_private_key, (GO_EC_KEY * arg0, const GO_BIGNUM *arg1), (arg0, arg1))
DEFINEFUNC(int, EC_KEY_set_public_key, (GO_EC_KEY * arg0, const GO_EC_POINT *arg1), (arg0, arg1))
DEFINEFUNC(const GO_BIGNUM *, EC_KEY_get0_private_key, (const GO_EC_KEY *arg0), (arg0))
Expand Down Expand Up @@ -499,22 +499,8 @@ _goboringcrypto_EC_KEY_oct2key(GO_EC_KEY *eckey, const unsigned char *buf, size_

#include <openssl/ecdsa.h>

typedef ECDSA_SIG GO_ECDSA_SIG;

DEFINEFUNC(GO_ECDSA_SIG *, ECDSA_SIG_new, (void), ())
DEFINEFUNC(void, ECDSA_SIG_free, (GO_ECDSA_SIG * arg0), (arg0))
DEFINEFUNC(GO_ECDSA_SIG *, ECDSA_do_sign, (const uint8_t *arg0, size_t arg1, const GO_EC_KEY *arg2), (arg0, arg1, arg2))
DEFINEFUNC(int, ECDSA_do_verify, (const uint8_t *arg0, size_t arg1, const GO_ECDSA_SIG *arg2, GO_EC_KEY *arg3), (arg0, arg1, arg2, arg3))
DEFINEFUNC(size_t, ECDSA_size, (const GO_EC_KEY *arg0), (arg0))

DEFINEFUNCINTERNAL(int, ECDSA_sign,
(int type, const unsigned char *dgst, size_t dgstlen, unsigned char *sig, unsigned int *siglen, EC_KEY *eckey),
(type, dgst, dgstlen, sig, siglen, eckey))

DEFINEFUNCINTERNAL(int, ECDSA_verify,
(int type, const unsigned char *dgst, size_t dgstlen, const unsigned char *sig, unsigned int siglen, EC_KEY *eckey),
(type, dgst, dgstlen, sig, siglen, eckey))

#if OPENSSL_VERSION_NUMBER < 0x10100000L
DEFINEFUNC(EVP_MD_CTX*, EVP_MD_CTX_create, (void), ())
#else
Expand Down Expand Up @@ -578,6 +564,13 @@ DEFINEFUNC(void, EVP_MD_CTX_free, (EVP_MD_CTX *ctx), (ctx))

int _goboringcrypto_ECDSA_sign(EVP_MD *md, const uint8_t *arg1, size_t arg2, uint8_t *arg3, size_t *arg4, GO_EC_KEY *arg5);
int _goboringcrypto_ECDSA_verify(EVP_MD *md, const uint8_t *arg1, size_t arg2, const uint8_t *arg3, unsigned int arg4, GO_EC_KEY *arg5);
int _goboringcrypto_ECDSA_sign_raw(EVP_MD *md, const uint8_t *msg,
size_t msgLen, uint8_t *sig, size_t *slen,
GO_EC_KEY *ec_key);
int _goboringcrypto_ECDSA_verify_raw(EVP_MD *md,
const uint8_t *msg, size_t msgLen,
const uint8_t *sig, unsigned int slen,
GO_EC_KEY *ec_key);

#include <openssl/rsa.h>

Expand Down Expand Up @@ -826,6 +819,7 @@ typedef EVP_PKEY GO_EVP_PKEY;
DEFINEFUNC(GO_EVP_PKEY *, EVP_PKEY_new, (void), ())
DEFINEFUNC(void, EVP_PKEY_free, (GO_EVP_PKEY * arg0), (arg0))
DEFINEFUNC(int, EVP_PKEY_set1_RSA, (GO_EVP_PKEY * arg0, GO_RSA *arg1), (arg0, arg1))
DEFINEFUNC(GO_EC_KEY *, EVP_PKEY_get1_EC_KEY, (GO_EVP_PKEY * arg0), (arg0))
DEFINEFUNC(int, EVP_PKEY_set1_EC_KEY, (GO_EVP_PKEY * arg0, GO_EC_KEY *arg1), (arg0, arg1))
DEFINEFUNC(int, EVP_PKEY_verify,
(EVP_PKEY_CTX *ctx, const unsigned char *sig, unsigned int siglen, const unsigned char *tbs, size_t tbslen),
Expand Down
9 changes: 6 additions & 3 deletions openssl/notboring.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,16 @@ func NewPrivateKeyECDSA(curve string, X, Y, D BigInt) (*PrivateKeyECDSA, error)
func NewPublicKeyECDSA(curve string, X, Y BigInt) (*PublicKeyECDSA, error) {
panic("boringcrypto: not available")
}
func SignECDSA(priv *PrivateKeyECDSA, hash []byte, h crypto.Hash) (r, s BigInt, err error) {
func HashSignECDSA(priv *PrivateKeyECDSA, hash []byte, h crypto.Hash) (r, s BigInt, err error) {
panic("boringcrypto: not available")
}
func SignMarshalECDSA(priv *PrivateKeyECDSA, hash []byte, h crypto.Hash) ([]byte, error) {
func HashVerifyECDSA(pub *PublicKeyECDSA, msg []byte, r, s *big.Int, h crypto.Hash) bool {
panic("boringcrypto: not available")
}
func VerifyECDSA(pub *PublicKeyECDSA, hash []byte, r, s BigInt, h crypto.Hash) bool {
func SignMarshalECDSA(priv *PrivateKeyECDSA, hash []byte) ([]byte, error) {
panic("boringcrypto: not available")
}
func VerifyECDSA(pub *PublicKeyECDSA, hash []byte, r, s BigInt) bool {
panic("boringcrypto: not available")
}

Expand Down
103 changes: 103 additions & 0 deletions openssl/openssl_ecdsa_signature.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,34 @@

#include "goopenssl.h"

// Only in BoringSSL.
GO_EC_KEY *_goboringcrypto_EC_KEY_generate_key_fips(int nid) {
GO_EVP_PKEY_CTX *ctx = NULL;
GO_EVP_PKEY *pkey = NULL;
GO_BIGNUM *e = NULL;
GO_EC_KEY *ret = NULL;

ctx = _goboringcrypto_EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
if (!ctx)
return NULL;

if (_goboringcrypto_EVP_PKEY_keygen_init(ctx) <= 0)
goto err;

if (_goboringcrypto_EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, nid) <= 0)
goto err;

if (_goboringcrypto_EVP_PKEY_keygen(ctx, &pkey) <= 0)
goto err;

ret = _goboringcrypto_EVP_PKEY_get1_EC_KEY(pkey);

err:
_goboringcrypto_EVP_PKEY_free(pkey);
_goboringcrypto_EVP_PKEY_CTX_free(ctx);
return ret;
}

int _goboringcrypto_ECDSA_sign(EVP_MD *md, const uint8_t *msg, size_t msgLen,
uint8_t *sig, size_t *slen,
GO_EC_KEY *eckey) {
Expand Down Expand Up @@ -44,3 +72,78 @@ int _goboringcrypto_ECDSA_verify(EVP_MD *md, const uint8_t *msg, size_t msgLen,
_goboringcrypto_EVP_PKEY_free(key);
return result;
}

int _goboringcrypto_ECDSA_sign_raw(EVP_MD *md, const uint8_t *msg,
size_t msgLen, uint8_t *sig, size_t *slen,
GO_EC_KEY *ec_key) {
int ret = 0;
GO_EVP_PKEY_CTX *ctx = NULL;
GO_EVP_PKEY *pkey = NULL;

pkey = _goboringcrypto_EVP_PKEY_new();
if (!pkey)
goto err;

if (_goboringcrypto_EVP_PKEY_set1_EC_KEY(pkey, ec_key) != 1)
goto err;

ctx = _goboringcrypto_EVP_PKEY_CTX_new(pkey, NULL);
if (!ctx)
goto err;

if (_goboringcrypto_EVP_PKEY_sign_init(ctx) != 1)
goto err;

if (md && _goboringcrypto_EVP_PKEY_CTX_set_signature_md(ctx, md) != 1)
goto err;

if (_goboringcrypto_EVP_PKEY_sign(ctx, sig, slen, msg, msgLen) != 1)
goto err;

/* Success */
ret = 1;

err:
_goboringcrypto_EVP_PKEY_CTX_free(ctx);
_goboringcrypto_EVP_PKEY_free(pkey);

return ret;
}

int _goboringcrypto_ECDSA_verify_raw(EVP_MD *md,
const uint8_t *msg, size_t msgLen,
const uint8_t *sig, unsigned int slen,
GO_EC_KEY *ec_key) {
int ret = 0;
GO_EVP_PKEY_CTX *ctx = NULL;
GO_EVP_PKEY *pkey = NULL;

pkey = _goboringcrypto_EVP_PKEY_new();
if (!pkey)
goto err;

if (_goboringcrypto_EVP_PKEY_set1_EC_KEY(pkey, ec_key) != 1)
goto err;

ctx = _goboringcrypto_EVP_PKEY_CTX_new(pkey, NULL);
if (!ctx)
goto err;

if (_goboringcrypto_EVP_PKEY_verify_init(ctx) != 1)
goto err;

if (md && _goboringcrypto_EVP_PKEY_CTX_set_signature_md(ctx, md) != 1)
goto err;

if (_goboringcrypto_EVP_PKEY_verify(ctx, sig, slen, msg, msgLen) != 1)
goto err;

/* Success */
ret = 1;

err:
_goboringcrypto_EVP_PKEY_CTX_free(ctx);
_goboringcrypto_EVP_PKEY_free(pkey);

return ret;
}