diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x config.status -x configure~ -x configure.ac openssh-9.3p1/digest.h openssh-9.3p1-patched/digest.h --- openssh-9.3p1/digest.h 2023-03-15 22:28:19.000000000 +0100 +++ openssh-9.3p1-patched/digest.h 2023-06-06 15:52:25.602551466 +0200 @@ -32,6 +32,12 @@ struct sshbuf; struct ssh_digest_ctx; +#ifdef WITH_OPENSSL +#include +/* Converts internal digest representation to the OpenSSL one */ +const EVP_MD *ssh_digest_to_md(int digest_type); +#endif + /* Looks up a digest algorithm by name */ int ssh_digest_alg_by_name(const char *name); diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x config.status -x configure~ -x configure.ac openssh-9.3p1/digest-openssl.c openssh-9.3p1-patched/digest-openssl.c --- openssh-9.3p1/digest-openssl.c 2023-03-15 22:28:19.000000000 +0100 +++ openssh-9.3p1-patched/digest-openssl.c 2023-06-06 15:52:25.601551454 +0200 @@ -64,6 +64,22 @@ { -1, NULL, 0, NULL }, }; +const EVP_MD * +ssh_digest_to_md(int digest_type) +{ + switch (digest_type) { + case SSH_DIGEST_SHA1: + return EVP_sha1(); + case SSH_DIGEST_SHA256: + return EVP_sha256(); + case SSH_DIGEST_SHA384: + return EVP_sha384(); + case SSH_DIGEST_SHA512: + return EVP_sha512(); + } + return NULL; +} + static const struct ssh_digest * ssh_digest_by_alg(int alg) { diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x config.status -x configure~ -x configure.ac openssh-9.3p1/ssh-dss.c openssh-9.3p1-patched/ssh-dss.c --- openssh-9.3p1/ssh-dss.c 2023-03-15 22:28:19.000000000 +0100 +++ openssh-9.3p1-patched/ssh-dss.c 2023-06-06 15:52:25.624551743 +0200 @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include #include @@ -261,11 +263,15 @@ const u_char *data, size_t datalen, const char *alg, const char *sk_provider, const char *sk_pin, u_int compat) { + EVP_PKEY *pkey = NULL; DSA_SIG *sig = NULL; const BIGNUM *sig_r, *sig_s; - u_char digest[SSH_DIGEST_MAX_LENGTH], sigblob[SIGBLOB_LEN]; - size_t rlen, slen, len, dlen = ssh_digest_bytes(SSH_DIGEST_SHA1); + u_char sigblob[SIGBLOB_LEN]; + size_t rlen, slen; + int len; struct sshbuf *b = NULL; + u_char *sigb = NULL; + const u_char *psig = NULL; int ret = SSH_ERR_INVALID_ARGUMENT; if (lenp != NULL) @@ -276,17 +282,23 @@ if (key == NULL || key->dsa == NULL || sshkey_type_plain(key->type) != KEY_DSA) return SSH_ERR_INVALID_ARGUMENT; - if (dlen == 0) - return SSH_ERR_INTERNAL_ERROR; - if ((ret = ssh_digest_memory(SSH_DIGEST_SHA1, data, datalen, - digest, sizeof(digest))) != 0) + if ((ret = ssh_create_evp_dss(key, &pkey)) != 0) + return ret; + ret = sshkey_calculate_signature(pkey, SSH_DIGEST_SHA1, &sigb, &len, + data, datalen); + EVP_PKEY_free(pkey); + if (ret < 0) { goto out; + } - if ((sig = DSA_do_sign(digest, dlen, key->dsa)) == NULL) { + psig = sigb; + if ((sig = d2i_DSA_SIG(NULL, &psig, len)) == NULL) { ret = SSH_ERR_LIBCRYPTO_ERROR; goto out; } + free(sigb); + sigb = NULL; DSA_SIG_get0(sig, &sig_r, &sig_s); rlen = BN_num_bytes(sig_r); @@ -319,7 +331,7 @@ *lenp = len; ret = 0; out: - explicit_bzero(digest, sizeof(digest)); + free(sigb); DSA_SIG_free(sig); sshbuf_free(b); return ret; @@ -331,20 +343,20 @@ const u_char *data, size_t dlen, const char *alg, u_int compat, struct sshkey_sig_details **detailsp) { + EVP_PKEY *pkey = NULL; DSA_SIG *dsig = NULL; BIGNUM *sig_r = NULL, *sig_s = NULL; - u_char digest[SSH_DIGEST_MAX_LENGTH], *sigblob = NULL; - size_t len, hlen = ssh_digest_bytes(SSH_DIGEST_SHA1); + u_char *sigblob = NULL; + size_t len, slen; int ret = SSH_ERR_INTERNAL_ERROR; struct sshbuf *b = NULL; char *ktype = NULL; + u_char *sigb = NULL, *psig = NULL; if (key == NULL || key->dsa == NULL || sshkey_type_plain(key->type) != KEY_DSA || sig == NULL || siglen == 0) return SSH_ERR_INVALID_ARGUMENT; - if (hlen == 0) - return SSH_ERR_INTERNAL_ERROR; /* fetch signature */ if ((b = sshbuf_from(sig, siglen)) == NULL) @@ -386,25 +398,28 @@ } sig_r = sig_s = NULL; /* transferred */ - /* sha1 the data */ - if ((ret = ssh_digest_memory(SSH_DIGEST_SHA1, data, dlen, - digest, sizeof(digest))) != 0) + if ((slen = i2d_DSA_SIG(dsig, NULL)) == 0) { + ret = SSH_ERR_LIBCRYPTO_ERROR; goto out; - - switch (DSA_do_verify(digest, hlen, dsig, key->dsa)) { - case 1: - ret = 0; - break; - case 0: - ret = SSH_ERR_SIGNATURE_INVALID; + } + if ((sigb = malloc(slen)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; goto out; - default: + } + psig = sigb; + if ((slen = i2d_DSA_SIG(dsig, &psig)) == 0) { ret = SSH_ERR_LIBCRYPTO_ERROR; goto out; } + if ((ret = ssh_create_evp_dss(key, &pkey)) != 0) + goto out; + ret = sshkey_verify_signature(pkey, SSH_DIGEST_SHA1, data, dlen, + sigb, slen); + EVP_PKEY_free(pkey); + out: - explicit_bzero(digest, sizeof(digest)); + free(sigb); DSA_SIG_free(dsig); BN_clear_free(sig_r); BN_clear_free(sig_s); @@ -415,6 +430,65 @@ return ret; } +int +ssh_create_evp_dss(const struct sshkey *k, EVP_PKEY **pkey) +{ + OSSL_PARAM_BLD *param_bld = NULL; + EVP_PKEY_CTX *ctx = NULL; + const BIGNUM *p = NULL, *q = NULL, *g = NULL, *pub = NULL, *priv = NULL; + int ret = 0; + + if (k == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((ctx = EVP_PKEY_CTX_new_from_name(NULL, "DSA", NULL)) == NULL || + (param_bld = OSSL_PARAM_BLD_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + + DSA_get0_pqg(k->dsa, &p, &q, &g); + DSA_get0_key(k->dsa, &pub, &priv); + + if (p != NULL && + OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_FFC_P, p) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if (q != NULL && + OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_FFC_Q, q) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if (g != NULL && + OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_FFC_G, g) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if (pub != NULL && + OSSL_PARAM_BLD_push_BN(param_bld, + OSSL_PKEY_PARAM_PUB_KEY, + pub) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if (priv != NULL && + OSSL_PARAM_BLD_push_BN(param_bld, + OSSL_PKEY_PARAM_PRIV_KEY, + priv) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if ((*pkey = sshkey_create_evp(param_bld, ctx)) == NULL) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + +out: + OSSL_PARAM_BLD_free(param_bld); + EVP_PKEY_CTX_free(ctx); + return ret; +} + static const struct sshkey_impl_funcs sshkey_dss_funcs = { /* .size = */ ssh_dss_size, /* .alloc = */ ssh_dss_alloc, diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x config.status -x configure~ -x configure.ac openssh-9.3p1/ssh-ecdsa.c openssh-9.3p1-patched/ssh-ecdsa.c --- openssh-9.3p1/ssh-ecdsa.c 2023-03-15 22:28:19.000000000 +0100 +++ openssh-9.3p1-patched/ssh-ecdsa.c 2023-06-06 15:52:25.626551768 +0200 @@ -34,6 +34,8 @@ #include #include #include +#include +#include #include @@ -44,6 +44,9 @@ #include "digest.h" #define SSHKEY_INTERNAL #include "sshkey.h" +#ifdef ENABLE_PKCS11 +#include "ssh-pkcs11.h" +#endif #include "openbsd-compat/openssl-compat.h" @@ -126,19 +128,29 @@ static int ssh_ecdsa_generate(struct sshkey *k, int bits) { - EC_KEY *private; + EVP_PKEY_CTX *ctx = NULL; + EVP_PKEY *res = NULL; if ((k->ecdsa_nid = sshkey_ecdsa_bits_to_nid(bits)) == -1) return SSH_ERR_KEY_LENGTH; - if ((private = EC_KEY_new_by_curve_name(k->ecdsa_nid)) == NULL) + + if ((ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL)) == NULL) return SSH_ERR_ALLOC_FAIL; - if (EC_KEY_generate_key(private) != 1) { - EC_KEY_free(private); + + if (EVP_PKEY_keygen_init(ctx) <= 0 || EVP_PKEY_CTX_set_group_name(ctx, OBJ_nid2sn(k->ecdsa_nid)) <= 0 + || EVP_PKEY_keygen(ctx, &res) <= 0) { + EVP_PKEY_CTX_free(ctx); + EVP_PKEY_free(res); return SSH_ERR_LIBCRYPTO_ERROR; } - EC_KEY_set_asn1_flag(private, OPENSSL_EC_NAMED_CURVE); - k->ecdsa = private; - return 0; + /* This function is deprecated in OpenSSL 3.0 but OpenSSH doesn't worry about it*/ + k->ecdsa = EVP_PKEY_get1_EC_KEY(res); + if (k->ecdsa) + EC_KEY_set_asn1_flag(k->ecdsa, OPENSSL_EC_NAMED_CURVE); + + EVP_PKEY_CTX_free(ctx); + EVP_PKEY_free(res); + return (k->ecdsa) ? 0 : SSH_ERR_LIBCRYPTO_ERROR; } static int @@ -228,11 +240,13 @@ const u_char *data, size_t dlen, const char *alg, const char *sk_provider, const char *sk_pin, u_int compat) { + EVP_PKEY *pkey = NULL; ECDSA_SIG *esig = NULL; + unsigned char *sigb = NULL; + const unsigned char *psig; const BIGNUM *sig_r, *sig_s; int hash_alg; - u_char digest[SSH_DIGEST_MAX_LENGTH]; - size_t len, hlen; + int len; struct sshbuf *b = NULL, *bb = NULL; int ret = SSH_ERR_INTERNAL_ERROR; @@ -245,18 +259,33 @@ sshkey_type_plain(key->type) != KEY_ECDSA) return SSH_ERR_INVALID_ARGUMENT; - if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 || - (hlen = ssh_digest_bytes(hash_alg)) == 0) + if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1) return SSH_ERR_INTERNAL_ERROR; - if ((ret = ssh_digest_memory(hash_alg, data, dlen, - digest, sizeof(digest))) != 0) + +#ifdef ENABLE_PKCS11 + if (is_ecdsa_pkcs11(key->ecdsa)) { + if ((pkey = EVP_PKEY_new()) == NULL || + EVP_PKEY_set1_EC_KEY(pkey, key->ecdsa) != 1) + return SSH_ERR_ALLOC_FAIL; + } else { +#endif + if ((ret = ssh_create_evp_ec(key->ecdsa, key->ecdsa_nid, &pkey)) != 0) + return ret; +#ifdef ENABLE_PKCS11 + } +#endif + ret = sshkey_calculate_signature(pkey, hash_alg, &sigb, &len, data, + dlen); + EVP_PKEY_free(pkey); + if (ret < 0) { goto out; + } - if ((esig = ECDSA_do_sign(digest, hlen, key->ecdsa)) == NULL) { + psig = sigb; + if (d2i_ECDSA_SIG(&esig, &psig, len) == NULL) { ret = SSH_ERR_LIBCRYPTO_ERROR; goto out; } - if ((bb = sshbuf_new()) == NULL || (b = sshbuf_new()) == NULL) { ret = SSH_ERR_ALLOC_FAIL; goto out; @@ -280,7 +309,7 @@ *lenp = len; ret = 0; out: - explicit_bzero(digest, sizeof(digest)); + free(sigb); sshbuf_free(b); sshbuf_free(bb); ECDSA_SIG_free(esig); @@ -293,22 +322,21 @@ const u_char *data, size_t dlen, const char *alg, u_int compat, struct sshkey_sig_details **detailsp) { + EVP_PKEY *pkey = NULL; ECDSA_SIG *esig = NULL; BIGNUM *sig_r = NULL, *sig_s = NULL; - int hash_alg; - u_char digest[SSH_DIGEST_MAX_LENGTH]; - size_t hlen; + int hash_alg, len; int ret = SSH_ERR_INTERNAL_ERROR; struct sshbuf *b = NULL, *sigbuf = NULL; char *ktype = NULL; + unsigned char *sigb = NULL, *psig = NULL; if (key == NULL || key->ecdsa == NULL || sshkey_type_plain(key->type) != KEY_ECDSA || sig == NULL || siglen == 0) return SSH_ERR_INVALID_ARGUMENT; - if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 || - (hlen = ssh_digest_bytes(hash_alg)) == 0) + if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1) return SSH_ERR_INTERNAL_ERROR; /* fetch signature */ @@ -344,28 +372,33 @@ } sig_r = sig_s = NULL; /* transferred */ - if (sshbuf_len(sigbuf) != 0) { - ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; + /* Figure out the length */ + if ((len = i2d_ECDSA_SIG(esig, NULL)) == 0) { + ret = SSH_ERR_LIBCRYPTO_ERROR; goto out; } - if ((ret = ssh_digest_memory(hash_alg, data, dlen, - digest, sizeof(digest))) != 0) - goto out; - - switch (ECDSA_do_verify(digest, hlen, esig, key->ecdsa)) { - case 1: - ret = 0; - break; - case 0: - ret = SSH_ERR_SIGNATURE_INVALID; + if ((sigb = malloc(len)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; goto out; - default: + } + psig = sigb; + if ((len = i2d_ECDSA_SIG(esig, &psig)) == 0) { ret = SSH_ERR_LIBCRYPTO_ERROR; goto out; } + if (sshbuf_len(sigbuf) != 0) { + ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; + goto out; + } + + if (ssh_create_evp_ec(key->ecdsa, key->ecdsa_nid, &pkey) != 0) + goto out; + ret = sshkey_verify_signature(pkey, hash_alg, data, dlen, sigb, len); + EVP_PKEY_free(pkey); + out: - explicit_bzero(digest, sizeof(digest)); + free(sigb); sshbuf_free(sigbuf); sshbuf_free(b); ECDSA_SIG_free(esig); @@ -375,6 +408,79 @@ return ret; } +int +ssh_create_evp_ec(EC_KEY *k, int ecdsa_nid, EVP_PKEY **pkey) +{ + OSSL_PARAM_BLD *param_bld = NULL; + EVP_PKEY_CTX *ctx = NULL; + BN_CTX *bn_ctx = NULL; + uint8_t *pub_ser = NULL; + const char *group_name; + const EC_POINT *pub = NULL; + const BIGNUM *priv = NULL; + int ret = 0; + + if (k == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL)) == NULL || + (param_bld = OSSL_PARAM_BLD_new()) == NULL || + (bn_ctx = BN_CTX_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + + if ((group_name = OSSL_EC_curve_nid2name(ecdsa_nid)) == NULL || + OSSL_PARAM_BLD_push_utf8_string(param_bld, + OSSL_PKEY_PARAM_GROUP_NAME, + group_name, + strlen(group_name)) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if ((pub = EC_KEY_get0_public_key(k)) != NULL) { + const EC_GROUP *group; + size_t len; + + group = EC_KEY_get0_group(k); + len = EC_POINT_point2oct(group, pub, + POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); + if ((pub_ser = malloc(len)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + EC_POINT_point2oct(group, + pub, + POINT_CONVERSION_UNCOMPRESSED, + pub_ser, + len, + bn_ctx); + if (OSSL_PARAM_BLD_push_octet_string(param_bld, + OSSL_PKEY_PARAM_PUB_KEY, + pub_ser, + len) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + } + if ((priv = EC_KEY_get0_private_key(k)) != NULL && + OSSL_PARAM_BLD_push_BN(param_bld, + OSSL_PKEY_PARAM_PRIV_KEY, priv) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if ((*pkey = sshkey_create_evp(param_bld, ctx)) == NULL) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + +out: + OSSL_PARAM_BLD_free(param_bld); + EVP_PKEY_CTX_free(ctx); + BN_CTX_free(bn_ctx); + free(pub_ser); + return ret; +} + /* NB. not static; used by ECDSA-SK */ const struct sshkey_impl_funcs sshkey_ecdsa_funcs = { /* .size = */ ssh_ecdsa_size, diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x config.status -x configure~ -x configure.ac openssh-9.3p1/sshkey.c openssh-9.3p1-patched/sshkey.c --- openssh-9.3p1/sshkey.c 2023-06-06 15:53:36.608444190 +0200 +++ openssh-9.3p1-patched/sshkey.c 2023-06-06 15:52:25.625551756 +0200 @@ -34,6 +34,8 @@ #include #include #include +#include +#include #endif #include "crypto_api.h" @@ -575,6 +577,86 @@ } #ifdef WITH_OPENSSL +int +sshkey_calculate_signature(EVP_PKEY *pkey, int hash_alg, u_char **sigp, + int *lenp, const u_char *data, size_t datalen) +{ + EVP_MD_CTX *ctx = NULL; + u_char *sig = NULL; + int ret, slen; + size_t len; + + if (sigp == NULL || lenp == NULL) { + return SSH_ERR_INVALID_ARGUMENT; + } + + slen = EVP_PKEY_get_size(pkey); + if (slen <= 0 || slen > SSHBUF_MAX_BIGNUM) + return SSH_ERR_INVALID_ARGUMENT; + + len = slen; + if ((sig = malloc(slen)) == NULL) { + return SSH_ERR_ALLOC_FAIL; + } + + if ((ctx = EVP_MD_CTX_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto error; + } + if (EVP_DigestSignInit(ctx, NULL, ssh_digest_to_md(hash_alg), + NULL, pkey) != 1 || + EVP_DigestSignUpdate(ctx, data, datalen) != 1 || + EVP_DigestSignFinal(ctx, sig, &len) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto error; + } + + *sigp = sig; + *lenp = len; + /* Now owned by the caller */ + sig = NULL; + ret = 0; + +error: + EVP_MD_CTX_free(ctx); + free(sig); + return ret; +} + +int +sshkey_verify_signature(EVP_PKEY *pkey, int hash_alg, const u_char *data, + size_t datalen, u_char *sigbuf, int siglen) +{ + EVP_MD_CTX *ctx = NULL; + int ret; + + if ((ctx = EVP_MD_CTX_new()) == NULL) { + return SSH_ERR_ALLOC_FAIL; + } + if (EVP_DigestVerifyInit(ctx, NULL, ssh_digest_to_md(hash_alg), + NULL, pkey) != 1 || + EVP_DigestVerifyUpdate(ctx, data, datalen) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto done; + } + ret = EVP_DigestVerifyFinal(ctx, sigbuf, siglen); + switch (ret) { + case 1: + ret = 0; + break; + case 0: + ret = SSH_ERR_SIGNATURE_INVALID; + break; + default: + ret = SSH_ERR_LIBCRYPTO_ERROR; + break; + } + +done: + EVP_MD_CTX_free(ctx); + return ret; +} + /* XXX: these are really begging for a table-driven approach */ int sshkey_curve_name_to_nid(const char *name) @@ -3763,3 +3845,27 @@ return 0; } #endif /* WITH_XMSS */ + +#ifdef WITH_OPENSSL +EVP_PKEY * +sshkey_create_evp(OSSL_PARAM_BLD *param_bld, EVP_PKEY_CTX *ctx) +{ + EVP_PKEY *ret = NULL; + OSSL_PARAM *params = NULL; + if (param_bld == NULL || ctx == NULL) { + debug2_f("param_bld or ctx is NULL"); + return NULL; + } + if ((params = OSSL_PARAM_BLD_to_param(param_bld)) == NULL) { + debug2_f("Could not build param list"); + return NULL; + } + if (EVP_PKEY_fromdata_init(ctx) != 1 || + EVP_PKEY_fromdata(ctx, &ret, EVP_PKEY_KEYPAIR, params) != 1) { + debug2_f("EVP_PKEY_fromdata failed"); + OSSL_PARAM_free(params); + return NULL; + } + return ret; +} +#endif /* WITH_OPENSSL */ diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x config.status -x configure~ -x configure.ac openssh-9.3p1/sshkey.h openssh-9.3p1-patched/sshkey.h --- openssh-9.3p1/sshkey.h 2023-06-06 15:53:36.608444190 +0200 +++ openssh-9.3p1-patched/sshkey.h 2023-06-06 15:52:25.626551768 +0200 @@ -31,6 +31,9 @@ #ifdef WITH_OPENSSL #include #include +#include +#include +#include # ifdef OPENSSL_HAS_ECC # include # include @@ -266,6 +266,10 @@ const char *sshkey_ssh_name_plain(const struct sshkey *); int sshkey_names_valid2(const char *, int, int); char *sshkey_alg_list(int, int, int, char); +int sshkey_calculate_signature(EVP_PKEY*, int, u_char **, + int *, const u_char *, size_t); +int sshkey_verify_signature(EVP_PKEY *, int, const u_char *, + size_t, u_char *, int); int sshkey_from_blob(const u_char *, size_t, struct sshkey **); int sshkey_fromb(struct sshbuf *, struct sshkey **); @@ -324,6 +331,13 @@ void sshkey_sig_details_free(struct sshkey_sig_details *); +#ifdef WITH_OPENSSL +EVP_PKEY *sshkey_create_evp(OSSL_PARAM_BLD *, EVP_PKEY_CTX *); +int ssh_create_evp_dss(const struct sshkey *, EVP_PKEY **); +int ssh_create_evp_rsa(const struct sshkey *, EVP_PKEY **); +int ssh_create_evp_ec(EC_KEY *, int, EVP_PKEY **); +#endif /* WITH_OPENSSL */ + #ifdef SSHKEY_INTERNAL int sshkey_sk_fields_equal(const struct sshkey *a, const struct sshkey *b); void sshkey_sk_cleanup(struct sshkey *k); @@ -338,6 +352,10 @@ #endif #endif +#ifdef ENABLE_PKCS11 +int pkcs11_get_ecdsa_idx(void); +#endif + #if !defined(WITH_OPENSSL) # undef RSA # undef DSA diff --git a/ssh-pkcs11.c b/ssh-pkcs11.c --- a/ssh-pkcs11.c (revision 8241b9c0529228b4b86d88b1a6076fb9f97e4a99) +++ b/ssh-pkcs11.c (date 1703110934679) @@ -620,8 +620,24 @@ return (0); } + +int +is_ecdsa_pkcs11(EC_KEY *ecdsa) +{ + if (EC_KEY_get_ex_data(ecdsa, ec_key_idx) != NULL) + return 1; + return 0; +} #endif /* OPENSSL_HAS_ECC && HAVE_EC_KEY_METHOD_NEW */ +int +is_rsa_pkcs11(RSA *rsa) +{ + if (RSA_get_ex_data(rsa, rsa_idx) != NULL) + return 1; + return 0; +} + /* remove trailing spaces. Note, that this does NOT guarantee the buffer * will be null terminated if there are no trailing spaces! */ static char * diff --git a/ssh-pkcs11-client.c b/ssh-pkcs11-client.c --- a/ssh-pkcs11-client.c (revision 8241b9c0529228b4b86d88b1a6076fb9f97e4a99) +++ b/ssh-pkcs11-client.c (date 1703110830967) @@ -402,8 +402,36 @@ if (helper->nrsa == 0 && helper->nec == 0) helper_terminate(helper); } + +int +is_ecdsa_pkcs11(EC_KEY *ecdsa) +{ + const EC_KEY_METHOD *meth; + ECDSA_SIG *(*sign_sig)(const unsigned char *dgst, int dgstlen, + const BIGNUM *kinv, const BIGNUM *rp, EC_KEY *eckey) = NULL; + + meth = EC_KEY_get_method(ecdsa); + EC_KEY_METHOD_get_sign(meth, NULL, NULL, &sign_sig); + if (sign_sig == ecdsa_do_sign) + return 1; + return 0; +} #endif /* defined(OPENSSL_HAS_ECC) && defined(HAVE_EC_KEY_METHOD_NEW) */ +int +is_rsa_pkcs11(RSA *rsa) +{ + const RSA_METHOD *meth; + int (*priv_enc)(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, int padding) = NULL; + + meth = RSA_get_method(rsa); + priv_enc = RSA_meth_get_priv_enc(meth); + if (priv_enc == rsa_encrypt) + return 1; + return 0; +} + /* redirect private key crypto operations to the ssh-pkcs11-helper */ static void wrap_key(struct helper *helper, struct sshkey *k) diff --git a/ssh-pkcs11.h b/ssh-pkcs11.h --- a/ssh-pkcs11.h (revision 8241b9c0529228b4b86d88b1a6076fb9f97e4a99) +++ b/ssh-pkcs11.h (date 1703111023334) @@ -38,6 +38,12 @@ /* Only available in ssh-pkcs11-client.c so far */ int pkcs11_make_cert(const struct sshkey *, const struct sshkey *, struct sshkey **); + +#ifdef HAVE_EC_KEY_METHOD_NEW +int is_ecdsa_pkcs11(EC_KEY *ecdsa); +#endif +int is_rsa_pkcs11(RSA *rsa); + #if !defined(WITH_OPENSSL) && defined(ENABLE_PKCS11) #undef ENABLE_PKCS11 #endif diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x config.status -x configure~ -x configure.ac openssh-9.3p1/ssh-rsa.c openssh-9.3p1-patched/ssh-rsa.c --- openssh-9.3p1/ssh-rsa.c 2023-03-15 22:28:19.000000000 +0100 +++ openssh-9.3p1-patched/ssh-rsa.c 2023-06-06 15:52:25.627551781 +0200 @@ -23,6 +23,8 @@ #include #include +#include +#include #include #include @@ -36,10 +36,13 @@ #include "sshkey.h" #include "digest.h" #include "log.h" +#ifdef ENABLE_PKCS11 +#include "ssh-pkcs11.h" +#endif #include "openbsd-compat/openssl-compat.h" -static int openssh_RSA_verify(int, u_char *, size_t, u_char *, size_t, RSA *); +static int openssh_RSA_verify(int, const u_char *, size_t, u_char *, size_t, EVP_PKEY *); static u_int ssh_rsa_size(const struct sshkey *key) @@ -131,27 +133,50 @@ static int ssh_rsa_generate(struct sshkey *k, int bits) { - RSA *private = NULL; + EVP_PKEY_CTX *ctx = NULL; + EVP_PKEY *res = NULL; BIGNUM *f4 = NULL; int ret = SSH_ERR_INTERNAL_ERROR; if (bits < SSH_RSA_MINIMUM_MODULUS_SIZE || bits > SSHBUF_MAX_BIGNUM * 8) return SSH_ERR_KEY_LENGTH; - if ((private = RSA_new()) == NULL || (f4 = BN_new()) == NULL) { + + if ((ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL)) == NULL + || (f4 = BN_new()) == NULL || !BN_set_word(f4, RSA_F4)) { ret = SSH_ERR_ALLOC_FAIL; goto out; } - if (!BN_set_word(f4, RSA_F4) || - !RSA_generate_key_ex(private, bits, f4, NULL)) { + + if (EVP_PKEY_keygen_init(ctx) <= 0) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + + if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) <= 0) { + ret = SSH_ERR_KEY_LENGTH; + goto out; + } + + if (EVP_PKEY_CTX_set1_rsa_keygen_pubexp(ctx, f4) <= 0) + goto out; + + if (EVP_PKEY_keygen(ctx, &res) <= 0) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + + /* This function is deprecated in OpenSSL 3.0 but OpenSSH doesn't worry about it*/ + k->rsa = EVP_PKEY_get1_RSA(res); + if (k->rsa) { + ret = 0; + } else { ret = SSH_ERR_LIBCRYPTO_ERROR; goto out; } - k->rsa = private; - private = NULL; - ret = 0; out: - RSA_free(private); + EVP_PKEY_CTX_free(ctx); + EVP_PKEY_free(res); BN_free(f4); return ret; } @@ -317,21 +342,6 @@ return -1; } -static int -rsa_hash_alg_nid(int type) -{ - switch (type) { - case SSH_DIGEST_SHA1: - return NID_sha1; - case SSH_DIGEST_SHA256: - return NID_sha256; - case SSH_DIGEST_SHA512: - return NID_sha512; - default: - return -1; - } -} - int ssh_rsa_complete_crt_parameters(struct sshkey *key, const BIGNUM *iqmp) { @@ -393,11 +403,10 @@ const u_char *data, size_t datalen, const char *alg, const char *sk_provider, const char *sk_pin, u_int compat) { - const BIGNUM *rsa_n; - u_char digest[SSH_DIGEST_MAX_LENGTH], *sig = NULL; - size_t slen = 0; - u_int hlen, len; - int nid, hash_alg, ret = SSH_ERR_INTERNAL_ERROR; + EVP_PKEY *pkey = NULL; + u_char *sig = NULL; + int len, slen = 0; + int hash_alg, ret = SSH_ERR_INTERNAL_ERROR; struct sshbuf *b = NULL; if (lenp != NULL) @@ -409,33 +418,33 @@ hash_alg = SSH_DIGEST_SHA1; else hash_alg = rsa_hash_id_from_keyname(alg); + if (key == NULL || key->rsa == NULL || hash_alg == -1 || sshkey_type_plain(key->type) != KEY_RSA) return SSH_ERR_INVALID_ARGUMENT; - RSA_get0_key(key->rsa, &rsa_n, NULL, NULL); - if (BN_num_bits(rsa_n) < SSH_RSA_MINIMUM_MODULUS_SIZE) - return SSH_ERR_KEY_LENGTH; slen = RSA_size(key->rsa); - if (slen <= 0 || slen > SSHBUF_MAX_BIGNUM) - return SSH_ERR_INVALID_ARGUMENT; - - /* hash the data */ - nid = rsa_hash_alg_nid(hash_alg); - if ((hlen = ssh_digest_bytes(hash_alg)) == 0) - return SSH_ERR_INTERNAL_ERROR; - if ((ret = ssh_digest_memory(hash_alg, data, datalen, - digest, sizeof(digest))) != 0) - goto out; + if (RSA_bits(key->rsa) < SSH_RSA_MINIMUM_MODULUS_SIZE) + return SSH_ERR_KEY_LENGTH; - if ((sig = malloc(slen)) == NULL) { - ret = SSH_ERR_ALLOC_FAIL; - goto out; +#ifdef ENABLE_PKCS11 + if (is_rsa_pkcs11(key->rsa)) { + if ((pkey = EVP_PKEY_new()) == NULL || + EVP_PKEY_set1_RSA(pkey, key->rsa) != 1) + return SSH_ERR_ALLOC_FAIL; + } else { +#endif + if ((ret = ssh_create_evp_rsa(key, &pkey)) != 0) + return ret; +#ifdef ENABLE_PKCS11 } - - if (RSA_sign(nid, digest, hlen, sig, &len, key->rsa) != 1) { - ret = SSH_ERR_LIBCRYPTO_ERROR; +#endif + ret = sshkey_calculate_signature(pkey, hash_alg, &sig, &len, data, + datalen); + EVP_PKEY_free(pkey); + if (ret < 0) { goto out; } + if (len < slen) { size_t diff = slen - len; memmove(sig + diff, sig, len); @@ -444,6 +453,7 @@ ret = SSH_ERR_INTERNAL_ERROR; goto out; } + /* encode signature */ if ((b = sshbuf_new()) == NULL) { ret = SSH_ERR_ALLOC_FAIL; @@ -464,7 +474,6 @@ *lenp = len; ret = 0; out: - explicit_bzero(digest, sizeof(digest)); freezero(sig, slen); sshbuf_free(b); return ret; @@ -476,10 +485,10 @@ const u_char *data, size_t dlen, const char *alg, u_int compat, struct sshkey_sig_details **detailsp) { - const BIGNUM *rsa_n; + EVP_PKEY *pkey = NULL; char *sigtype = NULL; int hash_alg, want_alg, ret = SSH_ERR_INTERNAL_ERROR; - size_t len = 0, diff, modlen, hlen; + size_t len = 0, diff, modlen; struct sshbuf *b = NULL; u_char digest[SSH_DIGEST_MAX_LENGTH], *osigblob, *sigblob = NULL; @@ -487,8 +496,7 @@ sshkey_type_plain(key->type) != KEY_RSA || sig == NULL || siglen == 0) return SSH_ERR_INVALID_ARGUMENT; - RSA_get0_key(key->rsa, &rsa_n, NULL, NULL); - if (BN_num_bits(rsa_n) < SSH_RSA_MINIMUM_MODULUS_SIZE) + if (RSA_bits(key->rsa) < SSH_RSA_MINIMUM_MODULUS_SIZE) return SSH_ERR_KEY_LENGTH; if ((b = sshbuf_from(sig, siglen)) == NULL) @@ -540,16 +548,13 @@ explicit_bzero(sigblob, diff); len = modlen; } - if ((hlen = ssh_digest_bytes(hash_alg)) == 0) { - ret = SSH_ERR_INTERNAL_ERROR; - goto out; - } - if ((ret = ssh_digest_memory(hash_alg, data, dlen, - digest, sizeof(digest))) != 0) + + if ((ret = ssh_create_evp_rsa(key, &pkey)) != 0) goto out; - ret = openssh_RSA_verify(hash_alg, digest, hlen, sigblob, len, - key->rsa); + ret = openssh_RSA_verify(hash_alg, data, dlen, sigblob, len, pkey); + EVP_PKEY_free(pkey); + out: freezero(sigblob, len); free(sigtype); @@ -558,125 +563,110 @@ return ret; } -/* - * See: - * http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/ - * ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.asn - */ - -/* - * id-sha1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) - * oiw(14) secsig(3) algorithms(2) 26 } - */ -static const u_char id_sha1[] = { - 0x30, 0x21, /* type Sequence, length 0x21 (33) */ - 0x30, 0x09, /* type Sequence, length 0x09 */ - 0x06, 0x05, /* type OID, length 0x05 */ - 0x2b, 0x0e, 0x03, 0x02, 0x1a, /* id-sha1 OID */ - 0x05, 0x00, /* NULL */ - 0x04, 0x14 /* Octet string, length 0x14 (20), followed by sha1 hash */ -}; - -/* - * See http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html - * id-sha256 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) - * organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2) - * id-sha256(1) } - */ -static const u_char id_sha256[] = { - 0x30, 0x31, /* type Sequence, length 0x31 (49) */ - 0x30, 0x0d, /* type Sequence, length 0x0d (13) */ - 0x06, 0x09, /* type OID, length 0x09 */ - 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, /* id-sha256 */ - 0x05, 0x00, /* NULL */ - 0x04, 0x20 /* Octet string, length 0x20 (32), followed by sha256 hash */ -}; - -/* - * See http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html - * id-sha512 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) - * organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2) - * id-sha256(3) } - */ -static const u_char id_sha512[] = { - 0x30, 0x51, /* type Sequence, length 0x51 (81) */ - 0x30, 0x0d, /* type Sequence, length 0x0d (13) */ - 0x06, 0x09, /* type OID, length 0x09 */ - 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, /* id-sha512 */ - 0x05, 0x00, /* NULL */ - 0x04, 0x40 /* Octet string, length 0x40 (64), followed by sha512 hash */ -}; - static int -rsa_hash_alg_oid(int hash_alg, const u_char **oidp, size_t *oidlenp) +openssh_RSA_verify(int hash_alg, const u_char *data, size_t datalen, + u_char *sigbuf, size_t siglen, EVP_PKEY *pkey) { - switch (hash_alg) { - case SSH_DIGEST_SHA1: - *oidp = id_sha1; - *oidlenp = sizeof(id_sha1); - break; - case SSH_DIGEST_SHA256: - *oidp = id_sha256; - *oidlenp = sizeof(id_sha256); - break; - case SSH_DIGEST_SHA512: - *oidp = id_sha512; - *oidlenp = sizeof(id_sha512); - break; - default: - return SSH_ERR_INVALID_ARGUMENT; - } - return 0; -} + size_t rsasize = 0; + int ret; -static int -openssh_RSA_verify(int hash_alg, u_char *hash, size_t hashlen, - u_char *sigbuf, size_t siglen, RSA *rsa) -{ - size_t rsasize = 0, oidlen = 0, hlen = 0; - int ret, len, oidmatch, hashmatch; - const u_char *oid = NULL; - u_char *decrypted = NULL; - - if ((ret = rsa_hash_alg_oid(hash_alg, &oid, &oidlen)) != 0) - return ret; - ret = SSH_ERR_INTERNAL_ERROR; - hlen = ssh_digest_bytes(hash_alg); - if (hashlen != hlen) { - ret = SSH_ERR_INVALID_ARGUMENT; - goto done; - } - rsasize = RSA_size(rsa); + rsasize = EVP_PKEY_get_size(pkey); if (rsasize <= 0 || rsasize > SSHBUF_MAX_BIGNUM || siglen == 0 || siglen > rsasize) { ret = SSH_ERR_INVALID_ARGUMENT; goto done; } - if ((decrypted = malloc(rsasize)) == NULL) { - ret = SSH_ERR_ALLOC_FAIL; - goto done; - } - if ((len = RSA_public_decrypt(siglen, sigbuf, decrypted, rsa, - RSA_PKCS1_PADDING)) < 0) { - ret = SSH_ERR_LIBCRYPTO_ERROR; - goto done; - } - if (len < 0 || (size_t)len != hlen + oidlen) { - ret = SSH_ERR_INVALID_FORMAT; - goto done; - } - oidmatch = timingsafe_bcmp(decrypted, oid, oidlen) == 0; - hashmatch = timingsafe_bcmp(decrypted + oidlen, hash, hlen) == 0; - if (!oidmatch || !hashmatch) { - ret = SSH_ERR_SIGNATURE_INVALID; - goto done; - } - ret = 0; + + ret = sshkey_verify_signature(pkey, hash_alg, data, datalen, + sigbuf, siglen); + done: - freezero(decrypted, rsasize); return ret; } +int +ssh_create_evp_rsa(const struct sshkey *k, EVP_PKEY **pkey) +{ + OSSL_PARAM_BLD *param_bld = NULL; + EVP_PKEY_CTX *ctx = NULL; + int ret = 0; + const BIGNUM *n = NULL, *e = NULL, *d = NULL, *p = NULL, *q = NULL; + const BIGNUM *dmp1 = NULL, *dmq1 = NULL, *iqmp = NULL; + + if (k == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL)) == NULL || + (param_bld = OSSL_PARAM_BLD_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + + RSA_get0_key(k->rsa, &n, &e, &d); + RSA_get0_factors(k->rsa, &p, &q); + RSA_get0_crt_params(k->rsa, &dmp1, &dmq1, &iqmp); + + if (n != NULL && + OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_N, n) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if (e != NULL && + OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_E, e) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if (d != NULL && + OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_D, d) != 1) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + + if ((*pkey = sshkey_create_evp(param_bld, ctx)) == NULL) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + + /* setting this to param_build makes the creation process fail */ + if (p != NULL && + EVP_PKEY_set_bn_param(*pkey, OSSL_PKEY_PARAM_RSA_FACTOR1, p) != 1) { + debug2_f("failed to add 'p' param"); + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if (q != NULL && + EVP_PKEY_set_bn_param(*pkey, OSSL_PKEY_PARAM_RSA_FACTOR2, q) != 1) { + debug2_f("failed to add 'q' param"); + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if (dmp1 != NULL && + EVP_PKEY_set_bn_param(*pkey, + OSSL_PKEY_PARAM_RSA_EXPONENT1, dmp1) != 1) { + debug2_f("failed to add 'dmp1' param"); + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if (dmq1 != NULL && + EVP_PKEY_set_bn_param(*pkey, + OSSL_PKEY_PARAM_RSA_EXPONENT2, dmq1) != 1) { + debug2_f("failed to add 'dmq1' param"); + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + if (iqmp != NULL && + EVP_PKEY_set_bn_param(*pkey, + OSSL_PKEY_PARAM_RSA_COEFFICIENT1, iqmp) != 1) { + debug2_f("failed to add 'iqmp' param"); + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + +out: + OSSL_PARAM_BLD_free(param_bld); + EVP_PKEY_CTX_free(ctx); + return ret; +} + static const struct sshkey_impl_funcs sshkey_rsa_funcs = { /* .size = */ ssh_rsa_size, /* .alloc = */ ssh_rsa_alloc,