diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /security/nss/lib/pk11wrap/pk11cxt.c | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | security/nss/lib/pk11wrap/pk11cxt.c | 1795 |
1 files changed, 1795 insertions, 0 deletions
diff --git a/security/nss/lib/pk11wrap/pk11cxt.c b/security/nss/lib/pk11wrap/pk11cxt.c new file mode 100644 index 0000000000..0f170c352b --- /dev/null +++ b/security/nss/lib/pk11wrap/pk11cxt.c @@ -0,0 +1,1795 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* + * This file PK11Contexts which are used in multipart hashing, + * encryption/decryption, and signing/verication operations. + */ + +#include "seccomon.h" +#include "secmod.h" +#include "nssilock.h" +#include "secmodi.h" +#include "secmodti.h" +#include "pkcs11.h" +#include "pk11func.h" +#include "secitem.h" +#include "secoid.h" +#include "sechash.h" +#include "secerr.h" +#include "blapit.h" +#include "secport.h" + +static const SECItem pk11_null_params = { 0 }; + +/********************************************************************** + * + * Now Deal with Crypto Contexts + * + **********************************************************************/ + +/* + * the monitors... + */ +void +PK11_EnterContextMonitor(PK11Context *cx) +{ + /* if we own the session and our slot is ThreadSafe, only monitor + * the Context */ + if ((cx->ownSession) && (cx->slot->isThreadSafe)) { + /* Should this use monitors instead? */ + PZ_Lock(cx->sessionLock); + } else { + PK11_EnterSlotMonitor(cx->slot); + } +} + +void +PK11_ExitContextMonitor(PK11Context *cx) +{ + /* if we own the session and our slot is ThreadSafe, only monitor + * the Context */ + if ((cx->ownSession) && (cx->slot->isThreadSafe)) { + /* Should this use monitors instead? */ + PZ_Unlock(cx->sessionLock); + } else { + PK11_ExitSlotMonitor(cx->slot); + } +} + +/* + * Free up a Cipher Context + */ +void +PK11_DestroyContext(PK11Context *context, PRBool freeit) +{ + pk11_CloseSession(context->slot, context->session, context->ownSession); + /* initialize the critical fields of the context */ + if (context->savedData != NULL) + PORT_Free(context->savedData); + if (context->key) + PK11_FreeSymKey(context->key); + if (context->param && context->param != &pk11_null_params) + SECITEM_FreeItem(context->param, PR_TRUE); + if (context->sessionLock) + PZ_DestroyLock(context->sessionLock); + PK11_FreeSlot(context->slot); + if (freeit) + PORT_Free(context); +} + +/* + * save the current context. Allocate Space if necessary. + */ +static unsigned char * +pk11_saveContextHelper(PK11Context *context, unsigned char *buffer, + unsigned long *savedLength) +{ + CK_RV crv; + + /* If buffer is NULL, this will get the length */ + crv = PK11_GETTAB(context->slot)->C_GetOperationState(context->session, (CK_BYTE_PTR)buffer, savedLength); + if (!buffer || (crv == CKR_BUFFER_TOO_SMALL)) { + /* the given buffer wasn't big enough (or was NULL), but we + * have the length, so try again with a new buffer and the + * correct length + */ + unsigned long bufLen = *savedLength; + buffer = PORT_Alloc(bufLen); + if (buffer == NULL) { + return (unsigned char *)NULL; + } + crv = PK11_GETTAB(context->slot)->C_GetOperationState(context->session, (CK_BYTE_PTR)buffer, savedLength); + if (crv != CKR_OK) { + PORT_ZFree(buffer, bufLen); + } + } + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError(crv)); + return (unsigned char *)NULL; + } + return buffer; +} + +void * +pk11_saveContext(PK11Context *context, void *space, unsigned long *savedLength) +{ + return pk11_saveContextHelper(context, + (unsigned char *)space, savedLength); +} + +/* + * restore the current context + */ +SECStatus +pk11_restoreContext(PK11Context *context, void *space, unsigned long savedLength) +{ + CK_RV crv; + CK_OBJECT_HANDLE objectID = context->objectID; + + PORT_Assert(space != NULL); + if (space == NULL) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + crv = PK11_GETTAB(context->slot)->C_SetOperationState(context->session, (CK_BYTE_PTR)space, savedLength, objectID, 0); + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError(crv)); + return SECFailure; + } + return SECSuccess; +} + +SECStatus pk11_Finalize(PK11Context *context); + +/* + * Initialize a Message function. Particular function is passed in as a + * function pointer. Since all C_Message*Init funcitons have the same + * prototype, we just pick one of the the prototypes to declare our init + * function. + */ +static CK_RV +pk11_contextInitMessage(PK11Context *context, CK_MECHANISM_PTR mech, + CK_C_MessageEncryptInit initFunc, + CK_FLAGS flags, CK_RV scrv) +{ + PK11SlotInfo *slot = context->slot; + CK_VERSION version = slot->module->cryptokiVersion; + CK_RV crv = CKR_OK; + + context->ivCounter = 0; + context->ivMaxCount = 0; + context->ivFixedBits = 0; + context->ivLen = 0; + context->ivGen = CKG_NO_GENERATE; + context->simulate_mechanism = (mech)->mechanism; + context->simulate_message = PR_FALSE; + /* check that we can do the Message interface. We need to check + * for either 1) are we using a PKCS #11 v3 interface and 2) is the + * Message flag set on the mechanism. If either is false we simulate + * the message interface for the Encrypt and Decrypt cases using the + * PKCS #11 V2 interface. + * Sign and verify do not have V2 interfaces, so we go ahead and fail + * if those cases */ + if ((version.major >= 3) && + PK11_DoesMechanismFlag(slot, (mech)->mechanism, flags)) { + PK11_EnterContextMonitor(context); + crv = (*initFunc)((context)->session, (mech), (context)->objectID); + PK11_ExitContextMonitor(context); + if ((crv == CKR_FUNCTION_NOT_SUPPORTED) || + (crv == CKR_MECHANISM_INVALID)) { + /* we have a 3.0 interface, and the flag was set (or ignored) + * but the implementation was not there, use the V2 interface */ + crv = (scrv); + context->simulate_message = PR_TRUE; + } + } else { + crv = (scrv); + context->simulate_message = PR_TRUE; + } + return crv; +} + +/* + * Context initialization. Used by all flavors of CreateContext + */ +static SECStatus +pk11_context_init(PK11Context *context, CK_MECHANISM *mech_info) +{ + CK_RV crv; + SECStatus rv = SECSuccess; + + context->simulate_message = PR_FALSE; + switch (context->operation) { + case CKA_ENCRYPT: + PK11_EnterContextMonitor(context); + crv = PK11_GETTAB(context->slot)->C_EncryptInit(context->session, mech_info, context->objectID); + PK11_ExitContextMonitor(context); + break; + case CKA_DECRYPT: + PK11_EnterContextMonitor(context); + if (context->fortezzaHack) { + CK_ULONG count = 0; + /* generate the IV for fortezza */ + crv = PK11_GETTAB(context->slot)->C_EncryptInit(context->session, mech_info, context->objectID); + if (crv != CKR_OK) { + PK11_ExitContextMonitor(context); + break; + } + PK11_GETTAB(context->slot) + ->C_EncryptFinal(context->session, + NULL, &count); + } + crv = PK11_GETTAB(context->slot)->C_DecryptInit(context->session, mech_info, context->objectID); + PK11_ExitContextMonitor(context); + break; + case CKA_SIGN: + PK11_EnterContextMonitor(context); + crv = PK11_GETTAB(context->slot)->C_SignInit(context->session, mech_info, context->objectID); + PK11_ExitContextMonitor(context); + break; + case CKA_VERIFY: + /* NOTE: we previously has this set to C_SignInit for Macing. + * It turns out now one could possibly use it that way, though, + * because PK11_HashOp() always called C_VerifyUpdate on CKA_VERIFY, + * which would have failed. So everyone just calls us with CKA_SIGN + * when Macing even when they are verifying, no need to 'do it + * for them'. It needs to be VerifyInit now so that we can do + * PKCS #11 hash/Verify combo operations. */ + PK11_EnterContextMonitor(context); + crv = PK11_GETTAB(context->slot)->C_VerifyInit(context->session, mech_info, context->objectID); + PK11_ExitContextMonitor(context); + break; + case CKA_DIGEST: + PK11_EnterContextMonitor(context); + crv = PK11_GETTAB(context->slot)->C_DigestInit(context->session, mech_info); + PK11_ExitContextMonitor(context); + break; + + case CKA_NSS_MESSAGE | CKA_ENCRYPT: + crv = pk11_contextInitMessage(context, mech_info, + PK11_GETTAB(context->slot)->C_MessageEncryptInit, + CKF_MESSAGE_ENCRYPT, CKR_OK); + break; + case CKA_NSS_MESSAGE | CKA_DECRYPT: + crv = pk11_contextInitMessage(context, mech_info, + PK11_GETTAB(context->slot)->C_MessageDecryptInit, + CKF_MESSAGE_DECRYPT, CKR_OK); + break; + case CKA_NSS_MESSAGE | CKA_SIGN: + crv = pk11_contextInitMessage(context, mech_info, + PK11_GETTAB(context->slot)->C_MessageSignInit, + CKF_MESSAGE_SIGN, CKR_FUNCTION_NOT_SUPPORTED); + break; + case CKA_NSS_MESSAGE | CKA_VERIFY: + crv = pk11_contextInitMessage(context, mech_info, + PK11_GETTAB(context->slot)->C_MessageVerifyInit, + CKF_MESSAGE_VERIFY, CKR_FUNCTION_NOT_SUPPORTED); + break; + default: + crv = CKR_OPERATION_NOT_INITIALIZED; + break; + } + + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError(crv)); + return SECFailure; + } + + /* handle the case where the token is using the old NSS mechanism */ + if (context->simulate_message && + !PK11_DoesMechanism(context->slot, context->simulate_mechanism)) { + if ((context->simulate_mechanism == CKM_CHACHA20_POLY1305) && + PK11_DoesMechanism(context->slot, CKM_NSS_CHACHA20_POLY1305)) { + context->simulate_mechanism = CKM_NSS_CHACHA20_POLY1305; + } else { + PORT_SetError(PK11_MapError(CKR_MECHANISM_INVALID)); + return SECFailure; + } + } + + /* + * handle session starvation case.. use our last session to multiplex + */ + if (!context->ownSession) { + PK11_EnterContextMonitor(context); + context->savedData = pk11_saveContext(context, context->savedData, + &context->savedLength); + if (context->savedData == NULL) + rv = SECFailure; + /* clear out out session for others to use */ + pk11_Finalize(context); + PK11_ExitContextMonitor(context); + } + return rv; +} + +/* + * Testing interfaces, not for general use. This function forces + * an AEAD context into simulation mode even though the target token + * can already do PKCS #11 v3.0 Message (i.e. softoken). + */ +SECStatus +_PK11_ContextSetAEADSimulation(PK11Context *context) +{ + CK_RV crv; + /* only message encrypt and message decrypt contexts can be simulated */ + if ((context->operation != (CKA_NSS_MESSAGE | CKA_ENCRYPT)) && + (context->operation != (CKA_NSS_MESSAGE | CKA_DECRYPT))) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + /* if we are already simulating, return */ + if (context->simulate_message) { + return SECSuccess; + } + /* we need to shutdown the existing AEAD operation */ + switch (context->operation) { + case CKA_NSS_MESSAGE | CKA_ENCRYPT: + crv = PK11_GETTAB(context->slot)->C_MessageEncryptFinal(context->session); + break; + case CKA_NSS_MESSAGE | CKA_DECRYPT: + crv = PK11_GETTAB(context->slot)->C_MessageDecryptFinal(context->session); + break; + default: + PORT_SetError(SEC_ERROR_NOT_INITIALIZED); + return SECFailure; + } + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError(crv)); + return SECFailure; + } + context->simulate_message = PR_TRUE; + return SECSuccess; +} + +PRBool +_PK11_ContextGetAEADSimulation(PK11Context *context) +{ + return context->simulate_message; +} + +/* + * Common Helper Function do come up with a new context. + */ +static PK11Context * +pk11_CreateNewContextInSlot(CK_MECHANISM_TYPE type, + PK11SlotInfo *slot, CK_ATTRIBUTE_TYPE operation, + PK11SymKey *symKey, CK_OBJECT_HANDLE objectID, + const SECItem *param, void *pwArg) +{ + CK_MECHANISM mech_info; + PK11Context *context; + SECStatus rv; + + PORT_Assert(slot != NULL); + if (!slot || ((objectID == CK_INVALID_HANDLE) && ((operation != CKA_DIGEST) || + (type == CKM_SKIPJACK_CBC64)))) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + context = (PK11Context *)PORT_Alloc(sizeof(PK11Context)); + if (context == NULL) { + return NULL; + } + + /* now deal with the fortezza hack... the fortezza hack is an attempt + * to get around the issue of the card not allowing you to do a FORTEZZA + * LoadIV/Encrypt, which was added because such a combination could be + * use to circumvent the key escrow system. Unfortunately SSL needs to + * do this kind of operation, so in SSL we do a loadIV (to verify it), + * Then GenerateIV, and through away the first 8 bytes on either side + * of the connection.*/ + context->fortezzaHack = PR_FALSE; + if (type == CKM_SKIPJACK_CBC64) { + if (symKey && (symKey->origin == PK11_OriginFortezzaHack)) { + context->fortezzaHack = PR_TRUE; + } + } + + /* initialize the critical fields of the context */ + context->operation = operation; + /* If we were given a symKey, keep our own reference to it so + * that the key doesn't disappear in the middle of the operation + * if the caller frees it. Public and Private keys are not reference + * counted, so the caller just has to keep his copies around until + * the operation completes */ + context->key = symKey ? PK11_ReferenceSymKey(symKey) : NULL; + context->objectID = objectID; + context->slot = PK11_ReferenceSlot(slot); + context->session = pk11_GetNewSession(slot, &context->ownSession); + context->pwArg = pwArg; + /* get our session */ + context->savedData = NULL; + + /* save the parameters so that some digesting stuff can do multiple + * begins on a single context */ + context->type = type; + if (param) { + if (param->len > 0) { + context->param = SECITEM_DupItem(param); + } else { + context->param = (SECItem *)&pk11_null_params; + } + } else { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + context->param = NULL; + } + context->init = PR_FALSE; + context->sessionLock = PZ_NewLock(nssILockPK11cxt); + if ((context->param == NULL) || (context->sessionLock == NULL)) { + PK11_DestroyContext(context, PR_TRUE); + return NULL; + } + + mech_info.mechanism = type; + mech_info.pParameter = param->data; + mech_info.ulParameterLen = param->len; + rv = pk11_context_init(context, &mech_info); + + if (rv != SECSuccess) { + PK11_DestroyContext(context, PR_TRUE); + return NULL; + } + context->init = PR_TRUE; + return context; +} + +/* + * put together the various PK11_Create_Context calls used by different + * parts of libsec. + */ +PK11Context * +__PK11_CreateContextByRawKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, + PK11Origin origin, CK_ATTRIBUTE_TYPE operation, SECItem *key, + SECItem *param, void *wincx) +{ + PK11SymKey *symKey = NULL; + PK11Context *context = NULL; + + /* first get a slot */ + if (slot == NULL) { + slot = PK11_GetBestSlot(type, wincx); + if (slot == NULL) { + PORT_SetError(SEC_ERROR_NO_MODULE); + goto loser; + } + } else { + PK11_ReferenceSlot(slot); + } + + /* now import the key */ + symKey = PK11_ImportSymKey(slot, type, origin, operation, key, wincx); + if (symKey == NULL) + goto loser; + + context = PK11_CreateContextBySymKey(type, operation, symKey, param); + +loser: + if (symKey) { + PK11_FreeSymKey(symKey); + } + if (slot) { + PK11_FreeSlot(slot); + } + + return context; +} + +PK11Context * +PK11_CreateContextByRawKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, + PK11Origin origin, CK_ATTRIBUTE_TYPE operation, SECItem *key, + SECItem *param, void *wincx) +{ + return __PK11_CreateContextByRawKey(slot, type, origin, operation, + key, param, wincx); +} + +/* + * Create a context from a key. We really should make sure we aren't using + * the same key in multiple sessions! + */ +PK11Context * +PK11_CreateContextBySymKey(CK_MECHANISM_TYPE type, CK_ATTRIBUTE_TYPE operation, + PK11SymKey *symKey, const SECItem *param) +{ + PK11SymKey *newKey; + PK11Context *context; + + /* if this slot doesn't support the mechanism, go to a slot that does */ + newKey = pk11_ForceSlot(symKey, type, operation); + if (newKey == NULL) { + PK11_ReferenceSymKey(symKey); + } else { + symKey = newKey; + } + + /* Context keeps its reference to the symKey, so it's safe to + * free our reference we we are through, even though we may have + * created the key using pk11_ForceSlot. */ + context = pk11_CreateNewContextInSlot(type, symKey->slot, operation, symKey, + symKey->objectID, param, symKey->cx); + PK11_FreeSymKey(symKey); + return context; +} + +/* To support multipart public key operations (like hash/verify operations), + * we need to create contexts with public keys. */ +PK11Context * +PK11_CreateContextByPubKey(CK_MECHANISM_TYPE type, CK_ATTRIBUTE_TYPE operation, + SECKEYPublicKey *pubKey, const SECItem *param, + void *pwArg) +{ + PK11SlotInfo *slot = pubKey->pkcs11Slot; + SECItem nullparam = { 0, 0, 0 }; + + /* if this slot doesn't support the mechanism, go to a slot that does */ + /* public keys have all their data in the public key data structure, + * so there's no need to export the old key, just import this one. The + * import manages consistancy of the public key data structure */ + if (slot == NULL || !PK11_DoesMechanism(slot, type)) { + CK_OBJECT_HANDLE objectID; + slot = PK11_GetBestSlot(type, NULL); + if (slot == NULL) { + return NULL; + } + objectID = PK11_ImportPublicKey(slot, pubKey, PR_FALSE); + PK11_FreeSlot(slot); + if (objectID == CK_INVALID_HANDLE) { + return NULL; + } + } + + /* unlike symkeys, we accept a NULL parameter. map a null parameter + * to the empty parameter. This matches the semantics of + * PK11_VerifyWithMechanism */ + return pk11_CreateNewContextInSlot(type, pubKey->pkcs11Slot, operation, + NULL, pubKey->pkcs11ID, + param ? param : &nullparam, pwArg); +} + +/* To support multipart private key operations (like hash/sign operations), + * we need to create contexts with private keys. */ +PK11Context * +PK11_CreateContextByPrivKey(CK_MECHANISM_TYPE type, CK_ATTRIBUTE_TYPE operation, + SECKEYPrivateKey *privKey, const SECItem *param) +{ + SECItem nullparam = { 0, 0, 0 }; + /* Private keys are generally not movable. If the token the + * private key lives on can't do the operation, generally we are + * stuck anyway. So no need to try to manipulate the key into + * another token */ + + /* if this slot doesn't support the mechanism, go to a slot that does */ + /* unlike symkeys, we accept a NULL parameter. map a null parameter + * to the empty parameter. This matches the semantics of + * PK11_SignWithMechanism */ + return pk11_CreateNewContextInSlot(type, privKey->pkcs11Slot, operation, + NULL, privKey->pkcs11ID, + param ? param : &nullparam, + privKey->wincx); +} + +/* + * Digest contexts don't need keys, but the do need to find a slot. + * Macing should use PK11_CreateContextBySymKey. + */ +PK11Context * +PK11_CreateDigestContext(SECOidTag hashAlg) +{ + /* digesting has to work without authentication to the slot */ + CK_MECHANISM_TYPE type; + PK11SlotInfo *slot; + PK11Context *context; + SECItem param; + + type = PK11_AlgtagToMechanism(hashAlg); + slot = PK11_GetBestSlot(type, NULL); + if (slot == NULL) { + PORT_SetError(SEC_ERROR_NO_MODULE); + return NULL; + } + + /* maybe should really be PK11_GenerateNewParam?? */ + param.data = NULL; + param.len = 0; + param.type = 0; + + context = pk11_CreateNewContextInSlot(type, slot, CKA_DIGEST, NULL, + CK_INVALID_HANDLE, ¶m, NULL); + PK11_FreeSlot(slot); + return context; +} + +/* + * create a new context which is the clone of the state of old context. + */ +PK11Context * +PK11_CloneContext(PK11Context *old) +{ + PK11Context *newcx; + PRBool needFree = PR_FALSE; + SECStatus rv = SECSuccess; + void *data; + unsigned long len; + + newcx = pk11_CreateNewContextInSlot(old->type, old->slot, old->operation, + old->key, old->objectID, old->param, + old->pwArg); + if (newcx == NULL) + return NULL; + + /* now clone the save state. First we need to find the save state + * of the old session. If the old context owns it's session, + * the state needs to be saved, otherwise the state is in saveData. */ + if (old->ownSession) { + PK11_EnterContextMonitor(old); + data = pk11_saveContext(old, NULL, &len); + PK11_ExitContextMonitor(old); + needFree = PR_TRUE; + } else { + data = old->savedData; + len = old->savedLength; + } + + if (data == NULL) { + PK11_DestroyContext(newcx, PR_TRUE); + return NULL; + } + + /* now copy that state into our new context. Again we have different + * work if the new context owns it's own session. If it does, we + * restore the state gathered above. If it doesn't, we copy the + * saveData pointer... */ + if (newcx->ownSession) { + PK11_EnterContextMonitor(newcx); + rv = pk11_restoreContext(newcx, data, len); + PK11_ExitContextMonitor(newcx); + } else { + PORT_Assert(newcx->savedData != NULL); + if ((newcx->savedData == NULL) || (newcx->savedLength < len)) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + rv = SECFailure; + } else { + PORT_Memcpy(newcx->savedData, data, len); + newcx->savedLength = len; + } + } + + if (needFree) + PORT_Free(data); + + if (rv != SECSuccess) { + PK11_DestroyContext(newcx, PR_TRUE); + return NULL; + } + return newcx; +} + +/* + * save the current context state into a variable. Required to make FORTEZZA + * work. + */ +SECStatus +PK11_SaveContext(PK11Context *cx, unsigned char *save, int *len, int saveLength) +{ + unsigned char *data = NULL; + CK_ULONG length = saveLength; + + if (cx->ownSession) { + PK11_EnterContextMonitor(cx); + data = pk11_saveContextHelper(cx, save, &length); + PK11_ExitContextMonitor(cx); + if (data) + *len = length; + } else if ((unsigned)saveLength >= cx->savedLength) { + data = (unsigned char *)cx->savedData; + if (cx->savedData) { + PORT_Memcpy(save, cx->savedData, cx->savedLength); + } + *len = cx->savedLength; + } + if (data != NULL) { + if (cx->ownSession) { + PORT_ZFree(data, length); + } + return SECSuccess; + } else { + return SECFailure; + } +} + +/* same as above, but may allocate the return buffer. */ +unsigned char * +PK11_SaveContextAlloc(PK11Context *cx, + unsigned char *preAllocBuf, unsigned int pabLen, + unsigned int *stateLen) +{ + unsigned char *stateBuf = NULL; + unsigned long length = (unsigned long)pabLen; + + if (cx->ownSession) { + PK11_EnterContextMonitor(cx); + stateBuf = pk11_saveContextHelper(cx, preAllocBuf, &length); + PK11_ExitContextMonitor(cx); + *stateLen = (stateBuf != NULL) ? length : 0; + } else { + if (pabLen < cx->savedLength) { + stateBuf = (unsigned char *)PORT_Alloc(cx->savedLength); + if (!stateBuf) { + return (unsigned char *)NULL; + } + } else { + stateBuf = preAllocBuf; + } + if (cx->savedData) { + PORT_Memcpy(stateBuf, cx->savedData, cx->savedLength); + } + *stateLen = cx->savedLength; + } + return stateBuf; +} + +/* + * restore the context state into a new running context. Also required for + * FORTEZZA . + */ +SECStatus +PK11_RestoreContext(PK11Context *cx, unsigned char *save, int len) +{ + SECStatus rv = SECSuccess; + if (cx->ownSession) { + PK11_EnterContextMonitor(cx); + pk11_Finalize(cx); + rv = pk11_restoreContext(cx, save, len); + PK11_ExitContextMonitor(cx); + } else { + PORT_Assert(cx->savedData != NULL); + if ((cx->savedData == NULL) || (cx->savedLength < (unsigned)len)) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + rv = SECFailure; + } else { + PORT_Memcpy(cx->savedData, save, len); + cx->savedLength = len; + } + } + return rv; +} + +/* + * This is to get FIPS compliance until we can convert + * libjar to use PK11_ hashing functions. It returns PR_FALSE + * if we can't get a PK11 Context. + */ +PRBool +PK11_HashOK(SECOidTag algID) +{ + PK11Context *cx; + + cx = PK11_CreateDigestContext(algID); + if (cx == NULL) + return PR_FALSE; + PK11_DestroyContext(cx, PR_TRUE); + return PR_TRUE; +} + +/* + * start a new digesting or Mac'ing operation on this context + */ +SECStatus +PK11_DigestBegin(PK11Context *cx) +{ + CK_MECHANISM mech_info; + SECStatus rv; + + if (cx->init == PR_TRUE) { + return SECSuccess; + } + + /* + * make sure the old context is clear first + */ + PK11_EnterContextMonitor(cx); + pk11_Finalize(cx); + PK11_ExitContextMonitor(cx); + + mech_info.mechanism = cx->type; + mech_info.pParameter = cx->param->data; + mech_info.ulParameterLen = cx->param->len; + rv = pk11_context_init(cx, &mech_info); + + if (rv != SECSuccess) { + return SECFailure; + } + cx->init = PR_TRUE; + return SECSuccess; +} + +SECStatus +PK11_HashBuf(SECOidTag hashAlg, unsigned char *out, const unsigned char *in, + PRInt32 len) +{ + PK11Context *context; + unsigned int max_length; + unsigned int out_length; + SECStatus rv; + + /* len will be passed to PK11_DigestOp as unsigned. */ + if (len < 0) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + context = PK11_CreateDigestContext(hashAlg); + if (context == NULL) + return SECFailure; + + rv = PK11_DigestBegin(context); + if (rv != SECSuccess) { + PK11_DestroyContext(context, PR_TRUE); + return rv; + } + + rv = PK11_DigestOp(context, in, len); + if (rv != SECSuccess) { + PK11_DestroyContext(context, PR_TRUE); + return rv; + } + + /* XXX This really should have been an argument to this function! */ + max_length = HASH_ResultLenByOidTag(hashAlg); + PORT_Assert(max_length); + if (!max_length) + max_length = HASH_LENGTH_MAX; + + rv = PK11_DigestFinal(context, out, &out_length, max_length); + PK11_DestroyContext(context, PR_TRUE); + return rv; +} + +/* + * execute a bulk encryption operation + */ +SECStatus +PK11_CipherOp(PK11Context *context, unsigned char *out, int *outlen, + int maxout, const unsigned char *in, int inlen) +{ + CK_RV crv = CKR_OK; + CK_ULONG length = maxout; + CK_ULONG offset = 0; + SECStatus rv = SECSuccess; + unsigned char *saveOut = out; + unsigned char *allocOut = NULL; + + /* if we ran out of session, we need to restore our previously stored + * state. + */ + PK11_EnterContextMonitor(context); + if (!context->ownSession) { + rv = pk11_restoreContext(context, context->savedData, + context->savedLength); + if (rv != SECSuccess) { + PK11_ExitContextMonitor(context); + return rv; + } + } + + /* + * The fortezza hack is to send 8 extra bytes on the first encrypted and + * lose them on the first decrypt. + */ + if (context->fortezzaHack) { + unsigned char random[8]; + if (context->operation == CKA_ENCRYPT) { + PK11_ExitContextMonitor(context); + rv = PK11_GenerateRandom(random, sizeof(random)); + PK11_EnterContextMonitor(context); + + /* since we are offseting the output, we can't encrypt back into + * the same buffer... allocate a temporary buffer just for this + * call. */ + allocOut = out = (unsigned char *)PORT_Alloc(maxout); + if (out == NULL) { + PK11_ExitContextMonitor(context); + return SECFailure; + } + crv = PK11_GETTAB(context->slot)->C_EncryptUpdate(context->session, random, sizeof(random), out, &length); + + out += length; + maxout -= length; + offset = length; + } else if (context->operation == CKA_DECRYPT) { + length = sizeof(random); + crv = PK11_GETTAB(context->slot)->C_DecryptUpdate(context->session, (CK_BYTE_PTR)in, sizeof(random), random, &length); + inlen -= length; + in += length; + context->fortezzaHack = PR_FALSE; + } + } + + switch (context->operation) { + case CKA_ENCRYPT: + length = maxout; + crv = PK11_GETTAB(context->slot)->C_EncryptUpdate(context->session, (CK_BYTE_PTR)in, inlen, out, &length); + length += offset; + break; + case CKA_DECRYPT: + length = maxout; + crv = PK11_GETTAB(context->slot)->C_DecryptUpdate(context->session, (CK_BYTE_PTR)in, inlen, out, &length); + break; + default: + crv = CKR_OPERATION_NOT_INITIALIZED; + break; + } + + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError(crv)); + *outlen = 0; + rv = SECFailure; + } else { + *outlen = length; + } + + if (context->fortezzaHack) { + if (context->operation == CKA_ENCRYPT) { + PORT_Assert(allocOut); + PORT_Memcpy(saveOut, allocOut, length); + PORT_Free(allocOut); + } + context->fortezzaHack = PR_FALSE; + } + + /* + * handle session starvation case.. use our last session to multiplex + */ + if (!context->ownSession) { + context->savedData = pk11_saveContext(context, context->savedData, + &context->savedLength); + if (context->savedData == NULL) + rv = SECFailure; + + /* clear out out session for others to use */ + pk11_Finalize(context); + } + PK11_ExitContextMonitor(context); + return rv; +} + +/* + * Simulate the IV generation that normally would happen in the token. + * + * This is a modifed copy of what is in freebl/gcm.c. We can't use the + * version in freebl because of layering, since freebl is inside the token + * boundary. These issues are traditionally handled by moving them to util, + * but we also have two different Random functions we have two switch between. + * Since this is primarily here for tokens that don't support the PKCS #11 + * Message Interface, it's OK if they diverge a bit. Slight semantic + * differences from the freebl/gcm.c version shouldn't be much more than the + * sematic differences between freebl and other tokens which do implement the + * Message Interface. */ +static SECStatus +pk11_GenerateIV(PK11Context *context, CK_GENERATOR_FUNCTION ivgen, + int fixedBits, unsigned char *iv, int ivLen) +{ + unsigned int i; + unsigned int flexBits; + unsigned int ivOffset; + unsigned int ivNewCount; + unsigned char ivMask; + unsigned char ivSave; + SECStatus rv; + + if (context->ivCounter != 0) { + /* If we've already generated a message, make sure all subsequent + * messages are using the same generator */ + if ((context->ivGen != ivgen) || + (context->ivFixedBits != fixedBits) || + (context->ivLen != ivLen)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + } else { + /* remember these values */ + context->ivGen = ivgen; + context->ivFixedBits = fixedBits; + context->ivLen = ivLen; + /* now calculate how may bits of IV we have to supply */ + flexBits = ivLen * PR_BITS_PER_BYTE; + /* first make sure we aren't going to overflow */ + if (flexBits < fixedBits) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + flexBits -= fixedBits; + /* if we are generating a random number reduce the acceptable bits to + * avoid birthday attacks */ + if (ivgen == CKG_GENERATE_RANDOM) { + if (flexBits <= GCMIV_RANDOM_BIRTHDAY_BITS) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + /* see freebl/blapit.h for how GCMIV_RANDOM_BIRTHDAY_BITS is + * calculated. */ + flexBits -= GCMIV_RANDOM_BIRTHDAY_BITS; + flexBits = flexBits >> 1; + } + if (flexBits == 0) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + /* Turn those bits into the number of IV's we can safely return */ + if (flexBits >= sizeof(context->ivMaxCount) * PR_BITS_PER_BYTE) { + context->ivMaxCount = PR_UINT64(0xffffffffffffffff); + } else { + context->ivMaxCount = (PR_UINT64(1) << flexBits); + } + } + + /* no generate, accept the IV from the source */ + if (ivgen == CKG_NO_GENERATE) { + context->ivCounter = 1; + return SECSuccess; + } + + /* make sure we haven't exceeded the number of IVs we can return + * for this key, generator, and IV size */ + if (context->ivCounter >= context->ivMaxCount) { + /* use a unique error from just bad user input */ + PORT_SetError(SEC_ERROR_EXTRA_INPUT); + return SECFailure; + } + + /* build to mask to handle the first byte of the IV */ + ivOffset = fixedBits / PR_BITS_PER_BYTE; + ivMask = 0xff >> ((PR_BITS_PER_BYTE - (fixedBits & 7)) & 7); + ivNewCount = ivLen - ivOffset; + + /* finally generate the IV */ + switch (ivgen) { + case CKG_GENERATE: /* default to counter */ + case CKG_GENERATE_COUNTER: + iv[ivOffset] = (iv[ivOffset] & ~ivMask) | + (PORT_GET_BYTE_BE(context->ivCounter, 0, ivNewCount) & ivMask); + for (i = 1; i < ivNewCount; i++) { + iv[ivOffset + i] = + PORT_GET_BYTE_BE(context->ivCounter, i, ivNewCount); + } + break; + case CKG_GENERATE_COUNTER_XOR: + iv[ivOffset] ^= + (PORT_GET_BYTE_BE(context->ivCounter, 0, ivNewCount) & ivMask); + for (i = 1; i < ivNewCount; i++) { + iv[ivOffset + i] ^= + PORT_GET_BYTE_BE(context->ivCounter, i, ivNewCount); + } + break; + case CKG_GENERATE_RANDOM: + ivSave = iv[ivOffset] & ~ivMask; + rv = PK11_GenerateRandom(iv + ivOffset, ivNewCount); + iv[ivOffset] = ivSave | (iv[ivOffset] & ivMask); + if (rv != SECSuccess) { + return rv; + } + break; + } + context->ivCounter++; + return SECSuccess; +} + +/* + * PKCS #11 v2.40 did not have a message interface. If our module can't + * do the message interface use the old method of doing AEAD */ +static SECStatus +pk11_AEADSimulateOp(PK11Context *context, void *params, int paramslen, + const unsigned char *aad, int aadlen, + unsigned char *out, int *outlen, + int maxout, const unsigned char *in, int inlen) +{ + unsigned int length = maxout; + SECStatus rv = SECSuccess; + unsigned char *saveOut = out; + unsigned char *allocOut = NULL; + + /* + * first we need to convert the single shot (v2.40) parameters into + * the message version of the parameters. This usually involves + * copying the Nonce or IV, setting the AAD from our parameter list + * and handling the tag differences */ + CK_GCM_PARAMS_V3 gcm; + CK_GCM_MESSAGE_PARAMS *gcm_message; + CK_CCM_PARAMS ccm; + CK_CCM_MESSAGE_PARAMS *ccm_message; + CK_SALSA20_CHACHA20_POLY1305_PARAMS chacha_poly; + CK_SALSA20_CHACHA20_POLY1305_MSG_PARAMS *chacha_poly_message; + CK_NSS_AEAD_PARAMS nss_chacha_poly; + CK_MECHANISM_TYPE mechanism = context->simulate_mechanism; + SECItem sim_params = { 0, NULL, 0 }; + unsigned char *tag = NULL; + unsigned int taglen; + PRBool encrypt; + + *outlen = 0; + /* figure out if we are encrypting or decrypting, as tags are + * handled differently in both */ + switch (context->operation) { + case CKA_NSS_MESSAGE | CKA_ENCRYPT: + encrypt = PR_TRUE; + break; + case CKA_NSS_MESSAGE | CKA_DECRYPT: + encrypt = PR_FALSE; + break; + default: + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + switch (mechanism) { + case CKM_CHACHA20_POLY1305: + case CKM_SALSA20_POLY1305: + if (paramslen != sizeof(CK_SALSA20_CHACHA20_POLY1305_MSG_PARAMS)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + chacha_poly_message = + (CK_SALSA20_CHACHA20_POLY1305_MSG_PARAMS *)params; + chacha_poly.pNonce = chacha_poly_message->pNonce; + chacha_poly.ulNonceLen = chacha_poly_message->ulNonceLen; + chacha_poly.pAAD = (CK_BYTE_PTR)aad; + chacha_poly.ulAADLen = aadlen; + tag = chacha_poly_message->pTag; + taglen = 16; + sim_params.data = (unsigned char *)&chacha_poly; + sim_params.len = sizeof(chacha_poly); + /* SALSA20_POLY1305 and CHACHA20_POLY1305 do not generate the iv + * internally, don't simulate it either */ + break; + case CKM_NSS_CHACHA20_POLY1305: + if (paramslen != sizeof(CK_SALSA20_CHACHA20_POLY1305_MSG_PARAMS)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + chacha_poly_message = + (CK_SALSA20_CHACHA20_POLY1305_MSG_PARAMS *)params; + tag = chacha_poly_message->pTag; + taglen = 16; + nss_chacha_poly.pNonce = chacha_poly_message->pNonce; + nss_chacha_poly.ulNonceLen = chacha_poly_message->ulNonceLen; + nss_chacha_poly.pAAD = (CK_BYTE_PTR)aad; + nss_chacha_poly.ulAADLen = aadlen; + nss_chacha_poly.ulTagLen = taglen; + sim_params.data = (unsigned char *)&nss_chacha_poly; + sim_params.len = sizeof(nss_chacha_poly); + /* CKM_NSS_CHACHA20_POLY1305 does not generate the iv + * internally, don't simulate it either */ + break; + case CKM_AES_CCM: + if (paramslen != sizeof(CK_CCM_MESSAGE_PARAMS)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + ccm_message = (CK_CCM_MESSAGE_PARAMS *)params; + ccm.ulDataLen = ccm_message->ulDataLen; + ccm.pNonce = ccm_message->pNonce; + ccm.ulNonceLen = ccm_message->ulNonceLen; + ccm.pAAD = (CK_BYTE_PTR)aad; + ccm.ulAADLen = aadlen; + ccm.ulMACLen = ccm_message->ulMACLen; + tag = ccm_message->pMAC; + taglen = ccm_message->ulMACLen; + sim_params.data = (unsigned char *)&ccm; + sim_params.len = sizeof(ccm); + if (encrypt) { + /* simulate generating the IV */ + rv = pk11_GenerateIV(context, ccm_message->nonceGenerator, + ccm_message->ulNonceFixedBits, + ccm_message->pNonce, + ccm_message->ulNonceLen); + if (rv != SECSuccess) { + return rv; + } + } + break; + case CKM_AES_GCM: + if (paramslen != sizeof(CK_GCM_MESSAGE_PARAMS)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + gcm_message = (CK_GCM_MESSAGE_PARAMS *)params; + gcm.pIv = gcm_message->pIv; + gcm.ulIvLen = gcm_message->ulIvLen; + gcm.ulIvBits = gcm.ulIvLen * PR_BITS_PER_BYTE; + gcm.pAAD = (CK_BYTE_PTR)aad; + gcm.ulAADLen = aadlen; + gcm.ulTagBits = gcm_message->ulTagBits; + tag = gcm_message->pTag; + taglen = (gcm_message->ulTagBits + (PR_BITS_PER_BYTE - 1)) / PR_BITS_PER_BYTE; + sim_params.data = (unsigned char *)&gcm; + sim_params.len = sizeof(gcm); + if (encrypt) { + /* simulate generating the IV */ + rv = pk11_GenerateIV(context, gcm_message->ivGenerator, + gcm_message->ulIvFixedBits, + gcm_message->pIv, gcm_message->ulIvLen); + if (rv != SECSuccess) { + return rv; + } + } + break; + default: + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return SECFailure; + } + /* now handle the tag. The message interface separates the tag from + * the data, while the single shot gets and puts the tag at the end of + * the encrypted data. */ + if (!encrypt) { + /* In the decrypt case, if the tag is already at the end of the + * input buffer we are golden, otherwise we'll need a new input + * buffer and copy the tag at the end of it */ + if (tag != in + inlen) { + allocOut = PORT_Alloc(inlen + taglen); + if (allocOut == NULL) { + return SECFailure; + } + PORT_Memcpy(allocOut, in, inlen); + PORT_Memcpy(allocOut + inlen, tag, taglen); + in = allocOut; + } + inlen = inlen + taglen; + } else { + /* if we end up allocating, we don't want to overrun this buffer, + * so we fail early here */ + if (maxout < inlen) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + /* in the encrypt case, we are fine if maxout is big enough to hold + * the tag. We'll copy the tag after the operation */ + if (maxout < inlen + taglen) { + allocOut = PORT_Alloc(inlen + taglen); + if (allocOut == NULL) { + return SECFailure; + } + out = allocOut; + length = maxout = inlen + taglen; + } + } + /* now do the operation */ + if (encrypt) { + rv = PK11_Encrypt(context->key, mechanism, &sim_params, out, &length, + maxout, in, inlen); + } else { + rv = PK11_Decrypt(context->key, mechanism, &sim_params, out, &length, + maxout, in, inlen); + } + if (rv != SECSuccess) { + /* If the mechanism was CKM_AES_GCM, the module may have been + * following the same error as old versions of NSS. Retry with + * the CK_NSS_GCM_PARAMS */ + if ((mechanism == CKM_AES_GCM) && + (PORT_GetError() == SEC_ERROR_BAD_DATA)) { + CK_NSS_GCM_PARAMS gcm_nss; + gcm_message = (CK_GCM_MESSAGE_PARAMS *)params; + gcm_nss.pIv = gcm_message->pIv; + gcm_nss.ulIvLen = gcm_message->ulIvLen; + gcm_nss.pAAD = (CK_BYTE_PTR)aad; + gcm_nss.ulAADLen = aadlen; + gcm_nss.ulTagBits = gcm_message->ulTagBits; + sim_params.data = (unsigned char *)&gcm_nss; + sim_params.len = sizeof(gcm_nss); + if (encrypt) { + rv = PK11_Encrypt(context->key, mechanism, &sim_params, out, + &length, maxout, in, inlen); + } else { + rv = PK11_Decrypt(context->key, mechanism, &sim_params, out, + &length, maxout, in, inlen); + } + if (rv != SECSuccess) { + goto fail; + } + } else { + goto fail; + } + } + + /* on encrypt, separate the output buffer from the tag */ + if (encrypt) { + if ((length < taglen) || (length > inlen + taglen)) { + /* PKCS #11 module should not return a length smaller than + * taglen, or bigger than inlen+taglen */ + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + rv = SECFailure; + goto fail; + } + length = length - taglen; + if (allocOut) { + /* + * If we used a temporary buffer, copy it out to the original + * buffer. + */ + PORT_Memcpy(saveOut, allocOut, length); + } + /* if the tag isn't in the right place, copy it out */ + if (tag != out + length) { + PORT_Memcpy(tag, out + length, taglen); + } + } + *outlen = length; + rv = SECSuccess; +fail: + if (allocOut) { + PORT_Free(allocOut); + } + return rv; +} + +/* + * Do an AEAD operation. This function optionally returns + * and IV on Encrypt for all mechanism. NSS knows which mechanisms + * generate IV's in the token and which don't. This allows the + * applications to make a single call without special handling for + * each AEAD mechanism (the special handling is all contained here. + */ +SECStatus +PK11_AEADOp(PK11Context *context, CK_GENERATOR_FUNCTION ivgen, + int fixedbits, unsigned char *iv, int ivlen, + const unsigned char *aad, int aadlen, + unsigned char *out, int *outlen, + int maxout, unsigned char *tag, int taglen, + const unsigned char *in, int inlen) +{ + CK_GCM_MESSAGE_PARAMS gcm_message; + CK_CCM_MESSAGE_PARAMS ccm_message; + CK_SALSA20_CHACHA20_POLY1305_MSG_PARAMS chacha_poly_message; + void *params; + int paramslen; + SECStatus rv; + + switch (context->simulate_mechanism) { + case CKM_CHACHA20_POLY1305: + case CKM_SALSA20_POLY1305: + case CKM_NSS_CHACHA20_POLY1305: + chacha_poly_message.pNonce = iv; + chacha_poly_message.ulNonceLen = ivlen; + chacha_poly_message.pTag = tag; + params = &chacha_poly_message; + paramslen = sizeof(CK_SALSA20_CHACHA20_POLY1305_MSG_PARAMS); + /* SALSA20_POLY1305 and CHACHA20_POLY1305 do not generate the iv + * internally, Do it here. */ + if (context->operation == (CKA_NSS_MESSAGE | CKA_ENCRYPT)) { + /* simulate generating the IV */ + rv = pk11_GenerateIV(context, ivgen, fixedbits, iv, ivlen); + if (rv != SECSuccess) { + return rv; + } + } + break; + case CKM_AES_GCM: + gcm_message.pIv = iv; + gcm_message.ulIvLen = ivlen; + gcm_message.ivGenerator = ivgen; + gcm_message.ulIvFixedBits = fixedbits; + gcm_message.pTag = tag; + gcm_message.ulTagBits = taglen * 8; + params = &gcm_message; + paramslen = sizeof(CK_GCM_MESSAGE_PARAMS); + /* GCM generates IV internally */ + break; + case CKM_AES_CCM: + ccm_message.ulDataLen = inlen; + ccm_message.pNonce = iv; + ccm_message.ulNonceLen = ivlen; + ccm_message.nonceGenerator = ivgen; + ccm_message.ulNonceFixedBits = fixedbits; + ccm_message.pMAC = tag; + ccm_message.ulMACLen = taglen; + params = &ccm_message; + paramslen = sizeof(CK_GCM_MESSAGE_PARAMS); + /* CCM generates IV internally */ + break; + + default: + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return SECFailure; + } + return PK11_AEADRawOp(context, params, paramslen, aad, aadlen, out, outlen, + maxout, in, inlen); +} + +/* Do and AED operation. The application builds the params on it's own + * and passes them in. This allows applications direct access to the params + * so they can use mechanisms not yet understood by, NSS, or get semantics + * not suppied by PK11_AEAD. */ +SECStatus +PK11_AEADRawOp(PK11Context *context, void *params, int paramslen, + const unsigned char *aad, int aadlen, + unsigned char *out, int *outlen, + int maxout, const unsigned char *in, int inlen) +{ + CK_RV crv = CKR_OK; + CK_ULONG length = maxout; + SECStatus rv = SECSuccess; + + PORT_Assert(outlen != NULL); + *outlen = 0; + if (((context->operation) & CKA_NSS_MESSAGE_MASK) != CKA_NSS_MESSAGE) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + /* + * The PKCS 11 module does not support the message interface, fall + * back to using single shot operation */ + if (context->simulate_message) { + return pk11_AEADSimulateOp(context, params, paramslen, aad, aadlen, + out, outlen, maxout, in, inlen); + } + + /* if we ran out of session, we need to restore our previously stored + * state. + */ + PK11_EnterContextMonitor(context); + if (!context->ownSession) { + rv = pk11_restoreContext(context, context->savedData, + context->savedLength); + if (rv != SECSuccess) { + PK11_ExitContextMonitor(context); + return rv; + } + } + + switch (context->operation) { + case CKA_NSS_MESSAGE | CKA_ENCRYPT: + length = maxout; + crv = PK11_GETTAB(context->slot)->C_EncryptMessage(context->session, params, paramslen, (CK_BYTE_PTR)aad, aadlen, (CK_BYTE_PTR)in, inlen, out, &length); + break; + case CKA_NSS_MESSAGE | CKA_DECRYPT: + length = maxout; + crv = PK11_GETTAB(context->slot)->C_DecryptMessage(context->session, params, paramslen, (CK_BYTE_PTR)aad, aadlen, (CK_BYTE_PTR)in, inlen, out, &length); + break; + case CKA_NSS_MESSAGE | CKA_SIGN: + length = maxout; + crv = PK11_GETTAB(context->slot)->C_SignMessage(context->session, params, paramslen, (CK_BYTE_PTR)in, inlen, out, &length); + break; + case CKA_NSS_MESSAGE | CKA_VERIFY: + length = maxout; /* sig length */ + crv = PK11_GETTAB(context->slot)->C_VerifyMessage(context->session, params, paramslen, (CK_BYTE_PTR)in, inlen, out /* sig */, length); + break; + default: + crv = CKR_OPERATION_NOT_INITIALIZED; + break; + } + + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError(crv)); + rv = SECFailure; + } else { + *outlen = length; + } + + /* + * handle session starvation case.. use our last session to multiplex + */ + if (!context->ownSession) { + context->savedData = pk11_saveContext(context, context->savedData, + &context->savedLength); + if (context->savedData == NULL) + rv = SECFailure; + + /* clear out out session for others to use */ + pk11_Finalize(context); + } + PK11_ExitContextMonitor(context); + return rv; +} + +/* + * execute a digest/signature operation + */ +SECStatus +PK11_DigestOp(PK11Context *context, const unsigned char *in, unsigned inLen) +{ + CK_RV crv = CKR_OK; + SECStatus rv = SECSuccess; + + if (inLen == 0) { + return SECSuccess; + } + if (!in) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + /* if we ran out of session, we need to restore our previously stored + * state. + */ + context->init = PR_FALSE; + PK11_EnterContextMonitor(context); + if (!context->ownSession) { + rv = pk11_restoreContext(context, context->savedData, + context->savedLength); + if (rv != SECSuccess) { + PK11_ExitContextMonitor(context); + return rv; + } + } + + switch (context->operation) { + /* also for MAC'ing */ + case CKA_SIGN: + crv = PK11_GETTAB(context->slot)->C_SignUpdate(context->session, (unsigned char *)in, inLen); + break; + case CKA_VERIFY: + crv = PK11_GETTAB(context->slot)->C_VerifyUpdate(context->session, (unsigned char *)in, inLen); + break; + case CKA_DIGEST: + crv = PK11_GETTAB(context->slot)->C_DigestUpdate(context->session, (unsigned char *)in, inLen); + break; + default: + crv = CKR_OPERATION_NOT_INITIALIZED; + break; + } + + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError(crv)); + rv = SECFailure; + } + + /* + * handle session starvation case.. use our last session to multiplex + */ + if (!context->ownSession) { + context->savedData = pk11_saveContext(context, context->savedData, + &context->savedLength); + if (context->savedData == NULL) + rv = SECFailure; + + /* clear out out session for others to use */ + pk11_Finalize(context); + } + PK11_ExitContextMonitor(context); + return rv; +} + +/* + * Digest a key if possible./ + */ +SECStatus +PK11_DigestKey(PK11Context *context, PK11SymKey *key) +{ + CK_RV crv = CKR_OK; + SECStatus rv = SECSuccess; + PK11SymKey *newKey = NULL; + + if (!context || !key) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + /* if we ran out of session, we need to restore our previously stored + * state. + */ + if (context->slot != key->slot) { + newKey = pk11_CopyToSlot(context->slot, CKM_SSL3_SHA1_MAC, CKA_SIGN, key); + } else { + newKey = PK11_ReferenceSymKey(key); + } + + context->init = PR_FALSE; + PK11_EnterContextMonitor(context); + if (!context->ownSession) { + rv = pk11_restoreContext(context, context->savedData, + context->savedLength); + if (rv != SECSuccess) { + PK11_ExitContextMonitor(context); + PK11_FreeSymKey(newKey); + return rv; + } + } + + if (newKey == NULL) { + crv = CKR_KEY_TYPE_INCONSISTENT; + if (key->data.data) { + crv = PK11_GETTAB(context->slot)->C_DigestUpdate(context->session, key->data.data, key->data.len); + } + } else { + crv = PK11_GETTAB(context->slot)->C_DigestKey(context->session, newKey->objectID); + } + + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError(crv)); + rv = SECFailure; + } + + /* + * handle session starvation case.. use our last session to multiplex + */ + if (!context->ownSession) { + context->savedData = pk11_saveContext(context, context->savedData, + &context->savedLength); + if (context->savedData == NULL) + rv = SECFailure; + + /* clear out out session for others to use */ + pk11_Finalize(context); + } + PK11_ExitContextMonitor(context); + if (newKey) + PK11_FreeSymKey(newKey); + return rv; +} + +/* + * externally callable version of the lowercase pk11_finalize(). + */ +SECStatus +PK11_Finalize(PK11Context *context) +{ + SECStatus rv; + + PK11_EnterContextMonitor(context); + rv = pk11_Finalize(context); + PK11_ExitContextMonitor(context); + return rv; +} + +/* + * clean up a cipher operation, so the session can be used by + * someone new. + */ +SECStatus +pk11_Finalize(PK11Context *context) +{ + CK_ULONG count = 0; + CK_RV crv; + unsigned char stackBuf[256]; + unsigned char *buffer = NULL; + + if (!context->ownSession) { + return SECSuccess; + } + +finalize: + switch (context->operation) { + case CKA_ENCRYPT: + crv = PK11_GETTAB(context->slot)->C_EncryptFinal(context->session, buffer, &count); + break; + case CKA_DECRYPT: + crv = PK11_GETTAB(context->slot)->C_DecryptFinal(context->session, buffer, &count); + break; + case CKA_SIGN: + crv = PK11_GETTAB(context->slot)->C_SignFinal(context->session, buffer, &count); + break; + case CKA_VERIFY: + crv = PK11_GETTAB(context->slot)->C_VerifyFinal(context->session, buffer, count); + break; + case CKA_DIGEST: + crv = PK11_GETTAB(context->slot)->C_DigestFinal(context->session, buffer, &count); + break; + case CKA_NSS_MESSAGE | CKA_ENCRYPT: + crv = PK11_GETTAB(context->slot)->C_MessageEncryptFinal(context->session); + break; + case CKA_NSS_MESSAGE | CKA_DECRYPT: + crv = PK11_GETTAB(context->slot)->C_MessageDecryptFinal(context->session); + break; + case CKA_NSS_MESSAGE | CKA_SIGN: + crv = PK11_GETTAB(context->slot)->C_MessageSignFinal(context->session); + break; + case CKA_NSS_MESSAGE | CKA_VERIFY: + crv = PK11_GETTAB(context->slot)->C_MessageVerifyFinal(context->session); + break; + default: + crv = CKR_OPERATION_NOT_INITIALIZED; + break; + } + + if (crv != CKR_OK) { + if (buffer != stackBuf) { + PORT_Free(buffer); + } + if (crv == CKR_OPERATION_NOT_INITIALIZED) { + /* if there's no operation, it is finalized */ + return SECSuccess; + } + PORT_SetError(PK11_MapError(crv)); + return SECFailure; + } + + /* Message interface does not need to allocate a final buffer */ + if (((context->operation) & CKA_NSS_MESSAGE_MASK) == CKA_NSS_MESSAGE) { + return SECSuccess; + } + + /* try to finalize the session with a buffer */ + if (buffer == NULL) { + if (count <= sizeof stackBuf) { + buffer = stackBuf; + } else { + buffer = PORT_Alloc(count); + if (buffer == NULL) { + return SECFailure; + } + } + goto finalize; + } + if (buffer != stackBuf) { + PORT_Free(buffer); + } + return SECSuccess; +} + +/* + * Return the final digested or signed data... + * this routine can either take pre initialized data, or allocate data + * either out of an arena or out of the standard heap. + */ +SECStatus +PK11_DigestFinal(PK11Context *context, unsigned char *data, + unsigned int *outLen, unsigned int length) +{ + CK_ULONG len; + CK_RV crv; + SECStatus rv; + + /* message interface returns no data on Final, Should not use DigestFinal + * in this case */ + if (((context->operation) & CKA_NSS_MESSAGE_MASK) == CKA_NSS_MESSAGE) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + /* if we ran out of session, we need to restore our previously stored + * state. + */ + PK11_EnterContextMonitor(context); + if (!context->ownSession) { + rv = pk11_restoreContext(context, context->savedData, + context->savedLength); + if (rv != SECSuccess) { + PK11_ExitContextMonitor(context); + return rv; + } + } + + len = length; + switch (context->operation) { + case CKA_SIGN: + crv = PK11_GETTAB(context->slot)->C_SignFinal(context->session, data, &len); + break; + case CKA_VERIFY: + crv = PK11_GETTAB(context->slot)->C_VerifyFinal(context->session, data, len); + break; + case CKA_DIGEST: + crv = PK11_GETTAB(context->slot)->C_DigestFinal(context->session, data, &len); + break; + case CKA_ENCRYPT: + crv = PK11_GETTAB(context->slot)->C_EncryptFinal(context->session, data, &len); + break; + case CKA_DECRYPT: + crv = PK11_GETTAB(context->slot)->C_DecryptFinal(context->session, data, &len); + break; + default: + crv = CKR_OPERATION_NOT_INITIALIZED; + break; + } + PK11_ExitContextMonitor(context); + + context->init = PR_FALSE; /* allow Begin to start up again */ + + if (crv != CKR_OK) { + PORT_SetError(PK11_MapError(crv)); + return SECFailure; + } + *outLen = (unsigned int)len; + return SECSuccess; +} + +PRBool +PK11_ContextGetFIPSStatus(PK11Context *context) +{ + if (context->slot == NULL) { + return PR_FALSE; + } + return pk11slot_GetFIPSStatus(context->slot, context->session, + CK_INVALID_HANDLE, context->init ? CKT_NSS_SESSION_CHECK : CKT_NSS_SESSION_LAST_CHECK); +} |