| /* |
| * Copyright 2018 The OpenSSL Project Authors. All Rights Reserved. |
| * |
| * 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 <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, const 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); |
| } |
| |
| const EVP_PKEY_METHOD cmac_pkey_meth = { |
| EVP_PKEY_CMAC, |
| 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 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 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 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 |
| }; |