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