diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/kdf.c | 167 | ||||
-rw-r--r-- | src/kex.c | 78 | ||||
-rw-r--r-- | src/libcrypto.c | 140 | ||||
-rw-r--r-- | src/libgcrypt.c | 63 | ||||
-rw-r--r-- | src/libmbedcrypto.c | 69 |
6 files changed, 274 insertions, 244 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3261d42c..fdb53baf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -130,6 +130,7 @@ set(libssh_SRCS error.c getpass.c init.c + kdf.c kex.c known_hosts.c knownhosts.c diff --git a/src/kdf.c b/src/kdf.c new file mode 100644 index 00000000..0e90e188 --- /dev/null +++ b/src/kdf.c @@ -0,0 +1,167 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2009 by Aris Adamantiadis + * Copyrihgt (c) 2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> + +#include "libssh/priv.h" +#include "libssh/crypto.h" +#include "libssh/buffer.h" +#include "libssh/session.h" +#include "libssh/misc.h" +#include "libssh/dh.h" +#include "libssh/ssh2.h" +#include "libssh/pki.h" +#include "libssh/bignum.h" + +#include "libssh/string.h" + + +/* The following implements the SSHKDF for crypto backend that + * do not have a native implementations */ +struct ssh_mac_ctx_struct { + enum ssh_kdf_digest digest_type; + union { + SHACTX sha1_ctx; + SHA256CTX sha256_ctx; + SHA384CTX sha384_ctx; + SHA512CTX sha512_ctx; + } ctx; +}; + +static ssh_mac_ctx ssh_mac_ctx_init(enum ssh_kdf_digest type) +{ + ssh_mac_ctx ctx = malloc(sizeof(struct ssh_mac_ctx_struct)); + if (ctx == NULL) { + return NULL; + } + + ctx->digest_type = type; + switch(type){ + case SSH_KDF_SHA1: + ctx->ctx.sha1_ctx = sha1_init(); + return ctx; + case SSH_KDF_SHA256: + ctx->ctx.sha256_ctx = sha256_init(); + return ctx; + case SSH_KDF_SHA384: + ctx->ctx.sha384_ctx = sha384_init(); + return ctx; + case SSH_KDF_SHA512: + ctx->ctx.sha512_ctx = sha512_init(); + return ctx; + default: + SAFE_FREE(ctx); + return NULL; + } +} + +static void ssh_mac_update(ssh_mac_ctx ctx, const void *data, size_t len) +{ + switch(ctx->digest_type){ + case SSH_KDF_SHA1: + sha1_update(ctx->ctx.sha1_ctx, data, len); + break; + case SSH_KDF_SHA256: + sha256_update(ctx->ctx.sha256_ctx, data, len); + break; + case SSH_KDF_SHA384: + sha384_update(ctx->ctx.sha384_ctx, data, len); + break; + case SSH_KDF_SHA512: + sha512_update(ctx->ctx.sha512_ctx, data, len); + break; + } +} + +static void ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx) +{ + switch(ctx->digest_type){ + case SSH_KDF_SHA1: + sha1_final(md,ctx->ctx.sha1_ctx); + break; + case SSH_KDF_SHA256: + sha256_final(md,ctx->ctx.sha256_ctx); + break; + case SSH_KDF_SHA384: + sha384_final(md,ctx->ctx.sha384_ctx); + break; + case SSH_KDF_SHA512: + sha512_final(md,ctx->ctx.sha512_ctx); + break; + } + SAFE_FREE(ctx); +} + +int sshkdf_derive_key(struct ssh_crypto_struct *crypto, + unsigned char *key, size_t key_len, + int key_type, unsigned char *output, + size_t requested_len) +{ + /* Can't use VLAs with Visual Studio, so allocate the biggest + * digest buffer we can possibly need */ + unsigned char digest[DIGEST_MAX_LEN]; + size_t output_len = crypto->digest_len; + char letter = key_type; + ssh_mac_ctx ctx; + + if (DIGEST_MAX_LEN < crypto->digest_len) { + return -1; + } + + ctx = ssh_mac_ctx_init(crypto->digest_type); + if (ctx == NULL) { + return -1; + } + + ssh_mac_update(ctx, key, key_len); + ssh_mac_update(ctx, crypto->secret_hash, crypto->digest_len); + ssh_mac_update(ctx, &letter, 1); + ssh_mac_update(ctx, crypto->session_id, crypto->digest_len); + ssh_mac_final(digest, ctx); + + if (requested_len < output_len) { + output_len = requested_len; + } + memcpy(output, digest, output_len); + + while (requested_len > output_len) { + ctx = ssh_mac_ctx_init(crypto->digest_type); + if (ctx == NULL) { + return -1; + } + ssh_mac_update(ctx, key, key_len); + ssh_mac_update(ctx, crypto->secret_hash, crypto->digest_len); + ssh_mac_update(ctx, output, output_len); + ssh_mac_final(digest, ctx); + if (requested_len < output_len + crypto->digest_len) { + memcpy(output + output_len, digest, requested_len - output_len); + } else { + memcpy(output + output_len, digest, crypto->digest_len); + } + output_len += crypto->digest_len; + } + + return 0; +} @@ -1196,7 +1196,7 @@ int ssh_make_sessionid(ssh_session session) case SSH_KEX_DH_GEX_SHA1: #endif /* WITH_GEX */ session->next_crypto->digest_len = SHA_DIGEST_LENGTH; - session->next_crypto->mac_type = SSH_MAC_SHA1; + session->next_crypto->digest_type = SSH_KDF_SHA1; session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len); if (session->next_crypto->secret_hash == NULL) { ssh_set_error_oom(session); @@ -1212,7 +1212,7 @@ int ssh_make_sessionid(ssh_session session) case SSH_KEX_DH_GEX_SHA256: #endif /* WITH_GEX */ session->next_crypto->digest_len = SHA256_DIGEST_LENGTH; - session->next_crypto->mac_type = SSH_MAC_SHA256; + session->next_crypto->digest_type = SSH_KDF_SHA256; session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len); if (session->next_crypto->secret_hash == NULL) { ssh_set_error_oom(session); @@ -1223,7 +1223,7 @@ int ssh_make_sessionid(ssh_session session) break; case SSH_KEX_ECDH_SHA2_NISTP384: session->next_crypto->digest_len = SHA384_DIGEST_LENGTH; - session->next_crypto->mac_type = SSH_MAC_SHA384; + session->next_crypto->digest_type = SSH_KDF_SHA384; session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len); if (session->next_crypto->secret_hash == NULL) { ssh_set_error_oom(session); @@ -1236,7 +1236,7 @@ int ssh_make_sessionid(ssh_session session) case SSH_KEX_DH_GROUP18_SHA512: case SSH_KEX_ECDH_SHA2_NISTP521: session->next_crypto->digest_len = SHA512_DIGEST_LENGTH; - session->next_crypto->mac_type = SSH_MAC_SHA512; + session->next_crypto->digest_type = SSH_KDF_SHA512; session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len); if (session->next_crypto->secret_hash == NULL) { ssh_set_error_oom(session); @@ -1346,60 +1346,18 @@ int ssh_hashbufin_add_cookie(ssh_session session, unsigned char *cookie) return 0; } -static int generate_one_key(ssh_string k, struct ssh_crypto_struct *crypto, - unsigned char *output, char letter, - size_t requested_size) -{ - ssh_mac_ctx ctx; - size_t size = crypto->digest_len; - unsigned char digest[size]; - - ctx = ssh_mac_ctx_init(crypto->mac_type); - if (ctx == NULL) { - return -1; - } - - ssh_mac_update(ctx, k, ssh_string_len(k) + 4); - ssh_mac_update(ctx, crypto->secret_hash, crypto->digest_len); - ssh_mac_update(ctx, &letter, 1); - ssh_mac_update(ctx, crypto->session_id, crypto->digest_len); - ssh_mac_final(digest, ctx); - - if (requested_size < size) { - size = requested_size; - } - memcpy(output, digest, size); - - while (requested_size > size) { - ctx = ssh_mac_ctx_init(crypto->mac_type); - if (ctx == NULL) { - return -1; - } - ssh_mac_update(ctx, k, ssh_string_len(k) + 4); - ssh_mac_update(ctx, crypto->secret_hash, crypto->digest_len); - ssh_mac_update(ctx, output, size); - ssh_mac_final(digest, ctx); - if (requested_size < size + crypto->digest_len) { - memcpy(output+size, digest, requested_size - size); - } else { - memcpy(output+size, digest, crypto->digest_len); - } - size += crypto->digest_len; - } - - return 0; -} - int ssh_generate_session_keys(ssh_session session) { ssh_string k_string = NULL; struct ssh_crypto_struct *crypto = session->next_crypto; + unsigned char *key = NULL; unsigned char *IV_cli_to_srv = NULL; unsigned char *IV_srv_to_cli = NULL; unsigned char *enckey_cli_to_srv = NULL; unsigned char *enckey_srv_to_cli = NULL; unsigned char *intkey_cli_to_srv = NULL; unsigned char *intkey_srv_to_cli = NULL; + size_t key_len = 0; size_t IV_len = 0; size_t enckey_cli_to_srv_len = 0; size_t enckey_srv_to_cli_len = 0; @@ -1412,6 +1370,10 @@ int ssh_generate_session_keys(ssh_session session) ssh_set_error_oom(session); goto error; } + /* See RFC4251 Section 5 for the definition of mpint which is the + * encoding we need to use for key in the SSH KDF */ + key = (unsigned char *)k_string; + key_len = ssh_string_len(k_string) + 4; IV_len = crypto->digest_len; if (session->client) { @@ -1440,33 +1402,33 @@ int ssh_generate_session_keys(ssh_session session) } /* IV */ - rc = generate_one_key(k_string, crypto, IV_cli_to_srv, 'A', IV_len); + rc = ssh_kdf(crypto, key, key_len, 'A', IV_cli_to_srv, IV_len); if (rc < 0) { goto error; } - rc = generate_one_key(k_string, crypto, IV_srv_to_cli, 'B', IV_len); + rc = ssh_kdf(crypto, key, key_len, 'B', IV_srv_to_cli, IV_len); if (rc < 0) { goto error; } /* Encryption Key */ - rc = generate_one_key(k_string, crypto, enckey_cli_to_srv, 'C', - enckey_cli_to_srv_len); + rc = ssh_kdf(crypto, key, key_len, 'C', enckey_cli_to_srv, + enckey_cli_to_srv_len); if (rc < 0) { goto error; } - rc = generate_one_key(k_string, crypto, enckey_srv_to_cli, 'D', - enckey_srv_to_cli_len); + rc = ssh_kdf(crypto, key, key_len, 'D', enckey_srv_to_cli, + enckey_srv_to_cli_len); if (rc < 0) { goto error; } /* Integrity Key */ - rc = generate_one_key(k_string, crypto, intkey_cli_to_srv, 'E', - intkey_cli_to_srv_len); + rc = ssh_kdf(crypto, key, key_len, 'E', intkey_cli_to_srv, + intkey_cli_to_srv_len); if (rc < 0) { goto error; } - rc = generate_one_key(k_string, crypto, intkey_srv_to_cli, 'F', - intkey_srv_to_cli_len); + rc = ssh_kdf(crypto, key, key_len, 'F', intkey_srv_to_cli, + intkey_srv_to_cli_len); if (rc < 0) { goto error; } diff --git a/src/libcrypto.c b/src/libcrypto.c index 3df6e0a9..a89adb3b 100644 --- a/src/libcrypto.c +++ b/src/libcrypto.c @@ -61,15 +61,11 @@ #include "libssh/crypto.h" -struct ssh_mac_ctx_struct { - enum ssh_mac_e mac_type; - union { - SHACTX sha1_ctx; - SHA256CTX sha256_ctx; - SHA384CTX sha384_ctx; - SHA512CTX sha512_ctx; - } ctx; -}; +#ifdef HAVE_OPENSSL_EVP_KDF_CTX_NEW_ID +#include <openssl/kdf.h> +#endif + +#include "libssh/crypto.h" static int libcrypto_initialized = 0; @@ -344,70 +340,80 @@ void md5_final(unsigned char *md, MD5CTX c) EVP_MD_CTX_destroy(c); } -ssh_mac_ctx ssh_mac_ctx_init(enum ssh_mac_e type){ - ssh_mac_ctx ctx = malloc(sizeof(struct ssh_mac_ctx_struct)); - if (ctx == NULL) { +#ifdef HAVE_OPENSSL_EVP_KDF_CTX_NEW_ID +static const EVP_MD *sshkdf_digest_to_md(enum ssh_kdf_digest digest_type) +{ + switch (digest_type) { + case SSH_KDF_SHA1: + return EVP_sha1(); + case SSH_KDF_SHA256: + return EVP_sha256(); + case SSH_KDF_SHA384: + return EVP_sha384(); + case SSH_KDF_SHA512: + return EVP_sha512(); + } return NULL; - } - - ctx->mac_type=type; - switch(type){ - case SSH_MAC_SHA1: - ctx->ctx.sha1_ctx = sha1_init(); - return ctx; - case SSH_MAC_SHA256: - ctx->ctx.sha256_ctx = sha256_init(); - return ctx; - case SSH_MAC_SHA384: - ctx->ctx.sha384_ctx = sha384_init(); - return ctx; - case SSH_MAC_SHA512: - ctx->ctx.sha512_ctx = sha512_init(); - return ctx; - default: - SAFE_FREE(ctx); - return NULL; - } } -void ssh_mac_update(ssh_mac_ctx ctx, const void *data, unsigned long len) { - switch(ctx->mac_type){ - case SSH_MAC_SHA1: - sha1_update(ctx->ctx.sha1_ctx, data, len); - break; - case SSH_MAC_SHA256: - sha256_update(ctx->ctx.sha256_ctx, data, len); - break; - case SSH_MAC_SHA384: - sha384_update(ctx->ctx.sha384_ctx, data, len); - break; - case SSH_MAC_SHA512: - sha512_update(ctx->ctx.sha512_ctx, data, len); - break; - default: - break; - } +int ssh_kdf(struct ssh_crypto_struct *crypto, + unsigned char *key, size_t key_len, + int key_type, unsigned char *output, + size_t requested_len) +{ + EVP_KDF_CTX *ctx = EVP_KDF_CTX_new_id(EVP_KDF_SSHKDF); + int rc; + + if (ctx == NULL) { + return -1; + } + + rc = EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_MD, + sshkdf_digest_to_md(crypto->digest_type)); + if (rc != 1) { + goto out; + } + rc = EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_KEY, key, key_len); + if (rc != 1) { + goto out; + } + rc = EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_SSHKDF_XCGHASH, + crypto->secret_hash, crypto->digest_len); + if (rc != 1) { + goto out; + } + rc = EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_SSHKDF_TYPE, key_type); + if (rc != 1) { + goto out; + } + rc = EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_SSHKDF_SESSION_ID, + crypto->session_id, crypto->digest_len); + if (rc != 1) { + goto out; + } + rc = EVP_KDF_derive(ctx, output, requested_len); + if (rc != 1) { + goto out; + } + +out: + EVP_KDF_CTX_free(ctx); + if (rc < 0) { + return rc; + } + return 0; } -void ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx) { - switch(ctx->mac_type){ - case SSH_MAC_SHA1: - sha1_final(md,ctx->ctx.sha1_ctx); - break; - case SSH_MAC_SHA256: - sha256_final(md,ctx->ctx.sha256_ctx); - break; - case SSH_MAC_SHA384: - sha384_final(md,ctx->ctx.sha384_ctx); - break; - case SSH_MAC_SHA512: - sha512_final(md,ctx->ctx.sha512_ctx); - break; - default: - break; - } - SAFE_FREE(ctx); +#else +int ssh_kdf(struct ssh_crypto_struct *crypto, + unsigned char *key, size_t key_len, + int key_type, unsigned char *output, + size_t requested_len) +{ + return sshkdf_derive_key(crypto, key, key_len, + key_type, output, requested_len); } +#endif HMACCTX hmac_init(const void *key, int len, enum ssh_hmac_e type) { HMACCTX ctx = NULL; diff --git a/src/libgcrypt.c b/src/libgcrypt.c index 25f2eddf..cccc3b3f 100644 --- a/src/libgcrypt.c +++ b/src/libgcrypt.c @@ -36,11 +36,6 @@ #ifdef HAVE_LIBGCRYPT #include <gcrypt.h> -struct ssh_mac_ctx_struct { - enum ssh_mac_e mac_type; - gcry_md_hd_t ctx; -}; - static int libgcrypt_initialized = 0; static int alloc_key(struct ssh_cipher_struct *cipher) { @@ -220,57 +215,13 @@ void md5_final(unsigned char *md, MD5CTX c) { gcry_md_close(c); } -ssh_mac_ctx ssh_mac_ctx_init(enum ssh_mac_e type){ - ssh_mac_ctx ctx = malloc(sizeof(struct ssh_mac_ctx_struct)); - if (ctx == NULL) { - return NULL; - } - - ctx->mac_type=type; - switch(type){ - case SSH_MAC_SHA1: - gcry_md_open(&ctx->ctx, GCRY_MD_SHA1, 0); - break; - case SSH_MAC_SHA256: - gcry_md_open(&ctx->ctx, GCRY_MD_SHA256, 0); - break; - case SSH_MAC_SHA384: - gcry_md_open(&ctx->ctx, GCRY_MD_SHA384, 0); - break; - case SSH_MAC_SHA512: - gcry_md_open(&ctx->ctx, GCRY_MD_SHA512, 0); - break; - default: - SAFE_FREE(ctx); - return NULL; - } - return ctx; -} - -void ssh_mac_update(ssh_mac_ctx ctx, const void *data, unsigned long len) { - gcry_md_write(ctx->ctx,data,len); -} - -void ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx) { - size_t len = 0; - switch(ctx->mac_type){ - case SSH_MAC_SHA1: - len=SHA_DIGEST_LEN; - break; - case SSH_MAC_SHA256: - len=SHA256_DIGEST_LEN; - break; - case SSH_MAC_SHA384: - len=SHA384_DIGEST_LEN; - break; - case SSH_MAC_SHA512: - len=SHA512_DIGEST_LEN; - break; - } - gcry_md_final(ctx->ctx); - memcpy(md, gcry_md_read(ctx->ctx, 0), len); - gcry_md_close(ctx->ctx); - SAFE_FREE(ctx); +int ssh_kdf(struct ssh_crypto_struct *crypto, + unsigned char *key, size_t key_len, + int key_type, unsigned char *output, + size_t requested_len) +{ + return sshkdf_derive_key(crypto, key, key_len, + key_type, output, requested_len); } HMACCTX hmac_init(const void *key, int len, enum ssh_hmac_e type) { diff --git a/src/libmbedcrypto.c b/src/libmbedcrypto.c index 748b3abb..02adffc0 100644 --- a/src/libmbedcrypto.c +++ b/src/libmbedcrypto.c @@ -37,10 +37,6 @@ static mbedtls_entropy_context ssh_mbedtls_entropy; static mbedtls_ctr_drbg_context ssh_mbedtls_ctr_drbg; -struct ssh_mac_ctx_struct { - enum ssh_mac_e mac_type; - mbedtls_md_context_t ctx; -}; static int libmbedcrypto_initialized = 0; void ssh_reseed(void) @@ -386,66 +382,13 @@ void md5_final(unsigned char *md, MD5CTX c) SAFE_FREE(c); } -ssh_mac_ctx ssh_mac_ctx_init(enum ssh_mac_e type) -{ - ssh_mac_ctx ctx = malloc(sizeof (struct ssh_mac_ctx_struct)); - const mbedtls_md_info_t *md_info; - int rc; - if (ctx == NULL) { - return NULL; - } - - ctx->mac_type=type; - switch(type) { - case SSH_MAC_SHA1: - md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1); - break; - case SSH_MAC_SHA256: - md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); - break; - case SSH_MAC_SHA384: - md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA384); - break; - case SSH_MAC_SHA512: - md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512); - break; - default: - goto error; - } - - if (md_info == NULL) { - goto error; - } - - mbedtls_md_init(&ctx->ctx); - - rc = mbedtls_md_setup(&ctx->ctx, md_info, 0); - if (rc != 0) { - goto error; - } - - rc = mbedtls_md_starts(&ctx->ctx); - if (rc != 0) { - goto error; - } - - return ctx; - -error: - SAFE_FREE(ctx); - return NULL; -} - -void ssh_mac_update(ssh_mac_ctx ctx, const void *data, unsigned long len) +int ssh_kdf(struct ssh_crypto_struct *crypto, + unsigned char *key, size_t key_len, + int key_type, unsigned char *output, + size_t requested_len) { - mbedtls_md_update(&ctx->ctx, data, len); -} - -void ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx) -{ - mbedtls_md_finish(&ctx->ctx, md); - mbedtls_md_free(&ctx->ctx); - SAFE_FREE(ctx); + return sshkdf_derive_key(crypto, key, key_len, + key_type, output, requested_len); } HMACCTX hmac_init(const void *key, int len, enum ssh_hmac_e type) |