/*
- * Copyright 2018 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2018-2020 The OpenSSL Project Authors. All Rights Reserved.
*
- * Licensed under the OpenSSL license (the "License"). You may not use
+ * 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 <string.h>
#include <openssl/err.h>
#include <openssl/evp.h>
-#include "internal/evp_int.h"
+#include <openssl/engine.h>
+#include <openssl/params.h>
+#include <openssl/core_names.h>
+#include "crypto/evp.h"
+#include "evp_local.h"
/* MAC PKEY context structure */
} raw_data;
} MAC_PKEY_CTX;
+static void pkey_mac_cleanup(EVP_PKEY_CTX *ctx);
+
static int pkey_mac_init(EVP_PKEY_CTX *ctx)
{
MAC_PKEY_CTX *hctx;
+ /* We're being smart and using the same base NIDs for PKEY and for MAC */
int nid = ctx->pmeth->pkey_id;
+ EVP_MAC *mac;
+
+ ERR_set_mark();
+ mac = EVP_MAC_fetch(ctx->libctx, OBJ_nid2sn(nid), ctx->propquery);
+ ERR_pop_to_mark();
+
+ /*
+ * mac == NULL may actually be ok in some situations. In an
+ * EVP_PKEY_new_mac_key() call a temporary EVP_PKEY_CTX is created with
+ * default libctx. We don't actually need the underlying MAC to be present
+ * to successfully set the key in that case. The resulting EVP_PKEY could
+ * then be used in some other libctx where the MAC *is* present
+ */
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 (mac != NULL) {
+ hctx->ctx = EVP_MAC_CTX_new(mac);
+ if (hctx->ctx == NULL) {
+ OPENSSL_free(hctx);
+ return 0;
+ }
}
if (nid == EVP_PKEY_CMAC) {
hctx->raw_data.ktmp.type = V_ASN1_OCTET_STRING;
}
+ pkey_mac_cleanup(ctx);
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)
+static int pkey_mac_copy(EVP_PKEY_CTX *dst, const EVP_PKEY_CTX *src)
{
MAC_PKEY_CTX *sctx, *dctx;
- if (!pkey_mac_init(dst))
+ sctx = EVP_PKEY_CTX_get_data(src);
+
+ if (sctx->ctx == NULL) {
+ /* This actually means the fetch failed during the init call */
+ EVPerr(0, EVP_R_FETCH_FAILED);
+ return 0;
+ }
+
+ if (sctx->ctx->data == NULL)
return 0;
- sctx = EVP_PKEY_CTX_get_data(src);
- dctx = EVP_PKEY_CTX_get_data(dst);
+ dctx = OPENSSL_zalloc(sizeof(*dctx));
+ if (dctx == NULL) {
+ EVPerr(EVP_F_PKEY_MAC_COPY, ERR_R_MALLOC_FAILURE);
+ return 0;
+ }
+
+ EVP_PKEY_CTX_set_data(dst, dctx);
+ dst->keygen_info_count = 0;
- if (!EVP_MAC_CTX_copy(dctx->ctx, sctx->ctx))
+ dctx->ctx = EVP_MAC_CTX_dup(sctx->ctx);
+ if (dctx->ctx == NULL)
goto err;
+ /*
+ * Normally, nothing special would be done with the MAC method. In
+ * this particular case, though, the MAC method was fetched internally
+ * by pkey_mac_init() above or by EVP_PKEY_new_CMAC_key() and passed
+ * via the EVP_MAC_CTX, so it is effectively like every new EVP_MAC_CTX
+ * fetches the MAC method anew in this case. Therefore, its reference
+ * count must be adjusted here.
+ */
+ if (!EVP_MAC_up_ref(EVP_MAC_CTX_mac(dctx->ctx)))
+ goto err;
+
+ dctx->type = sctx->type;
+
switch (dctx->type) {
case MAC_TYPE_RAW:
dctx->raw_data.md = sctx->raw_data.md;
}
return 1;
err:
- pkey_mac_cleanup (dst);
+ 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);
+ /*
+ * For the exact same reasons the MAC reference count is incremented
+ * in pkey_mac_copy() above, it must be explicitly freed here.
+ */
+
+ MAC_PKEY_CTX *hctx = ctx == NULL ? NULL : EVP_PKEY_CTX_get_data(ctx);
if (hctx != NULL) {
+ EVP_MAC *mac = hctx->ctx != NULL ? EVP_MAC_CTX_mac(hctx->ctx) : NULL;
+
switch (hctx->type) {
case MAC_TYPE_RAW:
OPENSSL_clear_free(hctx->raw_data.ktmp.data,
break;
}
EVP_MAC_CTX_free(hctx->ctx);
+ EVP_MAC_free(mac);
OPENSSL_free(hctx);
EVP_PKEY_CTX_set_data(ctx, NULL);
}
break;
case MAC_TYPE_MAC:
{
- EVP_MAC_CTX *cmkey = EVP_MAC_CTX_new_id(nid);
+ EVP_MAC_CTX *cmkey;
+ if (hctx->ctx == NULL) {
+ /* This actually means the fetch failed during the init call */
+ EVPerr(0, EVP_R_FETCH_FAILED);
+ return 0;
+ }
+
+ cmkey = EVP_MAC_CTX_dup(hctx->ctx);
if (cmkey == NULL)
return 0;
- if (!EVP_MAC_CTX_copy(cmkey, hctx->ctx)) {
- EVP_MAC_CTX_free(cmkey);
+ if (!EVP_MAC_up_ref(EVP_MAC_CTX_mac(hctx->ctx)))
return 0;
- }
EVP_PKEY_assign(pkey, nid, cmkey);
}
break;
hctx->type == MAC_TYPE_RAW
&& (ctx->pmeth->flags & EVP_PKEY_FLAG_SIGCTX_CUSTOM) != 0;
+ if (hctx->ctx == NULL) {
+ /* This actually means the fetch failed during the init call */
+ EVPerr(0, EVP_R_FETCH_FAILED);
+ return 0;
+ }
+
if (set_key) {
- if (EVP_PKEY_id(EVP_PKEY_CTX_get0_pkey(ctx))
- != EVP_MAC_nid(EVP_MAC_CTX_mac(hctx->ctx)))
+ if (!EVP_MAC_is_a(EVP_MAC_CTX_mac(hctx->ctx),
+ OBJ_nid2sn(EVP_PKEY_id(EVP_PKEY_CTX_get0_pkey(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;
+ /* Some MACs don't support this control... that's fine */
+ {
+ OSSL_PARAM params[3];
+ size_t params_n = 0;
+ int flags = EVP_MD_CTX_test_flags(mctx, ~EVP_MD_CTX_FLAG_NO_INIT);
+
+ /* TODO(3.0) "flags" isn't quite right, i.e. a quick hack for now */
+ params[params_n++] =
+ OSSL_PARAM_construct_int(OSSL_MAC_PARAM_FLAGS, &flags);
+ if (set_key)
+ params[params_n++] =
+ OSSL_PARAM_construct_octet_string(OSSL_MAC_PARAM_KEY,
+ key->data, key->length);
+ params[params_n++] = OSSL_PARAM_construct_end();
+ rv = EVP_MAC_CTX_set_params(hctx->ctx, params);
+ }
+ return rv;
}
static int pkey_mac_signctx(EVP_PKEY_CTX *ctx, unsigned char *sig,
{
MAC_PKEY_CTX *hctx = EVP_PKEY_CTX_get_data(ctx);
- return EVP_MAC_final(hctx->ctx, sig, siglen);
+ return EVP_MAC_final(hctx->ctx, sig, siglen, EVP_MAC_size(hctx->ctx));
}
static int pkey_mac_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
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;
+ OSSL_PARAM params[3];
+ size_t params_n = 0;
+ char *ciphname = (char *)OBJ_nid2sn(EVP_CIPHER_nid(p2));
+#ifndef OPENSSL_NO_ENGINE
+ char *engineid = (char *)ENGINE_get_id(ctx->engine);
+
+ params[params_n++] =
+ OSSL_PARAM_construct_utf8_string("engine", engineid, 0);
+#endif
+ params[params_n++] =
+ OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_CIPHER,
+ ciphname, 0);
+ params[params_n] = OSSL_PARAM_construct_end();
+
+ if (hctx->ctx == NULL) {
+ /*
+ * This actually means the fetch failed during the init call
+ */
+ EVPerr(0, EVP_R_FETCH_FAILED);
+ return 0;
+ }
+
+ if (!EVP_MAC_CTX_set_params(hctx->ctx, params)
+ || !EVP_MAC_init(hctx->ctx))
+ return 0;
}
break;
default:
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;
+ case MAC_TYPE_MAC: {
+ EVP_MAC_CTX *new_mac_ctx;
+
+ if (ctx->pkey == NULL)
+ return 0;
+ new_mac_ctx = EVP_MAC_CTX_dup(ctx->pkey->pkey.ptr);
+ if (new_mac_ctx == NULL)
+ return 0;
+ EVP_MAC_CTX_free(hctx->ctx);
+ hctx->ctx = new_mac_ctx;
+ }
break;
default:
/* This should be dead code */
break;
case EVP_PKEY_CTRL_SET_DIGEST_SIZE:
- return EVP_MAC_ctrl(hctx->ctx, EVP_MAC_CTRL_SET_SIZE, (size_t)p1);
+ {
+ OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END };
+ size_t size = (size_t)p1;
+ size_t verify = 0;
+
+ /*
+ * We verify that the length is actually set by getting back
+ * the same parameter and checking that it matches what we
+ * tried to set.
+ * TODO(3.0) when we have a more direct mechanism to check if
+ * a parameter was used, we must refactor this to use that.
+ */
+
+ params[0] =
+ OSSL_PARAM_construct_size_t(OSSL_MAC_PARAM_SIZE, &size);
+
+ if (hctx->ctx == NULL) {
+ /*
+ * This actually means the fetch failed during the init call
+ */
+ EVPerr(0, EVP_R_FETCH_FAILED);
+ return 0;
+ }
+
+ if (!EVP_MAC_CTX_set_params(hctx->ctx, params))
+ return 0;
+
+ params[0] =
+ OSSL_PARAM_construct_size_t(OSSL_MAC_PARAM_SIZE, &verify);
+
+ if (!EVP_MAC_CTX_get_params(hctx->ctx, params))
+ return 0;
+ /*
+ * Since EVP_MAC_CTX_{get,set}_params() returned successfully,
+ * we can only assume that the size was ignored, i.e. this
+ * control is unsupported.
+ */
+ if (verify != size)
+ return -2;
+ }
+ break;
case EVP_PKEY_CTRL_SET_MAC_KEY:
switch (hctx->type) {
case MAC_TYPE_RAW:
return 0;
break;
case MAC_TYPE_MAC:
- if (!EVP_MAC_ctrl(hctx->ctx, EVP_MAC_CTRL_SET_KEY, p2, p1))
- return 0;
+ {
+ OSSL_PARAM params[2];
+ size_t params_n = 0;
+
+ params[params_n++] =
+ OSSL_PARAM_construct_octet_string(OSSL_MAC_PARAM_KEY,
+ p2, p1);
+ params[params_n] = OSSL_PARAM_construct_end();
+
+ if (hctx->ctx == NULL) {
+ /*
+ * This actually means the fetch failed during the init call
+ */
+ EVPerr(0, EVP_R_FETCH_FAILED);
+ return 0;
+ }
+
+ return EVP_MAC_CTX_set_params(hctx->ctx, params);
+ }
break;
default:
/* This should be dead code */
case EVP_PKEY_CTRL_DIGESTINIT:
switch (hctx->type) {
case MAC_TYPE_RAW:
+ if (hctx->ctx == NULL) {
+ /* This actually means the fetch failed during the init call */
+ EVPerr(0, EVP_R_FETCH_FAILED);
+ return 0;
+ }
+
/* 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;
+ OSSL_PARAM params[4];
+ size_t params_n = 0;
+ char *mdname =
+ (char *)OBJ_nid2sn(EVP_MD_nid(hctx->raw_data.md));
+#ifndef OPENSSL_NO_ENGINE
+ char *engineid = ctx->engine == NULL
+ ? NULL : (char *)ENGINE_get_id(ctx->engine);
+
+ if (engineid != NULL)
+ params[params_n++] =
+ OSSL_PARAM_construct_utf8_string("engine", engineid, 0);
+#endif
+ params[params_n++] =
+ OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST,
+ mdname, 0);
+ params[params_n++] =
+ OSSL_PARAM_construct_octet_string(OSSL_MAC_PARAM_KEY,
+ key->data, key->length);
+ params[params_n] = OSSL_PARAM_construct_end();
+
+ return EVP_MAC_CTX_set_params(hctx->ctx, params);
}
break;
case MAC_TYPE_MAC:
}
static int pkey_mac_ctrl_str(EVP_PKEY_CTX *ctx,
- const char *type, const char *value)
+ const char *type, const char *value)
{
MAC_PKEY_CTX *hctx = EVP_PKEY_CTX_get_data(ctx);
+ const EVP_MAC *mac;
+ OSSL_PARAM params[2];
+ int ok = 0;
+
+ if (hctx == NULL) {
+ EVPerr(0, EVP_R_NULL_MAC_PKEY_CTX);
+ return 0;
+ }
+ if (hctx->ctx == NULL) {
+ /* This actually means the fetch failed during the init call */
+ EVPerr(0, EVP_R_FETCH_FAILED);
+ return 0;
+ }
+ mac = EVP_MAC_CTX_mac(hctx->ctx);
+
+ /*
+ * Translation of some control names that are equivalent to a single
+ * parameter name.
+ *
+ * "md" and "digest" are the same thing, we use the single "digest"
+ *
+ * "digestsize" was a setting control in siphash, but naming wise,
+ * it's really the same as "size".
+ */
+ if (strcmp(type, "md") == 0)
+ type = OSSL_MAC_PARAM_DIGEST;
+ else if (strcmp(type, "digestsize") == 0)
+ type = OSSL_MAC_PARAM_SIZE;
+
+ if (!OSSL_PARAM_allocate_from_text(¶ms[0],
+ EVP_MAC_settable_ctx_params(mac),
+ type, value, strlen(value) + 1, NULL))
+ return 0;
+ params[1] = OSSL_PARAM_construct_end();
- return EVP_MAC_ctrl_str(hctx->ctx, type, value);
+ ok = EVP_MAC_CTX_set_params(hctx->ctx, params);
+ OPENSSL_free(params[0].data);
+ return ok;
}
-const EVP_PKEY_METHOD cmac_pkey_meth = {
+static const EVP_PKEY_METHOD cmac_pkey_meth = {
EVP_PKEY_CMAC,
EVP_PKEY_FLAG_SIGCTX_CUSTOM,
pkey_mac_init,
pkey_mac_ctrl,
pkey_mac_ctrl_str
};
+
+const EVP_PKEY_METHOD *cmac_pkey_method(void)
+{
+ return &cmac_pkey_meth;
+}
+
+static const EVP_PKEY_METHOD hmac_pkey_meth = {
+ EVP_PKEY_HMAC,
+ 0,
+ 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
+};
+
+const EVP_PKEY_METHOD *hmac_pkey_method(void)
+{
+ return &hmac_pkey_meth;
+}
+
+static const EVP_PKEY_METHOD siphash_pkey_meth = {
+ EVP_PKEY_SIPHASH,
+ 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
+};
+
+const EVP_PKEY_METHOD *siphash_pkey_method(void)
+{
+ return &siphash_pkey_meth;
+}
+
+static const EVP_PKEY_METHOD poly1305_pkey_meth = {
+ EVP_PKEY_POLY1305,
+ 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
+};
+
+const EVP_PKEY_METHOD *poly1305_pkey_method(void)
+{
+ return &poly1305_pkey_meth;
+}