summaryrefslogtreecommitdiffstats
path: root/crypto
diff options
context:
space:
mode:
authorRichard Levitte <levitte@openssl.org>2018-10-24 18:35:32 +0200
committerRichard Levitte <levitte@openssl.org>2018-10-29 13:35:19 +0100
commit5e55159b3adbb30482992e8fa8621e47d331d012 (patch)
tree5000f21a6deb8c79b270a3430dd064509321d4ce /crypto
parent0145dd324e8fcfd2c0dfe296c12586101f0cf3b9 (diff)
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 <paul.dale@oracle.com> (Merged from https://github.com/openssl/openssl/pull/7393)
Diffstat (limited to 'crypto')
-rw-r--r--crypto/err/openssl.txt1
-rw-r--r--crypto/evp/build.info2
-rw-r--r--crypto/evp/evp_err.c1
-rw-r--r--crypto/evp/pkey_mac.c368
4 files changed, 371 insertions, 1 deletions
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 <openssl/err.h>
+#include <openssl/evp.h>
+#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
+};