Single step kdf implementation

Reviewed-by: Paul Dale <paul.dale@oracle.com>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/8230)
This commit is contained in:
Shane Lontis 2019-01-04 18:41:21 +10:00 committed by Matt Caswell
parent 6098b69e58
commit 9537fe5757
17 changed files with 2047 additions and 7 deletions

View File

@ -31,6 +31,12 @@
'enable-buildtest-c++'.
[Richard Levitte]
*) Add Single Step KDF (EVP_KDF_SS) to EVP_KDF.
[Shane Lontis]
*) Add KMAC to EVP_MAC.
[Shane Lontis]
*) Added property based algorithm implementation selection framework to
the core.
[Paul Dale]

View File

@ -898,6 +898,11 @@ KDF_F_PKEY_TLS1_PRF_CTRL_STR:100:pkey_tls1_prf_ctrl_str
KDF_F_PKEY_TLS1_PRF_DERIVE:101:pkey_tls1_prf_derive
KDF_F_PKEY_TLS1_PRF_INIT:110:pkey_tls1_prf_init
KDF_F_SCRYPT_SET_MEMBUF:129:scrypt_set_membuf
KDF_F_SSKDF_CTRL_STR:134:sskdf_ctrl_str
KDF_F_SSKDF_DERIVE:135:sskdf_derive
KDF_F_SSKDF_MAC2CTRL:136:sskdf_mac2ctrl
KDF_F_SSKDF_NEW:137:sskdf_new
KDF_F_SSKDF_SIZE:138:sskdf_size
KDF_F_TLS1_PRF_ALG:111:tls1_prf_alg
OBJ_F_OBJ_ADD_OBJECT:105:OBJ_add_object
OBJ_F_OBJ_ADD_SIGID:107:OBJ_add_sigid
@ -2128,7 +2133,6 @@ CONF_R_UNKNOWN_MODULE_NAME:113:unknown module name
CONF_R_VARIABLE_EXPANSION_TOO_LONG:116:variable expansion too long
CONF_R_VARIABLE_HAS_NO_VALUE:104:variable has no value
CRMF_R_BAD_PBM_ITERATIONCOUNT:100:bad pbm iterationcount
CRMF_R_MALFORMED_IV:101:malformed iv
CRMF_R_CRMFERROR:102:crmferror
CRMF_R_ERROR:103:error
CRMF_R_ERROR_DECODING_CERTIFICATE:104:error decoding certificate
@ -2136,6 +2140,7 @@ CRMF_R_ERROR_DECRYPTING_CERTIFICATE:105:error decrypting certificate
CRMF_R_ERROR_DECRYPTING_SYMMETRIC_KEY:106:error decrypting symmetric key
CRMF_R_FAILURE_OBTAINING_RANDOM:107:failure obtaining random
CRMF_R_ITERATIONCOUNT_BELOW_100:108:iterationcount below 100
CRMF_R_MALFORMED_IV:101:malformed iv
CRMF_R_NULL_ARGUMENT:109:null argument
CRMF_R_SETTING_MAC_ALGOR_FAILURE:110:setting mac algor failure
CRMF_R_SETTING_OWF_ALGOR_FAILURE:111:setting owf algor failure
@ -2402,6 +2407,7 @@ EVP_R_UNSUPPORTED_SALT_TYPE:126:unsupported salt type
EVP_R_WRAP_MODE_NOT_ALLOWED:170:wrap mode not allowed
EVP_R_WRONG_FINAL_BLOCK_LENGTH:109:wrong final block length
KDF_R_INVALID_DIGEST:100:invalid digest
KDF_R_INVALID_MAC_TYPE:116:invalid mac type
KDF_R_MISSING_ITERATION_COUNT:109:missing iteration count
KDF_R_MISSING_KEY:104:missing key
KDF_R_MISSING_MESSAGE_DIGEST:105:missing message digest
@ -2414,6 +2420,7 @@ KDF_R_MISSING_SESSION_ID:113:missing session id
KDF_R_MISSING_TYPE:114:missing type
KDF_R_MISSING_XCGHASH:115:missing xcghash
KDF_R_UNKNOWN_PARAMETER_TYPE:103:unknown parameter type
KDF_R_UNSUPPORTED_MAC_TYPE:117:unsupported mac type
KDF_R_VALUE_ERROR:108:value error
KDF_R_VALUE_MISSING:102:value missing
KDF_R_WRONG_OUTPUT_BUFFER_SIZE:112:wrong output buffer size

View File

