summaryrefslogtreecommitdiffstats
path: root/crypto/kdf
diff options
context:
space:
mode:
authorSimo Sorce <simo@redhat.com>2018-09-19 16:23:45 -0400
committerMatt Caswell <matt@openssl.org>2019-02-27 11:02:54 +0000
commit8d76481b189b7195ef932e0fb8f0e23ab0120771 (patch)
tree859eed33157350c35682cec39e7e450aaa5b7330 /crypto/kdf
parent149c12d5e41b238ce4af6d1b6b3a767b40293bd7 (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.info2
-rw-r--r--crypto/kdf/kdf_err.c8
-rw-r--r--crypto/kdf/sshkdf.c288
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;
+}
+