diff options
Diffstat (limited to 'src/libcrypto.c')
-rw-r--r-- | src/libcrypto.c | 807 |
1 files changed, 472 insertions, 335 deletions
diff --git a/src/libcrypto.c b/src/libcrypto.c index c14eeeea..f45ffa96 100644 --- a/src/libcrypto.c +++ b/src/libcrypto.c @@ -26,26 +26,31 @@ #include <string.h> #ifdef HAVE_SYS_TIME_H #include <sys/time.h> -#endif +#endif /* HAVE_SYS_TIME_H */ #include "libssh/priv.h" #include "libssh/session.h" #include "libssh/crypto.h" #include "libssh/wrapper.h" #include "libssh/libcrypto.h" -#if defined(HAVE_OPENSSL_EVP_CHACHA20) && defined(HAVE_OPENSSL_EVP_POLY1305) +#include "libssh/pki.h" +#ifdef HAVE_OPENSSL_EVP_CHACHA20 #include "libssh/bytearray.h" #include "libssh/chacha20-poly1305-common.h" #endif #ifdef HAVE_LIBCRYPTO +#include <openssl/opensslv.h> #include <openssl/sha.h> #include <openssl/md5.h> -#include <openssl/dsa.h> +#if OPENSSL_VERSION_NUMBER < 0x30000000L #include <openssl/rsa.h> #include <openssl/hmac.h> -#include <openssl/opensslv.h> +#else +#include <openssl/param_build.h> +#include <openssl/core_names.h> +#endif /* OPENSSL_VERSION_NUMBER */ #include <openssl/rand.h> #include <openssl/engine.h> @@ -54,11 +59,11 @@ #ifdef HAVE_OPENSSL_AES_H #define HAS_AES #include <openssl/aes.h> -#endif +#endif /* HAVE_OPENSSL_AES_H */ #ifdef HAVE_OPENSSL_DES_H #define HAS_DES #include <openssl/des.h> -#endif +#endif /* HAVE_OPENSSL_DES_H */ #if (defined(HAVE_VALGRIND_VALGRIND_H) && defined(HAVE_OPENSSL_IA32CAP_LOC)) #include <valgrind/valgrind.h> @@ -67,14 +72,19 @@ #include "libssh/crypto.h" -#ifdef HAVE_OPENSSL_EVP_KDF_CTX_NEW_ID +#ifdef HAVE_OPENSSL_EVP_KDF_CTX #include <openssl/kdf.h> -#endif +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#include <openssl/param_build.h> +#include <openssl/core_names.h> +#endif /* OPENSSL_VERSION_NUMBER */ +#endif /* HAVE_OPENSSL_EVP_KDF_CTX */ #include "libssh/crypto.h" static int libcrypto_initialized = 0; + void ssh_reseed(void){ #ifndef _WIN32 struct timeval tv; @@ -83,265 +93,42 @@ void ssh_reseed(void){ #endif } -/** - * @brief Get random bytes - * - * Make sure to always check the return code of this function! - * - * @param[in] where The buffer to fill with random bytes - * - * @param[in] len The size of the buffer to fill. - * - * @param[in] strong Use a strong or private RNG source. - * - * @return 1 on success, 0 on error. - */ -int ssh_get_random(void *where, int len, int strong) -{ -#ifdef HAVE_OPENSSL_RAND_PRIV_BYTES - if (strong) { - /* Returns -1 when not supported, 0 on error, 1 on success */ - return !!RAND_priv_bytes(where, len); - } -#else - (void)strong; -#endif /* HAVE_RAND_PRIV_BYTES */ - - /* Returns -1 when not supported, 0 on error, 1 on success */ - return !!RAND_bytes(where, len); -} - -SHACTX sha1_init(void) -{ - int rc; - SHACTX c = EVP_MD_CTX_new(); - if (c == NULL) { - return NULL; - } - rc = EVP_DigestInit_ex(c, EVP_sha1(), NULL); - if (rc == 0) { - EVP_MD_CTX_free(c); - c = NULL; - } - return c; -} - -void sha1_update(SHACTX c, const void *data, unsigned long len) -{ - EVP_DigestUpdate(c, data, len); -} +#ifndef WITH_PKCS11_PROVIDER +static ENGINE *engine = NULL; -void sha1_final(unsigned char *md, SHACTX c) +ENGINE *pki_get_engine(void) { - unsigned int mdlen = 0; - - EVP_DigestFinal(c, md, &mdlen); - EVP_MD_CTX_free(c); -} + int ok; -void sha1(const unsigned char *digest, int len, unsigned char *hash) -{ - SHACTX c = sha1_init(); - if (c != NULL) { - sha1_update(c, digest, len); - sha1_final(hash, c); - } -} + if (engine == NULL) { + ENGINE_load_builtin_engines(); -#ifdef HAVE_OPENSSL_ECC -static const EVP_MD *nid_to_evpmd(int nid) -{ - switch (nid) { - case NID_X9_62_prime256v1: - return EVP_sha256(); - case NID_secp384r1: - return EVP_sha384(); - case NID_secp521r1: - return EVP_sha512(); - default: + engine = ENGINE_by_id("pkcs11"); + if (engine == NULL) { + SSH_LOG(SSH_LOG_TRACE, + "Could not load the engine: %s", + ERR_error_string(ERR_get_error(), NULL)); return NULL; - } - - return NULL; -} - -void evp(int nid, unsigned char *digest, int len, unsigned char *hash, unsigned int *hlen) -{ - const EVP_MD *evp_md = nid_to_evpmd(nid); - EVP_MD_CTX *md = EVP_MD_CTX_new(); - - EVP_DigestInit(md, evp_md); - EVP_DigestUpdate(md, digest, len); - EVP_DigestFinal(md, hash, hlen); - EVP_MD_CTX_free(md); -} - -EVPCTX evp_init(int nid) -{ - const EVP_MD *evp_md = nid_to_evpmd(nid); - - EVPCTX ctx = EVP_MD_CTX_new(); - if (ctx == NULL) { - return NULL; - } - - EVP_DigestInit(ctx, evp_md); - - return ctx; -} - -void evp_update(EVPCTX ctx, const void *data, unsigned long len) -{ - EVP_DigestUpdate(ctx, data, len); -} - -void evp_final(EVPCTX ctx, unsigned char *md, unsigned int *mdlen) -{ - EVP_DigestFinal(ctx, md, mdlen); - EVP_MD_CTX_free(ctx); -} -#endif - -SHA256CTX sha256_init(void) -{ - int rc; - SHA256CTX c = EVP_MD_CTX_new(); - if (c == NULL) { - return NULL; - } - rc = EVP_DigestInit_ex(c, EVP_sha256(), NULL); - if (rc == 0) { - EVP_MD_CTX_free(c); - c = NULL; - } - return c; -} - -void sha256_update(SHA256CTX c, const void *data, unsigned long len) -{ - EVP_DigestUpdate(c, data, len); -} - -void sha256_final(unsigned char *md, SHA256CTX c) -{ - unsigned int mdlen = 0; - - EVP_DigestFinal(c, md, &mdlen); - EVP_MD_CTX_free(c); -} - -void sha256(const unsigned char *digest, int len, unsigned char *hash) -{ - SHA256CTX c = sha256_init(); - if (c != NULL) { - sha256_update(c, digest, len); - sha256_final(hash, c); - } -} - -SHA384CTX sha384_init(void) -{ - int rc; - SHA384CTX c = EVP_MD_CTX_new(); - if (c == NULL) { - return NULL; - } - rc = EVP_DigestInit_ex(c, EVP_sha384(), NULL); - if (rc == 0) { - EVP_MD_CTX_free(c); - c = NULL; - } - return c; -} - -void sha384_update(SHA384CTX c, const void *data, unsigned long len) -{ - EVP_DigestUpdate(c, data, len); -} - -void sha384_final(unsigned char *md, SHA384CTX c) -{ - unsigned int mdlen = 0; - - EVP_DigestFinal(c, md, &mdlen); - EVP_MD_CTX_free(c); -} - -void sha384(const unsigned char *digest, int len, unsigned char *hash) -{ - SHA384CTX c = sha384_init(); - if (c != NULL) { - sha384_update(c, digest, len); - sha384_final(hash, c); - } -} - -SHA512CTX sha512_init(void) -{ - int rc = 0; - SHA512CTX c = EVP_MD_CTX_new(); - if (c == NULL) { - return NULL; - } - rc = EVP_DigestInit_ex(c, EVP_sha512(), NULL); - if (rc == 0) { - EVP_MD_CTX_free(c); - c = NULL; - } - return c; -} - -void sha512_update(SHA512CTX c, const void *data, unsigned long len) -{ - EVP_DigestUpdate(c, data, len); -} - -void sha512_final(unsigned char *md, SHA512CTX c) -{ - unsigned int mdlen = 0; - - EVP_DigestFinal(c, md, &mdlen); - EVP_MD_CTX_free(c); -} - -void sha512(const unsigned char *digest, int len, unsigned char *hash) -{ - SHA512CTX c = sha512_init(); - if (c != NULL) { - sha512_update(c, digest, len); - sha512_final(hash, c); - } -} + } + SSH_LOG(SSH_LOG_DEBUG, "Engine loaded successfully"); + + ok = ENGINE_init(engine); + if (!ok) { + SSH_LOG(SSH_LOG_TRACE, + "Could not initialize the engine: %s", + ERR_error_string(ERR_get_error(), NULL)); + ENGINE_free(engine); + return NULL; + } -MD5CTX md5_init(void) -{ - int rc; - MD5CTX c = EVP_MD_CTX_new(); - if (c == NULL) { - return NULL; - } - rc = EVP_DigestInit_ex(c, EVP_md5(), NULL); - if(rc == 0) { - EVP_MD_CTX_free(c); - c = NULL; + SSH_LOG(SSH_LOG_DEBUG, "Engine init success"); } - return c; -} - -void md5_update(MD5CTX c, const void *data, unsigned long len) -{ - EVP_DigestUpdate(c, data, len); + return engine; } +#endif /* WITH_PKCS11_PROVIDER */ -void md5_final(unsigned char *md, MD5CTX c) -{ - unsigned int mdlen = 0; - - EVP_DigestFinal(c, md, &mdlen); - EVP_MD_CTX_free(c); -} - -#ifdef HAVE_OPENSSL_EVP_KDF_CTX_NEW_ID +#ifdef HAVE_OPENSSL_EVP_KDF_CTX +#if OPENSSL_VERSION_NUMBER < 0x30000000L static const EVP_MD *sshkdf_digest_to_md(enum ssh_kdf_digest digest_type) { switch (digest_type) { @@ -356,19 +143,50 @@ static const EVP_MD *sshkdf_digest_to_md(enum ssh_kdf_digest digest_type) } return NULL; } +#else +static const char *sshkdf_digest_to_md(enum ssh_kdf_digest digest_type) +{ + switch (digest_type) { + case SSH_KDF_SHA1: + return SN_sha1; + case SSH_KDF_SHA256: + return SN_sha256; + case SSH_KDF_SHA384: + return SN_sha384; + case SSH_KDF_SHA512: + return SN_sha512; + } + return NULL; +} +#endif /* OPENSSL_VERSION_NUMBER */ int ssh_kdf(struct ssh_crypto_struct *crypto, unsigned char *key, size_t key_len, - int key_type, unsigned char *output, + uint8_t key_type, unsigned char *output, size_t requested_len) { + int rc = -1; +#if OPENSSL_VERSION_NUMBER < 0x30000000L EVP_KDF_CTX *ctx = EVP_KDF_CTX_new_id(EVP_KDF_SSHKDF); - int rc; +#else + EVP_KDF *kdf = EVP_KDF_fetch(NULL, "SSHKDF", NULL); + EVP_KDF_CTX *ctx = EVP_KDF_CTX_new(kdf); + OSSL_PARAM_BLD *param_bld = OSSL_PARAM_BLD_new(); + OSSL_PARAM *params = NULL; + const char *md = sshkdf_digest_to_md(crypto->digest_type); + + EVP_KDF_free(kdf); + if (param_bld == NULL) { + EVP_KDF_CTX_free(ctx); + return -1; + } +#endif /* OPENSSL_VERSION_NUMBER */ if (ctx == NULL) { - return -1; + goto out; } +#if OPENSSL_VERSION_NUMBER < 0x30000000L rc = EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_MD, sshkdf_digest_to_md(crypto->digest_type)); if (rc != 1) { @@ -388,7 +206,7 @@ int ssh_kdf(struct ssh_crypto_struct *crypto, goto out; } rc = EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_SSHKDF_SESSION_ID, - crypto->session_id, crypto->digest_len); + crypto->session_id, crypto->session_id_len); if (rc != 1) { goto out; } @@ -396,8 +214,60 @@ int ssh_kdf(struct ssh_crypto_struct *crypto, if (rc != 1) { goto out; } +#else + rc = OSSL_PARAM_BLD_push_utf8_string(param_bld, OSSL_KDF_PARAM_DIGEST, + md, strlen(md)); + if (rc != 1) { + rc = -1; + goto out; + } + rc = OSSL_PARAM_BLD_push_octet_string(param_bld, OSSL_KDF_PARAM_KEY, + key, key_len); + if (rc != 1) { + rc = -1; + goto out; + } + rc = OSSL_PARAM_BLD_push_octet_string(param_bld, + OSSL_KDF_PARAM_SSHKDF_XCGHASH, + crypto->secret_hash, + crypto->digest_len); + if (rc != 1) { + rc = -1; + goto out; + } + rc = OSSL_PARAM_BLD_push_octet_string(param_bld, + OSSL_KDF_PARAM_SSHKDF_SESSION_ID, + crypto->session_id, + crypto->session_id_len); + if (rc != 1) { + rc = -1; + goto out; + } + rc = OSSL_PARAM_BLD_push_utf8_string(param_bld, OSSL_KDF_PARAM_SSHKDF_TYPE, + (const char*)&key_type, 1); + if (rc != 1) { + rc = -1; + goto out; + } + + params = OSSL_PARAM_BLD_to_param(param_bld); + if (params == NULL) { + rc = -1; + goto out; + } + + rc = EVP_KDF_derive(ctx, output, requested_len, params); + if (rc != 1) { + rc = -1; + goto out; + } +#endif /* OPENSSL_VERSION_NUMBER */ out: +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + OSSL_PARAM_BLD_free(param_bld); + OSSL_PARAM_free(params); +#endif EVP_KDF_CTX_free(ctx); if (rc < 0) { return rc; @@ -408,15 +278,15 @@ out: #else int ssh_kdf(struct ssh_crypto_struct *crypto, unsigned char *key, size_t key_len, - int key_type, unsigned char *output, + uint8_t key_type, unsigned char *output, size_t requested_len) { return sshkdf_derive_key(crypto, key, key_len, key_type, output, requested_len); } -#endif +#endif /* HAVE_OPENSSL_EVP_KDF_CTX */ -HMACCTX hmac_init(const void *key, int len, enum ssh_hmac_e type) +HMACCTX hmac_init(const void *key, size_t len, enum ssh_hmac_e type) { HMACCTX ctx = NULL; EVP_PKEY *pkey = NULL; @@ -461,17 +331,22 @@ error: return NULL; } -void hmac_update(HMACCTX ctx, const void *data, unsigned long len) +int hmac_update(HMACCTX ctx, const void *data, size_t len) { - EVP_DigestSignUpdate(ctx, data, len); + return EVP_DigestSignUpdate(ctx, data, len); } -void hmac_final(HMACCTX ctx, unsigned char *hashmacbuf, unsigned int *len) +int hmac_final(HMACCTX ctx, unsigned char *hashmacbuf, size_t *len) { - size_t res; - EVP_DigestSignFinal(ctx, hashmacbuf, &res); + size_t res = *len; + int rc; + rc = EVP_DigestSignFinal(ctx, hashmacbuf, &res); EVP_MD_CTX_free(ctx); - *len = res; + if (rc == 1) { + *len = res; + } + + return rc; } static void evp_cipher_init(struct ssh_cipher_struct *cipher) @@ -515,12 +390,12 @@ static void evp_cipher_init(struct ssh_cipher_struct *cipher) cipher->cipher = EVP_bf_cbc(); break; /* ciphers not using EVP */ -#endif +#endif /* WITH_BLOWFISH_CIPHER */ case SSH_AEAD_CHACHA20_POLY1305: - SSH_LOG(SSH_LOG_WARNING, "The ChaCha cipher cannot be handled here"); + SSH_LOG(SSH_LOG_TRACE, "The ChaCha cipher cannot be handled here"); break; case SSH_NO_CIPHER: - SSH_LOG(SSH_LOG_WARNING, "No valid ciphertype found"); + SSH_LOG(SSH_LOG_TRACE, "No valid ciphertype found"); break; } } @@ -534,7 +409,7 @@ static int evp_cipher_set_encrypt_key(struct ssh_cipher_struct *cipher, rc = EVP_EncryptInit_ex(cipher->ctx, cipher->cipher, NULL, key, IV); if (rc != 1){ - SSH_LOG(SSH_LOG_WARNING, "EVP_EncryptInit_ex failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_EncryptInit_ex failed"); return SSH_ERROR; } @@ -546,7 +421,7 @@ static int evp_cipher_set_encrypt_key(struct ssh_cipher_struct *cipher, -1, (uint8_t *)IV); if (rc != 1) { - SSH_LOG(SSH_LOG_WARNING, "EVP_CTRL_GCM_SET_IV_FIXED failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_CTRL_GCM_SET_IV_FIXED failed"); return SSH_ERROR; } } @@ -564,7 +439,7 @@ static int evp_cipher_set_decrypt_key(struct ssh_cipher_struct *cipher, rc = EVP_DecryptInit_ex(cipher->ctx, cipher->cipher, NULL, key, IV); if (rc != 1){ - SSH_LOG(SSH_LOG_WARNING, "EVP_DecryptInit_ex failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_DecryptInit_ex failed"); return SSH_ERROR; } @@ -576,7 +451,7 @@ static int evp_cipher_set_decrypt_key(struct ssh_cipher_struct *cipher, -1, (uint8_t *)IV); if (rc != 1) { - SSH_LOG(SSH_LOG_WARNING, "EVP_CTRL_GCM_SET_IV_FIXED failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_CTRL_GCM_SET_IV_FIXED failed"); return SSH_ERROR; } } @@ -601,11 +476,11 @@ static void evp_cipher_encrypt(struct ssh_cipher_struct *cipher, (unsigned char *)in, (int)len); if (rc != 1){ - SSH_LOG(SSH_LOG_WARNING, "EVP_EncryptUpdate failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_EncryptUpdate failed"); return; } if (outlen != (int)len){ - SSH_LOG(SSH_LOG_WARNING, + SSH_LOG(SSH_LOG_DEBUG, "EVP_EncryptUpdate: output size %d for %zu in", outlen, len); @@ -627,11 +502,11 @@ static void evp_cipher_decrypt(struct ssh_cipher_struct *cipher, (unsigned char *)in, (int)len); if (rc != 1){ - SSH_LOG(SSH_LOG_WARNING, "EVP_DecryptUpdate failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_DecryptUpdate failed"); return; } if (outlen != (int)len){ - SSH_LOG(SSH_LOG_WARNING, + SSH_LOG(SSH_LOG_DEBUG, "EVP_DecryptUpdate: output size %d for %zu in", outlen, len); @@ -686,7 +561,7 @@ evp_cipher_aead_encrypt(struct ssh_cipher_struct *cipher, 1, lastiv); if (rc == 0) { - SSH_LOG(SSH_LOG_WARNING, "EVP_CTRL_GCM_IV_GEN failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_CTRL_GCM_IV_GEN failed"); return; } @@ -698,7 +573,7 @@ evp_cipher_aead_encrypt(struct ssh_cipher_struct *cipher, (int)aadlen); outlen = tmplen; if (rc == 0 || outlen != aadlen) { - SSH_LOG(SSH_LOG_WARNING, "Failed to pass authenticated data"); + SSH_LOG(SSH_LOG_TRACE, "Failed to pass authenticated data"); return; } memcpy(out, in, aadlen); @@ -711,7 +586,7 @@ evp_cipher_aead_encrypt(struct ssh_cipher_struct *cipher, (int)len - aadlen); outlen = tmplen; if (rc != 1 || outlen != (int)len - aadlen) { - SSH_LOG(SSH_LOG_WARNING, "EVP_EncryptUpdate failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_EncryptUpdate failed"); return; } @@ -720,7 +595,7 @@ evp_cipher_aead_encrypt(struct ssh_cipher_struct *cipher, NULL, &tmplen); if (rc < 0) { - SSH_LOG(SSH_LOG_WARNING, "EVP_EncryptFinal failed: Failed to create a tag"); + SSH_LOG(SSH_LOG_TRACE, "EVP_EncryptFinal failed: Failed to create a tag"); return; } @@ -729,7 +604,7 @@ evp_cipher_aead_encrypt(struct ssh_cipher_struct *cipher, authlen, (unsigned char *)tag); if (rc != 1) { - SSH_LOG(SSH_LOG_WARNING, "EVP_CTRL_GCM_GET_TAG failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_CTRL_GCM_GET_TAG failed"); return; } } @@ -757,7 +632,7 @@ evp_cipher_aead_decrypt(struct ssh_cipher_struct *cipher, 1, lastiv); if (rc == 0) { - SSH_LOG(SSH_LOG_WARNING, "EVP_CTRL_GCM_IV_GEN failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_CTRL_GCM_IV_GEN failed"); return SSH_ERROR; } @@ -767,7 +642,7 @@ evp_cipher_aead_decrypt(struct ssh_cipher_struct *cipher, authlen, (unsigned char *)complete_packet + aadlen + encrypted_size); if (rc == 0) { - SSH_LOG(SSH_LOG_WARNING, "EVP_CTRL_GCM_SET_TAG failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_CTRL_GCM_SET_TAG failed"); return SSH_ERROR; } @@ -778,7 +653,7 @@ evp_cipher_aead_decrypt(struct ssh_cipher_struct *cipher, (unsigned char *)complete_packet, (int)aadlen); if (rc == 0) { - SSH_LOG(SSH_LOG_WARNING, "Failed to pass authenticated data"); + SSH_LOG(SSH_LOG_TRACE, "Failed to pass authenticated data"); return SSH_ERROR; } /* Do not copy the length to the target buffer, because it is already processed */ @@ -789,14 +664,14 @@ evp_cipher_aead_decrypt(struct ssh_cipher_struct *cipher, (unsigned char *)out, &outlen, (unsigned char *)complete_packet + aadlen, - encrypted_size /* already substracted aadlen*/); + encrypted_size /* already subtracted aadlen */); if (rc != 1) { - SSH_LOG(SSH_LOG_WARNING, "EVP_DecryptUpdate failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_DecryptUpdate failed"); return SSH_ERROR; } if (outlen != (int)encrypted_size) { - SSH_LOG(SSH_LOG_WARNING, + SSH_LOG(SSH_LOG_TRACE, "EVP_DecryptUpdate: output size %d for %zd in", outlen, encrypted_size); @@ -808,26 +683,31 @@ evp_cipher_aead_decrypt(struct ssh_cipher_struct *cipher, NULL, &outlen); if (rc < 0) { - SSH_LOG(SSH_LOG_WARNING, "EVP_DecryptFinal failed: Failed authentication"); + SSH_LOG(SSH_LOG_TRACE, "EVP_DecryptFinal failed: Failed authentication"); return SSH_ERROR; } return SSH_OK; } -#if defined(HAVE_OPENSSL_EVP_CHACHA20) && defined(HAVE_OPENSSL_EVP_POLY1305) +#ifdef HAVE_OPENSSL_EVP_CHACHA20 struct chacha20_poly1305_keysched { /* cipher handle used for encrypting the packets */ EVP_CIPHER_CTX *main_evp; /* cipher handle used for encrypting the length field */ EVP_CIPHER_CTX *header_evp; +#if OPENSSL_VERSION_NUMBER < 0x30000000L /* mac handle used for authenticating the packets */ EVP_PKEY_CTX *pctx; /* Poly1305 key */ EVP_PKEY *key; /* MD context for digesting data in poly1305 */ EVP_MD_CTX *mctx; +#else + /* MAC context used to do poly1305 */ + EVP_MAC_CTX *mctx; +#endif /* OPENSSL_VERSION_NUMBER */ }; static void @@ -845,11 +725,16 @@ chacha20_poly1305_cleanup(struct ssh_cipher_struct *cipher) ctx->main_evp = NULL; EVP_CIPHER_CTX_free(ctx->header_evp); ctx->header_evp = NULL; +#if OPENSSL_VERSION_NUMBER < 0x30000000L /* ctx->pctx is freed as part of MD context */ EVP_PKEY_free(ctx->key); ctx->key = NULL; EVP_MD_CTX_free(ctx->mctx); ctx->mctx = NULL; +#else + EVP_MAC_CTX_free(ctx->mctx); + ctx->mctx = NULL; +#endif /* OPENSSL_VERSION_NUMBER */ SAFE_FREE(cipher->chacha20_schedule); } @@ -862,6 +747,9 @@ chacha20_poly1305_set_key(struct ssh_cipher_struct *cipher, struct chacha20_poly1305_keysched *ctx = NULL; uint8_t *u8key = key; int ret = SSH_ERROR, rv; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + EVP_MAC *mac = NULL; +#endif if (cipher->chacha20_schedule == NULL) { ctx = calloc(1, sizeof(*ctx)); @@ -877,38 +765,54 @@ chacha20_poly1305_set_key(struct ssh_cipher_struct *cipher, /* K2 uses the first half of the key */ ctx->main_evp = EVP_CIPHER_CTX_new(); if (ctx->main_evp == NULL) { - SSH_LOG(SSH_LOG_WARNING, "EVP_CIPHER_CTX_new failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_CIPHER_CTX_new failed"); goto out; } rv = EVP_EncryptInit_ex(ctx->main_evp, EVP_chacha20(), NULL, u8key, NULL); if (rv != 1) { - SSH_LOG(SSH_LOG_WARNING, "EVP_CipherInit failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_CipherInit failed"); goto out; } /* K1 uses the second half of the key */ ctx->header_evp = EVP_CIPHER_CTX_new(); if (ctx->header_evp == NULL) { - SSH_LOG(SSH_LOG_WARNING, "EVP_CIPHER_CTX_new failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_CIPHER_CTX_new failed"); goto out; } ret = EVP_EncryptInit_ex(ctx->header_evp, EVP_chacha20(), NULL, u8key + CHACHA20_KEYLEN, NULL); if (ret != 1) { - SSH_LOG(SSH_LOG_WARNING, "EVP_CipherInit failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_CipherInit failed"); goto out; } /* The Poly1305 key initialization is delayed to the time we know * the actual key for packet so we do not need to create a bogus keys */ +#if OPENSSL_VERSION_NUMBER < 0x30000000L ctx->mctx = EVP_MD_CTX_new(); if (ctx->mctx == NULL) { - SSH_LOG(SSH_LOG_WARNING, "EVP_MD_CTX_new failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_MD_CTX_new failed"); return SSH_ERROR; } +#else + mac = EVP_MAC_fetch(NULL, "poly1305", NULL); + if (mac == NULL) { + SSH_LOG(SSH_LOG_TRACE, "EVP_MAC_fetch failed"); + goto out; + } + ctx->mctx = EVP_MAC_CTX_new(mac); + if (ctx->mctx == NULL) { + SSH_LOG(SSH_LOG_TRACE, "EVP_MAC_CTX_new failed"); + goto out; + } +#endif /* OPENSSL_VERSION_NUMBER */ ret = SSH_OK; out: +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + EVP_MAC_free(mac); +#endif if (ret != SSH_OK) { chacha20_poly1305_cleanup(cipher); } @@ -937,13 +841,13 @@ chacha20_poly1305_set_iv(struct ssh_cipher_struct *cipher, ret = EVP_CipherInit_ex(ctx->header_evp, NULL, NULL, NULL, seqbuf, do_encrypt); if (ret != 1) { - SSH_LOG(SSH_LOG_WARNING, "EVP_CipherInit_ex(header_evp) failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_CipherInit_ex(header_evp) failed"); return SSH_ERROR; } ret = EVP_CipherInit_ex(ctx->main_evp, NULL, NULL, NULL, seqbuf, do_encrypt); if (ret != 1) { - SSH_LOG(SSH_LOG_WARNING, "EVP_CipherInit_ex(main_evp) failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_CipherInit_ex(main_evp) failed"); return SSH_ERROR; } @@ -972,7 +876,7 @@ chacha20_poly1305_packet_setup(struct ssh_cipher_struct *cipher, rv = EVP_CipherUpdate(ctx->main_evp, poly_key, &len, (unsigned char *)zero_block, sizeof(zero_block)); if (rv != 1 || len != CHACHA20_BLOCKSIZE) { - SSH_LOG(SSH_LOG_WARNING, "EVP_EncryptUpdate failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_EncryptUpdate failed"); goto out; } #ifdef DEBUG_CRYPTO @@ -980,17 +884,18 @@ chacha20_poly1305_packet_setup(struct ssh_cipher_struct *cipher, #endif /* DEBUG_CRYPTO */ /* Set the Poly1305 key */ +#if OPENSSL_VERSION_NUMBER < 0x30000000L if (ctx->key == NULL) { /* Poly1305 Initialization needs to know the actual key */ ctx->key = EVP_PKEY_new_mac_key(EVP_PKEY_POLY1305, NULL, poly_key, POLY1305_KEYLEN); if (ctx->key == NULL) { - SSH_LOG(SSH_LOG_WARNING, "EVP_PKEY_new_mac_key failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_PKEY_new_mac_key failed"); goto out; } rv = EVP_DigestSignInit(ctx->mctx, &ctx->pctx, NULL, NULL, ctx->key); if (rv != 1) { - SSH_LOG(SSH_LOG_WARNING, "EVP_DigestSignInit failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_DigestSignInit failed"); goto out; } } else { @@ -999,10 +904,17 @@ chacha20_poly1305_packet_setup(struct ssh_cipher_struct *cipher, EVP_PKEY_CTRL_SET_MAC_KEY, POLY1305_KEYLEN, (void *)poly_key); if (rv <= 0) { - SSH_LOG(SSH_LOG_WARNING, "EVP_PKEY_CTX_ctrl failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_PKEY_CTX_ctrl failed"); goto out; } } +#else + rv = EVP_MAC_init(ctx->mctx, poly_key, POLY1305_KEYLEN, NULL); + if (rv != 1) { + SSH_LOG(SSH_LOG_TRACE, "EVP_MAC_init failed"); + goto out; + } +#endif /* OPENSSL_VERSION_NUMBER */ ret = SSH_OK; out: @@ -1036,7 +948,7 @@ chacha20_poly1305_aead_decrypt_length(struct ssh_cipher_struct *cipher, rv = EVP_CipherUpdate(ctx->header_evp, out, &outlen, in, len); if (rv != 1 || outlen != sizeof(uint32_t)) { - SSH_LOG(SSH_LOG_WARNING, "EVP_CipherUpdate failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_CipherUpdate failed"); return SSH_ERROR; } @@ -1046,7 +958,7 @@ chacha20_poly1305_aead_decrypt_length(struct ssh_cipher_struct *cipher, rv = EVP_CipherFinal_ex(ctx->header_evp, out + outlen, &outlen); if (rv != 1 || outlen != 0) { - SSH_LOG(SSH_LOG_WARNING, "EVP_CipherFinal_ex failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_CipherFinal_ex failed"); return SSH_ERROR; } @@ -1071,7 +983,7 @@ chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher, /* Prepare the Poly1305 key */ rv = chacha20_poly1305_packet_setup(cipher, seq, 0); if (rv != SSH_OK) { - SSH_LOG(SSH_LOG_WARNING, "Failed to setup packet"); + SSH_LOG(SSH_LOG_TRACE, "Failed to setup packet"); goto out; } @@ -1080,19 +992,34 @@ chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher, #endif /* DEBUG_CRYPTO */ /* Calculate MAC of received data */ +#if OPENSSL_VERSION_NUMBER < 0x30000000L rv = EVP_DigestSignUpdate(ctx->mctx, complete_packet, encrypted_size + sizeof(uint32_t)); if (rv != 1) { - SSH_LOG(SSH_LOG_WARNING, "EVP_DigestSignUpdate failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_DigestSignUpdate failed"); goto out; } rv = EVP_DigestSignFinal(ctx->mctx, tag, &taglen); if (rv != 1) { - SSH_LOG(SSH_LOG_WARNING, "poly1305 verify error"); + SSH_LOG(SSH_LOG_TRACE, "poly1305 verify error"); + goto out; + } +#else + rv = EVP_MAC_update(ctx->mctx, complete_packet, + encrypted_size + sizeof(uint32_t)); + if (rv != 1) { + SSH_LOG(SSH_LOG_TRACE, "EVP_MAC_update failed"); goto out; } + rv = EVP_MAC_final(ctx->mctx, tag, &taglen, POLY1305_TAGLEN); + if (rv != 1) { + SSH_LOG(SSH_LOG_TRACE, "EVP_MAC_final failed"); + goto out; + } +#endif /* OPENSSL_VERSION_NUMBER */ + #ifdef DEBUG_CRYPTO ssh_log_hexdump("calculated mac", tag, POLY1305_TAGLEN); #endif /* DEBUG_CRYPTO */ @@ -1110,13 +1037,13 @@ chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher, (uint8_t *)complete_packet + sizeof(uint32_t), encrypted_size); if (rv != 1) { - SSH_LOG(SSH_LOG_WARNING, "EVP_CipherUpdate failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_CipherUpdate failed"); goto out; } rv = EVP_CipherFinal_ex(ctx->main_evp, out + len, &len); if (rv != 1 || len != 0) { - SSH_LOG(SSH_LOG_WARNING, "EVP_CipherFinal_ex failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_CipherFinal_ex failed"); goto out; } @@ -1141,7 +1068,7 @@ chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher, /* Prepare the Poly1305 key */ ret = chacha20_poly1305_packet_setup(cipher, seq, 1); if (ret != SSH_OK) { - SSH_LOG(SSH_LOG_WARNING, "Failed to setup packet"); + SSH_LOG(SSH_LOG_TRACE, "Failed to setup packet"); return; } @@ -1156,7 +1083,7 @@ chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher, (unsigned char *)&in_packet->length, sizeof(uint32_t)); if (ret != 1 || outlen != sizeof(uint32_t)) { - SSH_LOG(SSH_LOG_WARNING, "EVP_CipherUpdate failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_CipherUpdate failed"); return; } #ifdef DEBUG_CRYPTO @@ -1165,7 +1092,7 @@ chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher, #endif /* DEBUG_CRYPTO */ ret = EVP_CipherFinal_ex(ctx->header_evp, (uint8_t *)out + outlen, &outlen); if (ret != 1 || outlen != 0) { - SSH_LOG(SSH_LOG_PACKET, "EVP_EncryptFinal_ex failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_EncryptFinal_ex failed"); return; } @@ -1177,23 +1104,37 @@ chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher, in_packet->payload, len - sizeof(uint32_t)); if (ret != 1) { - SSH_LOG(SSH_LOG_WARNING, "EVP_CipherUpdate failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_CipherUpdate failed"); return; } /* step 4, compute the MAC */ +#if OPENSSL_VERSION_NUMBER < 0x30000000L ret = EVP_DigestSignUpdate(ctx->mctx, out_packet, len); if (ret <= 0) { - SSH_LOG(SSH_LOG_WARNING, "EVP_DigestSignUpdate failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_DigestSignUpdate failed"); return; } ret = EVP_DigestSignFinal(ctx->mctx, tag, &taglen); if (ret <= 0) { - SSH_LOG(SSH_LOG_WARNING, "EVP_DigestSignFinal failed"); + SSH_LOG(SSH_LOG_TRACE, "EVP_DigestSignFinal failed"); + return; + } +#else + ret = EVP_MAC_update(ctx->mctx, (void*)out_packet, len); + if (ret != 1) { + SSH_LOG(SSH_LOG_TRACE, "EVP_MAC_update failed"); return; } + + ret = EVP_MAC_final(ctx->mctx, tag, &taglen, POLY1305_TAGLEN); + if (ret != 1) { + SSH_LOG(SSH_LOG_TRACE, "EVP_MAC_final failed"); + return; + } +#endif /* OPENSSL_VERSION_NUMBER */ } -#endif /* defined(HAVE_OPENSSL_EVP_CHACHA20) && defined(HAVE_OPENSSL_EVP_POLY1305) */ +#endif /* HAVE_OPENSSL_EVP_CHACHA20 */ #ifdef WITH_INSECURE_NONE static void @@ -1222,7 +1163,7 @@ static struct ssh_cipher_struct ssh_ciphertab[] = { .decrypt = evp_cipher_decrypt, .cleanup = evp_cipher_cleanup }, -#endif +#endif /* WITH_BLOWFISH_CIPHER */ #ifdef HAS_AES { .name = "aes128-ctr", @@ -1333,7 +1274,7 @@ static struct ssh_cipher_struct ssh_ciphertab[] = { }, #endif /* HAS_DES */ { -#if defined(HAVE_OPENSSL_EVP_CHACHA20) && defined(HAVE_OPENSSL_EVP_POLY1305) +#ifdef HAVE_OPENSSL_EVP_CHACHA20 .ciphertype = SSH_AEAD_CHACHA20_POLY1305, .name = "chacha20-poly1305@openssh.com", .blocksize = CHACHA20_BLOCKSIZE/8, @@ -1349,7 +1290,7 @@ static struct ssh_cipher_struct ssh_ciphertab[] = { .cleanup = chacha20_poly1305_cleanup #else .name = "chacha20-poly1305@openssh.com" -#endif /* defined(HAVE_OPENSSL_EVP_CHACHA20) && defined(HAVE_OPENSSL_EVP_POLY1305) */ +#endif /* HAVE_OPENSSL_EVP_CHACHA20 */ }, #ifdef WITH_INSECURE_NONE { @@ -1376,13 +1317,15 @@ struct ssh_cipher_struct *ssh_get_ciphertab(void) */ int ssh_crypto_init(void) { - UNUSED_VAR(size_t i); +#ifndef HAVE_OPENSSL_EVP_CHACHA20 + size_t i; +#endif if (libcrypto_initialized) { return SSH_OK; } if (OpenSSL_version_num() != OPENSSL_VERSION_NUMBER){ - SSH_LOG(SSH_LOG_WARNING, "libssh compiled with %s " + SSH_LOG(SSH_LOG_DEBUG, "libssh compiled with %s " "headers, currently running with %s.", OPENSSL_VERSION_TEXT, OpenSSL_version(OpenSSL_version_num()) @@ -1398,12 +1341,9 @@ int ssh_crypto_init(void) /* Bit #57 denotes AES-NI instruction set extension */ OPENSSL_ia32cap &= ~(1LL << 57); } -#endif -#if OPENSSL_VERSION_NUMBER < 0x10100000L - OpenSSL_add_all_algorithms(); -#endif +#endif /* CAN_DISABLE_AESNI */ -#if !defined(HAVE_OPENSSL_EVP_CHACHA20) || !defined(HAVE_OPENSSL_EVP_POLY1305) +#ifndef HAVE_OPENSSL_EVP_CHACHA20 for (i = 0; ssh_ciphertab[i].name != NULL; i++) { int cmp; @@ -1415,7 +1355,7 @@ int ssh_crypto_init(void) break; } } -#endif /* !defined(HAVE_OPENSSL_EVP_CHACHA20) || !defined(HAVE_OPENSSL_EVP_POLY1305) */ +#endif /* HAVE_OPENSSL_EVP_CHACHA20 */ libcrypto_initialized = 1; @@ -1432,13 +1372,210 @@ void ssh_crypto_finalize(void) return; } - ENGINE_cleanup(); -#if OPENSSL_VERSION_NUMBER < 0x10100000L - EVP_cleanup(); - CRYPTO_cleanup_all_ex_data(); +/* TODO this should finalize engine if it was started, but during atexit calls, + * we are crashing. AFAIK this is related to the dlopened pkcs11 modules calling + * the crypto cleanups earlier. */ +#if 0 + if (engine != NULL) { + ENGINE_finish(engine); + ENGINE_free(engine); + engine = NULL; + } #endif libcrypto_initialized = 0; } +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +/** + * @internal + * @brief Create EVP_PKEY from parameters + * + * @param[in] name Algorithm to use. For more info see manpage of EVP_PKEY_CTX_new_from_name + * + * @param[in] param_bld Constructed param builder for the pkey + * + * @param[out] pkey Created EVP_PKEY variable + * + * @param[in] selection Reference selections at man EVP_PKEY_FROMDATA + * + * @return 0 on success, -1 on error + */ +int evp_build_pkey(const char* name, OSSL_PARAM_BLD *param_bld, + EVP_PKEY **pkey, int selection) +{ + int rc; + EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, name, NULL); + OSSL_PARAM *params = NULL; + + if (ctx == NULL) { + return -1; + } + + params = OSSL_PARAM_BLD_to_param(param_bld); + if (params == NULL) { + EVP_PKEY_CTX_free(ctx); + return -1; + } + + rc = EVP_PKEY_fromdata_init(ctx); + if (rc != 1) { + OSSL_PARAM_free(params); + EVP_PKEY_CTX_free(ctx); + return -1; + } + + rc = EVP_PKEY_fromdata(ctx, pkey, selection, params); + if (rc != 1) { + SSH_LOG(SSH_LOG_WARNING, + "Failed to import private key: %s\n", + ERR_error_string(ERR_get_error(), NULL)); + OSSL_PARAM_free(params); + EVP_PKEY_CTX_free(ctx); + return -1; + } + + OSSL_PARAM_free(params); + EVP_PKEY_CTX_free(ctx); + + return SSH_OK; +} + +/** + * @brief creates a copy of EVP_PKEY + * + * @param[in] name Algorithm to use. For more info see manpage of + * EVP_PKEY_CTX_new_from_name + * + * @param[in] key Key being duplicated from + * + * @param[in] demote Same as at pki_key_dup, only the public + * part of the key gets duplicated if true + * + * @param[out] new_key The key where the duplicate is saved + * + * @return 0 on success, -1 on error + */ +static int +evp_dup_pkey(const char *name, const ssh_key key, int demote, ssh_key new_key) +{ + int rc; + EVP_PKEY_CTX *ctx = NULL; + OSSL_PARAM *params = NULL; + + /* The simple case -- just reference the existing key */ + if (!demote || (key->flags & SSH_KEY_FLAG_PRIVATE) == 0) { + rc = EVP_PKEY_up_ref(key->key); + if (rc != 1) { + return -1; + } + new_key->key = key->key; + return SSH_OK; + } + + /* demote == 1 */ + ctx = EVP_PKEY_CTX_new_from_name(NULL, name, NULL); + if (ctx == NULL) { + return -1; + } + + rc = EVP_PKEY_todata(key->key, EVP_PKEY_PUBLIC_KEY, ¶ms); + if (rc != 1) { + EVP_PKEY_CTX_free(ctx); + return -1; + } + + if (strcmp(name, "EC") == 0) { + OSSL_PARAM *locate_param = NULL; + /* For ECC keys provided by engine or provider, we need to have the + * explicit public part available, otherwise the key will not be + * usable */ + locate_param = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PUB_KEY); + if (locate_param == NULL) { + EVP_PKEY_CTX_free(ctx); + OSSL_PARAM_free(params); + return -1; + } + } + rc = EVP_PKEY_fromdata_init(ctx); + if (rc != 1) { + EVP_PKEY_CTX_free(ctx); + OSSL_PARAM_free(params); + return -1; + } + + rc = EVP_PKEY_fromdata(ctx, &(new_key->key), EVP_PKEY_PUBLIC_KEY, params); + if (rc != 1) { + EVP_PKEY_CTX_free(ctx); + OSSL_PARAM_free(params); + return -1; + } + + OSSL_PARAM_free(params); + EVP_PKEY_CTX_free(ctx); + + return SSH_OK; +} + +int evp_dup_rsa_pkey(const ssh_key key, ssh_key new_key, int demote) +{ + return evp_dup_pkey("RSA", key, demote, new_key); +} + +int evp_dup_ecdsa_pkey(const ssh_key key, ssh_key new_key, int demote) +{ + return evp_dup_pkey("EC", key, demote, new_key); +} +#endif /* OPENSSL_VERSION_NUMBER */ + +ssh_string +pki_key_make_ecpoint_string(const EC_GROUP *g, const EC_POINT *p) +{ + ssh_string s = NULL; + size_t len; + + len = EC_POINT_point2oct(g, + p, + POINT_CONVERSION_UNCOMPRESSED, + NULL, + 0, + NULL); + if (len == 0) { + return NULL; + } + + s = ssh_string_new(len); + if (s == NULL) { + return NULL; + } + + len = EC_POINT_point2oct(g, + p, + POINT_CONVERSION_UNCOMPRESSED, + ssh_string_data(s), + ssh_string_len(s), + NULL); + if (len != ssh_string_len(s)) { + SSH_STRING_FREE(s); + return NULL; + } + + return s; +} + +int pki_key_ecgroup_name_to_nid(const char *group) +{ + if (strcmp(group, NISTP256) == 0 || + strcmp(group, "secp256r1") == 0 || + strcmp(group, "prime256v1") == 0) { + return NID_X9_62_prime256v1; + } else if (strcmp(group, NISTP384) == 0 || + strcmp(group, "secp384r1") == 0) { + return NID_secp384r1; + } else if (strcmp(group, NISTP521) == 0 || + strcmp(group, "secp521r1") == 0) { + return NID_secp521r1; + } + return -1; +} #endif /* LIBCRYPTO */ |