summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Relyea <rrelyea@redhat.com>2019-11-20 14:20:43 -0800
committerRobert Relyea <rrelyea@redhat.com>2019-11-20 14:20:43 -0800
commit4bfbba29301790354610f43f19a5a531d406cd85 (patch)
treeda1099b5809d0990e7c0c01104d505675022299c
parentad467f0c99ead176fa5583fee850e32fbd17efd3 (diff)
downloadnss-hg-4bfbba29301790354610f43f19a5a531d406cd85.tar.gz
Bug 1596450 - softoken: unified MAC implementation
patch by Alex Scheel review by rrelyea https://phabricator.services.mozilla.com/D53032
-rw-r--r--lib/softoken/pkcs11c.c164
-rw-r--r--lib/softoken/pkcs11i.h91
-rw-r--r--lib/softoken/sftkhmac.c289
3 files changed, 410 insertions, 134 deletions
diff --git a/lib/softoken/pkcs11c.c b/lib/softoken/pkcs11c.c
index 854e9cd75..6bc5cc939 100644
--- a/lib/softoken/pkcs11c.c
+++ b/lib/softoken/pkcs11c.c
@@ -2004,111 +2004,42 @@ sftk_HMACCmp(CK_ULONG *copyLen, unsigned char *sig, unsigned int sigLen,
}
/*
- * common HMAC initalization routine
+ * common HMAC + CMAC initialization routine
*/
static CK_RV
-sftk_doHMACInit(SFTKSessionContext *context, HASH_HashType hash,
- SFTKObject *key, CK_ULONG mac_size)
+sftk_doMACInit(CK_MECHANISM_TYPE mech, SFTKSessionContext *session,
+ SFTKObject *key, CK_ULONG mac_size)
{
- SFTKAttribute *keyval;
- HMACContext *HMACcontext;
+ CK_RV crv;
+ sftk_MACCtx *context;
CK_ULONG *intpointer;
- const SECHashObject *hashObj = HASH_GetRawHashObject(hash);
PRBool isFIPS = (key->slot->slotID == FIPS_SLOT_ID);
- /* required by FIPS 198 Section 4 */
- if (isFIPS && (mac_size < 4 || mac_size < hashObj->length / 2)) {
- return CKR_BUFFER_TOO_SMALL;
- }
-
- keyval = sftk_FindAttribute(key, CKA_VALUE);
- if (keyval == NULL)
- return CKR_KEY_SIZE_RANGE;
-
- HMACcontext = HMAC_Create(hashObj,
- (const unsigned char *)keyval->attrib.pValue,
- keyval->attrib.ulValueLen, isFIPS);
- context->hashInfo = HMACcontext;
- context->multi = PR_TRUE;
- sftk_FreeAttribute(keyval);
- if (context->hashInfo == NULL) {
- if (PORT_GetError() == SEC_ERROR_INVALID_ARGS) {
- return CKR_KEY_SIZE_RANGE;
- }
- return CKR_HOST_MEMORY;
- }
- context->hashUpdate = (SFTKHash)HMAC_Update;
- context->end = (SFTKEnd)HMAC_Finish;
-
- context->hashdestroy = (SFTKDestroy)HMAC_Destroy;
- intpointer = PORT_New(CK_ULONG);
- if (intpointer == NULL) {
- return CKR_HOST_MEMORY;
- }
- *intpointer = mac_size;
- context->cipherInfo = intpointer;
- context->destroy = (SFTKDestroy)sftk_Space;
- context->update = (SFTKCipher)sftk_SignCopy;
- context->verify = (SFTKVerify)sftk_HMACCmp;
- context->maxLen = hashObj->length;
- HMAC_Begin(HMACcontext);
- return CKR_OK;
-}
-
-/*
- * common CMAC initialization routine
- */
-static CK_RV
-sftk_doCMACInit(SFTKSessionContext *session, CMACCipher type,
- SFTKObject *key, CK_ULONG mac_size)
-{
- SFTKAttribute *keyval;
- CMACContext *cmacContext;
- CK_ULONG *intpointer;
-
- /* Unlike HMAC, CMAC doesn't need to check key sizes as the underlying
- * block cipher does this for us: block ciphers support only a single
- * key size per variant.
- *
- * To introduce support for a CMAC based on a new block cipher, first add
- * support for the relevant block cipher to CMAC in the freebl layer. Then
- * update the switch statement at the end of this function. Also remember
- * to update the switch statement in NSC_SignInit with the PKCS#11
- * mechanism constants.
- */
-
- keyval = sftk_FindAttribute(key, CKA_VALUE);
- if (keyval == NULL) {
- return CKR_KEY_SIZE_RANGE;
+ /* Set up the initial context. */
+ crv = sftk_MAC_Create(mech, key, &context);
+ if (crv != CKR_OK) {
+ return crv;
}
- /* Create the underlying CMACContext and associate it with the
- * SFTKSessionContext's hashInfo field */
- cmacContext = CMAC_Create(type,
- (const unsigned char *)keyval->attrib.pValue,
- keyval->attrib.ulValueLen);
- sftk_FreeAttribute(keyval);
-
- if (cmacContext == NULL) {
- if (PORT_GetError() == SEC_ERROR_INVALID_ARGS) {
- return CKR_KEY_SIZE_RANGE;
- }
+ session->hashInfo = context;
+ session->multi = PR_TRUE;
- return CKR_HOST_MEMORY;
+ /* Required by FIPS 198 Section 4. Delay this check until after the MAC
+ * has been initialized to steal the output size of the MAC. */
+ if (isFIPS && (mac_size < 4 || mac_size < context->mac_size / 2)) {
+ sftk_MAC_Destroy(context, PR_TRUE);
+ return CKR_BUFFER_TOO_SMALL;
}
- session->hashInfo = cmacContext;
- /* MACs all behave roughly the same. However, CMAC can fail because
- * the underlying cipher can fail. In practice, this shouldn't occur
- * because we're not using any chaining modes, letting us safely ignore
- * the return value. */
- session->multi = PR_TRUE;
- session->hashUpdate = (SFTKHash)CMAC_Update;
- session->end = (SFTKEnd)CMAC_Finish;
- session->hashdestroy = (SFTKDestroy)CMAC_Destroy;
+ /* Configure our helper functions appropriately. Note that these casts
+ * ignore the return values. */
+ session->hashUpdate = (SFTKHash)sftk_MAC_Update;
+ session->end = (SFTKEnd)sftk_MAC_Finish;
+ session->hashdestroy = (SFTKDestroy)sftk_MAC_Destroy;
intpointer = PORT_New(CK_ULONG);
if (intpointer == NULL) {
+ sftk_MAC_Destroy(context, PR_TRUE);
return CKR_HOST_MEMORY;
}
*intpointer = mac_size;
@@ -2120,15 +2051,7 @@ sftk_doCMACInit(SFTKSessionContext *session, CMACCipher type,
session->verify = (SFTKVerify)sftk_HMACCmp;
session->destroy = (SFTKDestroy)sftk_Space;
- /* Will need to be updated for additional block ciphers in the future. */
- switch (type) {
- case CMAC_AES:
- session->maxLen = AES_BLOCK_SIZE;
- break;
- default:
- PORT_Assert(0);
- return CKR_KEY_SIZE_RANGE;
- }
+ session->maxLen = context->mac_size;
return CKR_OK;
}
@@ -2879,18 +2802,19 @@ NSC_SignInit(CK_SESSION_HANDLE hSession,
break;
-#define INIT_HMAC_MECH(mmm) \
- case CKM_##mmm##_HMAC_GENERAL: \
- PORT_Assert(pMechanism->pParameter); \
- if (!pMechanism->pParameter) { \
- crv = CKR_MECHANISM_PARAM_INVALID; \
- break; \
- } \
- crv = sftk_doHMACInit(context, HASH_Alg##mmm, key, \
- *(CK_ULONG *)pMechanism->pParameter); \
- break; \
- case CKM_##mmm##_HMAC: \
- crv = sftk_doHMACInit(context, HASH_Alg##mmm, key, mmm##_LENGTH); \
+#define INIT_HMAC_MECH(mmm) \
+ case CKM_##mmm##_HMAC_GENERAL: \
+ PORT_Assert(pMechanism->pParameter); \
+ if (!pMechanism->pParameter) { \
+ crv = CKR_MECHANISM_PARAM_INVALID; \
+ break; \
+ } \
+ crv = sftk_doMACInit(pMechanism->mechanism, context, key, \
+ *(CK_ULONG *)pMechanism->pParameter); \
+ break; \
+ case CKM_##mmm##_HMAC: \
+ crv = sftk_doMACInit(pMechanism->mechanism, context, key, \
+ mmm##_LENGTH); \
break;
INIT_HMAC_MECH(MD2)
@@ -2906,11 +2830,11 @@ NSC_SignInit(CK_SESSION_HANDLE hSession,
crv = CKR_MECHANISM_PARAM_INVALID;
break;
}
- crv = sftk_doHMACInit(context, HASH_AlgSHA1, key,
- *(CK_ULONG *)pMechanism->pParameter);
+ crv = sftk_doMACInit(pMechanism->mechanism, context, key,
+ *(CK_ULONG *)pMechanism->pParameter);
break;
case CKM_SHA_1_HMAC:
- crv = sftk_doHMACInit(context, HASH_AlgSHA1, key, SHA1_LENGTH);
+ crv = sftk_doMACInit(pMechanism->mechanism, context, key, SHA1_LENGTH);
break;
case CKM_AES_CMAC_GENERAL:
PORT_Assert(pMechanism->pParameter);
@@ -2918,10 +2842,10 @@ NSC_SignInit(CK_SESSION_HANDLE hSession,
crv = CKR_MECHANISM_PARAM_INVALID;
break;
}
- crv = sftk_doCMACInit(context, CMAC_AES, key, *(CK_ULONG *)pMechanism->pParameter);
+ crv = sftk_doMACInit(pMechanism->mechanism, context, key, *(CK_ULONG *)pMechanism->pParameter);
break;
case CKM_AES_CMAC:
- crv = sftk_doCMACInit(context, CMAC_AES, key, AES_BLOCK_SIZE);
+ crv = sftk_doMACInit(pMechanism->mechanism, context, key, AES_BLOCK_SIZE);
break;
case CKM_SSL3_MD5_MAC:
PORT_Assert(pMechanism->pParameter);
@@ -3596,11 +3520,11 @@ NSC_VerifyInit(CK_SESSION_HANDLE hSession,
crv = CKR_MECHANISM_PARAM_INVALID;
break;
}
- crv = sftk_doHMACInit(context, HASH_AlgSHA1, key,
- *(CK_ULONG *)pMechanism->pParameter);
+ crv = sftk_doMACInit(pMechanism->mechanism, context, key,
+ *(CK_ULONG *)pMechanism->pParameter);
break;
case CKM_SHA_1_HMAC:
- crv = sftk_doHMACInit(context, HASH_AlgSHA1, key, SHA1_LENGTH);
+ crv = sftk_doMACInit(pMechanism->mechanism, context, key, SHA1_LENGTH);
break;
case CKM_SSL3_MD5_MAC:
diff --git a/lib/softoken/pkcs11i.h b/lib/softoken/pkcs11i.h
index fccf0f0dd..51127a32a 100644
--- a/lib/softoken/pkcs11i.h
+++ b/lib/softoken/pkcs11i.h
@@ -17,6 +17,9 @@
#include "chacha20poly1305.h"
#include "hasht.h"
+#include "alghmac.h"
+#include "cmac.h"
+
/*
* Configuration Defines
*
@@ -595,6 +598,73 @@ typedef struct sftk_parametersStr {
#define CERT_DB_FMT "%scert%s.db"
#define KEY_DB_FMT "%skey%s.db"
+struct sftk_MACConstantTimeCtxStr {
+ const SECHashObject *hash;
+ unsigned char mac[64];
+ unsigned char secret[64];
+ unsigned int headerLength;
+ unsigned int secretLength;
+ unsigned int totalLength;
+ unsigned char header[75];
+};
+typedef struct sftk_MACConstantTimeCtxStr sftk_MACConstantTimeCtx;
+
+struct sftk_MACCtxStr {
+ /* This is a common MAC context that supports both HMAC and CMAC
+ * operations. This also presents a unified set of semantics:
+ *
+ * - Everything except Destroy returns a CK_RV, indicating success
+ * or failure. (This handles the difference between HMAC's and CMAC's
+ * interfaces, since the underlying AES _might_ fail with CMAC).
+ *
+ * - The underlying MAC is started on Init(...), so Update(...) can
+ * called right away. (This handles the difference between HMAC and
+ * CMAC in their *_Init(...) functions).
+ *
+ * - Calling semantics:
+ *
+ * - One of sftk_MAC_{Create,Init,InitRaw}(...) to set up the MAC
+ * context, checking the return code.
+ * - sftk_MAC_Update(...) as many times as necessary to process
+ * input data, checking the return code.
+ * - sftk_MAC_Finish(...) to get the output of the MAC; result_len
+ * may be NULL if the caller knows the expected output length,
+ * checking the return code. If result_len is NULL, this will
+ * PR_ASSERT(...) that the actual returned length was equal to
+ * max_result_len.
+ *
+ * Note: unlike HMAC_Finish(...), this allows the caller to specify
+ * a return value less than return length, to align with
+ * CMAC_Finish(...)'s semantics. This will force an additional
+ * stack allocation of size SFTK_MAX_MAC_LENGTH.
+ * - sftk_MAC_Reset(...) if the caller wishes to compute a new MAC
+ * with the same key, checking the return code.
+ * - sftk_MAC_Destroy(...) when the caller frees its associated
+ * memory, passing PR_TRUE if sftk_MAC_Create(...) was called,
+ * and PR_FALSE otherwise.
+ */
+
+ CK_MECHANISM_TYPE mech;
+ unsigned int mac_size;
+
+ union {
+ HMACContext *hmac;
+ CMACContext *cmac;
+
+ /* Functions to update when adding a new MAC or a new hash:
+ *
+ * - sftk_MAC_Init
+ * - sftk_MAC_Update
+ * - sftk_MAC_Finish
+ * - sftk_MAC_Reset
+ */
+ void *raw;
+ } mac;
+
+ void (*destroy_func)(void *ctx, PRBool free_it);
+};
+typedef struct sftk_MACCtxStr sftk_MACCtx;
+
SEC_BEGIN_PROTOS
/* shared functions between pkcs11.c and fipstokn.c */
@@ -766,17 +836,6 @@ extern CK_RV jpake_Final(HASH_HashType hashType,
SFTKObject *sourceKey, SFTKObject *key);
/* Constant time MAC functions (hmacct.c) */
-
-struct sftk_MACConstantTimeCtxStr {
- const SECHashObject *hash;
- unsigned char mac[64];
- unsigned char secret[64];
- unsigned int headerLength;
- unsigned int secretLength;
- unsigned int totalLength;
- unsigned char header[75];
-};
-typedef struct sftk_MACConstantTimeCtxStr sftk_MACConstantTimeCtx;
sftk_MACConstantTimeCtx *sftk_HMACConstantTime_New(
CK_MECHANISM_PTR mech, SFTKObject *key);
sftk_MACConstantTimeCtx *sftk_SSLv3MACConstantTime_New(
@@ -798,6 +857,16 @@ sftk_TLSPRFInit(SFTKSessionContext *context,
HASH_HashType hash_alg,
unsigned int out_len);
+/* PKCS#11 MAC implementation. See sftk_MACCtxStr declaration above for
+ * calling semantics for these functions. */
+CK_RV sftk_MAC_Create(CK_MECHANISM_TYPE mech, SFTKObject *key, sftk_MACCtx **ret_ctx);
+CK_RV sftk_MAC_Init(sftk_MACCtx *ctx, CK_MECHANISM_TYPE mech, SFTKObject *key);
+CK_RV sftk_MAC_InitRaw(sftk_MACCtx *ctx, CK_MECHANISM_TYPE mech, const unsigned char *key, unsigned int key_len, PRBool isFIPS);
+CK_RV sftk_MAC_Update(sftk_MACCtx *ctx, CK_BYTE_PTR data, unsigned int data_len);
+CK_RV sftk_MAC_Finish(sftk_MACCtx *ctx, CK_BYTE_PTR result, unsigned int *result_len, unsigned int max_result_len);
+CK_RV sftk_MAC_Reset(sftk_MACCtx *ctx);
+void sftk_MAC_Destroy(sftk_MACCtx *ctx, PRBool free_it);
+
SEC_END_PROTOS
#endif /* _PKCS11I_H_ */
diff --git a/lib/softoken/sftkhmac.c b/lib/softoken/sftkhmac.c
index be6344c70..617e6fd4e 100644
--- a/lib/softoken/sftkhmac.c
+++ b/lib/softoken/sftkhmac.c
@@ -9,12 +9,14 @@
#include "softoken.h"
#include "hmacct.h"
-/* MACMechanismToHash converts a PKCS#11 MAC mechanism into a freebl hash
+/* HMACMechanismToHash converts a PKCS#11 MAC mechanism into a freebl hash
* type. */
static HASH_HashType
-MACMechanismToHash(CK_MECHANISM_TYPE mech)
+HMACMechanismToHash(CK_MECHANISM_TYPE mech)
{
switch (mech) {
+ case CKM_MD2_HMAC:
+ return HASH_AlgMD2;
case CKM_MD5_HMAC:
case CKM_SSL3_MD5_MAC:
return HASH_AlgMD5;
@@ -48,7 +50,7 @@ SetupMAC(CK_MECHANISM_PTR mech, SFTKObject *key)
return NULL;
}
- alg = MACMechanismToHash(params->macAlg);
+ alg = HMACMechanismToHash(params->macAlg);
if (alg == HASH_AlgNULL) {
return NULL;
}
@@ -188,3 +190,284 @@ sftk_MACConstantTime_DestroyContext(void *pctx, PRBool free)
{
PORT_Free(pctx);
}
+
+CK_RV
+sftk_MAC_Create(CK_MECHANISM_TYPE mech, SFTKObject *key, sftk_MACCtx **ret_ctx)
+{
+ CK_RV ret;
+
+ if (ret_ctx == NULL || key == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+
+ *ret_ctx = PORT_New(sftk_MACCtx);
+ if (*ret_ctx == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+
+ ret = sftk_MAC_Init(*ret_ctx, mech, key);
+ if (ret != CKR_OK) {
+ sftk_MAC_Destroy(*ret_ctx, PR_TRUE);
+ }
+
+ return ret;
+}
+
+CK_RV
+sftk_MAC_Init(sftk_MACCtx *ctx, CK_MECHANISM_TYPE mech, SFTKObject *key)
+{
+ SFTKAttribute *keyval = NULL;
+ PRBool isFIPS = (key->slot->slotID == FIPS_SLOT_ID);
+ CK_RV ret = CKR_OK;
+
+ /* Find the actual value of the key. */
+ keyval = sftk_FindAttribute(key, CKA_VALUE);
+ if (keyval == NULL) {
+ ret = CKR_KEY_SIZE_RANGE;
+ goto done;
+ }
+
+ ret = sftk_MAC_InitRaw(ctx, mech,
+ (const unsigned char *)keyval->attrib.pValue,
+ keyval->attrib.ulValueLen, isFIPS);
+
+done:
+ sftk_FreeAttribute(keyval);
+ return ret;
+}
+
+CK_RV
+sftk_MAC_InitRaw(sftk_MACCtx *ctx, CK_MECHANISM_TYPE mech, const unsigned char *key, unsigned int key_len, PRBool isFIPS)
+{
+ const SECHashObject *hashObj = NULL;
+ CK_RV ret = CKR_OK;
+
+ if (ctx == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+
+ /* Clear the context before use. */
+ PORT_Memset(ctx, 0, sizeof(*ctx));
+
+ /* Save the mech. */
+ ctx->mech = mech;
+
+ /* Initialize the correct MAC context. */
+ switch (mech) {
+ case CKM_MD2_HMAC:
+ case CKM_MD5_HMAC:
+ case CKM_SHA_1_HMAC:
+ case CKM_SHA224_HMAC:
+ case CKM_SHA256_HMAC:
+ case CKM_SHA384_HMAC:
+ case CKM_SHA512_HMAC:
+ hashObj = HASH_GetRawHashObject(HMACMechanismToHash(mech));
+
+ /* Because we condition above only on hashes we know to be valid,
+ * hashObj should never be NULL. This assert is only useful when
+ * adding a new hash function (for which only partial support has
+ * been added); thus there is no need to turn it into an if and
+ * avoid the NULL dereference on the following line. */
+ PR_ASSERT(hashObj != NULL);
+ ctx->mac_size = hashObj->length;
+
+ goto hmac;
+ case CKM_AES_CMAC:
+ ctx->mac.cmac = CMAC_Create(CMAC_AES, key, key_len);
+ ctx->destroy_func = (void (*)(void *, PRBool))(&CMAC_Destroy);
+
+ /* Copy the behavior of sftk_doCMACInit here. */
+ if (ctx->mac.cmac == NULL) {
+ if (PORT_GetError() == SEC_ERROR_INVALID_ARGS) {
+ ret = CKR_KEY_SIZE_RANGE;
+ goto done;
+ }
+
+ ret = CKR_HOST_MEMORY;
+ goto done;
+ }
+
+ ctx->mac_size = AES_BLOCK_SIZE;
+
+ goto done;
+ default:
+ ret = CKR_MECHANISM_PARAM_INVALID;
+ goto done;
+ }
+
+hmac:
+ ctx->mac.hmac = HMAC_Create(hashObj, key, key_len, isFIPS);
+ ctx->destroy_func = (void (*)(void *, PRBool))(&HMAC_Destroy);
+
+ /* Copy the behavior of sftk_doHMACInit here. */
+ if (ctx->mac.hmac == NULL) {
+ if (PORT_GetError() == SEC_ERROR_INVALID_ARGS) {
+ ret = CKR_KEY_SIZE_RANGE;
+ goto done;
+ }
+ ret = CKR_HOST_MEMORY;
+ goto done;
+ }
+
+ /* Semantics: HMAC and CMAC should behave the same. Begin HMAC now. */
+ HMAC_Begin(ctx->mac.hmac);
+
+done:
+ /* Handle a failure: ctx->mac.raw should be NULL, but make sure
+ * destroy_func isn't set. */
+ if (ret != CKR_OK) {
+ ctx->destroy_func = NULL;
+ }
+
+ return ret;
+}
+
+CK_RV
+sftk_MAC_Reset(sftk_MACCtx *ctx)
+{
+ /* Useful for resetting the state of MAC prior to calling update again
+ *
+ * This lets the caller keep a single MAC instance and re-use it as long
+ * as the key stays the same. */
+ switch (ctx->mech) {
+ case CKM_MD2_HMAC:
+ case CKM_MD5_HMAC:
+ case CKM_SHA_1_HMAC:
+ case CKM_SHA224_HMAC:
+ case CKM_SHA256_HMAC:
+ case CKM_SHA384_HMAC:
+ case CKM_SHA512_HMAC:
+ HMAC_Begin(ctx->mac.hmac);
+ break;
+ case CKM_AES_CMAC:
+ if (CMAC_Begin(ctx->mac.cmac) != SECSuccess) {
+ return CKR_FUNCTION_FAILED;
+ }
+ break;
+ default:
+ /* This shouldn't happen -- asserting indicates partial support
+ * for a new MAC type. */
+ PR_ASSERT(PR_FALSE);
+ return CKR_FUNCTION_FAILED;
+ }
+
+ return CKR_OK;
+}
+
+CK_RV
+sftk_MAC_Update(sftk_MACCtx *ctx, CK_BYTE_PTR data, unsigned int data_len)
+{
+ switch (ctx->mech) {
+ case CKM_MD2_HMAC:
+ case CKM_MD5_HMAC:
+ case CKM_SHA_1_HMAC:
+ case CKM_SHA224_HMAC:
+ case CKM_SHA256_HMAC:
+ case CKM_SHA384_HMAC:
+ case CKM_SHA512_HMAC:
+ /* HMAC doesn't indicate failure in the return code. */
+ HMAC_Update(ctx->mac.hmac, data, data_len);
+ break;
+ case CKM_AES_CMAC:
+ /* CMAC indicates failure in the return code, however this is
+ * unlikely to occur. */
+ if (CMAC_Update(ctx->mac.cmac, data, data_len) != SECSuccess) {
+ return CKR_FUNCTION_FAILED;
+ }
+ break;
+ default:
+ /* This shouldn't happen -- asserting indicates partial support
+ * for a new MAC type. */
+ PR_ASSERT(PR_FALSE);
+ return CKR_FUNCTION_FAILED;
+ }
+ return CKR_OK;
+}
+
+CK_RV
+sftk_MAC_Finish(sftk_MACCtx *ctx, CK_BYTE_PTR result, unsigned int *result_len, unsigned int max_result_len)
+{
+ unsigned int actual_result_len;
+
+ switch (ctx->mech) {
+ case CKM_MD2_HMAC:
+ case CKM_MD5_HMAC:
+ case CKM_SHA_1_HMAC:
+ case CKM_SHA224_HMAC:
+ case CKM_SHA256_HMAC:
+ case CKM_SHA384_HMAC:
+ case CKM_SHA512_HMAC:
+ /* HMAC doesn't indicate failure in the return code. Additionally,
+ * unlike CMAC, it doesn't support partial results. This means that we
+ * need to allocate a buffer if max_result_len < ctx->mac_size. */
+ if (max_result_len >= ctx->mac_size) {
+ /* Split this into two calls to avoid an unnecessary stack
+ * allocation and memcpy when possible. */
+ HMAC_Finish(ctx->mac.hmac, result, &actual_result_len, max_result_len);
+ } else {
+ uint8_t tmp_buffer[SFTK_MAX_MAC_LENGTH];
+
+ /* Assumption: buffer is large enough to hold this HMAC's
+ * output. */
+ PR_ASSERT(SFTK_MAX_MAC_LENGTH >= ctx->mac_size);
+
+ HMAC_Finish(ctx->mac.hmac, tmp_buffer, &actual_result_len, SFTK_MAX_MAC_LENGTH);
+
+ if (actual_result_len > max_result_len) {
+ /* This should always be true since:
+ *
+ * (SFTK_MAX_MAC_LENGTH >= ctx->mac_size =
+ * actual_result_len) > max_result_len,
+ *
+ * but guard this truncation just in case. */
+ actual_result_len = max_result_len;
+ }
+
+ PORT_Memcpy(result, tmp_buffer, actual_result_len);
+ }
+ break;
+ case CKM_AES_CMAC:
+ /* CMAC indicates failure in the return code, however this is
+ * unlikely to occur. */
+ if (CMAC_Finish(ctx->mac.cmac, result, &actual_result_len, max_result_len) != SECSuccess) {
+ return CKR_FUNCTION_FAILED;
+ }
+ break;
+ default:
+ /* This shouldn't happen -- asserting indicates partial support
+ * for a new MAC type. */
+ PR_ASSERT(PR_FALSE);
+ return CKR_FUNCTION_FAILED;
+ }
+
+ if (result_len) {
+ /* When result length is passed, inform the caller of its value. */
+ *result_len = actual_result_len;
+ } else if (max_result_len == ctx->mac_size) {
+ /* Validate that the amount requested was what was actually given; the
+ * caller assumes that what they passed was the output size of the
+ * underlying MAC and that they got all the bytes the asked for. */
+ PR_ASSERT(actual_result_len == max_result_len);
+ }
+
+ return CKR_OK;
+}
+
+void
+sftk_MAC_Destroy(sftk_MACCtx *ctx, PRBool free_it)
+{
+ if (ctx == NULL) {
+ return;
+ }
+
+ if (ctx->mac.raw != NULL && ctx->destroy_func != NULL) {
+ ctx->destroy_func(ctx->mac.raw, PR_TRUE);
+ }
+
+ /* Clean up the struct so we don't double free accidentally. */
+ PORT_Memset(ctx, 0, sizeof(sftk_MACCtx));
+
+ if (free_it == PR_TRUE) {
+ PORT_Free(ctx);
+ }
+}