From: Richard Levitte Date: Wed, 24 Oct 2018 16:35:32 +0000 (+0200) Subject: Add generic EVP_PKEY_METHOD for EVP_MACs X-Git-Tag: openssl-3.0.0-alpha1~3001 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=5e55159b3adbb30482992e8fa8621e47d331d012;p=oweals%2Fopenssl.git Add generic EVP_PKEY_METHOD for EVP_MACs The MAC EVP_PKEY implementations are currently implemented for each MAC. However, with the EVP_MAC API, only one such implementation is needed. This implementation takes into account the differences between HMAC and CMAC implementations, and observes that all other current MAC implementations seem to follow the HMAC model. Reviewed-by: Paul Dale (Merged from https://github.com/openssl/openssl/pull/7393) --- diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt index 0fe35302bc..151bc83183 100644 --- a/crypto/err/openssl.txt +++ b/crypto/err/openssl.txt @@ -807,6 +807,7 @@ EVP_F_PKCS5_PBE_KEYIVGEN:117:PKCS5_PBE_keyivgen EVP_F_PKCS5_V2_PBE_KEYIVGEN:118:PKCS5_v2_PBE_keyivgen EVP_F_PKCS5_V2_PBKDF2_KEYIVGEN:164:PKCS5_v2_PBKDF2_keyivgen EVP_F_PKCS5_V2_SCRYPT_KEYIVGEN:180:PKCS5_v2_scrypt_keyivgen +EVP_F_PKEY_MAC_INIT:214:pkey_mac_init EVP_F_PKEY_SET_TYPE:158:pkey_set_type EVP_F_RC2_MAGIC_TO_METH:109:rc2_magic_to_meth EVP_F_RC5_CTRL:125:rc5_ctrl diff --git a/crypto/evp/build.info b/crypto/evp/build.info index e4fdedf3cc..84193b096a 100644 --- a/crypto/evp/build.info +++ b/crypto/evp/build.info @@ -13,7 +13,7 @@ SOURCE[../../libcrypto]=\ e_old.c pmeth_lib.c pmeth_fn.c pmeth_gn.c m_sigver.c \ e_aes_cbc_hmac_sha1.c e_aes_cbc_hmac_sha256.c e_rc4_hmac_md5.c \ e_chacha20_poly1305.c cmeth_lib.c \ - mac_lib.c c_allm.c + mac_lib.c c_allm.c pkey_mac.c INCLUDE[e_aes.o]=.. ../modes INCLUDE[e_aes_cbc_hmac_sha1.o]=../modes diff --git a/crypto/evp/evp_err.c b/crypto/evp/evp_err.c index 219a6c8641..32760db02f 100644 --- a/crypto/evp/evp_err.c +++ b/crypto/evp/evp_err.c @@ -150,6 +150,7 @@ static const ERR_STRING_DATA EVP_str_functs[] = { "PKCS5_v2_PBKDF2_keyivgen"}, {ERR_PACK(ERR_LIB_EVP, EVP_F_PKCS5_V2_SCRYPT_KEYIVGEN, 0), "PKCS5_v2_scrypt_keyivgen"}, + {ERR_PACK(ERR_LIB_EVP, EVP_F_PKEY_MAC_INIT, 0), "pkey_mac_init"}, {ERR_PACK(ERR_LIB_EVP, EVP_F_PKEY_SET_TYPE, 0), "pkey_set_type"}, {ERR_PACK(ERR_LIB_EVP, EVP_F_RC2_MAGIC_TO_METH, 0), "rc2_magic_to_meth"}, {ERR_PACK(ERR_LIB_EVP, EVP_F_RC5_CTRL, 0), "rc5_ctrl"}, diff --git a/crypto/evp/pkey_mac.c b/crypto/evp/pkey_mac.c new file mode 100644 index 0000000000..d4aa58546a --- /dev/null +++ b/crypto/evp/pkey_mac.c @@ -0,0 +1,368 @@ +/* + * Copyright 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 +#include +#include "internal/evp_int.h" + +/* MAC PKEY context structure */ + +typedef struct { + EVP_MAC_CTX *ctx; + + /* + * We know of two MAC types: + * + * 1. those who take a secret in raw form, i.e. raw data as a + * ASN1_OCTET_STRING embedded in a EVP_PKEY. So far, that's + * all of them but CMAC. + * 2. those who take a secret with associated cipher in very generic + * form, i.e. a complete EVP_MAC_CTX embedded in a PKEY. So far, + * only CMAC does this. + * + * (one might wonder why the second form isn't used for all) + */ +#define MAC_TYPE_RAW 1 /* HMAC like MAC type (all but CMAC so far) */ +#define MAC_TYPE_MAC 2 /* CMAC like MAC type (only CMAC known so far) */ + int type; + + /* The following is only used for MAC_TYPE_RAW implementations */ + struct { + const EVP_MD *md; /* temp storage of MD */ + ASN1_OCTET_STRING ktmp; /* temp storage for key */ + } raw_data; +} MAC_PKEY_CTX; + +static int pkey_mac_init(EVP_PKEY_CTX *ctx) +{ + MAC_PKEY_CTX *hctx; + int nid = ctx->pmeth->pkey_id; + + if ((hctx = OPENSSL_zalloc(sizeof(*hctx))) == NULL) { + EVPerr(EVP_F_PKEY_MAC_INIT, ERR_R_MALLOC_FAILURE); + return 0; + } + + /* We're being smart and using the same base NIDs for PKEY and for MAC */ + hctx->ctx = EVP_MAC_CTX_new_id(nid); + if (hctx->ctx == NULL) { + OPENSSL_free(hctx); + return 0; + } + + if (nid == EVP_PKEY_CMAC) { + hctx->type = MAC_TYPE_MAC; + } else { + hctx->type = MAC_TYPE_RAW; + hctx->raw_data.ktmp.type = V_ASN1_OCTET_STRING; + } + + EVP_PKEY_CTX_set_data(ctx, hctx); + ctx->keygen_info_count = 0; + + return 1; +} + +static void pkey_mac_cleanup(EVP_PKEY_CTX *ctx); + +static int pkey_mac_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src) +{ + MAC_PKEY_CTX *sctx, *dctx; + + if (!pkey_mac_init(dst)) + return 0; + + sctx = EVP_PKEY_CTX_get_data(src); + dctx = EVP_PKEY_CTX_get_data(dst); + + if (!EVP_MAC_CTX_copy(dctx->ctx, sctx->ctx)) + goto err; + + switch (dctx->type) { + case MAC_TYPE_RAW: + dctx->raw_data.md = sctx->raw_data.md; + if (ASN1_STRING_get0_data(&sctx->raw_data.ktmp) != NULL && + !ASN1_STRING_copy(&dctx->raw_data.ktmp, &sctx->raw_data.ktmp)) + goto err; + break; + case MAC_TYPE_MAC: + /* Nothing more to do */ + break; + default: + /* This should be dead code */ + return 0; + } + return 1; + err: + pkey_mac_cleanup (dst); + return 0; +} + +static void pkey_mac_cleanup(EVP_PKEY_CTX *ctx) +{ + MAC_PKEY_CTX *hctx = EVP_PKEY_CTX_get_data(ctx); + + if (hctx != NULL) { + switch (hctx->type) { + case MAC_TYPE_RAW: + OPENSSL_clear_free(hctx->raw_data.ktmp.data, + hctx->raw_data.ktmp.length); + break; + } + EVP_MAC_CTX_free(hctx->ctx); + OPENSSL_free(hctx); + EVP_PKEY_CTX_set_data(ctx, NULL); + } +} + +static int pkey_mac_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) +{ + MAC_PKEY_CTX *hctx = EVP_PKEY_CTX_get_data(ctx); + int nid = ctx->pmeth->pkey_id; + + switch (hctx->type) { + case MAC_TYPE_RAW: + { + ASN1_OCTET_STRING *hkey = NULL; + + if (!hctx->raw_data.ktmp.data) + return 0; + hkey = ASN1_OCTET_STRING_dup(&hctx->raw_data.ktmp); + if (!hkey) + return 0; + EVP_PKEY_assign(pkey, nid, hkey); + } + break; + case MAC_TYPE_MAC: + { + EVP_MAC_CTX *cmkey = EVP_MAC_CTX_new_id(nid); + + if (cmkey == NULL) + return 0; + if (!EVP_MAC_CTX_copy(cmkey, hctx->ctx)) { + EVP_MAC_CTX_free(cmkey); + return 0; + } + EVP_PKEY_assign(pkey, nid, cmkey); + } + break; + default: + /* This should be dead code */ + return 0; + } + + return 1; +} + +static int int_update(EVP_MD_CTX *ctx, const void *data, size_t count) +{ + MAC_PKEY_CTX *hctx = EVP_PKEY_CTX_get_data(EVP_MD_CTX_pkey_ctx(ctx)); + + if (!EVP_MAC_update(hctx->ctx, data, count)) + return 0; + return 1; +} + +static int pkey_mac_signctx_init(EVP_PKEY_CTX *ctx, EVP_MD_CTX *mctx) +{ + MAC_PKEY_CTX *hctx = EVP_PKEY_CTX_get_data(ctx); + ASN1_OCTET_STRING *key = NULL; + int rv = 1; + /* + * For MACs with the EVP_PKEY_FLAG_SIGCTX_CUSTOM flag set and that + * gets the key passed as an ASN.1 OCTET STRING, we set the key here, + * as this may be only time it's set during a DigestSign. + * + * MACs that pass around the key in form of EVP_MAC_CTX are setting + * the key through other mechanisms. (this is only CMAC for now) + */ + int set_key = + hctx->type == MAC_TYPE_RAW + && (ctx->pmeth->flags & EVP_PKEY_FLAG_SIGCTX_CUSTOM) != 0; + + if (set_key) { + if (EVP_PKEY_id(EVP_PKEY_CTX_get0_pkey(ctx)) + != EVP_MAC_nid(EVP_MAC_CTX_mac(hctx->ctx))) + return 0; + key = EVP_PKEY_get0(EVP_PKEY_CTX_get0_pkey(ctx)); + if (key == NULL) + return 0; + } + + /* Some MACs don't support this control... that's fine */ + EVP_MAC_ctrl(hctx->ctx, EVP_MAC_CTRL_SET_FLAGS, + EVP_MD_CTX_test_flags(mctx, ~EVP_MD_CTX_FLAG_NO_INIT)); + + EVP_MD_CTX_set_flags(mctx, EVP_MD_CTX_FLAG_NO_INIT); + EVP_MD_CTX_set_update_fn(mctx, int_update); + + if (set_key) + rv = EVP_MAC_ctrl(hctx->ctx, EVP_MAC_CTRL_SET_KEY, key->data, + key->length); + return rv > 0; +} + +static int pkey_mac_signctx(EVP_PKEY_CTX *ctx, unsigned char *sig, + size_t *siglen, EVP_MD_CTX *mctx) +{ + MAC_PKEY_CTX *hctx = EVP_PKEY_CTX_get_data(ctx); + + return EVP_MAC_final(hctx->ctx, sig, siglen); +} + +static int pkey_mac_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) +{ + MAC_PKEY_CTX *hctx = EVP_PKEY_CTX_get_data(ctx); + + switch (type) { + + case EVP_PKEY_CTRL_CIPHER: + switch (hctx->type) { + case MAC_TYPE_RAW: + return -2; /* The raw types don't support ciphers */ + case MAC_TYPE_MAC: + { + int rv; + + if ((rv = EVP_MAC_ctrl(hctx->ctx, EVP_MAC_CTRL_SET_ENGINE, + ctx->engine)) < 0 + || (rv = EVP_MAC_ctrl(hctx->ctx, EVP_MAC_CTRL_SET_CIPHER, + p2)) < 0 + || !(rv = EVP_MAC_init(hctx->ctx))) + return rv; + } + break; + default: + /* This should be dead code */ + return 0; + } + break; + + case EVP_PKEY_CTRL_MD: + switch (hctx->type) { + case MAC_TYPE_RAW: + hctx->raw_data.md = p2; + break; + case MAC_TYPE_MAC: + if (ctx->pkey != NULL + && !EVP_MAC_CTX_copy(hctx->ctx, + (EVP_MAC_CTX *)ctx->pkey->pkey.ptr)) + return 0; + if (!EVP_MAC_init(hctx->ctx)) + return 0; + break; + default: + /* This should be dead code */ + return 0; + } + break; + + case EVP_PKEY_CTRL_SET_DIGEST_SIZE: + return EVP_MAC_ctrl(hctx->ctx, EVP_MAC_CTRL_SET_SIZE, (size_t)p1); + + case EVP_PKEY_CTRL_SET_MAC_KEY: + switch (hctx->type) { + case MAC_TYPE_RAW: + if ((!p2 && p1 > 0) || (p1 < -1)) + return 0; + if (!ASN1_OCTET_STRING_set(&hctx->raw_data.ktmp, p2, p1)) + return 0; + break; + case MAC_TYPE_MAC: + if (!EVP_MAC_ctrl(hctx->ctx, EVP_MAC_CTRL_SET_KEY, p2, p1)) + return 0; + break; + default: + /* This should be dead code */ + return 0; + } + break; + + case EVP_PKEY_CTRL_DIGESTINIT: + switch (hctx->type) { + case MAC_TYPE_RAW: + /* Ensure that we have attached the implementation */ + if (!EVP_MAC_init(hctx->ctx)) + return 0; + { + int rv; + ASN1_OCTET_STRING *key = + (ASN1_OCTET_STRING *)ctx->pkey->pkey.ptr; + + if ((rv = EVP_MAC_ctrl(hctx->ctx, EVP_MAC_CTRL_SET_ENGINE, + ctx->engine)) < 0 + || (rv = EVP_MAC_ctrl(hctx->ctx, EVP_MAC_CTRL_SET_MD, + hctx->raw_data.md)) < 0 + || (rv = EVP_MAC_ctrl(hctx->ctx, EVP_MAC_CTRL_SET_KEY, + key->data, key->length)) < 0) + return rv; + } + break; + case MAC_TYPE_MAC: + return -2; /* The mac types don't support ciphers */ + default: + /* This should be dead code */ + return 0; + } + break; + + default: + return -2; + + } + return 1; +} + +static int pkey_mac_ctrl_str(EVP_PKEY_CTX *ctx, + const char *type, const char *value) +{ + MAC_PKEY_CTX *hctx = EVP_PKEY_CTX_get_data(ctx); + + return EVP_MAC_ctrl_str(hctx->ctx, type, value); +} + +/* + * When this is actually used, the following will be replaced with real + * EVP_PKEY_METHODs, all exactly the same apart from the type and possibly + * the flags. + */ + +extern const EVP_PKEY_METHOD FAKE_pkey_meth; +const EVP_PKEY_METHOD FAKE_pkey_meth = { + 20870442 /* EVP_PKEY_FAKE, a beast times 31337 (you do the math) */, + EVP_PKEY_FLAG_SIGCTX_CUSTOM, + pkey_mac_init, + pkey_mac_copy, + pkey_mac_cleanup, + + 0, 0, + + 0, + pkey_mac_keygen, + + 0, 0, + + 0, 0, + + 0, 0, + + pkey_mac_signctx_init, + pkey_mac_signctx, + + 0, 0, + + 0, 0, + + 0, 0, + + 0, 0, + + pkey_mac_ctrl, + pkey_mac_ctrl_str +}; diff --git a/include/openssl/evp.h b/include/openssl/evp.h index 79543d731b..bee003c6ec 100644 --- a/include/openssl/evp.h +++ b/include/openssl/evp.h @@ -1014,6 +1014,13 @@ void EVP_MAC_do_all_sorted(void (*fn) (const EVP_MAC *ciph, const char *from, const char *to, void *x), void *arg); +# define EVP_MAC_CTRL_SET_KEY 0x01 /* unsigned char *, size_t */ +# define EVP_MAC_CTRL_SET_FLAGS 0x02 /* unsigned long */ +# define EVP_MAC_CTRL_SET_ENGINE 0x03 /* ENGINE * */ +# define EVP_MAC_CTRL_SET_MD 0x04 /* EVP_MD * */ +# define EVP_MAC_CTRL_SET_CIPHER 0x04 /* EVP_CIPHER * */ +# define EVP_MAC_CTRL_SET_SIZE 0x05 /* size_t */ + /* PKEY stuff */ int EVP_PKEY_decrypt_old(unsigned char *dec_key, const unsigned char *enc_key, int enc_key_len, diff --git a/include/openssl/evperr.h b/include/openssl/evperr.h index 684bc7ceb0..a17e1599bb 100644 --- a/include/openssl/evperr.h +++ b/include/openssl/evperr.h @@ -117,6 +117,7 @@ int ERR_load_EVP_strings(void); # define EVP_F_PKCS5_V2_PBE_KEYIVGEN 118 # define EVP_F_PKCS5_V2_PBKDF2_KEYIVGEN 164 # define EVP_F_PKCS5_V2_SCRYPT_KEYIVGEN 180 +# define EVP_F_PKEY_MAC_INIT 214 # define EVP_F_PKEY_SET_TYPE 158 # define EVP_F_RC2_MAGIC_TO_METH 109 # define EVP_F_RC5_CTRL 125