diff options
author | Simo Sorce <simo@redhat.com> | 2018-09-19 16:23:45 -0400 |
---|---|---|
committer | Matt Caswell <matt@openssl.org> | 2019-02-27 11:02:54 +0000 |
commit | 8d76481b189b7195ef932e0fb8f0e23ab0120771 (patch) | |
tree | 859eed33157350c35682cec39e7e450aaa5b7330 /crypto/kdf | |
parent | 149c12d5e41b238ce4af6d1b6b3a767b40293bd7 (diff) |
Implement SSH KDF
SSH's KDF is defined in RFC 4253 in Section 7.2
Signed-off-by: Simo Sorce <simo@redhat.com>
Reviewed-by: Paul Dale <paul.dale@oracle.com>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/7290)
Diffstat (limited to 'crypto/kdf')
-rw-r--r-- | crypto/kdf/build.info | 2 | ||||
-rw-r--r-- | crypto/kdf/kdf_err.c | 8 | ||||
-rw-r--r-- | crypto/kdf/sshkdf.c | 288 |
3 files changed, 297 insertions, 1 deletions
diff --git a/crypto/kdf/build.info b/crypto/kdf/build.info index dce960e9e1..f483c779dd 100644 --- a/crypto/kdf/build.info +++ b/crypto/kdf/build.info @@ -1,3 +1,3 @@ LIBS=../../libcrypto SOURCE[../../libcrypto]=\ - tls1_prf.c kdf_err.c kdf_util.c hkdf.c scrypt.c pbkdf2.c + tls1_prf.c kdf_err.c kdf_util.c hkdf.c scrypt.c pbkdf2.c sshkdf.c diff --git a/crypto/kdf/kdf_err.c b/crypto/kdf/kdf_err.c index b62c25ff55..a70f5219a7 100644 --- a/crypto/kdf/kdf_err.c +++ b/crypto/kdf/kdf_err.c @@ -31,6 +31,11 @@ static const ERR_STRING_DATA KDF_str_functs[] = { "kdf_scrypt_ctrl_uint64"}, {ERR_PACK(ERR_LIB_KDF, KDF_F_KDF_SCRYPT_DERIVE, 0), "kdf_scrypt_derive"}, {ERR_PACK(ERR_LIB_KDF, KDF_F_KDF_SCRYPT_NEW, 0), "kdf_scrypt_new"}, + {ERR_PACK(ERR_LIB_KDF, KDF_F_KDF_SSHKDF_CTRL, 0), "kdf_sshkdf_ctrl"}, + {ERR_PACK(ERR_LIB_KDF, KDF_F_KDF_SSHKDF_CTRL_STR, 0), + "kdf_sshkdf_ctrl_str"}, + {ERR_PACK(ERR_LIB_KDF, KDF_F_KDF_SSHKDF_DERIVE, 0), "kdf_sshkdf_derive"}, + {ERR_PACK(ERR_LIB_KDF, KDF_F_KDF_SSHKDF_NEW, 0), "kdf_sshkdf_new"}, {ERR_PACK(ERR_LIB_KDF, KDF_F_KDF_TLS1_PRF_CTRL_STR, 0), "kdf_tls1_prf_ctrl_str"}, {ERR_PACK(ERR_LIB_KDF, KDF_F_KDF_TLS1_PRF_DERIVE, 0), @@ -70,6 +75,9 @@ static const ERR_STRING_DATA KDF_str_reasons[] = { {ERR_PACK(ERR_LIB_KDF, 0, KDF_R_MISSING_SALT), "missing salt"}, {ERR_PACK(ERR_LIB_KDF, 0, KDF_R_MISSING_SECRET), "missing secret"}, {ERR_PACK(ERR_LIB_KDF, 0, KDF_R_MISSING_SEED), "missing seed"}, + {ERR_PACK(ERR_LIB_KDF, 0, KDF_R_MISSING_SESSION_ID), "missing session id"}, + {ERR_PACK(ERR_LIB_KDF, 0, KDF_R_MISSING_TYPE), "missing type"}, + {ERR_PACK(ERR_LIB_KDF, 0, KDF_R_MISSING_XCGHASH), "missing xcghash"}, {ERR_PACK(ERR_LIB_KDF, 0, KDF_R_UNKNOWN_PARAMETER_TYPE), "unknown parameter type"}, {ERR_PACK(ERR_LIB_KDF, 0, KDF_R_VALUE_ERROR), "value error"}, diff --git a/crypto/kdf/sshkdf.c b/crypto/kdf/sshkdf.c new file mode 100644 index 0000000000..24f37cbed4 --- /dev/null +++ b/crypto/kdf/sshkdf.c @@ -0,0 +1,288 @@ +/* + * Copyright 2018-2018 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <openssl/evp.h> +#include <openssl/kdf.h> +#include "internal/cryptlib.h" +#include "internal/evp_int.h" +#include "kdf_local.h" + +/* See RFC 4253, Section 7.2 */ + +static void kdf_sshkdf_reset(EVP_KDF_IMPL *impl); +static int SSHKDF(const EVP_MD *evp_md, + const unsigned char *key, size_t key_len, + const unsigned char *xcghash, size_t xcghash_len, + const unsigned char *session_id, size_t session_id_len, + char type, unsigned char *okey, size_t okey_len); + +struct evp_kdf_impl_st { + const EVP_MD *md; + unsigned char *key; /* K */ + size_t key_len; + unsigned char *xcghash; /* H */ + size_t xcghash_len; + char type; /* X */ + unsigned char *session_id; + size_t session_id_len; +}; + +static EVP_KDF_IMPL *kdf_sshkdf_new(void) +{ + EVP_KDF_IMPL *impl; + + if ((impl = OPENSSL_zalloc(sizeof(*impl))) == NULL) + KDFerr(KDF_F_KDF_SSHKDF_NEW, ERR_R_MALLOC_FAILURE); + return impl; +} + +static void kdf_sshkdf_free(EVP_KDF_IMPL *impl) +{ + kdf_sshkdf_reset(impl); + OPENSSL_free(impl); +} + +static void kdf_sshkdf_reset(EVP_KDF_IMPL *impl) +{ + OPENSSL_clear_free(impl->key, impl->key_len); + OPENSSL_clear_free(impl->xcghash, impl->xcghash_len); + OPENSSL_clear_free(impl->session_id, impl->session_id_len); + memset(impl, 0, sizeof(*impl)); +} + +static int kdf_sshkdf_parse_buffer_arg(unsigned char **dst, size_t *dst_len, + va_list args) +{ + const unsigned char *p; + size_t len; + + p = va_arg(args, const unsigned char *); + len = va_arg(args, size_t); + OPENSSL_clear_free(*dst, *dst_len); + *dst = OPENSSL_memdup(p, len); + if (*dst == NULL) + return 0; + + *dst_len = len; + return 1; +} + +static int kdf_sshkdf_ctrl(EVP_KDF_IMPL *impl, int cmd, va_list args) +{ + int t; + + switch (cmd) { + case EVP_KDF_CTRL_SET_MD: + impl->md = va_arg(args, const EVP_MD *); + if (impl->md == NULL) + return 0; + + return 1; + + case EVP_KDF_CTRL_SET_KEY: + return kdf_sshkdf_parse_buffer_arg(&impl->key, + &impl->key_len, args); + + case EVP_KDF_CTRL_SET_SSHKDF_XCGHASH: + return kdf_sshkdf_parse_buffer_arg(&impl->xcghash, + &impl->xcghash_len, args); + + case EVP_KDF_CTRL_SET_SSHKDF_SESSION_ID: + return kdf_sshkdf_parse_buffer_arg(&impl->session_id, + &impl->session_id_len, args); + + case EVP_KDF_CTRL_SET_SSHKDF_TYPE: + t = va_arg(args, int); + if (t < 65 || t > 70) { + KDFerr(KDF_F_KDF_SSHKDF_CTRL, KDF_R_VALUE_ERROR); + return 0; + } + + impl->type = (char)t; + return 1; + + default: + return -2; + + } +} + +static int kdf_sshkdf_ctrl_str(EVP_KDF_IMPL *impl, const char *type, + const char *value) +{ + if (value == NULL) { + KDFerr(KDF_F_KDF_SSHKDF_CTRL_STR, KDF_R_VALUE_MISSING); + return 0; + } + + if (strcmp(type, "md") == 0) + return kdf_md2ctrl(impl, kdf_sshkdf_ctrl, EVP_KDF_CTRL_SET_MD, value); + + if (strcmp(type, "key") == 0) + return kdf_str2ctrl(impl, kdf_sshkdf_ctrl, + EVP_KDF_CTRL_SET_KEY, value); + + if (strcmp(type, "hexkey") == 0) + return kdf_hex2ctrl(impl, kdf_sshkdf_ctrl, + EVP_KDF_CTRL_SET_KEY, value); + + if (strcmp(type, "xcghash") == 0) + return kdf_str2ctrl(impl, kdf_sshkdf_ctrl, + EVP_KDF_CTRL_SET_SSHKDF_XCGHASH, value); + + if (strcmp(type, "hexxcghash") == 0) + return kdf_hex2ctrl(impl, kdf_sshkdf_ctrl, + EVP_KDF_CTRL_SET_SSHKDF_XCGHASH, value); + + if (strcmp(type, "session_id") == 0) + return kdf_str2ctrl(impl, kdf_sshkdf_ctrl, + EVP_KDF_CTRL_SET_SSHKDF_SESSION_ID, value); + + if (strcmp(type, "hexsession_id") == 0) + return kdf_hex2ctrl(impl, kdf_sshkdf_ctrl, + EVP_KDF_CTRL_SET_SSHKDF_SESSION_ID, value); + + if (strcmp(type, "type") == 0) { + if (strlen(value) != 1) { + KDFerr(KDF_F_KDF_SSHKDF_CTRL_STR, KDF_R_VALUE_ERROR); + return 0; + } + + return call_ctrl(kdf_sshkdf_ctrl, impl, EVP_KDF_CTRL_SET_SSHKDF_TYPE, + (int)value[0]); + } + + KDFerr(KDF_F_KDF_SSHKDF_CTRL_STR, KDF_R_UNKNOWN_PARAMETER_TYPE); + return -2; +} + +static size_t kdf_sshkdf_size(EVP_KDF_IMPL *impl) +{ + return SIZE_MAX; +} + +static int kdf_sshkdf_derive(EVP_KDF_IMPL *impl, unsigned char *key, + size_t keylen) +{ + if (impl->md == NULL) { + KDFerr(KDF_F_KDF_SSHKDF_DERIVE, KDF_R_MISSING_MESSAGE_DIGEST); + return 0; + } + if (impl->key == NULL) { + KDFerr(KDF_F_KDF_SSHKDF_DERIVE, KDF_R_MISSING_KEY); + return 0; + } + if (impl->xcghash == NULL) { + KDFerr(KDF_F_KDF_SSHKDF_DERIVE, KDF_R_MISSING_XCGHASH); + return 0; + } + if (impl->session_id == NULL) { + KDFerr(KDF_F_KDF_SSHKDF_DERIVE, KDF_R_MISSING_SESSION_ID); + return 0; + } + if (impl->type == 0) { + KDFerr(KDF_F_KDF_SSHKDF_DERIVE, KDF_R_MISSING_TYPE); + return 0; + } + return SSHKDF(impl->md, impl->key, impl->key_len, + impl->xcghash, impl->xcghash_len, + impl->session_id, impl->session_id_len, + impl->type, key, keylen); +} + +const EVP_KDF_METHOD sshkdf_kdf_meth = { + EVP_KDF_SSHKDF, + kdf_sshkdf_new, + kdf_sshkdf_free, + kdf_sshkdf_reset, + kdf_sshkdf_ctrl, + kdf_sshkdf_ctrl_str, + kdf_sshkdf_size, + kdf_sshkdf_derive, +}; + +static int SSHKDF(const EVP_MD *evp_md, + const unsigned char *key, size_t key_len, + const unsigned char *xcghash, size_t xcghash_len, + const unsigned char *session_id, size_t session_id_len, + char type, unsigned char *okey, size_t okey_len) +{ + EVP_MD_CTX *md = NULL; + unsigned char digest[EVP_MAX_MD_SIZE]; + unsigned int dsize = 0; + size_t cursize = 0; + int ret = 0; + + md = EVP_MD_CTX_new(); + if (md == NULL) + return 0; + + if (!EVP_DigestInit_ex(md, evp_md, NULL)) + goto out; + + if (!EVP_DigestUpdate(md, key, key_len)) + goto out; + + if (!EVP_DigestUpdate(md, xcghash, xcghash_len)) + goto out; + + if (!EVP_DigestUpdate(md, &type, 1)) + goto out; + + if (!EVP_DigestUpdate(md, session_id, session_id_len)) + goto out; + + if (!EVP_DigestFinal_ex(md, digest, &dsize)) + goto out; + + if (okey_len < dsize) { + memcpy(okey, digest, okey_len); + ret = 1; + goto out; + } + + memcpy(okey, digest, dsize); + + for (cursize = dsize; cursize < okey_len; cursize += dsize) { + + if (!EVP_DigestInit_ex(md, evp_md, NULL)) + goto out; + + if (!EVP_DigestUpdate(md, key, key_len)) + goto out; + + if (!EVP_DigestUpdate(md, xcghash, xcghash_len)) + goto out; + + if (!EVP_DigestUpdate(md, okey, cursize)) + goto out; + + if (!EVP_DigestFinal_ex(md, digest, &dsize)) + goto out; + + if (okey_len < cursize + dsize) { + memcpy(okey + cursize, digest, okey_len - cursize); + ret = 1; + goto out; + } + + memcpy(okey + cursize, digest, dsize); + } + + ret = 1; + +out: + EVP_MD_CTX_free(md); + OPENSSL_cleanse(digest, EVP_MAX_MD_SIZE); + return ret; +} + |