Add generic EVP_PKEY_METHOD for EVP_MACs
authorRichard Levitte <levitte@openssl.org>
Wed, 24 Oct 2018 16:35:32 +0000 (18:35 +0200)
committerRichard Levitte <levitte@openssl.org>
Mon, 29 Oct 2018 12:35:19 +0000 (13:35 +0100)
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)

crypto/err/openssl.txt
crypto/evp/build.info
crypto/evp/evp_err.c
crypto/evp/pkey_mac.c [new file with mode: 0644]
include/openssl/evp.h
include/openssl/evperr.h

index 0fe3530..151bc83 100644 (file)
@@ -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
index e4fdedf..84193b0 100644 (file)
@@ -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
index 219a6c8..32760db 100644 (file)
@@ -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 (file)
index 0000000..d4aa585
--- /dev/null
@@ -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
+};
index 79543d7..bee003c 100644 (file)
@@ -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,
index 684bc7c..a17e159 100644 (file)
@@ -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