From dd4074aa6c0ecf03ddb1070d0b3044d0086d7722 Mon Sep 17 00:00:00 2001 From: Quim Muntal Date: Tue, 10 Dec 2024 10:35:29 +0100 Subject: [PATCH] Generate PKEYs using EVP_PKEY_Q_keygen (#229) * generate PKEYs using EVP_PKEY_Q_keygen * Update shims.h Co-authored-by: Davis Goodin * Update comment on variadic functions * Fix indentation in generateEVPPKey function * Refactor curveID handling in generateEVPPKey --------- Co-authored-by: Davis Goodin --- cmd/checkheader/main.go | 5 ++++ ed25519.go | 5 +--- evp.go | 66 ++++++++++++++++++++++++++++------------- goopenssl.c | 8 +++++ goopenssl.h | 3 ++ shims.h | 12 ++++++++ 6 files changed, 75 insertions(+), 24 deletions(-) diff --git a/cmd/checkheader/main.go b/cmd/checkheader/main.go index 28cb1217..eb3590af 100644 --- a/cmd/checkheader/main.go +++ b/cmd/checkheader/main.go @@ -227,6 +227,11 @@ func tryConvertDefineFunc(w io.Writer, l string, i int) bool { if !strings.HasPrefix(l, "DEFINEFUNC") { return false } + if strings.HasPrefix(l, "DEFINEFUNC_VARIADIC") { + // Variadic functions are not supported. There is not enough + // information in the macro to create use it in writeDefineFunc. + return false + } i1 := strings.IndexByte(l, '(') // The first ")," match is always the end of the argument list parameter. // We are not interested in the last parameter and parsing them would complicate the algorithm. diff --git a/ed25519.go b/ed25519.go index 77ccbf59..cd237025 100644 --- a/ed25519.go +++ b/ed25519.go @@ -9,7 +9,6 @@ import ( "runtime" "strconv" "sync" - "unsafe" ) const ( @@ -37,9 +36,7 @@ var supportsEd25519 = sync.OnceValue(func() bool { } } case 3: - name := C.CString("ED25519") - defer C.free(unsafe.Pointer(name)) - sig := C.go_openssl_EVP_SIGNATURE_fetch(nil, name, nil) + sig := C.go_openssl_EVP_SIGNATURE_fetch(nil, keyTypeED25519, nil) if sig != nil { C.go_openssl_EVP_SIGNATURE_free(sig) return true diff --git a/evp.go b/evp.go index 85c84b8b..ef68bbfb 100644 --- a/evp.go +++ b/evp.go @@ -13,6 +13,12 @@ import ( "unsafe" ) +var ( + keyTypeRSA = C.CString("RSA") + keyTypeEC = C.CString("EC") + keyTypeED25519 = C.CString("ED25519") +) + // cacheMD is a cache of crypto.Hash to GO_EVP_MD_PTR. var cacheMD sync.Map @@ -157,33 +163,53 @@ func cryptoHashToMD(ch crypto.Hash) (md C.GO_EVP_MD_PTR) { return nil } +// generateEVPPKey generates a new EVP_PKEY with the given id and properties. func generateEVPPKey(id C.int, bits int, curve string) (C.GO_EVP_PKEY_PTR, error) { if bits != 0 && curve != "" { return nil, fail("incorrect generateEVPPKey parameters") } - ctx := C.go_openssl_EVP_PKEY_CTX_new_id(id, nil) - if ctx == nil { - return nil, newOpenSSLError("EVP_PKEY_CTX_new_id failed") - } - 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 failed") - } - if bits != 0 { - if C.go_openssl_EVP_PKEY_CTX_ctrl(ctx, id, -1, C.GO_EVP_PKEY_CTRL_RSA_KEYGEN_BITS, C.int(bits), nil) != 1 { - return nil, newOpenSSLError("EVP_PKEY_CTX_ctrl failed") + var pkey C.GO_EVP_PKEY_PTR + switch vMajor { + case 1: + ctx := C.go_openssl_EVP_PKEY_CTX_new_id(id, nil) + if ctx == nil { + return nil, newOpenSSLError("EVP_PKEY_CTX_new_id") + } + 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") + } + if bits != 0 { + if C.go_openssl_EVP_PKEY_CTX_ctrl(ctx, id, -1, C.GO_EVP_PKEY_CTRL_RSA_KEYGEN_BITS, C.int(bits), nil) != 1 { + return nil, newOpenSSLError("EVP_PKEY_CTX_ctrl") + } } - } - if curve != "" { - nid := curveNID(curve) - if C.go_openssl_EVP_PKEY_CTX_ctrl(ctx, id, -1, C.GO_EVP_PKEY_CTRL_EC_PARAMGEN_CURVE_NID, nid, nil) != 1 { - return nil, newOpenSSLError("EVP_PKEY_CTX_ctrl failed") + if curve != "" { + if C.go_openssl_EVP_PKEY_CTX_ctrl(ctx, id, -1, C.GO_EVP_PKEY_CTRL_EC_PARAMGEN_CURVE_NID, curveNID(curve), nil) != 1 { + return nil, newOpenSSLError("EVP_PKEY_CTX_ctrl") + } } + if C.go_openssl_EVP_PKEY_keygen(ctx, &pkey) != 1 { + return nil, newOpenSSLError("EVP_PKEY_keygen") + } + case 3: + switch id { + case C.GO_EVP_PKEY_RSA: + pkey = C.go_openssl_EVP_PKEY_Q_keygen_RSA(nil, nil, keyTypeRSA, C.size_t(bits)) + case C.GO_EVP_PKEY_EC: + pkey = C.go_openssl_EVP_PKEY_Q_keygen_EC(nil, nil, keyTypeEC, C.go_openssl_OBJ_nid2sn(curveNID(curve))) + case C.GO_EVP_PKEY_ED25519: + pkey = C.go_openssl_EVP_PKEY_Q_keygen(nil, nil, keyTypeED25519) + default: + panic("unsupported key type '" + strconv.Itoa(int(id)) + "'") + } + if pkey == nil { + return nil, newOpenSSLError("EVP_PKEY_Q_keygen") + } + default: + panic(errUnsupportedVersion()) } - var pkey C.GO_EVP_PKEY_PTR - if C.go_openssl_EVP_PKEY_keygen(ctx, &pkey) != 1 { - return nil, newOpenSSLError("EVP_PKEY_keygen failed") - } + return pkey, nil } diff --git a/goopenssl.c b/goopenssl.c index c3385b9d..626f184b 100644 --- a/goopenssl.c +++ b/goopenssl.c @@ -22,6 +22,7 @@ #define DEFINEFUNC_3_0(ret, func, args, argscall) DEFINEFUNC(ret, func, args, argscall) #define DEFINEFUNC_RENAMED_1_1(ret, func, oldfunc, args, argscall) DEFINEFUNC(ret, func, args, argscall) #define DEFINEFUNC_RENAMED_3_0(ret, func, oldfunc, args, argscall) DEFINEFUNC(ret, func, args, argscall) +#define DEFINEFUNC_VARIADIC_3_0(ret, func, newname, args, argscall) DEFINEFUNC(ret, newname, args, argscall) FOR_ALL_OPENSSL_FUNCTIONS @@ -34,6 +35,7 @@ FOR_ALL_OPENSSL_FUNCTIONS #undef DEFINEFUNC_3_0 #undef DEFINEFUNC_RENAMED_1_1 #undef DEFINEFUNC_RENAMED_3_0 +#undef DEFINEFUNC_VARIADIC_3_0 // go_openssl_fips_enabled returns 1 if FIPS mode is enabled, 0 otherwise. // As a special case, it returns -1 if it cannot determine if FIPS mode is enabled. @@ -140,6 +142,11 @@ go_openssl_load_functions(void* handle, unsigned int major, unsigned int minor, { \ DEFINEFUNC_INTERNAL(func, #func) \ } +#define DEFINEFUNC_VARIADIC_3_0(ret, func, newname, args, argscall) \ + if (major == 3) \ + { \ + DEFINEFUNC_INTERNAL(newname, #func) \ + } FOR_ALL_OPENSSL_FUNCTIONS @@ -152,6 +159,7 @@ FOR_ALL_OPENSSL_FUNCTIONS #undef DEFINEFUNC_3_0 #undef DEFINEFUNC_RENAMED_1_1 #undef DEFINEFUNC_RENAMED_3_0 +#undef DEFINEFUNC_VARIADIC_3_0 } static unsigned long diff --git a/goopenssl.h b/goopenssl.h index a50caa3d..1165f991 100644 --- a/goopenssl.h +++ b/goopenssl.h @@ -59,6 +59,8 @@ int go_openssl_DSA_set0_key_backport(GO_DSA_PTR d, GO_BIGNUM_PTR pub_key, GO_BIG DEFINEFUNC(ret, func, args, argscall) #define DEFINEFUNC_RENAMED_3_0(ret, func, oldfunc, args, argscall) \ DEFINEFUNC(ret, func, args, argscall) +#define DEFINEFUNC_VARIADIC_3_0(ret, func, newname, args, argscall) \ + DEFINEFUNC(ret, newname, args, argscall) FOR_ALL_OPENSSL_FUNCTIONS @@ -71,6 +73,7 @@ FOR_ALL_OPENSSL_FUNCTIONS #undef DEFINEFUNC_3_0 #undef DEFINEFUNC_RENAMED_1_1 #undef DEFINEFUNC_RENAMED_3_0 +#undef DEFINEFUNC_VARIADIC_3_0 // go_hash_sum copies ctx into ctx2 and calls EVP_DigestFinal using ctx2. // This is necessary because Go hash.Hash mandates that Sum has no effect diff --git a/shims.h b/shims.h index d16759d6..c8f599f7 100644 --- a/shims.h +++ b/shims.h @@ -157,6 +157,15 @@ typedef void* GO_SHA_CTX_PTR; // DEFINEFUNC_RENAMED_3_0 acts like DEFINEFUNC but tries to load the function using the new name when using >= 3.x // and the old name when using 1.x. In both cases the function will have the new name. // +// DEFINEFUNC_VARIADIC_3_0 acts like DEFINEFUNC but creates an alias with a more specific signature. +// This is necessary to call variadic functions (functions that accept a variable number of arguments) +// because variadic functions are not directly compatible with cgo. By defining a cgo-compatible alias +// for each desired signature, the C compiler handles the variadic arguments rather than cgo. +// Variadic functions are the only known incompatibility of this kind. +// If you use this macro for a different reason, consider renaming it to something more general first. +// See https://github.com/golang/go/issues/975. +// The process is aborted if the function can't be loaded when using 3.0.0 or higher. +// // #include // #include // #include @@ -298,6 +307,9 @@ 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_VARIADIC_3_0(GO_EVP_PKEY_PTR, EVP_PKEY_Q_keygen, EVP_PKEY_Q_keygen, (GO_OSSL_LIB_CTX_PTR ctx, const char *propq, const char *type), (ctx, propq, type)) \ +DEFINEFUNC_VARIADIC_3_0(GO_EVP_PKEY_PTR, EVP_PKEY_Q_keygen, EVP_PKEY_Q_keygen_RSA, (GO_OSSL_LIB_CTX_PTR ctx, const char *propq, const char *type, size_t arg1), (ctx, propq, type, arg1)) \ +DEFINEFUNC_VARIADIC_3_0(GO_EVP_PKEY_PTR, EVP_PKEY_Q_keygen, EVP_PKEY_Q_keygen_EC, (GO_OSSL_LIB_CTX_PTR ctx, const char *propq, const char *type, const char *arg1), (ctx, propq, type, arg1)) \ DEFINEFUNC(void, EVP_PKEY_CTX_free, (GO_EVP_PKEY_CTX_PTR arg0), (arg0)) \ DEFINEFUNC(int, EVP_PKEY_CTX_ctrl, (GO_EVP_PKEY_CTX_PTR ctx, int keytype, int optype, int cmd, int p1, void *p2), (ctx, keytype, optype, cmd, p1, p2)) \ DEFINEFUNC(int, EVP_PKEY_decrypt, (GO_EVP_PKEY_CTX_PTR arg0, unsigned char *arg1, size_t *arg2, const unsigned char *arg3, size_t arg4), (arg0, arg1, arg2, arg3, arg4)) \