@ -31,6 +31,7 @@ static const EVP_KDF_METHOD *standard_methods[] = {
&tls1_prf_kdf_meth,
&hkdf_kdf_meth,
&sshkdf_kdf_meth,
&ss_kdf_meth
};
DECLARE_OBJ_BSEARCH_CMP_FN(const EVP_KDF_METHOD *, const EVP_KDF_METHOD *,

View File

@ -169,6 +169,7 @@ extern const EVP_KDF_METHOD scrypt_kdf_meth;
extern const EVP_KDF_METHOD tls1_prf_kdf_meth;
extern const EVP_KDF_METHOD hkdf_kdf_meth;
extern const EVP_KDF_METHOD sshkdf_kdf_meth;
extern const EVP_KDF_METHOD ss_kdf_meth;
struct evp_md_st {
int type;

View File

@ -1,3 +1,4 @@
LIBS=../../libcrypto
SOURCE[../../libcrypto]=\
tls1_prf.c kdf_err.c kdf_util.c hkdf.c scrypt.c pbkdf2.c sshkdf.c
tls1_prf.c kdf_err.c kdf_util.c hkdf.c scrypt.c pbkdf2.c sshkdf.c \
sskdf.c

View File

@ -59,12 +59,18 @@ static const ERR_STRING_DATA KDF_str_functs[] = {
"pkey_tls1_prf_derive"},
{ERR_PACK(ERR_LIB_KDF, KDF_F_PKEY_TLS1_PRF_INIT, 0), "pkey_tls1_prf_init"},
{ERR_PACK(ERR_LIB_KDF, KDF_F_SCRYPT_SET_MEMBUF, 0), "scrypt_set_membuf"},
{ERR_PACK(ERR_LIB_KDF, KDF_F_SSKDF_CTRL_STR, 0), "sskdf_ctrl_str"},
{ERR_PACK(ERR_LIB_KDF, KDF_F_SSKDF_DERIVE, 0), "sskdf_derive"},
{ERR_PACK(ERR_LIB_KDF, KDF_F_SSKDF_MAC2CTRL, 0), "sskdf_mac2ctrl"},
{ERR_PACK(ERR_LIB_KDF, KDF_F_SSKDF_NEW, 0), "sskdf_new"},
{ERR_PACK(ERR_LIB_KDF, KDF_F_SSKDF_SIZE, 0), "sskdf_size"},
{ERR_PACK(ERR_LIB_KDF, KDF_F_TLS1_PRF_ALG, 0), "tls1_prf_alg"},
{0, NULL}
};
static const ERR_STRING_DATA KDF_str_reasons[] = {
{ERR_PACK(ERR_LIB_KDF, 0, KDF_R_INVALID_DIGEST), "invalid digest"},
{ERR_PACK(ERR_LIB_KDF, 0, KDF_R_INVALID_MAC_TYPE), "invalid mac type"},
{ERR_PACK(ERR_LIB_KDF, 0, KDF_R_MISSING_ITERATION_COUNT),
"missing iteration count"},
{ERR_PACK(ERR_LIB_KDF, 0, KDF_R_MISSING_KEY), "missing key"},
@ -80,6 +86,8 @@ static const ERR_STRING_DATA KDF_str_reasons[] = {
{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_UNSUPPORTED_MAC_TYPE),
"unsupported mac type"},
{ERR_PACK(ERR_LIB_KDF, 0, KDF_R_VALUE_ERROR), "value error"},
{ERR_PACK(ERR_LIB_KDF, 0, KDF_R_VALUE_MISSING), "value missing"},
{ERR_PACK(ERR_LIB_KDF, 0, KDF_R_WRONG_OUTPUT_BUFFER_SIZE),

481
crypto/kdf/sskdf.c Normal file
View File

@ -0,0 +1,481 @@
/*
* Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Apache License 2.0 (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
*/
/*
* Refer to https://csrc.nist.gov/publications/detail/sp/800-56c/rev-1/final
* Section 4.1.
*
* The Single Step KDF algorithm is given by:
*
* Result(0) = empty bit string (i.e., the null string).
* For i = 1 to reps, do the following:
* Increment counter by 1.
* Result(i) = Result(i 1) || H(counter || Z || FixedInfo).
* DKM = LeftmostBits(Result(reps), L))
*
* NOTES:
* Z is a shared secret required to produce the derived key material.
* counter is a 4 byte buffer.
* FixedInfo is a bit string containing context specific data.
* DKM is the output derived key material.
* L is the required size of the DKM.
* reps = [L / H_outputBits]
* H(x) is the auxiliary function that can be either a hash, HMAC or KMAC.
* H_outputBits is the length of the output of the auxiliary function H(x).
*
* Currently there is not a comprehensive list of test vectors for this
* algorithm, especially for H(x) = HMAC and H(x) = KMAC.
* Test vectors for H(x) = Hash are indirectly used by CAVS KAS tests.
*/
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <openssl/hmac.h>
#include <openssl/evp.h>
#include <openssl/kdf.h>
#include "internal/cryptlib.h"
#include "internal/evp_int.h"
#include "kdf_local.h"
struct evp_kdf_impl_st {
const EVP_MAC *mac; /* H(x) = HMAC_hash OR H(x) = KMAC */
const EVP_MD *md; /* H(x) = hash OR when H(x) = HMAC_hash */
unsigned char *secret;
size_t secret_len;
unsigned char *info;
size_t info_len;
unsigned char *salt;
size_t salt_len;
size_t out_len; /* optional KMAC parameter */
};
#define SSKDF_MAX_INLEN (1<<30)
#define SSKDF_KMAC128_DEFAULT_SALT_SIZE (168 - 4)
#define SSKDF_KMAC256_DEFAULT_SALT_SIZE (136 - 4)
/* KMAC uses a Customisation string of 'KDF' */
static const unsigned char kmac_custom_str[] = { 0x4B, 0x44, 0x46 };
/*
* Refer to https://csrc.nist.gov/publications/detail/sp/800-56c/rev-1/final
* Section 4. One-Step Key Derivation using H(x) = hash(x)
*/
static int SSKDF_hash_kdm(const EVP_MD *kdf_md,
const unsigned char *z, size_t z_len,
const unsigned char *info, size_t info_len,
unsigned char *derived_key, size_t derived_key_len)
{
int ret = 0, hlen;
size_t counter, out_len, len = derived_key_len;
unsigned char c[4];
unsigned char mac[EVP_MAX_MD_SIZE];
unsigned char *out = derived_key;
EVP_MD_CTX *ctx = NULL, *ctx_init = NULL;
if (z_len > SSKDF_MAX_INLEN || info_len > SSKDF_MAX_INLEN
|| derived_key_len > SSKDF_MAX_INLEN
|| derived_key_len == 0)
return 0;
hlen = EVP_MD_size(kdf_md);
if (hlen <= 0)
return 0;
out_len = (size_t)hlen;
ctx = EVP_MD_CTX_create();
ctx_init = EVP_MD_CTX_create();
if (ctx == NULL || ctx_init == NULL)
goto end;
if (!EVP_DigestInit(ctx_init, kdf_md))
goto end;
for (counter = 1;; counter++) {
c[0] = (unsigned char)((counter >> 24) & 0xff);
c[1] = (unsigned char)((counter >> 16) & 0xff);
c[2] = (unsigned char)((counter >> 8) & 0xff);
c[3] = (unsigned char)(counter & 0xff);
if (!(EVP_MD_CTX_copy_ex(ctx, ctx_init)
&& EVP_DigestUpdate(ctx, c, sizeof(c))
&& EVP_DigestUpdate(ctx, z, z_len)
&& EVP_DigestUpdate(ctx, info, info_len)))
goto end;
if (len >= out_len) {
if (!EVP_DigestFinal_ex(ctx, out, NULL))
goto end;
out += out_len;
len -= out_len;
if (len == 0)
break;
} else {
if (!EVP_DigestFinal_ex(ctx, mac, NULL))
goto end;
memcpy(out, mac, len);
break;
}
}
ret = 1;
end:
EVP_MD_CTX_destroy(ctx);
EVP_MD_CTX_destroy(ctx_init);
OPENSSL_cleanse(mac, sizeof(mac));
return ret;
}
static int kmac_init(EVP_MAC_CTX *ctx, const unsigned char *custom,
size_t custom_len, size_t kmac_out_len,
size_t derived_key_len, unsigned char **out)
{
/* Only KMAC has custom data - so return if not KMAC */
if (custom == NULL)
return 1;
if (!EVP_MAC_ctrl(ctx, EVP_MAC_CTRL_SET_CUSTOM, custom, custom_len))
return 0;
/* By default only do one iteration if kmac_out_len is not specified */
if (kmac_out_len == 0)
kmac_out_len = derived_key_len;
/* otherwise check the size is valid */
else if (!(kmac_out_len == derived_key_len
|| kmac_out_len == 20
|| kmac_out_len == 28
|| kmac_out_len == 32
|| kmac_out_len == 48
|| kmac_out_len == 64))
return 0;
if (!EVP_MAC_ctrl(ctx, EVP_MAC_CTRL_SET_SIZE, kmac_out_len))
return 0;
/*
* For kmac the output buffer can be larger than EVP_MAX_MD_SIZE: so
* alloc a buffer for this case.
*/
if (kmac_out_len > EVP_MAX_MD_SIZE) {
*out = OPENSSL_zalloc(kmac_out_len);
if (*out == NULL)
return 0;
}
return 1;
}
/*
* Refer to https://csrc.nist.gov/publications/detail/sp/800-56c/rev-1/final
* Section 4. One-Step Key Derivation using MAC: i.e either
* H(x) = HMAC-hash(salt, x) OR
* H(x) = KMAC#(salt, x, outbits, CustomString='KDF')
*/
static int SSKDF_mac_kdm(const EVP_MAC *kdf_mac, const EVP_MD *hmac_md,
const unsigned char *kmac_custom,
size_t kmac_custom_len, size_t kmac_out_len,
const unsigned char *salt, size_t salt_len,
const unsigned char *z, size_t z_len,
const unsigned char *info, size_t info_len,
unsigned char *derived_key, size_t derived_key_len)
{
int ret = 0;
size_t counter, out_len, len;
unsigned char c[4];
unsigned char mac_buf[EVP_MAX_MD_SIZE];
unsigned char *out = derived_key;
EVP_MAC_CTX *ctx = NULL, *ctx_init = NULL;
unsigned char *mac = mac_buf, *kmac_buffer = NULL;
if (z_len > SSKDF_MAX_INLEN || info_len > SSKDF_MAX_INLEN
|| derived_key_len > SSKDF_MAX_INLEN
|| derived_key_len == 0)
return 0;
ctx = EVP_MAC_CTX_new(kdf_mac);
ctx_init = EVP_MAC_CTX_new(kdf_mac);
if (ctx == NULL || ctx_init == NULL)
goto end;
if (hmac_md != NULL &&
!EVP_MAC_ctrl(ctx_init, EVP_MAC_CTRL_SET_MD, hmac_md))
goto end;
if (!EVP_MAC_ctrl(ctx_init, EVP_MAC_CTRL_SET_KEY, salt, salt_len))
goto end;
if (!kmac_init(ctx_init, kmac_custom, kmac_custom_len, kmac_out_len,
derived_key_len, &kmac_buffer))
goto end;
if (kmac_buffer != NULL)
mac = kmac_buffer;
if (!EVP_MAC_init(ctx_init))
goto end;
out_len = EVP_MAC_size(ctx_init); /* output size */
if (out_len <= 0)
goto end;
len = derived_key_len;
for (counter = 1;; counter++) {
c[0] = (unsigned char)((counter >> 24) & 0xff);
c[1] = (unsigned char)((counter >> 16) & 0xff);
c[2] = (unsigned char)((counter >> 8) & 0xff);
c[3] = (unsigned char)(counter & 0xff);
if (!(EVP_MAC_CTX_copy(ctx, ctx_init)
&& EVP_MAC_update(ctx, c, sizeof(c))
&& EVP_MAC_update(ctx, z, z_len)
&& EVP_MAC_update(ctx, info, info_len)))
goto end;
if (len >= out_len) {
if (!EVP_MAC_final(ctx, out, NULL))
goto end;
out += out_len;
len -= out_len;
if (len == 0)
break;
} else {
if (!EVP_MAC_final(ctx, mac, NULL))
goto end;
memcpy(out, mac, len);
break;
}
}
ret = 1;
end:
OPENSSL_free(kmac_buffer);
EVP_MAC_CTX_free(ctx);
EVP_MAC_CTX_free(ctx_init);
OPENSSL_cleanse(mac, sizeof(mac));
return ret;
}
static EVP_KDF_IMPL *sskdf_new(void)
{
EVP_KDF_IMPL *impl;
if ((impl = OPENSSL_zalloc(sizeof(*impl))) == NULL)
KDFerr(KDF_F_SSKDF_NEW, ERR_R_MALLOC_FAILURE);
return impl;
}
static void sskdf_reset(EVP_KDF_IMPL *impl)
{
OPENSSL_clear_free(impl->secret, impl->secret_len);
OPENSSL_clear_free(impl->info, impl->info_len);
OPENSSL_clear_free(impl->salt, impl->salt_len);
memset(impl, 0, sizeof(*impl));
}
static void sskdf_free(EVP_KDF_IMPL *impl)
{
sskdf_reset(impl);
OPENSSL_free(impl);
}
static int sskdf_set_buffer(va_list args, unsigned char **out, size_t *out_len)
{
const unsigned char *p;
size_t len;
p = va_arg(args, const unsigned char *);
len = va_arg(args, size_t);
if (len == 0 || p == NULL)
return 1;
OPENSSL_free(*out);
*out = OPENSSL_memdup(p, len);
if (*out == NULL)
return 0;
*out_len = len;
return 1;
}
static int sskdf_ctrl(EVP_KDF_IMPL *impl, int cmd, va_list args)
{
const EVP_MD *md;
const EVP_MAC *mac;
switch (cmd) {
case EVP_KDF_CTRL_SET_KEY:
return sskdf_set_buffer(args, &impl->secret, &impl->secret_len);
case EVP_KDF_CTRL_SET_SSKDF_INFO:
return sskdf_set_buffer(args, &impl->info, &impl->info_len);
case EVP_KDF_CTRL_SET_MD:
md = va_arg(args, const EVP_MD *);
if (md == NULL)
return 0;
impl->md = md;
return 1;
case EVP_KDF_CTRL_SET_MAC:
mac = va_arg(args, const EVP_MAC *);
if (mac == NULL)
return 0;
impl->mac = mac;
return 1;
case EVP_KDF_CTRL_SET_SALT:
return sskdf_set_buffer(args, &impl->salt, &impl->salt_len);
case EVP_KDF_CTRL_SET_MAC_SIZE:
impl->out_len = va_arg(args, size_t);
return 1;
default:
return -2;
}
}
/* Pass a mac to a ctrl */
static int sskdf_mac2ctrl(EVP_KDF_IMPL *impl,
int (*ctrl)(EVP_KDF_IMPL *impl, int cmd, va_list args),
int cmd, const char *mac_name)
{
const EVP_MAC *mac;
if (mac_name == NULL || (mac = EVP_get_macbyname(mac_name)) == NULL) {
KDFerr(KDF_F_SSKDF_MAC2CTRL, KDF_R_INVALID_MAC_TYPE);
return 0;
}
return call_ctrl(ctrl, impl, cmd, mac);
}
static int sskdf_ctrl_str(EVP_KDF_IMPL *impl, const char *type,
const char *value)
{
if (strcmp(type, "secret") == 0 || strcmp(type, "key") == 0)
return kdf_str2ctrl(impl, sskdf_ctrl, EVP_KDF_CTRL_SET_KEY,
value);
if (strcmp(type, "hexsecret") == 0 || strcmp(type, "hexkey") == 0)
return kdf_hex2ctrl(impl, sskdf_ctrl, EVP_KDF_CTRL_SET_KEY,
value);
if (strcmp(type, "info") == 0)
return kdf_str2ctrl(impl, sskdf_ctrl, EVP_KDF_CTRL_SET_SSKDF_INFO,
value);
if (strcmp(type, "hexinfo") == 0)
return kdf_hex2ctrl(impl, sskdf_ctrl, EVP_KDF_CTRL_SET_SSKDF_INFO,
value);
if (strcmp(type, "digest") == 0)
return kdf_md2ctrl(impl, sskdf_ctrl, EVP_KDF_CTRL_SET_MD, value);
if (strcmp(type, "mac") == 0)
return sskdf_mac2ctrl(impl, sskdf_ctrl, EVP_KDF_CTRL_SET_MAC, value);
if (strcmp(type, "salt") == 0)
return kdf_str2ctrl(impl, sskdf_ctrl, EVP_KDF_CTRL_SET_SALT, value);
if (strcmp(type, "hexsalt") == 0)
return kdf_hex2ctrl(impl, sskdf_ctrl, EVP_KDF_CTRL_SET_SALT, value);
if (strcmp(type, "maclen") == 0) {
int val = atoi(value);
if (val < 0) {
KDFerr(KDF_F_SSKDF_CTRL_STR, KDF_R_VALUE_ERROR);
return 0;
}
return call_ctrl(sskdf_ctrl, impl, EVP_KDF_CTRL_SET_MAC_SIZE,
(size_t)val);
}
return -2;
}
static size_t sskdf_size(EVP_KDF_IMPL *impl)
{
int len;
if (impl->md == NULL) {
KDFerr(KDF_F_SSKDF_SIZE, KDF_R_MISSING_MESSAGE_DIGEST);
return 0;
}
len = EVP_MD_size(impl->md);
return (len <= 0) ? 0 : (size_t)len;
}
static int sskdf_derive(EVP_KDF_IMPL *impl, unsigned char *key, size_t keylen)
{
if (impl->secret == NULL) {
KDFerr(KDF_F_SSKDF_DERIVE, KDF_R_MISSING_SECRET);
return 0;
}
if (impl->mac != NULL) {
/* H(x) = KMAC or H(x) = HMAC */
int ret;
const unsigned char *custom = NULL;
size_t custom_len = 0;
int nid;
int default_salt_len;
nid = EVP_MAC_nid(impl->mac);
if (nid == EVP_MAC_HMAC) {
/* H(x) = HMAC(x, salt, hash) */
if (impl->md == NULL) {
KDFerr(KDF_F_SSKDF_DERIVE, KDF_R_MISSING_MESSAGE_DIGEST);
return 0;
}
default_salt_len = EVP_MD_block_size(impl->md);
if (default_salt_len <= 0)
return 0;
} else if (nid == EVP_MAC_KMAC128 || nid == EVP_MAC_KMAC256) {
/* H(x) = KMACzzz(x, salt, custom) */
custom = kmac_custom_str;
custom_len = sizeof(kmac_custom_str);
if (nid == EVP_MAC_KMAC128)
default_salt_len = SSKDF_KMAC128_DEFAULT_SALT_SIZE;
else
default_salt_len = SSKDF_KMAC256_DEFAULT_SALT_SIZE;
} else {
KDFerr(KDF_F_SSKDF_DERIVE, KDF_R_UNSUPPORTED_MAC_TYPE);
return 0;
}
/* If no salt is set then use a default_salt of zeros */
if (impl->salt == NULL || impl->salt_len <= 0) {
impl->salt = OPENSSL_zalloc(default_salt_len);
if (impl->salt == NULL) {
KDFerr(KDF_F_SSKDF_DERIVE, ERR_R_MALLOC_FAILURE);
return 0;
}
impl->salt_len = default_salt_len;
}
ret = SSKDF_mac_kdm(impl->mac, impl->md,
custom, custom_len, impl->out_len,
impl->salt, impl->salt_len,
impl->secret, impl->secret_len,
impl->info, impl->info_len, key, keylen);
return ret;
} else {
/* H(x) = hash */
if (impl->md == NULL) {
KDFerr(KDF_F_SSKDF_DERIVE, KDF_R_MISSING_MESSAGE_DIGEST);
return 0;
}
return SSKDF_hash_kdm(impl->md, impl->secret, impl->secret_len,
impl->info, impl->info_len, key, keylen);
}
}
const EVP_KDF_METHOD ss_kdf_meth = {
EVP_KDF_SS,
sskdf_new,
sskdf_free,
sskdf_reset,
sskdf_ctrl,
sskdf_ctrl_str,
sskdf_size,
sskdf_derive
};

View File

@ -1080,7 +1080,7 @@ static const unsigned char so[7775] = {
0x2A,0x81,0x1C,0xCF,0x55,0x01,0x83,0x75, /* [ 7766] OBJ_SM2_with_SM3 */
};
#define NUM_NID 1205
#define NUM_NID 1206
static const ASN1_OBJECT nid_objs[NUM_NID] = {
{"UNDEF", "undefined", NID_undef},
{"rsadsi", "RSA Data Security, Inc.", NID_rsadsi, 6, &so[0]},
@ -2287,9 +2287,10 @@ static const ASN1_OBJECT nid_objs[NUM_NID] = {
{"BLAKE2SMAC", "blake2smac", NID_blake2smac},
{"SSHKDF", "sshkdf", NID_sshkdf},
{"SM2-SM3", "SM2-with-SM3", NID_SM2_with_SM3, 8, &so[7766]},
{"SSKDF", "sskdf", NID_sskdf},
};
#define NUM_SN 1196
#define NUM_SN 1197
static const unsigned int sn_objs[NUM_SN] = {
364, /* "AD_DVCS" */
419, /* "AES-128-CBC" */
@ -2577,6 +2578,7 @@ static const unsigned int sn_objs[NUM_SN] = {
100, /* "SN" */
1006, /* "SNILS" */
1203, /* "SSHKDF" */
1205, /* "SSKDF" */
16, /* "ST" */
143, /* "SXNetID" */
1062, /* "SipHash" */
@ -3489,7 +3491,7 @@ static const unsigned int sn_objs[NUM_SN] = {
1093, /* "x509ExtAdmission" */
};
#define NUM_LN 1196
#define NUM_LN 1197
static const unsigned int ln_objs[NUM_LN] = {
363, /* "AD Time Stamping" */
405, /* "ANSI X9.62" */
@ -4641,6 +4643,7 @@ static const unsigned int ln_objs[NUM_LN] = {
1133, /* "sm4-ecb" */
1135, /* "sm4-ofb" */
1203, /* "sshkdf" */
1205, /* "sskdf" */
16, /* "stateOrProvinceName" */
660, /* "streetAddress" */
498, /* "subtreeMaximumQuality" */

View File

@ -1202,3 +1202,4 @@ blake2bmac 1201
blake2smac 1202
sshkdf 1203
SM2_with_SM3 1204
sskdf 1205

View File

@ -1614,6 +1614,9 @@ secg-scheme 14 3 : dhSinglePass-cofactorDH-sha512kdf-scheme
# NID for SSHKDF
: SSHKDF : sshkdf
# NID for SSKDF
: SSKDF : sskdf
# RFC 4556
1 3 6 1 5 2 3 : id-pkinit
id-pkinit 4 : pkInitClientAuth : PKINIT Client Auth

View File

@ -135,6 +135,17 @@ EVP_KDF_ctrl_str() type string: "iter"
The value string is expected to be a decimal number.
=item B<EVP_KDF_CTRL_SET_MAC>
This control expects one argument: C<EVP_MAC *mac>
Some KDF implementations use a MAC as an underlying computation
algorithm, this control sets what the MAC algorithm should be.
EVP_KDF_ctrl_str() type string: "mac"
The value string is expected to be the name of a MAC.
=item B<EVP_KDF_CTRL_SET_MD>
This control expects one argument: C<EVP_MD *md>
@ -168,6 +179,19 @@ decoded before being passed on as the control value.
=back
=item B<EVP_KDF_CTRL_SET_MAC_SIZE>
This control expects one argument: C<size_t size>
Used by implementations that use a MAC with a variable output size (KMAC). For
those KDF implementations that support it, this control sets the MAC output size.
The default value, if any, is implementation dependent.
EVP_KDF_ctrl_str() type string: "outlen"
The value string is expected to be a decimal number.
=item B<EVP_KDF_CTRL_SET_MAXMEM_BYTES>
This control expects one argument: C<uint64_t maxmem_bytes>
@ -204,10 +228,18 @@ supported by the KDF algorithm.
=head1 SEE ALSO
L<EVP_KDF_SCRYPT(7)>
L<EVP_KDF_TLS1_PRF(7)>
L<EVP_KDF_PBKDF2(7)>
L<EVP_KDF_HKDF(7)>
L<EVP_KDF_SS(7)>
=head1 HISTORY
This functionality was added to OpenSSL 3.0.0.
=head1 COPYRIGHT
Copyright 2018 The OpenSSL Project Authors. All Rights Reserved.
Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
Licensed under the Apache License 2.0 (the "License"). You may not use
this file except in compliance with the License. You can obtain a copy

226
doc/man7/EVP_KDF_SS.pod Normal file
View File

@ -0,0 +1,226 @@
=pod
=head1 NAME
EVP_KDF_SS - The Single Step / One Step EVP_KDF implementation
=head1 DESCRIPTION
The EVP_KDF_SS algorithm implements the Single Step key derivation function (SSKDF).
SSKDF derives a key using input such as a shared secret key (that was generated
during the execution of a key establishment scheme) and fixedinfo.
SSKDF is also informally referred to as 'Concat KDF'.
=head2 Auxilary function
The implementation uses a selectable auxiliary function H, which can be one of:
=over 4
=item B<H(x) = hash(x, digest=md)>
=item B<H(x) = HMAC_hash(x, key=salt, digest=md)>
=item B<H(x) = KMACxxx(x, key=salt, custom="KDF", outlen=mac_size)>
=back
Both the HMAC and KMAC implementations set the key using the 'salt' value.
The hash and HMAC also require the digest to be set.
=head2 Numeric identity
B<EVP_KDF_SS> is the numeric identity for this implementation; it
can be used with the EVP_KDF_CTX_new_id() function.
=head2 Supported controls
The supported controls are:
=over 4
=item B<EVP_KDF_CTRL_SET_MD>
=item B<EVP_KDF_CTRL_SET_MAC>
=item B<EVP_MAC_CTRL_SET_MAC_SIZE>
=item B<EVP_KDF_CTRL_SET_SALT>
These controls work as described in L<EVP_KDF_CTX(3)/CONTROLS>.
=item B<EVP_KDF_CTRL_SET_KEY>
This control expects two arguments: C<unsigned char *secret>, C<size_t secretlen>
The shared secret used for key derivation. This control sets the secret.
EVP_KDF_ctrl_str() takes two type strings for this control:
=over 4
=item "secret"
The value string is used as is.
=item "hexsecret"
The value string is expected to be a hexadecimal number, which will be
decoded before being passed on as the control value.
=back
=item B<EVP_KDF_CTRL_SET_SSKDF_INFO>
This control expects two arguments: C<unsigned char *info>, C<size_t infolen>
An optional value for fixedinfo, also known as otherinfo. This control sets the fixedinfo.
EVP_KDF_ctrl_str() takes two type strings for this control:
=over 4
=item "info"
The value string is used as is.
=item "hexinfo"
The value string is expected to be a hexadecimal number, which will be
decoded before being passed on as the control value.
=back
=back
=head1 NOTES
A context for SSKDF can be obtained by calling:
EVP_KDF_CTX *kctx = EVP_KDF_CTX_new_id(EVP_KDF_SS);
The output length of an SSKDF is specified via the C<keylen>
parameter to the L<EVP_KDF_derive(3)> function.
=head1 EXAMPLE
This example derives 10 bytes using H(x) = SHA-256, with the secret key "secret"
and fixedinfo value "label":
EVP_KDF_CTX *kctx;
unsigned char out[10];
kctx = EVP_KDF_CTX_new_id(EVP_KDF_SS);
if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_MD, EVP_sha256()) <= 0) {
error("EVP_KDF_CTRL_SET_MD");
}
if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_KEY, "secret", (size_t)6) <= 0) {
error("EVP_KDF_CTRL_SET_KEY");
}
if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_SSKDF_INFO, "label", (size_t)5) <= 0) {
error("EVP_KDF_CTRL_SET_SSKDF_INFO");
}
if (EVP_KDF_derive(kctx, out, sizeof(out)) <= 0) {
error("EVP_KDF_derive");
}
EVP_KDF_CTX_free(kctx);
=head1 EXAMPLE
This example derives 10 bytes using H(x) = HMAC(SHA-256), with the secret key "secret",
fixedinfo value "label" and salt "salt":
EVP_KDF_CTX *kctx;
unsigned char out[10];
kctx = EVP_KDF_CTX_new_id(EVP_KDF_SS);
if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_MAC, EVP_get_macbyname("HMAC")) <= 0) {
error("EVP_KDF_CTRL_SET_MAC");
}
if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_MD, EVP_sha256()) <= 0) {
error("EVP_KDF_CTRL_SET_MD");
}
if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_KEY, "secret", (size_t)6) <= 0) {
error("EVP_KDF_CTRL_SET_KEY");
}
if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_SSKDF_INFO, "label", (size_t)5) <= 0) {
error("EVP_KDF_CTRL_SET_SSKDF_INFO");
}
if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_SALT, "salt", (size_t)4) <= 0) {
error("EVP_KDF_CTRL_SET_SALT");
}
if (EVP_KDF_derive(kctx, out, sizeof(out)) <= 0) {
error("EVP_KDF_derive");
}
EVP_KDF_CTX_free(kctx);
=head1 EXAMPLE
This example derives 10 bytes using H(x) = KMAC128(x,salt,outlen), with the secret key "secret"
fixedinfo value "label", salt of "salt" and KMAC outlen of 20:
EVP_KDF_CTX *kctx;
unsigned char out[10];
kctx = EVP_KDF_CTX_new_id(EVP_KDF_SS);
if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_MAC, EVP_get_macbyname("KMAC128")) <= 0) {
error("EVP_KDF_CTRL_SET_MAC");
}
if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_MD, EVP_sha256()) <= 0) {
error("EVP_KDF_CTRL_SET_MD");
}
if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_KEY, "secret", (size_t)6) <= 0) {
error("EVP_KDF_CTRL_SET_KEY");
}
if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_SSKDF_INFO, "label", (size_t)5) <= 0) {
error("EVP_KDF_CTRL_SET_SSKDF_INFO");
}
/* If not specified the salt will be set to a default value */
if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_SALT, "salt", (size_t)4) <= 0) {
error("EVP_KDF_CTRL_SET_SALT");
}
/* If not specified the default size will be the size of the derived key */
if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_MAC_SIZE, (size_t)20) <= 0) {
error("EVP_KDF_CTRL_SET_MAC_SIZE");
}
if (EVP_KDF_derive(kctx, out, sizeof(out)) <= 0) {
error("EVP_KDF_derive");
}
EVP_KDF_CTX_free(kctx);
=head1 CONFORMING TO
NIST SP800-56Cr1.
=head1 SEE ALSO
L<EVP_KDF_CTX>,
L<EVP_KDF_CTX_new_id(3)>,
L<EVP_KDF_CTX_free(3)>,
L<EVP_KDF_ctrl(3)>,
L<EVP_KDF_size(3)>,
L<EVP_KDF_derive(3)>,
L<EVP_KDF_CTX(3)/CONTROLS>
=head1 HISTORY
This functionality was added to OpenSSL 3.0.0.
=head1 COPYRIGHT
Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. Copyright
(c) 2019, Oracle and/or its affiliates. All rights reserved.
Licensed under the Apache License 2.0 (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
L<https://www.openssl.org/source/license.html>.
=cut

View File

@ -23,6 +23,7 @@ extern "C" {
# define EVP_KDF_TLS1_PRF NID_tls1_prf
# define EVP_KDF_HKDF NID_hkdf
# define EVP_KDF_SSHKDF NID_sshkdf
# define EVP_KDF_SS NID_sskdf
EVP_KDF_CTX *EVP_KDF_CTX_new_id(int id);
void EVP_KDF_CTX_free(EVP_KDF_CTX *ctx);
@ -53,6 +54,9 @@ int EVP_KDF_derive(EVP_KDF_CTX *ctx, unsigned char *key, size_t keylen);
# define EVP_KDF_CTRL_SET_SSHKDF_XCGHASH 0x10 /* unsigned char *, size_t */
# define EVP_KDF_CTRL_SET_SSHKDF_SESSION_ID 0x11 /* unsigned char *, size_t */
# define EVP_KDF_CTRL_SET_SSHKDF_TYPE 0x12 /* int */
# define EVP_KDF_CTRL_SET_MAC 0x13 /* EVP_MAC * */
# define EVP_KDF_CTRL_SET_MAC_SIZE 0x14 /* size_t */
# define EVP_KDF_CTRL_SET_SSKDF_INFO 0x15 /* unsigned char *, size_t */
# define EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND 0
# define EVP_KDF_HKDF_MODE_EXTRACT_ONLY 1

View File

@ -56,12 +56,18 @@ int ERR_load_KDF_strings(void);
# define KDF_F_PKEY_TLS1_PRF_DERIVE 101
# define KDF_F_PKEY_TLS1_PRF_INIT 110
# define KDF_F_SCRYPT_SET_MEMBUF 129
# define KDF_F_SSKDF_CTRL_STR 134
# define KDF_F_SSKDF_DERIVE 135
# define KDF_F_SSKDF_MAC2CTRL 136
# define KDF_F_SSKDF_NEW 137
# define KDF_F_SSKDF_SIZE 138
# define KDF_F_TLS1_PRF_ALG 111
/*
* KDF reason codes.
*/
# define KDF_R_INVALID_DIGEST 100
# define KDF_R_INVALID_MAC_TYPE 116
# define KDF_R_MISSING_ITERATION_COUNT 109
# define KDF_R_MISSING_KEY 104
# define KDF_R_MISSING_MESSAGE_DIGEST 105
@ -74,6 +80,7 @@ int ERR_load_KDF_strings(void);
# define KDF_R_MISSING_TYPE 114
# define KDF_R_MISSING_XCGHASH 115
# define KDF_R_UNKNOWN_PARAMETER_TYPE 103
# define KDF_R_UNSUPPORTED_MAC_TYPE 117
# define KDF_R_VALUE_ERROR 108
# define KDF_R_VALUE_MISSING 102
# define KDF_R_WRONG_OUTPUT_BUFFER_SIZE 112

View File

@ -5000,6 +5000,10 @@
#define LN_sshkdf "sshkdf"
#define NID_sshkdf 1203
#define SN_sskdf "SSKDF"
#define LN_sskdf "sskdf"
#define NID_sskdf 1205
#define SN_id_pkinit "id-pkinit"
#define NID_id_pkinit 1031
#define OBJ_id_pkinit 1L,3L,6L,1L,5L,2L,3L

View File

@ -225,6 +225,170 @@ err:
}
#endif
static int test_kdf_ss_hash(void)
{
EVP_KDF_CTX *kctx;
const unsigned char z[] = {
0x6d,0xbd,0xc2,0x3f,0x04,0x54,0x88,0xe4,0x06,0x27,0x57,0xb0,0x6b,0x9e,
0xba,0xe1,0x83,0xfc,0x5a,0x59,0x46,0xd8,0x0d,0xb9,0x3f,0xec,0x6f,0x62,
0xec,0x07,0xe3,0x72,0x7f,0x01,0x26,0xae,0xd1,0x2c,0xe4,0xb2,0x62,0xf4,
0x7d,0x48,0xd5,0x42,0x87,0xf8,0x1d,0x47,0x4c,0x7c,0x3b,0x18,0x50,0xe9
};
const unsigned char other[] = {
0xa1,0xb2,0xc3,0xd4,0xe5,0x43,0x41,0x56,0x53,0x69,0x64,0x3c,0x83,0x2e,
0x98,0x49,0xdc,0xdb,0xa7,0x1e,0x9a,0x31,0x39,0xe6,0x06,0xe0,0x95,0xde,
0x3c,0x26,0x4a,0x66,0xe9,0x8a,0x16,0x58,0x54,0xcd,0x07,0x98,0x9b,0x1e,
0xe0,0xec,0x3f,0x8d,0xbe
};
const unsigned char expected[] = {
0xa4,0x62,0xde,0x16,0xa8,0x9d,0xe8,0x46,0x6e,0xf5,0x46,0x0b,0x47,0xb8
};
unsigned char out[14];
kctx = EVP_KDF_CTX_new_id(EVP_KDF_SS);
if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_MD, EVP_sha224()) <= 0) {
TEST_error("EVP_KDF_CTRL_SET_MD");
return 0;
}
if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_KEY, z, sizeof(z)) <= 0) {
TEST_error("EVP_KDF_CTRL_SET_KEY");
return 0;
}
if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_SSKDF_INFO, other,
sizeof(other)) <= 0) {
TEST_error("EVP_KDF_CTRL_SET_OTHER");
return 0;
}
if (EVP_KDF_derive(kctx, out, sizeof(out)) <= 0) {
TEST_error("EVP_KDF_derive");
return 0;
}
if (!TEST_mem_eq(out, sizeof(out), expected, sizeof(expected)))
return 0;
EVP_KDF_CTX_free(kctx);
return 1;
}
static int test_kdf_ss_hmac(void)
{
EVP_KDF_CTX *kctx;
const EVP_MAC *mac;
const unsigned char z[] = {
0xb7,0x4a,0x14,0x9a,0x16,0x15,0x46,0xf8,0xc2,0x0b,0x06,0xac,0x4e,0xd4
};
const unsigned char other[] = {
0x34,0x8a,0x37,0xa2,0x7e,0xf1,0x28,0x2f,0x5f,0x02,0x0d,0xcc
};
const unsigned char salt[] = {
0x36,0x38,0x27,0x1c,0xcd,0x68,0xa2,0x5d,0xc2,0x4e,0xcd,0xdd,0x39,0xef,
0x3f,0x89
};
const unsigned char expected[] = {
0x44,0xf6,0x76,0xe8,0x5c,0x1b,0x1a,0x8b,0xbc,0x3d,0x31,0x92,0x18,0x63,
0x1c,0xa3
};
unsigned char out[16];
kctx = EVP_KDF_CTX_new_id(EVP_KDF_SS);
mac = EVP_get_macbyname("HMAC");
if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_MAC, mac) <= 0) {
TEST_error("EVP_KDF_CTRL_SET_MAC");
return 0;
}
if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_MD, EVP_sha256()) <= 0) {
TEST_error("EVP_KDF_CTRL_SET_MD");
return 0;
}
if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_KEY, z, sizeof(z)) <= 0) {
TEST_error("EVP_KDF_CTRL_SET_KEY");
return 0;
}
if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_SSKDF_INFO, other,
sizeof(other)) <= 0) {
TEST_error("EVP_KDF_CTRL_SET_OTHER");
return 0;
}
if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_SALT, salt, sizeof(salt)) <= 0) {
TEST_error("EVP_KDF_CTRL_SET_SALT");
return 0;
}
if (EVP_KDF_derive(kctx, out, sizeof(out)) <= 0) {
TEST_error("EVP_KDF_derive");
return 0;
}
if (!TEST_mem_eq(out, sizeof(out), expected, sizeof(expected)))
return 0;
EVP_KDF_CTX_free(kctx);
return 1;
}
static int test_kdf_ss_kmac(void)
{
EVP_KDF_CTX *kctx;
unsigned char out[64];
const EVP_MAC *mac;
const unsigned char z[] = {
0xb7,0x4a,0x14,0x9a,0x16,0x15,0x46,0xf8,0xc2,0x0b,0x06,0xac,0x4e,0xd4
};
const unsigned char other[] = {
0x34,0x8a,0x37,0xa2,0x7e,0xf1,0x28,0x2f,0x5f,0x02,0x0d,0xcc
};
const unsigned char salt[] = {
0x36,0x38,0x27,0x1c,0xcd,0x68,0xa2,0x5d,0xc2,0x4e,0xcd,0xdd,0x39,0xef,
0x3f,0x89
};
const unsigned char expected[] = {
0xe9,0xc1,0x84,0x53,0xa0,0x62,0xb5,0x3b,0xdb,0xfc,0xbb,0x5a,0x34,0xbd,
0xb8,0xe5,0xe7,0x07,0xee,0xbb,0x5d,0xd1,0x34,0x42,0x43,0xd8,0xcf,0xc2,
0xc2,0xe6,0x33,0x2f,0x91,0xbd,0xa5,0x86,0xf3,0x7d,0xe4,0x8a,0x65,0xd4,
0xc5,0x14,0xfd,0xef,0xaa,0x1e,0x67,0x54,0xf3,0x73,0xd2,0x38,0xe1,0x95,
0xae,0x15,0x7e,0x1d,0xe8,0x14,0x98,0x03
};
kctx = EVP_KDF_CTX_new_id(EVP_KDF_SS);
mac = EVP_get_macbyname("KMAC128");
if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_MAC, mac) <= 0) {
TEST_error("EVP_KDF_CTRL_SET_MAC");
return 0;
}
if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_KEY, z, sizeof(z)) <= 0) {
TEST_error("EVP_KDF_CTRL_SET_KEY");
return 0;
}
if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_SSKDF_INFO, other,
sizeof(other)) <= 0) {
TEST_error("EVP_KDF_CTRL_SET_OTHER");
return 0;
}
if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_SALT, salt, sizeof(salt)) <= 0) {
TEST_error("EVP_KDF_CTRL_SET_SALT");
return 0;
}
if (EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_MAC_SIZE, (size_t)20) <= 0) {
TEST_error("EVP_KDF_CTRL_SET_MACSIZE");
return 0;
}
if (EVP_KDF_derive(kctx, out, sizeof(out)) <= 0) {
TEST_error("EVP_KDF_derive");
return 0;
}
if (!TEST_mem_eq(out, sizeof(out), expected, sizeof(expected)))
return 0;
EVP_KDF_CTX_free(kctx);
return 1;
}
int setup_tests(void)
{
ADD_TEST(test_kdf_tls1_prf);
@ -233,5 +397,8 @@ int setup_tests(void)
#ifndef OPENSSL_NO_SCRYPT
ADD_TEST(test_kdf_scrypt);
#endif
ADD_TEST(test_kdf_ss_hash);
ADD_TEST(test_kdf_ss_hmac);
ADD_TEST(test_kdf_ss_kmac);
return 1;
}

File diff suppressed because it is too large Load Diff