From 17c728ea661881077707b1ce94a2a1b817ab2bb2 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Fri, 12 Jan 2024 17:00:44 +0100 Subject: [PATCH 01/22] implement crypto/dsa --- dsa.go | 278 ++++++++++++++++++++++++++++++++++++++++++++++++++++ dsa_test.go | 176 +++++++++++++++++++++++++++++++++ shims.h | 16 ++- 3 files changed, 469 insertions(+), 1 deletion(-) create mode 100644 dsa.go create mode 100644 dsa_test.go diff --git a/dsa.go b/dsa.go new file mode 100644 index 00000000..cb9be474 --- /dev/null +++ b/dsa.go @@ -0,0 +1,278 @@ +//go:build !cmd_go_bootstrap + +package openssl + +// #include "goopenssl.h" +import "C" +import ( + "runtime" + "unsafe" +) + +var ( + paramPbits = C.CString("pbits") + paramQbits = C.CString("qbits") + paramP = C.CString("p") + paramQ = C.CString("q") + paramG = C.CString("g") +) + +// DSAParameters contains the DSA parameters. +type DSAParameters struct { + P, Q, G BigInt +} + +func (p DSAParameters) keySize() uint32 { + return uint32(len(p.P)) +} + +func (p DSAParameters) groupSize() uint32 { + return uint32(len(p.Q)) +} + +// PrivateKeyDSA represents a DSA private key. +type PrivateKeyDSA struct { + DSAParameters + X, Y BigInt + + _pkey C.GO_EVP_PKEY_PTR +} + +func (k *PrivateKeyDSA) finalize() { + C.go_openssl_EVP_PKEY_free(k._pkey) +} + +func (k *PrivateKeyDSA) withKey(f func(C.GO_EVP_PKEY_PTR) C.int) C.int { + defer runtime.KeepAlive(k) + return f(k._pkey) +} + +// PublicKeyDSA represents a DSA public key. +type PublicKeyDSA struct { + DSAParameters + Y BigInt + + _pkey C.GO_EVP_PKEY_PTR +} + +func (k *PublicKeyDSA) finalize() { + C.go_openssl_EVP_PKEY_free(k._pkey) +} + +func (k *PublicKeyDSA) withKey(f func(C.GO_EVP_PKEY_PTR) C.int) C.int { + defer runtime.KeepAlive(k) + return f(k._pkey) +} + +// GenerateDSAParameters generates a set of DSA parameters. +func GenerateDSAParameters(L, N int) (DSAParameters, error) { + ctx := C.go_openssl_EVP_PKEY_CTX_new_id(C.GO_EVP_PKEY_DSA, nil) + if ctx == nil { + return DSAParameters{}, newOpenSSLError("EVP_PKEY_CTX_new_id failed") + } + defer C.go_openssl_EVP_PKEY_CTX_free(ctx) + if C.go_openssl_EVP_PKEY_paramgen_init(ctx) != 1 { + return DSAParameters{}, newOpenSSLError("EVP_PKEY_paramgen_init failed") + } + if C.go_openssl_EVP_PKEY_CTX_ctrl(ctx, C.GO_EVP_PKEY_DSA, -1, C.GO_EVP_PKEY_CTRL_DSA_PARAMGEN_BITS, C.int(L), nil) != 1 { + return DSAParameters{}, newOpenSSLError("EVP_PKEY_CTX_ctrl failed") + } + if C.go_openssl_EVP_PKEY_CTX_ctrl(ctx, C.GO_EVP_PKEY_DSA, -1, C.GO_EVP_PKEY_CTRL_DSA_PARAMGEN_Q_BITS, C.int(N), nil) != 1 { + return DSAParameters{}, newOpenSSLError("EVP_PKEY_CTX_ctrl failed") + } + var pkey C.GO_EVP_PKEY_PTR + if C.go_openssl_EVP_PKEY_paramgen(ctx, &pkey) != 1 { + return DSAParameters{}, newOpenSSLError("EVP_PKEY_paramgen failed") + } + defer C.go_openssl_EVP_PKEY_free(pkey) + var p, q, g C.GO_BIGNUM_PTR + switch vMajor { + case 1: + dsa := getDSA(pkey) + C.go_openssl_DSA_get0_pqg(dsa, &p, &q, &g) + case 3: + defer func() { + C.go_openssl_BN_free(p) + C.go_openssl_BN_free(q) + C.go_openssl_BN_free(g) + }() + if C.go_openssl_EVP_PKEY_get_bn_param(pkey, paramP, &p) != 1 || + C.go_openssl_EVP_PKEY_get_bn_param(pkey, paramQ, &q) != 1 || + C.go_openssl_EVP_PKEY_get_bn_param(pkey, paramG, &g) != 1 { + return DSAParameters{}, newOpenSSLError("EVP_PKEY_get_bn_param") + } + default: + panic(errUnsupportedVersion()) + } + + return DSAParameters{ + P: bnToBig(p), + Q: bnToBig(q), + G: bnToBig(g), + }, nil +} + +// NewPrivateKeyDSA creates a new DSA private key from the given parameters. +func NewPrivateKeyDSA(params DSAParameters, X, Y BigInt) (*PrivateKeyDSA, error) { + if X == nil || Y == nil { + panic("X and Y must not be nil") + } + pkey, err := newDSA(params, X, Y) + if err != nil { + return nil, err + } + k := &PrivateKeyDSA{params, X, Y, pkey} + runtime.SetFinalizer(k, (*PrivateKeyDSA).finalize) + return k, nil +} + +// NewPublicKeyDSA creates a new DSA public key from the given parameters. +func NewPublicKeyDSA(params DSAParameters, Y BigInt) (*PublicKeyDSA, error) { + if Y == nil { + panic("Y must not be nil") + } + pkey, err := newDSA(params, nil, Y) + if err != nil { + return nil, err + } + k := &PublicKeyDSA{params, Y, pkey} + runtime.SetFinalizer(k, (*PublicKeyDSA).finalize) + return k, nil +} + +// GenerateKeyDSA generates a new private DSA key using the given parameters. +func GenerateKeyDSA(params DSAParameters) (*PrivateKeyDSA, error) { + pkey, err := newDSA(params, nil, nil) + if err != nil { + return nil, err + } + var x, y C.GO_BIGNUM_PTR + switch vMajor { + case 1: + dsa := getDSA(pkey) + C.go_openssl_DSA_get0_key(dsa, &y, &x) + case 3: + defer func() { + C.go_openssl_BN_free(x) + C.go_openssl_BN_free(y) + }() + if C.go_openssl_EVP_PKEY_get_bn_param(pkey, paramPubKey, &y) != 1 || + C.go_openssl_EVP_PKEY_get_bn_param(pkey, paramPrivKey, &x) != 1 { + return nil, newOpenSSLError("EVP_PKEY_get_bn_param") + } + default: + panic(errUnsupportedVersion()) + } + k := &PrivateKeyDSA{params, bnToBig(x), bnToBig(y), pkey} + runtime.SetFinalizer(k, (*PrivateKeyDSA).finalize) + return k, nil +} + +// SignDSA signs a hash (which should be the result of hashing a larger message). +func SignDSA(priv *PrivateKeyDSA, hash []byte) ([]byte, error) { + return evpSign(priv.withKey, 0, 0, 0, hash) +} + +// VerifyDSA verifiessig using the public key, pub. +func VerifyDSA(pub *PublicKeyDSA, hash []byte, sig []byte) bool { + return evpVerify(pub.withKey, 0, 0, 0, sig, hash) == nil +} + +func newDSA(params DSAParameters, X, Y BigInt) (C.GO_EVP_PKEY_PTR, error) { + switch vMajor { + case 1: + return newDSA1(params, X, Y) + case 3: + return newDSA3(params, X, Y) + default: + panic(errUnsupportedVersion()) + } +} + +func newDSA1(params DSAParameters, X, Y BigInt) (C.GO_EVP_PKEY_PTR, error) { + if vMajor != 1 { + panic("incorrect vMajor version") + } + dsa := C.go_openssl_DSA_new() + p, q, g := bigToBN(params.P), bigToBN(params.Q), bigToBN(params.G) + if C.go_openssl_DSA_set0_pqg(dsa, p, q, g) != 1 { + C.go_openssl_DSA_free(dsa) + return nil, newOpenSSLError("DSA_set0_pqg failed") + } + if Y != nil { + pub, priv := bigToBN(Y), bigToBN(X) + if C.go_openssl_DSA_set0_key(dsa, pub, priv) != 1 { + C.go_openssl_DSA_free(dsa) + return nil, newOpenSSLError("DSA_set0_key failed") + } + } else { + if C.go_openssl_DSA_generate_key(dsa) != 1 { + C.go_openssl_DSA_free(dsa) + return nil, newOpenSSLError("DSA_generate_key failed") + } + } + pkey := C.go_openssl_EVP_PKEY_new() + if pkey == nil { + C.go_openssl_DSA_free(dsa) + return nil, newOpenSSLError("EVP_PKEY_new failed") + } + if C.go_openssl_EVP_PKEY_assign(pkey, C.GO_EVP_PKEY_DSA, unsafe.Pointer(dsa)) != 1 { + C.go_openssl_DSA_free(dsa) + C.go_openssl_EVP_PKEY_free(pkey) + return nil, newOpenSSLError("EVP_PKEY_assign failed") + } + return pkey, nil +} + +func newDSA3(params DSAParameters, X, Y BigInt) (C.GO_EVP_PKEY_PTR, error) { + if vMajor != 3 { + panic("incorrect vMajor version") + } + bld := C.go_openssl_OSSL_PARAM_BLD_new() + if bld == nil { + return nil, newOpenSSLError("OSSL_PARAM_BLD_new") + } + defer C.go_openssl_OSSL_PARAM_BLD_free(bld) + pub, priv := bigToBN(Y), bigToBN(X) + selection := C.int(C.GO_EVP_PKEY_PUBLIC_KEY) + if C.go_openssl_OSSL_PARAM_BLD_push_BN(bld, paramPubKey, pub) != 1 { + return nil, newOpenSSLError("OSSL_PARAM_BLD_push_BN") + } + if X != nil { + if C.go_openssl_OSSL_PARAM_BLD_push_BN(bld, paramPrivKey, priv) != 1 { + return nil, newOpenSSLError("OSSL_PARAM_BLD_push_BN") + } + selection = C.GO_EVP_PKEY_KEYPAIR + } + bldparams := C.go_openssl_OSSL_PARAM_BLD_to_param(bld) + if bldparams == nil { + return nil, newOpenSSLError("OSSL_PARAM_BLD_to_param") + } + defer C.go_openssl_OSSL_PARAM_free(bldparams) + pkey, err := newEvpFromParams(C.GO_EVP_PKEY_EC, selection, bldparams) + if err != nil { + return nil, err + } + if Y != nil { + return pkey, nil + } + // Generate the key. + return nil, nil +} + +// getDSA returns the DSA from pkey. +// If pkey does not contain an DSA it panics. +// The returned key should not be freed. +func getDSA(pkey C.GO_EVP_PKEY_PTR) (key C.GO_DSA_PTR) { + if vMajor == 1 && vMinor == 0 { + if key0 := C.go_openssl_EVP_PKEY_get0(pkey); key0 != nil { + key = C.GO_DSA_PTR(key0) + } + } else { + key = C.go_openssl_EVP_PKEY_get0_DSA(pkey) + } + if key == nil { + panic("pkey does not contain an DSA") + } + return key +} diff --git a/dsa_test.go b/dsa_test.go new file mode 100644 index 00000000..b570294b --- /dev/null +++ b/dsa_test.go @@ -0,0 +1,176 @@ +package openssl_test + +import ( + "crypto/dsa" + "encoding/asn1" + "math/big" + "testing" + + "github.com/golang-fips/openssl/v2" + "github.com/golang-fips/openssl/v2/bbig" +) + +type dsaSignature struct { + R, S *big.Int +} + +func TestGenerateDSAParameters(t *testing.T) { + testGenerateDSAParameters(t, 1024, 160) + testGenerateDSAParameters(t, 2048, 224) + testGenerateDSAParameters(t, 2048, 256) + testGenerateDSAParameters(t, 3072, 256) +} + +func testGenerateDSAParameters(t *testing.T, L, N int) { + params, err := openssl.GenerateDSAParameters(L, N) + if err != nil { + t.Errorf("%d-%d: error generating parameters: %s", L, N, err) + return + } + + P := bbig.Dec(params.P) + Q := bbig.Dec(params.Q) + G := bbig.Dec(params.G) + + if P.BitLen() != L { + t.Errorf("%d-%d: params.BitLen got:%d want:%d", L, N, P.BitLen(), L) + } + + if Q.BitLen() != N { + t.Errorf("%d-%d: q.BitLen got:%d want:%d", L, N, Q.BitLen(), L) + } + + one := new(big.Int) + one.SetInt64(1) + pm1 := new(big.Int).Sub(P, one) + quo, rem := new(big.Int).DivMod(pm1, Q, new(big.Int)) + if rem.Sign() != 0 { + t.Errorf("%d-%d: p-1 mod q != 0", L, N) + } + x := new(big.Int).Exp(G, quo, P) + if x.Cmp(one) == 0 { + t.Errorf("%d-%d: invalid generator", L, N) + } + + priv, err := openssl.GenerateKeyDSA(params) + if err != nil { + t.Errorf("error generating key: %s", err) + return + } + + testDSASignAndVerify(t, L, priv) +} + +func testDSASignAndVerify(t *testing.T, i int, priv *openssl.PrivateKeyDSA) { + hashed := []byte("testing") + sig, err := openssl.SignDSA(priv, hashed[:]) + if err != nil { + t.Errorf("%d: error signing: %s", i, err) + return + } + pub, err := openssl.NewPublicKeyDSA(priv.DSAParameters, priv.Y) + if err != nil { + t.Errorf("%d: error getting public key: %s", i, err) + return + } + if !openssl.VerifyDSA(pub, hashed[:], sig) { + t.Errorf("%d: error verifying", i) + return + } + + // Test compatibility with crypto/dsa. + priv1 := dsa.PrivateKey{ + PublicKey: dsa.PublicKey{ + Parameters: dsa.Parameters{ + P: bbig.Dec(priv.P), + Q: bbig.Dec(priv.Q), + G: bbig.Dec(priv.G), + }, + Y: bbig.Dec(priv.Y), + }, + X: bbig.Dec(priv.X), + } + var esig dsaSignature + if _, err := asn1.Unmarshal(sig, &esig); err != nil { + t.Error(err) + return + } + if !dsa.Verify(&priv1.PublicKey, hashed[:], esig.R, esig.S) { + t.Errorf("%d: compat: crypto/dsa can't verify OpenSSL signature", i) + } + r1, s1, err := dsa.Sign(openssl.RandReader, &priv1, hashed[:]) + if err != nil { + t.Errorf("%d: error signing: %s", i, err) + return + } + sig, err = asn1.Marshal(dsaSignature{r1, s1}) + if err != nil { + t.Error(err) + return + } + if !openssl.VerifyDSA(pub, hashed[:], sig) { + t.Errorf("%d: compat: OpenSSL can't verify crypto/dsa signature", i) + return + } +} + +func TestDSASignAndVerify(t *testing.T) { + params := openssl.DSAParameters{ + P: bbig.Enc(fromHex("A9B5B793FB4785793D246BAE77E8FF63CA52F442DA763C440259919FE1BC1D6065A9350637A04F75A2F039401D49F08E066C4D275A5A65DA5684BC563C14289D7AB8A67163BFBF79D85972619AD2CFF55AB0EE77A9002B0EF96293BDD0F42685EBB2C66C327079F6C98000FBCB79AACDE1BC6F9D5C7B1A97E3D9D54ED7951FEF")), + Q: bbig.Enc(fromHex("E1D3391245933D68A0714ED34BBCB7A1F422B9C1")), + G: bbig.Enc(fromHex("634364FC25248933D01D1993ECABD0657CC0CB2CEED7ED2E3E8AECDFCDC4A25C3B15E9E3B163ACA2984B5539181F3EFF1A5E8903D71D5B95DA4F27202B77D2C44B430BB53741A8D59A8F86887525C9F2A6A5980A195EAA7F2FF910064301DEF89D3AA213E1FAC7768D89365318E370AF54A112EFBA9246D9158386BA1B4EEFDA")), + } + Y := bbig.Enc(fromHex("32969E5780CFE1C849A1C276D7AEB4F38A23B591739AA2FE197349AEEBD31366AEE5EB7E6C6DDB7C57D02432B30DB5AA66D9884299FAA72568944E4EEDC92EA3FBC6F39F53412FBCC563208F7C15B737AC8910DBC2D9C9B8C001E72FDC40EB694AB1F06A5A2DBD18D9E36C66F31F566742F11EC0A52E9F7B89355C02FB5D32D2")) + X := bbig.Enc(fromHex("5078D4D29795CBE76D3AACFE48C9AF0BCDBEE91A")) + priv, err := openssl.NewPrivateKeyDSA(params, X, Y) + if err != nil { + t.Fatalf("error generating key: %s", err) + } + + testDSASignAndVerify(t, 0, priv) +} + +func TestNewPrivateKeyDSAWithDegenerateKeys(t *testing.T) { + // Signing with degenerate private keys should not cause an infinite + // loop. + badKeys := []struct { + p, q, g, y, x string + }{ + {"00", "01", "00", "00", "00"}, + {"01", "ff", "00", "00", "00"}, + } + + for i, test := range badKeys { + params := openssl.DSAParameters{ + P: bbig.Enc(fromHex(test.p)), + Q: bbig.Enc(fromHex(test.q)), + G: bbig.Enc(fromHex(test.g)), + } + _, err := openssl.NewPrivateKeyDSA(params, bbig.Enc(fromHex(test.x)), bbig.Enc(fromHex(test.y))) + if err == nil { + t.Errorf("#%d: error generating key: %s", i, err) + } + } +} + +func TestNewPublicKeyDSAWithBadPublicKey(t *testing.T) { + params := openssl.DSAParameters{ + P: bbig.Enc(fromHex("A9B5B793FB4785793D246BAE77E8FF63CA52F442DA763C440259919FE1BC1D6065A9350637A04F75A2F039401D49F08E066C4D275A5A65DA5684BC563C14289D7AB8A67163BFBF79D85972619AD2CFF55AB0EE77A9002B0EF96293BDD0F42685EBB2C66C327079F6C98000FBCB79AACDE1BC6F9D5C7B1A97E3D9D54ED7951FEF")), + Q: bbig.Enc(fromHex("FA")), + G: bbig.Enc(fromHex("634364FC25248933D01D1993ECABD0657CC0CB2CEED7ED2E3E8AECDFCDC4A25C3B15E9E3B163ACA2984B5539181F3EFF1A5E8903D71D5B95DA4F27202B77D2C44B430BB53741A8D59A8F86887525C9F2A6A5980A195EAA7F2FF910064301DEF89D3AA213E1FAC7768D89365318E370AF54A112EFBA9246D9158386BA1B4EEFDA")), + } + Y := bbig.Enc(fromHex("32969E5780CFE1C849A1C276D7AEB4F38A23B591739AA2FE197349AEEBD31366AEE5EB7E6C6DDB7C57D02432B30DB5AA66D9884299FAA72568944E4EEDC92EA3FBC6F39F53412FBCC563208F7C15B737AC8910DBC2D9C9B8C001E72FDC40EB694AB1F06A5A2DBD18D9E36C66F31F566742F11EC0A52E9F7B89355C02FB5D32D2")) + + _, err := openssl.NewPublicKeyDSA(params, Y) + if err == nil { + t.Errorf("Unexpected success with non-existent mod inverse of Q") + } +} + +func fromHex(s string) *big.Int { + result, ok := new(big.Int).SetString(s, 16) + if !ok { + panic(s) + } + return result +} diff --git a/shims.h b/shims.h index 99656f0c..91d6e2c2 100644 --- a/shims.h +++ b/shims.h @@ -19,6 +19,7 @@ enum { GO_EVP_PKEY_TLS1_PRF = 1021, GO_EVP_PKEY_HKDF = 1036, GO_EVP_PKEY_ED25519 = 1087, + GO_EVP_PKEY_DSA = 116, /* This is defined differently in OpenSSL 3 (1 << 11), but in our * code it is only used in OpenSSL 1. */ @@ -76,7 +77,9 @@ enum { GO_EVP_PKEY_CTRL_RSA_KEYGEN_BITS = 0x1003, GO_EVP_PKEY_CTRL_RSA_MGF1_MD = 0x1005, GO_EVP_PKEY_CTRL_RSA_OAEP_MD = 0x1009, - GO_EVP_PKEY_CTRL_RSA_OAEP_LABEL = 0x100A + GO_EVP_PKEY_CTRL_RSA_OAEP_LABEL = 0x100A, + GO_EVP_PKEY_CTRL_DSA_PARAMGEN_BITS = 0x1001, + GO_EVP_PKEY_CTRL_DSA_PARAMGEN_Q_BITS = 0x1002 }; typedef void* GO_OPENSSL_INIT_SETTINGS_PTR; @@ -102,6 +105,7 @@ typedef void* GO_OSSL_PARAM_BLD_PTR; typedef void* GO_OSSL_PARAM_PTR; typedef void* GO_CRYPTO_THREADID_PTR; typedef void* GO_EVP_SIGNATURE_PTR; +typedef void* GO_DSA_PTR; // #include typedef void* GO_MD5_CTX_PTR; @@ -280,6 +284,8 @@ DEFINEFUNC_LEGACY_1(int, EVP_PKEY_assign, (GO_EVP_PKEY_PTR pkey, int type, void DEFINEFUNC(int, EVP_PKEY_verify, (GO_EVP_PKEY_CTX_PTR ctx, const unsigned char *sig, size_t siglen, const unsigned char *tbs, size_t tbslen), (ctx, sig, siglen, tbs, tbslen)) \ DEFINEFUNC(GO_EVP_PKEY_CTX_PTR, EVP_PKEY_CTX_new, (GO_EVP_PKEY_PTR arg0, GO_ENGINE_PTR arg1), (arg0, arg1)) \ DEFINEFUNC(GO_EVP_PKEY_CTX_PTR, EVP_PKEY_CTX_new_id, (int id, GO_ENGINE_PTR e), (id, e)) \ +DEFINEFUNC(int, EVP_PKEY_paramgen_init, (GO_EVP_PKEY_CTX_PTR ctx), (ctx)) \ +DEFINEFUNC(int, EVP_PKEY_paramgen, (GO_EVP_PKEY_CTX_PTR ctx, GO_EVP_PKEY_PTR *ppkey), (ctx, ppkey)) \ DEFINEFUNC(int, EVP_PKEY_keygen_init, (GO_EVP_PKEY_CTX_PTR ctx), (ctx)) \ DEFINEFUNC(int, EVP_PKEY_keygen, (GO_EVP_PKEY_CTX_PTR ctx, GO_EVP_PKEY_PTR *ppkey), (ctx, ppkey)) \ DEFINEFUNC(void, EVP_PKEY_CTX_free, (GO_EVP_PKEY_CTX_PTR arg0), (arg0)) \ @@ -296,6 +302,7 @@ DEFINEFUNC(int, EVP_PKEY_derive_set_peer, (GO_EVP_PKEY_CTX_PTR ctx, GO_EVP_PKEY_ DEFINEFUNC(int, EVP_PKEY_derive, (GO_EVP_PKEY_CTX_PTR ctx, unsigned char *key, size_t *keylen), (ctx, key, keylen)) \ DEFINEFUNC_LEGACY_1_0(void*, EVP_PKEY_get0, (GO_EVP_PKEY_PTR pkey), (pkey)) \ DEFINEFUNC_LEGACY_1_1(GO_EC_KEY_PTR, EVP_PKEY_get0_EC_KEY, (GO_EVP_PKEY_PTR pkey), (pkey)) \ +DEFINEFUNC_LEGACY_1_1(GO_DSA_PTR, EVP_PKEY_get0_DSA, (GO_EVP_PKEY_PTR pkey), (pkey)) \ DEFINEFUNC_3_0(int, EVP_PKEY_fromdata_init, (GO_EVP_PKEY_CTX_PTR ctx), (ctx)) \ DEFINEFUNC_3_0(int, EVP_PKEY_fromdata, (GO_EVP_PKEY_CTX_PTR ctx, GO_EVP_PKEY_PTR *pkey, int selection, GO_OSSL_PARAM_PTR params), (ctx, pkey, selection, params)) \ DEFINEFUNC_3_0(int, EVP_PKEY_set1_encoded_public_key, (GO_EVP_PKEY_PTR pkey, const unsigned char *pub, size_t publen), (pkey, pub, publen)) \ @@ -368,4 +375,11 @@ DEFINEFUNC_1_1_1(int, EVP_PKEY_get_raw_public_key, (const GO_EVP_PKEY_PTR pkey, DEFINEFUNC_1_1_1(int, EVP_PKEY_get_raw_private_key, (const GO_EVP_PKEY_PTR pkey, unsigned char *priv, size_t *len), (pkey, priv, len)) \ DEFINEFUNC_3_0(GO_EVP_SIGNATURE_PTR, EVP_SIGNATURE_fetch, (GO_OSSL_LIB_CTX_PTR ctx, const char *algorithm, const char *properties), (ctx, algorithm, properties)) \ DEFINEFUNC_3_0(void, EVP_SIGNATURE_free, (GO_EVP_SIGNATURE_PTR signature), (signature)) \ +DEFINEFUNC_LEGACY_1(GO_DSA_PTR, DSA_new, (void), ()) \ +DEFINEFUNC_LEGACY_1(void, DSA_free, (GO_DSA_PTR r), (r)) \ +DEFINEFUNC_LEGACY_1(int, DSA_generate_key, (GO_DSA_PTR a), (a)) \ +DEFINEFUNC_LEGACY_1(void, DSA_get0_pqg, (const GO_DSA_PTR d, const GO_BIGNUM_PTR *p, const GO_BIGNUM_PTR *q, const GO_BIGNUM_PTR *g), (d, p, q, g)) \ +DEFINEFUNC_LEGACY_1(int, DSA_set0_pqg, (GO_DSA_PTR d, GO_BIGNUM_PTR p, GO_BIGNUM_PTR q, GO_BIGNUM_PTR g), (d, p, q, g)) \ +DEFINEFUNC_LEGACY_1(void, DSA_get0_key, (const GO_DSA_PTR d, const GO_BIGNUM_PTR *pub_key, const GO_BIGNUM_PTR *priv_key), (d, pub_key, priv_key)) \ +DEFINEFUNC_LEGACY_1(int, DSA_set0_key, (GO_DSA_PTR d, GO_BIGNUM_PTR pub_key, GO_BIGNUM_PTR priv_key), (d, pub_key, priv_key)) \ From 45ef69e6bbf33d5ddf40fbb3e6e584f43b727cc6 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Fri, 12 Jan 2024 17:04:32 +0100 Subject: [PATCH 02/22] add dsa.h include --- shims.h | 1 + 1 file changed, 1 insertion(+) diff --git a/shims.h b/shims.h index 91d6e2c2..489a80d7 100644 --- a/shims.h +++ b/shims.h @@ -162,6 +162,7 @@ typedef void* GO_SHA_CTX_PTR; // #include // #include // #include +// #include // #if OPENSSL_VERSION_NUMBER >= 0x30000000L // #include // #include From 4132293eb680cee7a61ef43e8c243a1139b5227f Mon Sep 17 00:00:00 2001 From: qmuntal Date: Wed, 17 Jan 2024 16:15:35 +0100 Subject: [PATCH 03/22] fix TestNewPublicKeyDSAWithBadPublicKey --- dsa_test.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/dsa_test.go b/dsa_test.go index b570294b..6ba5eb9a 100644 --- a/dsa_test.go +++ b/dsa_test.go @@ -161,8 +161,15 @@ func TestNewPublicKeyDSAWithBadPublicKey(t *testing.T) { } Y := bbig.Enc(fromHex("32969E5780CFE1C849A1C276D7AEB4F38A23B591739AA2FE197349AEEBD31366AEE5EB7E6C6DDB7C57D02432B30DB5AA66D9884299FAA72568944E4EEDC92EA3FBC6F39F53412FBCC563208F7C15B737AC8910DBC2D9C9B8C001E72FDC40EB694AB1F06A5A2DBD18D9E36C66F31F566742F11EC0A52E9F7B89355C02FB5D32D2")) - _, err := openssl.NewPublicKeyDSA(params, Y) + pub, err := openssl.NewPublicKeyDSA(params, Y) if err == nil { + t.Fatal(err) + } + sig, err := asn1.Marshal(dsaSignature{fromHex("2"), fromHex("4")}) + if err != nil { + t.Fatal(err) + } + if openssl.VerifyDSA(pub, []byte("testing"), sig) { t.Errorf("Unexpected success with non-existent mod inverse of Q") } } From 1d3a2fd7c806fcbfcc547e28601d61ba92397b81 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Wed, 17 Jan 2024 16:18:38 +0100 Subject: [PATCH 04/22] update dsa test names --- dsa_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dsa_test.go b/dsa_test.go index 6ba5eb9a..736cc087 100644 --- a/dsa_test.go +++ b/dsa_test.go @@ -14,7 +14,7 @@ type dsaSignature struct { R, S *big.Int } -func TestGenerateDSAParameters(t *testing.T) { +func TestDSAGenerateParameters(t *testing.T) { testGenerateDSAParameters(t, 1024, 160) testGenerateDSAParameters(t, 2048, 224) testGenerateDSAParameters(t, 2048, 256) @@ -130,7 +130,7 @@ func TestDSASignAndVerify(t *testing.T) { testDSASignAndVerify(t, 0, priv) } -func TestNewPrivateKeyDSAWithDegenerateKeys(t *testing.T) { +func TestDSANewPrivateKeyWithDegenerateKeys(t *testing.T) { // Signing with degenerate private keys should not cause an infinite // loop. badKeys := []struct { @@ -153,7 +153,7 @@ func TestNewPrivateKeyDSAWithDegenerateKeys(t *testing.T) { } } -func TestNewPublicKeyDSAWithBadPublicKey(t *testing.T) { +func TestDSANewPublicKeyWithBadPublicKey(t *testing.T) { params := openssl.DSAParameters{ P: bbig.Enc(fromHex("A9B5B793FB4785793D246BAE77E8FF63CA52F442DA763C440259919FE1BC1D6065A9350637A04F75A2F039401D49F08E066C4D275A5A65DA5684BC563C14289D7AB8A67163BFBF79D85972619AD2CFF55AB0EE77A9002B0EF96293BDD0F42685EBB2C66C327079F6C98000FBCB79AACDE1BC6F9D5C7B1A97E3D9D54ED7951FEF")), Q: bbig.Enc(fromHex("FA")), From 26f706f52a9d49b52ec2a4a5c7283b13c8ab9fde Mon Sep 17 00:00:00 2001 From: qmuntal Date: Wed, 17 Jan 2024 16:45:58 +0100 Subject: [PATCH 05/22] fix TestDSANewPublicKeyWithBadPublicKey --- dsa_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dsa_test.go b/dsa_test.go index 736cc087..7f1fee2a 100644 --- a/dsa_test.go +++ b/dsa_test.go @@ -162,7 +162,7 @@ func TestDSANewPublicKeyWithBadPublicKey(t *testing.T) { Y := bbig.Enc(fromHex("32969E5780CFE1C849A1C276D7AEB4F38A23B591739AA2FE197349AEEBD31366AEE5EB7E6C6DDB7C57D02432B30DB5AA66D9884299FAA72568944E4EEDC92EA3FBC6F39F53412FBCC563208F7C15B737AC8910DBC2D9C9B8C001E72FDC40EB694AB1F06A5A2DBD18D9E36C66F31F566742F11EC0A52E9F7B89355C02FB5D32D2")) pub, err := openssl.NewPublicKeyDSA(params, Y) - if err == nil { + if err != nil { t.Fatal(err) } sig, err := asn1.Marshal(dsaSignature{fromHex("2"), fromHex("4")}) From cfe4979137aac32ba805ea2d590b644012b5f731 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Wed, 17 Jan 2024 17:03:07 +0100 Subject: [PATCH 06/22] fix dsa memory leaks --- dsa.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/dsa.go b/dsa.go index cb9be474..4bfc6543 100644 --- a/dsa.go +++ b/dsa.go @@ -196,12 +196,17 @@ func newDSA1(params DSAParameters, X, Y BigInt) (C.GO_EVP_PKEY_PTR, error) { dsa := C.go_openssl_DSA_new() p, q, g := bigToBN(params.P), bigToBN(params.Q), bigToBN(params.G) if C.go_openssl_DSA_set0_pqg(dsa, p, q, g) != 1 { + C.go_openssl_BN_free(p) + C.go_openssl_BN_free(q) + C.go_openssl_BN_free(g) C.go_openssl_DSA_free(dsa) return nil, newOpenSSLError("DSA_set0_pqg failed") } if Y != nil { pub, priv := bigToBN(Y), bigToBN(X) if C.go_openssl_DSA_set0_key(dsa, pub, priv) != 1 { + C.go_openssl_BN_free(pub) + C.go_openssl_BN_clear_free(priv) C.go_openssl_DSA_free(dsa) return nil, newOpenSSLError("DSA_set0_key failed") } @@ -233,12 +238,15 @@ func newDSA3(params DSAParameters, X, Y BigInt) (C.GO_EVP_PKEY_PTR, error) { return nil, newOpenSSLError("OSSL_PARAM_BLD_new") } defer C.go_openssl_OSSL_PARAM_BLD_free(bld) - pub, priv := bigToBN(Y), bigToBN(X) selection := C.int(C.GO_EVP_PKEY_PUBLIC_KEY) + pub := bigToBN(Y) + defer C.go_openssl_BN_free(pub) if C.go_openssl_OSSL_PARAM_BLD_push_BN(bld, paramPubKey, pub) != 1 { return nil, newOpenSSLError("OSSL_PARAM_BLD_push_BN") } if X != nil { + priv := bigToBN(X) + defer C.go_openssl_BN_clear_free(priv) if C.go_openssl_OSSL_PARAM_BLD_push_BN(bld, paramPrivKey, priv) != 1 { return nil, newOpenSSLError("OSSL_PARAM_BLD_push_BN") } From 5c43b387c7f61ad1560376a16a05254d1ffd913e Mon Sep 17 00:00:00 2001 From: qmuntal Date: Wed, 17 Jan 2024 17:08:37 +0100 Subject: [PATCH 07/22] remove unused functions --- dsa.go | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/dsa.go b/dsa.go index 4bfc6543..73dcc3e8 100644 --- a/dsa.go +++ b/dsa.go @@ -22,14 +22,6 @@ type DSAParameters struct { P, Q, G BigInt } -func (p DSAParameters) keySize() uint32 { - return uint32(len(p.P)) -} - -func (p DSAParameters) groupSize() uint32 { - return uint32(len(p.Q)) -} - // PrivateKeyDSA represents a DSA private key. type PrivateKeyDSA struct { DSAParameters @@ -153,7 +145,7 @@ func GenerateKeyDSA(params DSAParameters) (*PrivateKeyDSA, error) { C.go_openssl_DSA_get0_key(dsa, &y, &x) case 3: defer func() { - C.go_openssl_BN_free(x) + C.go_openssl_BN_clear_free(x) C.go_openssl_BN_free(y) }() if C.go_openssl_EVP_PKEY_get_bn_param(pkey, paramPubKey, &y) != 1 || From d66349bc9258d1f42959e64799b83aab78785d93 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Mon, 22 Jan 2024 11:25:36 +0100 Subject: [PATCH 08/22] port necessary dsa functions to openssl 1.0.2 --- dsa.go | 27 ++++++++++++++--- goopenssl.h | 4 +++ port_dsa.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 port_dsa.c diff --git a/dsa.go b/dsa.go index 73dcc3e8..dec855ee 100644 --- a/dsa.go +++ b/dsa.go @@ -81,7 +81,11 @@ func GenerateDSAParameters(L, N int) (DSAParameters, error) { switch vMajor { case 1: dsa := getDSA(pkey) - C.go_openssl_DSA_get0_pqg(dsa, &p, &q, &g) + if vMinor == 0 { + C.go_openssl_DSA_get0_pqg_backport(dsa, &p, &q, &g) + } else { + C.go_openssl_DSA_get0_pqg(dsa, &p, &q, &g) + } case 3: defer func() { C.go_openssl_BN_free(p) @@ -142,7 +146,11 @@ func GenerateKeyDSA(params DSAParameters) (*PrivateKeyDSA, error) { switch vMajor { case 1: dsa := getDSA(pkey) - C.go_openssl_DSA_get0_key(dsa, &y, &x) + if vMinor == 0 { + C.go_openssl_DSA_get0_key_backport(dsa, &y, &x) + } else { + C.go_openssl_DSA_get0_key(dsa, &y, &x) + } case 3: defer func() { C.go_openssl_BN_clear_free(x) @@ -187,7 +195,13 @@ func newDSA1(params DSAParameters, X, Y BigInt) (C.GO_EVP_PKEY_PTR, error) { } dsa := C.go_openssl_DSA_new() p, q, g := bigToBN(params.P), bigToBN(params.Q), bigToBN(params.G) - if C.go_openssl_DSA_set0_pqg(dsa, p, q, g) != 1 { + var ret C.int + if vMinor == 0 { + ret = C.go_openssl_DSA_set0_pqg_backport(dsa, p, q, g) + } else { + ret = C.go_openssl_DSA_set0_pqg(dsa, p, q, g) + } + if ret != 1 { C.go_openssl_BN_free(p) C.go_openssl_BN_free(q) C.go_openssl_BN_free(g) @@ -196,7 +210,12 @@ func newDSA1(params DSAParameters, X, Y BigInt) (C.GO_EVP_PKEY_PTR, error) { } if Y != nil { pub, priv := bigToBN(Y), bigToBN(X) - if C.go_openssl_DSA_set0_key(dsa, pub, priv) != 1 { + if vMinor == 0 { + ret = C.go_openssl_DSA_set0_key_backport(dsa, pub, priv) + } else { + ret = C.go_openssl_DSA_set0_key(dsa, pub, priv) + } + if ret != 1 { C.go_openssl_BN_free(pub) C.go_openssl_BN_clear_free(priv) C.go_openssl_DSA_free(dsa) diff --git a/goopenssl.h b/goopenssl.h index e488bf20..a50caa3d 100644 --- a/goopenssl.h +++ b/goopenssl.h @@ -28,6 +28,10 @@ int go_openssl_version_patch(void* handle); int go_openssl_thread_setup(void); void go_openssl_load_functions(void* handle, unsigned int major, unsigned int minor, unsigned int patch); const GO_EVP_MD_PTR go_openssl_EVP_md5_sha1_backport(void); +void go_openssl_DSA_get0_pqg_backport(const GO_DSA_PTR d, GO_BIGNUM_PTR *p, GO_BIGNUM_PTR *q, GO_BIGNUM_PTR *g); +int go_openssl_DSA_set0_pqg_backport(GO_DSA_PTR d, GO_BIGNUM_PTR p, GO_BIGNUM_PTR q, GO_BIGNUM_PTR g); +void go_openssl_DSA_get0_key_backport(const GO_DSA_PTR d, GO_BIGNUM_PTR *pub_key, GO_BIGNUM_PTR *priv_key); +int go_openssl_DSA_set0_key_backport(GO_DSA_PTR d, GO_BIGNUM_PTR pub_key, GO_BIGNUM_PTR priv_key); // Define pointers to all the used OpenSSL functions. // Calling C function pointers from Go is currently not supported. diff --git a/port_dsa.c b/port_dsa.c new file mode 100644 index 00000000..e96c8500 --- /dev/null +++ b/port_dsa.c @@ -0,0 +1,83 @@ +// The following is a partial backport of crypto/dsa/dsa_pmeth.c, +// commit cbc8a839959418d8a2c2e3ec6bdf394852c9501e on the +// OpenSSL_1_1_0-stable branch. The ctrl function has been removed. + +#include "goopenssl.h" + +struct dsa_st +{ + int _ignored0; + long _ignored1; + int _ignored2; + GO_BIGNUM_PTR p; + GO_BIGNUM_PTR q; + GO_BIGNUM_PTR g; + GO_BIGNUM_PTR pub_key; + GO_BIGNUM_PTR priv_key; +}; + + +void go_openssl_DSA_get0_pqg_backport(const GO_DSA_PTR dsa, + GO_BIGNUM_PTR *p, GO_BIGNUM_PTR *q, GO_BIGNUM_PTR *g) +{ + const struct dsa_st *d = dsa; + if (p != NULL) + *p = d->p; + if (q != NULL) + *q = d->q; + if (g != NULL) + *g = d->g; +} + +int go_openssl_DSA_set0_pqg_backport(GO_DSA_PTR dsa, + GO_BIGNUM_PTR p, GO_BIGNUM_PTR q, GO_BIGNUM_PTR g) +{ + struct dsa_st *d = dsa; + if ((d->p == NULL && p == NULL) + || (d->q == NULL && q == NULL) + || (d->g == NULL && g == NULL)) + return 0; + + if (p != NULL) { + go_openssl_BN_free(d->p); + d->p = p; + } + if (q != NULL) { + go_openssl_BN_free(d->q); + d->q = q; + } + if (g != NULL) { + go_openssl_BN_free(d->g); + d->g = g; + } + + return 1; +} + +void go_openssl_DSA_get0_key_backport(const GO_DSA_PTR dsa, + GO_BIGNUM_PTR *pub_key, GO_BIGNUM_PTR *priv_key) +{ + const struct dsa_st *d = dsa; + if (pub_key != NULL) + *pub_key = d->pub_key; + if (priv_key != NULL) + *priv_key = d->priv_key; +} + +int go_openssl_DSA_set0_key_backport(GO_DSA_PTR dsa, GO_BIGNUM_PTR pub_key, GO_BIGNUM_PTR priv_key) +{ + struct dsa_st *d = dsa; + if (d->pub_key == NULL && pub_key == NULL) + return 0; + + if (pub_key != NULL) { + go_openssl_BN_free(d->pub_key); + d->pub_key = pub_key; + } + if (priv_key != NULL) { + go_openssl_BN_free(d->priv_key); + d->priv_key = priv_key; + } + + return 1; +} \ No newline at end of file From e0c41f4fee20680ebc147ef34bf86d36f27fa084 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Mon, 22 Jan 2024 11:34:38 +0100 Subject: [PATCH 09/22] fix legacy macros --- shims.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/shims.h b/shims.h index 489a80d7..fb16e91a 100644 --- a/shims.h +++ b/shims.h @@ -379,8 +379,8 @@ DEFINEFUNC_3_0(void, EVP_SIGNATURE_free, (GO_EVP_SIGNATURE_PTR signature), (sign DEFINEFUNC_LEGACY_1(GO_DSA_PTR, DSA_new, (void), ()) \ DEFINEFUNC_LEGACY_1(void, DSA_free, (GO_DSA_PTR r), (r)) \ DEFINEFUNC_LEGACY_1(int, DSA_generate_key, (GO_DSA_PTR a), (a)) \ -DEFINEFUNC_LEGACY_1(void, DSA_get0_pqg, (const GO_DSA_PTR d, const GO_BIGNUM_PTR *p, const GO_BIGNUM_PTR *q, const GO_BIGNUM_PTR *g), (d, p, q, g)) \ -DEFINEFUNC_LEGACY_1(int, DSA_set0_pqg, (GO_DSA_PTR d, GO_BIGNUM_PTR p, GO_BIGNUM_PTR q, GO_BIGNUM_PTR g), (d, p, q, g)) \ -DEFINEFUNC_LEGACY_1(void, DSA_get0_key, (const GO_DSA_PTR d, const GO_BIGNUM_PTR *pub_key, const GO_BIGNUM_PTR *priv_key), (d, pub_key, priv_key)) \ -DEFINEFUNC_LEGACY_1(int, DSA_set0_key, (GO_DSA_PTR d, GO_BIGNUM_PTR pub_key, GO_BIGNUM_PTR priv_key), (d, pub_key, priv_key)) \ +DEFINEFUNC_LEGACY_1_1(void, DSA_get0_pqg, (const GO_DSA_PTR d, const GO_BIGNUM_PTR *p, const GO_BIGNUM_PTR *q, const GO_BIGNUM_PTR *g), (d, p, q, g)) \ +DEFINEFUNC_LEGACY_1_1(int, DSA_set0_pqg, (GO_DSA_PTR d, GO_BIGNUM_PTR p, GO_BIGNUM_PTR q, GO_BIGNUM_PTR g), (d, p, q, g)) \ +DEFINEFUNC_LEGACY_1_1(void, DSA_get0_key, (const GO_DSA_PTR d, const GO_BIGNUM_PTR *pub_key, const GO_BIGNUM_PTR *priv_key), (d, pub_key, priv_key)) \ +DEFINEFUNC_LEGACY_1_1(int, DSA_set0_key, (GO_DSA_PTR d, GO_BIGNUM_PTR pub_key, GO_BIGNUM_PTR priv_key), (d, pub_key, priv_key)) \ From cb8a4489262272a9968fabe479600b026b1a444f Mon Sep 17 00:00:00 2001 From: Quim Muntal Date: Mon, 22 Jan 2024 12:10:50 +0000 Subject: [PATCH 10/22] port support openssl 3 --- dsa.go | 42 ++++++++++++++++++++++++++++++++++-------- dsa_test.go | 12 +++++++++--- shims.h | 1 + 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/dsa.go b/dsa.go index dec855ee..51905914 100644 --- a/dsa.go +++ b/dsa.go @@ -249,34 +249,60 @@ func newDSA3(params DSAParameters, X, Y BigInt) (C.GO_EVP_PKEY_PTR, error) { return nil, newOpenSSLError("OSSL_PARAM_BLD_new") } defer C.go_openssl_OSSL_PARAM_BLD_free(bld) - selection := C.int(C.GO_EVP_PKEY_PUBLIC_KEY) - pub := bigToBN(Y) - defer C.go_openssl_BN_free(pub) - if C.go_openssl_OSSL_PARAM_BLD_push_BN(bld, paramPubKey, pub) != 1 { + p, q, g := bigToBN(params.P), bigToBN(params.Q), bigToBN(params.G) + defer func() { + C.go_openssl_BN_free(p) + C.go_openssl_BN_free(q) + C.go_openssl_BN_free(g) + }() + if C.go_openssl_OSSL_PARAM_BLD_push_BN(bld, paramP, p) != 1 || + C.go_openssl_OSSL_PARAM_BLD_push_BN(bld, paramQ, q) != 1 || + C.go_openssl_OSSL_PARAM_BLD_push_BN(bld, paramG, g) != 1 { return nil, newOpenSSLError("OSSL_PARAM_BLD_push_BN") } + selection := C.int(C.GO_EVP_PKEY_KEYPAIR) + if Y != nil { + pub := bigToBN(Y) + defer C.go_openssl_BN_free(pub) + if C.go_openssl_OSSL_PARAM_BLD_push_BN(bld, paramPubKey, pub) != 1 { + return nil, newOpenSSLError("OSSL_PARAM_BLD_push_BN") + } + selection = C.int(C.GO_EVP_PKEY_PUBLIC_KEY) + } if X != nil { priv := bigToBN(X) defer C.go_openssl_BN_clear_free(priv) if C.go_openssl_OSSL_PARAM_BLD_push_BN(bld, paramPrivKey, priv) != 1 { return nil, newOpenSSLError("OSSL_PARAM_BLD_push_BN") } - selection = C.GO_EVP_PKEY_KEYPAIR } bldparams := C.go_openssl_OSSL_PARAM_BLD_to_param(bld) if bldparams == nil { return nil, newOpenSSLError("OSSL_PARAM_BLD_to_param") } defer C.go_openssl_OSSL_PARAM_free(bldparams) - pkey, err := newEvpFromParams(C.GO_EVP_PKEY_EC, selection, bldparams) + pkey, err := newEvpFromParams(C.GO_EVP_PKEY_DSA, selection, bldparams) if err != nil { return nil, err } if Y != nil { return pkey, nil } - // Generate the key. - return nil, nil + // pkey doesn't contain the public/private components. We use it + // as domain parameters placeholder to generate the final key. + defer C.go_openssl_EVP_PKEY_free(pkey) + ctx := C.go_openssl_EVP_PKEY_CTX_new_from_pkey(nil, pkey, nil) + if ctx == nil { + return nil, newOpenSSLError("EVP_PKEY_CTX_new_from_pkey") + } + if C.go_openssl_EVP_PKEY_keygen_init(ctx) != 1 { + return nil, newOpenSSLError("EVP_PKEY_keygen_init") + } + var gkey C.GO_EVP_PKEY_PTR + if C.go_openssl_EVP_PKEY_keygen(ctx, &gkey) != 1 { + return nil, newOpenSSLError("EVP_PKEY_keygen") + } + return gkey, nil } // getDSA returns the DSA from pkey. diff --git a/dsa_test.go b/dsa_test.go index 7f1fee2a..249791a3 100644 --- a/dsa_test.go +++ b/dsa_test.go @@ -146,9 +146,15 @@ func TestDSANewPrivateKeyWithDegenerateKeys(t *testing.T) { Q: bbig.Enc(fromHex(test.q)), G: bbig.Enc(fromHex(test.g)), } - _, err := openssl.NewPrivateKeyDSA(params, bbig.Enc(fromHex(test.x)), bbig.Enc(fromHex(test.y))) - if err == nil { - t.Errorf("#%d: error generating key: %s", i, err) + x, y := bbig.Enc(fromHex(test.x)), bbig.Enc(fromHex(test.y)) + priv, err := openssl.NewPrivateKeyDSA(params, x, y) + if err != nil { + // Some OpenSSL 1 fails to create degenerated private keys, which is fine. + continue + } + hashed := []byte("testing") + if _, err := openssl.SignDSA(priv, hashed); err == nil { + t.Errorf("#%d: unexpected success", i) } } } diff --git a/shims.h b/shims.h index fb16e91a..4c9a492b 100644 --- a/shims.h +++ b/shims.h @@ -285,6 +285,7 @@ DEFINEFUNC_LEGACY_1(int, EVP_PKEY_assign, (GO_EVP_PKEY_PTR pkey, int type, void DEFINEFUNC(int, EVP_PKEY_verify, (GO_EVP_PKEY_CTX_PTR ctx, const unsigned char *sig, size_t siglen, const unsigned char *tbs, size_t tbslen), (ctx, sig, siglen, tbs, tbslen)) \ DEFINEFUNC(GO_EVP_PKEY_CTX_PTR, EVP_PKEY_CTX_new, (GO_EVP_PKEY_PTR arg0, GO_ENGINE_PTR arg1), (arg0, arg1)) \ DEFINEFUNC(GO_EVP_PKEY_CTX_PTR, EVP_PKEY_CTX_new_id, (int id, GO_ENGINE_PTR e), (id, e)) \ +DEFINEFUNC_3_0(GO_EVP_PKEY_CTX_PTR, EVP_PKEY_CTX_new_from_pkey, (GO_OSSL_LIB_CTX_PTR libctx, GO_EVP_PKEY_PTR pkey, const char *propquery), (libctx, pkey, propquery)) \ DEFINEFUNC(int, EVP_PKEY_paramgen_init, (GO_EVP_PKEY_CTX_PTR ctx), (ctx)) \ DEFINEFUNC(int, EVP_PKEY_paramgen, (GO_EVP_PKEY_CTX_PTR ctx, GO_EVP_PKEY_PTR *ppkey), (ctx, ppkey)) \ DEFINEFUNC(int, EVP_PKEY_keygen_init, (GO_EVP_PKEY_CTX_PTR ctx), (ctx)) \ From ec760174cf93bff50a6cf205e9608e46b6794f8f Mon Sep 17 00:00:00 2001 From: qmuntal Date: Mon, 22 Jan 2024 15:21:40 +0100 Subject: [PATCH 11/22] fix newDSA3 --- dsa.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dsa.go b/dsa.go index 51905914..7626b17c 100644 --- a/dsa.go +++ b/dsa.go @@ -267,7 +267,9 @@ func newDSA3(params DSAParameters, X, Y BigInt) (C.GO_EVP_PKEY_PTR, error) { if C.go_openssl_OSSL_PARAM_BLD_push_BN(bld, paramPubKey, pub) != 1 { return nil, newOpenSSLError("OSSL_PARAM_BLD_push_BN") } - selection = C.int(C.GO_EVP_PKEY_PUBLIC_KEY) + if X == nil { + selection = C.int(C.GO_EVP_PKEY_PUBLIC_KEY) + } } if X != nil { priv := bigToBN(X) From 94c08b226f07fa7f1ad3378e4c7ae083f851c68f Mon Sep 17 00:00:00 2001 From: qmuntal Date: Mon, 22 Jan 2024 15:44:38 +0100 Subject: [PATCH 12/22] fix openssl3 in fips mode --- dsa_test.go | 52 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/dsa_test.go b/dsa_test.go index 249791a3..62a19670 100644 --- a/dsa_test.go +++ b/dsa_test.go @@ -3,6 +3,7 @@ package openssl_test import ( "crypto/dsa" "encoding/asn1" + "fmt" "math/big" "testing" @@ -15,16 +16,28 @@ type dsaSignature struct { } func TestDSAGenerateParameters(t *testing.T) { - testGenerateDSAParameters(t, 1024, 160) - testGenerateDSAParameters(t, 2048, 224) - testGenerateDSAParameters(t, 2048, 256) - testGenerateDSAParameters(t, 3072, 256) + var tests = []struct { + L, N int + }{ + {1024, 160}, + {2048, 224}, + {2048, 256}, + {3072, 256}, + } + for _, test := range tests { + t.Run(fmt.Sprintf("%d-%d", test.L, test.N), func(t *testing.T) { + if openssl.FIPS() { + t.Skip("generating DSA parameters with L = 2048 is not supported in FIPS mode") + } + testGenerateDSAParameters(t, test.L, test.N) + }) + } } func testGenerateDSAParameters(t *testing.T, L, N int) { params, err := openssl.GenerateDSAParameters(L, N) if err != nil { - t.Errorf("%d-%d: error generating parameters: %s", L, N, err) + t.Errorf("error generating parameters: %s", err) return } @@ -33,11 +46,11 @@ func testGenerateDSAParameters(t *testing.T, L, N int) { G := bbig.Dec(params.G) if P.BitLen() != L { - t.Errorf("%d-%d: params.BitLen got:%d want:%d", L, N, P.BitLen(), L) + t.Errorf("params.BitLen got:%d want:%d", P.BitLen(), L) } if Q.BitLen() != N { - t.Errorf("%d-%d: q.BitLen got:%d want:%d", L, N, Q.BitLen(), L) + t.Errorf("q.BitLen got:%d want:%d", Q.BitLen(), L) } one := new(big.Int) @@ -45,11 +58,11 @@ func testGenerateDSAParameters(t *testing.T, L, N int) { pm1 := new(big.Int).Sub(P, one) quo, rem := new(big.Int).DivMod(pm1, Q, new(big.Int)) if rem.Sign() != 0 { - t.Errorf("%d-%d: p-1 mod q != 0", L, N) + t.Error("p-1 mod q != 0") } x := new(big.Int).Exp(G, quo, P) if x.Cmp(one) == 0 { - t.Errorf("%d-%d: invalid generator", L, N) + t.Error("invalid generator") } priv, err := openssl.GenerateKeyDSA(params) @@ -58,23 +71,23 @@ func testGenerateDSAParameters(t *testing.T, L, N int) { return } - testDSASignAndVerify(t, L, priv) + testDSASignAndVerify(t, priv) } -func testDSASignAndVerify(t *testing.T, i int, priv *openssl.PrivateKeyDSA) { +func testDSASignAndVerify(t *testing.T, priv *openssl.PrivateKeyDSA) { hashed := []byte("testing") sig, err := openssl.SignDSA(priv, hashed[:]) if err != nil { - t.Errorf("%d: error signing: %s", i, err) + t.Errorf("error signing: %s", err) return } pub, err := openssl.NewPublicKeyDSA(priv.DSAParameters, priv.Y) if err != nil { - t.Errorf("%d: error getting public key: %s", i, err) + t.Errorf("error getting public key: %s", err) return } if !openssl.VerifyDSA(pub, hashed[:], sig) { - t.Errorf("%d: error verifying", i) + t.Error("error verifying") return } @@ -96,11 +109,11 @@ func testDSASignAndVerify(t *testing.T, i int, priv *openssl.PrivateKeyDSA) { return } if !dsa.Verify(&priv1.PublicKey, hashed[:], esig.R, esig.S) { - t.Errorf("%d: compat: crypto/dsa can't verify OpenSSL signature", i) + t.Error("compat: crypto/dsa can't verify OpenSSL signature") } r1, s1, err := dsa.Sign(openssl.RandReader, &priv1, hashed[:]) if err != nil { - t.Errorf("%d: error signing: %s", i, err) + t.Errorf("error signing: %s", err) return } sig, err = asn1.Marshal(dsaSignature{r1, s1}) @@ -109,12 +122,15 @@ func testDSASignAndVerify(t *testing.T, i int, priv *openssl.PrivateKeyDSA) { return } if !openssl.VerifyDSA(pub, hashed[:], sig) { - t.Errorf("%d: compat: OpenSSL can't verify crypto/dsa signature", i) + t.Error("compat: OpenSSL can't verify crypto/dsa signature") return } } func TestDSASignAndVerify(t *testing.T) { + if openssl.FIPS() { + t.Skip("DSA signing with L = 2048 is not supported in FIPS mode") + } params := openssl.DSAParameters{ P: bbig.Enc(fromHex("A9B5B793FB4785793D246BAE77E8FF63CA52F442DA763C440259919FE1BC1D6065A9350637A04F75A2F039401D49F08E066C4D275A5A65DA5684BC563C14289D7AB8A67163BFBF79D85972619AD2CFF55AB0EE77A9002B0EF96293BDD0F42685EBB2C66C327079F6C98000FBCB79AACDE1BC6F9D5C7B1A97E3D9D54ED7951FEF")), Q: bbig.Enc(fromHex("E1D3391245933D68A0714ED34BBCB7A1F422B9C1")), @@ -127,7 +143,7 @@ func TestDSASignAndVerify(t *testing.T) { t.Fatalf("error generating key: %s", err) } - testDSASignAndVerify(t, 0, priv) + testDSASignAndVerify(t, priv) } func TestDSANewPrivateKeyWithDegenerateKeys(t *testing.T) { From b50952f8059f34e4cfba334e2848e5aed9d642e9 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Mon, 22 Jan 2024 15:57:11 +0100 Subject: [PATCH 13/22] free context in newDSA3 --- dsa.go | 1 + 1 file changed, 1 insertion(+) diff --git a/dsa.go b/dsa.go index 7626b17c..1339cb17 100644 --- a/dsa.go +++ b/dsa.go @@ -297,6 +297,7 @@ func newDSA3(params DSAParameters, X, Y BigInt) (C.GO_EVP_PKEY_PTR, error) { if ctx == nil { return nil, newOpenSSLError("EVP_PKEY_CTX_new_from_pkey") } + defer C.go_openssl_EVP_PKEY_CTX_free(ctx) if C.go_openssl_EVP_PKEY_keygen_init(ctx) != 1 { return nil, newOpenSSLError("EVP_PKEY_keygen_init") } From 6162805f19df7fb22417d79f8ceba65071dd5d5b Mon Sep 17 00:00:00 2001 From: qmuntal Date: Mon, 2 Sep 2024 09:44:31 +0200 Subject: [PATCH 14/22] add some comments --- dsa.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/dsa.go b/dsa.go index 1339cb17..d9092d9d 100644 --- a/dsa.go +++ b/dsa.go @@ -58,6 +58,10 @@ func (k *PublicKeyDSA) withKey(f func(C.GO_EVP_PKEY_PTR) C.int) C.int { // GenerateDSAParameters generates a set of DSA parameters. func GenerateDSAParameters(L, N int) (DSAParameters, error) { + // The DSA parameters are generated by creating a new DSA key and + // extracting the domain parameters from it. + + // Generate a new DSA key context and set the known parameters. ctx := C.go_openssl_EVP_PKEY_CTX_new_id(C.GO_EVP_PKEY_DSA, nil) if ctx == nil { return DSAParameters{}, newOpenSSLError("EVP_PKEY_CTX_new_id failed") @@ -77,6 +81,8 @@ func GenerateDSAParameters(L, N int) (DSAParameters, error) { return DSAParameters{}, newOpenSSLError("EVP_PKEY_paramgen failed") } defer C.go_openssl_EVP_PKEY_free(pkey) + + // Extract the domain parameters from the generated key. var p, q, g C.GO_BIGNUM_PTR switch vMajor { case 1: @@ -290,8 +296,9 @@ func newDSA3(params DSAParameters, X, Y BigInt) (C.GO_EVP_PKEY_PTR, error) { if Y != nil { return pkey, nil } - // pkey doesn't contain the public/private components. We use it - // as domain parameters placeholder to generate the final key. + // pkey doesn't contain the public component, but the crypto/dsa expects + // it to be always there. Generate a new key using pkey as domain parameters + // placeholder. defer C.go_openssl_EVP_PKEY_free(pkey) ctx := C.go_openssl_EVP_PKEY_CTX_new_from_pkey(nil, pkey, nil) if ctx == nil { From da6c23dfc6c7dcfdbbfe310c534a202b552d5f8f Mon Sep 17 00:00:00 2001 From: Quim Muntal Date: Tue, 3 Sep 2024 08:33:22 +0200 Subject: [PATCH 15/22] Apply suggestions from code review Co-authored-by: Martijn Verburg --- dsa_test.go | 3 +-- port_dsa.c | 1 - shims.h | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/dsa_test.go b/dsa_test.go index 62a19670..a657d765 100644 --- a/dsa_test.go +++ b/dsa_test.go @@ -147,8 +147,7 @@ func TestDSASignAndVerify(t *testing.T) { } func TestDSANewPrivateKeyWithDegenerateKeys(t *testing.T) { - // Signing with degenerate private keys should not cause an infinite - // loop. + // Signing with degenerate private keys should not cause an infinite loop badKeys := []struct { p, q, g, y, x string }{ diff --git a/port_dsa.c b/port_dsa.c index e96c8500..58cf2295 100644 --- a/port_dsa.c +++ b/port_dsa.c @@ -16,7 +16,6 @@ struct dsa_st GO_BIGNUM_PTR priv_key; }; - void go_openssl_DSA_get0_pqg_backport(const GO_DSA_PTR dsa, GO_BIGNUM_PTR *p, GO_BIGNUM_PTR *q, GO_BIGNUM_PTR *g) { diff --git a/shims.h b/shims.h index 4c9a492b..98c6cfe3 100644 --- a/shims.h +++ b/shims.h @@ -22,7 +22,7 @@ enum { GO_EVP_PKEY_DSA = 116, /* This is defined differently in OpenSSL 3 (1 << 11), but in our * code it is only used in OpenSSL 1. - */ + */ GO1_EVP_PKEY_OP_DERIVE = (1 << 10), GO_EVP_MAX_MD_SIZE = 64, From 3f800cf4bcf93a623ec098591049953d7ed65391 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Tue, 3 Sep 2024 09:02:20 +0200 Subject: [PATCH 16/22] improve port_dsa.c description --- port_dsa.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/port_dsa.c b/port_dsa.c index 58cf2295..5a948eaf 100644 --- a/port_dsa.c +++ b/port_dsa.c @@ -1,6 +1,7 @@ -// The following is a partial backport of crypto/dsa/dsa_pmeth.c, -// commit cbc8a839959418d8a2c2e3ec6bdf394852c9501e on the -// OpenSSL_1_1_0-stable branch. The ctrl function has been removed. +// The following is a partial backport of crypto/dsa/dsa_lockl.h +// and crypto/dsa/dsa_lib.c, commit cbc8a839959418d8a2c2e3ec6bdf394852c9501e +// on the OpenSSL_1_1_0-stable branch. Only pqg and key getters/setters +// are backported. #include "goopenssl.h" @@ -14,6 +15,8 @@ struct dsa_st GO_BIGNUM_PTR g; GO_BIGNUM_PTR pub_key; GO_BIGNUM_PTR priv_key; + // The following members are not used by our backport, + // so we don't define them here. }; void go_openssl_DSA_get0_pqg_backport(const GO_DSA_PTR dsa, From 566095d1e015920e0f7a5d580484737ec0faa4c5 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Tue, 3 Sep 2024 09:24:54 +0200 Subject: [PATCH 17/22] implement checkMajorVersion --- dsa.go | 10 ++++------ ecdh.go | 10 ++++------ ecdsa.go | 10 ++++------ openssl.go | 7 +++++++ 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/dsa.go b/dsa.go index d9092d9d..286e097f 100644 --- a/dsa.go +++ b/dsa.go @@ -196,9 +196,8 @@ func newDSA(params DSAParameters, X, Y BigInt) (C.GO_EVP_PKEY_PTR, error) { } func newDSA1(params DSAParameters, X, Y BigInt) (C.GO_EVP_PKEY_PTR, error) { - if vMajor != 1 { - panic("incorrect vMajor version") - } + checkMajorVersion(1) + dsa := C.go_openssl_DSA_new() p, q, g := bigToBN(params.P), bigToBN(params.Q), bigToBN(params.G) var ret C.int @@ -247,9 +246,8 @@ func newDSA1(params DSAParameters, X, Y BigInt) (C.GO_EVP_PKEY_PTR, error) { } func newDSA3(params DSAParameters, X, Y BigInt) (C.GO_EVP_PKEY_PTR, error) { - if vMajor != 3 { - panic("incorrect vMajor version") - } + checkMajorVersion(3) + bld := C.go_openssl_OSSL_PARAM_BLD_new() if bld == nil { return nil, newOpenSSLError("OSSL_PARAM_BLD_new") diff --git a/ecdh.go b/ecdh.go index a1e627ef..911a1f3b 100644 --- a/ecdh.go +++ b/ecdh.go @@ -127,9 +127,8 @@ func newECDHPkey(curve string, bytes []byte, isPrivate bool) (C.GO_EVP_PKEY_PTR, } func newECDHPkey1(nid C.int, bytes []byte, isPrivate bool) (pkey C.GO_EVP_PKEY_PTR, err error) { - if vMajor != 1 { - panic("incorrect vMajor version") - } + checkMajorVersion(1) + key := C.go_openssl_EC_KEY_new_by_curve_name(nid) if key == nil { return nil, newOpenSSLError("EC_KEY_new_by_curve_name") @@ -166,9 +165,8 @@ func newECDHPkey1(nid C.int, bytes []byte, isPrivate bool) (pkey C.GO_EVP_PKEY_P } func newECDHPkey3(nid C.int, bytes []byte, isPrivate bool) (C.GO_EVP_PKEY_PTR, error) { - if vMajor != 3 { - panic("incorrect vMajor version") - } + checkMajorVersion(3) + bld := C.go_openssl_OSSL_PARAM_BLD_new() if bld == nil { return nil, newOpenSSLError("OSSL_PARAM_BLD_new") diff --git a/ecdsa.go b/ecdsa.go index 46b16abf..f5dca5ce 100644 --- a/ecdsa.go +++ b/ecdsa.go @@ -149,9 +149,8 @@ func newECDSAKey(curve string, X, Y, D BigInt) (C.GO_EVP_PKEY_PTR, error) { } func newECDSAKey1(nid C.int, bx, by, bd C.GO_BIGNUM_PTR) (pkey C.GO_EVP_PKEY_PTR, err error) { - if vMajor != 1 { - panic("incorrect vMajor version") - } + checkMajorVersion(1) + key := C.go_openssl_EC_KEY_new_by_curve_name(nid) if key == nil { return nil, newOpenSSLError("EC_KEY_new_by_curve_name failed") @@ -171,9 +170,8 @@ func newECDSAKey1(nid C.int, bx, by, bd C.GO_BIGNUM_PTR) (pkey C.GO_EVP_PKEY_PTR } func newECDSAKey3(nid C.int, bx, by, bd C.GO_BIGNUM_PTR) (C.GO_EVP_PKEY_PTR, error) { - if vMajor != 3 { - panic("incorrect vMajor version") - } + checkMajorVersion(3) + // Create the encoded public key public key from bx and by. pubBytes, err := generateAndEncodeEcPublicKey(nid, func(group C.GO_EC_GROUP_PTR) (C.GO_EC_POINT_PTR, error) { pt := C.go_openssl_EC_POINT_new(group) diff --git a/openssl.go b/openssl.go index 691bb16f..7cbac306 100644 --- a/openssl.go +++ b/openssl.go @@ -77,6 +77,13 @@ func errUnsupportedVersion() error { return errors.New("openssl: OpenSSL version: " + utoa(vMajor) + "." + utoa(vMinor) + "." + utoa(vPatch)) } +// checkMajorVersion panics if the current major version is not expected. +func checkMajorVersion(expected uint) { + if vMajor != expected { + panic("openssl: incorrect major version (" + strconv.Itoa(int(expected)) + "), expected " + strconv.Itoa(int(expected))) + } +} + type fail string func (e fail) Error() string { return "openssl: " + string(e) + " failed" } From 45e7f46391b7511c0290b63dd9bb10d244928077 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Wed, 4 Sep 2024 10:10:34 +0200 Subject: [PATCH 18/22] use lowercase for parameters --- dsa.go | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/dsa.go b/dsa.go index 286e097f..ab00953a 100644 --- a/dsa.go +++ b/dsa.go @@ -57,7 +57,7 @@ func (k *PublicKeyDSA) withKey(f func(C.GO_EVP_PKEY_PTR) C.int) C.int { } // GenerateDSAParameters generates a set of DSA parameters. -func GenerateDSAParameters(L, N int) (DSAParameters, error) { +func GenerateDSAParameters(l, n int) (DSAParameters, error) { // The DSA parameters are generated by creating a new DSA key and // extracting the domain parameters from it. @@ -70,10 +70,10 @@ func GenerateDSAParameters(L, N int) (DSAParameters, error) { if C.go_openssl_EVP_PKEY_paramgen_init(ctx) != 1 { return DSAParameters{}, newOpenSSLError("EVP_PKEY_paramgen_init failed") } - if C.go_openssl_EVP_PKEY_CTX_ctrl(ctx, C.GO_EVP_PKEY_DSA, -1, C.GO_EVP_PKEY_CTRL_DSA_PARAMGEN_BITS, C.int(L), nil) != 1 { + if C.go_openssl_EVP_PKEY_CTX_ctrl(ctx, C.GO_EVP_PKEY_DSA, -1, C.GO_EVP_PKEY_CTRL_DSA_PARAMGEN_BITS, C.int(l), nil) != 1 { return DSAParameters{}, newOpenSSLError("EVP_PKEY_CTX_ctrl failed") } - if C.go_openssl_EVP_PKEY_CTX_ctrl(ctx, C.GO_EVP_PKEY_DSA, -1, C.GO_EVP_PKEY_CTRL_DSA_PARAMGEN_Q_BITS, C.int(N), nil) != 1 { + if C.go_openssl_EVP_PKEY_CTX_ctrl(ctx, C.GO_EVP_PKEY_DSA, -1, C.GO_EVP_PKEY_CTRL_DSA_PARAMGEN_Q_BITS, C.int(n), nil) != 1 { return DSAParameters{}, newOpenSSLError("EVP_PKEY_CTX_ctrl failed") } var pkey C.GO_EVP_PKEY_PTR @@ -115,29 +115,29 @@ func GenerateDSAParameters(L, N int) (DSAParameters, error) { } // NewPrivateKeyDSA creates a new DSA private key from the given parameters. -func NewPrivateKeyDSA(params DSAParameters, X, Y BigInt) (*PrivateKeyDSA, error) { - if X == nil || Y == nil { - panic("X and Y must not be nil") +func NewPrivateKeyDSA(params DSAParameters, x, y BigInt) (*PrivateKeyDSA, error) { + if x == nil || y == nil { + panic("x and y must not be nil") } - pkey, err := newDSA(params, X, Y) + pkey, err := newDSA(params, x, y) if err != nil { return nil, err } - k := &PrivateKeyDSA{params, X, Y, pkey} + k := &PrivateKeyDSA{params, x, y, pkey} runtime.SetFinalizer(k, (*PrivateKeyDSA).finalize) return k, nil } // NewPublicKeyDSA creates a new DSA public key from the given parameters. -func NewPublicKeyDSA(params DSAParameters, Y BigInt) (*PublicKeyDSA, error) { - if Y == nil { - panic("Y must not be nil") +func NewPublicKeyDSA(params DSAParameters, y BigInt) (*PublicKeyDSA, error) { + if y == nil { + panic("y must not be nil") } - pkey, err := newDSA(params, nil, Y) + pkey, err := newDSA(params, nil, y) if err != nil { return nil, err } - k := &PublicKeyDSA{params, Y, pkey} + k := &PublicKeyDSA{params, y, pkey} runtime.SetFinalizer(k, (*PublicKeyDSA).finalize) return k, nil } @@ -184,18 +184,18 @@ func VerifyDSA(pub *PublicKeyDSA, hash []byte, sig []byte) bool { return evpVerify(pub.withKey, 0, 0, 0, sig, hash) == nil } -func newDSA(params DSAParameters, X, Y BigInt) (C.GO_EVP_PKEY_PTR, error) { +func newDSA(params DSAParameters, x, y BigInt) (C.GO_EVP_PKEY_PTR, error) { switch vMajor { case 1: - return newDSA1(params, X, Y) + return newDSA1(params, x, y) case 3: - return newDSA3(params, X, Y) + return newDSA3(params, x, y) default: panic(errUnsupportedVersion()) } } -func newDSA1(params DSAParameters, X, Y BigInt) (C.GO_EVP_PKEY_PTR, error) { +func newDSA1(params DSAParameters, x, y BigInt) (C.GO_EVP_PKEY_PTR, error) { checkMajorVersion(1) dsa := C.go_openssl_DSA_new() @@ -213,8 +213,8 @@ func newDSA1(params DSAParameters, X, Y BigInt) (C.GO_EVP_PKEY_PTR, error) { C.go_openssl_DSA_free(dsa) return nil, newOpenSSLError("DSA_set0_pqg failed") } - if Y != nil { - pub, priv := bigToBN(Y), bigToBN(X) + if y != nil { + pub, priv := bigToBN(y), bigToBN(x) if vMinor == 0 { ret = C.go_openssl_DSA_set0_key_backport(dsa, pub, priv) } else { @@ -245,7 +245,7 @@ func newDSA1(params DSAParameters, X, Y BigInt) (C.GO_EVP_PKEY_PTR, error) { return pkey, nil } -func newDSA3(params DSAParameters, X, Y BigInt) (C.GO_EVP_PKEY_PTR, error) { +func newDSA3(params DSAParameters, x, y BigInt) (C.GO_EVP_PKEY_PTR, error) { checkMajorVersion(3) bld := C.go_openssl_OSSL_PARAM_BLD_new() @@ -265,18 +265,18 @@ func newDSA3(params DSAParameters, X, Y BigInt) (C.GO_EVP_PKEY_PTR, error) { return nil, newOpenSSLError("OSSL_PARAM_BLD_push_BN") } selection := C.int(C.GO_EVP_PKEY_KEYPAIR) - if Y != nil { - pub := bigToBN(Y) + if y != nil { + pub := bigToBN(y) defer C.go_openssl_BN_free(pub) if C.go_openssl_OSSL_PARAM_BLD_push_BN(bld, paramPubKey, pub) != 1 { return nil, newOpenSSLError("OSSL_PARAM_BLD_push_BN") } - if X == nil { + if x == nil { selection = C.int(C.GO_EVP_PKEY_PUBLIC_KEY) } } - if X != nil { - priv := bigToBN(X) + if x != nil { + priv := bigToBN(x) defer C.go_openssl_BN_clear_free(priv) if C.go_openssl_OSSL_PARAM_BLD_push_BN(bld, paramPrivKey, priv) != 1 { return nil, newOpenSSLError("OSSL_PARAM_BLD_push_BN") @@ -291,7 +291,7 @@ func newDSA3(params DSAParameters, X, Y BigInt) (C.GO_EVP_PKEY_PTR, error) { if err != nil { return nil, err } - if Y != nil { + if y != nil { return pkey, nil } // pkey doesn't contain the public component, but the crypto/dsa expects From 9dca536da538c36743a4e900def1e47c972c85c7 Mon Sep 17 00:00:00 2001 From: Quim Muntal Date: Thu, 5 Sep 2024 11:22:48 +0200 Subject: [PATCH 19/22] Apply suggestions from code review Co-authored-by: Davis Goodin --- dsa.go | 2 ++ openssl.go | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/dsa.go b/dsa.go index ab00953a..f8bdddc2 100644 --- a/dsa.go +++ b/dsa.go @@ -27,6 +27,7 @@ type PrivateKeyDSA struct { DSAParameters X, Y BigInt + // _pkey MUST NOT be accessed directly. Instead, use the withKey method. _pkey C.GO_EVP_PKEY_PTR } @@ -44,6 +45,7 @@ type PublicKeyDSA struct { DSAParameters Y BigInt + // _pkey MUST NOT be accessed directly. Instead, use the withKey method. _pkey C.GO_EVP_PKEY_PTR } diff --git a/openssl.go b/openssl.go index 7cbac306..d82f5266 100644 --- a/openssl.go +++ b/openssl.go @@ -80,7 +80,7 @@ func errUnsupportedVersion() error { // checkMajorVersion panics if the current major version is not expected. func checkMajorVersion(expected uint) { if vMajor != expected { - panic("openssl: incorrect major version (" + strconv.Itoa(int(expected)) + "), expected " + strconv.Itoa(int(expected))) + panic("openssl: incorrect major version (" + strconv.Itoa(int(vMajor)) + "), expected " + strconv.Itoa(int(expected))) } } From 303fca308bb341cd69e96d21efec48fb9684708c Mon Sep 17 00:00:00 2001 From: qmuntal Date: Thu, 5 Sep 2024 11:28:19 +0200 Subject: [PATCH 20/22] use OpenSSL naming convention for FFC params --- dsa.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/dsa.go b/dsa.go index ab00953a..562e250b 100644 --- a/dsa.go +++ b/dsa.go @@ -10,11 +10,11 @@ import ( ) var ( - paramPbits = C.CString("pbits") - paramQbits = C.CString("qbits") - paramP = C.CString("p") - paramQ = C.CString("q") - paramG = C.CString("g") + OSSL_PKEY_PARAM_FFC_PBITS = C.CString("pbits") + OSSL_PKEY_PARAM_FFC_QBITS = C.CString("qbits") + OSSL_PKEY_PARAM_FFC_P = C.CString("p") + OSSL_PKEY_PARAM_FFC_Q = C.CString("q") + OSSL_PKEY_PARAM_FFC_G = C.CString("g") ) // DSAParameters contains the DSA parameters. @@ -98,9 +98,9 @@ func GenerateDSAParameters(l, n int) (DSAParameters, error) { C.go_openssl_BN_free(q) C.go_openssl_BN_free(g) }() - if C.go_openssl_EVP_PKEY_get_bn_param(pkey, paramP, &p) != 1 || - C.go_openssl_EVP_PKEY_get_bn_param(pkey, paramQ, &q) != 1 || - C.go_openssl_EVP_PKEY_get_bn_param(pkey, paramG, &g) != 1 { + if C.go_openssl_EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_FFC_P, &p) != 1 || + C.go_openssl_EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_FFC_Q, &q) != 1 || + C.go_openssl_EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_FFC_G, &g) != 1 { return DSAParameters{}, newOpenSSLError("EVP_PKEY_get_bn_param") } default: @@ -259,9 +259,9 @@ func newDSA3(params DSAParameters, x, y BigInt) (C.GO_EVP_PKEY_PTR, error) { C.go_openssl_BN_free(q) C.go_openssl_BN_free(g) }() - if C.go_openssl_OSSL_PARAM_BLD_push_BN(bld, paramP, p) != 1 || - C.go_openssl_OSSL_PARAM_BLD_push_BN(bld, paramQ, q) != 1 || - C.go_openssl_OSSL_PARAM_BLD_push_BN(bld, paramG, g) != 1 { + if C.go_openssl_OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_P, p) != 1 || + C.go_openssl_OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_Q, q) != 1 || + C.go_openssl_OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_G, g) != 1 { return nil, newOpenSSLError("OSSL_PARAM_BLD_push_BN") } selection := C.int(C.GO_EVP_PKEY_KEYPAIR) From 117675d679c105c25e306ad7ec9c82278ea03274 Mon Sep 17 00:00:00 2001 From: Quim Muntal Date: Fri, 6 Sep 2024 08:47:05 +0200 Subject: [PATCH 21/22] Apply suggestions from code review Co-authored-by: Davis Goodin --- dsa.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dsa.go b/dsa.go index 22d0a1b4..7ea50cf7 100644 --- a/dsa.go +++ b/dsa.go @@ -264,6 +264,7 @@ func newDSA3(params DSAParameters, x, y BigInt) (C.GO_EVP_PKEY_PTR, error) { if C.go_openssl_OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_P, p) != 1 || C.go_openssl_OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_Q, q) != 1 || C.go_openssl_OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_G, g) != 1 { + return nil, newOpenSSLError("OSSL_PARAM_BLD_push_BN") } selection := C.int(C.GO_EVP_PKEY_KEYPAIR) @@ -296,9 +297,9 @@ func newDSA3(params DSAParameters, x, y BigInt) (C.GO_EVP_PKEY_PTR, error) { if y != nil { return pkey, nil } - // pkey doesn't contain the public component, but the crypto/dsa expects - // it to be always there. Generate a new key using pkey as domain parameters - // placeholder. + // pkey doesn't contain the public component, but the crypto/dsa package + // expects it to be always there. Generate a new key using pkey as domain + // parameters placeholder. defer C.go_openssl_EVP_PKEY_free(pkey) ctx := C.go_openssl_EVP_PKEY_CTX_new_from_pkey(nil, pkey, nil) if ctx == nil { From d9bd6fd24f0ae9398da9d81d77e8c17f9ad1b0c3 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Fri, 6 Sep 2024 08:55:10 +0200 Subject: [PATCH 22/22] call DSA_free in a deferred function --- dsa.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/dsa.go b/dsa.go index 7ea50cf7..0172d393 100644 --- a/dsa.go +++ b/dsa.go @@ -197,10 +197,19 @@ func newDSA(params DSAParameters, x, y BigInt) (C.GO_EVP_PKEY_PTR, error) { } } -func newDSA1(params DSAParameters, x, y BigInt) (C.GO_EVP_PKEY_PTR, error) { +func newDSA1(params DSAParameters, x, y BigInt) (pkey C.GO_EVP_PKEY_PTR, err error) { checkMajorVersion(1) dsa := C.go_openssl_DSA_new() + if dsa == nil { + return nil, newOpenSSLError("DSA_new failed") + } + defer func() { + if pkey == nil { + C.go_openssl_DSA_free(dsa) + } + }() + p, q, g := bigToBN(params.P), bigToBN(params.Q), bigToBN(params.G) var ret C.int if vMinor == 0 { @@ -212,7 +221,6 @@ func newDSA1(params DSAParameters, x, y BigInt) (C.GO_EVP_PKEY_PTR, error) { C.go_openssl_BN_free(p) C.go_openssl_BN_free(q) C.go_openssl_BN_free(g) - C.go_openssl_DSA_free(dsa) return nil, newOpenSSLError("DSA_set0_pqg failed") } if y != nil { @@ -225,22 +233,18 @@ func newDSA1(params DSAParameters, x, y BigInt) (C.GO_EVP_PKEY_PTR, error) { if ret != 1 { C.go_openssl_BN_free(pub) C.go_openssl_BN_clear_free(priv) - C.go_openssl_DSA_free(dsa) return nil, newOpenSSLError("DSA_set0_key failed") } } else { if C.go_openssl_DSA_generate_key(dsa) != 1 { - C.go_openssl_DSA_free(dsa) return nil, newOpenSSLError("DSA_generate_key failed") } } - pkey := C.go_openssl_EVP_PKEY_new() + pkey = C.go_openssl_EVP_PKEY_new() if pkey == nil { - C.go_openssl_DSA_free(dsa) return nil, newOpenSSLError("EVP_PKEY_new failed") } if C.go_openssl_EVP_PKEY_assign(pkey, C.GO_EVP_PKEY_DSA, unsafe.Pointer(dsa)) != 1 { - C.go_openssl_DSA_free(dsa) C.go_openssl_EVP_PKEY_free(pkey) return nil, newOpenSSLError("EVP_PKEY_assign failed") }