diff options
-rw-r--r-- | crypto/dh/dh_kdf.c | 160 | ||||
-rw-r--r-- | crypto/err/openssl.txt | 10 | ||||
-rw-r--r-- | crypto/evp/c_allkdf.c | 3 | ||||
-rw-r--r-- | crypto/include/internal/evp_int.h | 1 | ||||
-rw-r--r-- | crypto/kdf/build.info | 2 | ||||
-rw-r--r-- | crypto/kdf/kdf_err.c | 11 | ||||
-rw-r--r-- | crypto/kdf/x942kdf.c | 407 | ||||
-rw-r--r-- | crypto/objects/obj_dat.h | 9 | ||||
-rw-r--r-- | crypto/objects/obj_mac.num | 1 | ||||
-rw-r--r-- | crypto/objects/objects.txt | 2 | ||||
-rw-r--r-- | doc/man3/EVP_KDF_CTX.pod | 1 | ||||
-rw-r--r-- | doc/man7/EVP_KDF_X942.pod | 150 | ||||
-rw-r--r-- | include/openssl/kdf.h | 11 | ||||
-rw-r--r-- | include/openssl/kdferr.h | 10 | ||||
-rw-r--r-- | include/openssl/obj_mac.h | 4 | ||||
-rw-r--r-- | test/evp_kdf_test.c | 34 | ||||
-rw-r--r-- | test/evp_test.c | 18 | ||||
-rw-r--r-- | test/recipes/30-test_evp_data/evpkdf.txt | 15 |
18 files changed, 710 insertions, 139 deletions
diff --git a/crypto/dh/dh_kdf.c b/crypto/dh/dh_kdf.c index 2a01bfce46..03b1e4edd5 100644 --- a/crypto/dh/dh_kdf.c +++ b/crypto/dh/dh_kdf.c @@ -1,5 +1,5 @@ /* - * Copyright 2013-2016 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2013-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 @@ -10,141 +10,43 @@ #include "e_os.h" #ifndef OPENSSL_NO_CMS -#include <string.h> -#include <openssl/dh.h> -#include <openssl/evp.h> -#include <openssl/asn1.h> -#include <openssl/cms.h> - - -/* Key derivation from X9.42/RFC2631 */ -/* Uses CMS functions, hence the #ifdef wrapper. */ - -#define DH_KDF_MAX (1L << 30) - -/* Skip past an ASN1 structure: for OBJECT skip content octets too */ - -static int skip_asn1(unsigned char **pp, long *plen, int exptag) -{ - const unsigned char *q = *pp; - int i, tag, xclass; - long tmplen; - i = ASN1_get_object(&q, &tmplen, &tag, &xclass, *plen); - if (i & 0x80) - return 0; - if (tag != exptag || xclass != V_ASN1_UNIVERSAL) - return 0; - if (tag == V_ASN1_OBJECT) - q += tmplen; - *plen -= q - *pp; - *pp = (unsigned char *)q; - return 1; -} - -/* - * Encode the DH shared info structure, return an offset to the counter value - * so we can update the structure without reencoding it. - */ - -static int dh_sharedinfo_encode(unsigned char **pder, unsigned char **pctr, - ASN1_OBJECT *key_oid, size_t outlen, - const unsigned char *ukm, size_t ukmlen) -{ - unsigned char *p; - int derlen; - long tlen; - /* "magic" value to check offset is sane */ - static unsigned char ctr[4] = { 0xF3, 0x17, 0x22, 0x53 }; - X509_ALGOR atmp; - ASN1_OCTET_STRING ctr_oct, ukm_oct, *pukm_oct; - ASN1_TYPE ctr_atype; - if (ukmlen > DH_KDF_MAX || outlen > DH_KDF_MAX) - return 0; - ctr_oct.data = ctr; - ctr_oct.length = 4; - ctr_oct.flags = 0; - ctr_oct.type = V_ASN1_OCTET_STRING; - ctr_atype.type = V_ASN1_OCTET_STRING; - ctr_atype.value.octet_string = &ctr_oct; - atmp.algorithm = key_oid; - atmp.parameter = &ctr_atype; - if (ukm) { - ukm_oct.type = V_ASN1_OCTET_STRING; - ukm_oct.flags = 0; - ukm_oct.data = (unsigned char *)ukm; - ukm_oct.length = ukmlen; - pukm_oct = &ukm_oct; - } else - pukm_oct = NULL; - derlen = CMS_SharedInfo_encode(pder, &atmp, pukm_oct, outlen); - if (derlen <= 0) - return 0; - p = *pder; - tlen = derlen; - if (!skip_asn1(&p, &tlen, V_ASN1_SEQUENCE)) - return 0; - if (!skip_asn1(&p, &tlen, V_ASN1_SEQUENCE)) - return 0; - if (!skip_asn1(&p, &tlen, V_ASN1_OBJECT)) - return 0; - if (!skip_asn1(&p, &tlen, V_ASN1_OCTET_STRING)) - return 0; - if (CRYPTO_memcmp(p, ctr, 4)) - return 0; - *pctr = p; - return derlen; -} +# include <string.h> +# include <openssl/dh.h> +# include <openssl/evp.h> +# include <openssl/asn1.h> +# include <openssl/kdf.h> int DH_KDF_X9_42(unsigned char *out, size_t outlen, const unsigned char *Z, size_t Zlen, ASN1_OBJECT *key_oid, const unsigned char *ukm, size_t ukmlen, const EVP_MD *md) { - EVP_MD_CTX *mctx = NULL; - int rv = 0; - unsigned int i; - size_t mdlen; - unsigned char *der = NULL, *ctr; - int derlen; - if (Zlen > DH_KDF_MAX) + int ret = 0, nid; + EVP_KDF_CTX *kctx = NULL; + const EVP_KDF *kdf = NULL; + const char *oid_sn; + + nid = OBJ_obj2nid(key_oid); + if (nid == NID_undef) return 0; - mctx = EVP_MD_CTX_new(); - if (mctx == NULL) + oid_sn = OBJ_nid2sn(nid); + if (oid_sn == NULL) return 0; - mdlen = EVP_MD_size(md); - derlen = dh_sharedinfo_encode(&der, &ctr, key_oid, outlen, ukm, ukmlen); - if (derlen == 0) + + kdf = EVP_get_kdfbyname(SN_x942kdf); + if (kdf == NULL) goto err; - for (i = 1;; i++) { - unsigned char mtmp[EVP_MAX_MD_SIZE]; - if (!EVP_DigestInit_ex(mctx, md, NULL) - || !EVP_DigestUpdate(mctx, Z, Zlen)) - goto err; - ctr[3] = i & 0xFF; - ctr[2] = (i >> 8) & 0xFF; - ctr[1] = (i >> 16) & 0xFF; - ctr[0] = (i >> 24) & 0xFF; - if (!EVP_DigestUpdate(mctx, der, derlen)) - goto err; - if (outlen >= mdlen) { - if (!EVP_DigestFinal(mctx, out, NULL)) - goto err; - outlen -= mdlen; - if (outlen == 0) - break; - out += mdlen; - } else { - if (!EVP_DigestFinal(mctx, mtmp, NULL)) - goto err; - memcpy(out, mtmp, outlen); - OPENSSL_cleanse(mtmp, mdlen); - break; - } - } - rv = 1; - err: - OPENSSL_free(der); - EVP_MD_CTX_free(mctx); - return rv; + kctx = EVP_KDF_CTX_new(kdf); + ret = + kctx != NULL + && EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_MD, md) > 0 + && EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_KEY, Z, Zlen) > 0 + && (ukm == NULL + || EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_UKM, ukm, ukmlen) > 0) + && EVP_KDF_ctrl(kctx, EVP_KDF_CTRL_SET_CEK_ALG, oid_sn) > 0 + && EVP_KDF_derive(kctx, out, outlen) > 0; +err: + EVP_KDF_CTX_free(kctx); + return ret; } -#endif +#endif /* OPENSSL_NO_CMS */ diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt index f1567c2fa6..ddff08c936 100644 --- a/crypto/err/openssl.txt +++ b/crypto/err/openssl.txt @@ -935,6 +935,11 @@ 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 +KDF_F_X942KDF_CTRL:142:x942kdf_ctrl +KDF_F_X942KDF_DERIVE:143:x942kdf_derive +KDF_F_X942KDF_HASH_KDM:144:x942kdf_hash_kdm +KDF_F_X942KDF_NEW:145:x942kdf_new +KDF_F_X942KDF_SIZE:146:x942kdf_size KDF_F_X963KDF_DERIVE:139:x963kdf_derive OBJ_F_OBJ_ADD_OBJECT:105:OBJ_add_object OBJ_F_OBJ_ADD_SIGID:107:OBJ_add_sigid @@ -2469,11 +2474,15 @@ EVP_R_WRAP_MODE_NOT_ALLOWED:170:wrap mode not allowed EVP_R_WRONG_FINAL_BLOCK_LENGTH:109:wrong final block length EVP_R_XTS_DATA_UNIT_IS_TOO_LARGE:191:xts data unit is too large EVP_R_XTS_DUPLICATED_KEYS:192:xts duplicated keys +KDF_R_BAD_ENCODING:122:bad encoding +KDF_R_BAD_LENGTH:123:bad length +KDF_R_INAVLID_UKM_LEN:124:inavlid ukm len KDF_R_INVALID_DIGEST:100:invalid digest KDF_R_INVALID_ITERATION_COUNT:119:invalid iteration count KDF_R_INVALID_KEY_LEN:120:invalid key len KDF_R_INVALID_MAC_TYPE:116:invalid mac type KDF_R_INVALID_SALT_LEN:121:invalid salt len +KDF_R_MISSING_CEK_ALG:125:missing cek alg 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 @@ -2487,6 +2496,7 @@ KDF_R_MISSING_TYPE:114:missing type KDF_R_MISSING_XCGHASH:115:missing xcghash KDF_R_NOT_SUPPORTED:118:not supported KDF_R_UNKNOWN_PARAMETER_TYPE:103:unknown parameter type +KDF_R_UNSUPPORTED_CEK_ALG:126:unsupported cek alg KDF_R_UNSUPPORTED_MAC_TYPE:117:unsupported mac type KDF_R_VALUE_ERROR:108:value error KDF_R_VALUE_MISSING:102:value missing diff --git a/crypto/evp/c_allkdf.c b/crypto/evp/c_allkdf.c index 2233fd9cac..860c11c5ce 100644 --- a/crypto/evp/c_allkdf.c +++ b/crypto/evp/c_allkdf.c @@ -21,4 +21,7 @@ void openssl_add_all_kdfs_int(void) EVP_add_kdf(&sshkdf_kdf_meth); EVP_add_kdf(&ss_kdf_meth); EVP_add_kdf(&x963_kdf_meth); +#ifndef OPENSSL_NO_CMS + EVP_add_kdf(&x942_kdf_meth); +#endif } diff --git a/crypto/include/internal/evp_int.h b/crypto/include/internal/evp_int.h index 183fc42932..732fad8d67 100644 --- a/crypto/include/internal/evp_int.h +++ b/crypto/include/internal/evp_int.h @@ -173,6 +173,7 @@ extern const EVP_KDF hkdf_kdf_meth; extern const EVP_KDF sshkdf_kdf_meth; extern const EVP_KDF ss_kdf_meth; extern const EVP_KDF x963_kdf_meth; +extern const EVP_KDF x942_kdf_meth; struct evp_md_st { /* nid */ diff --git a/crypto/kdf/build.info b/crypto/kdf/build.info index 52e40a4f2f..4fdaccddec 100644 --- a/crypto/kdf/build.info +++ b/crypto/kdf/build.info @@ -1,4 +1,4 @@ LIBS=../../libcrypto SOURCE[../../libcrypto]=\ tls1_prf.c kdf_err.c kdf_util.c hkdf.c scrypt.c pbkdf2.c sshkdf.c \ - sskdf.c + sskdf.c x942kdf.c diff --git a/crypto/kdf/kdf_err.c b/crypto/kdf/kdf_err.c index 98dc271efc..1b6e7845eb 100644 --- a/crypto/kdf/kdf_err.c +++ b/crypto/kdf/kdf_err.c @@ -67,17 +67,26 @@ static const ERR_STRING_DATA KDF_str_functs[] = { {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"}, + {ERR_PACK(ERR_LIB_KDF, KDF_F_X942KDF_CTRL, 0), "x942kdf_ctrl"}, + {ERR_PACK(ERR_LIB_KDF, KDF_F_X942KDF_DERIVE, 0), "x942kdf_derive"}, + {ERR_PACK(ERR_LIB_KDF, KDF_F_X942KDF_HASH_KDM, 0), "x942kdf_hash_kdm"}, + {ERR_PACK(ERR_LIB_KDF, KDF_F_X942KDF_NEW, 0), "x942kdf_new"}, + {ERR_PACK(ERR_LIB_KDF, KDF_F_X942KDF_SIZE, 0), "x942kdf_size"}, {ERR_PACK(ERR_LIB_KDF, KDF_F_X963KDF_DERIVE, 0), "x963kdf_derive"}, {0, NULL} }; static const ERR_STRING_DATA KDF_str_reasons[] = { + {ERR_PACK(ERR_LIB_KDF, 0, KDF_R_BAD_ENCODING), "bad encoding"}, + {ERR_PACK(ERR_LIB_KDF, 0, KDF_R_BAD_LENGTH), "bad length"}, + {ERR_PACK(ERR_LIB_KDF, 0, KDF_R_INAVLID_UKM_LEN), "inavlid ukm len"}, {ERR_PACK(ERR_LIB_KDF, 0, KDF_R_INVALID_DIGEST), "invalid digest"}, {ERR_PACK(ERR_LIB_KDF, 0, KDF_R_INVALID_ITERATION_COUNT), "invalid iteration count"}, {ERR_PACK(ERR_LIB_KDF, 0, KDF_R_INVALID_KEY_LEN), "invalid key len"}, {ERR_PACK(ERR_LIB_KDF, 0, KDF_R_INVALID_MAC_TYPE), "invalid mac type"}, {ERR_PACK(ERR_LIB_KDF, 0, KDF_R_INVALID_SALT_LEN), "invalid salt len"}, + {ERR_PACK(ERR_LIB_KDF, 0, KDF_R_MISSING_CEK_ALG), "missing cek alg"}, {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"}, @@ -94,6 +103,8 @@ static const ERR_STRING_DATA KDF_str_reasons[] = { {ERR_PACK(ERR_LIB_KDF, 0, KDF_R_NOT_SUPPORTED), "not supported"}, {ERR_PACK(ERR_LIB_KDF, 0, KDF_R_UNKNOWN_PARAMETER_TYPE), "unknown parameter type"}, + {ERR_PACK(ERR_LIB_KDF, 0, KDF_R_UNSUPPORTED_CEK_ALG), + "unsupported cek alg"}, {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"}, diff --git a/crypto/kdf/x942kdf.c b/crypto/kdf/x942kdf.c new file mode 100644 index 0000000000..ce9ad61035 --- /dev/null +++ b/crypto/kdf/x942kdf.c @@ -0,0 +1,407 @@ +/* + * 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 + */ + +#include "e_os.h" + +#ifndef OPENSSL_NO_CMS + +# include <stdlib.h> +# include <stdarg.h> +# include <string.h> +# include <openssl/hmac.h> +# include <openssl/cms.h> +# include <openssl/evp.h> +# include <openssl/kdf.h> +# include <openssl/x509.h> +# include <openssl/obj_mac.h> +# include "internal/cryptlib.h" +# include "internal/evp_int.h" +# include "kdf_local.h" + +# define X942KDF_MAX_INLEN (1 << 30) + +struct evp_kdf_impl_st { + const EVP_MD *md; + unsigned char *secret; + size_t secret_len; + int cek_nid; + unsigned char *ukm; + size_t ukm_len; + size_t dkm_len; +}; + +/* A table of allowed wrapping algorithms and the associated output lengths */ +static const struct { + int nid; + size_t keklen; /* size in bytes */ +} kek_algs[] = { + { NID_id_smime_alg_CMS3DESwrap, 24 }, + { NID_id_smime_alg_CMSRC2wrap, 16 }, + { NID_id_aes128_wrap, 16 }, + { NID_id_aes192_wrap, 24 }, + { NID_id_aes256_wrap, 32 }, + { NID_id_camellia128_wrap, 16 }, + { NID_id_camellia192_wrap, 24 }, + { NID_id_camellia256_wrap, 32 } +}; + +/* Skip past an ASN1 structure: for OBJECT skip content octets too */ +static int skip_asn1(unsigned char **pp, long *plen, int exptag) +{ + int i, tag, xclass; + long tmplen; + const unsigned char *q = *pp; + + i = ASN1_get_object(&q, &tmplen, &tag, &xclass, *plen); + if ((i & 0x80) != 0 || tag != exptag || xclass != V_ASN1_UNIVERSAL) + return 0; + if (tag == V_ASN1_OBJECT) + q += tmplen; + *pp = (unsigned char *)q; + *plen -= q - *pp; + return 1; +} + +/* + * Encode the other info structure. + * + * RFC2631 Section 2.1.2 Contains the following definition for otherinfo + * + * OtherInfo ::= SEQUENCE { + * keyInfo KeySpecificInfo, + * partyAInfo [0] OCTET STRING OPTIONAL, + * suppPubInfo [2] OCTET STRING + * } + * + * KeySpecificInfo ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * counter OCTET STRING SIZE (4..4) + * } + * + * |nid| is the algorithm object identifier. + * |keylen| is the length (in bytes) of the generated KEK. It is stored into + * suppPubInfo (in bits). + * |ukm| is the optional user keying material that is stored into partyAInfo. It + * can be NULL. + * |ukmlen| is the user keying material length (in bytes). + * |der| is the returned encoded data. It must be freed by the caller. + * |der_len| is the returned size of the encoded data. + * |out_ctr| returns a pointer to the counter data which is embedded inside the + * encoded data. This allows the counter bytes to be updated without re-encoding. + * + * Returns: 1 if successfully encoded, or 0 otherwise. + * Assumptions: |der|, |der_len| & |out_ctr| are not NULL. + */ +static int x942_encode_otherinfo(int nid, size_t keylen, + const unsigned char *ukm, size_t ukmlen, + unsigned char **der, size_t *der_len, + unsigned char **out_ctr) +{ + unsigned char *p, *encoded = NULL; + int ret = 0, encoded_len; + long tlen; + /* "magic" value to check offset is sane */ + static unsigned char ctr[4] = { 0x00, 0x00, 0x00, 0x01 }; + X509_ALGOR *ksi = NULL; + ASN1_OBJECT *alg_oid = NULL; + ASN1_OCTET_STRING *ctr_oct = NULL, *ukm_oct = NULL; + + /* set the KeySpecificInfo - which contains an algorithm oid and counter */ + ksi = X509_ALGOR_new(); + alg_oid = OBJ_dup(OBJ_nid2obj(nid)); + ctr_oct = ASN1_OCTET_STRING_new(); + if (ksi == NULL + || alg_oid == NULL + || ctr_oct == NULL + || !ASN1_OCTET_STRING_set(ctr_oct, ctr, sizeof(ctr)) + || !X509_ALGOR_set0(ksi, alg_oid, V_ASN1_OCTET_STRING, ctr_oct)) + goto err; + /* NULL these as they now belong to ksi */ + alg_oid = NULL; + ctr_oct = NULL; + + /* Set the optional partyAInfo */ + if (ukm != NULL) { + ukm_oct = ASN1_OCTET_STRING_new(); + if (ukm_oct == NULL) + goto err; + ASN1_OCTET_STRING_set(ukm_oct, (unsigned char *)ukm, ukmlen); + } + /* Generate the OtherInfo DER data */ + encoded_len = CMS_SharedInfo_encode(&encoded, ksi, ukm_oct, keylen); + if (encoded_len <= 0) + goto err; + + /* Parse the encoded data to find the offset of the counter data */ + p = encoded; + tlen = (long)encoded_len; + if (skip_asn1(&p, &tlen, V_ASN1_SEQUENCE) + && skip_asn1(&p, &tlen, V_ASN1_SEQUENCE) + && skip_asn1(&p, &tlen, V_ASN1_OBJECT) + && skip_asn1(&p, &tlen, V_ASN1_OCTET_STRING) + && CRYPTO_memcmp(p, ctr, 4) == 0) { + *out_ctr = p; + *der = encoded; + *der_len = (size_t)encoded_len; + ret = 1; + } +err: + if (ret != 1) + OPENSSL_free(encoded); + ASN1_OCTET_STRING_free(ctr_oct); + ASN1_OCTET_STRING_free(ukm_oct); + ASN1_OBJECT_free(alg_oid); + X509_ALGOR_free(ksi); + return ret; +} + +static int x942kdf_hash_kdm(const EVP_MD *kdf_md, + const unsigned char *z, size_t z_len, + const unsigned char *other, size_t other_len, + unsigned char *ctr, + unsigned char *derived_key, size_t derived_key_len) +{ + int ret = 0, hlen; + size_t counter, out_len, len = derived_key_len; + unsigned char mac[EVP_MAX_MD_SIZE]; + unsigned char *out = derived_key; + EVP_MD_CTX *ctx = NULL, *ctx_init = NULL; + + if (z_len > X942KDF_MAX_INLEN || other_len > X942KDF_MAX_INLEN + || derived_key_len > X942KDF_MAX_INLEN + || derived_key_len == 0) { + KDFerr(KDF_F_X942KDF_HASH_KDM, KDF_R_BAD_LENGTH); + 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++) { + /* updating the ctr modifies 4 bytes in the 'other' buffer */ + ctr[0] = (unsigned char)((counter >> 24) & 0xff); + ctr[1] = (unsigned char)((counter >> 16) & 0xff); + ctr[2] = (unsigned char)((counter >> 8) & 0xff); + ctr[3] = (unsigned char)(counter & 0xff); + + if (!EVP_MD_CTX_copy_ex(ctx, ctx_init) + || !EVP_DigestUpdate(ctx, z, z_len) + || !EVP_DigestUpdate(ctx, other, other_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_free(ctx); + EVP_MD_CTX_free(ctx_init); + OPENSSL_cleanse(mac, sizeof(mac)); + return ret; +} + +static EVP_KDF_IMPL *x942kdf_new(void) +{ + EVP_KDF_IMPL *impl; + + if ((impl = OPENSSL_zalloc(sizeof(*impl))) == NULL) + KDFerr(KDF_F_X942KDF_NEW, ERR_R_MALLOC_FAILURE); + return impl; +} + +static void x942kdf_reset(EVP_KDF_IMPL *impl) +{ + OPENSSL_clear_free(impl->secret, impl->secret_len); + OPENSSL_clear_free(impl->ukm, impl->ukm_len); + memset(impl, 0, sizeof(*impl)); +} + +static void x942kdf_free(EVP_KDF_IMPL *impl) +{ + x942kdf_reset(impl); + OPENSSL_free(impl); +} + +static int x942kdf_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 x942kdf_ctrl(EVP_KDF_IMPL *impl, int cmd, va_list args) +{ + const EVP_MD *md; + char *alg_str = NULL; + size_t i; + + switch (cmd) { + 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_KEY: + return x942kdf_set_buffer(args, &impl->secret, &impl->secret_len); + + case EVP_KDF_CTRL_SET_UKM: + return x942kdf_set_buffer(args, &impl->ukm, &impl->ukm_len); + + case EVP_KDF_CTRL_SET_CEK_ALG: + alg_str = va_arg(args, char *); + if (alg_str == NULL) + return 0; + impl->cek_nid = OBJ_sn2nid(alg_str); + for (i = 0; i < (size_t)OSSL_NELEM(kek_algs); ++i) { + if (kek_algs[i].nid == impl->cek_nid) { + impl->dkm_len = kek_algs[i].keklen; + return 1; + } + } + KDFerr(KDF_F_X942KDF_CTRL, KDF_R_UNSUPPORTED_CEK_ALG); + return 0; + + default: + return -2; + } +} + +static int x942kdf_ctrl_str(EVP_KDF_IMPL *impl, const char *type, + const char *value) +{ + if (strcmp(type, "digest") == 0) + return kdf_md2ctrl(impl, x942kdf_ctrl, EVP_KDF_CTRL_SET_MD, value); + + if (strcmp(type, "secret") == 0 || strcmp(type, "key") == 0) + return kdf_str2ctrl(impl, x942kdf_ctrl, EVP_KDF_CTRL_SET_KEY, + value); + + if (strcmp(type, "hexsecret") == 0 || strcmp(type, "hexkey") == 0) + return kdf_hex2ctrl(impl, x942kdf_ctrl, EVP_KDF_CTRL_SET_KEY, + value); + + if (strcmp(type, "ukm") == 0) + return kdf_str2ctrl(impl, x942kdf_ctrl, EVP_KDF_CTRL_SET_UKM, + value); + + if (strcmp(type, "hexukm") == 0) + return kdf_hex2ctrl(impl, x942kdf_ctrl, EVP_KDF_CTRL_SET_UKM, + value); + + if (strcmp(type, "cekalg") == 0) + return kdf_str2ctrl(impl, x942kdf_ctrl, EVP_KDF_CTRL_SET_CEK_ALG, + value); + + return -2; +} + +static size_t x942kdf_size(EVP_KDF_IMPL *impl) +{ + int len; + + if (impl->md == NULL) { + KDFerr(KDF_F_X942KDF_SIZE, KDF_R_MISSING_MESSAGE_DIGEST); + return 0; + } + len = EVP_MD_size(impl->md); + return (len <= 0) ? 0 : (size_t)len; +} + +static int x942kdf_derive(EVP_KDF_IMPL *impl, unsigned char *key, size_t keylen) +{ + int ret = 0; + unsigned char *ctr; + unsigned char *der = NULL; + size_t der_len = 0; + + if (impl->secret == NULL) { + KDFerr(KDF_F_X942KDF_DERIVE, KDF_R_MISSING_SECRET); + return 0; + } + if (impl->md == NULL) { + KDFerr(KDF_F_X942KDF_DERIVE, KDF_R_MISSING_MESSAGE_DIGEST); + return 0; + } + if (impl->cek_nid == NID_undef) { + KDFerr(KDF_F_X942KDF_DERIVE, KDF_R_MISSING_CEK_ALG); + return 0; + } + if (impl->ukm != NULL && impl->ukm_len >= X942KDF_MAX_INLEN) { + /* + * Note the ukm length MUST be 512 bits. + * For backwards compatibility the old check is being done. + */ + KDFerr(KDF_F_X942KDF_DERIVE, KDF_R_INAVLID_UKM_LEN); + return 0; + } + if (keylen != impl->dkm_len) { + KDFerr(KDF_F_X942KDF_DERIVE, KDF_R_MISSING_CEK_ALG); + return 0; + } + /* generate the otherinfo der */ + if (!x942_encode_otherinfo(impl->cek_nid, impl->dkm_len, + impl->ukm, impl->ukm_len, + &der, &der_len, &ctr)) { + KDFerr(KDF_F_X942KDF_DERIVE, KDF_R_BAD_ENCODING); + return 0; + } + ret = x942kdf_hash_kdm(impl->md, impl->secret, impl->secret_len, + der, der_len, ctr, key, keylen); + OPENSSL_free(der); + return ret; +} + +const EVP_KDF x942_kdf_meth = { + EVP_KDF_X942, + x942kdf_new, + x942kdf_free, + x942kdf_reset, + x942kdf_ctrl, + x942kdf_ctrl_str, + x942kdf_size, + x942kdf_derive +}; + +#endif /* OPENSSL_NO_CMS */ diff --git a/crypto/objects/obj_dat.h b/crypto/objects/obj_dat.h index 5c47d6bd81..0beeacfa40 100644 --- a/crypto/objects/obj_dat.h +++ b/crypto/objects/obj_dat.h @@ -1084,7 +1084,7 @@ static const unsigned char so[7813] = { 0x2A,0x81,0x1C,0xCF,0x55,0x01,0x83,0x75, /* [ 7804] OBJ_SM2_with_SM3 */ }; -#define NUM_NID 1207 +#define NUM_NID 1208 static const ASN1_OBJECT nid_objs[NUM_NID] = { {"UNDEF", "undefined", NID_undef}, {"rsadsi", "RSA Data Security, Inc.", NID_rsadsi, 6, &so[0]}, @@ -2293,9 +2293,10 @@ static const ASN1_OBJECT nid_objs[NUM_NID] = { {"SM2-SM3", "SM2-with-SM3", NID_SM2_with_SM3, 8, &so[7804]}, {"SSKDF", "sskdf", NID_sskdf}, {"X963KDF", "x963kdf", NID_x963kdf}, + {"X942KDF", "x942kdf", NID_x942kdf}, }; -#define NUM_SN 1198 +#define NUM_SN 1199 static const unsigned int sn_objs[NUM_SN] = { 364, /* "AD_DVCS" */ 419, /* "AES-128-CBC" */ @@ -2596,6 +2597,7 @@ static const unsigned int sn_objs[NUM_SN] = { 378, /* "X500algorithms" */ 12, /* "X509" */ 184, /* "X9-57" */ + 1207, /* "X942KDF" */ 1206, /* "X963KDF" */ 185, /* "X9cm" */ 125, /* "ZLIB" */ @@ -3497,7 +3499,7 @@ static const unsigned int sn_objs[NUM_SN] = { 1093, /* "x509ExtAdmission" */ }; -#define NUM_LN 1198 +#define NUM_LN 1199 static const unsigned int ln_objs[NUM_LN] = { 363, /* "AD Time Stamping" */ 405, /* "ANSI X9.62" */ @@ -4695,6 +4697,7 @@ static const unsigned int ln_objs[NUM_LN] = { 503, /* "x500UniqueIdentifier" */ 158, /* "x509Certificate" */ 160, /* "x509Crl" */ + 1207, /* "x942kdf" */ 1206, /* "x963kdf" */ 125, /* "zlib compression" */ }; diff --git a/crypto/objects/obj_mac.num b/crypto/objects/obj_mac.num index e0969fe1fd..022e64277c 100644 --- a/crypto/objects/obj_mac.num +++ b/crypto/objects/obj_mac.num @@ -1204,3 +1204,4 @@ sshkdf 1203 SM2_with_SM3 1204 sskdf 1205 x963kdf 1206 +x942kdf 1207 diff --git a/crypto/objects/objects.txt b/crypto/objects/objects.txt index dcdfa903c5..47cf2f183d 100644 --- a/crypto/objects/objects.txt +++ b/crypto/objects/objects.txt @@ -1616,6 +1616,8 @@ secg-scheme 14 3 : dhSinglePass-cofactorDH-sha512kdf-scheme # NID for SSKDF : SSKDF : sskdf +# NID for X942KDF + : X942KDF : x942kdf # NID for X963-2001 KDF : X963KDF : x963kdf diff --git a/doc/man3/EVP_KDF_CTX.pod b/doc/man3/EVP_KDF_CTX.pod index 342807fa14..3899361065 100644 --- a/doc/man3/EVP_KDF_CTX.pod +++ b/doc/man3/EVP_KDF_CTX.pod @@ -278,6 +278,7 @@ L<EVP_KDF_HKDF(7)> L<EVP_KDF_SS(7)> L<EVP_KDF_SSHKDF(7)> L<EVP_KDF_X963(7)> +L<EVP_KDF_X942KDF(7)> =head1 HISTORY diff --git a/doc/man7/EVP_KDF_X942.pod b/doc/man7/EVP_KDF_X942.pod new file mode 100644 index 0000000000..306ab2e48e --- /dev/null +++ b/doc/man7/EVP_KDF_X942.pod @@ -0,0 +1,150 @@ +=pod + +=head1 NAME + +EVP_KDF_X942 - The X9.42-2001 asn1 EVP_KDF implementation + +=head1 DESCRIPTION + +The EVP_KDF_X942 algorithm implements the key derivation function (X942KDF). +X942KDF is used by Cryptographic Message Syntax (CMS) for DH KeyAgreement, to +derive a key using input such as a shared secret key and other info. The other +info is DER encoded data that contains a 32 bit counter. + +=head2 Numeric identity + +B<EVP_KDF_X942> 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> + +This control works 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_UKM> + +This control expects two arguments: C<unsigned char *ukm>, C<size_t ukmlen> + +An optional random string that is provided by the sender called "partyAInfo". +In CMS this is the user keying material. + +EVP_KDF_ctrl_str() takes two type strings for this control: + +=over 4 + +=item "ukm" + +The value string is used as is. + +=item "hexukm" + +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_CEK_ALG> + +This control expects one argument: C<char *alg> + +The CEK wrapping algorithm name. + +EVP_KDF_ctrl_str() type string: "cekalg" + +The value string is used as is. + +=back + +=head1 NOTES + +A context for X942KDF can be obtained by calling: + +EVP_KDF_CTX *kctx = EVP_KDF_CTX_new_id(EVP_KDF_X942); + +The output length of an X942KDF is specified via the C<keylen> +parameter to the L<EVP_KDF_derive(3)> function. + +=head1 EXAMPLE + +This example derives 24 bytes, with the secret key "secret" and a random user +keying material: + + EVP_KDF_CTX *kctx; + unsigned char out[192/8]; + unsignred char ukm[64]; + + if (RAND_bytes(ukm, sizeof(ukm)) <= 0) |