diff --git a/dsa.go b/dsa.go new file mode 100644 index 00000000..0172d393 --- /dev/null +++ b/dsa.go @@ -0,0 +1,338 @@ +//go:build !cmd_go_bootstrap + +package openssl + +// #include "goopenssl.h" +import "C" +import ( + "runtime" + "unsafe" +) + +var ( + 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. +type DSAParameters struct { + P, Q, G BigInt +} + +// PrivateKeyDSA represents a DSA private key. +type PrivateKeyDSA struct { + DSAParameters + X, Y BigInt + + // _pkey MUST NOT be accessed directly. Instead, use the withKey method. + _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 MUST NOT be accessed directly. Instead, use the withKey method. + _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) { + // 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") + } + 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) + + // Extract the domain parameters from the generated key. + var p, q, g C.GO_BIGNUM_PTR + switch vMajor { + case 1: + dsa := getDSA(pkey) + 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) + C.go_openssl_BN_free(q) + C.go_openssl_BN_free(g) + }() + 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: + 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) + 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) + 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) (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 { + 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) + return nil, newOpenSSLError("DSA_set0_pqg failed") + } + if y != nil { + pub, priv := bigToBN(y), bigToBN(x) + 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) + return nil, newOpenSSLError("DSA_set0_key failed") + } + } else { + if C.go_openssl_DSA_generate_key(dsa) != 1 { + return nil, newOpenSSLError("DSA_generate_key failed") + } + } + pkey = C.go_openssl_EVP_PKEY_new() + if pkey == nil { + 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_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) { + checkMajorVersion(3) + + 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) + 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, 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) + 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 { + 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") + } + } + 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_DSA, selection, bldparams) + if err != nil { + return nil, err + } + if y != nil { + return pkey, nil + } + // 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 { + 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") + } + 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. +// 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..a657d765 --- /dev/null +++ b/dsa_test.go @@ -0,0 +1,204 @@ +package openssl_test + +import ( + "crypto/dsa" + "encoding/asn1" + "fmt" + "math/big" + "testing" + + "github.com/golang-fips/openssl/v2" + "github.com/golang-fips/openssl/v2/bbig" +) + +type dsaSignature struct { + R, S *big.Int +} + +func TestDSAGenerateParameters(t *testing.T) { + 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("error generating parameters: %s", err) + return + } + + P := bbig.Dec(params.P) + Q := bbig.Dec(params.Q) + G := bbig.Dec(params.G) + + if P.BitLen() != L { + t.Errorf("params.BitLen got:%d want:%d", P.BitLen(), L) + } + + if Q.BitLen() != N { + t.Errorf("q.BitLen got:%d want:%d", 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.Error("p-1 mod q != 0") + } + x := new(big.Int).Exp(G, quo, P) + if x.Cmp(one) == 0 { + t.Error("invalid generator") + } + + priv, err := openssl.GenerateKeyDSA(params) + if err != nil { + t.Errorf("error generating key: %s", err) + return + } + + testDSASignAndVerify(t, priv) +} + +func testDSASignAndVerify(t *testing.T, priv *openssl.PrivateKeyDSA) { + hashed := []byte("testing") + sig, err := openssl.SignDSA(priv, hashed[:]) + if err != nil { + t.Errorf("error signing: %s", err) + return + } + pub, err := openssl.NewPublicKeyDSA(priv.DSAParameters, priv.Y) + if err != nil { + t.Errorf("error getting public key: %s", err) + return + } + if !openssl.VerifyDSA(pub, hashed[:], sig) { + t.Error("error verifying") + 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.Error("compat: crypto/dsa can't verify OpenSSL signature") + } + r1, s1, err := dsa.Sign(openssl.RandReader, &priv1, hashed[:]) + if err != nil { + t.Errorf("error signing: %s", err) + return + } + sig, err = asn1.Marshal(dsaSignature{r1, s1}) + if err != nil { + t.Error(err) + return + } + if !openssl.VerifyDSA(pub, hashed[:], sig) { + 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")), + 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, priv) +} + +func TestDSANewPrivateKeyWithDegenerateKeys(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)), + } + 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) + } + } +} + +func TestDSANewPublicKeyWithBadPublicKey(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")) + + 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") + } +} + +func fromHex(s string) *big.Int { + result, ok := new(big.Int).SetString(s, 16) + if !ok { + panic(s) + } + return result +} 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/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/openssl.go b/openssl.go index 691bb16f..d82f5266 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(vMajor)) + "), expected " + strconv.Itoa(int(expected))) + } +} + type fail string func (e fail) Error() string { return "openssl: " + string(e) + " failed" } diff --git a/port_dsa.c b/port_dsa.c new file mode 100644 index 00000000..5a948eaf --- /dev/null +++ b/port_dsa.c @@ -0,0 +1,85 @@ +// 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" + +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; + // 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, + 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 diff --git a/shims.h b/shims.h index 99656f0c..98c6cfe3 100644 --- a/shims.h +++ b/shims.h @@ -19,9 +19,10 @@ 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. - */ + */ GO1_EVP_PKEY_OP_DERIVE = (1 << 10), GO_EVP_MAX_MD_SIZE = 64, @@ -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; @@ -158,6 +162,7 @@ typedef void* GO_SHA_CTX_PTR; // #include // #include // #include +// #include // #if OPENSSL_VERSION_NUMBER >= 0x30000000L // #include // #include @@ -280,6 +285,9 @@ 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)) \ 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 +304,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 +377,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_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)) \