diff options
Diffstat (limited to '')
-rw-r--r-- | security/nss/lib/softoken/pkcs11c.c | 9085 |
1 files changed, 9085 insertions, 0 deletions
diff --git a/security/nss/lib/softoken/pkcs11c.c b/security/nss/lib/softoken/pkcs11c.c new file mode 100644 index 0000000000..856c98e7cf --- /dev/null +++ b/security/nss/lib/softoken/pkcs11c.c @@ -0,0 +1,9085 @@ +/* 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 implements PKCS 11 on top of our existing security modules + * + * For more information about PKCS 11 See PKCS 11 Token Inteface Standard. + * This implementation has two slots: + * slot 1 is our generic crypto support. It does not require login. + * It supports Public Key ops, and all they bulk ciphers and hashes. + * It can also support Private Key ops for imported Private keys. It does + * not have any token storage. + * slot 2 is our private key support. It requires a login before use. It + * can store Private Keys and Certs as token objects. Currently only private + * keys and their associated Certificates are saved on the token. + * + * In this implementation, session objects are only visible to the session + * that created or generated them. + */ + +#include <limits.h> /* for UINT_MAX and ULONG_MAX */ + +#include "seccomon.h" +#include "secitem.h" +#include "secport.h" +#include "blapi.h" +#include "pkcs11.h" +#include "pkcs11i.h" +#include "pkcs1sig.h" +#include "lowkeyi.h" +#include "secder.h" +#include "secdig.h" +#include "lowpbe.h" /* We do PBE below */ +#include "pkcs11t.h" +#include "secoid.h" +#include "cmac.h" +#include "alghmac.h" +#include "softoken.h" +#include "secasn1.h" +#include "secerr.h" +#include "kem.h" +#include "kyber.h" + +#include "prprf.h" +#include "prenv.h" + +#define __PASTE(x, y) x##y +#define BAD_PARAM_CAST(pMech, typeSize) (!pMech->pParameter || pMech->ulParameterLen < typeSize) +/* + * we renamed all our internal functions, get the correct + * definitions for them... + */ +#undef CK_PKCS11_FUNCTION_INFO +#undef CK_NEED_ARG_LIST + +#define CK_PKCS11_3_0 1 + +#define CK_EXTERN extern +#define CK_PKCS11_FUNCTION_INFO(func) \ + CK_RV __PASTE(NS, func) +#define CK_NEED_ARG_LIST 1 + +#include "pkcs11f.h" + +/* create a definition of SHA1 that's consistent + * with the rest of the CKM_SHAxxx hashes*/ +#define CKM_SHA1 CKM_SHA_1 +#define CKM_SHA1_HMAC CKM_SHA_1_HMAC +#define CKM_SHA1_HMAC_GENERAL CKM_SHA_1_HMAC_GENERAL + +typedef struct { + PRUint8 client_version[2]; + PRUint8 random[46]; +} SSL3RSAPreMasterSecret; + +static void +sftk_Null(void *data, PRBool freeit) +{ + return; +} + +#ifdef EC_DEBUG +#define SEC_PRINT(str1, str2, num, sitem) \ + printf("pkcs11c.c:%s:%s (keytype=%d) [len=%d]\n", \ + str1, str2, num, sitem->len); \ + for (i = 0; i < sitem->len; i++) { \ + printf("%02x:", sitem->data[i]); \ + } \ + printf("\n") +#else +#undef EC_DEBUG +#define SEC_PRINT(a, b, c, d) +#endif + +/* + * free routines.... Free local type allocated data, and convert + * other free routines to the destroy signature. + */ +static void +sftk_FreePrivKey(NSSLOWKEYPrivateKey *key, PRBool freeit) +{ + nsslowkey_DestroyPrivateKey(key); +} + +static void +sftk_Space(void *data, PRBool freeit) +{ + PORT_Free(data); +} + +static void +sftk_ZSpace(void *data, PRBool freeit) +{ + size_t len = *(size_t *)data; + PORT_ZFree(data, len); +} + +/* + * turn a CDMF key into a des key. CDMF is an old IBM scheme to export DES by + * Deprecating a full des key to 40 bit key strenth. + */ +static CK_RV +sftk_cdmf2des(unsigned char *cdmfkey, unsigned char *deskey) +{ + unsigned char key1[8] = { 0xc4, 0x08, 0xb0, 0x54, 0x0b, 0xa1, 0xe0, 0xae }; + unsigned char key2[8] = { 0xef, 0x2c, 0x04, 0x1c, 0xe6, 0x38, 0x2f, 0xe6 }; + unsigned char enc_src[8]; + unsigned char enc_dest[8]; + unsigned int leng, i; + DESContext *descx; + SECStatus rv; + CK_RV crv = CKR_OK; + + /* zero the parity bits */ + for (i = 0; i < 8; i++) { + enc_src[i] = cdmfkey[i] & 0xfe; + } + + /* encrypt with key 1 */ + descx = DES_CreateContext(key1, NULL, NSS_DES, PR_TRUE); + if (descx == NULL) { + crv = CKR_HOST_MEMORY; + goto done; + } + rv = DES_Encrypt(descx, enc_dest, &leng, 8, enc_src, 8); + DES_DestroyContext(descx, PR_TRUE); + if (rv != SECSuccess) { + crv = sftk_MapCryptError(PORT_GetError()); + goto done; + } + + /* xor source with des, zero the parity bits and deprecate the key*/ + for (i = 0; i < 8; i++) { + if (i & 1) { + enc_src[i] = (enc_src[i] ^ enc_dest[i]) & 0xfe; + } else { + enc_src[i] = (enc_src[i] ^ enc_dest[i]) & 0x0e; + } + } + + /* encrypt with key 2 */ + descx = DES_CreateContext(key2, NULL, NSS_DES, PR_TRUE); + if (descx == NULL) { + crv = CKR_HOST_MEMORY; + goto done; + } + rv = DES_Encrypt(descx, deskey, &leng, 8, enc_src, 8); + DES_DestroyContext(descx, PR_TRUE); + if (rv != SECSuccess) { + crv = sftk_MapCryptError(PORT_GetError()); + goto done; + } + + /* set the corret parity on our new des key */ + sftk_FormatDESKey(deskey, 8); +done: + PORT_Memset(enc_src, 0, sizeof enc_src); + PORT_Memset(enc_dest, 0, sizeof enc_dest); + return crv; +} + +/* NSC_DestroyObject destroys an object. */ +CK_RV +NSC_DestroyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject) +{ + SFTKSlot *slot = sftk_SlotFromSessionHandle(hSession); + SFTKSession *session; + SFTKObject *object; + SFTKFreeStatus status; + + CHECK_FORK(); + + if (slot == NULL) { + return CKR_SESSION_HANDLE_INVALID; + } + /* + * This whole block just makes sure we really can destroy the + * requested object. + */ + session = sftk_SessionFromHandle(hSession); + if (session == NULL) { + return CKR_SESSION_HANDLE_INVALID; + } + + object = sftk_ObjectFromHandle(hObject, session); + if (object == NULL) { + sftk_FreeSession(session); + return CKR_OBJECT_HANDLE_INVALID; + } + + /* don't destroy a private object if we aren't logged in */ + if ((!slot->isLoggedIn) && (slot->needLogin) && + (sftk_isTrue(object, CKA_PRIVATE))) { + sftk_FreeSession(session); + sftk_FreeObject(object); + return CKR_USER_NOT_LOGGED_IN; + } + + /* don't destroy a token object if we aren't in a rw session */ + + if (((session->info.flags & CKF_RW_SESSION) == 0) && + (sftk_isTrue(object, CKA_TOKEN))) { + sftk_FreeSession(session); + sftk_FreeObject(object); + return CKR_SESSION_READ_ONLY; + } + + sftk_DeleteObject(session, object); + + sftk_FreeSession(session); + + /* + * get some indication if the object is destroyed. Note: this is not + * 100%. Someone may have an object reference outstanding (though that + * should not be the case by here. Also note that the object is "half" + * destroyed. Our internal representation is destroyed, but it may still + * be in the data base. + */ + status = sftk_FreeObject(object); + + return (status != SFTK_DestroyFailure) ? CKR_OK : CKR_DEVICE_ERROR; +} + +/* + * Returns true if "params" contains a valid set of PSS parameters + */ +static PRBool +sftk_ValidatePssParams(const CK_RSA_PKCS_PSS_PARAMS *params) +{ + if (!params) { + return PR_FALSE; + } + if (sftk_GetHashTypeFromMechanism(params->hashAlg) == HASH_AlgNULL || + sftk_GetHashTypeFromMechanism(params->mgf) == HASH_AlgNULL) { + return PR_FALSE; + } + return PR_TRUE; +} + +/* + * Returns true if "params" contains a valid set of OAEP parameters + */ +static PRBool +sftk_ValidateOaepParams(const CK_RSA_PKCS_OAEP_PARAMS *params) +{ + if (!params) { + return PR_FALSE; + } + /* The requirements of ulSourceLen/pSourceData come from PKCS #11, which + * state: + * If the parameter is empty, pSourceData must be NULL and + * ulSourceDataLen must be zero. + */ + if (params->source != CKZ_DATA_SPECIFIED || + (sftk_GetHashTypeFromMechanism(params->hashAlg) == HASH_AlgNULL) || + (sftk_GetHashTypeFromMechanism(params->mgf) == HASH_AlgNULL) || + (params->ulSourceDataLen == 0 && params->pSourceData != NULL) || + (params->ulSourceDataLen != 0 && params->pSourceData == NULL)) { + return PR_FALSE; + } + return PR_TRUE; +} + +/* + * return a context based on the SFTKContext type. + */ +SFTKSessionContext * +sftk_ReturnContextByType(SFTKSession *session, SFTKContextType type) +{ + switch (type) { + case SFTK_ENCRYPT: + case SFTK_DECRYPT: + case SFTK_MESSAGE_ENCRYPT: + case SFTK_MESSAGE_DECRYPT: + return session->enc_context; + case SFTK_HASH: + return session->hash_context; + case SFTK_SIGN: + case SFTK_SIGN_RECOVER: + case SFTK_VERIFY: + case SFTK_VERIFY_RECOVER: + case SFTK_MESSAGE_SIGN: + case SFTK_MESSAGE_VERIFY: + return session->hash_context; + } + return NULL; +} + +/* + * change a context based on the SFTKContext type. + */ +void +sftk_SetContextByType(SFTKSession *session, SFTKContextType type, + SFTKSessionContext *context) +{ + switch (type) { + case SFTK_ENCRYPT: + case SFTK_DECRYPT: + case SFTK_MESSAGE_ENCRYPT: + case SFTK_MESSAGE_DECRYPT: + session->enc_context = context; + break; + case SFTK_HASH: + session->hash_context = context; + break; + case SFTK_SIGN: + case SFTK_SIGN_RECOVER: + case SFTK_VERIFY: + case SFTK_VERIFY_RECOVER: + case SFTK_MESSAGE_SIGN: + case SFTK_MESSAGE_VERIFY: + session->hash_context = context; + break; + } + return; +} + +/* + * code to grab the context. Needed by every C_XXXUpdate, C_XXXFinal, + * and C_XXX function. The function takes a session handle, the context type, + * and wether or not the session needs to be multipart. It returns the context, + * and optionally returns the session pointer (if sessionPtr != NULL) if session + * pointer is returned, the caller is responsible for freeing it. + */ +CK_RV +sftk_GetContext(CK_SESSION_HANDLE handle, SFTKSessionContext **contextPtr, + SFTKContextType type, PRBool needMulti, SFTKSession **sessionPtr) +{ + SFTKSession *session; + SFTKSessionContext *context; + + session = sftk_SessionFromHandle(handle); + if (session == NULL) + return CKR_SESSION_HANDLE_INVALID; + context = sftk_ReturnContextByType(session, type); + /* make sure the context is valid */ + if ((context == NULL) || (context->type != type) || (needMulti && !(context->multi))) { + sftk_FreeSession(session); + return CKR_OPERATION_NOT_INITIALIZED; + } + *contextPtr = context; + if (sessionPtr != NULL) { + *sessionPtr = session; + } else { + sftk_FreeSession(session); + } + return CKR_OK; +} + +/** Terminate operation (in the PKCS#11 spec sense). + * Intuitive name for FreeContext/SetNullContext pair. + */ +void +sftk_TerminateOp(SFTKSession *session, SFTKContextType ctype, + SFTKSessionContext *context) +{ + session->lastOpWasFIPS = context->isFIPS; + sftk_FreeContext(context); + sftk_SetContextByType(session, ctype, NULL); +} + +/* + ************** Crypto Functions: Encrypt ************************ + */ + +/* + * All the NSC_InitXXX functions have a set of common checks and processing they + * all need to do at the beginning. This is done here. + */ +CK_RV +sftk_InitGeneric(SFTKSession *session, CK_MECHANISM *pMechanism, + SFTKSessionContext **contextPtr, + SFTKContextType ctype, SFTKObject **keyPtr, + CK_OBJECT_HANDLE hKey, CK_KEY_TYPE *keyTypePtr, + CK_OBJECT_CLASS pubKeyType, CK_ATTRIBUTE_TYPE operation) +{ + SFTKObject *key = NULL; + SFTKAttribute *att; + SFTKSessionContext *context; + + /* We can only init if there is not current context active */ + if (sftk_ReturnContextByType(session, ctype) != NULL) { + return CKR_OPERATION_ACTIVE; + } + + /* find the key */ + if (keyPtr) { + key = sftk_ObjectFromHandle(hKey, session); + if (key == NULL) { + return CKR_KEY_HANDLE_INVALID; + } + + /* make sure it's a valid key for this operation */ + if (((key->objclass != CKO_SECRET_KEY) && + (key->objclass != pubKeyType)) || + !sftk_isTrue(key, operation)) { + sftk_FreeObject(key); + return CKR_KEY_TYPE_INCONSISTENT; + } + /* get the key type */ + att = sftk_FindAttribute(key, CKA_KEY_TYPE); + if (att == NULL) { + sftk_FreeObject(key); + return CKR_KEY_TYPE_INCONSISTENT; + } + PORT_Assert(att->attrib.ulValueLen == sizeof(CK_KEY_TYPE)); + if (att->attrib.ulValueLen != sizeof(CK_KEY_TYPE)) { + sftk_FreeAttribute(att); + sftk_FreeObject(key); + return CKR_ATTRIBUTE_VALUE_INVALID; + } + PORT_Memcpy(keyTypePtr, att->attrib.pValue, sizeof(CK_KEY_TYPE)); + sftk_FreeAttribute(att); + *keyPtr = key; + } + + /* allocate the context structure */ + context = (SFTKSessionContext *)PORT_Alloc(sizeof(SFTKSessionContext)); + if (context == NULL) { + if (key) + sftk_FreeObject(key); + return CKR_HOST_MEMORY; + } + context->type = ctype; + context->multi = PR_TRUE; + context->rsa = PR_FALSE; + context->cipherInfo = NULL; + context->hashInfo = NULL; + context->doPad = PR_FALSE; + context->padDataLength = 0; + context->key = key; + context->blockSize = 0; + context->maxLen = 0; + context->isFIPS = sftk_operationIsFIPS(session->slot, pMechanism, + operation, key); + *contextPtr = context; + return CKR_OK; +} + +static int +sftk_aes_mode(CK_MECHANISM_TYPE mechanism) +{ + switch (mechanism) { + case CKM_AES_CBC_PAD: + case CKM_AES_CBC: + return NSS_AES_CBC; + case CKM_AES_ECB: + return NSS_AES; + case CKM_AES_CTS: + return NSS_AES_CTS; + case CKM_AES_CTR: + return NSS_AES_CTR; + case CKM_AES_GCM: + return NSS_AES_GCM; + } + return -1; +} + +static SECStatus +sftk_RSAEncryptRaw(NSSLOWKEYPublicKey *key, unsigned char *output, + unsigned int *outputLen, unsigned int maxLen, + const unsigned char *input, unsigned int inputLen) +{ + SECStatus rv = SECFailure; + + PORT_Assert(key->keyType == NSSLOWKEYRSAKey); + if (key->keyType != NSSLOWKEYRSAKey) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + return SECFailure; + } + + rv = RSA_EncryptRaw(&key->u.rsa, output, outputLen, maxLen, input, + inputLen); + if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { + sftk_fatalError = PR_TRUE; + } + + return rv; +} + +static SECStatus +sftk_RSADecryptRaw(NSSLOWKEYPrivateKey *key, unsigned char *output, + unsigned int *outputLen, unsigned int maxLen, + const unsigned char *input, unsigned int inputLen) +{ + SECStatus rv = SECFailure; + + PORT_Assert(key->keyType == NSSLOWKEYRSAKey); + if (key->keyType != NSSLOWKEYRSAKey) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + return SECFailure; + } + + rv = RSA_DecryptRaw(&key->u.rsa, output, outputLen, maxLen, input, + inputLen); + if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { + sftk_fatalError = PR_TRUE; + } + + return rv; +} + +static SECStatus +sftk_RSAEncrypt(NSSLOWKEYPublicKey *key, unsigned char *output, + unsigned int *outputLen, unsigned int maxLen, + const unsigned char *input, unsigned int inputLen) +{ + SECStatus rv = SECFailure; + + PORT_Assert(key->keyType == NSSLOWKEYRSAKey); + if (key->keyType != NSSLOWKEYRSAKey) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + return SECFailure; + } + + rv = RSA_EncryptBlock(&key->u.rsa, output, outputLen, maxLen, input, + inputLen); + if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { + sftk_fatalError = PR_TRUE; + } + + return rv; +} + +static SECStatus +sftk_RSADecrypt(NSSLOWKEYPrivateKey *key, unsigned char *output, + unsigned int *outputLen, unsigned int maxLen, + const unsigned char *input, unsigned int inputLen) +{ + SECStatus rv = SECFailure; + + PORT_Assert(key->keyType == NSSLOWKEYRSAKey); + if (key->keyType != NSSLOWKEYRSAKey) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + return SECFailure; + } + + rv = RSA_DecryptBlock(&key->u.rsa, output, outputLen, maxLen, input, + inputLen); + if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { + sftk_fatalError = PR_TRUE; + } + + return rv; +} + +static void +sftk_freeRSAOAEPInfo(SFTKOAEPInfo *info, PRBool freeit) +{ + PORT_ZFree(info->params.pSourceData, info->params.ulSourceDataLen); + PORT_ZFree(info, sizeof(SFTKOAEPInfo)); +} + +static SECStatus +sftk_RSAEncryptOAEP(SFTKOAEPInfo *info, unsigned char *output, + unsigned int *outputLen, unsigned int maxLen, + const unsigned char *input, unsigned int inputLen) +{ + HASH_HashType hashAlg; + HASH_HashType maskHashAlg; + + PORT_Assert(info->key.pub->keyType == NSSLOWKEYRSAKey); + if (info->key.pub->keyType != NSSLOWKEYRSAKey) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + return SECFailure; + } + + hashAlg = sftk_GetHashTypeFromMechanism(info->params.hashAlg); + maskHashAlg = sftk_GetHashTypeFromMechanism(info->params.mgf); + + return RSA_EncryptOAEP(&info->key.pub->u.rsa, hashAlg, maskHashAlg, + (const unsigned char *)info->params.pSourceData, + info->params.ulSourceDataLen, NULL, 0, + output, outputLen, maxLen, input, inputLen); +} + +static SECStatus +sftk_RSADecryptOAEP(SFTKOAEPInfo *info, unsigned char *output, + unsigned int *outputLen, unsigned int maxLen, + const unsigned char *input, unsigned int inputLen) +{ + SECStatus rv = SECFailure; + HASH_HashType hashAlg; + HASH_HashType maskHashAlg; + + PORT_Assert(info->key.priv->keyType == NSSLOWKEYRSAKey); + if (info->key.priv->keyType != NSSLOWKEYRSAKey) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + return SECFailure; + } + + hashAlg = sftk_GetHashTypeFromMechanism(info->params.hashAlg); + maskHashAlg = sftk_GetHashTypeFromMechanism(info->params.mgf); + + rv = RSA_DecryptOAEP(&info->key.priv->u.rsa, hashAlg, maskHashAlg, + (const unsigned char *)info->params.pSourceData, + info->params.ulSourceDataLen, + output, outputLen, maxLen, input, inputLen); + if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { + sftk_fatalError = PR_TRUE; + } + return rv; +} + +static SFTKChaCha20Poly1305Info * +sftk_ChaCha20Poly1305_CreateContext(const unsigned char *key, + unsigned int keyLen, + const CK_NSS_AEAD_PARAMS *params) +{ + SFTKChaCha20Poly1305Info *ctx; + + if (params->ulNonceLen != sizeof(ctx->nonce)) { + PORT_SetError(SEC_ERROR_INPUT_LEN); + return NULL; + } + + ctx = PORT_New(SFTKChaCha20Poly1305Info); + if (ctx == NULL) { + return NULL; + } + + if (ChaCha20Poly1305_InitContext(&ctx->freeblCtx, key, keyLen, + params->ulTagLen) != SECSuccess) { + PORT_Free(ctx); + return NULL; + } + + PORT_Memcpy(ctx->nonce, params->pNonce, sizeof(ctx->nonce)); + + /* AAD data and length must both be null, or both non-null. */ + PORT_Assert((params->pAAD == NULL) == (params->ulAADLen == 0)); + + if (params->ulAADLen > sizeof(ctx->ad)) { + /* Need to allocate an overflow buffer for the additional data. */ + ctx->adOverflow = (unsigned char *)PORT_Alloc(params->ulAADLen); + if (!ctx->adOverflow) { + PORT_Free(ctx); + return NULL; + } + PORT_Memcpy(ctx->adOverflow, params->pAAD, params->ulAADLen); + } else { + ctx->adOverflow = NULL; + if (params->pAAD) { + PORT_Memcpy(ctx->ad, params->pAAD, params->ulAADLen); + } + } + ctx->adLen = params->ulAADLen; + + return ctx; +} + +static void +sftk_ChaCha20Poly1305_DestroyContext(SFTKChaCha20Poly1305Info *ctx, + PRBool freeit) +{ + ChaCha20Poly1305_DestroyContext(&ctx->freeblCtx, PR_FALSE); + if (ctx->adOverflow != NULL) { + PORT_ZFree(ctx->adOverflow, ctx->adLen); + ctx->adOverflow = NULL; + } else { + PORT_Memset(ctx->ad, 0, ctx->adLen); + } + ctx->adLen = 0; + if (freeit) { + PORT_Free(ctx); + } +} + +static SECStatus +sftk_ChaCha20Poly1305_Encrypt(const SFTKChaCha20Poly1305Info *ctx, + unsigned char *output, unsigned int *outputLen, + unsigned int maxOutputLen, + const unsigned char *input, unsigned int inputLen) +{ + const unsigned char *ad = ctx->adOverflow; + + if (ad == NULL) { + ad = ctx->ad; + } + + return ChaCha20Poly1305_Seal(&ctx->freeblCtx, output, outputLen, + maxOutputLen, input, inputLen, ctx->nonce, + sizeof(ctx->nonce), ad, ctx->adLen); +} + +static SECStatus +sftk_ChaCha20Poly1305_Decrypt(const SFTKChaCha20Poly1305Info *ctx, + unsigned char *output, unsigned int *outputLen, + unsigned int maxOutputLen, + const unsigned char *input, unsigned int inputLen) +{ + const unsigned char *ad = ctx->adOverflow; + + if (ad == NULL) { + ad = ctx->ad; + } + + return ChaCha20Poly1305_Open(&ctx->freeblCtx, output, outputLen, + maxOutputLen, input, inputLen, ctx->nonce, + sizeof(ctx->nonce), ad, ctx->adLen); +} + +static SECStatus +sftk_ChaCha20Ctr(const SFTKChaCha20CtrInfo *ctx, + unsigned char *output, unsigned int *outputLen, + unsigned int maxOutputLen, + const unsigned char *input, unsigned int inputLen) +{ + if (maxOutputLen < inputLen) { + PORT_SetError(SEC_ERROR_OUTPUT_LEN); + return SECFailure; + } + ChaCha20_Xor(output, input, inputLen, ctx->key, + ctx->nonce, ctx->counter); + *outputLen = inputLen; + return SECSuccess; +} + +static void +sftk_ChaCha20Ctr_DestroyContext(SFTKChaCha20CtrInfo *ctx, + PRBool freeit) +{ + memset(ctx, 0, sizeof(*ctx)); + if (freeit) { + PORT_Free(ctx); + } +} + +/** NSC_CryptInit initializes an encryption/Decryption operation. + * + * Always called by NSC_EncryptInit, NSC_DecryptInit, NSC_WrapKey,NSC_UnwrapKey. + * Called by NSC_SignInit, NSC_VerifyInit (via sftk_InitCBCMac) only for block + * ciphers MAC'ing. + */ +CK_RV +sftk_CryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey, + CK_ATTRIBUTE_TYPE mechUsage, CK_ATTRIBUTE_TYPE keyUsage, + SFTKContextType contextType, PRBool isEncrypt) +{ + SFTKSession *session; + SFTKObject *key; + SFTKSessionContext *context; + SFTKAttribute *att; +#ifndef NSS_DISABLE_DEPRECATED_RC2 + CK_RC2_CBC_PARAMS *rc2_param; + unsigned effectiveKeyLength; +#endif +#if NSS_SOFTOKEN_DOES_RC5 + CK_RC5_CBC_PARAMS *rc5_param; + SECItem rc5Key; +#endif + CK_NSS_GCM_PARAMS nss_gcm_param; + void *aes_param; + CK_NSS_AEAD_PARAMS nss_aead_params; + CK_NSS_AEAD_PARAMS *nss_aead_params_ptr = NULL; + CK_KEY_TYPE key_type; + CK_RV crv = CKR_OK; + unsigned char newdeskey[24]; + PRBool useNewKey = PR_FALSE; + int t; + + if (!pMechanism) { + return CKR_MECHANISM_PARAM_INVALID; + } + + crv = sftk_MechAllowsOperation(pMechanism->mechanism, mechUsage); + if (crv != CKR_OK) + return crv; + + session = sftk_SessionFromHandle(hSession); + if (session == NULL) + return CKR_SESSION_HANDLE_INVALID; + + crv = sftk_InitGeneric(session, pMechanism, &context, contextType, &key, + hKey, &key_type, + isEncrypt ? CKO_PUBLIC_KEY : CKO_PRIVATE_KEY, + keyUsage); + + if (crv != CKR_OK) { + sftk_FreeSession(session); + return crv; + } + + context->doPad = PR_FALSE; + switch (pMechanism->mechanism) { + case CKM_RSA_PKCS: + case CKM_RSA_X_509: + if (key_type != CKK_RSA) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + context->multi = PR_FALSE; + context->rsa = PR_TRUE; + if (isEncrypt) { + NSSLOWKEYPublicKey *pubKey = sftk_GetPubKey(key, CKK_RSA, &crv); + if (pubKey == NULL) { + crv = CKR_KEY_HANDLE_INVALID; + break; + } + context->maxLen = nsslowkey_PublicModulusLen(pubKey); + context->cipherInfo = (void *)pubKey; + context->update = (SFTKCipher)(pMechanism->mechanism == CKM_RSA_X_509 + ? sftk_RSAEncryptRaw + : sftk_RSAEncrypt); + } else { + NSSLOWKEYPrivateKey *privKey = sftk_GetPrivKey(key, CKK_RSA, &crv); + if (privKey == NULL) { + crv = CKR_KEY_HANDLE_INVALID; + break; + } + context->maxLen = nsslowkey_PrivateModulusLen(privKey); + context->cipherInfo = (void *)privKey; + context->update = (SFTKCipher)(pMechanism->mechanism == CKM_RSA_X_509 + ? sftk_RSADecryptRaw + : sftk_RSADecrypt); + } + context->destroy = sftk_Null; + break; + case CKM_RSA_PKCS_OAEP: + if (key_type != CKK_RSA) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + if (pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_OAEP_PARAMS) || + !sftk_ValidateOaepParams((CK_RSA_PKCS_OAEP_PARAMS *)pMechanism->pParameter)) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + context->multi = PR_FALSE; + context->rsa = PR_TRUE; + { + SFTKOAEPInfo *info; + CK_RSA_PKCS_OAEP_PARAMS *params = + (CK_RSA_PKCS_OAEP_PARAMS *)pMechanism->pParameter; + /* make a copy of the source data value for future + * use (once the user has reclaimed his data in pParameter)*/ + void *newSource = NULL; + if (params->pSourceData) { + newSource = PORT_Alloc(params->ulSourceDataLen); + if (newSource == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + PORT_Memcpy(newSource, params->pSourceData, params->ulSourceDataLen); + } + info = PORT_New(SFTKOAEPInfo); + if (info == NULL) { + PORT_ZFree(newSource, params->ulSourceDataLen); + crv = CKR_HOST_MEMORY; + break; + } + info->params = *params; + info->params.pSourceData = newSource; + info->isEncrypt = isEncrypt; + + /* now setup encryption and decryption contexts */ + if (isEncrypt) { + info->key.pub = sftk_GetPubKey(key, CKK_RSA, &crv); + if (info->key.pub == NULL) { + sftk_freeRSAOAEPInfo(info, PR_TRUE); + crv = CKR_KEY_HANDLE_INVALID; + break; + } + context->update = (SFTKCipher)sftk_RSAEncryptOAEP; + context->maxLen = nsslowkey_PublicModulusLen(info->key.pub); + } else { + info->key.priv = sftk_GetPrivKey(key, CKK_RSA, &crv); + if (info->key.priv == NULL) { + sftk_freeRSAOAEPInfo(info, PR_TRUE); + crv = CKR_KEY_HANDLE_INVALID; + break; + } + context->update = (SFTKCipher)sftk_RSADecryptOAEP; + context->maxLen = nsslowkey_PrivateModulusLen(info->key.priv); + } + context->cipherInfo = info; + } + context->destroy = (SFTKDestroy)sftk_freeRSAOAEPInfo; + break; +#ifndef NSS_DISABLE_DEPRECATED_RC2 + case CKM_RC2_CBC_PAD: + context->doPad = PR_TRUE; + /* fall thru */ + case CKM_RC2_ECB: + case CKM_RC2_CBC: + context->blockSize = 8; + if (key_type != CKK_RC2) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + att = sftk_FindAttribute(key, CKA_VALUE); + if (att == NULL) { + crv = CKR_KEY_HANDLE_INVALID; + break; + } + + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_RC2_CBC_PARAMS))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + rc2_param = (CK_RC2_CBC_PARAMS *)pMechanism->pParameter; + effectiveKeyLength = (rc2_param->ulEffectiveBits + 7) / 8; + context->cipherInfo = + RC2_CreateContext((unsigned char *)att->attrib.pValue, + att->attrib.ulValueLen, rc2_param->iv, + pMechanism->mechanism == CKM_RC2_ECB ? NSS_RC2 : NSS_RC2_CBC, effectiveKeyLength); + sftk_FreeAttribute(att); + if (context->cipherInfo == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + context->update = (SFTKCipher)(isEncrypt ? RC2_Encrypt : RC2_Decrypt); + context->destroy = (SFTKDestroy)RC2_DestroyContext; + break; +#endif /* NSS_DISABLE_DEPRECATED_RC2 */ + +#if NSS_SOFTOKEN_DOES_RC5 + case CKM_RC5_CBC_PAD: + context->doPad = PR_TRUE; + /* fall thru */ + case CKM_RC5_ECB: + case CKM_RC5_CBC: + if (key_type != CKK_RC5) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + att = sftk_FindAttribute(key, CKA_VALUE); + if (att == NULL) { + crv = CKR_KEY_HANDLE_INVALID; + break; + } + + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_RC5_CBC_PARAMS))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + rc5_param = (CK_RC5_CBC_PARAMS *)pMechanism->pParameter; + context->blockSize = rc5_param->ulWordsize * 2; + rc5Key.data = (unsigned char *)att->attrib.pValue; + rc5Key.len = att->attrib.ulValueLen; + context->cipherInfo = RC5_CreateContext(&rc5Key, rc5_param->ulRounds, + rc5_param->ulWordsize, rc5_param->pIv, + pMechanism->mechanism == CKM_RC5_ECB ? NSS_RC5 : NSS_RC5_CBC); + sftk_FreeAttribute(att); + if (context->cipherInfo == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + context->update = (SFTKCipher)(isEncrypt ? RC5_Encrypt : RC5_Decrypt); + context->destroy = (SFTKDestroy)RC5_DestroyContext; + break; +#endif + case CKM_RC4: + if (key_type != CKK_RC4) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + att = sftk_FindAttribute(key, CKA_VALUE); + if (att == NULL) { + crv = CKR_KEY_HANDLE_INVALID; + break; + } + context->cipherInfo = + RC4_CreateContext((unsigned char *)att->attrib.pValue, + att->attrib.ulValueLen); + sftk_FreeAttribute(att); + if (context->cipherInfo == NULL) { + crv = CKR_HOST_MEMORY; /* WRONG !!! */ + break; + } + context->update = (SFTKCipher)(isEncrypt ? RC4_Encrypt : RC4_Decrypt); + context->destroy = (SFTKDestroy)RC4_DestroyContext; + break; + case CKM_CDMF_CBC_PAD: + context->doPad = PR_TRUE; + /* fall thru */ + case CKM_CDMF_ECB: + case CKM_CDMF_CBC: + if (key_type != CKK_CDMF) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + t = (pMechanism->mechanism == CKM_CDMF_ECB) ? NSS_DES : NSS_DES_CBC; + goto finish_des; + case CKM_DES_ECB: + if (key_type != CKK_DES) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + t = NSS_DES; + goto finish_des; + case CKM_DES_CBC_PAD: + context->doPad = PR_TRUE; + /* fall thru */ + case CKM_DES_CBC: + if (key_type != CKK_DES) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + t = NSS_DES_CBC; + goto finish_des; + case CKM_DES3_ECB: + if ((key_type != CKK_DES2) && (key_type != CKK_DES3)) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + t = NSS_DES_EDE3; + goto finish_des; + case CKM_DES3_CBC_PAD: + context->doPad = PR_TRUE; + /* fall thru */ + case CKM_DES3_CBC: + if ((key_type != CKK_DES2) && (key_type != CKK_DES3)) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + t = NSS_DES_EDE3_CBC; + finish_des: + if ((t != NSS_DES && t != NSS_DES_EDE3) && (pMechanism->pParameter == NULL || + pMechanism->ulParameterLen < 8)) { + crv = CKR_DOMAIN_PARAMS_INVALID; + break; + } + context->blockSize = 8; + att = sftk_FindAttribute(key, CKA_VALUE); + if (att == NULL) { + crv = CKR_KEY_HANDLE_INVALID; + break; + } + if (key_type == CKK_DES2 && + (t == NSS_DES_EDE3_CBC || t == NSS_DES_EDE3)) { + /* extend DES2 key to DES3 key. */ + memcpy(newdeskey, att->attrib.pValue, 16); + memcpy(newdeskey + 16, newdeskey, 8); + useNewKey = PR_TRUE; + } else if (key_type == CKK_CDMF) { + crv = sftk_cdmf2des((unsigned char *)att->attrib.pValue, newdeskey); + if (crv != CKR_OK) { + sftk_FreeAttribute(att); + break; + } + useNewKey = PR_TRUE; + } + context->cipherInfo = DES_CreateContext( + useNewKey ? newdeskey : (unsigned char *)att->attrib.pValue, + (unsigned char *)pMechanism->pParameter, t, isEncrypt); + if (useNewKey) + memset(newdeskey, 0, sizeof newdeskey); + sftk_FreeAttribute(att); + if (context->cipherInfo == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + context->update = (SFTKCipher)(isEncrypt ? DES_Encrypt : DES_Decrypt); + context->destroy = (SFTKDestroy)DES_DestroyContext; + break; +#ifndef NSS_DISABLE_DEPRECATED_SEED + case CKM_SEED_CBC_PAD: + context->doPad = PR_TRUE; + /* fall thru */ + case CKM_SEED_CBC: + if (!pMechanism->pParameter || + pMechanism->ulParameterLen != 16) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + /* fall thru */ + case CKM_SEED_ECB: + context->blockSize = 16; + if (key_type != CKK_SEED) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + att = sftk_FindAttribute(key, CKA_VALUE); + if (att == NULL) { + crv = CKR_KEY_HANDLE_INVALID; + break; + } + context->cipherInfo = SEED_CreateContext( + (unsigned char *)att->attrib.pValue, + (unsigned char *)pMechanism->pParameter, + pMechanism->mechanism == CKM_SEED_ECB ? NSS_SEED : NSS_SEED_CBC, + isEncrypt); + sftk_FreeAttribute(att); + if (context->cipherInfo == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + context->update = (SFTKCipher)(isEncrypt ? SEED_Encrypt : SEED_Decrypt); + context->destroy = (SFTKDestroy)SEED_DestroyContext; + break; +#endif /* NSS_DISABLE_DEPRECATED_SEED */ + case CKM_CAMELLIA_CBC_PAD: + context->doPad = PR_TRUE; + /* fall thru */ + case CKM_CAMELLIA_CBC: + if (!pMechanism->pParameter || + pMechanism->ulParameterLen != 16) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + /* fall thru */ + case CKM_CAMELLIA_ECB: + context->blockSize = 16; + if (key_type != CKK_CAMELLIA) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + att = sftk_FindAttribute(key, CKA_VALUE); + if (att == NULL) { + crv = CKR_KEY_HANDLE_INVALID; + break; + } + context->cipherInfo = Camellia_CreateContext( + (unsigned char *)att->attrib.pValue, + (unsigned char *)pMechanism->pParameter, + pMechanism->mechanism == + CKM_CAMELLIA_ECB + ? NSS_CAMELLIA + : NSS_CAMELLIA_CBC, + isEncrypt, att->attrib.ulValueLen); + sftk_FreeAttribute(att); + if (context->cipherInfo == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + context->update = (SFTKCipher)(isEncrypt ? Camellia_Encrypt : Camellia_Decrypt); + context->destroy = (SFTKDestroy)Camellia_DestroyContext; + break; + + case CKM_AES_CBC_PAD: + context->doPad = PR_TRUE; + /* fall thru */ + case CKM_AES_ECB: + case CKM_AES_CBC: + context->blockSize = 16; + case CKM_AES_CTS: + case CKM_AES_CTR: + case CKM_AES_GCM: + aes_param = pMechanism->pParameter; + /* + * Due to a mismatch between the documentation and the header + * file, two different definitions for CK_GCM_PARAMS exist. + * The header file is normative according to Oasis, but NSS used + * the documentation. In PKCS #11 v3.0, this was reconciled in + * favor of the header file definition. To maintain binary + * compatibility, NSS now defines CK_GCM_PARAMS_V3 as the official + * version v3 (V2.4 header file) and CK_NSS_GCM_PARAMS as the + * legacy (V2.4 documentation, NSS version). CK_GCM_PARAMS + * is defined as CK_GCM_PARAMS_V3 if NSS_PKCS11_2_0_COMPAT is not + * defined and CK_NSS_GCM_PARAMS if it is. Internally + * softoken continues to use the legacy version. The code below + * automatically detects which parameter was passed in and + * converts CK_GCM_PARAMS_V3 to the CK_NSS_GCM_PARAMS (legacy + * version) on the fly. NSS proper will eventually start + * using the CK_GCM_PARAMS_V3 version and fall back to the + * CK_NSS_GCM_PARAMS if the CK_GCM_PARAMS_V3 version fails with + * CKR_MECHANISM_PARAM_INVALID. + */ + if (pMechanism->mechanism == CKM_AES_GCM) { + if (!aes_param) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + if (pMechanism->ulParameterLen == sizeof(CK_GCM_PARAMS_V3)) { + /* convert the true V3 parameters into the old NSS parameters */ + CK_GCM_PARAMS_V3 *gcm_params = (CK_GCM_PARAMS_V3 *)aes_param; + if (gcm_params->ulIvLen * 8 != gcm_params->ulIvBits) { + /* only support byte aligned IV lengths */ + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + aes_param = (void *)&nss_gcm_param; + nss_gcm_param.pIv = gcm_params->pIv; + nss_gcm_param.ulIvLen = gcm_params->ulIvLen; + nss_gcm_param.pAAD = gcm_params->pAAD; + nss_gcm_param.ulAADLen = gcm_params->ulAADLen; + nss_gcm_param.ulTagBits = gcm_params->ulTagBits; + } else if (pMechanism->ulParameterLen != sizeof(CK_NSS_GCM_PARAMS)) { + /* neither old nor new style params, must be invalid */ + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + } else if ((pMechanism->mechanism == CKM_AES_CTR && BAD_PARAM_CAST(pMechanism, sizeof(CK_AES_CTR_PARAMS))) || + ((pMechanism->mechanism == CKM_AES_CBC || pMechanism->mechanism == CKM_AES_CTS) && BAD_PARAM_CAST(pMechanism, AES_BLOCK_SIZE))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + + if (pMechanism->mechanism == CKM_AES_GCM) { + context->multi = PR_FALSE; + } + if (key_type != CKK_AES) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + att = sftk_FindAttribute(key, CKA_VALUE); + if (att == NULL) { + crv = CKR_KEY_HANDLE_INVALID; + break; + } + context->cipherInfo = AES_CreateContext( + (unsigned char *)att->attrib.pValue, + (unsigned char *)aes_param, + sftk_aes_mode(pMechanism->mechanism), + isEncrypt, att->attrib.ulValueLen, 16); + sftk_FreeAttribute(att); + if (context->cipherInfo == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + context->update = (SFTKCipher)(isEncrypt ? AES_Encrypt : AES_Decrypt); + context->destroy = (SFTKDestroy)AES_DestroyContext; + break; + + case CKM_NSS_CHACHA20_POLY1305: + case CKM_CHACHA20_POLY1305: + if (pMechanism->mechanism == CKM_NSS_CHACHA20_POLY1305) { + if (key_type != CKK_NSS_CHACHA20) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + if ((pMechanism->pParameter == NULL) || + (pMechanism->ulParameterLen != sizeof(CK_NSS_AEAD_PARAMS))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + nss_aead_params_ptr = (CK_NSS_AEAD_PARAMS *)pMechanism->pParameter; + } else { + CK_SALSA20_CHACHA20_POLY1305_PARAMS_PTR chacha_poly_params; + if (key_type != CKK_CHACHA20) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + if ((pMechanism->pParameter == NULL) || + (pMechanism->ulParameterLen != + sizeof(CK_SALSA20_CHACHA20_POLY1305_PARAMS))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + chacha_poly_params = (CK_SALSA20_CHACHA20_POLY1305_PARAMS_PTR) + pMechanism->pParameter; + nss_aead_params_ptr = &nss_aead_params; + nss_aead_params.pNonce = chacha_poly_params->pNonce; + nss_aead_params.ulNonceLen = chacha_poly_params->ulNonceLen; + nss_aead_params.pAAD = chacha_poly_params->pAAD; + nss_aead_params.ulAADLen = chacha_poly_params->ulAADLen; + nss_aead_params.ulTagLen = 16; /* Poly1305 is always 16 */ + } + + context->multi = PR_FALSE; + att = sftk_FindAttribute(key, CKA_VALUE); + if (att == NULL) { + crv = CKR_KEY_HANDLE_INVALID; + break; + } + context->cipherInfo = sftk_ChaCha20Poly1305_CreateContext( + (unsigned char *)att->attrib.pValue, att->attrib.ulValueLen, + nss_aead_params_ptr); + sftk_FreeAttribute(att); + if (context->cipherInfo == NULL) { + crv = sftk_MapCryptError(PORT_GetError()); + break; + } + context->update = (SFTKCipher)(isEncrypt ? sftk_ChaCha20Poly1305_Encrypt : sftk_ChaCha20Poly1305_Decrypt); + context->destroy = (SFTKDestroy)sftk_ChaCha20Poly1305_DestroyContext; + break; + + case CKM_NSS_CHACHA20_CTR: /* old NSS private version */ + case CKM_CHACHA20: /* PKCS #11 v3 version */ + { + unsigned char *counter; + unsigned char *nonce; + unsigned long counter_len; + unsigned long nonce_len; + context->multi = PR_FALSE; + if (pMechanism->mechanism == CKM_NSS_CHACHA20_CTR) { + if (key_type != CKK_NSS_CHACHA20) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + if (pMechanism->pParameter == NULL || pMechanism->ulParameterLen != 16) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + counter_len = 4; + counter = pMechanism->pParameter; + nonce = counter + 4; + nonce_len = 12; + } else { + CK_CHACHA20_PARAMS_PTR chacha20_param_ptr; + if (key_type != CKK_CHACHA20) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + if (pMechanism->pParameter == NULL || pMechanism->ulParameterLen != sizeof(CK_CHACHA20_PARAMS)) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + chacha20_param_ptr = (CK_CHACHA20_PARAMS_PTR)pMechanism->pParameter; + if ((chacha20_param_ptr->blockCounterBits != 32) && + (chacha20_param_ptr->blockCounterBits != 64)) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + counter_len = chacha20_param_ptr->blockCounterBits / PR_BITS_PER_BYTE; + counter = chacha20_param_ptr->pBlockCounter; + nonce = chacha20_param_ptr->pNonce; + nonce_len = chacha20_param_ptr->ulNonceBits / PR_BITS_PER_BYTE; + } + + att = sftk_FindAttribute(key, CKA_VALUE); + if (att == NULL) { + crv = CKR_KEY_HANDLE_INVALID; + break; + } + SFTKChaCha20CtrInfo *ctx = PORT_ZNew(SFTKChaCha20CtrInfo); + if (!ctx) { + sftk_FreeAttribute(att); + crv = CKR_HOST_MEMORY; + break; + } + if (att->attrib.ulValueLen != sizeof(ctx->key)) { + sftk_FreeAttribute(att); + PORT_Free(ctx); + crv = CKR_KEY_HANDLE_INVALID; + break; + } + memcpy(ctx->key, att->attrib.pValue, att->attrib.ulValueLen); + sftk_FreeAttribute(att); + + /* make sure we don't overflow our parameters */ + if ((sizeof(ctx->counter) < counter_len) || + (sizeof(ctx->nonce) < nonce_len)) { + PORT_Free(ctx); + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + + /* The counter is little endian. */ + int i = 0; + for (; i < counter_len; ++i) { + ctx->counter |= (PRUint32)counter[i] << (i * 8); + } + memcpy(ctx->nonce, nonce, nonce_len); + context->cipherInfo = ctx; + context->update = (SFTKCipher)sftk_ChaCha20Ctr; + context->destroy = (SFTKDestroy)sftk_ChaCha20Ctr_DestroyContext; + break; + } + + case CKM_NSS_AES_KEY_WRAP_PAD: + case CKM_AES_KEY_WRAP_PAD: + context->doPad = PR_TRUE; + /* fall thru */ + case CKM_NSS_AES_KEY_WRAP: + case CKM_AES_KEY_WRAP: + context->blockSize = 8; + case CKM_AES_KEY_WRAP_KWP: + context->multi = PR_FALSE; + if (key_type != CKK_AES) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + att = sftk_FindAttribute(key, CKA_VALUE); + if (att == NULL) { + crv = CKR_KEY_HANDLE_INVALID; + break; + } + context->cipherInfo = AESKeyWrap_CreateContext( + (unsigned char *)att->attrib.pValue, + (unsigned char *)pMechanism->pParameter, + isEncrypt, att->attrib.ulValueLen); + sftk_FreeAttribute(att); + if (context->cipherInfo == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + if (pMechanism->mechanism == CKM_AES_KEY_WRAP_KWP) { + context->update = (SFTKCipher)(isEncrypt ? AESKeyWrap_EncryptKWP + : AESKeyWrap_DecryptKWP); + } else { + context->update = (SFTKCipher)(isEncrypt ? AESKeyWrap_Encrypt + : AESKeyWrap_Decrypt); + } + context->destroy = (SFTKDestroy)AESKeyWrap_DestroyContext; + break; + + default: + crv = CKR_MECHANISM_INVALID; + break; + } + + if (crv != CKR_OK) { + sftk_FreeContext(context); + sftk_FreeSession(session); + return crv; + } + sftk_SetContextByType(session, contextType, context); + sftk_FreeSession(session); + return CKR_OK; +} + +/* NSC_EncryptInit initializes an encryption operation. */ +CK_RV +NSC_EncryptInit(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) +{ + CHECK_FORK(); + return sftk_CryptInit(hSession, pMechanism, hKey, CKA_ENCRYPT, CKA_ENCRYPT, + SFTK_ENCRYPT, PR_TRUE); +} + +/* NSC_EncryptUpdate continues a multiple-part encryption operation. */ +CK_RV +NSC_EncryptUpdate(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, + CK_ULONG_PTR pulEncryptedPartLen) +{ + SFTKSessionContext *context; + unsigned int outlen, i; + unsigned int padoutlen = 0; + unsigned int maxout = *pulEncryptedPartLen; + CK_RV crv; + SECStatus rv; + + CHECK_FORK(); + + /* make sure we're legal */ + crv = sftk_GetContext(hSession, &context, SFTK_ENCRYPT, PR_TRUE, NULL); + if (crv != CKR_OK) + return crv; + + if (!pEncryptedPart) { + if (context->doPad) { + CK_ULONG totalDataAvailable = ulPartLen + context->padDataLength; + CK_ULONG blocksToSend = totalDataAvailable / context->blockSize; + + *pulEncryptedPartLen = blocksToSend * context->blockSize; + return CKR_OK; + } + *pulEncryptedPartLen = ulPartLen; + return CKR_OK; + } + + /* do padding */ + if (context->doPad) { + /* deal with previous buffered data */ + if (context->padDataLength != 0) { + /* fill in the padded to a full block size */ + for (i = context->padDataLength; + (ulPartLen != 0) && i < context->blockSize; i++) { + context->padBuf[i] = *pPart++; + ulPartLen--; + context->padDataLength++; + } + + /* not enough data to encrypt yet? then return */ + if (context->padDataLength != context->blockSize) { + *pulEncryptedPartLen = 0; + return CKR_OK; + } + /* encrypt the current padded data */ + rv = (*context->update)(context->cipherInfo, pEncryptedPart, + &padoutlen, maxout, context->padBuf, + context->blockSize); + if (rv != SECSuccess) { + return sftk_MapCryptError(PORT_GetError()); + } + pEncryptedPart += padoutlen; + maxout -= padoutlen; + } + /* save the residual */ + context->padDataLength = ulPartLen % context->blockSize; + if (context->padDataLength) { + PORT_Memcpy(context->padBuf, + &pPart[ulPartLen - context->padDataLength], + context->padDataLength); + ulPartLen -= context->padDataLength; + } + /* if we've exhausted our new buffer, we're done */ + if (ulPartLen == 0) { + *pulEncryptedPartLen = padoutlen; + return CKR_OK; + } + } + + /* do it: NOTE: this assumes buf size in is >= buf size out! */ + rv = (*context->update)(context->cipherInfo, pEncryptedPart, + &outlen, maxout, pPart, ulPartLen); + if (rv != SECSuccess) { + return sftk_MapCryptError(PORT_GetError()); + } + *pulEncryptedPartLen = (CK_ULONG)(outlen + padoutlen); + return CKR_OK; +} + +/* NSC_EncryptFinal finishes a multiple-part encryption operation. */ +CK_RV +NSC_EncryptFinal(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pLastEncryptedPart, CK_ULONG_PTR pulLastEncryptedPartLen) +{ + SFTKSession *session; + SFTKSessionContext *context; + unsigned int outlen, i; + unsigned int maxout = *pulLastEncryptedPartLen; + CK_RV crv; + SECStatus rv = SECSuccess; + PRBool contextFinished = PR_TRUE; + + CHECK_FORK(); + + /* make sure we're legal */ + crv = sftk_GetContext(hSession, &context, SFTK_ENCRYPT, PR_TRUE, &session); + if (crv != CKR_OK) + return crv; + + *pulLastEncryptedPartLen = 0; + if (!pLastEncryptedPart) { + /* caller is checking the amount of remaining data */ + if (context->blockSize > 0 && context->doPad) { + *pulLastEncryptedPartLen = context->blockSize; + contextFinished = PR_FALSE; /* still have padding to go */ + } + goto finish; + } + + /* do padding */ + if (context->doPad) { + unsigned char padbyte = (unsigned char)(context->blockSize - context->padDataLength); + /* fill out rest of pad buffer with pad magic*/ + for (i = context->padDataLength; i < context->blockSize; i++) { + context->padBuf[i] = padbyte; + } + rv = (*context->update)(context->cipherInfo, pLastEncryptedPart, + &outlen, maxout, context->padBuf, context->blockSize); + if (rv == SECSuccess) + *pulLastEncryptedPartLen = (CK_ULONG)outlen; + } + +finish: + if (contextFinished) + sftk_TerminateOp(session, SFTK_ENCRYPT, context); + sftk_FreeSession(session); + return (rv == SECSuccess) ? CKR_OK : sftk_MapCryptError(PORT_GetError()); +} + +/* NSC_Encrypt encrypts single-part data. */ +CK_RV +NSC_Encrypt(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, + CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData, + CK_ULONG_PTR pulEncryptedDataLen) +{ + SFTKSession *session; + SFTKSessionContext *context; + unsigned int outlen; + unsigned int maxoutlen = *pulEncryptedDataLen; + CK_RV crv; + CK_RV crv2; + SECStatus rv = SECSuccess; + SECItem pText; + + pText.type = siBuffer; + pText.data = pData; + pText.len = ulDataLen; + + CHECK_FORK(); + + /* make sure we're legal */ + crv = sftk_GetContext(hSession, &context, SFTK_ENCRYPT, PR_FALSE, &session); + if (crv != CKR_OK) + return crv; + + if (!pEncryptedData) { + outlen = context->rsa ? context->maxLen : ulDataLen + 2 * context->blockSize; + goto done; + } + + if (context->doPad) { + if (context->multi) { + CK_ULONG updateLen = maxoutlen; + CK_ULONG finalLen; + /* padding is fairly complicated, have the update and final + * code deal with it */ + sftk_FreeSession(session); + crv = NSC_EncryptUpdate(hSession, pData, ulDataLen, pEncryptedData, + &updateLen); + if (crv != CKR_OK) { + updateLen = 0; + } + maxoutlen -= updateLen; + pEncryptedData += updateLen; + finalLen = maxoutlen; + crv2 = NSC_EncryptFinal(hSession, pEncryptedData, &finalLen); + if (crv == CKR_OK && crv2 == CKR_OK) { + *pulEncryptedDataLen = updateLen + finalLen; + } + return crv == CKR_OK ? crv2 : crv; + } + /* doPad without multi means that padding must be done on the first + ** and only update. There will be no final. + */ + PORT_Assert(context->blockSize > 1); + if (context->blockSize > 1) { + CK_ULONG remainder = ulDataLen % context->blockSize; + CK_ULONG padding = context->blockSize - remainder; + pText.len += padding; + pText.data = PORT_ZAlloc(pText.len); + if (pText.data) { + memcpy(pText.data, pData, ulDataLen); + memset(pText.data + ulDataLen, padding, padding); + } else { + crv = CKR_HOST_MEMORY; + goto fail; + } + } + } + + /* do it: NOTE: this assumes buf size is big enough. */ + rv = (*context->update)(context->cipherInfo, pEncryptedData, + &outlen, maxoutlen, pText.data, pText.len); + crv = (rv == SECSuccess) ? CKR_OK : sftk_MapCryptError(PORT_GetError()); + if (pText.data != pData) + PORT_ZFree(pText.data, pText.len); +fail: + sftk_TerminateOp(session, SFTK_ENCRYPT, context); +done: + sftk_FreeSession(session); + if (crv == CKR_OK) { + *pulEncryptedDataLen = (CK_ULONG)outlen; + } + return crv; +} + +/* + ************** Crypto Functions: Decrypt ************************ + */ + +/* NSC_DecryptInit initializes a decryption operation. */ +CK_RV +NSC_DecryptInit(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) +{ + CHECK_FORK(); + return sftk_CryptInit(hSession, pMechanism, hKey, CKA_DECRYPT, CKA_DECRYPT, + SFTK_DECRYPT, PR_FALSE); +} + +/* NSC_DecryptUpdate continues a multiple-part decryption operation. */ +CK_RV +NSC_DecryptUpdate(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, + CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) +{ + SFTKSessionContext *context; + unsigned int padoutlen = 0; + unsigned int outlen; + unsigned int maxout = *pulPartLen; + CK_RV crv; + SECStatus rv; + + CHECK_FORK(); + + /* make sure we're legal */ + crv = sftk_GetContext(hSession, &context, SFTK_DECRYPT, PR_TRUE, NULL); + if (crv != CKR_OK) + return crv; + + /* this can only happen on an NSS programming error */ + PORT_Assert((context->padDataLength == 0) || context->padDataLength == context->blockSize); + + if (context->doPad) { + /* Check the data length for block ciphers. If we are padding, + * then we must be using a block cipher. In the non-padding case + * the error will be returned by the underlying decryption + * function when we do the actual decrypt. We need to do the + * check here to avoid returning a negative length to the caller + * or reading before the beginning of the pEncryptedPart buffer. + */ + if ((ulEncryptedPartLen == 0) || + (ulEncryptedPartLen % context->blockSize) != 0) { + return CKR_ENCRYPTED_DATA_LEN_RANGE; + } + } + + if (!pPart) { + if (context->doPad) { + *pulPartLen = + ulEncryptedPartLen + context->padDataLength - context->blockSize; + return CKR_OK; + } + /* for stream ciphers there is are no constraints on ulEncryptedPartLen. + * for block ciphers, it must be a multiple of blockSize. The error is + * detected when this function is called again do decrypt the output. + */ + *pulPartLen = ulEncryptedPartLen; + return CKR_OK; + } + + if (context->doPad) { + /* first decrypt our saved buffer */ + if (context->padDataLength != 0) { + rv = (*context->update)(context->cipherInfo, pPart, &padoutlen, + maxout, context->padBuf, context->blockSize); + if (rv != SECSuccess) + return sftk_MapDecryptError(PORT_GetError()); + pPart += padoutlen; + maxout -= padoutlen; + } + /* now save the final block for the next decrypt or the final */ + PORT_Memcpy(context->padBuf, &pEncryptedPart[ulEncryptedPartLen - context->blockSize], + context->blockSize); + context->padDataLength = context->blockSize; + ulEncryptedPartLen -= context->padDataLength; + } + + /* do it: NOTE: this assumes buf size in is >= buf size out! */ + rv = (*context->update)(context->cipherInfo, pPart, &outlen, + maxout, pEncryptedPart, ulEncryptedPartLen); + if (rv != SECSuccess) { + return sftk_MapDecryptError(PORT_GetError()); + } + *pulPartLen = (CK_ULONG)(outlen + padoutlen); + return CKR_OK; +} + +/* NSC_DecryptFinal finishes a multiple-part decryption operation. */ +CK_RV +NSC_DecryptFinal(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pLastPart, CK_ULONG_PTR pulLastPartLen) +{ + SFTKSession *session; + SFTKSessionContext *context; + unsigned int outlen; + unsigned int maxout = *pulLastPartLen; + CK_RV crv; + SECStatus rv = SECSuccess; + + CHECK_FORK(); + + /* make sure we're legal */ + crv = sftk_GetContext(hSession, &context, SFTK_DECRYPT, PR_TRUE, &session); + if (crv != CKR_OK) + return crv; + + *pulLastPartLen = 0; + if (!pLastPart) { + /* caller is checking the amount of remaining data */ + if (context->padDataLength > 0) { + *pulLastPartLen = context->padDataLength; + } + goto finish; + } + + if (context->doPad) { + /* decrypt our saved buffer */ + if (context->padDataLength != 0) { + /* this assumes that pLastPart is big enough to hold the *whole* + * buffer!!! */ + rv = (*context->update)(context->cipherInfo, pLastPart, &outlen, + maxout, context->padBuf, context->blockSize); + if (rv != SECSuccess) { + crv = sftk_MapDecryptError(PORT_GetError()); + } else { + unsigned int padSize = 0; + crv = sftk_CheckCBCPadding(pLastPart, outlen, + context->blockSize, &padSize); + /* Update pulLastPartLen, in constant time, if crv is OK */ + *pulLastPartLen = PORT_CT_SEL(sftk_CKRVToMask(crv), outlen - padSize, *pulLastPartLen); + } + } + } + + sftk_TerminateOp(session, SFTK_DECRYPT, context); +finish: + sftk_FreeSession(session); + return crv; +} + +/* NSC_Decrypt decrypts encrypted data in a single part. */ +CK_RV +NSC_Decrypt(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, CK_BYTE_PTR pData, + CK_ULONG_PTR pulDataLen) +{ + SFTKSession *session; + SFTKSessionContext *context; + unsigned int outlen; + unsigned int maxoutlen = *pulDataLen; + CK_RV crv; + CK_RV crv2; + SECStatus rv = SECSuccess; + + CHECK_FORK(); + + /* make sure we're legal */ + crv = sftk_GetContext(hSession, &context, SFTK_DECRYPT, PR_FALSE, &session); + if (crv != CKR_OK) + return crv; + + if (!pData) { + *pulDataLen = (CK_ULONG)(ulEncryptedDataLen + context->blockSize); + goto done; + } + + if (context->doPad && context->multi) { + CK_ULONG updateLen = maxoutlen; + CK_ULONG finalLen; + /* padding is fairly complicated, have the update and final + * code deal with it */ + sftk_FreeSession(session); + crv = NSC_DecryptUpdate(hSession, pEncryptedData, ulEncryptedDataLen, + pData, &updateLen); + if (crv == CKR_OK) { + maxoutlen -= updateLen; + pData += updateLen; + } + finalLen = maxoutlen; + crv2 = NSC_DecryptFinal(hSession, pData, &finalLen); + if (crv == CKR_OK) { + *pulDataLen = PORT_CT_SEL(sftk_CKRVToMask(crv2), updateLen + finalLen, *pulDataLen); + return crv2; + } else { + return crv; + } + } + + rv = (*context->update)(context->cipherInfo, pData, &outlen, maxoutlen, + pEncryptedData, ulEncryptedDataLen); + /* XXX need to do MUCH better error mapping than this. */ + crv = (rv == SECSuccess) ? CKR_OK : sftk_MapDecryptError(PORT_GetError()); + if (rv == SECSuccess) { + if (context->doPad) { + unsigned int padSize = 0; + crv = sftk_CheckCBCPadding(pData, outlen, context->blockSize, + &padSize); + /* Update pulDataLen, in constant time, if crv is OK */ + *pulDataLen = PORT_CT_SEL(sftk_CKRVToMask(crv), outlen - padSize, *pulDataLen); + } else { + *pulDataLen = (CK_ULONG)outlen; + } + } + sftk_TerminateOp(session, SFTK_DECRYPT, context); +done: + sftk_FreeSession(session); + return crv; +} + +/* + ************** Crypto Functions: Digest (HASH) ************************ + */ + +/* NSC_DigestInit initializes a message-digesting operation. */ +CK_RV +NSC_DigestInit(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism) +{ + SFTKSession *session; + SFTKSessionContext *context; + CK_RV crv = CKR_OK; + + CHECK_FORK(); + + session = sftk_SessionFromHandle(hSession); + if (session == NULL) + return CKR_SESSION_HANDLE_INVALID; + crv = sftk_InitGeneric(session, pMechanism, &context, SFTK_HASH, + NULL, 0, NULL, 0, CKA_DIGEST); + if (crv != CKR_OK) { + sftk_FreeSession(session); + return crv; + } + +#define INIT_MECH(mmm) \ + case CKM_##mmm: { \ + mmm##Context *mmm##_ctx = mmm##_NewContext(); \ + context->cipherInfo = (void *)mmm##_ctx; \ + context->cipherInfoLen = mmm##_FlattenSize(mmm##_ctx); \ + context->currentMech = CKM_##mmm; \ + context->hashUpdate = (SFTKHash)mmm##_Update; \ + context->end = (SFTKEnd)mmm##_End; \ + context->destroy = (SFTKDestroy)mmm##_DestroyContext; \ + context->maxLen = mmm##_LENGTH; \ + if (mmm##_ctx) \ + mmm##_Begin(mmm##_ctx); \ + else \ + crv = CKR_HOST_MEMORY; \ + break; \ + } + + switch (pMechanism->mechanism) { + INIT_MECH(MD2) + INIT_MECH(MD5) + INIT_MECH(SHA1) + INIT_MECH(SHA224) + INIT_MECH(SHA256) + INIT_MECH(SHA384) + INIT_MECH(SHA512) + INIT_MECH(SHA3_224) + INIT_MECH(SHA3_256) + INIT_MECH(SHA3_384) + INIT_MECH(SHA3_512) + + default: + crv = CKR_MECHANISM_INVALID; + break; + } + + if (crv != CKR_OK) { + sftk_FreeContext(context); + sftk_FreeSession(session); + return crv; + } + sftk_SetContextByType(session, SFTK_HASH, context); + sftk_FreeSession(session); + return CKR_OK; +} + +/* NSC_Digest digests data in a single part. */ +CK_RV +NSC_Digest(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pDigest, + CK_ULONG_PTR pulDigestLen) +{ + SFTKSession *session; + SFTKSessionContext *context; + unsigned int digestLen; + unsigned int maxout = *pulDigestLen; + CK_RV crv; + + CHECK_FORK(); + + /* make sure we're legal */ + crv = sftk_GetContext(hSession, &context, SFTK_HASH, PR_FALSE, &session); + if (crv != CKR_OK) + return crv; + + if (pDigest == NULL) { + *pulDigestLen = context->maxLen; + goto finish; + } + +#if (ULONG_MAX > UINT_MAX) + /* The context->hashUpdate function takes an unsigned int for its data + * length argument, but NSC_Digest takes an unsigned long. */ + while (ulDataLen > UINT_MAX) { + (*context->hashUpdate)(context->cipherInfo, pData, UINT_MAX); + pData += UINT_MAX; + ulDataLen -= UINT_MAX; + } +#endif + (*context->hashUpdate)(context->cipherInfo, pData, ulDataLen); + + /* NOTE: this assumes buf size is bigenough for the algorithm */ + (*context->end)(context->cipherInfo, pDigest, &digestLen, maxout); + *pulDigestLen = digestLen; + + sftk_TerminateOp(session, SFTK_HASH, context); +finish: + sftk_FreeSession(session); + return CKR_OK; +} + +/* NSC_DigestUpdate continues a multiple-part message-digesting operation. */ +CK_RV +NSC_DigestUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, + CK_ULONG ulPartLen) +{ + SFTKSessionContext *context; + CK_RV crv; + + CHECK_FORK(); + + /* make sure we're legal */ + crv = sftk_GetContext(hSession, &context, SFTK_HASH, PR_TRUE, NULL); + if (crv != CKR_OK) + return crv; + +#if (ULONG_MAX > UINT_MAX) + /* The context->hashUpdate function takes an unsigned int for its data + * length argument, but NSC_DigestUpdate takes an unsigned long. */ + while (ulPartLen > UINT_MAX) { + (*context->hashUpdate)(context->cipherInfo, pPart, UINT_MAX); + pPart += UINT_MAX; + ulPartLen -= UINT_MAX; + } +#endif + (*context->hashUpdate)(context->cipherInfo, pPart, ulPartLen); + + return CKR_OK; +} + +/* NSC_DigestFinal finishes a multiple-part message-digesting operation. */ +CK_RV +NSC_DigestFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pDigest, + CK_ULONG_PTR pulDigestLen) +{ + SFTKSession *session; + SFTKSessionContext *context; + unsigned int maxout = *pulDigestLen; + unsigned int digestLen; + CK_RV crv; + + CHECK_FORK(); + + /* make sure we're legal */ + crv = sftk_GetContext(hSession, &context, SFTK_HASH, PR_TRUE, &session); + if (crv != CKR_OK) + return crv; + + if (pDigest != NULL) { + (*context->end)(context->cipherInfo, pDigest, &digestLen, maxout); + *pulDigestLen = digestLen; + sftk_TerminateOp(session, SFTK_HASH, context); + } else { + *pulDigestLen = context->maxLen; + } + + sftk_FreeSession(session); + return CKR_OK; +} + +/* + * these helper functions are used by Generic Macing and Signing functions + * that use hashes as part of their operations. + */ +#define DOSUB(mmm) \ + static CK_RV \ + sftk_doSub##mmm(SFTKSessionContext *context) \ + { \ + mmm##Context *mmm##_ctx = mmm##_NewContext(); \ + context->hashInfo = (void *)mmm##_ctx; \ + context->hashUpdate = (SFTKHash)mmm##_Update; \ + context->end = (SFTKEnd)mmm##_End; \ + context->hashdestroy = (SFTKDestroy)mmm##_DestroyContext; \ + if (!context->hashInfo) { \ + return CKR_HOST_MEMORY; \ + } \ + mmm##_Begin(mmm##_ctx); \ + return CKR_OK; \ + } + +DOSUB(MD2) +DOSUB(MD5) +DOSUB(SHA1) +DOSUB(SHA224) +DOSUB(SHA256) +DOSUB(SHA384) +DOSUB(SHA512) + +static SECStatus +sftk_SignCopy( + CK_ULONG *copyLen, + void *out, unsigned int *outLength, + unsigned int maxLength, + const unsigned char *hashResult, + unsigned int hashResultLength) +{ + unsigned int toCopy = *copyLen; + if (toCopy > maxLength) { + toCopy = maxLength; + } + if (toCopy > hashResultLength) { + toCopy = hashResultLength; + } + memcpy(out, hashResult, toCopy); + if (outLength) { + *outLength = toCopy; + } + return SECSuccess; +} + +/* Verify is just a compare for HMAC */ +static SECStatus +sftk_HMACCmp(CK_ULONG *copyLen, unsigned char *sig, unsigned int sigLen, + unsigned char *hash, unsigned int hashLen) +{ + if (NSS_SecureMemcmp(sig, hash, *copyLen) == 0) { + return SECSuccess; + } + + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + return SECFailure; +} + +/* + * common HMAC + CMAC initialization routine + */ +static CK_RV +sftk_doMACInit(CK_MECHANISM_TYPE mech, SFTKSessionContext *session, + SFTKObject *key, CK_ULONG mac_size) +{ + CK_RV crv; + sftk_MACCtx *context; + CK_ULONG *intpointer; + PRBool isFIPS = sftk_isFIPS(key->slot->slotID); + + /* Set up the initial context. */ + crv = sftk_MAC_Create(mech, key, &context); + if (crv != CKR_OK) { + return crv; + } + + session->hashInfo = context; + session->multi = PR_TRUE; + + /* 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; + } + + /* 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; + session->cipherInfo = intpointer; + + /* Since we're only "hashing", copy the result from session->end to the + * caller using sftk_SignCopy. */ + session->update = (SFTKCipher)sftk_SignCopy; + session->verify = (SFTKVerify)sftk_HMACCmp; + session->destroy = (SFTKDestroy)sftk_Space; + + session->maxLen = context->mac_size; + + return CKR_OK; +} + +/* + * SSL Macing support. SSL Macs are inited, then update with the base + * hashing algorithm, then finalized in sign and verify + */ + +/* + * FROM SSL: + * 60 bytes is 3 times the maximum length MAC size that is supported. + * We probably should have one copy of this table. We still need this table + * in ssl to 'sign' the handshake hashes. + */ +static unsigned char ssl_pad_1[60] = { + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36 +}; +static unsigned char ssl_pad_2[60] = { + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c +}; + +static SECStatus +sftk_SSLMACSign(SFTKSSLMACInfo *info, unsigned char *sig, unsigned int *sigLen, + unsigned int maxLen, unsigned char *hash, unsigned int hashLen) +{ + unsigned char tmpBuf[SFTK_MAX_MAC_LENGTH]; + unsigned int out; + + info->begin(info->hashContext); + info->update(info->hashContext, info->key, info->keySize); + info->update(info->hashContext, ssl_pad_2, info->padSize); + info->update(info->hashContext, hash, hashLen); + info->end(info->hashContext, tmpBuf, &out, SFTK_MAX_MAC_LENGTH); + PORT_Memcpy(sig, tmpBuf, info->macSize); + PORT_Memset(tmpBuf, 0, info->macSize); + *sigLen = info->macSize; + return SECSuccess; +} + +static SECStatus +sftk_SSLMACVerify(SFTKSSLMACInfo *info, unsigned char *sig, unsigned int sigLen, + unsigned char *hash, unsigned int hashLen) +{ + unsigned char tmpBuf[SFTK_MAX_MAC_LENGTH]; + unsigned int out; + int cmp; + + info->begin(info->hashContext); + info->update(info->hashContext, info->key, info->keySize); + info->update(info->hashContext, ssl_pad_2, info->padSize); + info->update(info->hashContext, hash, hashLen); + info->end(info->hashContext, tmpBuf, &out, SFTK_MAX_MAC_LENGTH); + cmp = NSS_SecureMemcmp(sig, tmpBuf, info->macSize); + PORT_Memset(tmpBuf, 0, info->macSize); + return (cmp == 0) ? SECSuccess : SECFailure; +} + +/* + * common HMAC initalization routine + */ +static CK_RV +sftk_doSSLMACInit(SFTKSessionContext *context, SECOidTag oid, + SFTKObject *key, CK_ULONG mac_size) +{ + SFTKAttribute *keyval; + SFTKBegin begin; + int padSize; + SFTKSSLMACInfo *sslmacinfo; + CK_RV crv = CKR_MECHANISM_INVALID; + + if (oid == SEC_OID_SHA1) { + crv = sftk_doSubSHA1(context); + if (crv != CKR_OK) + return crv; + begin = (SFTKBegin)SHA1_Begin; + padSize = 40; + } else { + crv = sftk_doSubMD5(context); + if (crv != CKR_OK) + return crv; + begin = (SFTKBegin)MD5_Begin; + padSize = 48; + } + context->multi = PR_TRUE; + + keyval = sftk_FindAttribute(key, CKA_VALUE); + if (keyval == NULL) + return CKR_KEY_SIZE_RANGE; + + context->hashUpdate(context->hashInfo, keyval->attrib.pValue, + keyval->attrib.ulValueLen); + context->hashUpdate(context->hashInfo, ssl_pad_1, padSize); + sslmacinfo = (SFTKSSLMACInfo *)PORT_Alloc(sizeof(SFTKSSLMACInfo)); + if (sslmacinfo == NULL) { + sftk_FreeAttribute(keyval); + return CKR_HOST_MEMORY; + } + sslmacinfo->size = sizeof(SFTKSSLMACInfo); + sslmacinfo->macSize = mac_size; + sslmacinfo->hashContext = context->hashInfo; + PORT_Memcpy(sslmacinfo->key, keyval->attrib.pValue, + keyval->attrib.ulValueLen); + sslmacinfo->keySize = keyval->attrib.ulValueLen; + sslmacinfo->begin = begin; + sslmacinfo->end = context->end; + sslmacinfo->update = context->hashUpdate; + sslmacinfo->padSize = padSize; + sftk_FreeAttribute(keyval); + context->cipherInfo = (void *)sslmacinfo; + context->destroy = (SFTKDestroy)sftk_ZSpace; + context->update = (SFTKCipher)sftk_SSLMACSign; + context->verify = (SFTKVerify)sftk_SSLMACVerify; + context->maxLen = mac_size; + return CKR_OK; +} + +/* + ************** Crypto Functions: Sign ************************ + */ + +/** + * Check if We're using CBCMacing and initialize the session context if we are. + * @param contextType SFTK_SIGN or SFTK_VERIFY + * @param keyUsage check whether key allows this usage + */ +static CK_RV +sftk_InitCBCMac(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey, CK_ATTRIBUTE_TYPE keyUsage, + SFTKContextType contextType) + +{ + CK_MECHANISM cbc_mechanism; + CK_ULONG mac_bytes = SFTK_INVALID_MAC_SIZE; +#ifndef NSS_DISABLE_DEPRECATED_RC2 + CK_RC2_CBC_PARAMS rc2_params; +#endif +#if NSS_SOFTOKEN_DOES_RC5 + CK_RC5_CBC_PARAMS rc5_params; + CK_RC5_MAC_GENERAL_PARAMS *rc5_mac; +#endif + unsigned char ivBlock[SFTK_MAX_BLOCK_SIZE]; + unsigned char k2[SFTK_MAX_BLOCK_SIZE]; + unsigned char k3[SFTK_MAX_BLOCK_SIZE]; + SFTKSessionContext *context; + CK_RV crv; + unsigned int blockSize; + PRBool isXCBC = PR_FALSE; + + if (!pMechanism) { + return CKR_MECHANISM_PARAM_INVALID; + } + + switch (pMechanism->mechanism) { +#ifndef NSS_DISABLE_DEPRECATED_RC2 + case CKM_RC2_MAC_GENERAL: + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_RC2_MAC_GENERAL_PARAMS))) { + return CKR_MECHANISM_PARAM_INVALID; + } + mac_bytes = + ((CK_RC2_MAC_GENERAL_PARAMS *)pMechanism->pParameter)->ulMacLength; + /* fall through */ + case CKM_RC2_MAC: + /* this works because ulEffectiveBits is in the same place in both the + * CK_RC2_MAC_GENERAL_PARAMS and CK_RC2_CBC_PARAMS */ + rc2_params.ulEffectiveBits = ((CK_RC2_MAC_GENERAL_PARAMS *) + pMechanism->pParameter) + ->ulEffectiveBits; + PORT_Memset(rc2_params.iv, 0, sizeof(rc2_params.iv)); + cbc_mechanism.mechanism = CKM_RC2_CBC; + cbc_mechanism.pParameter = &rc2_params; + cbc_mechanism.ulParameterLen = sizeof(rc2_params); + blockSize = 8; + break; +#endif /* NSS_DISABLE_DEPRECATED_RC2 */ + +#if NSS_SOFTOKEN_DOES_RC5 + case CKM_RC5_MAC_GENERAL: + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_RC5_MAC_GENERAL_PARAMS))) { + return CKR_MECHANISM_PARAM_INVALID; + } + mac_bytes = + ((CK_RC5_MAC_GENERAL_PARAMS *)pMechanism->pParameter)->ulMacLength; + /* fall through */ + case CKM_RC5_MAC: + /* this works because ulEffectiveBits is in the same place in both the + * CK_RC5_MAC_GENERAL_PARAMS and CK_RC5_CBC_PARAMS */ + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_RC5_MAC_GENERAL_PARAMS))) { + return CKR_MECHANISM_PARAM_INVALID; + } + rc5_mac = (CK_RC5_MAC_GENERAL_PARAMS *)pMechanism->pParameter; + rc5_params.ulWordsize = rc5_mac->ulWordsize; + rc5_params.ulRounds = rc5_mac->ulRounds; + rc5_params.pIv = ivBlock; + if ((blockSize = rc5_mac->ulWordsize * 2) > SFTK_MAX_BLOCK_SIZE) + return CKR_MECHANISM_PARAM_INVALID; + rc5_params.ulIvLen = blockSize; + PORT_Memset(ivBlock, 0, blockSize); + cbc_mechanism.mechanism = CKM_RC5_CBC; + cbc_mechanism.pParameter = &rc5_params; + cbc_mechanism.ulParameterLen = sizeof(rc5_params); + break; +#endif + /* add cast and idea later */ + case CKM_DES_MAC_GENERAL: + mac_bytes = *(CK_ULONG *)pMechanism->pParameter; + /* fall through */ + case CKM_DES_MAC: + blockSize = 8; + PORT_Memset(ivBlock, 0, blockSize); + cbc_mechanism.mechanism = CKM_DES_CBC; + cbc_mechanism.pParameter = &ivBlock; + cbc_mechanism.ulParameterLen = blockSize; + break; + case CKM_DES3_MAC_GENERAL: + mac_bytes = *(CK_ULONG *)pMechanism->pParameter; + /* fall through */ + case CKM_DES3_MAC: + blockSize = 8; + PORT_Memset(ivBlock, 0, blockSize); + cbc_mechanism.mechanism = CKM_DES3_CBC; + cbc_mechanism.pParameter = &ivBlock; + cbc_mechanism.ulParameterLen = blockSize; + break; + case CKM_CDMF_MAC_GENERAL: + mac_bytes = *(CK_ULONG *)pMechanism->pParameter; + /* fall through */ + case CKM_CDMF_MAC: + blockSize = 8; + PORT_Memset(ivBlock, 0, blockSize); + cbc_mechanism.mechanism = CKM_CDMF_CBC; + cbc_mechanism.pParameter = &ivBlock; + cbc_mechanism.ulParameterLen = blockSize; + break; +#ifndef NSS_DISABLE_DEPRECATED_SEED + case CKM_SEED_MAC_GENERAL: + mac_bytes = *(CK_ULONG *)pMechanism->pParameter; + /* fall through */ + case CKM_SEED_MAC: + blockSize = 16; + PORT_Memset(ivBlock, 0, blockSize); + cbc_mechanism.mechanism = CKM_SEED_CBC; + cbc_mechanism.pParameter = &ivBlock; + cbc_mechanism.ulParameterLen = blockSize; + break; +#endif /* NSS_DISABLE_DEPRECATED_SEED */ + case CKM_CAMELLIA_MAC_GENERAL: + mac_bytes = *(CK_ULONG *)pMechanism->pParameter; + /* fall through */ + case CKM_CAMELLIA_MAC: + blockSize = 16; + PORT_Memset(ivBlock, 0, blockSize); + cbc_mechanism.mechanism = CKM_CAMELLIA_CBC; + cbc_mechanism.pParameter = &ivBlock; + cbc_mechanism.ulParameterLen = blockSize; + break; + case CKM_AES_MAC_GENERAL: + mac_bytes = *(CK_ULONG *)pMechanism->pParameter; + /* fall through */ + case CKM_AES_MAC: + blockSize = 16; + PORT_Memset(ivBlock, 0, blockSize); + cbc_mechanism.mechanism = CKM_AES_CBC; + cbc_mechanism.pParameter = &ivBlock; + cbc_mechanism.ulParameterLen = blockSize; + break; + case CKM_AES_XCBC_MAC_96: + case CKM_AES_XCBC_MAC: + /* The only difference between CKM_AES_XCBC_MAC + * and CKM_AES_XCBC_MAC_96 is the size of the returned mac. */ + mac_bytes = pMechanism->mechanism == CKM_AES_XCBC_MAC_96 ? 12 : 16; + blockSize = 16; + PORT_Memset(ivBlock, 0, blockSize); + cbc_mechanism.mechanism = CKM_AES_CBC; + cbc_mechanism.pParameter = &ivBlock; + cbc_mechanism.ulParameterLen = blockSize; + /* is XCBC requires extra processing at the end of the operation */ + isXCBC = PR_TRUE; + /* The input key is used to generate k1, k2, and k3. k2 and k3 + * are used at the end in the pad step. k1 replaces the input + * key in the aes cbc mac */ + crv = sftk_aes_xcbc_new_keys(hSession, hKey, &hKey, k2, k3); + if (crv != CKR_OK) { + return crv; + } + break; + default: + return CKR_FUNCTION_NOT_SUPPORTED; + } + + /* if MAC size is externally supplied, it should be checked. + */ + if (mac_bytes == SFTK_INVALID_MAC_SIZE) + mac_bytes = blockSize >> 1; + else { + if (mac_bytes > blockSize) { + crv = CKR_MECHANISM_PARAM_INVALID; + goto fail; + } + } + + crv = sftk_CryptInit(hSession, &cbc_mechanism, hKey, + CKA_ENCRYPT, /* CBC mech is able to ENCRYPT, not SIGN/VERIFY */ + keyUsage, contextType, PR_TRUE); + if (crv != CKR_OK) + goto fail; + crv = sftk_GetContext(hSession, &context, contextType, PR_TRUE, NULL); + + /* this shouldn't happen! */ + PORT_Assert(crv == CKR_OK); + if (crv != CKR_OK) + goto fail; + context->blockSize = blockSize; + context->macSize = mac_bytes; + context->isXCBC = isXCBC; + if (isXCBC) { + /* save the xcbc specific parameters */ + PORT_Memcpy(context->k2, k2, blockSize); + PORT_Memcpy(context->k3, k3, blockSize); + PORT_Memset(k2, 0, blockSize); + PORT_Memset(k3, 0, blockSize); + /* get rid of the temp key now that the context has been created */ + NSC_DestroyObject(hSession, hKey); + } + return CKR_OK; +fail: + if (isXCBC) { + PORT_Memset(k2, 0, blockSize); + PORT_Memset(k3, 0, blockSize); + NSC_DestroyObject(hSession, hKey); /* get rid of our temp key */ + } + return crv; +} + +/* + * encode RSA PKCS #1 Signature data before signing... + */ +static SECStatus +sftk_RSAHashSign(SFTKHashSignInfo *info, unsigned char *sig, + unsigned int *sigLen, unsigned int maxLen, + const unsigned char *hash, unsigned int hashLen) +{ + PORT_Assert(info->key->keyType == NSSLOWKEYRSAKey); + if (info->key->keyType != NSSLOWKEYRSAKey) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + return SECFailure; + } + + return RSA_HashSign(info->hashOid, info->key, sig, sigLen, maxLen, + hash, hashLen); +} + +/* XXX Old template; want to expunge it eventually. */ +static DERTemplate SECAlgorithmIDTemplate[] = { + { DER_SEQUENCE, + 0, NULL, sizeof(SECAlgorithmID) }, + { DER_OBJECT_ID, + offsetof(SECAlgorithmID, algorithm) }, + { DER_OPTIONAL | DER_ANY, + offsetof(SECAlgorithmID, parameters) }, + { 0 } +}; + +/* + * XXX OLD Template. Once all uses have been switched over to new one, + * remove this. + */ +static DERTemplate SGNDigestInfoTemplate[] = { + { DER_SEQUENCE, + 0, NULL, sizeof(SGNDigestInfo) }, + { DER_INLINE, + offsetof(SGNDigestInfo, digestAlgorithm), + SECAlgorithmIDTemplate }, + { DER_OCTET_STRING, + offsetof(SGNDigestInfo, digest) }, + { 0 } +}; + +/* + * encode RSA PKCS #1 Signature data before signing... + */ +SECStatus +RSA_HashSign(SECOidTag hashOid, NSSLOWKEYPrivateKey *key, + unsigned char *sig, unsigned int *sigLen, unsigned int maxLen, + const unsigned char *hash, unsigned int hashLen) +{ + SECStatus rv = SECFailure; + SECItem digder; + PLArenaPool *arena = NULL; + SGNDigestInfo *di = NULL; + + digder.data = NULL; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + goto loser; + } + + /* Construct digest info */ + di = SGN_CreateDigestInfo(hashOid, hash, hashLen); + if (!di) { + goto loser; + } + + /* Der encode the digest as a DigestInfo */ + rv = DER_Encode(arena, &digder, SGNDigestInfoTemplate, di); + if (rv != SECSuccess) { + goto loser; + } + + /* + ** Encrypt signature after constructing appropriate PKCS#1 signature + ** block + */ + rv = RSA_Sign(&key->u.rsa, sig, sigLen, maxLen, digder.data, + digder.len); + if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { + sftk_fatalError = PR_TRUE; + } + +loser: + SGN_DestroyDigestInfo(di); + if (arena != NULL) { + PORT_FreeArena(arena, PR_TRUE); + } + return rv; +} + +static SECStatus +sftk_RSASign(NSSLOWKEYPrivateKey *key, unsigned char *output, + unsigned int *outputLen, unsigned int maxOutputLen, + const unsigned char *input, unsigned int inputLen) +{ + SECStatus rv = SECFailure; + + PORT_Assert(key->keyType == NSSLOWKEYRSAKey); + if (key->keyType != NSSLOWKEYRSAKey) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + return SECFailure; + } + + rv = RSA_Sign(&key->u.rsa, output, outputLen, maxOutputLen, input, + inputLen); + if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { + sftk_fatalError = PR_TRUE; + } + return rv; +} + +static SECStatus +sftk_RSASignRaw(NSSLOWKEYPrivateKey *key, unsigned char *output, + unsigned int *outputLen, unsigned int maxOutputLen, + const unsigned char *input, unsigned int inputLen) +{ + SECStatus rv = SECFailure; + + PORT_Assert(key->keyType == NSSLOWKEYRSAKey); + if (key->keyType != NSSLOWKEYRSAKey) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + return SECFailure; + } + + rv = RSA_SignRaw(&key->u.rsa, output, outputLen, maxOutputLen, input, + inputLen); + if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { + sftk_fatalError = PR_TRUE; + } + return rv; +} + +static SECStatus +sftk_RSASignPSS(SFTKPSSSignInfo *info, unsigned char *sig, + unsigned int *sigLen, unsigned int maxLen, + const unsigned char *hash, unsigned int hashLen) +{ + SECStatus rv = SECFailure; + HASH_HashType hashAlg; + HASH_HashType maskHashAlg; + CK_RSA_PKCS_PSS_PARAMS *params = &info->params; + + PORT_Assert(info->key->keyType == NSSLOWKEYRSAKey); + if (info->key->keyType != NSSLOWKEYRSAKey) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + return SECFailure; + } + + hashAlg = sftk_GetHashTypeFromMechanism(params->hashAlg); + maskHashAlg = sftk_GetHashTypeFromMechanism(params->mgf); + + rv = RSA_SignPSS(&info->key->u.rsa, hashAlg, maskHashAlg, NULL, + params->sLen, sig, sigLen, maxLen, hash, hashLen); + if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { + sftk_fatalError = PR_TRUE; + } + return rv; +} + +static SECStatus +nsc_DSA_Verify_Stub(void *ctx, void *sigBuf, unsigned int sigLen, + void *dataBuf, unsigned int dataLen) +{ + SECItem signature, digest; + NSSLOWKEYPublicKey *key = (NSSLOWKEYPublicKey *)ctx; + + signature.data = (unsigned char *)sigBuf; + signature.len = sigLen; + digest.data = (unsigned char *)dataBuf; + digest.len = dataLen; + return DSA_VerifyDigest(&(key->u.dsa), &signature, &digest); +} + +static SECStatus +nsc_DSA_Sign_Stub(void *ctx, void *sigBuf, + unsigned int *sigLen, unsigned int maxSigLen, + void *dataBuf, unsigned int dataLen) +{ + SECItem signature, digest; + SECStatus rv; + NSSLOWKEYPrivateKey *key = (NSSLOWKEYPrivateKey *)ctx; + + signature.data = (unsigned char *)sigBuf; + signature.len = maxSigLen; + digest.data = (unsigned char *)dataBuf; + digest.len = dataLen; + rv = DSA_SignDigest(&(key->u.dsa), &signature, &digest); + if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { + sftk_fatalError = PR_TRUE; + } + *sigLen = signature.len; + return rv; +} + +static SECStatus +nsc_ECDSAVerifyStub(void *ctx, void *sigBuf, unsigned int sigLen, + void *dataBuf, unsigned int dataLen) +{ + SECItem signature, digest; + NSSLOWKEYPublicKey *key = (NSSLOWKEYPublicKey *)ctx; + + signature.data = (unsigned char *)sigBuf; + signature.len = sigLen; + digest.data = (unsigned char *)dataBuf; + digest.len = dataLen; + return ECDSA_VerifyDigest(&(key->u.ec), &signature, &digest); +} + +static SECStatus +nsc_ECDSASignStub(void *ctx, void *sigBuf, + unsigned int *sigLen, unsigned int maxSigLen, + void *dataBuf, unsigned int dataLen) +{ + SECItem signature, digest; + SECStatus rv; + NSSLOWKEYPrivateKey *key = (NSSLOWKEYPrivateKey *)ctx; + + signature.data = (unsigned char *)sigBuf; + signature.len = maxSigLen; + digest.data = (unsigned char *)dataBuf; + digest.len = dataLen; + rv = ECDSA_SignDigest(&(key->u.ec), &signature, &digest); + if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { + sftk_fatalError = PR_TRUE; + } + *sigLen = signature.len; + return rv; +} + +/* NSC_SignInit setups up the signing operations. There are three basic + * types of signing: + * (1) the tradition single part, where "Raw RSA" or "Raw DSA" is applied + * to data in a single Sign operation (which often looks a lot like an + * encrypt, with data coming in and data going out). + * (2) Hash based signing, where we continually hash the data, then apply + * some sort of signature to the end. + * (3) Block Encryption CBC MAC's, where the Data is encrypted with a key, + * and only the final block is part of the mac. + * + * For case number 3, we initialize a context much like the Encryption Context + * (in fact we share code). We detect case 3 in C_SignUpdate, C_Sign, and + * C_Final by the following method... if it's not multi-part, and it's doesn't + * have a hash context, it must be a block Encryption CBC MAC. + * + * For case number 2, we initialize a hash structure, as well as make it + * multi-part. Updates are simple calls to the hash update function. Final + * calls the hashend, then passes the result to the 'update' function (which + * operates as a final signature function). In some hash based MAC'ing (as + * opposed to hash base signatures), the update function is can be simply a + * copy (as is the case with HMAC). + */ +CK_RV +NSC_SignInit(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) +{ + SFTKSession *session; + SFTKObject *key; + SFTKSessionContext *context; + CK_KEY_TYPE key_type; + CK_RV crv = CKR_OK; + NSSLOWKEYPrivateKey *privKey; + SFTKHashSignInfo *info = NULL; + SFTKPSSSignInfo *pinfo = NULL; + + CHECK_FORK(); + + /* Block Cipher MACing Algorithms use a different Context init method..*/ + crv = sftk_InitCBCMac(hSession, pMechanism, hKey, CKA_SIGN, SFTK_SIGN); + if (crv != CKR_FUNCTION_NOT_SUPPORTED) + return crv; + + /* we're not using a block cipher mac */ + session = sftk_SessionFromHandle(hSession); + if (session == NULL) + return CKR_SESSION_HANDLE_INVALID; + crv = sftk_InitGeneric(session, pMechanism, &context, SFTK_SIGN, &key, + hKey, &key_type, CKO_PRIVATE_KEY, CKA_SIGN); + if (crv != CKR_OK) { + sftk_FreeSession(session); + return crv; + } + + context->multi = PR_FALSE; + +#define INIT_RSA_SIGN_MECH(mmm) \ + case CKM_##mmm##_RSA_PKCS: \ + context->multi = PR_TRUE; \ + crv = sftk_doSub##mmm(context); \ + if (crv != CKR_OK) \ + break; \ + context->update = (SFTKCipher)sftk_RSAHashSign; \ + info = PORT_New(SFTKHashSignInfo); \ + if (info == NULL) { \ + crv = CKR_HOST_MEMORY; \ + break; \ + } \ + info->hashOid = SEC_OID_##mmm; \ + goto finish_rsa; + + switch (pMechanism->mechanism) { + INIT_RSA_SIGN_MECH(MD5) + INIT_RSA_SIGN_MECH(MD2) + INIT_RSA_SIGN_MECH(SHA1) + INIT_RSA_SIGN_MECH(SHA224) + INIT_RSA_SIGN_MECH(SHA256) + INIT_RSA_SIGN_MECH(SHA384) + INIT_RSA_SIGN_MECH(SHA512) + + case CKM_RSA_PKCS: + context->update = (SFTKCipher)sftk_RSASign; + goto finish_rsa; + case CKM_RSA_X_509: + context->update = (SFTKCipher)sftk_RSASignRaw; + finish_rsa: + if (key_type != CKK_RSA) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + context->rsa = PR_TRUE; + privKey = sftk_GetPrivKey(key, CKK_RSA, &crv); + if (privKey == NULL) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + /* OK, info is allocated only if we're doing hash and sign mechanism. + * It's necessary to be able to set the correct OID in the final + * signature. + */ + if (info) { + info->key = privKey; + context->cipherInfo = info; + context->destroy = (SFTKDestroy)sftk_Space; + } else { + context->cipherInfo = privKey; + context->destroy = (SFTKDestroy)sftk_Null; + } + context->maxLen = nsslowkey_PrivateModulusLen(privKey); + break; + +#define INIT_RSA_PSS_SIG_MECH(mmm) \ + case CKM_##mmm##_RSA_PKCS_PSS: \ + context->multi = PR_TRUE; \ + crv = sftk_doSub##mmm(context); \ + if (crv != CKR_OK) \ + break; \ + if (pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS)) { \ + crv = CKR_MECHANISM_PARAM_INVALID; \ + break; \ + } \ + if (((const CK_RSA_PKCS_PSS_PARAMS *)pMechanism->pParameter)->hashAlg != CKM_##mmm) { \ + crv = CKR_MECHANISM_PARAM_INVALID; \ + break; \ + } \ + goto finish_rsa_pss; + INIT_RSA_PSS_SIG_MECH(SHA1) + INIT_RSA_PSS_SIG_MECH(SHA224) + INIT_RSA_PSS_SIG_MECH(SHA256) + INIT_RSA_PSS_SIG_MECH(SHA384) + INIT_RSA_PSS_SIG_MECH(SHA512) + case CKM_RSA_PKCS_PSS: + finish_rsa_pss: + if (key_type != CKK_RSA) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + context->rsa = PR_TRUE; + if (pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS) || + !sftk_ValidatePssParams((const CK_RSA_PKCS_PSS_PARAMS *)pMechanism->pParameter)) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + pinfo = PORT_New(SFTKPSSSignInfo); + if (pinfo == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + pinfo->size = sizeof(SFTKPSSSignInfo); + pinfo->params = *(CK_RSA_PKCS_PSS_PARAMS *)pMechanism->pParameter; + pinfo->key = sftk_GetPrivKey(key, CKK_RSA, &crv); + if (pinfo->key == NULL) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + context->cipherInfo = pinfo; + context->destroy = (SFTKDestroy)sftk_ZSpace; + context->update = (SFTKCipher)sftk_RSASignPSS; + context->maxLen = nsslowkey_PrivateModulusLen(pinfo->key); + break; + +#define INIT_DSA_SIG_MECH(mmm) \ + case CKM_DSA_##mmm: \ + context->multi = PR_TRUE; \ + crv = sftk_doSub##mmm(context); \ + if (crv != CKR_OK) \ + break; \ + goto finish_dsa; + INIT_DSA_SIG_MECH(SHA1) + INIT_DSA_SIG_MECH(SHA224) + INIT_DSA_SIG_MECH(SHA256) + INIT_DSA_SIG_MECH(SHA384) + INIT_DSA_SIG_MECH(SHA512) + case CKM_DSA: + finish_dsa: + if (key_type != CKK_DSA) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + privKey = sftk_GetPrivKey(key, CKK_DSA, &crv); + if (privKey == NULL) { + break; + } + context->cipherInfo = privKey; + context->update = (SFTKCipher)nsc_DSA_Sign_Stub; + context->destroy = (privKey == key->objectInfo) ? (SFTKDestroy)sftk_Null : (SFTKDestroy)sftk_FreePrivKey; + context->maxLen = DSA_MAX_SIGNATURE_LEN; + + break; + +#define INIT_ECDSA_SIG_MECH(mmm) \ + case CKM_ECDSA_##mmm: \ + context->multi = PR_TRUE; \ + crv = sftk_doSub##mmm(context); \ + if (crv != CKR_OK) \ + break; \ + goto finish_ecdsa; + INIT_ECDSA_SIG_MECH(SHA1) + INIT_ECDSA_SIG_MECH(SHA224) + INIT_ECDSA_SIG_MECH(SHA256) + INIT_ECDSA_SIG_MECH(SHA384) + INIT_ECDSA_SIG_MECH(SHA512) + case CKM_ECDSA: + finish_ecdsa: + if (key_type != CKK_EC) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + privKey = sftk_GetPrivKey(key, CKK_EC, &crv); + if (privKey == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + context->cipherInfo = privKey; + context->update = (SFTKCipher)nsc_ECDSASignStub; + context->destroy = (privKey == key->objectInfo) ? (SFTKDestroy)sftk_Null : (SFTKDestroy)sftk_FreePrivKey; + context->maxLen = MAX_ECKEY_LEN * 2; + + 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_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) + INIT_HMAC_MECH(MD5) + INIT_HMAC_MECH(SHA1) + INIT_HMAC_MECH(SHA224) + INIT_HMAC_MECH(SHA256) + INIT_HMAC_MECH(SHA384) + INIT_HMAC_MECH(SHA512) + INIT_HMAC_MECH(SHA3_224) + INIT_HMAC_MECH(SHA3_256) + INIT_HMAC_MECH(SHA3_384) + INIT_HMAC_MECH(SHA3_512) + + case CKM_AES_CMAC_GENERAL: + PORT_Assert(pMechanism->pParameter); + if (!pMechanism->pParameter || pMechanism->ulParameterLen != sizeof(CK_MAC_GENERAL_PARAMS)) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + crv = sftk_doMACInit(pMechanism->mechanism, context, key, *(CK_ULONG *)pMechanism->pParameter); + break; + case CKM_AES_CMAC: + crv = sftk_doMACInit(pMechanism->mechanism, context, key, AES_BLOCK_SIZE); + break; + case CKM_SSL3_MD5_MAC: + PORT_Assert(pMechanism->pParameter); + if (!pMechanism->pParameter) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + crv = sftk_doSSLMACInit(context, SEC_OID_MD5, key, + *(CK_ULONG *)pMechanism->pParameter); + break; + case CKM_SSL3_SHA1_MAC: + PORT_Assert(pMechanism->pParameter); + if (!pMechanism->pParameter) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + crv = sftk_doSSLMACInit(context, SEC_OID_SHA1, key, + *(CK_ULONG *)pMechanism->pParameter); + break; + case CKM_TLS_PRF_GENERAL: + crv = sftk_TLSPRFInit(context, key, key_type, HASH_AlgNULL, 0); + break; + case CKM_TLS_MAC: { + CK_TLS_MAC_PARAMS *tls12_mac_params; + HASH_HashType tlsPrfHash; + const char *label; + + if (pMechanism->ulParameterLen != sizeof(CK_TLS_MAC_PARAMS)) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + tls12_mac_params = (CK_TLS_MAC_PARAMS *)pMechanism->pParameter; + if (tls12_mac_params->prfHashMechanism == CKM_TLS_PRF) { + /* The TLS 1.0 and 1.1 PRF */ + tlsPrfHash = HASH_AlgNULL; + if (tls12_mac_params->ulMacLength != 12) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + } else { + /* The hash function for the TLS 1.2 PRF */ + tlsPrfHash = + sftk_GetHashTypeFromMechanism(tls12_mac_params->prfHashMechanism); + if (tlsPrfHash == HASH_AlgNULL || + tls12_mac_params->ulMacLength < 12) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + } + if (tls12_mac_params->ulServerOrClient == 1) { + label = "server finished"; + } else if (tls12_mac_params->ulServerOrClient == 2) { + label = "client finished"; + } else { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + crv = sftk_TLSPRFInit(context, key, key_type, tlsPrfHash, + tls12_mac_params->ulMacLength); + if (crv == CKR_OK) { + context->hashUpdate(context->hashInfo, label, 15); + } + break; + } + case CKM_NSS_TLS_PRF_GENERAL_SHA256: + crv = sftk_TLSPRFInit(context, key, key_type, HASH_AlgSHA256, 0); + break; + + case CKM_NSS_HMAC_CONSTANT_TIME: { + sftk_MACConstantTimeCtx *ctx = + sftk_HMACConstantTime_New(pMechanism, key); + CK_ULONG *intpointer; + + if (ctx == NULL) { + crv = CKR_ARGUMENTS_BAD; + break; + } + intpointer = PORT_New(CK_ULONG); + if (intpointer == NULL) { + PORT_Free(ctx); + crv = CKR_HOST_MEMORY; + break; + } + *intpointer = ctx->hash->length; + + context->cipherInfo = intpointer; + context->hashInfo = ctx; + context->currentMech = pMechanism->mechanism; + context->hashUpdate = sftk_HMACConstantTime_Update; + context->hashdestroy = sftk_MACConstantTime_DestroyContext; + context->end = sftk_MACConstantTime_EndHash; + context->update = (SFTKCipher)sftk_SignCopy; + context->destroy = sftk_Space; + context->maxLen = 64; + context->multi = PR_TRUE; + break; + } + + case CKM_NSS_SSL3_MAC_CONSTANT_TIME: { + sftk_MACConstantTimeCtx *ctx = + sftk_SSLv3MACConstantTime_New(pMechanism, key); + CK_ULONG *intpointer; + + if (ctx == NULL) { + crv = CKR_ARGUMENTS_BAD; + break; + } + intpointer = PORT_New(CK_ULONG); + if (intpointer == NULL) { + PORT_Free(ctx); + crv = CKR_HOST_MEMORY; + break; + } + *intpointer = ctx->hash->length; + + context->cipherInfo = intpointer; + context->hashInfo = ctx; + context->currentMech = pMechanism->mechanism; + context->hashUpdate = sftk_SSLv3MACConstantTime_Update; + context->hashdestroy = sftk_MACConstantTime_DestroyContext; + context->end = sftk_MACConstantTime_EndHash; + context->update = (SFTKCipher)sftk_SignCopy; + context->destroy = sftk_Space; + context->maxLen = 64; + context->multi = PR_TRUE; + break; + } + + default: + crv = CKR_MECHANISM_INVALID; + break; + } + + if (crv != CKR_OK) { + if (info) + PORT_Free(info); + if (pinfo) + PORT_ZFree(pinfo, pinfo->size); + sftk_FreeContext(context); + sftk_FreeSession(session); + return crv; + } + sftk_SetContextByType(session, SFTK_SIGN, context); + sftk_FreeSession(session); + return CKR_OK; +} + +/** MAC one block of data by block cipher + */ +static CK_RV +sftk_MACBlock(SFTKSessionContext *ctx, void *blk) +{ + unsigned int outlen; + return (SECSuccess == (ctx->update)(ctx->cipherInfo, ctx->macBuf, &outlen, + SFTK_MAX_BLOCK_SIZE, blk, ctx->blockSize)) + ? CKR_OK + : sftk_MapCryptError(PORT_GetError()); +} + +/** MAC last (incomplete) block of data by block cipher + * + * Call once, then terminate MACing operation. + */ +static CK_RV +sftk_MACFinal(SFTKSessionContext *ctx) +{ + unsigned int padLen = ctx->padDataLength; + /* pad and proceed the residual */ + if (ctx->isXCBC) { + CK_RV crv = sftk_xcbc_mac_pad(ctx->padBuf, padLen, ctx->blockSize, + ctx->k2, ctx->k3); + if (crv != CKR_OK) + return crv; + return sftk_MACBlock(ctx, ctx->padBuf); + } + if (padLen) { + /* shd clr ctx->padLen to make sftk_MACFinal idempotent */ + PORT_Memset(ctx->padBuf + padLen, 0, ctx->blockSize - padLen); + return sftk_MACBlock(ctx, ctx->padBuf); + } else + return CKR_OK; +} + +/** The common implementation for {Sign,Verify}Update. (S/V only vary in their + * setup and final operations). + * + * A call which results in an error terminates the operation [PKCS#11,v2.11] + */ +static CK_RV +sftk_MACUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, + CK_ULONG ulPartLen, SFTKContextType type) +{ + SFTKSession *session; + SFTKSessionContext *context; + CK_RV crv; + + /* make sure we're legal */ + crv = sftk_GetContext(hSession, &context, type, PR_TRUE, &session); + if (crv != CKR_OK) + return crv; + + if (context->hashInfo) { +#if (ULONG_MAX > UINT_MAX) + while (ulPartLen > UINT_MAX) { + (*context->hashUpdate)(context->cipherInfo, pPart, UINT_MAX); + pPart += UINT_MAX; + ulPartLen -= UINT_MAX; + } +#endif + (*context->hashUpdate)(context->hashInfo, pPart, ulPartLen); + } else { + /* must be block cipher MACing */ + + unsigned int blkSize = context->blockSize; + unsigned char *residual = /* free room in context->padBuf */ + context->padBuf + context->padDataLength; + unsigned int minInput = /* min input for MACing at least one block */ + blkSize - context->padDataLength; + + /* not enough data even for one block */ + if (ulPartLen <= minInput) { + PORT_Memcpy(residual, pPart, ulPartLen); + context->padDataLength += ulPartLen; + goto cleanup; + } + /* MACing residual */ + if (context->padDataLength) { + PORT_Memcpy(residual, pPart, minInput); + ulPartLen -= minInput; + pPart += minInput; + if (CKR_OK != (crv = sftk_MACBlock(context, context->padBuf))) + goto terminate; + } + /* MACing full blocks */ + while (ulPartLen > blkSize) { + if (CKR_OK != (crv = sftk_MACBlock(context, pPart))) + goto terminate; + ulPartLen -= blkSize; + pPart += blkSize; + } + /* save the residual */ + if ((context->padDataLength = ulPartLen)) + PORT_Memcpy(context->padBuf, pPart, ulPartLen); + } /* blk cipher MACing */ + + goto cleanup; + +terminate: + sftk_TerminateOp(session, type, context); +cleanup: + sftk_FreeSession(session); + return crv; +} + +/* NSC_SignUpdate continues a multiple-part signature operation, + * where the signature is (will be) an appendix to the data, + * and plaintext cannot be recovered from the signature + * + * A call which results in an error terminates the operation [PKCS#11,v2.11] + */ +CK_RV +NSC_SignUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, + CK_ULONG ulPartLen) +{ + CHECK_FORK(); + return sftk_MACUpdate(hSession, pPart, ulPartLen, SFTK_SIGN); +} + +struct SFTK_SESSION_FLAGS { + CK_FLAGS flag; + SFTKContextType type; +}; + +const static struct SFTK_SESSION_FLAGS sftk_session_flags[] = { + { CKF_ENCRYPT, SFTK_ENCRYPT }, + { CKF_DECRYPT, SFTK_DECRYPT }, + { CKF_DIGEST, SFTK_HASH }, + { CKF_SIGN, SFTK_SIGN }, + { CKF_SIGN_RECOVER, SFTK_SIGN_RECOVER }, + { CKF_VERIFY, SFTK_VERIFY }, + { CKF_VERIFY_RECOVER, SFTK_VERIFY_RECOVER }, + { CKF_MESSAGE_ENCRYPT, SFTK_MESSAGE_ENCRYPT }, + { CKF_MESSAGE_DECRYPT, SFTK_MESSAGE_DECRYPT }, + { CKF_MESSAGE_SIGN, SFTK_MESSAGE_SIGN }, + { CKF_MESSAGE_VERIFY, SFTK_MESSAGE_VERIFY }, +}; +const static int sftk_flag_count = PR_ARRAY_SIZE(sftk_session_flags); + +/* + * Cancel one or more operations running on the existing session. + */ +CK_RV +NSC_SessionCancel(CK_SESSION_HANDLE hSession, CK_FLAGS flags) +{ + SFTKSession *session; + SFTKSessionContext *context; + CK_RV gcrv = CKR_OK; + CK_RV crv; + int i; + + for (i = 0; i < sftk_flag_count; i++) { + if (flags & sftk_session_flags[i].flag) { + flags &= ~sftk_session_flags[i].flag; + crv = sftk_GetContext(hSession, &context, sftk_session_flags[i].type, PR_TRUE, &session); + if (crv != CKR_OK) { + gcrv = CKR_OPERATION_CANCEL_FAILED; + continue; + } + sftk_TerminateOp(session, sftk_session_flags[i].type, context); + } + } + if (flags & CKF_FIND_OBJECTS) { + flags &= ~CKF_FIND_OBJECTS; + crv = NSC_FindObjectsFinal(hSession); + if (crv != CKR_OK) { + gcrv = CKR_OPERATION_CANCEL_FAILED; + } + } + if (flags) { + gcrv = CKR_OPERATION_CANCEL_FAILED; + } + return gcrv; +} + +/* NSC_SignFinal finishes a multiple-part signature operation, + * returning the signature. */ +CK_RV +NSC_SignFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, + CK_ULONG_PTR pulSignatureLen) +{ + SFTKSession *session; + SFTKSessionContext *context; + unsigned int outlen; + unsigned int maxoutlen = *pulSignatureLen; + CK_RV crv; + + CHECK_FORK(); + + /* make sure we're legal */ + crv = sftk_GetContext(hSession, &context, SFTK_SIGN, PR_TRUE, &session); + if (crv != CKR_OK) + return crv; + + if (context->hashInfo) { + unsigned int digestLen; + unsigned char tmpbuf[SFTK_MAX_MAC_LENGTH]; + + if (!pSignature) { + outlen = context->maxLen; + goto finish; + } + (*context->end)(context->hashInfo, tmpbuf, &digestLen, sizeof(tmpbuf)); + if (SECSuccess != (context->update)(context->cipherInfo, pSignature, + &outlen, maxoutlen, tmpbuf, digestLen)) + crv = sftk_MapCryptError(PORT_GetError()); + /* CKR_BUFFER_TOO_SMALL here isn't continuable, let operation terminate. + * Keeping "too small" CK_RV intact is a standard violation, but allows + * application read EXACT signature length */ + PORT_Memset(tmpbuf, 0, sizeof tmpbuf); + } else { + /* must be block cipher MACing */ + outlen = context->macSize; + /* null or "too small" buf doesn't terminate operation [PKCS#11,v2.11]*/ + if (!pSignature || maxoutlen < outlen) { + if (pSignature) + crv = CKR_BUFFER_TOO_SMALL; + goto finish; + } + if (CKR_OK == (crv = sftk_MACFinal(context))) + PORT_Memcpy(pSignature, context->macBuf, outlen); + } + + sftk_TerminateOp(session, SFTK_SIGN, context); +finish: + *pulSignatureLen = outlen; + sftk_FreeSession(session); + return crv; +} + +/* NSC_Sign signs (encrypts with private key) data in a single part, + * where the signature is (will be) an appendix to the data, + * and plaintext cannot be recovered from the signature */ +CK_RV +NSC_Sign(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, + CK_ULONG_PTR pulSignatureLen) +{ + SFTKSession *session; + SFTKSessionContext *context; + CK_RV crv; + + CHECK_FORK(); + + /* make sure we're legal */ + crv = sftk_GetContext(hSession, &context, SFTK_SIGN, PR_FALSE, &session); + if (crv != CKR_OK) + return crv; + + if (!pSignature) { + /* see also how C_SignUpdate implements this */ + *pulSignatureLen = (!context->multi || context->hashInfo) + ? context->maxLen + : context->macSize; /* must be block cipher MACing */ + goto finish; + } + + /* multi part Signing are completely implemented by SignUpdate and + * sign Final */ + if (context->multi) { + /* SignFinal can't follow failed SignUpdate */ + if (CKR_OK == (crv = NSC_SignUpdate(hSession, pData, ulDataLen))) + crv = NSC_SignFinal(hSession, pSignature, pulSignatureLen); + } else { + /* single-part PKC signature (e.g. CKM_ECDSA) */ + unsigned int outlen; + unsigned int maxoutlen = *pulSignatureLen; + if (SECSuccess != (*context->update)(context->cipherInfo, pSignature, + &outlen, maxoutlen, pData, ulDataLen)) + crv = sftk_MapCryptError(PORT_GetError()); + *pulSignatureLen = (CK_ULONG)outlen; + /* "too small" here is certainly continuable */ + if (crv != CKR_BUFFER_TOO_SMALL) + sftk_TerminateOp(session, SFTK_SIGN, context); + } /* single-part */ + +finish: + sftk_FreeSession(session); + return crv; +} + +/* + ************** Crypto Functions: Sign Recover ************************ + */ +/* NSC_SignRecoverInit initializes a signature operation, + * where the (digest) data can be recovered from the signature. + * E.g. encryption with the user's private key */ +CK_RV +NSC_SignRecoverInit(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) +{ + CHECK_FORK(); + + switch (pMechanism->mechanism) { + case CKM_RSA_PKCS: + case CKM_RSA_X_509: + return NSC_SignInit(hSession, pMechanism, hKey); + default: + break; + } + return CKR_MECHANISM_INVALID; +} + +/* NSC_SignRecover signs data in a single operation + * where the (digest) data can be recovered from the signature. + * E.g. encryption with the user's private key */ +CK_RV +NSC_SignRecover(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, + CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) +{ + CHECK_FORK(); + + return NSC_Sign(hSession, pData, ulDataLen, pSignature, pulSignatureLen); +} + +/* + ************** Crypto Functions: verify ************************ + */ + +/* Handle RSA Signature formatting */ +static SECStatus +sftk_hashCheckSign(SFTKHashVerifyInfo *info, const unsigned char *sig, + unsigned int sigLen, const unsigned char *digest, + unsigned int digestLen) +{ + PORT_Assert(info->key->keyType == NSSLOWKEYRSAKey); + if (info->key->keyType != NSSLOWKEYRSAKey) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + return SECFailure; + } + + return RSA_HashCheckSign(info->hashOid, info->key, sig, sigLen, digest, + digestLen); +} + +SECStatus +RSA_HashCheckSign(SECOidTag digestOid, NSSLOWKEYPublicKey *key, + const unsigned char *sig, unsigned int sigLen, + const unsigned char *digestData, unsigned int digestLen) +{ + unsigned char *pkcs1DigestInfoData; + SECItem pkcs1DigestInfo; + SECItem digest; + unsigned int bufferSize; + SECStatus rv; + + /* pkcs1DigestInfo.data must be less than key->u.rsa.modulus.len */ + bufferSize = key->u.rsa.modulus.len; + pkcs1DigestInfoData = PORT_ZAlloc(bufferSize); + if (!pkcs1DigestInfoData) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + pkcs1DigestInfo.data = pkcs1DigestInfoData; + pkcs1DigestInfo.len = bufferSize; + + /* decrypt the block */ + rv = RSA_CheckSignRecover(&key->u.rsa, pkcs1DigestInfo.data, + &pkcs1DigestInfo.len, pkcs1DigestInfo.len, + sig, sigLen); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + } else { + digest.data = (PRUint8 *)digestData; + digest.len = digestLen; + rv = _SGN_VerifyPKCS1DigestInfo( + digestOid, &digest, &pkcs1DigestInfo, + PR_FALSE /*XXX: unsafeAllowMissingParameters*/); + } + + PORT_ZFree(pkcs1DigestInfoData, bufferSize); + return rv; +} + +static SECStatus +sftk_RSACheckSign(NSSLOWKEYPublicKey *key, const unsigned char *sig, + unsigned int sigLen, const unsigned char *digest, + unsigned int digestLen) +{ + PORT_Assert(key->keyType == NSSLOWKEYRSAKey); + if (key->keyType != NSSLOWKEYRSAKey) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + return SECFailure; + } + + return RSA_CheckSign(&key->u.rsa, sig, sigLen, digest, digestLen); +} + +static SECStatus +sftk_RSACheckSignRaw(NSSLOWKEYPublicKey *key, const unsigned char *sig, + unsigned int sigLen, const unsigned char *digest, + unsigned int digestLen) +{ + PORT_Assert(key->keyType == NSSLOWKEYRSAKey); + if (key->keyType != NSSLOWKEYRSAKey) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + return SECFailure; + } + + return RSA_CheckSignRaw(&key->u.rsa, sig, sigLen, digest, digestLen); +} + +static SECStatus +sftk_RSACheckSignPSS(SFTKPSSVerifyInfo *info, const unsigned char *sig, + unsigned int sigLen, const unsigned char *digest, + unsigned int digestLen) +{ + HASH_HashType hashAlg; + HASH_HashType maskHashAlg; + CK_RSA_PKCS_PSS_PARAMS *params = &info->params; + + PORT_Assert(info->key->keyType == NSSLOWKEYRSAKey); + if (info->key->keyType != NSSLOWKEYRSAKey) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + return SECFailure; + } + + hashAlg = sftk_GetHashTypeFromMechanism(params->hashAlg); + maskHashAlg = sftk_GetHashTypeFromMechanism(params->mgf); + + return RSA_CheckSignPSS(&info->key->u.rsa, hashAlg, maskHashAlg, + params->sLen, sig, sigLen, digest, digestLen); +} + +/* NSC_VerifyInit initializes a verification operation, + * where the signature is an appendix to the data, + * and plaintext cannot be recovered from the signature (e.g. DSA) */ +CK_RV +NSC_VerifyInit(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) +{ + SFTKSession *session; + SFTKObject *key; + SFTKSessionContext *context; + CK_KEY_TYPE key_type; + CK_RV crv = CKR_OK; + NSSLOWKEYPublicKey *pubKey; + SFTKHashVerifyInfo *info = NULL; + SFTKPSSVerifyInfo *pinfo = NULL; + + CHECK_FORK(); + + /* Block Cipher MACing Algorithms use a different Context init method..*/ + crv = sftk_InitCBCMac(hSession, pMechanism, hKey, CKA_VERIFY, SFTK_VERIFY); + if (crv != CKR_FUNCTION_NOT_SUPPORTED) + return crv; + + session = sftk_SessionFromHandle(hSession); + if (session == NULL) + return CKR_SESSION_HANDLE_INVALID; + crv = sftk_InitGeneric(session, pMechanism, &context, SFTK_VERIFY, &key, + hKey, &key_type, CKO_PUBLIC_KEY, CKA_VERIFY); + if (crv != CKR_OK) { + sftk_FreeSession(session); + return crv; + } + + context->multi = PR_FALSE; + +#define INIT_RSA_VFY_MECH(mmm) \ + case CKM_##mmm##_RSA_PKCS: \ + context->multi = PR_TRUE; \ + crv = sftk_doSub##mmm(context); \ + if (crv != CKR_OK) \ + break; \ + context->verify = (SFTKVerify)sftk_hashCheckSign; \ + info = PORT_New(SFTKHashVerifyInfo); \ + if (info == NULL) { \ + crv = CKR_HOST_MEMORY; \ + break; \ + } \ + info->hashOid = SEC_OID_##mmm; \ + goto finish_rsa; + + switch (pMechanism->mechanism) { + INIT_RSA_VFY_MECH(MD5) + INIT_RSA_VFY_MECH(MD2) + INIT_RSA_VFY_MECH(SHA1) + INIT_RSA_VFY_MECH(SHA224) + INIT_RSA_VFY_MECH(SHA256) + INIT_RSA_VFY_MECH(SHA384) + INIT_RSA_VFY_MECH(SHA512) + + case CKM_RSA_PKCS: + context->verify = (SFTKVerify)sftk_RSACheckSign; + goto finish_rsa; + case CKM_RSA_X_509: + context->verify = (SFTKVerify)sftk_RSACheckSignRaw; + finish_rsa: + if (key_type != CKK_RSA) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + context->rsa = PR_TRUE; + pubKey = sftk_GetPubKey(key, CKK_RSA, &crv); + if (pubKey == NULL) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + if (info) { + info->key = pubKey; + context->cipherInfo = info; + context->destroy = sftk_Space; + } else { + context->cipherInfo = pubKey; + context->destroy = sftk_Null; + } + break; + + INIT_RSA_PSS_SIG_MECH(SHA1) + INIT_RSA_PSS_SIG_MECH(SHA224) + INIT_RSA_PSS_SIG_MECH(SHA256) + INIT_RSA_PSS_SIG_MECH(SHA384) + INIT_RSA_PSS_SIG_MECH(SHA512) + case CKM_RSA_PKCS_PSS: + finish_rsa_pss: + if (key_type != CKK_RSA) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + context->rsa = PR_TRUE; + if (pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS) || + !sftk_ValidatePssParams((const CK_RSA_PKCS_PSS_PARAMS *)pMechanism->pParameter)) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + pinfo = PORT_New(SFTKPSSVerifyInfo); + if (pinfo == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + pinfo->size = sizeof(SFTKPSSVerifyInfo); + pinfo->params = *(CK_RSA_PKCS_PSS_PARAMS *)pMechanism->pParameter; + pinfo->key = sftk_GetPubKey(key, CKK_RSA, &crv); + if (pinfo->key == NULL) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + context->cipherInfo = pinfo; + context->destroy = (SFTKDestroy)sftk_ZSpace; + context->verify = (SFTKVerify)sftk_RSACheckSignPSS; + break; + + INIT_DSA_SIG_MECH(SHA1) + INIT_DSA_SIG_MECH(SHA224) + INIT_DSA_SIG_MECH(SHA256) + INIT_DSA_SIG_MECH(SHA384) + INIT_DSA_SIG_MECH(SHA512) + case CKM_DSA: + finish_dsa: + if (key_type != CKK_DSA) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + pubKey = sftk_GetPubKey(key, CKK_DSA, &crv); + if (pubKey == NULL) { + break; + } + context->cipherInfo = pubKey; + context->verify = (SFTKVerify)nsc_DSA_Verify_Stub; + context->destroy = sftk_Null; + break; + + INIT_ECDSA_SIG_MECH(SHA1) + INIT_ECDSA_SIG_MECH(SHA224) + INIT_ECDSA_SIG_MECH(SHA256) + INIT_ECDSA_SIG_MECH(SHA384) + INIT_ECDSA_SIG_MECH(SHA512) + case CKM_ECDSA: + finish_ecdsa: + if (key_type != CKK_EC) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + pubKey = sftk_GetPubKey(key, CKK_EC, &crv); + if (pubKey == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + context->cipherInfo = pubKey; + context->verify = (SFTKVerify)nsc_ECDSAVerifyStub; + context->destroy = sftk_Null; + break; + + INIT_HMAC_MECH(MD2) + INIT_HMAC_MECH(MD5) + INIT_HMAC_MECH(SHA1) + INIT_HMAC_MECH(SHA224) + INIT_HMAC_MECH(SHA256) + INIT_HMAC_MECH(SHA384) + INIT_HMAC_MECH(SHA512) + INIT_HMAC_MECH(SHA3_224) + INIT_HMAC_MECH(SHA3_256) + INIT_HMAC_MECH(SHA3_384) + INIT_HMAC_MECH(SHA3_512) + + case CKM_SSL3_MD5_MAC: + PORT_Assert(pMechanism->pParameter); + if (!pMechanism->pParameter) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + crv = sftk_doSSLMACInit(context, SEC_OID_MD5, key, + *(CK_ULONG *)pMechanism->pParameter); + break; + case CKM_SSL3_SHA1_MAC: + PORT_Assert(pMechanism->pParameter); + if (!pMechanism->pParameter) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + crv = sftk_doSSLMACInit(context, SEC_OID_SHA1, key, + *(CK_ULONG *)pMechanism->pParameter); + break; + case CKM_TLS_PRF_GENERAL: + crv = sftk_TLSPRFInit(context, key, key_type, HASH_AlgNULL, 0); + break; + case CKM_NSS_TLS_PRF_GENERAL_SHA256: + crv = sftk_TLSPRFInit(context, key, key_type, HASH_AlgSHA256, 0); + break; + + default: + crv = CKR_MECHANISM_INVALID; + break; + } + + if (crv != CKR_OK) { + if (info) + PORT_Free(info); + if (pinfo) + PORT_ZFree(pinfo, pinfo->size); + sftk_FreeContext(context); + sftk_FreeSession(session); + return crv; + } + sftk_SetContextByType(session, SFTK_VERIFY, context); + sftk_FreeSession(session); + return CKR_OK; +} + +/* NSC_Verify verifies a signature in a single-part operation, + * where the signature is an appendix to the data, + * and plaintext cannot be recovered from the signature */ +CK_RV +NSC_Verify(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, + CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) +{ + SFTKSession *session; + SFTKSessionContext *context; + CK_RV crv; + + CHECK_FORK(); + + /* make sure we're legal */ + crv = sftk_GetContext(hSession, &context, SFTK_VERIFY, PR_FALSE, &session); + if (crv != CKR_OK) + return crv; + + /* multi part Verifying are completely implemented by VerifyUpdate and + * VerifyFinal */ + if (context->multi) { + /* VerifyFinal can't follow failed VerifyUpdate */ + if (CKR_OK == (crv = NSC_VerifyUpdate(hSession, pData, ulDataLen))) + crv = NSC_VerifyFinal(hSession, pSignature, ulSignatureLen); + } else { + if (SECSuccess != (*context->verify)(context->cipherInfo, pSignature, + ulSignatureLen, pData, ulDataLen)) + crv = sftk_MapCryptError(PORT_GetError()); + + sftk_TerminateOp(session, SFTK_VERIFY, context); + } + sftk_FreeSession(session); + return crv; +} + +/* NSC_VerifyUpdate continues a multiple-part verification operation, + * where the signature is an appendix to the data, + * and plaintext cannot be recovered from the signature + * + * A call which results in an error terminates the operation [PKCS#11,v2.11] + */ +CK_RV +NSC_VerifyUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, + CK_ULONG ulPartLen) +{ + CHECK_FORK(); + return sftk_MACUpdate(hSession, pPart, ulPartLen, SFTK_VERIFY); +} + +/* NSC_VerifyFinal finishes a multiple-part verification operation, + * checking the signature. */ +CK_RV +NSC_VerifyFinal(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) +{ + SFTKSession *session; + SFTKSessionContext *context; + CK_RV crv; + + CHECK_FORK(); + + if (!pSignature) + return CKR_ARGUMENTS_BAD; + + /* make sure we're legal */ + crv = sftk_GetContext(hSession, &context, SFTK_VERIFY, PR_TRUE, &session); + if (crv != CKR_OK) + return crv; + + if (context->hashInfo) { + unsigned int digestLen; + unsigned char tmpbuf[SFTK_MAX_MAC_LENGTH]; + + (*context->end)(context->hashInfo, tmpbuf, &digestLen, sizeof(tmpbuf)); + if (SECSuccess != (context->verify)(context->cipherInfo, pSignature, + ulSignatureLen, tmpbuf, digestLen)) + crv = sftk_MapCryptError(PORT_GetError()); + PORT_Memset(tmpbuf, 0, sizeof tmpbuf); + } else if (ulSignatureLen != context->macSize) { + /* must be block cipher MACing */ + crv = CKR_SIGNATURE_LEN_RANGE; + } else if (CKR_OK == (crv = sftk_MACFinal(context))) { + if (NSS_SecureMemcmp(pSignature, context->macBuf, ulSignatureLen)) + crv = CKR_SIGNATURE_INVALID; + } + + sftk_TerminateOp(session, SFTK_VERIFY, context); + sftk_FreeSession(session); + return crv; +} + +/* + ************** Crypto Functions: Verify Recover ************************ + */ +static SECStatus +sftk_RSACheckSignRecover(NSSLOWKEYPublicKey *key, unsigned char *data, + unsigned int *dataLen, unsigned int maxDataLen, + const unsigned char *sig, unsigned int sigLen) +{ + PORT_Assert(key->keyType == NSSLOWKEYRSAKey); + if (key->keyType != NSSLOWKEYRSAKey) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + return SECFailure; + } + + return RSA_CheckSignRecover(&key->u.rsa, data, dataLen, maxDataLen, + sig, sigLen); +} + +static SECStatus +sftk_RSACheckSignRecoverRaw(NSSLOWKEYPublicKey *key, unsigned char *data, + unsigned int *dataLen, unsigned int maxDataLen, + const unsigned char *sig, unsigned int sigLen) +{ + PORT_Assert(key->keyType == NSSLOWKEYRSAKey); + if (key->keyType != NSSLOWKEYRSAKey) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + return SECFailure; + } + + return RSA_CheckSignRecoverRaw(&key->u.rsa, data, dataLen, maxDataLen, + sig, sigLen); +} + +/* NSC_VerifyRecoverInit initializes a signature verification operation, + * where the data is recovered from the signature. + * E.g. Decryption with the user's public key */ +CK_RV +NSC_VerifyRecoverInit(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) +{ + SFTKSession *session; + SFTKObject *key; + SFTKSessionContext *context; + CK_KEY_TYPE key_type; + CK_RV crv = CKR_OK; + NSSLOWKEYPublicKey *pubKey; + + CHECK_FORK(); + + session = sftk_SessionFromHandle(hSession); + if (session == NULL) + return CKR_SESSION_HANDLE_INVALID; + crv = sftk_InitGeneric(session, pMechanism, &context, SFTK_VERIFY_RECOVER, + &key, hKey, &key_type, CKO_PUBLIC_KEY, CKA_VERIFY_RECOVER); + if (crv != CKR_OK) { + sftk_FreeSession(session); + return crv; + } + + context->multi = PR_TRUE; + + switch (pMechanism->mechanism) { + case CKM_RSA_PKCS: + case CKM_RSA_X_509: + if (key_type != CKK_RSA) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + context->multi = PR_FALSE; + context->rsa = PR_TRUE; + pubKey = sftk_GetPubKey(key, CKK_RSA, &crv); + if (pubKey == NULL) { + break; + } + context->cipherInfo = pubKey; + context->update = (SFTKCipher)(pMechanism->mechanism == CKM_RSA_X_509 + ? sftk_RSACheckSignRecoverRaw + : sftk_RSACheckSignRecover); + context->destroy = sftk_Null; + break; + default: + crv = CKR_MECHANISM_INVALID; + break; + } + + if (crv != CKR_OK) { + PORT_Free(context); + sftk_FreeSession(session); + return crv; + } + sftk_SetContextByType(session, SFTK_VERIFY_RECOVER, context); + sftk_FreeSession(session); + return CKR_OK; +} + +/* NSC_VerifyRecover verifies a signature in a single-part operation, + * where the data is recovered from the signature. + * E.g. Decryption with the user's public key */ +CK_RV +NSC_VerifyRecover(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen, + CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) +{ + SFTKSession *session; + SFTKSessionContext *context; + unsigned int outlen; + unsigned int maxoutlen = *pulDataLen; + CK_RV crv; + SECStatus rv; + + CHECK_FORK(); + + /* make sure we're legal */ + crv = sftk_GetContext(hSession, &context, SFTK_VERIFY_RECOVER, + PR_FALSE, &session); + if (crv != CKR_OK) + return crv; + if (pData == NULL) { + /* to return the actual size, we need to do the decrypt, just return + * the max size, which is the size of the input signature. */ + *pulDataLen = ulSignatureLen; + rv = SECSuccess; + goto finish; + } + + rv = (*context->update)(context->cipherInfo, pData, &outlen, maxoutlen, + pSignature, ulSignatureLen); + *pulDataLen = (CK_ULONG)outlen; + + sftk_TerminateOp(session, SFTK_VERIFY_RECOVER, context); +finish: + sftk_FreeSession(session); + return (rv == SECSuccess) ? CKR_OK : sftk_MapVerifyError(PORT_GetError()); +} + +/* + **************************** Random Functions: ************************ + */ + +/* NSC_SeedRandom mixes additional seed material into the token's random number + * generator. */ +CK_RV +NSC_SeedRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSeed, + CK_ULONG ulSeedLen) +{ + SECStatus rv; + + CHECK_FORK(); + + rv = RNG_RandomUpdate(pSeed, ulSeedLen); + return (rv == SECSuccess) ? CKR_OK : sftk_MapCryptError(PORT_GetError()); +} + +/* NSC_GenerateRandom generates random data. */ +CK_RV +NSC_GenerateRandom(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pRandomData, CK_ULONG ulRandomLen) +{ + SECStatus rv; + + CHECK_FORK(); + + rv = RNG_GenerateGlobalRandomBytes(pRandomData, ulRandomLen); + /* + * This may fail with SEC_ERROR_NEED_RANDOM, which means the RNG isn't + * seeded with enough entropy. + */ + return (rv == SECSuccess) ? CKR_OK : sftk_MapCryptError(PORT_GetError()); +} + +/* + **************************** Key Functions: ************************ + */ + +/* + * generate a password based encryption key. This code uses + * PKCS5 to do the work. + */ +static CK_RV +nsc_pbe_key_gen(NSSPKCS5PBEParameter *pkcs5_pbe, CK_MECHANISM_PTR pMechanism, + void *buf, CK_ULONG *key_length, PRBool faulty3DES) +{ + SECItem *pbe_key = NULL, iv, pwitem; + CK_PBE_PARAMS *pbe_params = NULL; + CK_PKCS5_PBKD2_PARAMS *pbkd2_params = NULL; + + *key_length = 0; + iv.data = NULL; + iv.len = 0; + + if (pMechanism->mechanism == CKM_PKCS5_PBKD2) { + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_PKCS5_PBKD2_PARAMS))) { + return CKR_MECHANISM_PARAM_INVALID; + } + pbkd2_params = (CK_PKCS5_PBKD2_PARAMS *)pMechanism->pParameter; + pwitem.data = (unsigned char *)pbkd2_params->pPassword; + /* was this a typo in the PKCS #11 spec? */ + pwitem.len = *pbkd2_params->ulPasswordLen; + } else { + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_PBE_PARAMS))) { + return CKR_MECHANISM_PARAM_INVALID; + } + pbe_params = (CK_PBE_PARAMS *)pMechanism->pParameter; + pwitem.data = (unsigned char *)pbe_params->pPassword; + pwitem.len = pbe_params->ulPasswordLen; + } + pbe_key = nsspkcs5_ComputeKeyAndIV(pkcs5_pbe, &pwitem, &iv, faulty3DES); + if (pbe_key == NULL) { + return CKR_HOST_MEMORY; + } + + PORT_Memcpy(buf, pbe_key->data, pbe_key->len); + *key_length = pbe_key->len; + SECITEM_ZfreeItem(pbe_key, PR_TRUE); + pbe_key = NULL; + + if (iv.data) { + if (pbe_params && pbe_params->pInitVector != NULL) { + PORT_Memcpy(pbe_params->pInitVector, iv.data, iv.len); + } + PORT_Free(iv.data); + } + + return CKR_OK; +} + +/* + * this is coded for "full" support. These selections will be limitted to + * the official subset by freebl. + */ +static unsigned int +sftk_GetSubPrimeFromPrime(unsigned int primeBits) +{ + if (primeBits <= 1024) { + return 160; + } else if (primeBits <= 2048) { + return 224; + } else if (primeBits <= 3072) { + return 256; + } else if (primeBits <= 7680) { + return 384; + } else { + return 512; + } +} + +static CK_RV +nsc_parameter_gen(CK_KEY_TYPE key_type, SFTKObject *key) +{ + SFTKAttribute *attribute; + CK_ULONG counter; + unsigned int seedBits = 0; + unsigned int subprimeBits = 0; + unsigned int primeBits; + unsigned int j = 8; /* default to 1024 bits */ + CK_RV crv = CKR_OK; + PQGParams *params = NULL; + PQGVerify *vfy = NULL; + SECStatus rv; + + attribute = sftk_FindAttribute(key, CKA_PRIME_BITS); + if (attribute == NULL) { + attribute = sftk_FindAttribute(key, CKA_PRIME); + if (attribute == NULL) { + return CKR_TEMPLATE_INCOMPLETE; + } else { + primeBits = attribute->attrib.ulValueLen; + sftk_FreeAttribute(attribute); + } + } else { + primeBits = (unsigned int)*(CK_ULONG *)attribute->attrib.pValue; + sftk_FreeAttribute(attribute); + } + if (primeBits < 1024) { + j = PQG_PBITS_TO_INDEX(primeBits); + if (j == (unsigned int)-1) { + return CKR_ATTRIBUTE_VALUE_INVALID; + } + } + + attribute = sftk_FindAttribute(key, CKA_NSS_PQG_SEED_BITS); + if (attribute != NULL) { + seedBits = (unsigned int)*(CK_ULONG *)attribute->attrib.pValue; + sftk_FreeAttribute(attribute); + } + + attribute = sftk_FindAttribute(key, CKA_SUBPRIME_BITS); + if (attribute != NULL) { + subprimeBits = (unsigned int)*(CK_ULONG *)attribute->attrib.pValue; + sftk_FreeAttribute(attribute); + } + + /* if P and Q are supplied, we want to generate a new G */ + attribute = sftk_FindAttribute(key, CKA_PRIME); + if (attribute != NULL) { + PLArenaPool *arena; + + sftk_FreeAttribute(attribute); + arena = PORT_NewArena(1024); + if (arena == NULL) { + crv = CKR_HOST_MEMORY; + goto loser; + } + params = PORT_ArenaAlloc(arena, sizeof(*params)); + if (params == NULL) { + crv = CKR_HOST_MEMORY; + goto loser; + } + params->arena = arena; + crv = sftk_Attribute2SSecItem(arena, ¶ms->prime, key, CKA_PRIME); + if (crv != CKR_OK) { + goto loser; + } + crv = sftk_Attribute2SSecItem(arena, ¶ms->subPrime, + key, CKA_SUBPRIME); + if (crv != CKR_OK) { + goto loser; + } + + arena = PORT_NewArena(1024); + if (arena == NULL) { + crv = CKR_HOST_MEMORY; + goto loser; + } + vfy = PORT_ArenaAlloc(arena, sizeof(*vfy)); + if (vfy == NULL) { + crv = CKR_HOST_MEMORY; + goto loser; + } + vfy->arena = arena; + crv = sftk_Attribute2SSecItem(arena, &vfy->seed, key, CKA_NSS_PQG_SEED); + if (crv != CKR_OK) { + goto loser; + } + crv = sftk_Attribute2SSecItem(arena, &vfy->h, key, CKA_NSS_PQG_H); + if (crv != CKR_OK) { + goto loser; + } + sftk_DeleteAttributeType(key, CKA_PRIME); + sftk_DeleteAttributeType(key, CKA_SUBPRIME); + sftk_DeleteAttributeType(key, CKA_NSS_PQG_SEED); + sftk_DeleteAttributeType(key, CKA_NSS_PQG_H); + } + + sftk_DeleteAttributeType(key, CKA_PRIME_BITS); + sftk_DeleteAttributeType(key, CKA_SUBPRIME_BITS); + sftk_DeleteAttributeType(key, CKA_NSS_PQG_SEED_BITS); + + /* use the old PQG interface if we have old input data */ + if ((primeBits < 1024) || ((primeBits == 1024) && (subprimeBits == 0))) { + if (seedBits == 0) { + rv = PQG_ParamGen(j, ¶ms, &vfy); + } else { + rv = PQG_ParamGenSeedLen(j, seedBits / 8, ¶ms, &vfy); + } + } else { + if (subprimeBits == 0) { + subprimeBits = sftk_GetSubPrimeFromPrime(primeBits); + } + if (seedBits == 0) { + seedBits = primeBits; + } + rv = PQG_ParamGenV2(primeBits, subprimeBits, seedBits / 8, ¶ms, &vfy); + } + + if (rv != SECSuccess) { + if (PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { + sftk_fatalError = PR_TRUE; + } + return sftk_MapCryptError(PORT_GetError()); + } + crv = sftk_AddAttributeType(key, CKA_PRIME, + params->prime.data, params->prime.len); + if (crv != CKR_OK) + goto loser; + crv = sftk_AddAttributeType(key, CKA_SUBPRIME, + params->subPrime.data, params->subPrime.len); + if (crv != CKR_OK) + goto loser; + crv = sftk_AddAttributeType(key, CKA_BASE, + params->base.data, params->base.len); + if (crv != CKR_OK) + goto loser; + counter = vfy->counter; + crv = sftk_AddAttributeType(key, CKA_NSS_PQG_COUNTER, + &counter, sizeof(counter)); + crv = sftk_AddAttributeType(key, CKA_NSS_PQG_SEED, + vfy->seed.data, vfy->seed.len); + if (crv != CKR_OK) + goto loser; + crv = sftk_AddAttributeType(key, CKA_NSS_PQG_H, + vfy->h.data, vfy->h.len); + if (crv != CKR_OK) + goto loser; + +loser: + if (params) { + PQG_DestroyParams(params); + } + + if (vfy) { + PQG_DestroyVerify(vfy); + } + return crv; +} + +static CK_RV +nsc_SetupBulkKeyGen(CK_MECHANISM_TYPE mechanism, CK_KEY_TYPE *key_type, + CK_ULONG *key_length) +{ + CK_RV crv = CKR_OK; + + switch (mechanism) { +#ifndef NSS_DISABLE_DEPRECATED_RC2 + case CKM_RC2_KEY_GEN: + *key_type = CKK_RC2; + if (*key_length == 0) + crv = CKR_TEMPLATE_INCOMPLETE; + break; +#endif /* NSS_DISABLE_DEPRECATED_RC2 */ +#if NSS_SOFTOKEN_DOES_RC5 + case CKM_RC5_KEY_GEN: + *key_type = CKK_RC5; + if (*key_length == 0) + crv = CKR_TEMPLATE_INCOMPLETE; + break; +#endif + case CKM_RC4_KEY_GEN: + *key_type = CKK_RC4; + if (*key_length == 0) + crv = CKR_TEMPLATE_INCOMPLETE; + break; + case CKM_GENERIC_SECRET_KEY_GEN: + *key_type = CKK_GENERIC_SECRET; + if (*key_length == 0) + crv = CKR_TEMPLATE_INCOMPLETE; + break; + case CKM_CDMF_KEY_GEN: + *key_type = CKK_CDMF; + *key_length = 8; + break; + case CKM_DES_KEY_GEN: + *key_type = CKK_DES; + *key_length = 8; + break; + case CKM_DES2_KEY_GEN: + *key_type = CKK_DES2; + *key_length = 16; + break; + case CKM_DES3_KEY_GEN: + *key_type = CKK_DES3; + *key_length = 24; + break; +#ifndef NSS_DISABLE_DEPRECATED_SEED + case CKM_SEED_KEY_GEN: + *key_type = CKK_SEED; + *key_length = 16; + break; +#endif /* NSS_DISABLE_DEPRECATED_SEED */ + case CKM_CAMELLIA_KEY_GEN: + *key_type = CKK_CAMELLIA; + if (*key_length == 0) + crv = CKR_TEMPLATE_INCOMPLETE; + break; + case CKM_AES_KEY_GEN: + *key_type = CKK_AES; + if (*key_length == 0) + crv = CKR_TEMPLATE_INCOMPLETE; + break; + case CKM_NSS_CHACHA20_KEY_GEN: + *key_type = CKK_NSS_CHACHA20; + *key_length = 32; + break; + case CKM_CHACHA20_KEY_GEN: + *key_type = CKK_CHACHA20; + *key_length = 32; + break; + case CKM_HKDF_KEY_GEN: + *key_type = CKK_HKDF; + if (*key_length == 0) + crv = CKR_TEMPLATE_INCOMPLETE; + break; + default: + PORT_Assert(0); + crv = CKR_MECHANISM_INVALID; + break; + } + + return crv; +} + +CK_RV +nsc_SetupHMACKeyGen(CK_MECHANISM_PTR pMechanism, NSSPKCS5PBEParameter **pbe) +{ + SECItem salt; + CK_PBE_PARAMS *pbe_params = NULL; + NSSPKCS5PBEParameter *params; + PLArenaPool *arena = NULL; + SECStatus rv; + + *pbe = NULL; + + arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); + if (arena == NULL) { + return CKR_HOST_MEMORY; + } + + params = (NSSPKCS5PBEParameter *)PORT_ArenaZAlloc(arena, + sizeof(NSSPKCS5PBEParameter)); + if (params == NULL) { + PORT_FreeArena(arena, PR_TRUE); + return CKR_HOST_MEMORY; + } + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_PBE_PARAMS))) { + PORT_FreeArena(arena, PR_TRUE); + return CKR_MECHANISM_PARAM_INVALID; + } + + params->poolp = arena; + params->ivLen = 0; + params->pbeType = NSSPKCS5_PKCS12_V2; + params->hashType = HASH_AlgSHA1; + params->encAlg = SEC_OID_SHA1; /* any invalid value */ + params->is2KeyDES = PR_FALSE; + params->keyID = pbeBitGenIntegrityKey; + pbe_params = (CK_PBE_PARAMS *)pMechanism->pParameter; + params->iter = pbe_params->ulIteration; + + salt.data = (unsigned char *)pbe_params->pSalt; + salt.len = (unsigned int)pbe_params->ulSaltLen; + salt.type = siBuffer; + rv = SECITEM_CopyItem(arena, ¶ms->salt, &salt); + if (rv != SECSuccess) { + PORT_FreeArena(arena, PR_TRUE); + return CKR_HOST_MEMORY; + } + switch (pMechanism->mechanism) { + case CKM_NSS_PBE_SHA1_HMAC_KEY_GEN: + case CKM_PBA_SHA1_WITH_SHA1_HMAC: + params->hashType = HASH_AlgSHA1; + params->keyLen = 20; + break; + case CKM_NSS_PBE_MD5_HMAC_KEY_GEN: + params->hashType = HASH_AlgMD5; + params->keyLen = 16; + break; + case CKM_NSS_PBE_MD2_HMAC_KEY_GEN: + params->hashType = HASH_AlgMD2; + params->keyLen = 16; + break; + case CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN: + params->hashType = HASH_AlgSHA224; + params->keyLen = 28; + break; + case CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN: + params->hashType = HASH_AlgSHA256; + params->keyLen = 32; + break; + case CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN: + params->hashType = HASH_AlgSHA384; + params->keyLen = 48; + break; + case CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN: + params->hashType = HASH_AlgSHA512; + params->keyLen = 64; + break; + default: + PORT_FreeArena(arena, PR_TRUE); + return CKR_MECHANISM_INVALID; + } + *pbe = params; + return CKR_OK; +} + +/* maybe this should be table driven? */ +static CK_RV +nsc_SetupPBEKeyGen(CK_MECHANISM_PTR pMechanism, NSSPKCS5PBEParameter **pbe, + CK_KEY_TYPE *key_type, CK_ULONG *key_length) +{ + CK_RV crv = CKR_OK; + SECOidData *oid; + CK_PBE_PARAMS *pbe_params = NULL; + NSSPKCS5PBEParameter *params = NULL; + HASH_HashType hashType = HASH_AlgSHA1; + CK_PKCS5_PBKD2_PARAMS *pbkd2_params = NULL; + SECItem salt; + CK_ULONG iteration = 0; + + *pbe = NULL; + + oid = SECOID_FindOIDByMechanism(pMechanism->mechanism); + if (oid == NULL) { + return CKR_MECHANISM_INVALID; + } + + if (pMechanism->mechanism == CKM_PKCS5_PBKD2) { + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_PKCS5_PBKD2_PARAMS))) { + return CKR_MECHANISM_PARAM_INVALID; + } + pbkd2_params = (CK_PKCS5_PBKD2_PARAMS *)pMechanism->pParameter; + switch (pbkd2_params->prf) { + case CKP_PKCS5_PBKD2_HMAC_SHA1: + hashType = HASH_AlgSHA1; + break; + case CKP_PKCS5_PBKD2_HMAC_SHA224: + hashType = HASH_AlgSHA224; + break; + case CKP_PKCS5_PBKD2_HMAC_SHA256: + hashType = HASH_AlgSHA256; + break; + case CKP_PKCS5_PBKD2_HMAC_SHA384: + hashType = HASH_AlgSHA384; + break; + case CKP_PKCS5_PBKD2_HMAC_SHA512: + hashType = HASH_AlgSHA512; + break; + default: + return CKR_MECHANISM_PARAM_INVALID; + } + if (pbkd2_params->saltSource != CKZ_SALT_SPECIFIED) { + return CKR_MECHANISM_PARAM_INVALID; + } + salt.data = (unsigned char *)pbkd2_params->pSaltSourceData; + salt.len = (unsigned int)pbkd2_params->ulSaltSourceDataLen; + iteration = pbkd2_params->iterations; + } else { + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_PBE_PARAMS))) { + return CKR_MECHANISM_PARAM_INVALID; + } + pbe_params = (CK_PBE_PARAMS *)pMechanism->pParameter; + salt.data = (unsigned char *)pbe_params->pSalt; + salt.len = (unsigned int)pbe_params->ulSaltLen; + iteration = pbe_params->ulIteration; + } + params = nsspkcs5_NewParam(oid->offset, hashType, &salt, iteration); + if (params == NULL) { + return CKR_MECHANISM_INVALID; + } + + switch (params->encAlg) { + case SEC_OID_DES_CBC: + *key_type = CKK_DES; + *key_length = params->keyLen; + break; + case SEC_OID_DES_EDE3_CBC: + *key_type = params->is2KeyDES ? CKK_DES2 : CKK_DES3; + *key_length = params->keyLen; + break; +#ifndef NSS_DISABLE_DEPRECATED_RC2 + case SEC_OID_RC2_CBC: + *key_type = CKK_RC2; + *key_length = params->keyLen; + break; +#endif /* NSS_DISABLE_DEPRECATED_RC2 */ + case SEC_OID_RC4: + *key_type = CKK_RC4; + *key_length = params->keyLen; + break; + case SEC_OID_PKCS5_PBKDF2: + /* key type must already be set */ + if (*key_type == CKK_INVALID_KEY_TYPE) { + crv = CKR_TEMPLATE_INCOMPLETE; + break; + } + /* PBKDF2 needs to calculate the key length from the other parameters + */ + if (*key_length == 0) { + *key_length = sftk_MapKeySize(*key_type); + } + if (*key_length == 0) { + crv = CKR_TEMPLATE_INCOMPLETE; + break; + } + params->keyLen = *key_length; + break; + default: + crv = CKR_MECHANISM_INVALID; + break; + } + if (crv == CKR_OK) { + *pbe = params; + } else { + nsspkcs5_DestroyPBEParameter(params); + } + return crv; +} + +/* NSC_GenerateKey generates a secret key, creating a new key object. */ +CK_RV +NSC_GenerateKey(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, + CK_OBJECT_HANDLE_PTR phKey) +{ + SFTKObject *key; + SFTKSession *session; + PRBool checkWeak = PR_FALSE; + CK_ULONG key_length = 0; + CK_KEY_TYPE key_type = CKK_INVALID_KEY_TYPE; + CK_OBJECT_CLASS objclass = CKO_SECRET_KEY; + CK_RV crv = CKR_OK; + CK_BBOOL cktrue = CK_TRUE; + NSSPKCS5PBEParameter *pbe_param = NULL; + int i; + SFTKSlot *slot = sftk_SlotFromSessionHandle(hSession); + unsigned char buf[MAX_KEY_LEN]; + enum { nsc_pbe, + nsc_ssl, + nsc_bulk, + nsc_param, + nsc_jpake } key_gen_type; + SSL3RSAPreMasterSecret *rsa_pms; + CK_VERSION *version; + /* in very old versions of NSS, there were implementation errors with key + * generation methods. We want to beable to read these, but not + * produce them any more. The affected algorithm was 3DES. + */ + PRBool faultyPBE3DES = PR_FALSE; + HASH_HashType hashType = HASH_AlgNULL; + + CHECK_FORK(); + + if (!slot) { + return CKR_SESSION_HANDLE_INVALID; + } + /* + * now lets create an object to hang the attributes off of + */ + key = sftk_NewObject(slot); /* fill in the handle later */ + if (key == NULL) { + return CKR_HOST_MEMORY; + } + + /* + * load the template values into the object + */ + for (i = 0; i < (int)ulCount; i++) { + if (pTemplate[i].type == CKA_VALUE_LEN) { + key_length = *(CK_ULONG *)pTemplate[i].pValue; + continue; + } + /* some algorithms need keytype specified */ + if (pTemplate[i].type == CKA_KEY_TYPE) { + key_type = *(CK_ULONG *)pTemplate[i].pValue; + continue; + } + + crv = sftk_AddAttributeType(key, sftk_attr_expand(&pTemplate[i])); + if (crv != CKR_OK) { + break; + } + } + if (crv != CKR_OK) { + goto loser; + } + + /* make sure we don't have any class, key_type, or value fields */ + sftk_DeleteAttributeType(key, CKA_CLASS); + sftk_DeleteAttributeType(key, CKA_KEY_TYPE); + sftk_DeleteAttributeType(key, CKA_VALUE); + + /* Now Set up the parameters to generate the key (based on mechanism) */ + key_gen_type = nsc_bulk; /* bulk key by default */ + switch (pMechanism->mechanism) { + case CKM_CDMF_KEY_GEN: + case CKM_DES_KEY_GEN: + case CKM_DES2_KEY_GEN: + case CKM_DES3_KEY_GEN: + checkWeak = PR_TRUE; +/* fall through */ +#ifndef NSS_DISABLE_DEPRECATED_RC2 + case CKM_RC2_KEY_GEN: +#endif + case CKM_RC4_KEY_GEN: + case CKM_GENERIC_SECRET_KEY_GEN: +#ifndef NSS_DISABLE_DEPRECATED_SEED + case CKM_SEED_KEY_GEN: +#endif + case CKM_CAMELLIA_KEY_GEN: + case CKM_AES_KEY_GEN: + case CKM_NSS_CHACHA20_KEY_GEN: + case CKM_CHACHA20_KEY_GEN: +#if NSS_SOFTOKEN_DOES_RC5 + case CKM_RC5_KEY_GEN: +#endif + crv = nsc_SetupBulkKeyGen(pMechanism->mechanism, &key_type, &key_length); + break; + case CKM_SSL3_PRE_MASTER_KEY_GEN: + key_type = CKK_GENERIC_SECRET; + key_length = 48; + key_gen_type = nsc_ssl; + break; + case CKM_PBA_SHA1_WITH_SHA1_HMAC: + case CKM_NSS_PBE_SHA1_HMAC_KEY_GEN: + case CKM_NSS_PBE_MD5_HMAC_KEY_GEN: + case CKM_NSS_PBE_MD2_HMAC_KEY_GEN: + case CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN: + case CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN: + case CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN: + case CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN: + key_gen_type = nsc_pbe; + key_type = CKK_GENERIC_SECRET; + crv = nsc_SetupHMACKeyGen(pMechanism, &pbe_param); + break; + case CKM_NSS_PBE_SHA1_FAULTY_3DES_CBC: + faultyPBE3DES = PR_TRUE; + /* fall through */ + case CKM_NSS_PBE_SHA1_TRIPLE_DES_CBC: +#ifndef NSS_DISABLE_DEPRECATED_RC2 + case CKM_NSS_PBE_SHA1_40_BIT_RC2_CBC: + case CKM_NSS_PBE_SHA1_128_BIT_RC2_CBC: + case CKM_PBE_SHA1_RC2_128_CBC: + case CKM_PBE_SHA1_RC2_40_CBC: +#endif + case CKM_NSS_PBE_SHA1_DES_CBC: + case CKM_NSS_PBE_SHA1_40_BIT_RC4: + case CKM_NSS_PBE_SHA1_128_BIT_RC4: + case CKM_PBE_SHA1_DES3_EDE_CBC: + case CKM_PBE_SHA1_DES2_EDE_CBC: + case CKM_PBE_SHA1_RC4_128: + case CKM_PBE_SHA1_RC4_40: + case CKM_PBE_MD5_DES_CBC: + case CKM_PBE_MD2_DES_CBC: + case CKM_PKCS5_PBKD2: + key_gen_type = nsc_pbe; + crv = nsc_SetupPBEKeyGen(pMechanism, &pbe_param, &key_type, &key_length); + break; + case CKM_DSA_PARAMETER_GEN: + key_gen_type = nsc_param; + key_type = CKK_DSA; + objclass = CKO_DOMAIN_PARAMETERS; + crv = CKR_OK; + break; + case CKM_NSS_JPAKE_ROUND1_SHA1: + hashType = HASH_AlgSHA1; + goto jpake1; + case CKM_NSS_JPAKE_ROUND1_SHA256: + hashType = HASH_AlgSHA256; + goto jpake1; + case CKM_NSS_JPAKE_ROUND1_SHA384: + hashType = HASH_AlgSHA384; + goto jpake1; + case CKM_NSS_JPAKE_ROUND1_SHA512: + hashType = HASH_AlgSHA512; + goto jpake1; + jpake1: + key_gen_type = nsc_jpake; + key_type = CKK_NSS_JPAKE_ROUND1; + objclass = CKO_PRIVATE_KEY; + if (pMechanism->pParameter == NULL || + pMechanism->ulParameterLen != sizeof(CK_NSS_JPAKERound1Params)) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + if (sftk_isTrue(key, CKA_TOKEN)) { + crv = CKR_TEMPLATE_INCONSISTENT; + break; + } + crv = CKR_OK; + break; + default: + crv = CKR_MECHANISM_INVALID; + break; + } + + /* make sure we aren't going to overflow the buffer */ + if (sizeof(buf) < key_length) { + /* someone is getting pretty optimistic about how big their key can + * be... */ + crv = CKR_TEMPLATE_INCONSISTENT; + } + + if (crv != CKR_OK) { + if (pbe_param) { + nsspkcs5_DestroyPBEParameter(pbe_param); + } + goto loser; + } + + /* if there was no error, + * key_type *MUST* be set in the switch statement above */ + PORT_Assert(key_type != CKK_INVALID_KEY_TYPE); + + /* + * now to the actual key gen. + */ + switch (key_gen_type) { + case nsc_pbe: + crv = nsc_pbe_key_gen(pbe_param, pMechanism, buf, &key_length, + faultyPBE3DES); + nsspkcs5_DestroyPBEParameter(pbe_param); + break; + case nsc_ssl: + rsa_pms = (SSL3RSAPreMasterSecret *)buf; + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_VERSION))) { + crv = CKR_MECHANISM_PARAM_INVALID; + goto loser; + } + version = (CK_VERSION *)pMechanism->pParameter; + rsa_pms->client_version[0] = version->major; + rsa_pms->client_version[1] = version->minor; + crv = + NSC_GenerateRandom(0, &rsa_pms->random[0], sizeof(rsa_pms->random)); + break; + case nsc_bulk: + /* get the key, check for weak keys and repeat if found */ + do { + crv = NSC_GenerateRandom(0, buf, key_length); + } while (crv == CKR_OK && checkWeak && sftk_IsWeakKey(buf, key_type)); + break; + case nsc_param: + /* generate parameters */ + *buf = 0; + crv = nsc_parameter_gen(key_type, key); + break; + case nsc_jpake: + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_NSS_JPAKERound1Params))) { + crv = CKR_MECHANISM_PARAM_INVALID; + goto loser; + } + crv = jpake_Round1(hashType, + (CK_NSS_JPAKERound1Params *)pMechanism->pParameter, + key); + break; + } + + if (crv != CKR_OK) { + goto loser; + } + + /* Add the class, key_type, and value */ + crv = sftk_AddAttributeType(key, CKA_CLASS, &objclass, sizeof(CK_OBJECT_CLASS)); + if (crv != CKR_OK) { + goto loser; + } + crv = sftk_AddAttributeType(key, CKA_KEY_TYPE, &key_type, sizeof(CK_KEY_TYPE)); + if (crv != CKR_OK) { + goto loser; + } + if (key_length != 0) { + crv = sftk_AddAttributeType(key, CKA_VALUE, buf, key_length); + if (crv != CKR_OK) { + goto loser; + } + } + + /* get the session */ + session = sftk_SessionFromHandle(hSession); + if (session == NULL) { + crv = CKR_SESSION_HANDLE_INVALID; + goto loser; + } + + /* + * handle the base object stuff + */ + crv = sftk_handleObject(key, session); + sftk_FreeSession(session); + if (crv == CKR_OK && sftk_isTrue(key, CKA_SENSITIVE)) { + crv = sftk_forceAttribute(key, CKA_ALWAYS_SENSITIVE, &cktrue, sizeof(CK_BBOOL)); + } + if (crv == CKR_OK && !sftk_isTrue(key, CKA_EXTRACTABLE)) { + crv = sftk_forceAttribute(key, CKA_NEVER_EXTRACTABLE, &cktrue, sizeof(CK_BBOOL)); + } + if (crv == CKR_OK) { + *phKey = key->handle; + } +loser: + PORT_Memset(buf, 0, sizeof buf); + sftk_FreeObject(key); + return crv; +} + +#define PAIRWISE_DIGEST_LENGTH SHA1_LENGTH /* 160-bits */ +#define PAIRWISE_MESSAGE_LENGTH 20 /* 160-bits */ + +/* + * FIPS 140-2 pairwise consistency check utilized to validate key pair. + * + * This function returns + * CKR_OK if pairwise consistency check passed + * CKR_GENERAL_ERROR if pairwise consistency check failed + * other error codes if paiswise consistency check could not be + * performed, for example, CKR_HOST_MEMORY. + */ +static CK_RV +sftk_PairwiseConsistencyCheck(CK_SESSION_HANDLE hSession, SFTKSlot *slot, + SFTKObject *publicKey, SFTKObject *privateKey, CK_KEY_TYPE keyType) +{ + /* + * Key type Mechanism type + * -------------------------------- + * For encrypt/decrypt: CKK_RSA => CKM_RSA_PKCS + * others => CKM_INVALID_MECHANISM + * + * For sign/verify: CKK_RSA => CKM_RSA_PKCS + * CKK_DSA => CKM_DSA + * CKK_EC => CKM_ECDSA + * others => CKM_INVALID_MECHANISM + * + * None of these mechanisms has a parameter. + * + * For derive CKK_DH => CKM_DH_PKCS_DERIVE + * CKK_EC => CKM_ECDH1_DERIVE + * others => CKM_INVALID_MECHANISM + * + * The parameters for these mechanisms is the public key. + */ + CK_MECHANISM mech = { 0, NULL, 0 }; + + CK_ULONG modulusLen = 0; + CK_ULONG subPrimeLen = 0; + PRBool isEncryptable = PR_FALSE; + PRBool canSignVerify = PR_FALSE; + PRBool isDerivable = PR_FALSE; + CK_RV crv; + + /* Variables used for Encrypt/Decrypt functions. */ + unsigned char *known_message = (unsigned char *)"Known Crypto Message"; + unsigned char plaintext[PAIRWISE_MESSAGE_LENGTH]; + CK_ULONG bytes_decrypted; + unsigned char *ciphertext; + unsigned char *text_compared; + CK_ULONG bytes_encrypted; + CK_ULONG bytes_compared; + CK_ULONG pairwise_digest_length = PAIRWISE_DIGEST_LENGTH; + + /* Variables used for Signature/Verification functions. */ + /* Must be at least 256 bits for DSA2 digest */ + unsigned char *known_digest = (unsigned char *)"Mozilla Rules the World through NSS!"; + unsigned char *signature; + CK_ULONG signature_length; + + if (keyType == CKK_RSA) { + SFTKAttribute *attribute; + + /* Get modulus length of private key. */ + attribute = sftk_FindAttribute(privateKey, CKA_MODULUS); + if (attribute == NULL) { + return CKR_DEVICE_ERROR; + } + modulusLen = attribute->attrib.ulValueLen; + if (*(unsigned char *)attribute->attrib.pValue == 0) { + modulusLen--; + } + sftk_FreeAttribute(attribute); + } else if (keyType == CKK_DSA) { + SFTKAttribute *attribute; + + /* Get subprime length of private key. */ + attribute = sftk_FindAttribute(privateKey, CKA_SUBPRIME); + if (attribute == NULL) { + return CKR_DEVICE_ERROR; + } + subPrimeLen = attribute->attrib.ulValueLen; + if (subPrimeLen > 1 && *(unsigned char *)attribute->attrib.pValue == 0) { + subPrimeLen--; + } + sftk_FreeAttribute(attribute); + } + + /**************************************************/ + /* Pairwise Consistency Check of Encrypt/Decrypt. */ + /**************************************************/ + + isEncryptable = sftk_isTrue(privateKey, CKA_DECRYPT); + + /* + * If the decryption attribute is set, attempt to encrypt + * with the public key and decrypt with the private key. + */ + if (isEncryptable) { + if (keyType != CKK_RSA) { + return CKR_DEVICE_ERROR; + } + bytes_encrypted = modulusLen; + mech.mechanism = CKM_RSA_PKCS; + + /* Allocate space for ciphertext. */ + ciphertext = (unsigned char *)PORT_ZAlloc(bytes_encrypted); + if (ciphertext == NULL) { + return CKR_HOST_MEMORY; + } + + /* Prepare for encryption using the public key. */ + crv = NSC_EncryptInit(hSession, &mech, publicKey->handle); + if (crv != CKR_OK) { + PORT_Free(ciphertext); + return crv; + } + + /* Encrypt using the public key. */ + crv = NSC_Encrypt(hSession, + known_message, + PAIRWISE_MESSAGE_LENGTH, + ciphertext, + &bytes_encrypted); + if (crv != CKR_OK) { + PORT_Free(ciphertext); + return crv; + } + + /* Always use the smaller of these two values . . . */ + bytes_compared = PR_MIN(bytes_encrypted, PAIRWISE_MESSAGE_LENGTH); + + /* + * If there was a failure, the plaintext + * goes at the end, therefore . . . + */ + text_compared = ciphertext + bytes_encrypted - bytes_compared; + + /* + * Check to ensure that ciphertext does + * NOT EQUAL known input message text + * per FIPS PUB 140-2 directive. + */ + if (PORT_Memcmp(text_compared, known_message, + bytes_compared) == 0) { + /* Set error to Invalid PRIVATE Key. */ + PORT_SetError(SEC_ERROR_INVALID_KEY); + PORT_Free(ciphertext); + return CKR_GENERAL_ERROR; + } + + /* Prepare for decryption using the private key. */ + crv = NSC_DecryptInit(hSession, &mech, privateKey->handle); + if (crv != CKR_OK) { + PORT_Free(ciphertext); + return crv; + } + + memset(plaintext, 0, PAIRWISE_MESSAGE_LENGTH); + + /* + * Initialize bytes decrypted to be the + * expected PAIRWISE_MESSAGE_LENGTH. + */ + bytes_decrypted = PAIRWISE_MESSAGE_LENGTH; + + /* + * Decrypt using the private key. + * NOTE: No need to reset the + * value of bytes_encrypted. + */ + crv = NSC_Decrypt(hSession, + ciphertext, + bytes_encrypted, + plaintext, + &bytes_decrypted); + + /* Finished with ciphertext; free it. */ + PORT_Free(ciphertext); + + if (crv != CKR_OK) { + return crv; + } + + /* + * Check to ensure that the output plaintext + * does EQUAL known input message text. + */ + if ((bytes_decrypted != PAIRWISE_MESSAGE_LENGTH) || + (PORT_Memcmp(plaintext, known_message, + PAIRWISE_MESSAGE_LENGTH) != 0)) { + /* Set error to Bad PUBLIC Key. */ + PORT_SetError(SEC_ERROR_BAD_KEY); + return CKR_GENERAL_ERROR; + } + } + + /**********************************************/ + /* Pairwise Consistency Check of Sign/Verify. */ + /**********************************************/ + + canSignVerify = sftk_isTrue(privateKey, CKA_SIGN); + /* Unfortunately CKA_SIGN is always true in lg dbs. We have to check the + * actual curve to determine if we can do sign/verify. */ + if (canSignVerify && keyType == CKK_EC) { + NSSLOWKEYPrivateKey *privKey = sftk_GetPrivKey(privateKey, CKK_EC, &crv); + if (privKey && privKey->u.ec.ecParams.name == ECCurve25519) { + canSignVerify = PR_FALSE; + } + } + + if (canSignVerify) { + /* Determine length of signature. */ + switch (keyType) { + case CKK_RSA: + signature_length = modulusLen; + mech.mechanism = CKM_RSA_PKCS; + break; + case CKK_DSA: + signature_length = DSA_MAX_SIGNATURE_LEN; + pairwise_digest_length = subPrimeLen; + mech.mechanism = CKM_DSA; + break; + case CKK_EC: + signature_length = MAX_ECKEY_LEN * 2; + mech.mechanism = CKM_ECDSA; + break; + default: + return CKR_DEVICE_ERROR; + } + + /* Allocate space for signature data. */ + signature = (unsigned char *)PORT_ZAlloc(signature_length); + if (signature == NULL) { + return CKR_HOST_MEMORY; + } + + /* Sign the known hash using the private key. */ + crv = NSC_SignInit(hSession, &mech, privateKey->handle); + if (crv != CKR_OK) { + PORT_Free(signature); + return crv; + } + + crv = NSC_Sign(hSession, + known_digest, + pairwise_digest_length, + signature, + &signature_length); + if (crv != CKR_OK) { + PORT_Free(signature); + return crv; + } + + /* detect trivial signing transforms */ + if ((signature_length >= pairwise_digest_length) && + (PORT_Memcmp(known_digest, signature + (signature_length - pairwise_digest_length), pairwise_digest_length) == 0)) { + PORT_Free(signature); + return CKR_DEVICE_ERROR; + } + + /* Verify the known hash using the public key. */ + crv = NSC_VerifyInit(hSession, &mech, publicKey->handle); + if (crv != CKR_OK) { + PORT_Free(signature); + return crv; + } + + crv = NSC_Verify(hSession, + known_digest, + pairwise_digest_length, + signature, + signature_length); + + /* Free signature data. */ + PORT_Free(signature); + + if ((crv == CKR_SIGNATURE_LEN_RANGE) || + (crv == CKR_SIGNATURE_INVALID)) { + return CKR_GENERAL_ERROR; + } + if (crv != CKR_OK) { + return crv; + } + } + + /**********************************************/ + /* Pairwise Consistency Check for Derivation */ + /**********************************************/ + + isDerivable = sftk_isTrue(privateKey, CKA_DERIVE); + + if (isDerivable) { + SFTKAttribute *pubAttribute = NULL; + CK_OBJECT_HANDLE newKey; + PRBool isFIPS = sftk_isFIPS(slot->slotID); + CK_RV crv2; + CK_OBJECT_CLASS secret = CKO_SECRET_KEY; + CK_KEY_TYPE generic = CKK_GENERIC_SECRET; + CK_ULONG keyLen = 128; + CK_BBOOL ckTrue = CK_TRUE; + CK_ATTRIBUTE template[] = { + { CKA_CLASS, &secret, sizeof(secret) }, + { CKA_KEY_TYPE, &generic, sizeof(generic) }, + { CKA_VALUE_LEN, &keyLen, sizeof(keyLen) }, + { CKA_DERIVE, &ckTrue, sizeof(ckTrue) } + }; + CK_ULONG templateCount = PR_ARRAY_SIZE(template); + CK_ECDH1_DERIVE_PARAMS ecParams; + + crv = CKR_OK; /*paranoia, already get's set before we drop to the end */ + /* FIPS 140-2 requires we verify that the resulting key is a valid key. + * The easiest way to do this is to do a derive operation, which checks + * the validity of the key */ + + switch (keyType) { + case CKK_DH: + mech.mechanism = CKM_DH_PKCS_DERIVE; + pubAttribute = sftk_FindAttribute(publicKey, CKA_VALUE); + if (pubAttribute == NULL) { + return CKR_DEVICE_ERROR; + } + mech.pParameter = pubAttribute->attrib.pValue; + mech.ulParameterLen = pubAttribute->attrib.ulValueLen; + break; + case CKK_EC: + mech.mechanism = CKM_ECDH1_DERIVE; + pubAttribute = sftk_FindAttribute(publicKey, CKA_EC_POINT); + if (pubAttribute == NULL) { + return CKR_DEVICE_ERROR; + } + ecParams.kdf = CKD_NULL; + ecParams.ulSharedDataLen = 0; + ecParams.pSharedData = NULL; + ecParams.ulPublicDataLen = pubAttribute->attrib.ulValueLen; + ecParams.pPublicData = pubAttribute->attrib.pValue; + mech.pParameter = &ecParams; + mech.ulParameterLen = sizeof(ecParams); + break; + default: + return CKR_DEVICE_ERROR; + } + + crv = NSC_DeriveKey(hSession, &mech, privateKey->handle, template, templateCount, &newKey); + if (crv != CKR_OK) { + sftk_FreeAttribute(pubAttribute); + return crv; + } + /* FIPS requires full validation, but in fipx mode NSC_Derive + * only does partial validation with approved primes, now handle + * full validation */ + if (isFIPS && keyType == CKK_DH) { + SECItem pubKey; + SECItem prime; + SECItem subPrime; + const SECItem *subPrimePtr = &subPrime; + + pubKey.data = pubAttribute->attrib.pValue; + pubKey.len = pubAttribute->attrib.ulValueLen; + prime.data = subPrime.data = NULL; + prime.len = subPrime.len = 0; + crv = sftk_Attribute2SecItem(NULL, &prime, privateKey, CKA_PRIME); + if (crv != CKR_OK) { + goto done; + } + crv = sftk_Attribute2SecItem(NULL, &prime, privateKey, CKA_PRIME); + /* we ignore the return code an only look at the length */ + if (subPrime.len == 0) { + /* subprime not supplied, In this case look it up. + * This only works with approved primes, but in FIPS mode + * that's the only kine of prime that will get here */ + subPrimePtr = sftk_VerifyDH_Prime(&prime, isFIPS); + if (subPrimePtr == NULL) { + crv = CKR_GENERAL_ERROR; + goto done; + } + } + if (!KEA_Verify(&pubKey, &prime, (SECItem *)subPrimePtr)) { + crv = CKR_GENERAL_ERROR; + } + done: + SECITEM_ZfreeItem(&subPrime, PR_FALSE); + SECITEM_ZfreeItem(&prime, PR_FALSE); + } + /* clean up before we return */ + sftk_FreeAttribute(pubAttribute); + crv2 = NSC_DestroyObject(hSession, newKey); + if (crv != CKR_OK) { + return crv; + } + if (crv2 != CKR_OK) { + return crv2; + } + } + + return CKR_OK; +} + +/* NSC_GenerateKeyPair generates a public-key/private-key pair, + * creating new key objects. */ +CK_RV +NSC_GenerateKeyPair(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pPublicKeyTemplate, + CK_ULONG ulPublicKeyAttributeCount, CK_ATTRIBUTE_PTR pPrivateKeyTemplate, + CK_ULONG ulPrivateKeyAttributeCount, CK_OBJECT_HANDLE_PTR phPublicKey, + CK_OBJECT_HANDLE_PTR phPrivateKey) +{ + SFTKObject *publicKey, *privateKey; + SFTKSession *session; + CK_KEY_TYPE key_type; + CK_RV crv = CKR_OK; + CK_BBOOL cktrue = CK_TRUE; + SECStatus rv; + CK_OBJECT_CLASS pubClass = CKO_PUBLIC_KEY; + CK_OBJECT_CLASS privClass = CKO_PRIVATE_KEY; + int i; + SFTKSlot *slot = sftk_SlotFromSessionHandle(hSession); + unsigned int bitSize; + + /* RSA */ + int public_modulus_bits = 0; + SECItem pubExp; + RSAPrivateKey *rsaPriv; + + /* DSA */ + PQGParams pqgParam; + DHParams dhParam; + DSAPrivateKey *dsaPriv; + + /* Diffie Hellman */ + DHPrivateKey *dhPriv; + + /* Elliptic Curve Cryptography */ + SECItem ecEncodedParams; /* DER Encoded parameters */ + ECPrivateKey *ecPriv; + ECParams *ecParams; + + /* Kyber */ + CK_NSS_KEM_PARAMETER_SET_TYPE ckKyberParamSet; + + CHECK_FORK(); + + if (!slot) { + return CKR_SESSION_HANDLE_INVALID; + } + /* + * now lets create an object to hang the attributes off of + */ + publicKey = sftk_NewObject(slot); /* fill in the handle later */ + if (publicKey == NULL) { + return CKR_HOST_MEMORY; + } + + /* + * load the template values into the publicKey + */ + for (i = 0; i < (int)ulPublicKeyAttributeCount; i++) { + if (pPublicKeyTemplate[i].type == CKA_MODULUS_BITS) { + public_modulus_bits = *(CK_ULONG *)pPublicKeyTemplate[i].pValue; + continue; + } + + if (pPublicKeyTemplate[i].type == CKA_NSS_PARAMETER_SET) { + ckKyberParamSet = *(CK_NSS_KEM_PARAMETER_SET_TYPE *)pPublicKeyTemplate[i].pValue; + continue; + } + + crv = sftk_AddAttributeType(publicKey, + sftk_attr_expand(&pPublicKeyTemplate[i])); + if (crv != CKR_OK) + break; + } + + if (crv != CKR_OK) { + sftk_FreeObject(publicKey); + return CKR_HOST_MEMORY; + } + + privateKey = sftk_NewObject(slot); /* fill in the handle later */ + if (privateKey == NULL) { + sftk_FreeObject(publicKey); + return CKR_HOST_MEMORY; + } + /* + * now load the private key template + */ + for (i = 0; i < (int)ulPrivateKeyAttributeCount; i++) { + if (pPrivateKeyTemplate[i].type == CKA_VALUE_BITS) { + continue; + } + + crv = sftk_AddAttributeType(privateKey, + sftk_attr_expand(&pPrivateKeyTemplate[i])); + if (crv != CKR_OK) + break; + } + + if (crv != CKR_OK) { + sftk_FreeObject(publicKey); + sftk_FreeObject(privateKey); + return CKR_HOST_MEMORY; + } + sftk_DeleteAttributeType(privateKey, CKA_CLASS); + sftk_DeleteAttributeType(privateKey, CKA_KEY_TYPE); + sftk_DeleteAttributeType(privateKey, CKA_VALUE); + sftk_DeleteAttributeType(publicKey, CKA_CLASS); + sftk_DeleteAttributeType(publicKey, CKA_KEY_TYPE); + sftk_DeleteAttributeType(publicKey, CKA_VALUE); + + /* Now Set up the parameters to generate the key (based on mechanism) */ + switch (pMechanism->mechanism) { + case CKM_RSA_PKCS_KEY_PAIR_GEN: + /* format the keys */ + sftk_DeleteAttributeType(publicKey, CKA_MODULUS); + sftk_DeleteAttributeType(privateKey, CKA_NSS_DB); + sftk_DeleteAttributeType(privateKey, CKA_MODULUS); + sftk_DeleteAttributeType(privateKey, CKA_PRIVATE_EXPONENT); + sftk_DeleteAttributeType(privateKey, CKA_PUBLIC_EXPONENT); + sftk_DeleteAttributeType(privateKey, CKA_PRIME_1); + sftk_DeleteAttributeType(privateKey, CKA_PRIME_2); + sftk_DeleteAttributeType(privateKey, CKA_EXPONENT_1); + sftk_DeleteAttributeType(privateKey, CKA_EXPONENT_2); + sftk_DeleteAttributeType(privateKey, CKA_COEFFICIENT); + key_type = CKK_RSA; + if (public_modulus_bits == 0) { + crv = CKR_TEMPLATE_INCOMPLETE; + break; + } + if (public_modulus_bits < RSA_MIN_MODULUS_BITS) { + crv = CKR_ATTRIBUTE_VALUE_INVALID; + break; + } + if (public_modulus_bits % 2 != 0) { + crv = CKR_ATTRIBUTE_VALUE_INVALID; + break; + } + + /* extract the exponent */ + crv = sftk_Attribute2SSecItem(NULL, &pubExp, publicKey, CKA_PUBLIC_EXPONENT); + if (crv != CKR_OK) + break; + bitSize = sftk_GetLengthInBits(pubExp.data, pubExp.len); + if (bitSize < 2) { + crv = CKR_ATTRIBUTE_VALUE_INVALID; + SECITEM_ZfreeItem(&pubExp, PR_FALSE); + break; + } + crv = sftk_AddAttributeType(privateKey, CKA_PUBLIC_EXPONENT, + sftk_item_expand(&pubExp)); + if (crv != CKR_OK) { + SECITEM_ZfreeItem(&pubExp, PR_FALSE); + break; + } + + rsaPriv = RSA_NewKey(public_modulus_bits, &pubExp); + SECITEM_ZfreeItem(&pubExp, PR_FALSE); + if (rsaPriv == NULL) { + if (PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { + sftk_fatalError = PR_TRUE; + } + crv = sftk_MapCryptError(PORT_GetError()); + break; + } + /* now fill in the RSA dependent paramenters in the public key */ + crv = sftk_AddAttributeType(publicKey, CKA_MODULUS, + sftk_item_expand(&rsaPriv->modulus)); + if (crv != CKR_OK) + goto kpg_done; + /* now fill in the RSA dependent paramenters in the private key */ + crv = sftk_AddAttributeType(privateKey, CKA_NSS_DB, + sftk_item_expand(&rsaPriv->modulus)); + if (crv != CKR_OK) + goto kpg_done; + crv = sftk_AddAttributeType(privateKey, CKA_MODULUS, + sftk_item_expand(&rsaPriv->modulus)); + if (crv != CKR_OK) + goto kpg_done; + crv = sftk_AddAttributeType(privateKey, CKA_PRIVATE_EXPONENT, + sftk_item_expand(&rsaPriv->privateExponent)); + if (crv != CKR_OK) + goto kpg_done; + crv = sftk_AddAttributeType(privateKey, CKA_PRIME_1, + sftk_item_expand(&rsaPriv->prime1)); + if (crv != CKR_OK) + goto kpg_done; + crv = sftk_AddAttributeType(privateKey, CKA_PRIME_2, + sftk_item_expand(&rsaPriv->prime2)); + if (crv != CKR_OK) + goto kpg_done; + crv = sftk_AddAttributeType(privateKey, CKA_EXPONENT_1, + sftk_item_expand(&rsaPriv->exponent1)); + if (crv != CKR_OK) + goto kpg_done; + crv = sftk_AddAttributeType(privateKey, CKA_EXPONENT_2, + sftk_item_expand(&rsaPriv->exponent2)); + if (crv != CKR_OK) + goto kpg_done; + crv = sftk_AddAttributeType(privateKey, CKA_COEFFICIENT, + sftk_item_expand(&rsaPriv->coefficient)); + kpg_done: + /* Should zeroize the contents first, since this func doesn't. */ + PORT_FreeArena(rsaPriv->arena, PR_TRUE); + break; + case CKM_DSA_KEY_PAIR_GEN: + sftk_DeleteAttributeType(publicKey, CKA_VALUE); + sftk_DeleteAttributeType(privateKey, CKA_NSS_DB); + sftk_DeleteAttributeType(privateKey, CKA_PRIME); + sftk_DeleteAttributeType(privateKey, CKA_SUBPRIME); + sftk_DeleteAttributeType(privateKey, CKA_BASE); + key_type = CKK_DSA; + + /* extract the necessary parameters and copy them to the private key */ + crv = sftk_Attribute2SSecItem(NULL, &pqgParam.prime, publicKey, CKA_PRIME); + if (crv != CKR_OK) + break; + crv = sftk_Attribute2SSecItem(NULL, &pqgParam.subPrime, publicKey, + CKA_SUBPRIME); + if (crv != CKR_OK) { + SECITEM_ZfreeItem(&pqgParam.prime, PR_FALSE); + break; + } + crv = sftk_Attribute2SSecItem(NULL, &pqgParam.base, publicKey, CKA_BASE); + if (crv != CKR_OK) { + SECITEM_ZfreeItem(&pqgParam.prime, PR_FALSE); + SECITEM_ZfreeItem(&pqgParam.subPrime, PR_FALSE); + break; + } + crv = sftk_AddAttributeType(privateKey, CKA_PRIME, + sftk_item_expand(&pqgParam.prime)); + if (crv != CKR_OK) { + SECITEM_ZfreeItem(&pqgParam.prime, PR_FALSE); + SECITEM_ZfreeItem(&pqgParam.subPrime, PR_FALSE); + SECITEM_ZfreeItem(&pqgParam.base, PR_FALSE); + break; + } + crv = sftk_AddAttributeType(privateKey, CKA_SUBPRIME, + sftk_item_expand(&pqgParam.subPrime)); + if (crv != CKR_OK) { + SECITEM_ZfreeItem(&pqgParam.prime, PR_FALSE); + SECITEM_ZfreeItem(&pqgParam.subPrime, PR_FALSE); + SECITEM_ZfreeItem(&pqgParam.base, PR_FALSE); + break; + } + crv = sftk_AddAttributeType(privateKey, CKA_BASE, + sftk_item_expand(&pqgParam.base)); + if (crv != CKR_OK) { + SECITEM_ZfreeItem(&pqgParam.prime, PR_FALSE); + SECITEM_ZfreeItem(&pqgParam.subPrime, PR_FALSE); + SECITEM_ZfreeItem(&pqgParam.base, PR_FALSE); + break; + } + + /* + * these are checked by DSA_NewKey + */ + bitSize = sftk_GetLengthInBits(pqgParam.subPrime.data, + pqgParam.subPrime.len); + if ((bitSize < DSA_MIN_Q_BITS) || (bitSize > DSA_MAX_Q_BITS)) { + crv = CKR_TEMPLATE_INCOMPLETE; + SECITEM_ZfreeItem(&pqgParam.prime, PR_FALSE); + SECITEM_ZfreeItem(&pqgParam.subPrime, PR_FALSE); + SECITEM_ZfreeItem(&pqgParam.base, PR_FALSE); + break; + } + bitSize = sftk_GetLengthInBits(pqgParam.prime.data, pqgParam.prime.len); + if ((bitSize < DSA_MIN_P_BITS) || (bitSize > DSA_MAX_P_BITS)) { + crv = CKR_TEMPLATE_INCOMPLETE; + SECITEM_ZfreeItem(&pqgParam.prime, PR_FALSE); + SECITEM_ZfreeItem(&pqgParam.subPrime, PR_FALSE); + SECITEM_ZfreeItem(&pqgParam.base, PR_FALSE); + break; + } + bitSize = sftk_GetLengthInBits(pqgParam.base.data, pqgParam.base.len); + if ((bitSize < 2) || (bitSize > DSA_MAX_P_BITS)) { + crv = CKR_TEMPLATE_INCOMPLETE; + SECITEM_ZfreeItem(&pqgParam.prime, PR_FALSE); + SECITEM_ZfreeItem(&pqgParam.subPrime, PR_FALSE); + SECITEM_ZfreeItem(&pqgParam.base, PR_FALSE); + break; + } + + /* Generate the key */ + rv = DSA_NewKey(&pqgParam, &dsaPriv); + + SECITEM_ZfreeItem(&pqgParam.prime, PR_FALSE); + SECITEM_ZfreeItem(&pqgParam.subPrime, PR_FALSE); + SECITEM_ZfreeItem(&pqgParam.base, PR_FALSE); + + if (rv != SECSuccess) { + if (PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { + sftk_fatalError = PR_TRUE; + } + crv = sftk_MapCryptError(PORT_GetError()); + break; + } + + /* store the generated key into the attributes */ + crv = sftk_AddAttributeType(publicKey, CKA_VALUE, + sftk_item_expand(&dsaPriv->publicValue)); + if (crv != CKR_OK) + goto dsagn_done; + + /* now fill in the RSA dependent paramenters in the private key */ + crv = sftk_AddAttributeType(privateKey, CKA_NSS_DB, + sftk_item_expand(&dsaPriv->publicValue)); + if (crv != CKR_OK) + goto dsagn_done; + crv = sftk_AddAttributeType(privateKey, CKA_VALUE, + sftk_item_expand(&dsaPriv->privateValue)); + + dsagn_done: + /* should zeroize, since this function doesn't. */ + PORT_FreeArena(dsaPriv->params.arena, PR_TRUE); + break; + + case CKM_DH_PKCS_KEY_PAIR_GEN: + sftk_DeleteAttributeType(privateKey, CKA_PRIME); + sftk_DeleteAttributeType(privateKey, CKA_BASE); + sftk_DeleteAttributeType(privateKey, CKA_VALUE); + sftk_DeleteAttributeType(privateKey, CKA_NSS_DB); + key_type = CKK_DH; + + /* extract the necessary parameters and copy them to private keys */ + crv = sftk_Attribute2SSecItem(NULL, &dhParam.prime, publicKey, + CKA_PRIME); + if (crv != CKR_OK) + break; + crv = sftk_Attribute2SSecItem(NULL, &dhParam.base, publicKey, CKA_BASE); + if (crv != CKR_OK) { + SECITEM_ZfreeItem(&dhParam.prime, PR_FALSE); + break; + } + crv = sftk_AddAttributeType(privateKey, CKA_PRIME, + sftk_item_expand(&dhParam.prime)); + if (crv != CKR_OK) { + SECITEM_ZfreeItem(&dhParam.prime, PR_FALSE); + SECITEM_ZfreeItem(&dhParam.base, PR_FALSE); + break; + } + crv = sftk_AddAttributeType(privateKey, CKA_BASE, + sftk_item_expand(&dhParam.base)); + if (crv != CKR_OK) { + SECITEM_ZfreeItem(&dhParam.prime, PR_FALSE); + SECITEM_ZfreeItem(&dhParam.base, PR_FALSE); + break; + } + bitSize = sftk_GetLengthInBits(dhParam.prime.data, dhParam.prime.len); + if ((bitSize < DH_MIN_P_BITS) || (bitSize > DH_MAX_P_BITS)) { + crv = CKR_TEMPLATE_INCOMPLETE; + SECITEM_ZfreeItem(&dhParam.prime, PR_FALSE); + SECITEM_ZfreeItem(&dhParam.base, PR_FALSE); + break; + } + bitSize = sftk_GetLengthInBits(dhParam.base.data, dhParam.base.len); + if ((bitSize < 1) || (bitSize > DH_MAX_P_BITS)) { + crv = CKR_TEMPLATE_INCOMPLETE; + SECITEM_ZfreeItem(&dhParam.prime, PR_FALSE); + SECITEM_ZfreeItem(&dhParam.base, PR_FALSE); + break; + } + + rv = DH_NewKey(&dhParam, &dhPriv); + SECITEM_ZfreeItem(&dhParam.prime, PR_FALSE); + SECITEM_ZfreeItem(&dhParam.base, PR_FALSE); + if (rv != SECSuccess) { + if (PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { + sftk_fatalError = PR_TRUE; + } + crv = sftk_MapCryptError(PORT_GetError()); + break; + } + + crv = sftk_AddAttributeType(publicKey, CKA_VALUE, + sftk_item_expand(&dhPriv->publicValue)); + if (crv != CKR_OK) + goto dhgn_done; + + crv = sftk_AddAttributeType(privateKey, CKA_NSS_DB, + sftk_item_expand(&dhPriv->publicValue)); + if (crv != CKR_OK) + goto dhgn_done; + + crv = sftk_AddAttributeType(privateKey, CKA_VALUE, + sftk_item_expand(&dhPriv->privateValue)); + + dhgn_done: + /* should zeroize, since this function doesn't. */ + PORT_FreeArena(dhPriv->arena, PR_TRUE); + break; + + case CKM_EC_KEY_PAIR_GEN: + sftk_DeleteAttributeType(privateKey, CKA_EC_PARAMS); + sftk_DeleteAttributeType(privateKey, CKA_VALUE); + sftk_DeleteAttributeType(privateKey, CKA_NSS_DB); + key_type = CKK_EC; + + /* extract the necessary parameters and copy them to private keys */ + crv = sftk_Attribute2SSecItem(NULL, &ecEncodedParams, publicKey, + CKA_EC_PARAMS); + if (crv != CKR_OK) + break; + + crv = sftk_AddAttributeType(privateKey, CKA_EC_PARAMS, + sftk_item_expand(&ecEncodedParams)); + if (crv != CKR_OK) { + SECITEM_ZfreeItem(&ecEncodedParams, PR_FALSE); + break; + } + + /* Decode ec params before calling EC_NewKey */ + rv = EC_DecodeParams(&ecEncodedParams, &ecParams); + SECITEM_ZfreeItem(&ecEncodedParams, PR_FALSE); + if (rv != SECSuccess) { + crv = sftk_MapCryptError(PORT_GetError()); + break; + } + rv = EC_NewKey(ecParams, &ecPriv); + if (rv != SECSuccess) { + if (PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { + sftk_fatalError = PR_TRUE; + } + PORT_FreeArena(ecParams->arena, PR_TRUE); + crv = sftk_MapCryptError(PORT_GetError()); + break; + } + + if (PR_GetEnvSecure("NSS_USE_DECODED_CKA_EC_POINT") || + ecParams->type != ec_params_named) { + PORT_FreeArena(ecParams->arena, PR_TRUE); + crv = sftk_AddAttributeType(publicKey, CKA_EC_POINT, + sftk_item_expand(&ecPriv->publicValue)); + } else { + PORT_FreeArena(ecParams->arena, PR_TRUE); + SECItem *pubValue = SEC_ASN1EncodeItem(NULL, NULL, + &ecPriv->publicValue, + SEC_ASN1_GET(SEC_OctetStringTemplate)); + if (!pubValue) { + crv = CKR_ARGUMENTS_BAD; + goto ecgn_done; + } + crv = sftk_AddAttributeType(publicKey, CKA_EC_POINT, + sftk_item_expand(pubValue)); + SECITEM_ZfreeItem(pubValue, PR_TRUE); + } + if (crv != CKR_OK) + goto ecgn_done; + + crv = sftk_AddAttributeType(privateKey, CKA_VALUE, + sftk_item_expand(&ecPriv->privateValue)); + if (crv != CKR_OK) + goto ecgn_done; + + crv = sftk_AddAttributeType(privateKey, CKA_NSS_DB, + sftk_item_expand(&ecPriv->publicValue)); + ecgn_done: + /* should zeroize, since this function doesn't. */ + PORT_FreeArena(ecPriv->ecParams.arena, PR_TRUE); + break; + + case CKM_NSS_KYBER_KEY_PAIR_GEN: + sftk_DeleteAttributeType(privateKey, CKA_NSS_DB); + key_type = CKK_NSS_KYBER; + + SECItem privKey = { siBuffer, NULL, 0 }; + SECItem pubKey = { siBuffer, NULL, 0 }; + KyberParams kyberParams = sftk_kyber_PK11ParamToInternal(ckKyberParamSet); + if (!sftk_kyber_AllocPrivKeyItem(kyberParams, &privKey)) { + crv = CKR_HOST_MEMORY; + goto kyber_done; + } + if (!sftk_kyber_AllocPubKeyItem(kyberParams, &pubKey)) { + crv = CKR_HOST_MEMORY; + goto kyber_done; + } + rv = Kyber_NewKey(kyberParams, NULL, &privKey, &pubKey); + if (rv != SECSuccess) { + crv = sftk_MapCryptError(PORT_GetError()); + goto kyber_done; + } + + crv = sftk_AddAttributeType(publicKey, CKA_VALUE, sftk_item_expand(&pubKey)); + if (crv != CKR_OK) { + goto kyber_done; + } + crv = sftk_AddAttributeType(publicKey, CKA_NSS_PARAMETER_SET, + &ckKyberParamSet, sizeof(CK_NSS_KEM_PARAMETER_SET_TYPE)); + if (crv != CKR_OK) { + goto kyber_done; + } + crv = sftk_AddAttributeType(privateKey, CKA_VALUE, + sftk_item_expand(&privKey)); + if (crv != CKR_OK) { + goto kyber_done; + } + crv = sftk_AddAttributeType(privateKey, CKA_NSS_PARAMETER_SET, + &ckKyberParamSet, sizeof(CK_NSS_KEM_PARAMETER_SET_TYPE)); + if (crv != CKR_OK) { + goto kyber_done; + } + crv = sftk_AddAttributeType(privateKey, CKA_NSS_DB, + sftk_item_expand(&pubKey)); + kyber_done: + SECITEM_ZfreeItem(&privKey, PR_FALSE); + SECITEM_FreeItem(&pubKey, PR_FALSE); + break; + + default: + crv = CKR_MECHANISM_INVALID; + } + + if (crv != CKR_OK) { + sftk_FreeObject(privateKey); + sftk_FreeObject(publicKey); + return crv; + } + + /* Add the class, key_type The loop lets us check errors blow out + * on errors and clean up at the bottom */ + session = NULL; /* make pedtantic happy... session cannot leave the*/ + /* loop below NULL unless an error is set... */ + do { + crv = sftk_AddAttributeType(privateKey, CKA_CLASS, &privClass, + sizeof(CK_OBJECT_CLASS)); + if (crv != CKR_OK) + break; + crv = sftk_AddAttributeType(publicKey, CKA_CLASS, &pubClass, + sizeof(CK_OBJECT_CLASS)); + if (crv != CKR_OK) + break; + crv = sftk_AddAttributeType(privateKey, CKA_KEY_TYPE, &key_type, + sizeof(CK_KEY_TYPE)); + if (crv != CKR_OK) + break; + crv = sftk_AddAttributeType(publicKey, CKA_KEY_TYPE, &key_type, + sizeof(CK_KEY_TYPE)); + if (crv != CKR_OK) + break; + session = sftk_SessionFromHandle(hSession); + if (session == NULL) + crv = CKR_SESSION_HANDLE_INVALID; + } while (0); + + if (crv != CKR_OK) { + sftk_FreeObject(privateKey); + sftk_FreeObject(publicKey); + return crv; + } + + /* + * handle the base object cleanup for the public Key + */ + crv = sftk_handleObject(privateKey, session); + if (crv != CKR_OK) { + sftk_FreeSession(session); + sftk_FreeObject(privateKey); + sftk_FreeObject(publicKey); + return crv; + } + + /* + * handle the base object cleanup for the private Key + * If we have any problems, we destroy the public Key we've + * created and linked. + */ + crv = sftk_handleObject(publicKey, session); + sftk_FreeSession(session); + if (crv != CKR_OK) { + sftk_FreeObject(publicKey); + NSC_DestroyObject(hSession, privateKey->handle); + sftk_FreeObject(privateKey); + return crv; + } + if (sftk_isTrue(privateKey, CKA_SENSITIVE)) { + crv = sftk_forceAttribute(privateKey, CKA_ALWAYS_SENSITIVE, + &cktrue, sizeof(CK_BBOOL)); + } + if (crv == CKR_OK && sftk_isTrue(publicKey, CKA_SENSITIVE)) { + crv = sftk_forceAttribute(publicKey, CKA_ALWAYS_SENSITIVE, + &cktrue, sizeof(CK_BBOOL)); + } + if (crv == CKR_OK && !sftk_isTrue(privateKey, CKA_EXTRACTABLE)) { + crv = sftk_forceAttribute(privateKey, CKA_NEVER_EXTRACTABLE, + &cktrue, sizeof(CK_BBOOL)); + } + if (crv == CKR_OK && !sftk_isTrue(publicKey, CKA_EXTRACTABLE)) { + crv = sftk_forceAttribute(publicKey, CKA_NEVER_EXTRACTABLE, + &cktrue, sizeof(CK_BBOOL)); + } + + if (crv == CKR_OK && key_type != CKK_NSS_KYBER) { + /* Perform FIPS 140-2 pairwise consistency check. */ + crv = sftk_PairwiseConsistencyCheck(hSession, slot, + publicKey, privateKey, key_type); + if (crv != CKR_OK) { + if (sftk_audit_enabled) { + char msg[128]; + PR_snprintf(msg, sizeof msg, + "C_GenerateKeyPair(hSession=0x%08lX, " + "pMechanism->mechanism=0x%08lX)=0x%08lX " + "self-test: pair-wise consistency test failed", + (PRUint32)hSession, (PRUint32)pMechanism->mechanism, + (PRUint32)crv); + sftk_LogAuditMessage(NSS_AUDIT_ERROR, NSS_AUDIT_SELF_TEST, msg); + } + } + } + + if (crv != CKR_OK) { + NSC_DestroyObject(hSession, publicKey->handle); + sftk_FreeObject(publicKey); + NSC_DestroyObject(hSession, privateKey->handle); + sftk_FreeObject(privateKey); + return crv; + } + + *phPrivateKey = privateKey->handle; + *phPublicKey = publicKey->handle; + sftk_FreeObject(publicKey); + sftk_FreeObject(privateKey); + + return CKR_OK; +} + +static SECItem * +sftk_PackagePrivateKey(SFTKObject *key, CK_RV *crvp) +{ + NSSLOWKEYPrivateKey *lk = NULL; + NSSLOWKEYPrivateKeyInfo *pki = NULL; + SFTKAttribute *attribute = NULL; + PLArenaPool *arena = NULL; + SECOidTag algorithm = SEC_OID_UNKNOWN; + void *dummy, *param = NULL; + SECStatus rv = SECSuccess; + SECItem *encodedKey = NULL; +#ifdef EC_DEBUG + SECItem *fordebug; +#endif + int savelen; + + if (!key) { + *crvp = CKR_KEY_HANDLE_INVALID; /* really can't happen */ + return NULL; + } + + attribute = sftk_FindAttribute(key, CKA_KEY_TYPE); + if (!attribute) { + *crvp = CKR_KEY_TYPE_INCONSISTENT; + return NULL; + } + + lk = sftk_GetPrivKey(key, *(CK_KEY_TYPE *)attribute->attrib.pValue, crvp); + sftk_FreeAttribute(attribute); + if (!lk) { + return NULL; + } + + arena = PORT_NewArena(2048); /* XXX different size? */ + if (!arena) { + *crvp = CKR_HOST_MEMORY; + rv = SECFailure; + goto loser; + } + + pki = (NSSLOWKEYPrivateKeyInfo *)PORT_ArenaZAlloc(arena, + sizeof(NSSLOWKEYPrivateKeyInfo)); + if (!pki) { + *crvp = CKR_HOST_MEMORY; + rv = SECFailure; + goto loser; + } + pki->arena = arena; + + param = NULL; + switch (lk->keyType) { + case NSSLOWKEYRSAKey: + prepare_low_rsa_priv_key_for_asn1(lk); + dummy = SEC_ASN1EncodeItem(arena, &pki->privateKey, lk, + nsslowkey_RSAPrivateKeyTemplate); + + /* determine RSA key type from the CKA_PUBLIC_KEY_INFO if present */ + attribute = sftk_FindAttribute(key, CKA_PUBLIC_KEY_INFO); + if (attribute) { + NSSLOWKEYSubjectPublicKeyInfo *publicKeyInfo; + SECItem spki; + + spki.data = attribute->attrib.pValue; + spki.len = attribute->attrib.ulValueLen; + + publicKeyInfo = PORT_ArenaZAlloc(arena, + sizeof(NSSLOWKEYSubjectPublicKeyInfo)); + if (!publicKeyInfo) { + sftk_FreeAttribute(attribute); + *crvp = CKR_HOST_MEMORY; + rv = SECFailure; + goto loser; + } + rv = SEC_QuickDERDecodeItem(arena, publicKeyInfo, + nsslowkey_SubjectPublicKeyInfoTemplate, + &spki); + if (rv != SECSuccess) { + sftk_FreeAttribute(attribute); + *crvp = CKR_KEY_TYPE_INCONSISTENT; + goto loser; + } + algorithm = SECOID_GetAlgorithmTag(&publicKeyInfo->algorithm); + if (algorithm != SEC_OID_PKCS1_RSA_ENCRYPTION && + algorithm != SEC_OID_PKCS1_RSA_PSS_SIGNATURE) { + sftk_FreeAttribute(attribute); + rv = SECFailure; + *crvp = CKR_KEY_TYPE_INCONSISTENT; + goto loser; + } + param = SECITEM_DupItem(&publicKeyInfo->algorithm.parameters); + if (!param) { + sftk_FreeAttribute(attribute); + rv = SECFailure; + *crvp = CKR_HOST_MEMORY; + goto loser; + } + sftk_FreeAttribute(attribute); + } else { + /* default to PKCS #1 */ + algorithm = SEC_OID_PKCS1_RSA_ENCRYPTION; + } + break; + case NSSLOWKEYDSAKey: + prepare_low_dsa_priv_key_export_for_asn1(lk); + dummy = SEC_ASN1EncodeItem(arena, &pki->privateKey, lk, + nsslowkey_DSAPrivateKeyExportTemplate); + prepare_low_pqg_params_for_asn1(&lk->u.dsa.params); + param = SEC_ASN1EncodeItem(NULL, NULL, &(lk->u.dsa.params), + nsslowkey_PQGParamsTemplate); + algorithm = SEC_OID_ANSIX9_DSA_SIGNATURE; + break; + case NSSLOWKEYECKey: + prepare_low_ec_priv_key_for_asn1(lk); + /* Public value is encoded as a bit string so adjust length + * to be in bits before ASN encoding and readjust + * immediately after. + * + * Since the SECG specification recommends not including the + * parameters as part of ECPrivateKey, we zero out the curveOID + * length before encoding and restore it later. + */ + lk->u.ec.publicValue.len <<= 3; + savelen = lk->u.ec.ecParams.curveOID.len; + lk->u.ec.ecParams.curveOID.len = 0; + dummy = SEC_ASN1EncodeItem(arena, &pki->privateKey, lk, + nsslowkey_ECPrivateKeyTemplate); + lk->u.ec.ecParams.curveOID.len = savelen; + lk->u.ec.publicValue.len >>= 3; + +#ifdef EC_DEBUG + fordebug = &pki->privateKey; + SEC_PRINT("sftk_PackagePrivateKey()", "PrivateKey", lk->keyType, + fordebug); +#endif + + param = SECITEM_DupItem(&lk->u.ec.ecParams.DEREncoding); + + algorithm = SEC_OID_ANSIX962_EC_PUBLIC_KEY; + break; + case NSSLOWKEYDHKey: + default: + dummy = NULL; + break; + } + + if (!dummy || ((lk->keyType == NSSLOWKEYDSAKey) && !param)) { + *crvp = CKR_DEVICE_ERROR; /* should map NSS SECError */ + rv = SECFailure; + goto loser; + } + + rv = SECOID_SetAlgorithmID(arena, &pki->algorithm, algorithm, + (SECItem *)param); + if (rv != SECSuccess) { + *crvp = CKR_DEVICE_ERROR; /* should map NSS SECError */ + rv = SECFailure; + goto loser; + } + + dummy = SEC_ASN1EncodeInteger(arena, &pki->version, + NSSLOWKEY_PRIVATE_KEY_INFO_VERSION); + if (!dummy) { + *crvp = CKR_DEVICE_ERROR; /* should map NSS SECError */ + rv = SECFailure; + goto loser; + } + + encodedKey = SEC_ASN1EncodeItem(NULL, NULL, pki, + nsslowkey_PrivateKeyInfoTemplate); + *crvp = encodedKey ? CKR_OK : CKR_DEVICE_ERROR; + +#ifdef EC_DEBUG + fordebug = encodedKey; + SEC_PRINT("sftk_PackagePrivateKey()", "PrivateKeyInfo", lk->keyType, + fordebug); +#endif +loser: + if (arena) { + PORT_FreeArena(arena, PR_TRUE); + } + + if (lk && (lk != key->objectInfo)) { + nsslowkey_DestroyPrivateKey(lk); + } + + if (param) { + SECITEM_ZfreeItem((SECItem *)param, PR_TRUE); + } + + if (rv != SECSuccess) { + return NULL; + } + + return encodedKey; +} + +/* it doesn't matter yet, since we colapse error conditions in the + * level above, but we really should map those few key error differences */ +static CK_RV +sftk_mapWrap(CK_RV crv) +{ + switch (crv) { + case CKR_ENCRYPTED_DATA_INVALID: + crv = CKR_WRAPPED_KEY_INVALID; + break; + } + return crv; +} + +/* NSC_WrapKey wraps (i.e., encrypts) a key. */ +CK_RV +NSC_WrapKey(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hWrappingKey, + CK_OBJECT_HANDLE hKey, CK_BYTE_PTR pWrappedKey, + CK_ULONG_PTR pulWrappedKeyLen) +{ + SFTKSession *session; + SFTKAttribute *attribute; + SFTKObject *key; + CK_RV crv; + + CHECK_FORK(); + + session = sftk_SessionFromHandle(hSession); + if (session == NULL) { + return CKR_SESSION_HANDLE_INVALID; + } + + key = sftk_ObjectFromHandle(hKey, session); + if (key == NULL) { + sftk_FreeSession(session); + return CKR_KEY_HANDLE_INVALID; + } + + switch (key->objclass) { + case CKO_SECRET_KEY: { + SFTKSessionContext *context = NULL; + SECItem pText; + + attribute = sftk_FindAttribute(key, CKA_VALUE); + + if (attribute == NULL) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + crv = sftk_CryptInit(hSession, pMechanism, hWrappingKey, + CKA_WRAP, CKA_WRAP, SFTK_ENCRYPT, PR_TRUE); + if (crv != CKR_OK) { + sftk_FreeAttribute(attribute); + break; + } + + pText.type = siBuffer; + pText.data = (unsigned char *)attribute->attrib.pValue; + pText.len = attribute->attrib.ulValueLen; + + /* Find out if this is a block cipher. */ + crv = sftk_GetContext(hSession, &context, SFTK_ENCRYPT, PR_FALSE, NULL); + if (crv != CKR_OK || !context) + break; + if (context->blockSize > 1) { + unsigned int remainder = pText.len % context->blockSize; + if (!context->doPad && remainder) { + /* When wrapping secret keys with unpadded block ciphers, + ** the keys are zero padded, if necessary, to fill out + ** a full block. + */ + pText.len += context->blockSize - remainder; + pText.data = PORT_ZAlloc(pText.len); + if (pText.data) + memcpy(pText.data, attribute->attrib.pValue, + attribute->attrib.ulValueLen); + else { + crv = CKR_HOST_MEMORY; + break; + } + } + } + + crv = NSC_Encrypt(hSession, (CK_BYTE_PTR)pText.data, + pText.len, pWrappedKey, pulWrappedKeyLen); + /* always force a finalize, both on errors and when + * we are just getting the size */ + if (crv != CKR_OK || pWrappedKey == NULL) { + CK_RV lcrv; + lcrv = sftk_GetContext(hSession, &context, + SFTK_ENCRYPT, PR_FALSE, NULL); + sftk_SetContextByType(session, SFTK_ENCRYPT, NULL); + if (lcrv == CKR_OK && context) { + sftk_FreeContext(context); + } + } + + if (pText.data != (unsigned char *)attribute->attrib.pValue) + PORT_ZFree(pText.data, pText.len); + sftk_FreeAttribute(attribute); + break; + } + + case CKO_PRIVATE_KEY: { + SECItem *bpki = sftk_PackagePrivateKey(key, &crv); + SFTKSessionContext *context = NULL; + + if (!bpki) { + break; + } + + crv = sftk_CryptInit(hSession, pMechanism, hWrappingKey, + CKA_WRAP, CKA_WRAP, SFTK_ENCRYPT, PR_TRUE); + if (crv != CKR_OK) { + SECITEM_ZfreeItem(bpki, PR_TRUE); + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + + crv = NSC_Encrypt(hSession, bpki->data, bpki->len, + pWrappedKey, pulWrappedKeyLen); + /* always force a finalize */ + if (crv != CKR_OK || pWrappedKey == NULL) { + CK_RV lcrv; + lcrv = sftk_GetContext(hSession, &context, + SFTK_ENCRYPT, PR_FALSE, NULL); + sftk_SetContextByType(session, SFTK_ENCRYPT, NULL); + if (lcrv == CKR_OK && context) { + sftk_FreeContext(context); + } + } + SECITEM_ZfreeItem(bpki, PR_TRUE); + break; + } + + default: + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + sftk_FreeObject(key); + sftk_FreeSession(session); + return sftk_mapWrap(crv); +} + +/* + * import a pprivate key info into the desired slot + */ +static SECStatus +sftk_unwrapPrivateKey(SFTKObject *key, SECItem *bpki) +{ + CK_BBOOL cktrue = CK_TRUE; + CK_KEY_TYPE keyType = CKK_RSA; + SECStatus rv = SECFailure; + const SEC_ASN1Template *keyTemplate, *paramTemplate; + void *paramDest = NULL; + PLArenaPool *arena; + NSSLOWKEYPrivateKey *lpk = NULL; + NSSLOWKEYPrivateKeyInfo *pki = NULL; + CK_RV crv = CKR_KEY_TYPE_INCONSISTENT; + + arena = PORT_NewArena(2048); + if (!arena) { + return SECFailure; + } + + pki = (NSSLOWKEYPrivateKeyInfo *)PORT_ArenaZAlloc(arena, + sizeof(NSSLOWKEYPrivateKeyInfo)); + if (!pki) { + PORT_FreeArena(arena, PR_FALSE); + return SECFailure; + } + + if (SEC_ASN1DecodeItem(arena, pki, nsslowkey_PrivateKeyInfoTemplate, bpki) != SECSuccess) { + PORT_FreeArena(arena, PR_TRUE); + return SECFailure; + } + + lpk = (NSSLOWKEYPrivateKey *)PORT_ArenaZAlloc(arena, + sizeof(NSSLOWKEYPrivateKey)); + if (lpk == NULL) { + goto loser; + } + lpk->arena = arena; + + switch (SECOID_GetAlgorithmTag(&pki->algorithm)) { + case SEC_OID_PKCS1_RSA_ENCRYPTION: + case SEC_OID_PKCS1_RSA_PSS_SIGNATURE: + keyTemplate = nsslowkey_RSAPrivateKeyTemplate; + paramTemplate = NULL; + paramDest = NULL; + lpk->keyType = NSSLOWKEYRSAKey; + prepare_low_rsa_priv_key_for_asn1(lpk); + break; + case SEC_OID_ANSIX9_DSA_SIGNATURE: + keyTemplate = nsslowkey_DSAPrivateKeyExportTemplate; + paramTemplate = nsslowkey_PQGParamsTemplate; + paramDest = &(lpk->u.dsa.params); + lpk->keyType = NSSLOWKEYDSAKey; + prepare_low_dsa_priv_key_export_for_asn1(lpk); + prepare_low_pqg_params_for_asn1(&lpk->u.dsa.params); + break; + /* case NSSLOWKEYDHKey: */ + case SEC_OID_ANSIX962_EC_PUBLIC_KEY: + keyTemplate = nsslowkey_ECPrivateKeyTemplate; + paramTemplate = NULL; + paramDest = &(lpk->u.ec.ecParams.DEREncoding); + lpk->keyType = NSSLOWKEYECKey; + prepare_low_ec_priv_key_for_asn1(lpk); + prepare_low_ecparams_for_asn1(&lpk->u.ec.ecParams); + break; + default: + keyTemplate = NULL; + paramTemplate = NULL; + paramDest = NULL; + break; + } + + if (!keyTemplate) { + goto loser; + } + + /* decode the private key and any algorithm parameters */ + rv = SEC_QuickDERDecodeItem(arena, lpk, keyTemplate, &pki->privateKey); + + if (lpk->keyType == NSSLOWKEYECKey) { + /* convert length in bits to length in bytes */ + lpk->u.ec.publicValue.len >>= 3; + rv = SECITEM_CopyItem(arena, + &(lpk->u.ec.ecParams.DEREncoding), + &(pki->algorithm.parameters)); + if (rv != SECSuccess) { + goto loser; + } + } + + if (rv != SECSuccess) { + goto loser; + } + if (paramDest && paramTemplate) { + rv = SEC_QuickDERDecodeItem(arena, paramDest, paramTemplate, + &(pki->algorithm.parameters)); + if (rv != SECSuccess) { + goto loser; + } + } + + rv = SECFailure; + + switch (lpk->keyType) { + case NSSLOWKEYRSAKey: + keyType = CKK_RSA; + if (sftk_hasAttribute(key, CKA_NSS_DB)) { + sftk_DeleteAttributeType(key, CKA_NSS_DB); + } + crv = sftk_AddAttributeType(key, CKA_KEY_TYPE, &keyType, + sizeof(keyType)); + if (crv != CKR_OK) + break; + crv = sftk_AddAttributeType(key, CKA_UNWRAP, &cktrue, + sizeof(CK_BBOOL)); + if (crv != CKR_OK) + break; + crv = sftk_AddAttributeType(key, CKA_DECRYPT, &cktrue, + sizeof(CK_BBOOL)); + if (crv != CKR_OK) + break; + crv = sftk_AddAttributeType(key, CKA_SIGN, &cktrue, + sizeof(CK_BBOOL)); + if (crv != CKR_OK) + break; + crv = sftk_AddAttributeType(key, CKA_SIGN_RECOVER, &cktrue, + sizeof(CK_BBOOL)); + if (crv != CKR_OK) + break; + crv = sftk_AddAttributeType(key, CKA_MODULUS, + sftk_item_expand(&lpk->u.rsa.modulus)); + if (crv != CKR_OK) + break; + crv = sftk_AddAttributeType(key, CKA_PUBLIC_EXPONENT, + sftk_item_expand(&lpk->u.rsa.publicExponent)); + if (crv != CKR_OK) + break; + crv = sftk_AddAttributeType(key, CKA_PRIVATE_EXPONENT, + sftk_item_expand(&lpk->u.rsa.privateExponent)); + if (crv != CKR_OK) + break; + crv = sftk_AddAttributeType(key, CKA_PRIME_1, + sftk_item_expand(&lpk->u.rsa.prime1)); + if (crv != CKR_OK) + break; + crv = sftk_AddAttributeType(key, CKA_PRIME_2, + sftk_item_expand(&lpk->u.rsa.prime2)); + if (crv != CKR_OK) + break; + crv = sftk_AddAttributeType(key, CKA_EXPONENT_1, + sftk_item_expand(&lpk->u.rsa.exponent1)); + if (crv != CKR_OK) + break; + crv = sftk_AddAttributeType(key, CKA_EXPONENT_2, + sftk_item_expand(&lpk->u.rsa.exponent2)); + if (crv != CKR_OK) + break; + crv = sftk_AddAttributeType(key, CKA_COEFFICIENT, + sftk_item_expand(&lpk->u.rsa.coefficient)); + break; + case NSSLOWKEYDSAKey: + keyType = CKK_DSA; + crv = (sftk_hasAttribute(key, CKA_NSS_DB)) ? CKR_OK : CKR_KEY_TYPE_INCONSISTENT; + if (crv != CKR_OK) + break; + crv = sftk_AddAttributeType(key, CKA_KEY_TYPE, &keyType, + sizeof(keyType)); + if (crv != CKR_OK) + break; + crv = sftk_AddAttributeType(key, CKA_SIGN, &cktrue, + sizeof(CK_BBOOL)); + if (crv != CKR_OK) + break; + crv = sftk_AddAttributeType(key, CKA_SIGN_RECOVER, &cktrue, + sizeof(CK_BBOOL)); + if (crv != CKR_OK) + break; + crv = sftk_AddAttributeType(key, CKA_PRIME, + sftk_item_expand(&lpk->u.dsa.params.prime)); + if (crv != CKR_OK) + break; + crv = sftk_AddAttributeType(key, CKA_SUBPRIME, + sftk_item_expand(&lpk->u.dsa.params.subPrime)); + if (crv != CKR_OK) + break; + crv = sftk_AddAttributeType(key, CKA_BASE, + sftk_item_expand(&lpk->u.dsa.params.base)); + if (crv != CKR_OK) + break; + crv = sftk_AddAttributeType(key, CKA_VALUE, + sftk_item_expand(&lpk->u.dsa.privateValue)); + if (crv != CKR_OK) + break; + break; +#ifdef notdef + case NSSLOWKEYDHKey: + template = dhTemplate; + templateCount = sizeof(dhTemplate) / sizeof(CK_ATTRIBUTE); + keyType = CKK_DH; + break; +#endif + /* what about fortezza??? */ + case NSSLOWKEYECKey: + keyType = CKK_EC; + crv = (sftk_hasAttribute(key, CKA_NSS_DB)) ? CKR_OK : CKR_KEY_TYPE_INCONSISTENT; + if (crv != CKR_OK) + break; + crv = sftk_AddAttributeType(key, CKA_KEY_TYPE, &keyType, + sizeof(keyType)); + if (crv != CKR_OK) + break; + crv = sftk_AddAttributeType(key, CKA_SIGN, &cktrue, + sizeof(CK_BBOOL)); + if (crv != CKR_OK) + break; + crv = sftk_AddAttributeType(key, CKA_SIGN_RECOVER, &cktrue, + sizeof(CK_BBOOL)); + if (crv != CKR_OK) + break; + crv = sftk_AddAttributeType(key, CKA_DERIVE, &cktrue, + sizeof(CK_BBOOL)); + if (crv != CKR_OK) + break; + crv = sftk_AddAttributeType(key, CKA_EC_PARAMS, + sftk_item_expand(&lpk->u.ec.ecParams.DEREncoding)); + if (crv != CKR_OK) + break; + crv = sftk_AddAttributeType(key, CKA_VALUE, + sftk_item_expand(&lpk->u.ec.privateValue)); + if (crv != CKR_OK) + break; + /* XXX Do we need to decode the EC Params here ?? */ + break; + default: + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + + if (crv != CKR_OK) { + goto loser; + } + + /* For RSA-PSS, record the original algorithm parameters so + * they can be encrypted altoghether when wrapping */ + if (SECOID_GetAlgorithmTag(&pki->algorithm) == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) { + NSSLOWKEYSubjectPublicKeyInfo spki; + NSSLOWKEYPublicKey pubk; + SECItem *publicKeyInfo; + + memset(&spki, 0, sizeof(NSSLOWKEYSubjectPublicKeyInfo)); + rv = SECOID_CopyAlgorithmID(arena, &spki.algorithm, &pki->algorithm); + if (rv != SECSuccess) { + crv = CKR_HOST_MEMORY; + goto loser; + } + + prepare_low_rsa_pub_key_for_asn1(&pubk); + + rv = SECITEM_CopyItem(arena, &pubk.u.rsa.modulus, &lpk->u.rsa.modulus); + if (rv != SECSuccess) { + crv = CKR_HOST_MEMORY; + goto loser; + } + rv = SECITEM_CopyItem(arena, &pubk.u.rsa.publicExponent, &lpk->u.rsa.publicExponent); + if (rv != SECSuccess) { + crv = CKR_HOST_MEMORY; + goto loser; + } + + if (SEC_ASN1EncodeItem(arena, &spki.subjectPublicKey, + &pubk, nsslowkey_RSAPublicKeyTemplate) == NULL) { + crv = CKR_HOST_MEMORY; + goto loser; + } + + publicKeyInfo = SEC_ASN1EncodeItem(arena, NULL, + &spki, nsslowkey_SubjectPublicKeyInfoTemplate); + if (!publicKeyInfo) { + crv = CKR_HOST_MEMORY; + goto loser; + } + crv = sftk_AddAttributeType(key, CKA_PUBLIC_KEY_INFO, + sftk_item_expand(publicKeyInfo)); + } + +loser: + if (lpk) { + nsslowkey_DestroyPrivateKey(lpk); + } + + if (crv != CKR_OK) { + return SECFailure; + } + + return SECSuccess; +} + +/* NSC_UnwrapKey unwraps (decrypts) a wrapped key, creating a new key object. */ +CK_RV +NSC_UnwrapKey(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hUnwrappingKey, + CK_BYTE_PTR pWrappedKey, CK_ULONG ulWrappedKeyLen, + CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, + CK_OBJECT_HANDLE_PTR phKey) +{ + SFTKObject *key = NULL; + SFTKSession *session; + CK_ULONG key_length = 0; + unsigned char *buf = NULL; + CK_RV crv = CKR_OK; + int i; + CK_ULONG bsize = ulWrappedKeyLen; + SFTKSlot *slot = sftk_SlotFromSessionHandle(hSession); + SECItem bpki; + CK_OBJECT_CLASS target_type = CKO_SECRET_KEY; + + CHECK_FORK(); + + if (!slot) { + return CKR_SESSION_HANDLE_INVALID; + } + /* + * now lets create an object to hang the attributes off of + */ + key = sftk_NewObject(slot); /* fill in the handle later */ + if (key == NULL) { + return CKR_HOST_MEMORY; + } + + /* + * load the template values into the object + */ + for (i = 0; i < (int)ulAttributeCount; i++) { + if (pTemplate[i].type == CKA_VALUE_LEN) { + key_length = *(CK_ULONG *)pTemplate[i].pValue; + continue; + } + if (pTemplate[i].type == CKA_CLASS) { + target_type = *(CK_OBJECT_CLASS *)pTemplate[i].pValue; + } + crv = sftk_AddAttributeType(key, sftk_attr_expand(&pTemplate[i])); + if (crv != CKR_OK) + break; + } + if (crv != CKR_OK) { + sftk_FreeObject(key); + return crv; + } + + crv = sftk_CryptInit(hSession, pMechanism, hUnwrappingKey, CKA_UNWRAP, + CKA_UNWRAP, SFTK_DECRYPT, PR_FALSE); + if (crv != CKR_OK) { + sftk_FreeObject(key); + return sftk_mapWrap(crv); + } + + /* allocate the buffer to decrypt into + * this assumes the unwrapped key is never larger than the + * wrapped key. For all the mechanisms we support this is true */ + buf = (unsigned char *)PORT_Alloc(ulWrappedKeyLen); + bsize = ulWrappedKeyLen; + + crv = NSC_Decrypt(hSession, pWrappedKey, ulWrappedKeyLen, buf, &bsize); + if (crv != CKR_OK) { + sftk_FreeObject(key); + PORT_Free(buf); + return sftk_mapWrap(crv); + } + + switch (target_type) { + case CKO_SECRET_KEY: + if (!sftk_hasAttribute(key, CKA_KEY_TYPE)) { + crv = CKR_TEMPLATE_INCOMPLETE; + break; + } + + if (key_length == 0 || key_length > bsize) { + key_length = bsize; + } + if (key_length > MAX_KEY_LEN) { + crv = CKR_TEMPLATE_INCONSISTENT; + break; + } + + /* add the value */ + crv = sftk_AddAttributeType(key, CKA_VALUE, buf, key_length); + break; + case CKO_PRIVATE_KEY: + bpki.data = (unsigned char *)buf; + bpki.len = bsize; + crv = CKR_OK; + if (sftk_unwrapPrivateKey(key, &bpki) != SECSuccess) { + crv = CKR_TEMPLATE_INCOMPLETE; + } + break; + default: + crv = CKR_TEMPLATE_INCONSISTENT; + break; + } + + PORT_ZFree(buf, bsize); + if (crv != CKR_OK) { + sftk_FreeObject(key); + return crv; + } + + /* get the session */ + session = sftk_SessionFromHandle(hSession); + if (session == NULL) { + sftk_FreeObject(key); + return CKR_SESSION_HANDLE_INVALID; + } + + /* mark the key as FIPS if the previous operation was all FIPS */ + key->isFIPS = session->lastOpWasFIPS; + + /* + * handle the base object stuff + */ + crv = sftk_handleObject(key, session); + *phKey = key->handle; + sftk_FreeSession(session); + sftk_FreeObject(key); + + return crv; +} + +/* + * The SSL key gen mechanism create's lots of keys. This function handles the + * details of each of these key creation. + */ +static CK_RV +sftk_buildSSLKey(CK_SESSION_HANDLE hSession, SFTKObject *baseKey, + PRBool isMacKey, unsigned char *keyBlock, unsigned int keySize, + CK_OBJECT_HANDLE *keyHandle) +{ + SFTKObject *key; + SFTKSession *session; + CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; + CK_BBOOL cktrue = CK_TRUE; + CK_BBOOL ckfalse = CK_FALSE; + CK_RV crv = CKR_HOST_MEMORY; + + /* + * now lets create an object to hang the attributes off of + */ + *keyHandle = CK_INVALID_HANDLE; + key = sftk_NewObject(baseKey->slot); + if (key == NULL) + return CKR_HOST_MEMORY; + sftk_narrowToSessionObject(key)->wasDerived = PR_TRUE; + + crv = sftk_CopyObject(key, baseKey); + if (crv != CKR_OK) + goto loser; + if (isMacKey) { + crv = sftk_forceAttribute(key, CKA_KEY_TYPE, &keyType, sizeof(keyType)); + if (crv != CKR_OK) + goto loser; + crv = sftk_forceAttribute(key, CKA_DERIVE, &cktrue, sizeof(CK_BBOOL)); + if (crv != CKR_OK) + goto loser; + crv = sftk_forceAttribute(key, CKA_ENCRYPT, &ckfalse, sizeof(CK_BBOOL)); + if (crv != CKR_OK) + goto loser; + crv = sftk_forceAttribute(key, CKA_DECRYPT, &ckfalse, sizeof(CK_BBOOL)); + if (crv != CKR_OK) + goto loser; + crv = sftk_forceAttribute(key, CKA_SIGN, &cktrue, sizeof(CK_BBOOL)); + if (crv != CKR_OK) + goto loser; + crv = sftk_forceAttribute(key, CKA_VERIFY, &cktrue, sizeof(CK_BBOOL)); + if (crv != CKR_OK) + goto loser; + crv = sftk_forceAttribute(key, CKA_WRAP, &ckfalse, sizeof(CK_BBOOL)); + if (crv != CKR_OK) + goto loser; + crv = sftk_forceAttribute(key, CKA_UNWRAP, &ckfalse, sizeof(CK_BBOOL)); + if (crv != CKR_OK) + goto loser; + } + crv = sftk_forceAttribute(key, CKA_VALUE, keyBlock, keySize); + if (crv != CKR_OK) + goto loser; + + /* get the session */ + crv = CKR_HOST_MEMORY; + session = sftk_SessionFromHandle(hSession); + if (session == NULL) { + goto loser; + } + + crv = sftk_handleObject(key, session); + sftk_FreeSession(session); + *keyHandle = key->handle; +loser: + if (key) + sftk_FreeObject(key); + return crv; +} + +/* + * if there is an error, we need to free the keys we already created in SSL + * This is the routine that will do it.. + */ +static void +sftk_freeSSLKeys(CK_SESSION_HANDLE session, + CK_SSL3_KEY_MAT_OUT *returnedMaterial) +{ + if (returnedMaterial->hClientMacSecret != CK_INVALID_HANDLE) { + NSC_DestroyObject(session, returnedMaterial->hClientMacSecret); + } + if (returnedMaterial->hServerMacSecret != CK_INVALID_HANDLE) { + NSC_DestroyObject(session, returnedMaterial->hServerMacSecret); + } + if (returnedMaterial->hClientKey != CK_INVALID_HANDLE) { + NSC_DestroyObject(session, returnedMaterial->hClientKey); + } + if (returnedMaterial->hServerKey != CK_INVALID_HANDLE) { + NSC_DestroyObject(session, returnedMaterial->hServerKey); + } +} + +/* + * when deriving from sensitive and extractable keys, we need to preserve some + * of the semantics in the derived key. This helper routine maintains these + * semantics. + */ +static CK_RV +sftk_DeriveSensitiveCheck(SFTKObject *baseKey, SFTKObject *destKey, + PRBool canBeData) +{ + PRBool hasSensitive; + PRBool sensitive = PR_FALSE; + CK_BBOOL bFalse = CK_FALSE; + PRBool hasExtractable; + PRBool extractable = PR_TRUE; + CK_BBOOL bTrue = CK_TRUE; + CK_RV crv = CKR_OK; + SFTKAttribute *att; + PRBool isData = PR_TRUE; + + if (canBeData) { + CK_OBJECT_CLASS objClass; + + /* if the target key is actually data, don't set the unexpected + * attributes */ + crv = sftk_GetULongAttribute(destKey, CKA_CLASS, &objClass); + if (crv != CKR_OK) { + return crv; + } + if (objClass == CKO_DATA) { + return CKR_OK; + } + + /* if the base key is data, it doesn't have sensitive attributes, + * allow the destKey to get it's own */ + crv = sftk_GetULongAttribute(baseKey, CKA_CLASS, &objClass); + if (crv != CKR_OK) { + return crv; + } + if (objClass == CKO_DATA) { + isData = PR_TRUE; + } + } + + hasSensitive = PR_FALSE; + att = sftk_FindAttribute(destKey, CKA_SENSITIVE); + if (att) { + hasSensitive = PR_TRUE; + sensitive = (PRBool) * (CK_BBOOL *)att->attrib.pValue; + sftk_FreeAttribute(att); + } + + hasExtractable = PR_FALSE; + att = sftk_FindAttribute(destKey, CKA_EXTRACTABLE); + if (att) { + hasExtractable = PR_TRUE; + extractable = (PRBool) * (CK_BBOOL *)att->attrib.pValue; + sftk_FreeAttribute(att); + } + + /* don't make a key more accessible */ + if (sftk_isTrue(baseKey, CKA_SENSITIVE) && hasSensitive && + (sensitive == PR_FALSE)) { + return CKR_KEY_FUNCTION_NOT_PERMITTED; + } + if (!sftk_isTrue(baseKey, CKA_EXTRACTABLE) && hasExtractable && + (extractable == PR_TRUE)) { + return CKR_KEY_FUNCTION_NOT_PERMITTED; + } + + /* inherit parent's sensitivity */ + if (!hasSensitive) { + att = sftk_FindAttribute(baseKey, CKA_SENSITIVE); + if (att != NULL) { + crv = sftk_defaultAttribute(destKey, + sftk_attr_expand(&att->attrib)); + sftk_FreeAttribute(att); + } else if (isData) { + crv = sftk_defaultAttribute(destKey, CKA_SENSITIVE, + &bFalse, sizeof(bFalse)); + } else { + return CKR_KEY_TYPE_INCONSISTENT; + } + if (crv != CKR_OK) + return crv; + } + if (!hasExtractable) { + att = sftk_FindAttribute(baseKey, CKA_EXTRACTABLE); + if (att != NULL) { + crv = sftk_defaultAttribute(destKey, + sftk_attr_expand(&att->attrib)); + sftk_FreeAttribute(att); + } else if (isData) { + crv = sftk_defaultAttribute(destKey, CKA_EXTRACTABLE, + &bTrue, sizeof(bTrue)); + } else { + return CKR_KEY_TYPE_INCONSISTENT; + } + if (crv != CKR_OK) + return crv; + } + + /* we should inherit the parent's always extractable/ never sensitive info, + * but handleObject always forces this attributes, so we would need to do + * something special. */ + return CKR_OK; +} + +/* + * make known fixed PKCS #11 key types to their sizes in bytes + */ +unsigned long +sftk_MapKeySize(CK_KEY_TYPE keyType) +{ + switch (keyType) { + case CKK_CDMF: + return 8; + case CKK_DES: + return 8; + case CKK_DES2: + return 16; + case CKK_DES3: + return 24; + /* IDEA and CAST need to be added */ + default: + break; + } + return 0; +} + +/* Inputs: + * key_len: Length of derived key to be generated. + * SharedSecret: a shared secret that is the output of a key agreement primitive. + * SharedInfo: (Optional) some data shared by the entities computing the secret key. + * SharedInfoLen: the length in octets of SharedInfo + * Hash: The hash function to be used in the KDF + * HashLen: the length in octets of the output of Hash + * Output: + * key: Pointer to a buffer containing derived key, if return value is SECSuccess. + */ +static CK_RV +sftk_compute_ANSI_X9_63_kdf(CK_BYTE **key, CK_ULONG key_len, SECItem *SharedSecret, + CK_BYTE_PTR SharedInfo, CK_ULONG SharedInfoLen, + SECStatus Hash(unsigned char *, const unsigned char *, PRUint32), + CK_ULONG HashLen) +{ + unsigned char *buffer = NULL, *output_buffer = NULL; + PRUint32 buffer_len, max_counter, i; + SECStatus rv; + CK_RV crv; + + /* Check that key_len isn't too long. The maximum key length could be + * greatly increased if the code below did not limit the 4-byte counter + * to a maximum value of 255. */ + if (key_len > 254 * HashLen) + return CKR_ARGUMENTS_BAD; + + if (SharedInfo == NULL) + SharedInfoLen = 0; + + buffer_len = SharedSecret->len + 4 + SharedInfoLen; + buffer = (CK_BYTE *)PORT_Alloc(buffer_len); + if (buffer == NULL) { + crv = CKR_HOST_MEMORY; + goto loser; + } + + max_counter = key_len / HashLen; + if (key_len > max_counter * HashLen) + max_counter++; + + output_buffer = (CK_BYTE *)PORT_Alloc(max_counter * HashLen); + if (output_buffer == NULL) { + crv = CKR_HOST_MEMORY; + goto loser; + } + + /* Populate buffer with SharedSecret || Counter || [SharedInfo] + * where Counter is 0x00000001 */ + PORT_Memcpy(buffer, SharedSecret->data, SharedSecret->len); + buffer[SharedSecret->len] = 0; + buffer[SharedSecret->len + 1] = 0; + buffer[SharedSecret->len + 2] = 0; + buffer[SharedSecret->len + 3] = 1; + if (SharedInfo) { + PORT_Memcpy(&buffer[SharedSecret->len + 4], SharedInfo, SharedInfoLen); + } + + for (i = 0; i < max_counter; i++) { + rv = Hash(&output_buffer[i * HashLen], buffer, buffer_len); + if (rv != SECSuccess) { + /* 'Hash' should not fail. */ + crv = CKR_FUNCTION_FAILED; + goto loser; + } + + /* Increment counter (assumes max_counter < 255) */ + buffer[SharedSecret->len + 3]++; + } + + PORT_ZFree(buffer, buffer_len); + if (key_len < max_counter * HashLen) { + PORT_Memset(output_buffer + key_len, 0, max_counter * HashLen - key_len); + } + *key = output_buffer; + + return CKR_OK; + +loser: + if (buffer) { + PORT_ZFree(buffer, buffer_len); + } + if (output_buffer) { + PORT_ZFree(output_buffer, max_counter * HashLen); + } + return crv; +} + +static CK_RV +sftk_ANSI_X9_63_kdf(CK_BYTE **key, CK_ULONG key_len, + SECItem *SharedSecret, + CK_BYTE_PTR SharedInfo, CK_ULONG SharedInfoLen, + CK_EC_KDF_TYPE kdf) +{ + if (kdf == CKD_SHA1_KDF) + return sftk_compute_ANSI_X9_63_kdf(key, key_len, SharedSecret, SharedInfo, + SharedInfoLen, SHA1_HashBuf, SHA1_LENGTH); + else if (kdf == CKD_SHA224_KDF) + return sftk_compute_ANSI_X9_63_kdf(key, key_len, SharedSecret, SharedInfo, + SharedInfoLen, SHA224_HashBuf, SHA224_LENGTH); + else if (kdf == CKD_SHA256_KDF) + return sftk_compute_ANSI_X9_63_kdf(key, key_len, SharedSecret, SharedInfo, + SharedInfoLen, SHA256_HashBuf, SHA256_LENGTH); + else if (kdf == CKD_SHA384_KDF) + return sftk_compute_ANSI_X9_63_kdf(key, key_len, SharedSecret, SharedInfo, + SharedInfoLen, SHA384_HashBuf, SHA384_LENGTH); + else if (kdf == CKD_SHA512_KDF) + return sftk_compute_ANSI_X9_63_kdf(key, key_len, SharedSecret, SharedInfo, + SharedInfoLen, SHA512_HashBuf, SHA512_LENGTH); + else + return CKR_MECHANISM_INVALID; +} + +/* + * Handle the derive from a block encryption cipher + */ +CK_RV +sftk_DeriveEncrypt(SFTKCipher encrypt, void *cipherInfo, + int blockSize, SFTKObject *key, CK_ULONG keySize, + unsigned char *data, CK_ULONG len) +{ + /* large enough for a 512-bit key */ + unsigned char tmpdata[SFTK_MAX_DERIVE_KEY_SIZE]; + SECStatus rv; + unsigned int outLen; + CK_RV crv; + + if ((len % blockSize) != 0) { + return CKR_MECHANISM_PARAM_INVALID; + } + if (len > SFTK_MAX_DERIVE_KEY_SIZE) { + return CKR_MECHANISM_PARAM_INVALID; + } + if (keySize && (len < keySize)) { + return CKR_MECHANISM_PARAM_INVALID; + } + if (keySize == 0) { + keySize = len; + } + + rv = (*encrypt)(cipherInfo, &tmpdata, &outLen, len, data, len); + if (rv != SECSuccess) { + crv = sftk_MapCryptError(PORT_GetError()); + return crv; + } + + crv = sftk_forceAttribute(key, CKA_VALUE, tmpdata, keySize); + PORT_Memset(tmpdata, 0, sizeof tmpdata); + return crv; +} + +CK_RV +sftk_HKDF(CK_HKDF_PARAMS_PTR params, CK_SESSION_HANDLE hSession, + SFTKObject *sourceKey, const unsigned char *sourceKeyBytes, + int sourceKeyLen, SFTKObject *key, unsigned char *outKeyBytes, + int keySize, PRBool canBeData, PRBool isFIPS) +{ + SFTKSession *session; + SFTKAttribute *saltKey_att = NULL; + const SECHashObject *rawHash; + unsigned hashLen; + unsigned genLen = 0; + unsigned char hashbuf[HASH_LENGTH_MAX]; + unsigned char keyBlock[9 * SFTK_MAX_MAC_LENGTH]; + unsigned char *keyBlockAlloc = NULL; /* allocated keyBlock */ + unsigned char *keyBlockData = keyBlock; /* pointer to current keyBlock */ + const unsigned char *prk; /* psuedo-random key */ + CK_ULONG prkLen; + const unsigned char *okm; /* output keying material */ + HASH_HashType hashType = sftk_GetHashTypeFromMechanism(params->prfHashMechanism); + SFTKObject *saltKey = NULL; + CK_RV crv = CKR_OK; + + /* Spec says it should be the base hash, but also accept the HMAC */ + if (hashType == HASH_AlgNULL) { + hashType = sftk_HMACMechanismToHash(params->prfHashMechanism); + } + rawHash = HASH_GetRawHashObject(hashType); + if (rawHash == NULL || rawHash->length > sizeof(hashbuf)) { + return CKR_MECHANISM_INVALID; + } + hashLen = rawHash->length; + + if ((!params->bExpand && !params->bExtract) || + (params->bExtract && params->ulSaltLen > 0 && !params->pSalt) || + (params->bExpand && params->ulInfoLen > 0 && !params->pInfo)) { + return CKR_MECHANISM_PARAM_INVALID; + } + if ((params->bExpand && keySize == 0) || + (!params->bExpand && keySize > hashLen) || + (params->bExpand && keySize > 255 * hashLen)) { + return CKR_TEMPLATE_INCONSISTENT; + } + + /* sourceKey is NULL if we are called from the POST, skip the + * sensitiveCheck */ + if (sourceKey != NULL) { + crv = sftk_DeriveSensitiveCheck(sourceKey, key, canBeData); + if (crv != CKR_OK) + return crv; + } + + /* HKDF-Extract(salt, base key value) */ + if (params->bExtract) { + CK_BYTE *salt; + CK_ULONG saltLen; + HMACContext *hmac; + unsigned int bufLen; + + switch (params->ulSaltType) { + case CKF_HKDF_SALT_NULL: + saltLen = hashLen; + salt = hashbuf; + memset(salt, 0, saltLen); + break; + case CKF_HKDF_SALT_DATA: + salt = params->pSalt; + saltLen = params->ulSaltLen; + if ((salt == NULL) || (params->ulSaltLen == 0)) { + return CKR_MECHANISM_PARAM_INVALID; + } + break; + case CKF_HKDF_SALT_KEY: + /* lookup key */ + session = sftk_SessionFromHandle(hSession); + if (session == NULL) { + return CKR_SESSION_HANDLE_INVALID; + } + + saltKey = sftk_ObjectFromHandle(params->hSaltKey, session); + sftk_FreeSession(session); + if (saltKey == NULL) { + return CKR_KEY_HANDLE_INVALID; + } + /* if the base key is not fips, but the salt key is, the + * resulting key can be fips */ + if (isFIPS && (key->isFIPS == 0) && (saltKey->isFIPS == 1)) { + CK_MECHANISM mech; + mech.mechanism = CKM_HKDF_DERIVE; + mech.pParameter = params; + mech.ulParameterLen = sizeof(*params); + key->isFIPS = sftk_operationIsFIPS(saltKey->slot, &mech, + CKA_DERIVE, saltKey); + } + saltKey_att = sftk_FindAttribute(saltKey, CKA_VALUE); + if (saltKey_att == NULL) { + sftk_FreeObject(saltKey); + return CKR_KEY_HANDLE_INVALID; + } + /* save the resulting salt */ + salt = saltKey_att->attrib.pValue; + saltLen = saltKey_att->attrib.ulValueLen; + break; + default: + return CKR_MECHANISM_PARAM_INVALID; + break; + } + + hmac = HMAC_Create(rawHash, salt, saltLen, isFIPS); + if (saltKey_att) { + sftk_FreeAttribute(saltKey_att); + } + if (saltKey) { + sftk_FreeObject(saltKey); + } + if (!hmac) { + return CKR_HOST_MEMORY; + } + HMAC_Begin(hmac); + HMAC_Update(hmac, sourceKeyBytes, sourceKeyLen); + HMAC_Finish(hmac, hashbuf, &bufLen, sizeof(hashbuf)); + HMAC_Destroy(hmac, PR_TRUE); + PORT_Assert(bufLen == rawHash->length); + prk = hashbuf; + prkLen = bufLen; + } else { + /* PRK = base key value */ + prk = sourceKeyBytes; + prkLen = sourceKeyLen; + } + + /* HKDF-Expand */ + if (!params->bExpand) { + okm = prk; + keySize = genLen = hashLen; + } else { + /* T(1) = HMAC-Hash(prk, "" | info | 0x01) + * T(n) = HMAC-Hash(prk, T(n-1) | info | n + * key material = T(1) | ... | T(n) + */ + HMACContext *hmac; + CK_BYTE bi; + unsigned iterations; + + genLen = PR_ROUNDUP(keySize, hashLen); + iterations = genLen / hashLen; + + if (genLen > sizeof(keyBlock)) { + keyBlockAlloc = PORT_Alloc(genLen); + if (keyBlockAlloc == NULL) { + return CKR_HOST_MEMORY; + } + keyBlockData = keyBlockAlloc; + } + hmac = HMAC_Create(rawHash, prk, prkLen, isFIPS); + if (hmac == NULL) { + PORT_Free(keyBlockAlloc); + return CKR_HOST_MEMORY; + } + for (bi = 1; bi <= iterations && bi > 0; ++bi) { + unsigned len; + HMAC_Begin(hmac); + if (bi > 1) { + HMAC_Update(hmac, &keyBlockData[(bi - 2) * hashLen], hashLen); + } + if (params->ulInfoLen != 0) { + HMAC_Update(hmac, params->pInfo, params->ulInfoLen); + } + HMAC_Update(hmac, &bi, 1); + HMAC_Finish(hmac, &keyBlockData[(bi - 1) * hashLen], &len, + hashLen); + PORT_Assert(len == hashLen); + } + HMAC_Destroy(hmac, PR_TRUE); + okm = &keyBlockData[0]; + } + /* key material = okm */ + crv = CKR_OK; + if (key) { + crv = sftk_forceAttribute(key, CKA_VALUE, okm, keySize); + } else { + PORT_Assert(outKeyBytes != NULL); + PORT_Memcpy(outKeyBytes, okm, keySize); + } + PORT_Memset(keyBlockData, 0, genLen); + PORT_Memset(hashbuf, 0, sizeof(hashbuf)); + PORT_Free(keyBlockAlloc); + return crv; +} + +/* + * SSL Key generation given pre master secret + */ +#define NUM_MIXERS 9 +static const char *const mixers[NUM_MIXERS] = { + "A", + "BB", + "CCC", + "DDDD", + "EEEEE", + "FFFFFF", + "GGGGGGG", + "HHHHHHHH", + "IIIIIIIII" +}; +#define SSL3_PMS_LENGTH 48 +#define SSL3_MASTER_SECRET_LENGTH 48 +#define SSL3_RANDOM_LENGTH 32 + +/* NSC_DeriveKey derives a key from a base key, creating a new key object. */ +CK_RV +NSC_DeriveKey(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hBaseKey, + CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, + CK_OBJECT_HANDLE_PTR phKey) +{ + SFTKSession *session; + SFTKSlot *slot = sftk_SlotFromSessionHandle(hSession); + SFTKObject *key; + SFTKObject *sourceKey; + SFTKAttribute *att = NULL; + SFTKAttribute *att2 = NULL; + unsigned char *buf; + SHA1Context *sha; + MD5Context *md5; + MD2Context *md2; + CK_ULONG macSize; + CK_ULONG tmpKeySize; + CK_ULONG IVSize; + CK_ULONG keySize = 0; + CK_RV crv = CKR_OK; + CK_BBOOL cktrue = CK_TRUE; + CK_BBOOL ckfalse = CK_FALSE; + CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; + CK_OBJECT_CLASS classType = CKO_SECRET_KEY; + CK_KEY_DERIVATION_STRING_DATA *stringPtr; + PRBool isTLS = PR_FALSE; + PRBool isDH = PR_FALSE; + HASH_HashType tlsPrfHash = HASH_AlgNULL; + SECStatus rv; + int i; + unsigned int outLen; + unsigned char sha_out[SHA1_LENGTH]; + unsigned char key_block[NUM_MIXERS * SFTK_MAX_MAC_LENGTH]; + PRBool isFIPS; + HASH_HashType hashType; + CK_MECHANISM_TYPE hashMech; + PRBool extractValue = PR_TRUE; + CK_NSS_IKE1_APP_B_PRF_DERIVE_PARAMS ikeAppB; + CK_NSS_IKE1_APP_B_PRF_DERIVE_PARAMS *pIkeAppB; + + CHECK_FORK(); + + if (!slot) { + return CKR_SESSION_HANDLE_INVALID; + } + if (!pMechanism) { + return CKR_MECHANISM_PARAM_INVALID; + } + CK_MECHANISM_TYPE mechanism = pMechanism->mechanism; + + /* + * now lets create an object to hang the attributes off of + */ + if (phKey) + *phKey = CK_INVALID_HANDLE; + + key = sftk_NewObject(slot); /* fill in the handle later */ + if (key == NULL) { + return CKR_HOST_MEMORY; + } + isFIPS = sftk_isFIPS(slot->slotID); + + /* + * load the template values into the object + */ + for (i = 0; i < (int)ulAttributeCount; i++) { + crv = sftk_AddAttributeType(key, sftk_attr_expand(&pTemplate[i])); + if (crv != CKR_OK) + break; + + if (pTemplate[i].type == CKA_KEY_TYPE) { + keyType = *(CK_KEY_TYPE *)pTemplate[i].pValue; + } + if (pTemplate[i].type == CKA_VALUE_LEN) { + keySize = *(CK_ULONG *)pTemplate[i].pValue; + } + } + if (crv != CKR_OK) { + sftk_FreeObject(key); + return crv; + } + + if (keySize == 0) { + keySize = sftk_MapKeySize(keyType); + } + + switch (mechanism) { + case CKM_NSS_JPAKE_ROUND2_SHA1: /* fall through */ + case CKM_NSS_JPAKE_ROUND2_SHA256: /* fall through */ + case CKM_NSS_JPAKE_ROUND2_SHA384: /* fall through */ + case CKM_NSS_JPAKE_ROUND2_SHA512: + extractValue = PR_FALSE; + classType = CKO_PRIVATE_KEY; + break; + case CKM_NSS_PUB_FROM_PRIV: + extractValue = PR_FALSE; + classType = CKO_PUBLIC_KEY; + break; + case CKM_HKDF_DATA: /* fall through */ + case CKM_NSS_SP800_108_COUNTER_KDF_DERIVE_DATA: /* fall through */ + case CKM_NSS_SP800_108_FEEDBACK_KDF_DERIVE_DATA: /* fall through */ + case CKM_NSS_SP800_108_DOUBLE_PIPELINE_KDF_DERIVE_DATA: + classType = CKO_DATA; + break; + case CKM_NSS_JPAKE_FINAL_SHA1: /* fall through */ + case CKM_NSS_JPAKE_FINAL_SHA256: /* fall through */ + case CKM_NSS_JPAKE_FINAL_SHA384: /* fall through */ + case CKM_NSS_JPAKE_FINAL_SHA512: + extractValue = PR_FALSE; + /* fall through */ + default: + classType = CKO_SECRET_KEY; + } + + crv = sftk_forceAttribute(key, CKA_CLASS, &classType, sizeof(classType)); + if (crv != CKR_OK) { + sftk_FreeObject(key); + return crv; + } + + /* look up the base key we're deriving with */ + session = sftk_SessionFromHandle(hSession); + if (session == NULL) { + sftk_FreeObject(key); + return CKR_SESSION_HANDLE_INVALID; + } + + sourceKey = sftk_ObjectFromHandle(hBaseKey, session); + sftk_FreeSession(session); + /* is this eventually succeeds, lastOpWasFIPS will be set the resulting key's + * FIPS state below. */ + session->lastOpWasFIPS = PR_FALSE; + if (sourceKey == NULL) { + sftk_FreeObject(key); + return CKR_KEY_HANDLE_INVALID; + } + + if (extractValue) { + /* get the value of the base key */ + att = sftk_FindAttribute(sourceKey, CKA_VALUE); + if (att == NULL) { + sftk_FreeObject(key); + sftk_FreeObject(sourceKey); + return CKR_KEY_HANDLE_INVALID; + } + } + key->isFIPS = sftk_operationIsFIPS(slot, pMechanism, CKA_DERIVE, sourceKey); + + switch (mechanism) { + /* get a public key from a private key. nsslowkey_ConvertToPublickey() + * will generate the public portion if it doesn't already exist. */ + case CKM_NSS_PUB_FROM_PRIV: { + NSSLOWKEYPrivateKey *privKey; + NSSLOWKEYPublicKey *pubKey; + int error; + + crv = sftk_GetULongAttribute(sourceKey, CKA_KEY_TYPE, &keyType); + if (crv != CKR_OK) { + break; + } + + /* privKey is stored in sourceKey and will be destroyed when + * the sourceKey is freed. */ + privKey = sftk_GetPrivKey(sourceKey, keyType, &crv); + if (privKey == NULL) { + break; + } + pubKey = nsslowkey_ConvertToPublicKey(privKey); + if (pubKey == NULL) { + error = PORT_GetError(); + crv = sftk_MapCryptError(error); + break; + } + crv = sftk_PutPubKey(key, sourceKey, keyType, pubKey); + nsslowkey_DestroyPublicKey(pubKey); + break; + } + case CKM_NSS_IKE_PRF_DERIVE: + if (pMechanism->ulParameterLen != + sizeof(CK_NSS_IKE_PRF_DERIVE_PARAMS)) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + crv = sftk_ike_prf(hSession, att, + (CK_NSS_IKE_PRF_DERIVE_PARAMS *)pMechanism->pParameter, key); + break; + case CKM_NSS_IKE1_PRF_DERIVE: + if (pMechanism->ulParameterLen != + sizeof(CK_NSS_IKE1_PRF_DERIVE_PARAMS)) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + crv = sftk_ike1_prf(hSession, att, + (CK_NSS_IKE1_PRF_DERIVE_PARAMS *)pMechanism->pParameter, + key, keySize); + break; + case CKM_NSS_IKE1_APP_B_PRF_DERIVE: + pIkeAppB = (CK_NSS_IKE1_APP_B_PRF_DERIVE_PARAMS *)pMechanism->pParameter; + if (pMechanism->ulParameterLen == + sizeof(CK_MECHANISM_TYPE)) { + ikeAppB.prfMechanism = *(CK_MECHANISM_TYPE *)pMechanism->pParameter; + ikeAppB.bHasKeygxy = PR_FALSE; + ikeAppB.hKeygxy = CK_INVALID_HANDLE; + ikeAppB.pExtraData = NULL; + ikeAppB.ulExtraDataLen = 0; + pIkeAppB = &ikeAppB; + } else if (pMechanism->ulParameterLen != + sizeof(CK_NSS_IKE1_APP_B_PRF_DERIVE_PARAMS)) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + crv = sftk_ike1_appendix_b_prf(hSession, att, pIkeAppB, key, + keySize); + break; + case CKM_NSS_IKE_PRF_PLUS_DERIVE: + if (pMechanism->ulParameterLen != + sizeof(CK_NSS_IKE_PRF_PLUS_DERIVE_PARAMS)) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + crv = sftk_ike_prf_plus(hSession, att, + (CK_NSS_IKE_PRF_PLUS_DERIVE_PARAMS *)pMechanism->pParameter, + key, keySize); + break; + /* + * generate the master secret + */ + case CKM_TLS12_MASTER_KEY_DERIVE: + case CKM_TLS12_MASTER_KEY_DERIVE_DH: + case CKM_NSS_TLS_MASTER_KEY_DERIVE_SHA256: + case CKM_NSS_TLS_MASTER_KEY_DERIVE_DH_SHA256: + case CKM_TLS_MASTER_KEY_DERIVE: + case CKM_TLS_MASTER_KEY_DERIVE_DH: + case CKM_SSL3_MASTER_KEY_DERIVE: + case CKM_SSL3_MASTER_KEY_DERIVE_DH: { + CK_SSL3_MASTER_KEY_DERIVE_PARAMS *ssl3_master; + SSL3RSAPreMasterSecret *rsa_pms; + unsigned char crsrdata[SSL3_RANDOM_LENGTH * 2]; + + if ((mechanism == CKM_TLS12_MASTER_KEY_DERIVE) || + (mechanism == CKM_TLS12_MASTER_KEY_DERIVE_DH)) { + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_TLS12_MASTER_KEY_DERIVE_PARAMS))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + CK_TLS12_MASTER_KEY_DERIVE_PARAMS *tls12_master = + (CK_TLS12_MASTER_KEY_DERIVE_PARAMS *)pMechanism->pParameter; + tlsPrfHash = sftk_GetHashTypeFromMechanism(tls12_master->prfHashMechanism); + if (tlsPrfHash == HASH_AlgNULL) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + } else if ((mechanism == CKM_NSS_TLS_MASTER_KEY_DERIVE_SHA256) || + (mechanism == CKM_NSS_TLS_MASTER_KEY_DERIVE_DH_SHA256)) { + tlsPrfHash = HASH_AlgSHA256; + } + + if ((mechanism != CKM_SSL3_MASTER_KEY_DERIVE) && + (mechanism != CKM_SSL3_MASTER_KEY_DERIVE_DH)) { + isTLS = PR_TRUE; + } + if ((mechanism == CKM_SSL3_MASTER_KEY_DERIVE_DH) || + (mechanism == CKM_TLS_MASTER_KEY_DERIVE_DH) || + (mechanism == CKM_NSS_TLS_MASTER_KEY_DERIVE_DH_SHA256) || + (mechanism == CKM_TLS12_MASTER_KEY_DERIVE_DH)) { + isDH = PR_TRUE; + } + + /* first do the consistency checks */ + if (!isDH && (att->attrib.ulValueLen != SSL3_PMS_LENGTH)) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + att2 = sftk_FindAttribute(sourceKey, CKA_KEY_TYPE); + if ((att2 == NULL) || (*(CK_KEY_TYPE *)att2->attrib.pValue != + CKK_GENERIC_SECRET)) { + if (att2) + sftk_FreeAttribute(att2); + crv = CKR_KEY_FUNCTION_NOT_PERMITTED; + break; + } + sftk_FreeAttribute(att2); + if (keyType != CKK_GENERIC_SECRET) { + crv = CKR_KEY_FUNCTION_NOT_PERMITTED; + break; + } + if ((keySize != 0) && (keySize != SSL3_MASTER_SECRET_LENGTH)) { + crv = CKR_KEY_FUNCTION_NOT_PERMITTED; + break; + } + + /* finally do the key gen */ + ssl3_master = (CK_SSL3_MASTER_KEY_DERIVE_PARAMS *) + pMechanism->pParameter; + + if (ssl3_master->pVersion) { + SFTKSessionObject *sessKey = sftk_narrowToSessionObject(key); + rsa_pms = (SSL3RSAPreMasterSecret *)att->attrib.pValue; + /* don't leak more key material then necessary for SSL to work */ + if ((sessKey == NULL) || sessKey->wasDerived) { + ssl3_master->pVersion->major = 0xff; + ssl3_master->pVersion->minor = 0xff; + } else { + ssl3_master->pVersion->major = rsa_pms->client_version[0]; + ssl3_master->pVersion->minor = rsa_pms->client_version[1]; + } + } + if (ssl3_master->RandomInfo.ulClientRandomLen != SSL3_RANDOM_LENGTH) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + if (ssl3_master->RandomInfo.ulServerRandomLen != SSL3_RANDOM_LENGTH) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + PORT_Memcpy(crsrdata, + ssl3_master->RandomInfo.pClientRandom, SSL3_RANDOM_LENGTH); + PORT_Memcpy(crsrdata + SSL3_RANDOM_LENGTH, + ssl3_master->RandomInfo.pServerRandom, SSL3_RANDOM_LENGTH); + + if (isTLS) { + SECStatus status; + SECItem crsr = { siBuffer, NULL, 0 }; + SECItem master = { siBuffer, NULL, 0 }; + SECItem pms = { siBuffer, NULL, 0 }; + + crsr.data = crsrdata; + crsr.len = sizeof crsrdata; + master.data = key_block; + master.len = SSL3_MASTER_SECRET_LENGTH; + pms.data = (unsigned char *)att->attrib.pValue; + pms.len = att->attrib.ulValueLen; + + if (tlsPrfHash != HASH_AlgNULL) { + status = TLS_P_hash(tlsPrfHash, &pms, "master secret", + &crsr, &master, isFIPS); + } else { + status = TLS_PRF(&pms, "master secret", &crsr, &master, isFIPS); + } + if (status != SECSuccess) { + PORT_Memset(crsrdata, 0, sizeof crsrdata); + crv = CKR_FUNCTION_FAILED; + break; + } + } else { + /* now allocate the hash contexts */ + md5 = MD5_NewContext(); + if (md5 == NULL) { + PORT_Memset(crsrdata, 0, sizeof crsrdata); + crv = CKR_HOST_MEMORY; + break; + } + sha = SHA1_NewContext(); + if (sha == NULL) { + PORT_Memset(crsrdata, 0, sizeof crsrdata); + PORT_Free(md5); + crv = CKR_HOST_MEMORY; + break; + } + for (i = 0; i < 3; i++) { + SHA1_Begin(sha); + SHA1_Update(sha, (unsigned char *)mixers[i], strlen(mixers[i])); + SHA1_Update(sha, (const unsigned char *)att->attrib.pValue, + att->attrib.ulValueLen); + SHA1_Update(sha, crsrdata, sizeof crsrdata); + SHA1_End(sha, sha_out, &outLen, SHA1_LENGTH); + PORT_Assert(outLen == SHA1_LENGTH); + + MD5_Begin(md5); + MD5_Update(md5, (const unsigned char *)att->attrib.pValue, + att->attrib.ulValueLen); + MD5_Update(md5, sha_out, outLen); + MD5_End(md5, &key_block[i * MD5_LENGTH], &outLen, MD5_LENGTH); + PORT_Assert(outLen == MD5_LENGTH); + } + PORT_Free(md5); + PORT_Free(sha); + PORT_Memset(crsrdata, 0, sizeof crsrdata); + PORT_Memset(sha_out, 0, sizeof sha_out); + } + + /* store the results */ + crv = sftk_forceAttribute(key, CKA_VALUE, key_block, SSL3_MASTER_SECRET_LENGTH); + PORT_Memset(key_block, 0, sizeof key_block); + if (crv != CKR_OK) + break; + keyType = CKK_GENERIC_SECRET; + crv = sftk_forceAttribute(key, CKA_KEY_TYPE, &keyType, sizeof(keyType)); + if (isTLS) { + /* TLS's master secret is used to "sign" finished msgs with PRF. */ + /* XXX This seems like a hack. But SFTK_Derive only accepts + * one "operation" argument. */ + crv = sftk_forceAttribute(key, CKA_SIGN, &cktrue, sizeof(CK_BBOOL)); + if (crv != CKR_OK) + break; + crv = sftk_forceAttribute(key, CKA_VERIFY, &cktrue, sizeof(CK_BBOOL)); + if (crv != CKR_OK) + break; + /* While we're here, we might as well force this, too. */ + crv = sftk_forceAttribute(key, CKA_DERIVE, &cktrue, sizeof(CK_BBOOL)); + if (crv != CKR_OK) + break; + } + break; + } + + /* Extended master key derivation [draft-ietf-tls-session-hash] */ + case CKM_NSS_TLS_EXTENDED_MASTER_KEY_DERIVE: + case CKM_NSS_TLS_EXTENDED_MASTER_KEY_DERIVE_DH: { + CK_NSS_TLS_EXTENDED_MASTER_KEY_DERIVE_PARAMS *ems_params; + SSL3RSAPreMasterSecret *rsa_pms; + SECStatus status; + SECItem pms = { siBuffer, NULL, 0 }; + SECItem seed = { siBuffer, NULL, 0 }; + SECItem master = { siBuffer, NULL, 0 }; + + ems_params = (CK_NSS_TLS_EXTENDED_MASTER_KEY_DERIVE_PARAMS *) + pMechanism->pParameter; + + /* First do the consistency checks */ + if ((mechanism == CKM_NSS_TLS_EXTENDED_MASTER_KEY_DERIVE) && + (att->attrib.ulValueLen != SSL3_PMS_LENGTH)) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + att2 = sftk_FindAttribute(sourceKey, CKA_KEY_TYPE); + if ((att2 == NULL) || + (*(CK_KEY_TYPE *)att2->attrib.pValue != CKK_GENERIC_SECRET)) { + if (att2) + sftk_FreeAttribute(att2); + crv = CKR_KEY_FUNCTION_NOT_PERMITTED; + break; + } + sftk_FreeAttribute(att2); + if (keyType != CKK_GENERIC_SECRET) { + crv = CKR_KEY_FUNCTION_NOT_PERMITTED; + break; + } + if ((keySize != 0) && (keySize != SSL3_MASTER_SECRET_LENGTH)) { + crv = CKR_KEY_FUNCTION_NOT_PERMITTED; + break; + } + + /* Do the key derivation */ + pms.data = (unsigned char *)att->attrib.pValue; + pms.len = att->attrib.ulValueLen; + seed.data = ems_params->pSessionHash; + seed.len = ems_params->ulSessionHashLen; + master.data = key_block; + master.len = SSL3_MASTER_SECRET_LENGTH; + if (ems_params->prfHashMechanism == CKM_TLS_PRF) { + /* + * In this case, the session hash is the concatenation of SHA-1 + * and MD5, so it should be 36 bytes long. + */ + if (seed.len != MD5_LENGTH + SHA1_LENGTH) { + crv = CKR_TEMPLATE_INCONSISTENT; + break; + } + + status = TLS_PRF(&pms, "extended master secret", + &seed, &master, isFIPS); + } else { + const SECHashObject *hashObj; + + tlsPrfHash = sftk_GetHashTypeFromMechanism(ems_params->prfHashMechanism); + if (tlsPrfHash == HASH_AlgNULL) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + + hashObj = HASH_GetRawHashObject(tlsPrfHash); + if (seed.len != hashObj->length) { + crv = CKR_TEMPLATE_INCONSISTENT; + break; + } + + status = TLS_P_hash(tlsPrfHash, &pms, "extended master secret", + &seed, &master, isFIPS); + } + if (status != SECSuccess) { + crv = CKR_FUNCTION_FAILED; + break; + } + + /* Reflect the version if required */ + if (ems_params->pVersion) { + SFTKSessionObject *sessKey = sftk_narrowToSessionObject(key); + rsa_pms = (SSL3RSAPreMasterSecret *)att->attrib.pValue; + /* don't leak more key material than necessary for SSL to work */ + if ((sessKey == NULL) || sessKey->wasDerived) { + ems_params->pVersion->major = 0xff; + ems_params->pVersion->minor = 0xff; + } else { + ems_params->pVersion->major = rsa_pms->client_version[0]; + ems_params->pVersion->minor = rsa_pms->client_version[1]; + } + } + + /* Store the results */ + crv = sftk_forceAttribute(key, CKA_VALUE, key_block, + SSL3_MASTER_SECRET_LENGTH); + PORT_Memset(key_block, 0, sizeof key_block); + break; + } + + case CKM_TLS12_KEY_AND_MAC_DERIVE: + case CKM_NSS_TLS_KEY_AND_MAC_DERIVE_SHA256: + case CKM_TLS_KEY_AND_MAC_DERIVE: + case CKM_SSL3_KEY_AND_MAC_DERIVE: { + CK_SSL3_KEY_MAT_PARAMS *ssl3_keys; + CK_SSL3_KEY_MAT_OUT *ssl3_keys_out; + CK_ULONG effKeySize; + unsigned int block_needed; + unsigned char srcrdata[SSL3_RANDOM_LENGTH * 2]; + + if (mechanism == CKM_TLS12_KEY_AND_MAC_DERIVE) { + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_TLS12_KEY_MAT_PARAMS))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + CK_TLS12_KEY_MAT_PARAMS *tls12_keys = + (CK_TLS12_KEY_MAT_PARAMS *)pMechanism->pParameter; + tlsPrfHash = sftk_GetHashTypeFromMechanism(tls12_keys->prfHashMechanism); + if (tlsPrfHash == HASH_AlgNULL) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + } else if (mechanism == CKM_NSS_TLS_KEY_AND_MAC_DERIVE_SHA256) { + tlsPrfHash = HASH_AlgSHA256; + } + + if (mechanism != CKM_SSL3_KEY_AND_MAC_DERIVE) { + isTLS = PR_TRUE; + } + + crv = sftk_DeriveSensitiveCheck(sourceKey, key, PR_FALSE); + if (crv != CKR_OK) + break; + + if (att->attrib.ulValueLen != SSL3_MASTER_SECRET_LENGTH) { + crv = CKR_KEY_FUNCTION_NOT_PERMITTED; + break; + } + att2 = sftk_FindAttribute(sourceKey, CKA_KEY_TYPE); + if ((att2 == NULL) || (*(CK_KEY_TYPE *)att2->attrib.pValue != + CKK_GENERIC_SECRET)) { + if (att2) + sftk_FreeAttribute(att2); + crv = CKR_KEY_FUNCTION_NOT_PERMITTED; + break; + } + sftk_FreeAttribute(att2); + md5 = MD5_NewContext(); + if (md5 == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + sha = SHA1_NewContext(); + if (sha == NULL) { + MD5_DestroyContext(md5, PR_TRUE); + crv = CKR_HOST_MEMORY; + break; + } + + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_SSL3_KEY_MAT_PARAMS))) { + MD5_DestroyContext(md5, PR_TRUE); + SHA1_DestroyContext(sha, PR_TRUE); + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + ssl3_keys = (CK_SSL3_KEY_MAT_PARAMS *)pMechanism->pParameter; + + PORT_Memcpy(srcrdata, + ssl3_keys->RandomInfo.pServerRandom, SSL3_RANDOM_LENGTH); + PORT_Memcpy(srcrdata + SSL3_RANDOM_LENGTH, + ssl3_keys->RandomInfo.pClientRandom, SSL3_RANDOM_LENGTH); + + /* + * clear out our returned keys so we can recover on failure + */ + ssl3_keys_out = ssl3_keys->pReturnedKeyMaterial; + ssl3_keys_out->hClientMacSecret = CK_INVALID_HANDLE; + ssl3_keys_out->hServerMacSecret = CK_INVALID_HANDLE; + ssl3_keys_out->hClientKey = CK_INVALID_HANDLE; + ssl3_keys_out->hServerKey = CK_INVALID_HANDLE; + + /* + * How much key material do we need? + */ + macSize = ssl3_keys->ulMacSizeInBits / 8; + effKeySize = ssl3_keys->ulKeySizeInBits / 8; + IVSize = ssl3_keys->ulIVSizeInBits / 8; + if (keySize == 0) { + effKeySize = keySize; + } + + /* bIsExport must be false. */ + if (ssl3_keys->bIsExport) { + MD5_DestroyContext(md5, PR_TRUE); + SHA1_DestroyContext(sha, PR_TRUE); + PORT_Memset(srcrdata, 0, sizeof srcrdata); + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + + block_needed = 2 * (macSize + effKeySize + IVSize); + PORT_Assert(block_needed <= sizeof key_block); + if (block_needed > sizeof key_block) + block_needed = sizeof key_block; + + /* + * generate the key material: This looks amazingly similar to the + * PMS code, and is clearly crying out for a function to provide it. + */ + if (isTLS) { + SECStatus status; + SECItem srcr = { siBuffer, NULL, 0 }; + SECItem keyblk = { siBuffer, NULL, 0 }; + SECItem master = { siBuffer, NULL, 0 }; + + srcr.data = srcrdata; + srcr.len = sizeof srcrdata; + keyblk.data = key_block; + keyblk.len = block_needed; + master.data = (unsigned char *)att->attrib.pValue; + master.len = att->attrib.ulValueLen; + + if (tlsPrfHash != HASH_AlgNULL) { + status = TLS_P_hash(tlsPrfHash, &master, "key expansion", + &srcr, &keyblk, isFIPS); + } else { + status = TLS_PRF(&master, "key expansion", &srcr, &keyblk, + isFIPS); + } + if (status != SECSuccess) { + goto key_and_mac_derive_fail; + } + } else { + unsigned int block_bytes = 0; + /* key_block = + * MD5(master_secret + SHA('A' + master_secret + + * ServerHello.random + ClientHello.random)) + + * MD5(master_secret + SHA('BB' + master_secret + + * ServerHello.random + ClientHello.random)) + + * MD5(master_secret + SHA('CCC' + master_secret + + * ServerHello.random + ClientHello.random)) + + * [...]; + */ + for (i = 0; i < NUM_MIXERS && block_bytes < block_needed; i++) { + SHA1_Begin(sha); + SHA1_Update(sha, (unsigned char *)mixers[i], strlen(mixers[i])); + SHA1_Update(sha, (const unsigned char *)att->attrib.pValue, + att->attrib.ulValueLen); + SHA1_Update(sha, srcrdata, sizeof srcrdata); + SHA1_End(sha, sha_out, &outLen, SHA1_LENGTH); + PORT_Assert(outLen == SHA1_LENGTH); + MD5_Begin(md5); + MD5_Update(md5, (const unsigned char *)att->attrib.pValue, + att->attrib.ulValueLen); + MD5_Update(md5, sha_out, outLen); + MD5_End(md5, &key_block[i * MD5_LENGTH], &outLen, MD5_LENGTH); + PORT_Assert(outLen == MD5_LENGTH); + block_bytes += outLen; + } + PORT_Memset(sha_out, 0, sizeof sha_out); + } + + /* + * Put the key material where it goes. + */ + i = 0; /* now shows how much consumed */ + + /* + * The key_block is partitioned as follows: + * client_write_MAC_secret[CipherSpec.hash_size] + */ + crv = sftk_buildSSLKey(hSession, key, PR_TRUE, &key_block[i], macSize, + &ssl3_keys_out->hClientMacSecret); + if (crv != CKR_OK) + goto key_and_mac_derive_fail; + + i += macSize; + + /* + * server_write_MAC_secret[CipherSpec.hash_size] + */ + crv = sftk_buildSSLKey(hSession, key, PR_TRUE, &key_block[i], macSize, + &ssl3_keys_out->hServerMacSecret); + if (crv != CKR_OK) { + goto key_and_mac_derive_fail; + } + i += macSize; + + if (keySize) { + /* + ** Generate Domestic write keys and IVs. + ** client_write_key[CipherSpec.key_material] + */ + crv = sftk_buildSSLKey(hSession, key, PR_FALSE, &key_block[i], + keySize, &ssl3_keys_out->hClientKey); + if (crv != CKR_OK) { + goto key_and_mac_derive_fail; + } + i += keySize; + + /* + ** server_write_key[CipherSpec.key_material] + */ + crv = sftk_buildSSLKey(hSession, key, PR_FALSE, &key_block[i], + keySize, &ssl3_keys_out->hServerKey); + if (crv != CKR_OK) { + goto key_and_mac_derive_fail; + } + i += keySize; + + /* + ** client_write_IV[CipherSpec.IV_size] + */ + if (IVSize > 0) { + PORT_Memcpy(ssl3_keys_out->pIVClient, + &key_block[i], IVSize); + i += IVSize; + } + + /* + ** server_write_IV[CipherSpec.IV_size] + */ + if (IVSize > 0) { + PORT_Memcpy(ssl3_keys_out->pIVServer, + &key_block[i], IVSize); + i += IVSize; + } + PORT_Assert(i <= sizeof key_block); + } + + crv = CKR_OK; + + if (0) { + key_and_mac_derive_fail: + if (crv == CKR_OK) + crv = CKR_FUNCTION_FAILED; + sftk_freeSSLKeys(hSession, ssl3_keys_out); + } + PORT_Memset(srcrdata, 0, sizeof srcrdata); + PORT_Memset(key_block, 0, sizeof key_block); + MD5_DestroyContext(md5, PR_TRUE); + SHA1_DestroyContext(sha, PR_TRUE); + sftk_FreeObject(key); + key = NULL; + break; + } + + case CKM_DES3_ECB_ENCRYPT_DATA: + case CKM_DES3_CBC_ENCRYPT_DATA: { + void *cipherInfo; + unsigned char des3key[MAX_DES3_KEY_SIZE]; + CK_DES_CBC_ENCRYPT_DATA_PARAMS *desEncryptPtr; + int mode; + unsigned char *iv; + unsigned char *data; + CK_ULONG len; + + if (mechanism == CKM_DES3_ECB_ENCRYPT_DATA) { + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_KEY_DERIVATION_STRING_DATA))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + stringPtr = (CK_KEY_DERIVATION_STRING_DATA *) + pMechanism->pParameter; + mode = NSS_DES_EDE3; + iv = NULL; + data = stringPtr->pData; + len = stringPtr->ulLen; + } else { + mode = NSS_DES_EDE3_CBC; + desEncryptPtr = + (CK_DES_CBC_ENCRYPT_DATA_PARAMS *) + pMechanism->pParameter; + iv = desEncryptPtr->iv; + data = desEncryptPtr->pData; + len = desEncryptPtr->length; + } + if (att->attrib.ulValueLen == 16) { + PORT_Memcpy(des3key, att->attrib.pValue, 16); + PORT_Memcpy(des3key + 16, des3key, 8); + } else if (att->attrib.ulValueLen == 24) { + PORT_Memcpy(des3key, att->attrib.pValue, 24); + } else { + crv = CKR_KEY_SIZE_RANGE; + break; + } + cipherInfo = DES_CreateContext(des3key, iv, mode, PR_TRUE); + PORT_Memset(des3key, 0, 24); + if (cipherInfo == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + crv = sftk_DeriveEncrypt((SFTKCipher)DES_Encrypt, + cipherInfo, 8, key, keySize, + data, len); + DES_DestroyContext(cipherInfo, PR_TRUE); + break; + } + + case CKM_AES_ECB_ENCRYPT_DATA: + case CKM_AES_CBC_ENCRYPT_DATA: { + void *cipherInfo; + CK_AES_CBC_ENCRYPT_DATA_PARAMS *aesEncryptPtr; + int mode; + unsigned char *iv; + unsigned char *data; + CK_ULONG len; + + if (mechanism == CKM_AES_ECB_ENCRYPT_DATA) { + mode = NSS_AES; + iv = NULL; + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_KEY_DERIVATION_STRING_DATA))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + stringPtr = (CK_KEY_DERIVATION_STRING_DATA *)pMechanism->pParameter; + data = stringPtr->pData; + len = stringPtr->ulLen; + } else { + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_AES_CBC_ENCRYPT_DATA_PARAMS))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + aesEncryptPtr = + (CK_AES_CBC_ENCRYPT_DATA_PARAMS *)pMechanism->pParameter; + mode = NSS_AES_CBC; + iv = aesEncryptPtr->iv; + data = aesEncryptPtr->pData; + len = aesEncryptPtr->length; + } + + cipherInfo = AES_CreateContext((unsigned char *)att->attrib.pValue, + iv, mode, PR_TRUE, + att->attrib.ulValueLen, 16); + if (cipherInfo == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + crv = sftk_DeriveEncrypt((SFTKCipher)AES_Encrypt, + cipherInfo, 16, key, keySize, + data, len); + AES_DestroyContext(cipherInfo, PR_TRUE); + break; + } + + case CKM_CAMELLIA_ECB_ENCRYPT_DATA: + case CKM_CAMELLIA_CBC_ENCRYPT_DATA: { + void *cipherInfo; + CK_AES_CBC_ENCRYPT_DATA_PARAMS *aesEncryptPtr; + int mode; + unsigned char *iv; + unsigned char *data; + CK_ULONG len; + + if (mechanism == CKM_CAMELLIA_ECB_ENCRYPT_DATA) { + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_KEY_DERIVATION_STRING_DATA))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + stringPtr = (CK_KEY_DERIVATION_STRING_DATA *) + pMechanism->pParameter; + aesEncryptPtr = NULL; + mode = NSS_CAMELLIA; + data = stringPtr->pData; + len = stringPtr->ulLen; + iv = NULL; + } else { + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_AES_CBC_ENCRYPT_DATA_PARAMS))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + stringPtr = NULL; + aesEncryptPtr = (CK_AES_CBC_ENCRYPT_DATA_PARAMS *) + pMechanism->pParameter; + mode = NSS_CAMELLIA_CBC; + iv = aesEncryptPtr->iv; + data = aesEncryptPtr->pData; + len = aesEncryptPtr->length; + } + + cipherInfo = Camellia_CreateContext((unsigned char *)att->attrib.pValue, + iv, mode, PR_TRUE, + att->attrib.ulValueLen); + if (cipherInfo == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + crv = sftk_DeriveEncrypt((SFTKCipher)Camellia_Encrypt, + cipherInfo, 16, key, keySize, + data, len); + Camellia_DestroyContext(cipherInfo, PR_TRUE); + break; + } + +#ifndef NSS_DISABLE_DEPRECATED_SEED + case CKM_SEED_ECB_ENCRYPT_DATA: + case CKM_SEED_CBC_ENCRYPT_DATA: { + void *cipherInfo; + CK_AES_CBC_ENCRYPT_DATA_PARAMS *aesEncryptPtr; + int mode; + unsigned char *iv; + unsigned char *data; + CK_ULONG len; + + if (mechanism == CKM_SEED_ECB_ENCRYPT_DATA) { + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_KEY_DERIVATION_STRING_DATA))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + mode = NSS_SEED; + stringPtr = (CK_KEY_DERIVATION_STRING_DATA *) + pMechanism->pParameter; + aesEncryptPtr = NULL; + data = stringPtr->pData; + len = stringPtr->ulLen; + iv = NULL; + } else { + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_AES_CBC_ENCRYPT_DATA_PARAMS))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + mode = NSS_SEED_CBC; + aesEncryptPtr = (CK_AES_CBC_ENCRYPT_DATA_PARAMS *) + pMechanism->pParameter; + iv = aesEncryptPtr->iv; + data = aesEncryptPtr->pData; + len = aesEncryptPtr->length; + } + + cipherInfo = SEED_CreateContext((unsigned char *)att->attrib.pValue, + iv, mode, PR_TRUE); + if (cipherInfo == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + crv = sftk_DeriveEncrypt((SFTKCipher)SEED_Encrypt, + cipherInfo, 16, key, keySize, + data, len); + SEED_DestroyContext(cipherInfo, PR_TRUE); + break; + } +#endif /* NSS_DISABLE_DEPRECATED_SEED */ + + case CKM_CONCATENATE_BASE_AND_KEY: { + SFTKObject *paramKey; + + crv = sftk_DeriveSensitiveCheck(sourceKey, key, PR_FALSE); + if (crv != CKR_OK) + break; + + session = sftk_SessionFromHandle(hSession); + if (session == NULL) { + crv = CKR_SESSION_HANDLE_INVALID; + break; + } + + paramKey = sftk_ObjectFromHandle(*(CK_OBJECT_HANDLE *) + pMechanism->pParameter, + session); + sftk_FreeSession(session); + if (paramKey == NULL) { + crv = CKR_KEY_HANDLE_INVALID; + break; + } + + if (sftk_isTrue(paramKey, CKA_SENSITIVE)) { + crv = sftk_forceAttribute(key, CKA_SENSITIVE, &cktrue, + sizeof(CK_BBOOL)); + if (crv != CKR_OK) { + sftk_FreeObject(paramKey); + break; + } + } + + if (sftk_hasAttribute(paramKey, CKA_EXTRACTABLE) && !sftk_isTrue(paramKey, CKA_EXTRACTABLE)) { + crv = sftk_forceAttribute(key, CKA_EXTRACTABLE, &ckfalse, sizeof(CK_BBOOL)); + if (crv != CKR_OK) { + sftk_FreeObject(paramKey); + break; + } + } + + att2 = sftk_FindAttribute(paramKey, CKA_VALUE); + if (att2 == NULL) { + sftk_FreeObject(paramKey); + crv = CKR_KEY_HANDLE_INVALID; + break; + } + tmpKeySize = att->attrib.ulValueLen + att2->attrib.ulValueLen; + if (keySize == 0) + keySize = tmpKeySize; + if (keySize > tmpKeySize) { + sftk_FreeObject(paramKey); + sftk_FreeAttribute(att2); + crv = CKR_TEMPLATE_INCONSISTENT; + break; + } + buf = (unsigned char *)PORT_Alloc(tmpKeySize); + if (buf == NULL) { + sftk_FreeAttribute(att2); + sftk_FreeObject(paramKey); + crv = CKR_HOST_MEMORY; + break; + } + + PORT_Memcpy(buf, att->attrib.pValue, att->attrib.ulValueLen); + PORT_Memcpy(buf + att->attrib.ulValueLen, + att2->attrib.pValue, att2->attrib.ulValueLen); + + crv = sftk_forceAttribute(key, CKA_VALUE, buf, keySize); + PORT_ZFree(buf, tmpKeySize); + sftk_FreeAttribute(att2); + sftk_FreeObject(paramKey); + break; + } + + case CKM_CONCATENATE_BASE_AND_DATA: + crv = sftk_DeriveSensitiveCheck(sourceKey, key, PR_FALSE); + if (crv != CKR_OK) + break; + + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_KEY_DERIVATION_STRING_DATA))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + stringPtr = (CK_KEY_DERIVATION_STRING_DATA *)pMechanism->pParameter; + tmpKeySize = att->attrib.ulValueLen + stringPtr->ulLen; + if (keySize == 0) + keySize = tmpKeySize; + if (keySize > tmpKeySize) { + crv = CKR_TEMPLATE_INCONSISTENT; + break; + } + buf = (unsigned char *)PORT_Alloc(tmpKeySize); + if (buf == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + + PORT_Memcpy(buf, att->attrib.pValue, att->attrib.ulValueLen); + PORT_Memcpy(buf + att->attrib.ulValueLen, stringPtr->pData, + stringPtr->ulLen); + + crv = sftk_forceAttribute(key, CKA_VALUE, buf, keySize); + PORT_ZFree(buf, tmpKeySize); + break; + case CKM_CONCATENATE_DATA_AND_BASE: + crv = sftk_DeriveSensitiveCheck(sourceKey, key, PR_FALSE); + if (crv != CKR_OK) + break; + + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_KEY_DERIVATION_STRING_DATA))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + stringPtr = (CK_KEY_DERIVATION_STRING_DATA *)pMechanism->pParameter; + tmpKeySize = att->attrib.ulValueLen + stringPtr->ulLen; + if (keySize == 0) + keySize = tmpKeySize; + if (keySize > tmpKeySize) { + crv = CKR_TEMPLATE_INCONSISTENT; + break; + } + buf = (unsigned char *)PORT_Alloc(tmpKeySize); + if (buf == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + + PORT_Memcpy(buf, stringPtr->pData, stringPtr->ulLen); + PORT_Memcpy(buf + stringPtr->ulLen, att->attrib.pValue, + att->attrib.ulValueLen); + + crv = sftk_forceAttribute(key, CKA_VALUE, buf, keySize); + PORT_ZFree(buf, tmpKeySize); + break; + case CKM_XOR_BASE_AND_DATA: + crv = sftk_DeriveSensitiveCheck(sourceKey, key, PR_FALSE); + if (crv != CKR_OK) + break; + + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_KEY_DERIVATION_STRING_DATA))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + stringPtr = (CK_KEY_DERIVATION_STRING_DATA *)pMechanism->pParameter; + tmpKeySize = PR_MIN(att->attrib.ulValueLen, stringPtr->ulLen); + if (keySize == 0) + keySize = tmpKeySize; + if (keySize > tmpKeySize) { + crv = CKR_TEMPLATE_INCONSISTENT; + break; + } + buf = (unsigned char *)PORT_Alloc(keySize); + if (buf == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + + PORT_Memcpy(buf, att->attrib.pValue, keySize); + for (i = 0; i < (int)keySize; i++) { + buf[i] ^= stringPtr->pData[i]; + } + + crv = sftk_forceAttribute(key, CKA_VALUE, buf, keySize); + PORT_ZFree(buf, keySize); + break; + + case CKM_EXTRACT_KEY_FROM_KEY: { + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_EXTRACT_PARAMS))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + /* the following assumes 8 bits per byte */ + CK_ULONG extract = *(CK_EXTRACT_PARAMS *)pMechanism->pParameter; + CK_ULONG shift = extract & 0x7; /* extract mod 8 the fast way */ + CK_ULONG offset = extract >> 3; /* extract div 8 the fast way */ + + crv = sftk_DeriveSensitiveCheck(sourceKey, key, PR_FALSE); + if (crv != CKR_OK) + break; + + if (keySize == 0) { + crv = CKR_TEMPLATE_INCOMPLETE; + break; + } + /* make sure we have enough bits in the original key */ + if (att->attrib.ulValueLen < + (offset + keySize + ((shift != 0) ? 1 : 0))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + buf = (unsigned char *)PORT_Alloc(keySize); + if (buf == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + + /* copy the bits we need into the new key */ + for (i = 0; i < (int)keySize; i++) { + unsigned char *value = + ((unsigned char *)att->attrib.pValue) + offset + i; + if (shift) { + buf[i] = (value[0] << (shift)) | (value[1] >> (8 - shift)); + } else { + buf[i] = value[0]; + } + } + + crv = sftk_forceAttribute(key, CKA_VALUE, buf, keySize); + PORT_ZFree(buf, keySize); + break; + } + case CKM_MD2_KEY_DERIVATION: + if (keySize == 0) + keySize = MD2_LENGTH; + if (keySize > MD2_LENGTH) { + crv = CKR_TEMPLATE_INCONSISTENT; + break; + } + /* now allocate the hash contexts */ + md2 = MD2_NewContext(); + if (md2 == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + MD2_Begin(md2); + MD2_Update(md2, (const unsigned char *)att->attrib.pValue, + att->attrib.ulValueLen); + MD2_End(md2, key_block, &outLen, MD2_LENGTH); + MD2_DestroyContext(md2, PR_TRUE); + + crv = sftk_forceAttribute(key, CKA_VALUE, key_block, keySize); + PORT_Memset(key_block, 0, MD2_LENGTH); + break; +#define DERIVE_KEY_HASH(hash) \ + case CKM_##hash##_KEY_DERIVATION: \ + if (keySize == 0) \ + keySize = hash##_LENGTH; \ + if (keySize > hash##_LENGTH) { \ + crv = CKR_TEMPLATE_INCONSISTENT; \ + break; \ + } \ + hash##_HashBuf(key_block, (const unsigned char *)att->attrib.pValue, \ + att->attrib.ulValueLen); \ + crv = sftk_forceAttribute(key, CKA_VALUE, key_block, keySize); \ + PORT_Memset(key_block, 0, hash##_LENGTH); \ + break; + DERIVE_KEY_HASH(MD5) + DERIVE_KEY_HASH(SHA1) + DERIVE_KEY_HASH(SHA224) + DERIVE_KEY_HASH(SHA256) + DERIVE_KEY_HASH(SHA384) + DERIVE_KEY_HASH(SHA512) + DERIVE_KEY_HASH(SHA3_224) + DERIVE_KEY_HASH(SHA3_256) + DERIVE_KEY_HASH(SHA3_384) + DERIVE_KEY_HASH(SHA3_512) + + case CKM_DH_PKCS_DERIVE: { + SECItem derived, dhPublic; + SECItem dhPrime, dhValue; + const SECItem *subPrime; + /* sourceKey - values for the local existing low key */ + /* get prime and value attributes */ + crv = sftk_Attribute2SecItem(NULL, &dhPrime, sourceKey, CKA_PRIME); + if (crv != CKR_OK) + break; + + dhPublic.data = pMechanism->pParameter; + dhPublic.len = pMechanism->ulParameterLen; + + /* if the prime is an approved prime, we can skip all the other + * checks. */ + subPrime = sftk_VerifyDH_Prime(&dhPrime, isFIPS); + if (subPrime == NULL) { + SECItem dhSubPrime; + /* If the caller set the subprime value, it means that + * either the caller knows the subprime value and wants us + * to validate the key against the subprime, or that the + * caller wants us to verify that the prime is a safe prime + * by passing in subprime = (prime-1)/2 */ + dhSubPrime.data = NULL; + dhSubPrime.len = 0; + crv = sftk_Attribute2SecItem(NULL, &dhSubPrime, + sourceKey, CKA_SUBPRIME); + /* we ignore the value of crv here, We treat a valid + * return of len = 0 and a failure to find a subrime the same + * NOTE: we free the subprime in both cases depending on + * PORT_Free of NULL to be a noop */ + if (dhSubPrime.len != 0) { + PRBool isSafe = PR_FALSE; + + /* Callers can set dhSubPrime to q=(p-1)/2 to force + * checks for safe primes. If so we only need to check + * q and p for primality and skip the group test. */ + rv = sftk_IsSafePrime(&dhPrime, &dhSubPrime, &isSafe); + if (rv != SECSuccess) { + /* either p or q was even and therefore not prime, + * we can stop processing here and fail now */ + crv = CKR_ARGUMENTS_BAD; + SECITEM_ZfreeItem(&dhPrime, PR_FALSE); + SECITEM_ZfreeItem(&dhSubPrime, PR_FALSE); + break; + } + + /* first make sure the primes are really prime */ + if (!KEA_PrimeCheck(&dhPrime)) { + crv = CKR_ARGUMENTS_BAD; + SECITEM_ZfreeItem(&dhPrime, PR_FALSE); + SECITEM_ZfreeItem(&dhSubPrime, PR_FALSE); + break; + } + if (!KEA_PrimeCheck(&dhSubPrime)) { + crv = CKR_ARGUMENTS_BAD; + SECITEM_ZfreeItem(&dhPrime, PR_FALSE); + SECITEM_ZfreeItem(&dhSubPrime, PR_FALSE); + break; + } + if (isFIPS || !isSafe) { + /* With safe primes, there is only one other small + * subgroup. As long as y isn't 0, 1, or -1 mod p, + * any other y is safe. Only do the full check for + * non-safe primes, except in FIPS mode we need + * to do this check on all primes in which + * we receive the subprime value */ + if (!KEA_Verify(&dhPublic, &dhPrime, &dhSubPrime)) { + crv = CKR_ARGUMENTS_BAD; + SECITEM_ZfreeItem(&dhPrime, PR_FALSE); + SECITEM_ZfreeItem(&dhSubPrime, PR_FALSE); + break; + } + } + } else if (isFIPS) { + /* In FIPS mode we only accept approved primes, or + * primes with the full subprime value */ + crv = CKR_ARGUMENTS_BAD; + SECITEM_ZfreeItem(&dhPrime, PR_FALSE); + break; + } + /* checks are complete, no need for the subPrime any longer */ + SECITEM_ZfreeItem(&dhSubPrime, PR_FALSE); + } + + /* now that the prime is validated, get the private value */ + crv = sftk_Attribute2SecItem(NULL, &dhValue, sourceKey, CKA_VALUE); + if (crv != CKR_OK) { + SECITEM_ZfreeItem(&dhPrime, PR_FALSE); + break; + } + + /* calculate private value - oct */ + rv = DH_Derive(&dhPublic, &dhPrime, &dhValue, &derived, keySize); + + SECITEM_ZfreeItem(&dhPrime, PR_FALSE); + SECITEM_ZfreeItem(&dhValue, PR_FALSE); + + if (rv == SECSuccess) { + sftk_forceAttribute(key, CKA_VALUE, derived.data, derived.len); + SECITEM_ZfreeItem(&derived, PR_FALSE); + crv = CKR_OK; + } else + crv = CKR_HOST_MEMORY; + + break; + } + + case CKM_ECDH1_DERIVE: + case CKM_ECDH1_COFACTOR_DERIVE: { + SECItem ecScalar, ecPoint; + SECItem tmp; + PRBool withCofactor = PR_FALSE; + unsigned char *secret; + unsigned char *keyData = NULL; + unsigned int secretlen, pubKeyLen; + CK_ECDH1_DERIVE_PARAMS *mechParams; + NSSLOWKEYPrivateKey *privKey; + PLArenaPool *arena = NULL; + + /* Check mechanism parameters */ + mechParams = (CK_ECDH1_DERIVE_PARAMS *)pMechanism->pParameter; + if ((pMechanism->ulParameterLen != sizeof(CK_ECDH1_DERIVE_PARAMS)) || + ((mechParams->kdf == CKD_NULL) && + ((mechParams->ulSharedDataLen != 0) || + (mechParams->pSharedData != NULL)))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + + privKey = sftk_GetPrivKey(sourceKey, CKK_EC, &crv); + if (privKey == NULL) { + break; + } + + /* Now we are working with a non-NULL private key */ + SECITEM_CopyItem(NULL, &ecScalar, &privKey->u.ec.privateValue); + + ecPoint.data = mechParams->pPublicData; + ecPoint.len = mechParams->ulPublicDataLen; + + pubKeyLen = EC_GetPointSize(&privKey->u.ec.ecParams); + + /* if the len is too large, might be an encoded point */ + if (ecPoint.len > pubKeyLen) { + SECItem newPoint; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + goto ec_loser; + } + + rv = SEC_QuickDERDecodeItem(arena, &newPoint, + SEC_ASN1_GET(SEC_OctetStringTemplate), + &ecPoint); + if (rv != SECSuccess) { + goto ec_loser; + } + ecPoint = newPoint; + } + + if (mechanism == CKM_ECDH1_COFACTOR_DERIVE) { + withCofactor = PR_TRUE; + } + + rv = ECDH_Derive(&ecPoint, &privKey->u.ec.ecParams, &ecScalar, + withCofactor, &tmp); + SECITEM_ZfreeItem(&ecScalar, PR_FALSE); + ecScalar.data = NULL; + if (privKey != sourceKey->objectInfo) { + nsslowkey_DestroyPrivateKey(privKey); + privKey = NULL; + } + if (arena) { + PORT_FreeArena(arena, PR_FALSE); + arena = NULL; + } + + if (rv != SECSuccess) { + crv = sftk_MapCryptError(PORT_GetError()); + break; + } + + /* + * apply the kdf function. + */ + if (mechParams->kdf == CKD_NULL) { + /* + * tmp is the raw data created by ECDH_Derive, + * secret and secretlen are the values we will + * eventually pass as our generated key. + */ + secret = tmp.data; + secretlen = tmp.len; + } else { + secretlen = keySize; + crv = sftk_ANSI_X9_63_kdf(&secret, keySize, + &tmp, mechParams->pSharedData, + mechParams->ulSharedDataLen, mechParams->kdf); + PORT_ZFree(tmp.data, tmp.len); + if (crv != CKR_OK) { + break; + } + tmp.data = secret; + tmp.len = secretlen; + } + + /* + * if keySize is supplied, then we are generating a key of a specific + * length. This is done by taking the least significant 'keySize' + * bytes from the unsigned value calculated by ECDH. Note: this may + * mean padding temp with extra leading zeros from what ECDH_Derive + * already returned (which itself may contain leading zeros). + */ + if (keySize) { + if (secretlen < keySize) { + keyData = PORT_ZAlloc(keySize); + if (!keyData) { + PORT_ZFree(tmp.data, tmp.len); + crv = CKR_HOST_MEMORY; + break; + } + PORT_Memcpy(&keyData[keySize - secretlen], secret, secretlen); + secret = keyData; + } else { + secret += (secretlen - keySize); + } + secretlen = keySize; + } + + sftk_forceAttribute(key, CKA_VALUE, secret, secretlen); + PORT_ZFree(tmp.data, tmp.len); + if (keyData) { + PORT_ZFree(keyData, keySize); + } + break; + + ec_loser: + crv = CKR_ARGUMENTS_BAD; + SECITEM_ZfreeItem(&ecScalar, PR_FALSE); + if (privKey != sourceKey->objectInfo) + nsslowkey_DestroyPrivateKey(privKey); + if (arena) { + PORT_FreeArena(arena, PR_TRUE); + } + break; + } + /* See RFC 5869 and CK_NSS_HKDFParams for documentation. */ + case CKM_NSS_HKDF_SHA1: + hashMech = CKM_SHA_1; + goto hkdf; + case CKM_NSS_HKDF_SHA256: + hashMech = CKM_SHA256; + goto hkdf; + case CKM_NSS_HKDF_SHA384: + hashMech = CKM_SHA384; + goto hkdf; + case CKM_NSS_HKDF_SHA512: + hashMech = CKM_SHA512; + goto hkdf; + hkdf : { + const CK_NSS_HKDFParams *params = + (const CK_NSS_HKDFParams *)pMechanism->pParameter; + CK_HKDF_PARAMS hkdfParams; + + if (pMechanism->ulParameterLen != sizeof(CK_NSS_HKDFParams)) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + hkdfParams.bExtract = params->bExtract; + hkdfParams.bExpand = params->bExpand; + if (params->pSalt) { + hkdfParams.ulSaltType = CKF_HKDF_SALT_DATA; + } else { + hkdfParams.ulSaltType = CKF_HKDF_SALT_NULL; + } + hkdfParams.pSalt = params->pSalt; + hkdfParams.ulSaltLen = params->ulSaltLen; + hkdfParams.hSaltKey = CK_INVALID_HANDLE; + hkdfParams.pInfo = params->pInfo; + hkdfParams.ulInfoLen = params->ulInfoLen; + hkdfParams.prfHashMechanism = hashMech; + + crv = sftk_HKDF(&hkdfParams, hSession, sourceKey, + att->attrib.pValue, att->attrib.ulValueLen, + key, NULL, keySize, PR_FALSE, isFIPS); + } break; + case CKM_HKDF_DERIVE: + case CKM_HKDF_DATA: /* only difference is the class of key */ + if ((pMechanism->pParameter == NULL) || + (pMechanism->ulParameterLen != sizeof(CK_HKDF_PARAMS))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + crv = sftk_HKDF((CK_HKDF_PARAMS_PTR)pMechanism->pParameter, + hSession, sourceKey, att->attrib.pValue, + att->attrib.ulValueLen, key, NULL, keySize, PR_TRUE, + isFIPS); + break; + case CKM_NSS_JPAKE_ROUND2_SHA1: + hashType = HASH_AlgSHA1; + goto jpake2; + case CKM_NSS_JPAKE_ROUND2_SHA256: + hashType = HASH_AlgSHA256; + goto jpake2; + case CKM_NSS_JPAKE_ROUND2_SHA384: + hashType = HASH_AlgSHA384; + goto jpake2; + case CKM_NSS_JPAKE_ROUND2_SHA512: + hashType = HASH_AlgSHA512; + goto jpake2; + jpake2: + if (pMechanism->pParameter == NULL || + pMechanism->ulParameterLen != sizeof(CK_NSS_JPAKERound2Params)) + crv = CKR_MECHANISM_PARAM_INVALID; + if (crv == CKR_OK && sftk_isTrue(key, CKA_TOKEN)) + crv = CKR_TEMPLATE_INCONSISTENT; + if (crv == CKR_OK) + crv = sftk_DeriveSensitiveCheck(sourceKey, key, PR_FALSE); + if (crv == CKR_OK) + crv = jpake_Round2(hashType, + (CK_NSS_JPAKERound2Params *)pMechanism->pParameter, + sourceKey, key); + break; + + case CKM_NSS_JPAKE_FINAL_SHA1: + hashType = HASH_AlgSHA1; + goto jpakeFinal; + case CKM_NSS_JPAKE_FINAL_SHA256: + hashType = HASH_AlgSHA256; + goto jpakeFinal; + case CKM_NSS_JPAKE_FINAL_SHA384: + hashType = HASH_AlgSHA384; + goto jpakeFinal; + case CKM_NSS_JPAKE_FINAL_SHA512: + hashType = HASH_AlgSHA512; + goto jpakeFinal; + jpakeFinal: + if (pMechanism->pParameter == NULL || + pMechanism->ulParameterLen != sizeof(CK_NSS_JPAKEFinalParams)) + crv = CKR_MECHANISM_PARAM_INVALID; + /* We purposely do not do the derive sensitivity check; we want to be + able to derive non-sensitive keys while allowing the ROUND1 and + ROUND2 keys to be sensitive (which they always are, since they are + in the CKO_PRIVATE_KEY class). The caller must include CKA_SENSITIVE + in the template in order for the resultant keyblock key to be + sensitive. + */ + if (crv == CKR_OK) + crv = jpake_Final(hashType, + (CK_NSS_JPAKEFinalParams *)pMechanism->pParameter, + sourceKey, key); + break; + + case CKM_NSS_SP800_108_COUNTER_KDF_DERIVE_DATA: /* fall through */ + case CKM_NSS_SP800_108_FEEDBACK_KDF_DERIVE_DATA: /* fall through */ + case CKM_NSS_SP800_108_DOUBLE_PIPELINE_KDF_DERIVE_DATA: /* fall through */ + case CKM_SP800_108_COUNTER_KDF: /* fall through */ + case CKM_SP800_108_FEEDBACK_KDF: /* fall through */ + case CKM_SP800_108_DOUBLE_PIPELINE_KDF: + crv = sftk_DeriveSensitiveCheck(sourceKey, key, PR_FALSE); + if (crv != CKR_OK) { + break; + } + + crv = kbkdf_Dispatch(mechanism, hSession, pMechanism, sourceKey, key, keySize); + break; + default: + crv = CKR_MECHANISM_INVALID; + } + if (att) { + sftk_FreeAttribute(att); + } + sftk_FreeObject(sourceKey); + if (crv != CKR_OK) { + if (key) + sftk_FreeObject(key); + return crv; + } + + /* link the key object into the list */ + if (key) { + SFTKSessionObject *sessKey = sftk_narrowToSessionObject(key); + PORT_Assert(sessKey); + /* get the session */ + sessKey->wasDerived = PR_TRUE; + session = sftk_SessionFromHandle(hSession); + if (session == NULL) { + sftk_FreeObject(key); + return CKR_HOST_MEMORY; + } + + crv = sftk_handleObject(key, session); + session->lastOpWasFIPS = key->isFIPS; + sftk_FreeSession(session); + *phKey = key->handle; + sftk_FreeObject(key); + } + return crv; +} + +/* NSC_GetFunctionStatus obtains an updated status of a function running + * in parallel with an application. */ +CK_RV +NSC_GetFunctionStatus(CK_SESSION_HANDLE hSession) +{ + CHECK_FORK(); + + return CKR_FUNCTION_NOT_PARALLEL; +} + +/* NSC_CancelFunction cancels a function running in parallel */ +CK_RV +NSC_CancelFunction(CK_SESSION_HANDLE hSession) +{ + CHECK_FORK(); + + return CKR_FUNCTION_NOT_PARALLEL; +} + +/* NSC_GetOperationState saves the state of the cryptographic + * operation in a session. + * NOTE: This code only works for digest functions for now. eventually need + * to add full flatten/resurect to our state stuff so that all types of state + * can be saved */ +CK_RV +NSC_GetOperationState(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pOperationState, CK_ULONG_PTR pulOperationStateLen) +{ + SFTKSessionContext *context; + SFTKSession *session; + CK_RV crv; + CK_ULONG pOSLen = *pulOperationStateLen; + + CHECK_FORK(); + + /* make sure we're legal */ + crv = sftk_GetContext(hSession, &context, SFTK_HASH, PR_TRUE, &session); + if (crv != CKR_OK) + return crv; + + /* a zero cipherInfoLen signals that this context cannot be serialized */ + if (context->cipherInfoLen == 0) { + return CKR_STATE_UNSAVEABLE; + } + + *pulOperationStateLen = context->cipherInfoLen + sizeof(CK_MECHANISM_TYPE) + sizeof(SFTKContextType); + if (pOperationState == NULL) { + sftk_FreeSession(session); + return CKR_OK; + } else { + if (pOSLen < *pulOperationStateLen) { + return CKR_BUFFER_TOO_SMALL; + } + } + PORT_Memcpy(pOperationState, &context->type, sizeof(SFTKContextType)); + pOperationState += sizeof(SFTKContextType); + PORT_Memcpy(pOperationState, &context->currentMech, + sizeof(CK_MECHANISM_TYPE)); + pOperationState += sizeof(CK_MECHANISM_TYPE); + PORT_Memcpy(pOperationState, context->cipherInfo, context->cipherInfoLen); + sftk_FreeSession(session); + return CKR_OK; +} + +#define sftk_Decrement(stateSize, len) \ + stateSize = ((stateSize) > (CK_ULONG)(len)) ? ((stateSize) - (CK_ULONG)(len)) : 0; + +/* NSC_SetOperationState restores the state of the cryptographic + * operation in a session. This is coded like it can restore lots of + * states, but it only works for truly flat cipher structures. */ +CK_RV +NSC_SetOperationState(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pOperationState, CK_ULONG ulOperationStateLen, + CK_OBJECT_HANDLE hEncryptionKey, CK_OBJECT_HANDLE hAuthenticationKey) +{ + SFTKSessionContext *context; + SFTKSession *session; + SFTKContextType type; + CK_MECHANISM mech; + CK_RV crv = CKR_OK; + + CHECK_FORK(); + + while (ulOperationStateLen != 0) { + /* get what type of state we're dealing with... */ + PORT_Memcpy(&type, pOperationState, sizeof(SFTKContextType)); + + /* fix up session contexts based on type */ + session = sftk_SessionFromHandle(hSession); + if (session == NULL) + return CKR_SESSION_HANDLE_INVALID; + context = sftk_ReturnContextByType(session, type); + sftk_SetContextByType(session, type, NULL); + if (context) { + sftk_FreeContext(context); + } + pOperationState += sizeof(SFTKContextType); + sftk_Decrement(ulOperationStateLen, sizeof(SFTKContextType)); + + /* get the mechanism structure */ + PORT_Memcpy(&mech.mechanism, pOperationState, sizeof(CK_MECHANISM_TYPE)); + pOperationState += sizeof(CK_MECHANISM_TYPE); + sftk_Decrement(ulOperationStateLen, sizeof(CK_MECHANISM_TYPE)); + /* should be filled in... but not necessary for hash */ + mech.pParameter = NULL; + mech.ulParameterLen = 0; + switch (type) { + case SFTK_HASH: + crv = NSC_DigestInit(hSession, &mech); + if (crv != CKR_OK) + break; + crv = sftk_GetContext(hSession, &context, SFTK_HASH, PR_TRUE, + NULL); + if (crv != CKR_OK) + break; + if (context->cipherInfoLen == 0) { + crv = CKR_SAVED_STATE_INVALID; + break; + } + PORT_Memcpy(context->cipherInfo, pOperationState, + context->cipherInfoLen); + pOperationState += context->cipherInfoLen; + sftk_Decrement(ulOperationStateLen, context->cipherInfoLen); + break; + default: + /* do sign/encrypt/decrypt later */ + crv = CKR_SAVED_STATE_INVALID; + } + sftk_FreeSession(session); + if (crv != CKR_OK) + break; + } + return crv; +} + +/* Dual-function cryptographic operations */ + +/* NSC_DigestEncryptUpdate continues a multiple-part digesting and encryption + * operation. */ +CK_RV +NSC_DigestEncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, + CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, + CK_ULONG_PTR pulEncryptedPartLen) +{ + CK_RV crv; + + CHECK_FORK(); + + crv = NSC_EncryptUpdate(hSession, pPart, ulPartLen, pEncryptedPart, + pulEncryptedPartLen); + if (crv != CKR_OK) + return crv; + crv = NSC_DigestUpdate(hSession, pPart, ulPartLen); + + return crv; +} + +/* NSC_DecryptDigestUpdate continues a multiple-part decryption and + * digesting operation. */ +CK_RV +NSC_DecryptDigestUpdate(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, + CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) +{ + CK_RV crv; + + CHECK_FORK(); + + crv = NSC_DecryptUpdate(hSession, pEncryptedPart, ulEncryptedPartLen, + pPart, pulPartLen); + if (crv != CKR_OK) + return crv; + crv = NSC_DigestUpdate(hSession, pPart, *pulPartLen); + + return crv; +} + +/* NSC_SignEncryptUpdate continues a multiple-part signing and + * encryption operation. */ +CK_RV +NSC_SignEncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, + CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, + CK_ULONG_PTR pulEncryptedPartLen) +{ + CK_RV crv; + + CHECK_FORK(); + + crv = NSC_EncryptUpdate(hSession, pPart, ulPartLen, pEncryptedPart, + pulEncryptedPartLen); + if (crv != CKR_OK) + return crv; + crv = NSC_SignUpdate(hSession, pPart, ulPartLen); + + return crv; +} + +/* NSC_DecryptVerifyUpdate continues a multiple-part decryption + * and verify operation. */ +CK_RV +NSC_DecryptVerifyUpdate(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, + CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) +{ + CK_RV crv; + + CHECK_FORK(); + + crv = NSC_DecryptUpdate(hSession, pEncryptedData, ulEncryptedDataLen, + pData, pulDataLen); + if (crv != CKR_OK) + return crv; + crv = NSC_VerifyUpdate(hSession, pData, *pulDataLen); + + return crv; +} + +/* NSC_DigestKey continues a multi-part message-digesting operation, + * by digesting the value of a secret key as part of the data already digested. + */ +CK_RV +NSC_DigestKey(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hKey) +{ + SFTKSession *session = NULL; + SFTKObject *key = NULL; + SFTKAttribute *att; + CK_RV crv; + + CHECK_FORK(); + + session = sftk_SessionFromHandle(hSession); + if (session == NULL) + return CKR_SESSION_HANDLE_INVALID; + + key = sftk_ObjectFromHandle(hKey, session); + sftk_FreeSession(session); + if (key == NULL) + return CKR_KEY_HANDLE_INVALID; + + /* PUT ANY DIGEST KEY RESTRICTION CHECKS HERE */ + + /* make sure it's a valid key for this operation */ + if (key->objclass != CKO_SECRET_KEY) { + sftk_FreeObject(key); + return CKR_KEY_TYPE_INCONSISTENT; + } + /* get the key value */ + att = sftk_FindAttribute(key, CKA_VALUE); + sftk_FreeObject(key); + if (!att) { + return CKR_KEY_HANDLE_INVALID; + } + crv = NSC_DigestUpdate(hSession, (CK_BYTE_PTR)att->attrib.pValue, + att->attrib.ulValueLen); + sftk_FreeAttribute(att); + return crv; +} |