michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: /* michael@0: * This file implements PKCS 11 on top of our existing security modules michael@0: * michael@0: * For more information about PKCS 11 See PKCS 11 Token Inteface Standard. michael@0: * This implementation has two slots: michael@0: * slot 1 is our generic crypto support. It does not require login. michael@0: * It supports Public Key ops, and all they bulk ciphers and hashes. michael@0: * It can also support Private Key ops for imported Private keys. It does michael@0: * not have any token storage. michael@0: * slot 2 is our private key support. It requires a login before use. It michael@0: * can store Private Keys and Certs as token objects. Currently only private michael@0: * keys and their associated Certificates are saved on the token. michael@0: * michael@0: * In this implementation, session objects are only visible to the session michael@0: * that created or generated them. michael@0: */ michael@0: #include "seccomon.h" michael@0: #include "secitem.h" michael@0: #include "secport.h" michael@0: #include "blapi.h" michael@0: #include "pkcs11.h" michael@0: #include "pkcs11i.h" michael@0: #include "pkcs1sig.h" michael@0: #include "lowkeyi.h" michael@0: #include "secder.h" michael@0: #include "secdig.h" michael@0: #include "lowpbe.h" /* We do PBE below */ michael@0: #include "pkcs11t.h" michael@0: #include "secoid.h" michael@0: #include "alghmac.h" michael@0: #include "softoken.h" michael@0: #include "secasn1.h" michael@0: #include "secerr.h" michael@0: michael@0: #include "prprf.h" michael@0: michael@0: #define __PASTE(x,y) x##y michael@0: michael@0: /* michael@0: * we renamed all our internal functions, get the correct michael@0: * definitions for them... michael@0: */ michael@0: #undef CK_PKCS11_FUNCTION_INFO michael@0: #undef CK_NEED_ARG_LIST michael@0: michael@0: #define CK_EXTERN extern michael@0: #define CK_PKCS11_FUNCTION_INFO(func) \ michael@0: CK_RV __PASTE(NS,func) michael@0: #define CK_NEED_ARG_LIST 1 michael@0: michael@0: #include "pkcs11f.h" michael@0: michael@0: typedef struct { michael@0: PRUint8 client_version[2]; michael@0: PRUint8 random[46]; michael@0: } SSL3RSAPreMasterSecret; michael@0: michael@0: static void sftk_Null(void *data, PRBool freeit) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: #ifndef NSS_DISABLE_ECC michael@0: #ifdef EC_DEBUG michael@0: #define SEC_PRINT(str1, str2, num, sitem) \ michael@0: printf("pkcs11c.c:%s:%s (keytype=%d) [len=%d]\n", \ michael@0: str1, str2, num, sitem->len); \ michael@0: for (i = 0; i < sitem->len; i++) { \ michael@0: printf("%02x:", sitem->data[i]); \ michael@0: } \ michael@0: printf("\n") michael@0: #else michael@0: #define SEC_PRINT(a, b, c, d) michael@0: #endif michael@0: #endif /* NSS_DISABLE_ECC */ michael@0: michael@0: /* michael@0: * free routines.... Free local type allocated data, and convert michael@0: * other free routines to the destroy signature. michael@0: */ michael@0: static void michael@0: sftk_FreePrivKey(NSSLOWKEYPrivateKey *key, PRBool freeit) michael@0: { michael@0: nsslowkey_DestroyPrivateKey(key); michael@0: } michael@0: michael@0: static void michael@0: sftk_Space(void *data, PRBool freeit) michael@0: { michael@0: PORT_Free(data); michael@0: } michael@0: michael@0: /* michael@0: * map all the SEC_ERROR_xxx error codes that may be returned by freebl michael@0: * functions to CKR_xxx. return CKR_DEVICE_ERROR by default for backward michael@0: * compatibility. michael@0: */ michael@0: static CK_RV michael@0: sftk_MapCryptError(int error) michael@0: { michael@0: switch (error) { michael@0: case SEC_ERROR_INVALID_ARGS: michael@0: case SEC_ERROR_BAD_DATA: /* MP_RANGE gets mapped to this */ michael@0: return CKR_ARGUMENTS_BAD; michael@0: case SEC_ERROR_INPUT_LEN: michael@0: return CKR_DATA_LEN_RANGE; michael@0: case SEC_ERROR_OUTPUT_LEN: michael@0: return CKR_BUFFER_TOO_SMALL; michael@0: case SEC_ERROR_LIBRARY_FAILURE: michael@0: return CKR_GENERAL_ERROR; michael@0: case SEC_ERROR_NO_MEMORY: michael@0: return CKR_HOST_MEMORY; michael@0: case SEC_ERROR_BAD_SIGNATURE: michael@0: return CKR_SIGNATURE_INVALID; michael@0: case SEC_ERROR_INVALID_KEY: michael@0: return CKR_KEY_SIZE_RANGE; michael@0: case SEC_ERROR_BAD_KEY: /* an EC public key that fails validation */ michael@0: return CKR_KEY_SIZE_RANGE; /* the closest error code */ michael@0: case SEC_ERROR_UNSUPPORTED_EC_POINT_FORM: michael@0: return CKR_TEMPLATE_INCONSISTENT; michael@0: /* EC functions set this error if NSS_DISABLE_ECC is defined */ michael@0: case SEC_ERROR_UNSUPPORTED_KEYALG: michael@0: return CKR_MECHANISM_INVALID; michael@0: case SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE: michael@0: return CKR_DOMAIN_PARAMS_INVALID; michael@0: /* key pair generation failed after max number of attempts */ michael@0: case SEC_ERROR_NEED_RANDOM: michael@0: return CKR_FUNCTION_FAILED; michael@0: } michael@0: return CKR_DEVICE_ERROR; michael@0: } michael@0: michael@0: /* used by Decrypt and UnwrapKey (indirectly) */ michael@0: static CK_RV michael@0: sftk_MapDecryptError(int error) michael@0: { michael@0: switch (error) { michael@0: case SEC_ERROR_BAD_DATA: michael@0: return CKR_ENCRYPTED_DATA_INVALID; michael@0: default: michael@0: return sftk_MapCryptError(error); michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * return CKR_SIGNATURE_INVALID instead of CKR_DEVICE_ERROR by default for michael@0: * backward compatibilty. michael@0: */ michael@0: static CK_RV michael@0: sftk_MapVerifyError(int error) michael@0: { michael@0: CK_RV crv = sftk_MapCryptError(error); michael@0: if (crv == CKR_DEVICE_ERROR) michael@0: crv = CKR_SIGNATURE_INVALID; michael@0: return crv; michael@0: } michael@0: michael@0: michael@0: /* michael@0: * turn a CDMF key into a des key. CDMF is an old IBM scheme to export DES by michael@0: * Deprecating a full des key to 40 bit key strenth. michael@0: */ michael@0: static CK_RV michael@0: sftk_cdmf2des(unsigned char *cdmfkey, unsigned char *deskey) michael@0: { michael@0: unsigned char key1[8] = { 0xc4, 0x08, 0xb0, 0x54, 0x0b, 0xa1, 0xe0, 0xae }; michael@0: unsigned char key2[8] = { 0xef, 0x2c, 0x04, 0x1c, 0xe6, 0x38, 0x2f, 0xe6 }; michael@0: unsigned char enc_src[8]; michael@0: unsigned char enc_dest[8]; michael@0: unsigned int leng,i; michael@0: DESContext *descx; michael@0: SECStatus rv; michael@0: michael@0: michael@0: /* zero the parity bits */ michael@0: for (i=0; i < 8; i++) { michael@0: enc_src[i] = cdmfkey[i] & 0xfe; michael@0: } michael@0: michael@0: /* encrypt with key 1 */ michael@0: descx = DES_CreateContext(key1, NULL, NSS_DES, PR_TRUE); michael@0: if (descx == NULL) return CKR_HOST_MEMORY; michael@0: rv = DES_Encrypt(descx, enc_dest, &leng, 8, enc_src, 8); michael@0: DES_DestroyContext(descx,PR_TRUE); michael@0: if (rv != SECSuccess) return sftk_MapCryptError(PORT_GetError()); michael@0: michael@0: /* xor source with des, zero the parity bits and deprecate the key*/ michael@0: for (i=0; i < 8; i++) { michael@0: if (i & 1) { michael@0: enc_src[i] = (enc_src[i] ^ enc_dest[i]) & 0xfe; michael@0: } else { michael@0: enc_src[i] = (enc_src[i] ^ enc_dest[i]) & 0x0e; michael@0: } michael@0: } michael@0: michael@0: /* encrypt with key 2 */ michael@0: descx = DES_CreateContext(key2, NULL, NSS_DES, PR_TRUE); michael@0: if (descx == NULL) return CKR_HOST_MEMORY; michael@0: rv = DES_Encrypt(descx, deskey, &leng, 8, enc_src, 8); michael@0: DES_DestroyContext(descx,PR_TRUE); michael@0: if (rv != SECSuccess) return sftk_MapCryptError(PORT_GetError()); michael@0: michael@0: /* set the corret parity on our new des key */ michael@0: sftk_FormatDESKey(deskey, 8); michael@0: return CKR_OK; michael@0: } michael@0: michael@0: michael@0: /* NSC_DestroyObject destroys an object. */ michael@0: CK_RV michael@0: NSC_DestroyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject) michael@0: { michael@0: SFTKSlot *slot = sftk_SlotFromSessionHandle(hSession); michael@0: SFTKSession *session; michael@0: SFTKObject *object; michael@0: SFTKFreeStatus status; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: if (slot == NULL) { michael@0: return CKR_SESSION_HANDLE_INVALID; michael@0: } michael@0: /* michael@0: * This whole block just makes sure we really can destroy the michael@0: * requested object. michael@0: */ michael@0: session = sftk_SessionFromHandle(hSession); michael@0: if (session == NULL) { michael@0: return CKR_SESSION_HANDLE_INVALID; michael@0: } michael@0: michael@0: object = sftk_ObjectFromHandle(hObject,session); michael@0: if (object == NULL) { michael@0: sftk_FreeSession(session); michael@0: return CKR_OBJECT_HANDLE_INVALID; michael@0: } michael@0: michael@0: /* don't destroy a private object if we aren't logged in */ michael@0: if ((!slot->isLoggedIn) && (slot->needLogin) && michael@0: (sftk_isTrue(object,CKA_PRIVATE))) { michael@0: sftk_FreeSession(session); michael@0: sftk_FreeObject(object); michael@0: return CKR_USER_NOT_LOGGED_IN; michael@0: } michael@0: michael@0: /* don't destroy a token object if we aren't in a rw session */ michael@0: michael@0: if (((session->info.flags & CKF_RW_SESSION) == 0) && michael@0: (sftk_isTrue(object,CKA_TOKEN))) { michael@0: sftk_FreeSession(session); michael@0: sftk_FreeObject(object); michael@0: return CKR_SESSION_READ_ONLY; michael@0: } michael@0: michael@0: sftk_DeleteObject(session,object); michael@0: michael@0: sftk_FreeSession(session); michael@0: michael@0: /* michael@0: * get some indication if the object is destroyed. Note: this is not michael@0: * 100%. Someone may have an object reference outstanding (though that michael@0: * should not be the case by here. Also note that the object is "half" michael@0: * destroyed. Our internal representation is destroyed, but it may still michael@0: * be in the data base. michael@0: */ michael@0: status = sftk_FreeObject(object); michael@0: michael@0: return (status != SFTK_DestroyFailure) ? CKR_OK : CKR_DEVICE_ERROR; michael@0: } michael@0: michael@0: michael@0: /* michael@0: ************** Crypto Functions: Utilities ************************ michael@0: */ michael@0: /* michael@0: * Utility function for converting PSS/OAEP parameter types into michael@0: * HASH_HashTypes. Note: Only SHA family functions are defined in RFC 3447. michael@0: */ michael@0: static HASH_HashType michael@0: GetHashTypeFromMechanism(CK_MECHANISM_TYPE mech) michael@0: { michael@0: switch (mech) { michael@0: case CKM_SHA_1: michael@0: case CKG_MGF1_SHA1: michael@0: return HASH_AlgSHA1; michael@0: case CKM_SHA224: michael@0: case CKG_MGF1_SHA224: michael@0: return HASH_AlgSHA224; michael@0: case CKM_SHA256: michael@0: case CKG_MGF1_SHA256: michael@0: return HASH_AlgSHA256; michael@0: case CKM_SHA384: michael@0: case CKG_MGF1_SHA384: michael@0: return HASH_AlgSHA384; michael@0: case CKM_SHA512: michael@0: case CKG_MGF1_SHA512: michael@0: return HASH_AlgSHA512; michael@0: default: michael@0: return HASH_AlgNULL; michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * Returns true if "params" contains a valid set of PSS parameters michael@0: */ michael@0: static PRBool michael@0: sftk_ValidatePssParams(const CK_RSA_PKCS_PSS_PARAMS *params) michael@0: { michael@0: if (!params) { michael@0: return PR_FALSE; michael@0: } michael@0: if (GetHashTypeFromMechanism(params->hashAlg) == HASH_AlgNULL || michael@0: GetHashTypeFromMechanism(params->mgf) == HASH_AlgNULL) { michael@0: return PR_FALSE; michael@0: } michael@0: return PR_TRUE; michael@0: } michael@0: michael@0: /* michael@0: * Returns true if "params" contains a valid set of OAEP parameters michael@0: */ michael@0: static PRBool michael@0: sftk_ValidateOaepParams(const CK_RSA_PKCS_OAEP_PARAMS *params) michael@0: { michael@0: if (!params) { michael@0: return PR_FALSE; michael@0: } michael@0: /* The requirements of ulSourceLen/pSourceData come from PKCS #11, which michael@0: * state: michael@0: * If the parameter is empty, pSourceData must be NULL and michael@0: * ulSourceDataLen must be zero. michael@0: */ michael@0: if (params->source != CKZ_DATA_SPECIFIED || michael@0: (GetHashTypeFromMechanism(params->hashAlg) == HASH_AlgNULL) || michael@0: (GetHashTypeFromMechanism(params->mgf) == HASH_AlgNULL) || michael@0: (params->ulSourceDataLen == 0 && params->pSourceData != NULL) || michael@0: (params->ulSourceDataLen != 0 && params->pSourceData == NULL)) { michael@0: return PR_FALSE; michael@0: } michael@0: return PR_TRUE; michael@0: } michael@0: michael@0: /* michael@0: * return a context based on the SFTKContext type. michael@0: */ michael@0: SFTKSessionContext * michael@0: sftk_ReturnContextByType(SFTKSession *session, SFTKContextType type) michael@0: { michael@0: switch (type) { michael@0: case SFTK_ENCRYPT: michael@0: case SFTK_DECRYPT: michael@0: return session->enc_context; michael@0: case SFTK_HASH: michael@0: return session->hash_context; michael@0: case SFTK_SIGN: michael@0: case SFTK_SIGN_RECOVER: michael@0: case SFTK_VERIFY: michael@0: case SFTK_VERIFY_RECOVER: michael@0: return session->hash_context; michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: /* michael@0: * change a context based on the SFTKContext type. michael@0: */ michael@0: void michael@0: sftk_SetContextByType(SFTKSession *session, SFTKContextType type, michael@0: SFTKSessionContext *context) michael@0: { michael@0: switch (type) { michael@0: case SFTK_ENCRYPT: michael@0: case SFTK_DECRYPT: michael@0: session->enc_context = context; michael@0: break; michael@0: case SFTK_HASH: michael@0: session->hash_context = context; michael@0: break; michael@0: case SFTK_SIGN: michael@0: case SFTK_SIGN_RECOVER: michael@0: case SFTK_VERIFY: michael@0: case SFTK_VERIFY_RECOVER: michael@0: session->hash_context = context; michael@0: break; michael@0: } michael@0: return; michael@0: } michael@0: michael@0: /* michael@0: * code to grab the context. Needed by every C_XXXUpdate, C_XXXFinal, michael@0: * and C_XXX function. The function takes a session handle, the context type, michael@0: * and wether or not the session needs to be multipart. It returns the context, michael@0: * and optionally returns the session pointer (if sessionPtr != NULL) if session michael@0: * pointer is returned, the caller is responsible for freeing it. michael@0: */ michael@0: static CK_RV michael@0: sftk_GetContext(CK_SESSION_HANDLE handle,SFTKSessionContext **contextPtr, michael@0: SFTKContextType type, PRBool needMulti, SFTKSession **sessionPtr) michael@0: { michael@0: SFTKSession *session; michael@0: SFTKSessionContext *context; michael@0: michael@0: session = sftk_SessionFromHandle(handle); michael@0: if (session == NULL) return CKR_SESSION_HANDLE_INVALID; michael@0: context = sftk_ReturnContextByType(session,type); michael@0: /* make sure the context is valid */ michael@0: if((context==NULL)||(context->type!=type)||(needMulti&&!(context->multi))){ michael@0: sftk_FreeSession(session); michael@0: return CKR_OPERATION_NOT_INITIALIZED; michael@0: } michael@0: *contextPtr = context; michael@0: if (sessionPtr != NULL) { michael@0: *sessionPtr = session; michael@0: } else { michael@0: sftk_FreeSession(session); michael@0: } michael@0: return CKR_OK; michael@0: } michael@0: michael@0: /** Terminate operation (in the PKCS#11 spec sense). michael@0: * Intuitive name for FreeContext/SetNullContext pair. michael@0: */ michael@0: static void michael@0: sftk_TerminateOp( SFTKSession *session, SFTKContextType ctype, michael@0: SFTKSessionContext *context ) michael@0: { michael@0: sftk_FreeContext( context ); michael@0: sftk_SetContextByType( session, ctype, NULL ); michael@0: } michael@0: michael@0: /* michael@0: ************** Crypto Functions: Encrypt ************************ michael@0: */ michael@0: michael@0: /* michael@0: * All the NSC_InitXXX functions have a set of common checks and processing they michael@0: * all need to do at the beginning. This is done here. michael@0: */ michael@0: static CK_RV michael@0: sftk_InitGeneric(SFTKSession *session,SFTKSessionContext **contextPtr, michael@0: SFTKContextType ctype,SFTKObject **keyPtr, michael@0: CK_OBJECT_HANDLE hKey, CK_KEY_TYPE *keyTypePtr, michael@0: CK_OBJECT_CLASS pubKeyType, CK_ATTRIBUTE_TYPE operation) michael@0: { michael@0: SFTKObject *key = NULL; michael@0: SFTKAttribute *att; michael@0: SFTKSessionContext *context; michael@0: michael@0: /* We can only init if there is not current context active */ michael@0: if (sftk_ReturnContextByType(session,ctype) != NULL) { michael@0: return CKR_OPERATION_ACTIVE; michael@0: } michael@0: michael@0: /* find the key */ michael@0: if (keyPtr) { michael@0: key = sftk_ObjectFromHandle(hKey,session); michael@0: if (key == NULL) { michael@0: return CKR_KEY_HANDLE_INVALID; michael@0: } michael@0: michael@0: /* make sure it's a valid key for this operation */ michael@0: if (((key->objclass != CKO_SECRET_KEY) && (key->objclass != pubKeyType)) michael@0: || !sftk_isTrue(key,operation)) { michael@0: sftk_FreeObject(key); michael@0: return CKR_KEY_TYPE_INCONSISTENT; michael@0: } michael@0: /* get the key type */ michael@0: att = sftk_FindAttribute(key,CKA_KEY_TYPE); michael@0: if (att == NULL) { michael@0: sftk_FreeObject(key); michael@0: return CKR_KEY_TYPE_INCONSISTENT; michael@0: } michael@0: PORT_Assert(att->attrib.ulValueLen == sizeof(CK_KEY_TYPE)); michael@0: if (att->attrib.ulValueLen != sizeof(CK_KEY_TYPE)) { michael@0: sftk_FreeAttribute(att); michael@0: sftk_FreeObject(key); michael@0: return CKR_ATTRIBUTE_VALUE_INVALID; michael@0: } michael@0: PORT_Memcpy(keyTypePtr, att->attrib.pValue, sizeof(CK_KEY_TYPE)); michael@0: sftk_FreeAttribute(att); michael@0: *keyPtr = key; michael@0: } michael@0: michael@0: /* allocate the context structure */ michael@0: context = (SFTKSessionContext *)PORT_Alloc(sizeof(SFTKSessionContext)); michael@0: if (context == NULL) { michael@0: if (key) sftk_FreeObject(key); michael@0: return CKR_HOST_MEMORY; michael@0: } michael@0: context->type = ctype; michael@0: context->multi = PR_TRUE; michael@0: context->rsa = PR_FALSE; michael@0: context->cipherInfo = NULL; michael@0: context->hashInfo = NULL; michael@0: context->doPad = PR_FALSE; michael@0: context->padDataLength = 0; michael@0: context->key = key; michael@0: context->blockSize = 0; michael@0: context->maxLen = 0; michael@0: michael@0: *contextPtr = context; michael@0: return CKR_OK; michael@0: } michael@0: michael@0: static int michael@0: sftk_aes_mode(CK_MECHANISM_TYPE mechanism) michael@0: { michael@0: switch (mechanism) { michael@0: case CKM_AES_CBC_PAD: michael@0: case CKM_AES_CBC: michael@0: return NSS_AES_CBC; michael@0: case CKM_AES_ECB: michael@0: return NSS_AES; michael@0: case CKM_AES_CTS: michael@0: return NSS_AES_CTS; michael@0: case CKM_AES_CTR: michael@0: return NSS_AES_CTR; michael@0: case CKM_AES_GCM: michael@0: return NSS_AES_GCM; michael@0: } michael@0: return -1; michael@0: } michael@0: michael@0: static SECStatus michael@0: sftk_RSAEncryptRaw(NSSLOWKEYPublicKey *key, unsigned char *output, michael@0: unsigned int *outputLen, unsigned int maxLen, michael@0: const unsigned char *input, unsigned int inputLen) michael@0: { michael@0: SECStatus rv = SECFailure; michael@0: michael@0: PORT_Assert(key->keyType == NSSLOWKEYRSAKey); michael@0: if (key->keyType != NSSLOWKEYRSAKey) { michael@0: PORT_SetError(SEC_ERROR_INVALID_KEY); michael@0: return SECFailure; michael@0: } michael@0: michael@0: rv = RSA_EncryptRaw(&key->u.rsa, output, outputLen, maxLen, input, michael@0: inputLen); michael@0: if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { michael@0: sftk_fatalError = PR_TRUE; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: static SECStatus michael@0: sftk_RSADecryptRaw(NSSLOWKEYPrivateKey *key, unsigned char *output, michael@0: unsigned int *outputLen, unsigned int maxLen, michael@0: const unsigned char *input, unsigned int inputLen) michael@0: { michael@0: SECStatus rv = SECFailure; michael@0: michael@0: PORT_Assert(key->keyType == NSSLOWKEYRSAKey); michael@0: if (key->keyType != NSSLOWKEYRSAKey) { michael@0: PORT_SetError(SEC_ERROR_INVALID_KEY); michael@0: return SECFailure; michael@0: } michael@0: michael@0: rv = RSA_DecryptRaw(&key->u.rsa, output, outputLen, maxLen, input, michael@0: inputLen); michael@0: if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { michael@0: sftk_fatalError = PR_TRUE; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: static SECStatus michael@0: sftk_RSAEncrypt(NSSLOWKEYPublicKey *key, unsigned char *output, michael@0: unsigned int *outputLen, unsigned int maxLen, michael@0: const unsigned char *input, unsigned int inputLen) michael@0: { michael@0: SECStatus rv = SECFailure; michael@0: michael@0: PORT_Assert(key->keyType == NSSLOWKEYRSAKey); michael@0: if (key->keyType != NSSLOWKEYRSAKey) { michael@0: PORT_SetError(SEC_ERROR_INVALID_KEY); michael@0: return SECFailure; michael@0: } michael@0: michael@0: rv = RSA_EncryptBlock(&key->u.rsa, output, outputLen, maxLen, input, michael@0: inputLen); michael@0: if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { michael@0: sftk_fatalError = PR_TRUE; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: static SECStatus michael@0: sftk_RSADecrypt(NSSLOWKEYPrivateKey *key, unsigned char *output, michael@0: unsigned int *outputLen, unsigned int maxLen, michael@0: const unsigned char *input, unsigned int inputLen) michael@0: { michael@0: SECStatus rv = SECFailure; michael@0: michael@0: PORT_Assert(key->keyType == NSSLOWKEYRSAKey); michael@0: if (key->keyType != NSSLOWKEYRSAKey) { michael@0: PORT_SetError(SEC_ERROR_INVALID_KEY); michael@0: return SECFailure; michael@0: } michael@0: michael@0: rv = RSA_DecryptBlock(&key->u.rsa, output, outputLen, maxLen, input, michael@0: inputLen); michael@0: if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { michael@0: sftk_fatalError = PR_TRUE; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: static SECStatus michael@0: sftk_RSAEncryptOAEP(SFTKOAEPEncryptInfo *info, unsigned char *output, michael@0: unsigned int *outputLen, unsigned int maxLen, michael@0: const unsigned char *input, unsigned int inputLen) michael@0: { michael@0: HASH_HashType hashAlg; michael@0: HASH_HashType maskHashAlg; michael@0: michael@0: PORT_Assert(info->key->keyType == NSSLOWKEYRSAKey); michael@0: if (info->key->keyType != NSSLOWKEYRSAKey) { michael@0: PORT_SetError(SEC_ERROR_INVALID_KEY); michael@0: return SECFailure; michael@0: } michael@0: michael@0: hashAlg = GetHashTypeFromMechanism(info->params->hashAlg); michael@0: maskHashAlg = GetHashTypeFromMechanism(info->params->mgf); michael@0: michael@0: return RSA_EncryptOAEP(&info->key->u.rsa, hashAlg, maskHashAlg, michael@0: (const unsigned char*)info->params->pSourceData, michael@0: info->params->ulSourceDataLen, NULL, 0, michael@0: output, outputLen, maxLen, input, inputLen); michael@0: } michael@0: michael@0: static SECStatus michael@0: sftk_RSADecryptOAEP(SFTKOAEPDecryptInfo *info, unsigned char *output, michael@0: unsigned int *outputLen, unsigned int maxLen, michael@0: const unsigned char *input, unsigned int inputLen) michael@0: { michael@0: SECStatus rv = SECFailure; michael@0: HASH_HashType hashAlg; michael@0: HASH_HashType maskHashAlg; michael@0: michael@0: PORT_Assert(info->key->keyType == NSSLOWKEYRSAKey); michael@0: if (info->key->keyType != NSSLOWKEYRSAKey) { michael@0: PORT_SetError(SEC_ERROR_INVALID_KEY); michael@0: return SECFailure; michael@0: } michael@0: michael@0: hashAlg = GetHashTypeFromMechanism(info->params->hashAlg); michael@0: maskHashAlg = GetHashTypeFromMechanism(info->params->mgf); michael@0: michael@0: rv = RSA_DecryptOAEP(&info->key->u.rsa, hashAlg, maskHashAlg, michael@0: (const unsigned char*)info->params->pSourceData, michael@0: info->params->ulSourceDataLen, michael@0: output, outputLen, maxLen, input, inputLen); michael@0: if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { michael@0: sftk_fatalError = PR_TRUE; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: /** NSC_CryptInit initializes an encryption/Decryption operation. michael@0: * michael@0: * Always called by NSC_EncryptInit, NSC_DecryptInit, NSC_WrapKey,NSC_UnwrapKey. michael@0: * Called by NSC_SignInit, NSC_VerifyInit (via sftk_InitCBCMac) only for block michael@0: * ciphers MAC'ing. michael@0: */ michael@0: static CK_RV michael@0: sftk_CryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, michael@0: CK_OBJECT_HANDLE hKey, michael@0: CK_ATTRIBUTE_TYPE mechUsage, CK_ATTRIBUTE_TYPE keyUsage, michael@0: SFTKContextType contextType, PRBool isEncrypt) michael@0: { michael@0: SFTKSession *session; michael@0: SFTKObject *key; michael@0: SFTKSessionContext *context; michael@0: SFTKAttribute *att; michael@0: CK_RC2_CBC_PARAMS *rc2_param; michael@0: #if NSS_SOFTOKEN_DOES_RC5 michael@0: CK_RC5_CBC_PARAMS *rc5_param; michael@0: SECItem rc5Key; michael@0: #endif michael@0: CK_KEY_TYPE key_type; michael@0: CK_RV crv = CKR_OK; michael@0: unsigned effectiveKeyLength; michael@0: unsigned char newdeskey[24]; michael@0: PRBool useNewKey=PR_FALSE; michael@0: int t; michael@0: michael@0: crv = sftk_MechAllowsOperation(pMechanism->mechanism, mechUsage ); michael@0: if (crv != CKR_OK) michael@0: return crv; michael@0: michael@0: session = sftk_SessionFromHandle(hSession); michael@0: if (session == NULL) return CKR_SESSION_HANDLE_INVALID; michael@0: michael@0: crv = sftk_InitGeneric(session,&context,contextType,&key,hKey,&key_type, michael@0: isEncrypt ?CKO_PUBLIC_KEY:CKO_PRIVATE_KEY, keyUsage); michael@0: michael@0: if (crv != CKR_OK) { michael@0: sftk_FreeSession(session); michael@0: return crv; michael@0: } michael@0: michael@0: context->doPad = PR_FALSE; michael@0: switch(pMechanism->mechanism) { michael@0: case CKM_RSA_PKCS: michael@0: case CKM_RSA_X_509: michael@0: if (key_type != CKK_RSA) { michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: context->multi = PR_FALSE; michael@0: context->rsa = PR_TRUE; michael@0: if (isEncrypt) { michael@0: NSSLOWKEYPublicKey *pubKey = sftk_GetPubKey(key,CKK_RSA,&crv); michael@0: if (pubKey == NULL) { michael@0: crv = CKR_KEY_HANDLE_INVALID; michael@0: break; michael@0: } michael@0: context->maxLen = nsslowkey_PublicModulusLen(pubKey); michael@0: context->cipherInfo = (void *)pubKey; michael@0: context->update = (SFTKCipher) michael@0: (pMechanism->mechanism == CKM_RSA_X_509 michael@0: ? sftk_RSAEncryptRaw : sftk_RSAEncrypt); michael@0: } else { michael@0: NSSLOWKEYPrivateKey *privKey = sftk_GetPrivKey(key,CKK_RSA,&crv); michael@0: if (privKey == NULL) { michael@0: crv = CKR_KEY_HANDLE_INVALID; michael@0: break; michael@0: } michael@0: context->maxLen = nsslowkey_PrivateModulusLen(privKey); michael@0: context->cipherInfo = (void *)privKey; michael@0: context->update = (SFTKCipher) michael@0: (pMechanism->mechanism == CKM_RSA_X_509 michael@0: ? sftk_RSADecryptRaw : sftk_RSADecrypt); michael@0: } michael@0: context->destroy = sftk_Null; michael@0: break; michael@0: case CKM_RSA_PKCS_OAEP: michael@0: if (key_type != CKK_RSA) { michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: if (pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_OAEP_PARAMS) || michael@0: !sftk_ValidateOaepParams((CK_RSA_PKCS_OAEP_PARAMS*)pMechanism->pParameter)) { michael@0: crv = CKR_MECHANISM_PARAM_INVALID; michael@0: break; michael@0: } michael@0: context->multi = PR_FALSE; michael@0: context->rsa = PR_TRUE; michael@0: if (isEncrypt) { michael@0: SFTKOAEPEncryptInfo *info = PORT_New(SFTKOAEPEncryptInfo); michael@0: if (info == NULL) { michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: info->params = pMechanism->pParameter; michael@0: info->key = sftk_GetPubKey(key, CKK_RSA, &crv); michael@0: if (info->key == NULL) { michael@0: PORT_Free(info); michael@0: crv = CKR_KEY_HANDLE_INVALID; michael@0: break; michael@0: } michael@0: context->update = (SFTKCipher) sftk_RSAEncryptOAEP; michael@0: context->maxLen = nsslowkey_PublicModulusLen(info->key); michael@0: context->cipherInfo = info; michael@0: } else { michael@0: SFTKOAEPDecryptInfo *info = PORT_New(SFTKOAEPDecryptInfo); michael@0: if (info == NULL) { michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: info->params = pMechanism->pParameter; michael@0: info->key = sftk_GetPrivKey(key, CKK_RSA, &crv); michael@0: if (info->key == NULL) { michael@0: PORT_Free(info); michael@0: crv = CKR_KEY_HANDLE_INVALID; michael@0: break; michael@0: } michael@0: context->update = (SFTKCipher) sftk_RSADecryptOAEP; michael@0: context->maxLen = nsslowkey_PrivateModulusLen(info->key); michael@0: context->cipherInfo = info; michael@0: } michael@0: context->destroy = (SFTKDestroy) sftk_Space; michael@0: break; michael@0: case CKM_RC2_CBC_PAD: michael@0: context->doPad = PR_TRUE; michael@0: /* fall thru */ michael@0: case CKM_RC2_ECB: michael@0: case CKM_RC2_CBC: michael@0: context->blockSize = 8; michael@0: if (key_type != CKK_RC2) { michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: att = sftk_FindAttribute(key,CKA_VALUE); michael@0: if (att == NULL) { michael@0: crv = CKR_KEY_HANDLE_INVALID; michael@0: break; michael@0: } michael@0: rc2_param = (CK_RC2_CBC_PARAMS *)pMechanism->pParameter; michael@0: effectiveKeyLength = (rc2_param->ulEffectiveBits+7)/8; michael@0: context->cipherInfo = michael@0: RC2_CreateContext((unsigned char*)att->attrib.pValue, michael@0: att->attrib.ulValueLen, rc2_param->iv, michael@0: pMechanism->mechanism == CKM_RC2_ECB ? NSS_RC2 : michael@0: NSS_RC2_CBC,effectiveKeyLength); michael@0: sftk_FreeAttribute(att); michael@0: if (context->cipherInfo == NULL) { michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: context->update = (SFTKCipher) (isEncrypt ? RC2_Encrypt : RC2_Decrypt); michael@0: context->destroy = (SFTKDestroy) RC2_DestroyContext; michael@0: break; michael@0: #if NSS_SOFTOKEN_DOES_RC5 michael@0: case CKM_RC5_CBC_PAD: michael@0: context->doPad = PR_TRUE; michael@0: /* fall thru */ michael@0: case CKM_RC5_ECB: michael@0: case CKM_RC5_CBC: michael@0: if (key_type != CKK_RC5) { michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: att = sftk_FindAttribute(key,CKA_VALUE); michael@0: if (att == NULL) { michael@0: crv = CKR_KEY_HANDLE_INVALID; michael@0: break; michael@0: } michael@0: rc5_param = (CK_RC5_CBC_PARAMS *)pMechanism->pParameter; michael@0: context->blockSize = rc5_param->ulWordsize*2; michael@0: rc5Key.data = (unsigned char*)att->attrib.pValue; michael@0: rc5Key.len = att->attrib.ulValueLen; michael@0: context->cipherInfo = RC5_CreateContext(&rc5Key,rc5_param->ulRounds, michael@0: rc5_param->ulWordsize,rc5_param->pIv, michael@0: pMechanism->mechanism == CKM_RC5_ECB ? NSS_RC5 : NSS_RC5_CBC); michael@0: sftk_FreeAttribute(att); michael@0: if (context->cipherInfo == NULL) { michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: context->update = (SFTKCipher) (isEncrypt ? RC5_Encrypt : RC5_Decrypt); michael@0: context->destroy = (SFTKDestroy) RC5_DestroyContext; michael@0: break; michael@0: #endif michael@0: case CKM_RC4: michael@0: if (key_type != CKK_RC4) { michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: att = sftk_FindAttribute(key,CKA_VALUE); michael@0: if (att == NULL) { michael@0: crv = CKR_KEY_HANDLE_INVALID; michael@0: break; michael@0: } michael@0: context->cipherInfo = michael@0: RC4_CreateContext((unsigned char*)att->attrib.pValue, michael@0: att->attrib.ulValueLen); michael@0: sftk_FreeAttribute(att); michael@0: if (context->cipherInfo == NULL) { michael@0: crv = CKR_HOST_MEMORY; /* WRONG !!! */ michael@0: break; michael@0: } michael@0: context->update = (SFTKCipher) (isEncrypt ? RC4_Encrypt : RC4_Decrypt); michael@0: context->destroy = (SFTKDestroy) RC4_DestroyContext; michael@0: break; michael@0: case CKM_CDMF_CBC_PAD: michael@0: context->doPad = PR_TRUE; michael@0: /* fall thru */ michael@0: case CKM_CDMF_ECB: michael@0: case CKM_CDMF_CBC: michael@0: if (key_type != CKK_CDMF) { michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: t = (pMechanism->mechanism == CKM_CDMF_ECB) ? NSS_DES : NSS_DES_CBC; michael@0: if (crv != CKR_OK) break; michael@0: goto finish_des; michael@0: case CKM_DES_ECB: michael@0: if (key_type != CKK_DES) { michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: t = NSS_DES; michael@0: goto finish_des; michael@0: case CKM_DES_CBC_PAD: michael@0: context->doPad = PR_TRUE; michael@0: /* fall thru */ michael@0: case CKM_DES_CBC: michael@0: if (key_type != CKK_DES) { michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: t = NSS_DES_CBC; michael@0: goto finish_des; michael@0: case CKM_DES3_ECB: michael@0: if ((key_type != CKK_DES2) && (key_type != CKK_DES3)) { michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: t = NSS_DES_EDE3; michael@0: goto finish_des; michael@0: case CKM_DES3_CBC_PAD: michael@0: context->doPad = PR_TRUE; michael@0: /* fall thru */ michael@0: case CKM_DES3_CBC: michael@0: if ((key_type != CKK_DES2) && (key_type != CKK_DES3)) { michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: t = NSS_DES_EDE3_CBC; michael@0: finish_des: michael@0: context->blockSize = 8; michael@0: att = sftk_FindAttribute(key,CKA_VALUE); michael@0: if (att == NULL) { michael@0: crv = CKR_KEY_HANDLE_INVALID; michael@0: break; michael@0: } michael@0: if (key_type == CKK_DES2 && michael@0: (t == NSS_DES_EDE3_CBC || t == NSS_DES_EDE3)) { michael@0: /* extend DES2 key to DES3 key. */ michael@0: memcpy(newdeskey, att->attrib.pValue, 16); michael@0: memcpy(newdeskey + 16, newdeskey, 8); michael@0: useNewKey=PR_TRUE; michael@0: } else if (key_type == CKK_CDMF) { michael@0: crv = sftk_cdmf2des((unsigned char*)att->attrib.pValue,newdeskey); michael@0: if (crv != CKR_OK) { michael@0: sftk_FreeAttribute(att); michael@0: break; michael@0: } michael@0: useNewKey=PR_TRUE; michael@0: } michael@0: context->cipherInfo = DES_CreateContext( michael@0: useNewKey ? newdeskey : (unsigned char*)att->attrib.pValue, michael@0: (unsigned char*)pMechanism->pParameter,t, isEncrypt); michael@0: if (useNewKey) michael@0: memset(newdeskey, 0, sizeof newdeskey); michael@0: sftk_FreeAttribute(att); michael@0: if (context->cipherInfo == NULL) { michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: context->update = (SFTKCipher) (isEncrypt ? DES_Encrypt : DES_Decrypt); michael@0: context->destroy = (SFTKDestroy) DES_DestroyContext; michael@0: break; michael@0: case CKM_SEED_CBC_PAD: michael@0: context->doPad = PR_TRUE; michael@0: /* fall thru */ michael@0: case CKM_SEED_CBC: michael@0: if (!pMechanism->pParameter || michael@0: pMechanism->ulParameterLen != 16) { michael@0: crv = CKR_MECHANISM_PARAM_INVALID; michael@0: break; michael@0: } michael@0: /* fall thru */ michael@0: case CKM_SEED_ECB: michael@0: context->blockSize = 16; michael@0: if (key_type != CKK_SEED) { michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: att = sftk_FindAttribute(key,CKA_VALUE); michael@0: if (att == NULL) { michael@0: crv = CKR_KEY_HANDLE_INVALID; michael@0: break; michael@0: } michael@0: context->cipherInfo = SEED_CreateContext( michael@0: (unsigned char*)att->attrib.pValue, michael@0: (unsigned char*)pMechanism->pParameter, michael@0: pMechanism->mechanism == CKM_SEED_ECB ? NSS_SEED : NSS_SEED_CBC, michael@0: isEncrypt); michael@0: sftk_FreeAttribute(att); michael@0: if (context->cipherInfo == NULL) { michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: context->update = (SFTKCipher)(isEncrypt ? SEED_Encrypt : SEED_Decrypt); michael@0: context->destroy = (SFTKDestroy) SEED_DestroyContext; michael@0: break; michael@0: michael@0: case CKM_CAMELLIA_CBC_PAD: michael@0: context->doPad = PR_TRUE; michael@0: /* fall thru */ michael@0: case CKM_CAMELLIA_CBC: michael@0: if (!pMechanism->pParameter || michael@0: pMechanism->ulParameterLen != 16) { michael@0: crv = CKR_MECHANISM_PARAM_INVALID; michael@0: break; michael@0: } michael@0: /* fall thru */ michael@0: case CKM_CAMELLIA_ECB: michael@0: context->blockSize = 16; michael@0: if (key_type != CKK_CAMELLIA) { michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: att = sftk_FindAttribute(key,CKA_VALUE); michael@0: if (att == NULL) { michael@0: crv = CKR_KEY_HANDLE_INVALID; michael@0: break; michael@0: } michael@0: context->cipherInfo = Camellia_CreateContext( michael@0: (unsigned char*)att->attrib.pValue, michael@0: (unsigned char*)pMechanism->pParameter, michael@0: pMechanism->mechanism == michael@0: CKM_CAMELLIA_ECB ? NSS_CAMELLIA : NSS_CAMELLIA_CBC, michael@0: isEncrypt, att->attrib.ulValueLen); michael@0: sftk_FreeAttribute(att); michael@0: if (context->cipherInfo == NULL) { michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: context->update = (SFTKCipher) (isEncrypt ? michael@0: Camellia_Encrypt : Camellia_Decrypt); michael@0: context->destroy = (SFTKDestroy) Camellia_DestroyContext; michael@0: break; michael@0: michael@0: case CKM_AES_CBC_PAD: michael@0: context->doPad = PR_TRUE; michael@0: /* fall thru */ michael@0: case CKM_AES_ECB: michael@0: case CKM_AES_CBC: michael@0: context->blockSize = 16; michael@0: case CKM_AES_CTS: michael@0: case CKM_AES_CTR: michael@0: case CKM_AES_GCM: michael@0: if (pMechanism->mechanism == CKM_AES_GCM) { michael@0: context->multi = PR_FALSE; michael@0: } michael@0: if (key_type != CKK_AES) { michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: att = sftk_FindAttribute(key,CKA_VALUE); michael@0: if (att == NULL) { michael@0: crv = CKR_KEY_HANDLE_INVALID; michael@0: break; michael@0: } michael@0: context->cipherInfo = AES_CreateContext( michael@0: (unsigned char*)att->attrib.pValue, michael@0: (unsigned char*)pMechanism->pParameter, michael@0: sftk_aes_mode(pMechanism->mechanism), michael@0: isEncrypt, att->attrib.ulValueLen, 16); michael@0: sftk_FreeAttribute(att); michael@0: if (context->cipherInfo == NULL) { michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: context->update = (SFTKCipher) (isEncrypt ? AES_Encrypt : AES_Decrypt); michael@0: context->destroy = (SFTKDestroy) AES_DestroyContext; michael@0: break; michael@0: michael@0: case CKM_NETSCAPE_AES_KEY_WRAP_PAD: michael@0: context->doPad = PR_TRUE; michael@0: /* fall thru */ michael@0: case CKM_NETSCAPE_AES_KEY_WRAP: michael@0: context->multi = PR_FALSE; michael@0: context->blockSize = 8; michael@0: if (key_type != CKK_AES) { michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: att = sftk_FindAttribute(key,CKA_VALUE); michael@0: if (att == NULL) { michael@0: crv = CKR_KEY_HANDLE_INVALID; michael@0: break; michael@0: } michael@0: context->cipherInfo = AESKeyWrap_CreateContext( michael@0: (unsigned char*)att->attrib.pValue, michael@0: (unsigned char*)pMechanism->pParameter, michael@0: isEncrypt, att->attrib.ulValueLen); michael@0: sftk_FreeAttribute(att); michael@0: if (context->cipherInfo == NULL) { michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: context->update = (SFTKCipher) (isEncrypt ? AESKeyWrap_Encrypt michael@0: : AESKeyWrap_Decrypt); michael@0: context->destroy = (SFTKDestroy) AESKeyWrap_DestroyContext; michael@0: break; michael@0: michael@0: default: michael@0: crv = CKR_MECHANISM_INVALID; michael@0: break; michael@0: } michael@0: michael@0: if (crv != CKR_OK) { michael@0: sftk_FreeContext(context); michael@0: sftk_FreeSession(session); michael@0: return crv; michael@0: } michael@0: sftk_SetContextByType(session, contextType, context); michael@0: sftk_FreeSession(session); michael@0: return CKR_OK; michael@0: } michael@0: michael@0: /* NSC_EncryptInit initializes an encryption operation. */ michael@0: CK_RV NSC_EncryptInit(CK_SESSION_HANDLE hSession, michael@0: CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) michael@0: { michael@0: CHECK_FORK(); michael@0: return sftk_CryptInit(hSession, pMechanism, hKey, CKA_ENCRYPT, CKA_ENCRYPT, michael@0: SFTK_ENCRYPT, PR_TRUE); michael@0: } michael@0: michael@0: /* NSC_EncryptUpdate continues a multiple-part encryption operation. */ michael@0: CK_RV NSC_EncryptUpdate(CK_SESSION_HANDLE hSession, michael@0: CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, michael@0: CK_ULONG_PTR pulEncryptedPartLen) michael@0: { michael@0: SFTKSessionContext *context; michael@0: unsigned int outlen,i; michael@0: unsigned int padoutlen = 0; michael@0: unsigned int maxout = *pulEncryptedPartLen; michael@0: CK_RV crv; michael@0: SECStatus rv; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: /* make sure we're legal */ michael@0: crv = sftk_GetContext(hSession,&context,SFTK_ENCRYPT,PR_TRUE,NULL); michael@0: if (crv != CKR_OK) return crv; michael@0: michael@0: if (!pEncryptedPart) { michael@0: if (context->doPad) { michael@0: CK_ULONG totalDataAvailable = ulPartLen + context->padDataLength; michael@0: CK_ULONG blocksToSend = totalDataAvailable/context->blockSize; michael@0: michael@0: *pulEncryptedPartLen = blocksToSend * context->blockSize; michael@0: return CKR_OK; michael@0: } michael@0: *pulEncryptedPartLen = ulPartLen; michael@0: return CKR_OK; michael@0: } michael@0: michael@0: /* do padding */ michael@0: if (context->doPad) { michael@0: /* deal with previous buffered data */ michael@0: if (context->padDataLength != 0) { michael@0: /* fill in the padded to a full block size */ michael@0: for (i=context->padDataLength; michael@0: (ulPartLen != 0) && i < context->blockSize; i++) { michael@0: context->padBuf[i] = *pPart++; michael@0: ulPartLen--; michael@0: context->padDataLength++; michael@0: } michael@0: michael@0: /* not enough data to encrypt yet? then return */ michael@0: if (context->padDataLength != context->blockSize) { michael@0: *pulEncryptedPartLen = 0; michael@0: return CKR_OK; michael@0: } michael@0: /* encrypt the current padded data */ michael@0: rv = (*context->update)(context->cipherInfo, pEncryptedPart, michael@0: &padoutlen, context->blockSize, context->padBuf, michael@0: context->blockSize); michael@0: if (rv != SECSuccess) { michael@0: return sftk_MapCryptError(PORT_GetError()); michael@0: } michael@0: pEncryptedPart += padoutlen; michael@0: maxout -= padoutlen; michael@0: } michael@0: /* save the residual */ michael@0: context->padDataLength = ulPartLen % context->blockSize; michael@0: if (context->padDataLength) { michael@0: PORT_Memcpy(context->padBuf, michael@0: &pPart[ulPartLen-context->padDataLength], michael@0: context->padDataLength); michael@0: ulPartLen -= context->padDataLength; michael@0: } michael@0: /* if we've exhausted our new buffer, we're done */ michael@0: if (ulPartLen == 0) { michael@0: *pulEncryptedPartLen = padoutlen; michael@0: return CKR_OK; michael@0: } michael@0: } michael@0: michael@0: michael@0: /* do it: NOTE: this assumes buf size in is >= buf size out! */ michael@0: rv = (*context->update)(context->cipherInfo,pEncryptedPart, michael@0: &outlen, maxout, pPart, ulPartLen); michael@0: *pulEncryptedPartLen = (CK_ULONG) (outlen + padoutlen); michael@0: return (rv == SECSuccess) ? CKR_OK : sftk_MapCryptError(PORT_GetError()); michael@0: } michael@0: michael@0: michael@0: /* NSC_EncryptFinal finishes a multiple-part encryption operation. */ michael@0: CK_RV NSC_EncryptFinal(CK_SESSION_HANDLE hSession, michael@0: CK_BYTE_PTR pLastEncryptedPart, CK_ULONG_PTR pulLastEncryptedPartLen) michael@0: { michael@0: SFTKSession *session; michael@0: SFTKSessionContext *context; michael@0: unsigned int outlen,i; michael@0: unsigned int maxout = *pulLastEncryptedPartLen; michael@0: CK_RV crv; michael@0: SECStatus rv = SECSuccess; michael@0: PRBool contextFinished = PR_TRUE; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: /* make sure we're legal */ michael@0: crv = sftk_GetContext(hSession,&context,SFTK_ENCRYPT,PR_TRUE,&session); michael@0: if (crv != CKR_OK) return crv; michael@0: michael@0: *pulLastEncryptedPartLen = 0; michael@0: if (!pLastEncryptedPart) { michael@0: /* caller is checking the amount of remaining data */ michael@0: if (context->blockSize > 0 && context->doPad) { michael@0: *pulLastEncryptedPartLen = context->blockSize; michael@0: contextFinished = PR_FALSE; /* still have padding to go */ michael@0: } michael@0: goto finish; michael@0: } michael@0: michael@0: /* do padding */ michael@0: if (context->doPad) { michael@0: unsigned char padbyte = (unsigned char) michael@0: (context->blockSize - context->padDataLength); michael@0: /* fill out rest of pad buffer with pad magic*/ michael@0: for (i=context->padDataLength; i < context->blockSize; i++) { michael@0: context->padBuf[i] = padbyte; michael@0: } michael@0: rv = (*context->update)(context->cipherInfo,pLastEncryptedPart, michael@0: &outlen, maxout, context->padBuf, context->blockSize); michael@0: if (rv == SECSuccess) *pulLastEncryptedPartLen = (CK_ULONG) outlen; michael@0: } michael@0: michael@0: finish: michael@0: if (contextFinished) michael@0: sftk_TerminateOp( session, SFTK_ENCRYPT, context ); michael@0: sftk_FreeSession(session); michael@0: return (rv == SECSuccess) ? CKR_OK : sftk_MapCryptError(PORT_GetError()); michael@0: } michael@0: michael@0: /* NSC_Encrypt encrypts single-part data. */ michael@0: CK_RV NSC_Encrypt (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, michael@0: CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData, michael@0: CK_ULONG_PTR pulEncryptedDataLen) michael@0: { michael@0: SFTKSession *session; michael@0: SFTKSessionContext *context; michael@0: unsigned int outlen; michael@0: unsigned int maxoutlen = *pulEncryptedDataLen; michael@0: CK_RV crv; michael@0: CK_RV crv2; michael@0: SECStatus rv = SECSuccess; michael@0: SECItem pText; michael@0: michael@0: pText.type = siBuffer; michael@0: pText.data = pData; michael@0: pText.len = ulDataLen; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: /* make sure we're legal */ michael@0: crv = sftk_GetContext(hSession,&context,SFTK_ENCRYPT,PR_FALSE,&session); michael@0: if (crv != CKR_OK) return crv; michael@0: michael@0: if (!pEncryptedData) { michael@0: *pulEncryptedDataLen = context->rsa ? context->maxLen : michael@0: ulDataLen + 2 * context->blockSize; michael@0: goto finish; michael@0: } michael@0: michael@0: if (context->doPad) { michael@0: if (context->multi) { michael@0: CK_ULONG finalLen; michael@0: /* padding is fairly complicated, have the update and final michael@0: * code deal with it */ michael@0: sftk_FreeSession(session); michael@0: crv = NSC_EncryptUpdate(hSession, pData, ulDataLen, pEncryptedData, michael@0: pulEncryptedDataLen); michael@0: if (crv != CKR_OK) michael@0: *pulEncryptedDataLen = 0; michael@0: maxoutlen -= *pulEncryptedDataLen; michael@0: pEncryptedData += *pulEncryptedDataLen; michael@0: finalLen = maxoutlen; michael@0: crv2 = NSC_EncryptFinal(hSession, pEncryptedData, &finalLen); michael@0: if (crv2 == CKR_OK) michael@0: *pulEncryptedDataLen += finalLen; michael@0: return crv == CKR_OK ? crv2 : crv; michael@0: } michael@0: /* doPad without multi means that padding must be done on the first michael@0: ** and only update. There will be no final. michael@0: */ michael@0: PORT_Assert(context->blockSize > 1); michael@0: if (context->blockSize > 1) { michael@0: CK_ULONG remainder = ulDataLen % context->blockSize; michael@0: CK_ULONG padding = context->blockSize - remainder; michael@0: pText.len += padding; michael@0: pText.data = PORT_ZAlloc(pText.len); michael@0: if (pText.data) { michael@0: memcpy(pText.data, pData, ulDataLen); michael@0: memset(pText.data + ulDataLen, padding, padding); michael@0: } else { michael@0: crv = CKR_HOST_MEMORY; michael@0: goto fail; michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* do it: NOTE: this assumes buf size is big enough. */ michael@0: rv = (*context->update)(context->cipherInfo, pEncryptedData, michael@0: &outlen, maxoutlen, pText.data, pText.len); michael@0: crv = (rv == SECSuccess) ? CKR_OK : sftk_MapCryptError(PORT_GetError()); michael@0: *pulEncryptedDataLen = (CK_ULONG) outlen; michael@0: if (pText.data != pData) michael@0: PORT_ZFree(pText.data, pText.len); michael@0: fail: michael@0: sftk_TerminateOp( session, SFTK_ENCRYPT, context ); michael@0: finish: michael@0: sftk_FreeSession(session); michael@0: michael@0: return crv; michael@0: } michael@0: michael@0: michael@0: /* michael@0: ************** Crypto Functions: Decrypt ************************ michael@0: */ michael@0: michael@0: /* NSC_DecryptInit initializes a decryption operation. */ michael@0: CK_RV NSC_DecryptInit( CK_SESSION_HANDLE hSession, michael@0: CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) michael@0: { michael@0: CHECK_FORK(); michael@0: return sftk_CryptInit(hSession, pMechanism, hKey, CKA_DECRYPT, CKA_DECRYPT, michael@0: SFTK_DECRYPT, PR_FALSE); michael@0: } michael@0: michael@0: /* NSC_DecryptUpdate continues a multiple-part decryption operation. */ michael@0: CK_RV NSC_DecryptUpdate(CK_SESSION_HANDLE hSession, michael@0: CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, michael@0: CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) michael@0: { michael@0: SFTKSessionContext *context; michael@0: unsigned int padoutlen = 0; michael@0: unsigned int outlen; michael@0: unsigned int maxout = *pulPartLen; michael@0: CK_RV crv; michael@0: SECStatus rv; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: /* make sure we're legal */ michael@0: crv = sftk_GetContext(hSession,&context,SFTK_DECRYPT,PR_TRUE,NULL); michael@0: if (crv != CKR_OK) return crv; michael@0: michael@0: /* this can only happen on an NSS programming error */ michael@0: PORT_Assert((context->padDataLength == 0) michael@0: || context->padDataLength == context->blockSize); michael@0: michael@0: michael@0: if (context->doPad) { michael@0: /* Check the data length for block ciphers. If we are padding, michael@0: * then we must be using a block cipher. In the non-padding case michael@0: * the error will be returned by the underlying decryption michael@0: * function when we do the actual decrypt. We need to do the michael@0: * check here to avoid returning a negative length to the caller michael@0: * or reading before the beginning of the pEncryptedPart buffer. michael@0: */ michael@0: if ((ulEncryptedPartLen == 0) || michael@0: (ulEncryptedPartLen % context->blockSize) != 0) { michael@0: return CKR_ENCRYPTED_DATA_LEN_RANGE; michael@0: } michael@0: } michael@0: michael@0: if (!pPart) { michael@0: if (context->doPad) { michael@0: *pulPartLen = michael@0: ulEncryptedPartLen + context->padDataLength - context->blockSize; michael@0: return CKR_OK; michael@0: } michael@0: /* for stream ciphers there is are no constraints on ulEncryptedPartLen. michael@0: * for block ciphers, it must be a multiple of blockSize. The error is michael@0: * detected when this function is called again do decrypt the output. michael@0: */ michael@0: *pulPartLen = ulEncryptedPartLen; michael@0: return CKR_OK; michael@0: } michael@0: michael@0: if (context->doPad) { michael@0: /* first decrypt our saved buffer */ michael@0: if (context->padDataLength != 0) { michael@0: rv = (*context->update)(context->cipherInfo, pPart, &padoutlen, michael@0: maxout, context->padBuf, context->blockSize); michael@0: if (rv != SECSuccess) return sftk_MapDecryptError(PORT_GetError()); michael@0: pPart += padoutlen; michael@0: maxout -= padoutlen; michael@0: } michael@0: /* now save the final block for the next decrypt or the final */ michael@0: PORT_Memcpy(context->padBuf,&pEncryptedPart[ulEncryptedPartLen - michael@0: context->blockSize], context->blockSize); michael@0: context->padDataLength = context->blockSize; michael@0: ulEncryptedPartLen -= context->padDataLength; michael@0: } michael@0: michael@0: /* do it: NOTE: this assumes buf size in is >= buf size out! */ michael@0: rv = (*context->update)(context->cipherInfo,pPart, &outlen, michael@0: maxout, pEncryptedPart, ulEncryptedPartLen); michael@0: *pulPartLen = (CK_ULONG) (outlen + padoutlen); michael@0: return (rv == SECSuccess) ? CKR_OK : sftk_MapDecryptError(PORT_GetError()); michael@0: } michael@0: michael@0: michael@0: /* NSC_DecryptFinal finishes a multiple-part decryption operation. */ michael@0: CK_RV NSC_DecryptFinal(CK_SESSION_HANDLE hSession, michael@0: CK_BYTE_PTR pLastPart, CK_ULONG_PTR pulLastPartLen) michael@0: { michael@0: SFTKSession *session; michael@0: SFTKSessionContext *context; michael@0: unsigned int outlen; michael@0: unsigned int maxout = *pulLastPartLen; michael@0: CK_RV crv; michael@0: SECStatus rv = SECSuccess; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: /* make sure we're legal */ michael@0: crv = sftk_GetContext(hSession,&context,SFTK_DECRYPT,PR_TRUE,&session); michael@0: if (crv != CKR_OK) return crv; michael@0: michael@0: *pulLastPartLen = 0; michael@0: if (!pLastPart) { michael@0: /* caller is checking the amount of remaining data */ michael@0: if (context->padDataLength > 0) { michael@0: *pulLastPartLen = context->padDataLength; michael@0: } michael@0: goto finish; michael@0: } michael@0: michael@0: if (context->doPad) { michael@0: /* decrypt our saved buffer */ michael@0: if (context->padDataLength != 0) { michael@0: /* this assumes that pLastPart is big enough to hold the *whole* michael@0: * buffer!!! */ michael@0: rv = (*context->update)(context->cipherInfo, pLastPart, &outlen, michael@0: maxout, context->padBuf, context->blockSize); michael@0: if (rv != SECSuccess) { michael@0: crv = sftk_MapDecryptError(PORT_GetError()); michael@0: } else { michael@0: unsigned int padSize = michael@0: (unsigned int) pLastPart[context->blockSize-1]; michael@0: if ((padSize > context->blockSize) || (padSize == 0)) { michael@0: crv = CKR_ENCRYPTED_DATA_INVALID; michael@0: } else { michael@0: unsigned int i; michael@0: unsigned int badPadding = 0; /* used as a boolean */ michael@0: for (i = 0; i < padSize; i++) { michael@0: badPadding |= michael@0: (unsigned int) pLastPart[context->blockSize-1-i] ^ michael@0: padSize; michael@0: } michael@0: if (badPadding) { michael@0: crv = CKR_ENCRYPTED_DATA_INVALID; michael@0: } else { michael@0: *pulLastPartLen = outlen - padSize; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: sftk_TerminateOp( session, SFTK_DECRYPT, context ); michael@0: finish: michael@0: sftk_FreeSession(session); michael@0: return crv; michael@0: } michael@0: michael@0: /* NSC_Decrypt decrypts encrypted data in a single part. */ michael@0: CK_RV NSC_Decrypt(CK_SESSION_HANDLE hSession, michael@0: CK_BYTE_PTR pEncryptedData,CK_ULONG ulEncryptedDataLen,CK_BYTE_PTR pData, michael@0: CK_ULONG_PTR pulDataLen) michael@0: { michael@0: SFTKSession *session; michael@0: SFTKSessionContext *context; michael@0: unsigned int outlen; michael@0: unsigned int maxoutlen = *pulDataLen; michael@0: CK_RV crv; michael@0: CK_RV crv2; michael@0: SECStatus rv = SECSuccess; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: /* make sure we're legal */ michael@0: crv = sftk_GetContext(hSession,&context,SFTK_DECRYPT,PR_FALSE,&session); michael@0: if (crv != CKR_OK) return crv; michael@0: michael@0: if (!pData) { michael@0: *pulDataLen = ulEncryptedDataLen + context->blockSize; michael@0: goto finish; michael@0: } michael@0: michael@0: if (context->doPad && context->multi) { michael@0: CK_ULONG finalLen; michael@0: /* padding is fairly complicated, have the update and final michael@0: * code deal with it */ michael@0: sftk_FreeSession(session); michael@0: crv = NSC_DecryptUpdate(hSession,pEncryptedData,ulEncryptedDataLen, michael@0: pData, pulDataLen); michael@0: if (crv != CKR_OK) michael@0: *pulDataLen = 0; michael@0: maxoutlen -= *pulDataLen; michael@0: pData += *pulDataLen; michael@0: finalLen = maxoutlen; michael@0: crv2 = NSC_DecryptFinal(hSession, pData, &finalLen); michael@0: if (crv2 == CKR_OK) michael@0: *pulDataLen += finalLen; michael@0: return crv == CKR_OK ? crv2 : crv; michael@0: } michael@0: michael@0: rv = (*context->update)(context->cipherInfo, pData, &outlen, maxoutlen, michael@0: pEncryptedData, ulEncryptedDataLen); michael@0: /* XXX need to do MUCH better error mapping than this. */ michael@0: crv = (rv == SECSuccess) ? CKR_OK : sftk_MapDecryptError(PORT_GetError()); michael@0: if (rv == SECSuccess && context->doPad) { michael@0: unsigned int padding = pData[outlen - 1]; michael@0: if (padding > context->blockSize || !padding) { michael@0: crv = CKR_ENCRYPTED_DATA_INVALID; michael@0: } else { michael@0: unsigned int i; michael@0: unsigned int badPadding = 0; /* used as a boolean */ michael@0: for (i = 0; i < padding; i++) { michael@0: badPadding |= (unsigned int) pData[outlen - 1 - i] ^ padding; michael@0: } michael@0: if (badPadding) { michael@0: crv = CKR_ENCRYPTED_DATA_INVALID; michael@0: } else { michael@0: outlen -= padding; michael@0: } michael@0: } michael@0: } michael@0: *pulDataLen = (CK_ULONG) outlen; michael@0: sftk_TerminateOp( session, SFTK_DECRYPT, context ); michael@0: finish: michael@0: sftk_FreeSession(session); michael@0: return crv; michael@0: } michael@0: michael@0: michael@0: michael@0: /* michael@0: ************** Crypto Functions: Digest (HASH) ************************ michael@0: */ michael@0: michael@0: /* NSC_DigestInit initializes a message-digesting operation. */ michael@0: CK_RV NSC_DigestInit(CK_SESSION_HANDLE hSession, michael@0: CK_MECHANISM_PTR pMechanism) michael@0: { michael@0: SFTKSession *session; michael@0: SFTKSessionContext *context; michael@0: CK_RV crv = CKR_OK; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: session = sftk_SessionFromHandle(hSession); michael@0: if (session == NULL) michael@0: return CKR_SESSION_HANDLE_INVALID; michael@0: crv = sftk_InitGeneric(session,&context,SFTK_HASH,NULL,0,NULL, 0, 0); michael@0: if (crv != CKR_OK) { michael@0: sftk_FreeSession(session); michael@0: return crv; michael@0: } michael@0: michael@0: michael@0: #define INIT_MECH(mech,mmm) \ michael@0: case mech: { \ michael@0: mmm ## Context * mmm ## _ctx = mmm ## _NewContext(); \ michael@0: context->cipherInfo = (void *)mmm ## _ctx; \ michael@0: context->cipherInfoLen = mmm ## _FlattenSize(mmm ## _ctx); \ michael@0: context->currentMech = mech; \ michael@0: context->hashUpdate = (SFTKHash) mmm ## _Update; \ michael@0: context->end = (SFTKEnd) mmm ## _End; \ michael@0: context->destroy = (SFTKDestroy) mmm ## _DestroyContext; \ michael@0: context->maxLen = mmm ## _LENGTH; \ michael@0: if (mmm ## _ctx) \ michael@0: mmm ## _Begin(mmm ## _ctx); \ michael@0: else \ michael@0: crv = CKR_HOST_MEMORY; \ michael@0: break; \ michael@0: } michael@0: michael@0: switch(pMechanism->mechanism) { michael@0: INIT_MECH(CKM_MD2, MD2) michael@0: INIT_MECH(CKM_MD5, MD5) michael@0: INIT_MECH(CKM_SHA_1, SHA1) michael@0: INIT_MECH(CKM_SHA224, SHA224) michael@0: INIT_MECH(CKM_SHA256, SHA256) michael@0: INIT_MECH(CKM_SHA384, SHA384) michael@0: INIT_MECH(CKM_SHA512, SHA512) michael@0: michael@0: default: michael@0: crv = CKR_MECHANISM_INVALID; michael@0: break; michael@0: } michael@0: michael@0: if (crv != CKR_OK) { michael@0: sftk_FreeContext(context); michael@0: sftk_FreeSession(session); michael@0: return crv; michael@0: } michael@0: sftk_SetContextByType(session, SFTK_HASH, context); michael@0: sftk_FreeSession(session); michael@0: return CKR_OK; michael@0: } michael@0: michael@0: michael@0: /* NSC_Digest digests data in a single part. */ michael@0: CK_RV NSC_Digest(CK_SESSION_HANDLE hSession, michael@0: CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pDigest, michael@0: CK_ULONG_PTR pulDigestLen) michael@0: { michael@0: SFTKSession *session; michael@0: SFTKSessionContext *context; michael@0: unsigned int digestLen; michael@0: unsigned int maxout = *pulDigestLen; michael@0: CK_RV crv; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: /* make sure we're legal */ michael@0: crv = sftk_GetContext(hSession,&context,SFTK_HASH,PR_FALSE,&session); michael@0: if (crv != CKR_OK) return crv; michael@0: michael@0: if (pDigest == NULL) { michael@0: *pulDigestLen = context->maxLen; michael@0: goto finish; michael@0: } michael@0: michael@0: /* do it: */ michael@0: (*context->hashUpdate)(context->cipherInfo, pData, ulDataLen); michael@0: /* NOTE: this assumes buf size is bigenough for the algorithm */ michael@0: (*context->end)(context->cipherInfo, pDigest, &digestLen,maxout); michael@0: *pulDigestLen = digestLen; michael@0: michael@0: sftk_TerminateOp( session, SFTK_HASH, context ); michael@0: finish: michael@0: sftk_FreeSession(session); michael@0: return CKR_OK; michael@0: } michael@0: michael@0: michael@0: /* NSC_DigestUpdate continues a multiple-part message-digesting operation. */ michael@0: CK_RV NSC_DigestUpdate(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pPart, michael@0: CK_ULONG ulPartLen) michael@0: { michael@0: SFTKSessionContext *context; michael@0: CK_RV crv; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: /* make sure we're legal */ michael@0: crv = sftk_GetContext(hSession,&context,SFTK_HASH,PR_TRUE,NULL); michael@0: if (crv != CKR_OK) return crv; michael@0: /* do it: */ michael@0: (*context->hashUpdate)(context->cipherInfo, pPart, ulPartLen); michael@0: return CKR_OK; michael@0: } michael@0: michael@0: michael@0: /* NSC_DigestFinal finishes a multiple-part message-digesting operation. */ michael@0: CK_RV NSC_DigestFinal(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pDigest, michael@0: CK_ULONG_PTR pulDigestLen) michael@0: { michael@0: SFTKSession *session; michael@0: SFTKSessionContext *context; michael@0: unsigned int maxout = *pulDigestLen; michael@0: unsigned int digestLen; michael@0: CK_RV crv; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: /* make sure we're legal */ michael@0: crv = sftk_GetContext(hSession, &context, SFTK_HASH, PR_TRUE, &session); michael@0: if (crv != CKR_OK) return crv; michael@0: michael@0: if (pDigest != NULL) { michael@0: (*context->end)(context->cipherInfo, pDigest, &digestLen, maxout); michael@0: *pulDigestLen = digestLen; michael@0: sftk_TerminateOp( session, SFTK_HASH, context ); michael@0: } else { michael@0: *pulDigestLen = context->maxLen; michael@0: } michael@0: michael@0: sftk_FreeSession(session); michael@0: return CKR_OK; michael@0: } michael@0: michael@0: /* michael@0: * these helper functions are used by Generic Macing and Signing functions michael@0: * that use hashes as part of their operations. michael@0: */ michael@0: #define DOSUB(mmm) \ michael@0: static CK_RV \ michael@0: sftk_doSub ## mmm(SFTKSessionContext *context) { \ michael@0: mmm ## Context * mmm ## _ctx = mmm ## _NewContext(); \ michael@0: context->hashInfo = (void *) mmm ## _ctx; \ michael@0: context->hashUpdate = (SFTKHash) mmm ## _Update; \ michael@0: context->end = (SFTKEnd) mmm ## _End; \ michael@0: context->hashdestroy = (SFTKDestroy) mmm ## _DestroyContext; \ michael@0: if (!context->hashInfo) { \ michael@0: return CKR_HOST_MEMORY; \ michael@0: } \ michael@0: mmm ## _Begin( mmm ## _ctx ); \ michael@0: return CKR_OK; \ michael@0: } michael@0: michael@0: DOSUB(MD2) michael@0: DOSUB(MD5) michael@0: DOSUB(SHA1) michael@0: DOSUB(SHA224) michael@0: DOSUB(SHA256) michael@0: DOSUB(SHA384) michael@0: DOSUB(SHA512) michael@0: michael@0: static SECStatus michael@0: sftk_SignCopy( michael@0: CK_ULONG *copyLen, michael@0: void *out, unsigned int *outLength, michael@0: unsigned int maxLength, michael@0: const unsigned char *hashResult, michael@0: unsigned int hashResultLength) michael@0: { michael@0: unsigned int toCopy = *copyLen; michael@0: if (toCopy > maxLength) { michael@0: toCopy = maxLength; michael@0: } michael@0: if (toCopy > hashResultLength) { michael@0: toCopy = hashResultLength; michael@0: } michael@0: memcpy(out, hashResult, toCopy); michael@0: if (outLength) { michael@0: *outLength = toCopy; michael@0: } michael@0: return SECSuccess; michael@0: } michael@0: michael@0: /* Verify is just a compare for HMAC */ michael@0: static SECStatus michael@0: sftk_HMACCmp(CK_ULONG *copyLen,unsigned char *sig,unsigned int sigLen, michael@0: unsigned char *hash, unsigned int hashLen) michael@0: { michael@0: return (PORT_Memcmp(sig,hash,*copyLen) == 0) ? SECSuccess : SECFailure ; michael@0: } michael@0: michael@0: /* michael@0: * common HMAC initalization routine michael@0: */ michael@0: static CK_RV michael@0: sftk_doHMACInit(SFTKSessionContext *context,HASH_HashType hash, michael@0: SFTKObject *key, CK_ULONG mac_size) michael@0: { michael@0: SFTKAttribute *keyval; michael@0: HMACContext *HMACcontext; michael@0: CK_ULONG *intpointer; michael@0: const SECHashObject *hashObj = HASH_GetRawHashObject(hash); michael@0: PRBool isFIPS = (key->slot->slotID == FIPS_SLOT_ID); michael@0: michael@0: /* required by FIPS 198 Section 4 */ michael@0: if (isFIPS && (mac_size < 4 || mac_size < hashObj->length/2)) { michael@0: return CKR_BUFFER_TOO_SMALL; michael@0: } michael@0: michael@0: keyval = sftk_FindAttribute(key,CKA_VALUE); michael@0: if (keyval == NULL) return CKR_KEY_SIZE_RANGE; michael@0: michael@0: HMACcontext = HMAC_Create(hashObj, michael@0: (const unsigned char*)keyval->attrib.pValue, michael@0: keyval->attrib.ulValueLen, isFIPS); michael@0: context->hashInfo = HMACcontext; michael@0: context->multi = PR_TRUE; michael@0: sftk_FreeAttribute(keyval); michael@0: if (context->hashInfo == NULL) { michael@0: if (PORT_GetError() == SEC_ERROR_INVALID_ARGS) { michael@0: return CKR_KEY_SIZE_RANGE; michael@0: } michael@0: return CKR_HOST_MEMORY; michael@0: } michael@0: context->hashUpdate = (SFTKHash) HMAC_Update; michael@0: context->end = (SFTKEnd) HMAC_Finish; michael@0: michael@0: context->hashdestroy = (SFTKDestroy) HMAC_Destroy; michael@0: intpointer = PORT_New(CK_ULONG); michael@0: if (intpointer == NULL) { michael@0: return CKR_HOST_MEMORY; michael@0: } michael@0: *intpointer = mac_size; michael@0: context->cipherInfo = intpointer; michael@0: context->destroy = (SFTKDestroy) sftk_Space; michael@0: context->update = (SFTKCipher) sftk_SignCopy; michael@0: context->verify = (SFTKVerify) sftk_HMACCmp; michael@0: context->maxLen = hashObj->length; michael@0: HMAC_Begin(HMACcontext); michael@0: return CKR_OK; michael@0: } michael@0: michael@0: /* michael@0: * SSL Macing support. SSL Macs are inited, then update with the base michael@0: * hashing algorithm, then finalized in sign and verify michael@0: */ michael@0: michael@0: /* michael@0: * FROM SSL: michael@0: * 60 bytes is 3 times the maximum length MAC size that is supported. michael@0: * We probably should have one copy of this table. We still need this table michael@0: * in ssl to 'sign' the handshake hashes. michael@0: */ michael@0: static unsigned char ssl_pad_1 [60] = { michael@0: 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, michael@0: 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, michael@0: 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, michael@0: 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, michael@0: 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, michael@0: 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, michael@0: 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, michael@0: 0x36, 0x36, 0x36, 0x36 michael@0: }; michael@0: static unsigned char ssl_pad_2 [60] = { michael@0: 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, michael@0: 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, michael@0: 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, michael@0: 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, michael@0: 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, michael@0: 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, michael@0: 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, michael@0: 0x5c, 0x5c, 0x5c, 0x5c michael@0: }; michael@0: michael@0: static SECStatus michael@0: sftk_SSLMACSign(SFTKSSLMACInfo *info,unsigned char *sig,unsigned int *sigLen, michael@0: unsigned int maxLen,unsigned char *hash, unsigned int hashLen) michael@0: { michael@0: unsigned char tmpBuf[SFTK_MAX_MAC_LENGTH]; michael@0: unsigned int out; michael@0: michael@0: info->begin(info->hashContext); michael@0: info->update(info->hashContext,info->key,info->keySize); michael@0: info->update(info->hashContext,ssl_pad_2,info->padSize); michael@0: info->update(info->hashContext,hash,hashLen); michael@0: info->end(info->hashContext,tmpBuf,&out,SFTK_MAX_MAC_LENGTH); michael@0: PORT_Memcpy(sig,tmpBuf,info->macSize); michael@0: *sigLen = info->macSize; michael@0: return SECSuccess; michael@0: } michael@0: michael@0: static SECStatus michael@0: sftk_SSLMACVerify(SFTKSSLMACInfo *info,unsigned char *sig,unsigned int sigLen, michael@0: unsigned char *hash, unsigned int hashLen) michael@0: { michael@0: unsigned char tmpBuf[SFTK_MAX_MAC_LENGTH]; michael@0: unsigned int out; michael@0: michael@0: info->begin(info->hashContext); michael@0: info->update(info->hashContext,info->key,info->keySize); michael@0: info->update(info->hashContext,ssl_pad_2,info->padSize); michael@0: info->update(info->hashContext,hash,hashLen); michael@0: info->end(info->hashContext,tmpBuf,&out,SFTK_MAX_MAC_LENGTH); michael@0: return (PORT_Memcmp(sig,tmpBuf,info->macSize) == 0) ? michael@0: SECSuccess : SECFailure; michael@0: } michael@0: michael@0: /* michael@0: * common HMAC initalization routine michael@0: */ michael@0: static CK_RV michael@0: sftk_doSSLMACInit(SFTKSessionContext *context,SECOidTag oid, michael@0: SFTKObject *key, CK_ULONG mac_size) michael@0: { michael@0: SFTKAttribute *keyval; michael@0: SFTKBegin begin; michael@0: int padSize; michael@0: SFTKSSLMACInfo *sslmacinfo; michael@0: CK_RV crv = CKR_MECHANISM_INVALID; michael@0: michael@0: if (oid == SEC_OID_SHA1) { michael@0: crv = sftk_doSubSHA1(context); michael@0: if (crv != CKR_OK) return crv; michael@0: begin = (SFTKBegin) SHA1_Begin; michael@0: padSize = 40; michael@0: } else { michael@0: crv = sftk_doSubMD5(context); michael@0: if (crv != CKR_OK) return crv; michael@0: begin = (SFTKBegin) MD5_Begin; michael@0: padSize = 48; michael@0: } michael@0: context->multi = PR_TRUE; michael@0: michael@0: keyval = sftk_FindAttribute(key,CKA_VALUE); michael@0: if (keyval == NULL) return CKR_KEY_SIZE_RANGE; michael@0: michael@0: context->hashUpdate(context->hashInfo,keyval->attrib.pValue, michael@0: keyval->attrib.ulValueLen); michael@0: context->hashUpdate(context->hashInfo,ssl_pad_1,padSize); michael@0: sslmacinfo = (SFTKSSLMACInfo *) PORT_Alloc(sizeof(SFTKSSLMACInfo)); michael@0: if (sslmacinfo == NULL) { michael@0: sftk_FreeAttribute(keyval); michael@0: return CKR_HOST_MEMORY; michael@0: } michael@0: sslmacinfo->macSize = mac_size; michael@0: sslmacinfo->hashContext = context->hashInfo; michael@0: PORT_Memcpy(sslmacinfo->key,keyval->attrib.pValue, michael@0: keyval->attrib.ulValueLen); michael@0: sslmacinfo->keySize = keyval->attrib.ulValueLen; michael@0: sslmacinfo->begin = begin; michael@0: sslmacinfo->end = context->end; michael@0: sslmacinfo->update = context->hashUpdate; michael@0: sslmacinfo->padSize = padSize; michael@0: sftk_FreeAttribute(keyval); michael@0: context->cipherInfo = (void *) sslmacinfo; michael@0: context->destroy = (SFTKDestroy) sftk_Space; michael@0: context->update = (SFTKCipher) sftk_SSLMACSign; michael@0: context->verify = (SFTKVerify) sftk_SSLMACVerify; michael@0: context->maxLen = mac_size; michael@0: return CKR_OK; michael@0: } michael@0: michael@0: /* michael@0: ************** Crypto Functions: Sign ************************ michael@0: */ michael@0: michael@0: /** michael@0: * Check if We're using CBCMacing and initialize the session context if we are. michael@0: * @param contextType SFTK_SIGN or SFTK_VERIFY michael@0: * @param keyUsage check whether key allows this usage michael@0: */ michael@0: static CK_RV michael@0: sftk_InitCBCMac(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, michael@0: CK_OBJECT_HANDLE hKey, CK_ATTRIBUTE_TYPE keyUsage, michael@0: SFTKContextType contextType) michael@0: michael@0: { michael@0: CK_MECHANISM cbc_mechanism; michael@0: CK_ULONG mac_bytes = SFTK_INVALID_MAC_SIZE; michael@0: CK_RC2_CBC_PARAMS rc2_params; michael@0: #if NSS_SOFTOKEN_DOES_RC5 michael@0: CK_RC5_CBC_PARAMS rc5_params; michael@0: CK_RC5_MAC_GENERAL_PARAMS *rc5_mac; michael@0: #endif michael@0: unsigned char ivBlock[SFTK_MAX_BLOCK_SIZE]; michael@0: SFTKSessionContext *context; michael@0: CK_RV crv; michael@0: unsigned int blockSize; michael@0: michael@0: switch (pMechanism->mechanism) { michael@0: case CKM_RC2_MAC_GENERAL: michael@0: mac_bytes = michael@0: ((CK_RC2_MAC_GENERAL_PARAMS *)pMechanism->pParameter)->ulMacLength; michael@0: /* fall through */ michael@0: case CKM_RC2_MAC: michael@0: /* this works because ulEffectiveBits is in the same place in both the michael@0: * CK_RC2_MAC_GENERAL_PARAMS and CK_RC2_CBC_PARAMS */ michael@0: rc2_params.ulEffectiveBits = ((CK_RC2_MAC_GENERAL_PARAMS *) michael@0: pMechanism->pParameter)->ulEffectiveBits; michael@0: PORT_Memset(rc2_params.iv,0,sizeof(rc2_params.iv)); michael@0: cbc_mechanism.mechanism = CKM_RC2_CBC; michael@0: cbc_mechanism.pParameter = &rc2_params; michael@0: cbc_mechanism.ulParameterLen = sizeof(rc2_params); michael@0: blockSize = 8; michael@0: break; michael@0: #if NSS_SOFTOKEN_DOES_RC5 michael@0: case CKM_RC5_MAC_GENERAL: michael@0: mac_bytes = michael@0: ((CK_RC5_MAC_GENERAL_PARAMS *)pMechanism->pParameter)->ulMacLength; michael@0: /* fall through */ michael@0: case CKM_RC5_MAC: michael@0: /* this works because ulEffectiveBits is in the same place in both the michael@0: * CK_RC5_MAC_GENERAL_PARAMS and CK_RC5_CBC_PARAMS */ michael@0: rc5_mac = (CK_RC5_MAC_GENERAL_PARAMS *)pMechanism->pParameter; michael@0: rc5_params.ulWordsize = rc5_mac->ulWordsize; michael@0: rc5_params.ulRounds = rc5_mac->ulRounds; michael@0: rc5_params.pIv = ivBlock; michael@0: if( (blockSize = rc5_mac->ulWordsize*2) > SFTK_MAX_BLOCK_SIZE ) michael@0: return CKR_MECHANISM_PARAM_INVALID; michael@0: rc5_params.ulIvLen = blockSize; michael@0: PORT_Memset(ivBlock,0,blockSize); michael@0: cbc_mechanism.mechanism = CKM_RC5_CBC; michael@0: cbc_mechanism.pParameter = &rc5_params; michael@0: cbc_mechanism.ulParameterLen = sizeof(rc5_params); michael@0: break; michael@0: #endif michael@0: /* add cast and idea later */ michael@0: case CKM_DES_MAC_GENERAL: michael@0: mac_bytes = *(CK_ULONG *)pMechanism->pParameter; michael@0: /* fall through */ michael@0: case CKM_DES_MAC: michael@0: blockSize = 8; michael@0: PORT_Memset(ivBlock,0,blockSize); michael@0: cbc_mechanism.mechanism = CKM_DES_CBC; michael@0: cbc_mechanism.pParameter = &ivBlock; michael@0: cbc_mechanism.ulParameterLen = blockSize; michael@0: break; michael@0: case CKM_DES3_MAC_GENERAL: michael@0: mac_bytes = *(CK_ULONG *)pMechanism->pParameter; michael@0: /* fall through */ michael@0: case CKM_DES3_MAC: michael@0: blockSize = 8; michael@0: PORT_Memset(ivBlock,0,blockSize); michael@0: cbc_mechanism.mechanism = CKM_DES3_CBC; michael@0: cbc_mechanism.pParameter = &ivBlock; michael@0: cbc_mechanism.ulParameterLen = blockSize; michael@0: break; michael@0: case CKM_CDMF_MAC_GENERAL: michael@0: mac_bytes = *(CK_ULONG *)pMechanism->pParameter; michael@0: /* fall through */ michael@0: case CKM_CDMF_MAC: michael@0: blockSize = 8; michael@0: PORT_Memset(ivBlock,0,blockSize); michael@0: cbc_mechanism.mechanism = CKM_CDMF_CBC; michael@0: cbc_mechanism.pParameter = &ivBlock; michael@0: cbc_mechanism.ulParameterLen = blockSize; michael@0: break; michael@0: case CKM_SEED_MAC_GENERAL: michael@0: mac_bytes = *(CK_ULONG *)pMechanism->pParameter; michael@0: /* fall through */ michael@0: case CKM_SEED_MAC: michael@0: blockSize = 16; michael@0: PORT_Memset(ivBlock,0,blockSize); michael@0: cbc_mechanism.mechanism = CKM_SEED_CBC; michael@0: cbc_mechanism.pParameter = &ivBlock; michael@0: cbc_mechanism.ulParameterLen = blockSize; michael@0: break; michael@0: case CKM_CAMELLIA_MAC_GENERAL: michael@0: mac_bytes = *(CK_ULONG *)pMechanism->pParameter; michael@0: /* fall through */ michael@0: case CKM_CAMELLIA_MAC: michael@0: blockSize = 16; michael@0: PORT_Memset(ivBlock,0,blockSize); michael@0: cbc_mechanism.mechanism = CKM_CAMELLIA_CBC; michael@0: cbc_mechanism.pParameter = &ivBlock; michael@0: cbc_mechanism.ulParameterLen = blockSize; michael@0: break; michael@0: case CKM_AES_MAC_GENERAL: michael@0: mac_bytes = *(CK_ULONG *)pMechanism->pParameter; michael@0: /* fall through */ michael@0: case CKM_AES_MAC: michael@0: blockSize = 16; michael@0: PORT_Memset(ivBlock,0,blockSize); michael@0: cbc_mechanism.mechanism = CKM_AES_CBC; michael@0: cbc_mechanism.pParameter = &ivBlock; michael@0: cbc_mechanism.ulParameterLen = blockSize; michael@0: break; michael@0: default: michael@0: return CKR_FUNCTION_NOT_SUPPORTED; michael@0: } michael@0: michael@0: /* if MAC size is externally supplied, it should be checked. michael@0: */ michael@0: if (mac_bytes == SFTK_INVALID_MAC_SIZE) michael@0: mac_bytes = blockSize >> 1; michael@0: else { michael@0: if( mac_bytes > blockSize ) michael@0: return CKR_MECHANISM_PARAM_INVALID; michael@0: } michael@0: michael@0: crv = sftk_CryptInit(hSession, &cbc_mechanism, hKey, michael@0: CKA_ENCRYPT, /* CBC mech is able to ENCRYPT, not SIGN/VERIFY */ michael@0: keyUsage, contextType, PR_TRUE ); michael@0: if (crv != CKR_OK) return crv; michael@0: crv = sftk_GetContext(hSession,&context,contextType,PR_TRUE,NULL); michael@0: michael@0: /* this shouldn't happen! */ michael@0: PORT_Assert(crv == CKR_OK); michael@0: if (crv != CKR_OK) return crv; michael@0: context->blockSize = blockSize; michael@0: context->macSize = mac_bytes; michael@0: return CKR_OK; michael@0: } michael@0: michael@0: /* michael@0: * encode RSA PKCS #1 Signature data before signing... michael@0: */ michael@0: static SECStatus michael@0: sftk_RSAHashSign(SFTKHashSignInfo *info, unsigned char *sig, michael@0: unsigned int *sigLen, unsigned int maxLen, michael@0: const unsigned char *hash, unsigned int hashLen) michael@0: { michael@0: PORT_Assert(info->key->keyType == NSSLOWKEYRSAKey); michael@0: if (info->key->keyType != NSSLOWKEYRSAKey) { michael@0: PORT_SetError(SEC_ERROR_INVALID_KEY); michael@0: return SECFailure; michael@0: } michael@0: michael@0: return RSA_HashSign(info->hashOid, info->key, sig, sigLen, maxLen, michael@0: hash, hashLen); michael@0: } michael@0: michael@0: /* XXX Old template; want to expunge it eventually. */ michael@0: static DERTemplate SECAlgorithmIDTemplate[] = { michael@0: { DER_SEQUENCE, michael@0: 0, NULL, sizeof(SECAlgorithmID) }, michael@0: { DER_OBJECT_ID, michael@0: offsetof(SECAlgorithmID,algorithm), }, michael@0: { DER_OPTIONAL | DER_ANY, michael@0: offsetof(SECAlgorithmID,parameters), }, michael@0: { 0, } michael@0: }; michael@0: michael@0: /* michael@0: * XXX OLD Template. Once all uses have been switched over to new one, michael@0: * remove this. michael@0: */ michael@0: static DERTemplate SGNDigestInfoTemplate[] = { michael@0: { DER_SEQUENCE, michael@0: 0, NULL, sizeof(SGNDigestInfo) }, michael@0: { DER_INLINE, michael@0: offsetof(SGNDigestInfo,digestAlgorithm), michael@0: SECAlgorithmIDTemplate, }, michael@0: { DER_OCTET_STRING, michael@0: offsetof(SGNDigestInfo,digest), }, michael@0: { 0, } michael@0: }; michael@0: michael@0: /* michael@0: * encode RSA PKCS #1 Signature data before signing... michael@0: */ michael@0: SECStatus michael@0: RSA_HashSign(SECOidTag hashOid, NSSLOWKEYPrivateKey *key, michael@0: unsigned char *sig, unsigned int *sigLen, unsigned int maxLen, michael@0: const unsigned char *hash, unsigned int hashLen) michael@0: { michael@0: SECStatus rv = SECFailure; michael@0: SECItem digder; michael@0: PLArenaPool *arena = NULL; michael@0: SGNDigestInfo *di = NULL; michael@0: michael@0: digder.data = NULL; michael@0: michael@0: arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); michael@0: if (!arena) { michael@0: goto loser; michael@0: } michael@0: michael@0: /* Construct digest info */ michael@0: di = SGN_CreateDigestInfo(hashOid, hash, hashLen); michael@0: if (!di) { michael@0: goto loser; michael@0: } michael@0: michael@0: /* Der encode the digest as a DigestInfo */ michael@0: rv = DER_Encode(arena, &digder, SGNDigestInfoTemplate, di); michael@0: if (rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: michael@0: /* michael@0: ** Encrypt signature after constructing appropriate PKCS#1 signature michael@0: ** block michael@0: */ michael@0: rv = RSA_Sign(&key->u.rsa, sig, sigLen, maxLen, digder.data, michael@0: digder.len); michael@0: if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { michael@0: sftk_fatalError = PR_TRUE; michael@0: } michael@0: michael@0: loser: michael@0: SGN_DestroyDigestInfo(di); michael@0: if (arena != NULL) { michael@0: PORT_FreeArena(arena, PR_FALSE); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: static SECStatus michael@0: sftk_RSASign(NSSLOWKEYPrivateKey *key, unsigned char *output, michael@0: unsigned int *outputLen, unsigned int maxOutputLen, michael@0: const unsigned char *input, unsigned int inputLen) michael@0: { michael@0: SECStatus rv = SECFailure; michael@0: michael@0: PORT_Assert(key->keyType == NSSLOWKEYRSAKey); michael@0: if (key->keyType != NSSLOWKEYRSAKey) { michael@0: PORT_SetError(SEC_ERROR_INVALID_KEY); michael@0: return SECFailure; michael@0: } michael@0: michael@0: rv = RSA_Sign(&key->u.rsa, output, outputLen, maxOutputLen, input, michael@0: inputLen); michael@0: if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { michael@0: sftk_fatalError = PR_TRUE; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: static SECStatus michael@0: sftk_RSASignRaw(NSSLOWKEYPrivateKey *key, unsigned char *output, michael@0: unsigned int *outputLen, unsigned int maxOutputLen, michael@0: const unsigned char *input, unsigned int inputLen) michael@0: { michael@0: SECStatus rv = SECFailure; michael@0: michael@0: PORT_Assert(key->keyType == NSSLOWKEYRSAKey); michael@0: if (key->keyType != NSSLOWKEYRSAKey) { michael@0: PORT_SetError(SEC_ERROR_INVALID_KEY); michael@0: return SECFailure; michael@0: } michael@0: michael@0: rv = RSA_SignRaw(&key->u.rsa, output, outputLen, maxOutputLen, input, michael@0: inputLen); michael@0: if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { michael@0: sftk_fatalError = PR_TRUE; michael@0: } michael@0: return rv; michael@0: michael@0: } michael@0: michael@0: static SECStatus michael@0: sftk_RSASignPSS(SFTKHashSignInfo *info, unsigned char *sig, michael@0: unsigned int *sigLen, unsigned int maxLen, michael@0: const unsigned char *hash, unsigned int hashLen) michael@0: { michael@0: SECStatus rv = SECFailure; michael@0: HASH_HashType hashAlg; michael@0: HASH_HashType maskHashAlg; michael@0: CK_RSA_PKCS_PSS_PARAMS *params = (CK_RSA_PKCS_PSS_PARAMS *)info->params; michael@0: michael@0: PORT_Assert(info->key->keyType == NSSLOWKEYRSAKey); michael@0: if (info->key->keyType != NSSLOWKEYRSAKey) { michael@0: PORT_SetError(SEC_ERROR_INVALID_KEY); michael@0: return SECFailure; michael@0: } michael@0: michael@0: hashAlg = GetHashTypeFromMechanism(params->hashAlg); michael@0: maskHashAlg = GetHashTypeFromMechanism(params->mgf); michael@0: michael@0: rv = RSA_SignPSS(&info->key->u.rsa, hashAlg, maskHashAlg, NULL, michael@0: params->sLen, sig, sigLen, maxLen, hash, hashLen); michael@0: if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { michael@0: sftk_fatalError = PR_TRUE; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: static SECStatus michael@0: nsc_DSA_Verify_Stub(void *ctx, void *sigBuf, unsigned int sigLen, michael@0: void *dataBuf, unsigned int dataLen) michael@0: { michael@0: SECItem signature, digest; michael@0: NSSLOWKEYPublicKey *key = (NSSLOWKEYPublicKey *)ctx; michael@0: michael@0: signature.data = (unsigned char *)sigBuf; michael@0: signature.len = sigLen; michael@0: digest.data = (unsigned char *)dataBuf; michael@0: digest.len = dataLen; michael@0: return DSA_VerifyDigest(&(key->u.dsa), &signature, &digest); michael@0: } michael@0: michael@0: static SECStatus michael@0: nsc_DSA_Sign_Stub(void *ctx, void *sigBuf, michael@0: unsigned int *sigLen, unsigned int maxSigLen, michael@0: void *dataBuf, unsigned int dataLen) michael@0: { michael@0: SECItem signature, digest; michael@0: SECStatus rv; michael@0: NSSLOWKEYPrivateKey *key = (NSSLOWKEYPrivateKey *)ctx; michael@0: michael@0: signature.data = (unsigned char *)sigBuf; michael@0: signature.len = maxSigLen; michael@0: digest.data = (unsigned char *)dataBuf; michael@0: digest.len = dataLen; michael@0: rv = DSA_SignDigest(&(key->u.dsa), &signature, &digest); michael@0: if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { michael@0: sftk_fatalError = PR_TRUE; michael@0: } michael@0: *sigLen = signature.len; michael@0: return rv; michael@0: } michael@0: michael@0: #ifndef NSS_DISABLE_ECC michael@0: static SECStatus michael@0: nsc_ECDSAVerifyStub(void *ctx, void *sigBuf, unsigned int sigLen, michael@0: void *dataBuf, unsigned int dataLen) michael@0: { michael@0: SECItem signature, digest; michael@0: NSSLOWKEYPublicKey *key = (NSSLOWKEYPublicKey *)ctx; michael@0: michael@0: signature.data = (unsigned char *)sigBuf; michael@0: signature.len = sigLen; michael@0: digest.data = (unsigned char *)dataBuf; michael@0: digest.len = dataLen; michael@0: return ECDSA_VerifyDigest(&(key->u.ec), &signature, &digest); michael@0: } michael@0: michael@0: static SECStatus michael@0: nsc_ECDSASignStub(void *ctx, void *sigBuf, michael@0: unsigned int *sigLen, unsigned int maxSigLen, michael@0: void *dataBuf, unsigned int dataLen) michael@0: { michael@0: SECItem signature, digest; michael@0: SECStatus rv; michael@0: NSSLOWKEYPrivateKey *key = (NSSLOWKEYPrivateKey *)ctx; michael@0: michael@0: signature.data = (unsigned char *)sigBuf; michael@0: signature.len = maxSigLen; michael@0: digest.data = (unsigned char *)dataBuf; michael@0: digest.len = dataLen; michael@0: rv = ECDSA_SignDigest(&(key->u.ec), &signature, &digest); michael@0: if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { michael@0: sftk_fatalError = PR_TRUE; michael@0: } michael@0: *sigLen = signature.len; michael@0: return rv; michael@0: } michael@0: #endif /* NSS_DISABLE_ECC */ michael@0: michael@0: /* NSC_SignInit setups up the signing operations. There are three basic michael@0: * types of signing: michael@0: * (1) the tradition single part, where "Raw RSA" or "Raw DSA" is applied michael@0: * to data in a single Sign operation (which often looks a lot like an michael@0: * encrypt, with data coming in and data going out). michael@0: * (2) Hash based signing, where we continually hash the data, then apply michael@0: * some sort of signature to the end. michael@0: * (3) Block Encryption CBC MAC's, where the Data is encrypted with a key, michael@0: * and only the final block is part of the mac. michael@0: * michael@0: * For case number 3, we initialize a context much like the Encryption Context michael@0: * (in fact we share code). We detect case 3 in C_SignUpdate, C_Sign, and michael@0: * C_Final by the following method... if it's not multi-part, and it's doesn't michael@0: * have a hash context, it must be a block Encryption CBC MAC. michael@0: * michael@0: * For case number 2, we initialize a hash structure, as well as make it michael@0: * multi-part. Updates are simple calls to the hash update function. Final michael@0: * calls the hashend, then passes the result to the 'update' function (which michael@0: * operates as a final signature function). In some hash based MAC'ing (as michael@0: * opposed to hash base signatures), the update function is can be simply a michael@0: * copy (as is the case with HMAC). michael@0: */ michael@0: CK_RV NSC_SignInit(CK_SESSION_HANDLE hSession, michael@0: CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) michael@0: { michael@0: SFTKSession *session; michael@0: SFTKObject *key; michael@0: SFTKSessionContext *context; michael@0: CK_KEY_TYPE key_type; michael@0: CK_RV crv = CKR_OK; michael@0: NSSLOWKEYPrivateKey *privKey; michael@0: SFTKHashSignInfo *info = NULL; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: /* Block Cipher MACing Algorithms use a different Context init method..*/ michael@0: crv = sftk_InitCBCMac(hSession, pMechanism, hKey, CKA_SIGN, SFTK_SIGN); michael@0: if (crv != CKR_FUNCTION_NOT_SUPPORTED) return crv; michael@0: michael@0: /* we're not using a block cipher mac */ michael@0: session = sftk_SessionFromHandle(hSession); michael@0: if (session == NULL) return CKR_SESSION_HANDLE_INVALID; michael@0: crv = sftk_InitGeneric(session,&context,SFTK_SIGN,&key,hKey,&key_type, michael@0: CKO_PRIVATE_KEY,CKA_SIGN); michael@0: if (crv != CKR_OK) { michael@0: sftk_FreeSession(session); michael@0: return crv; michael@0: } michael@0: michael@0: context->multi = PR_FALSE; michael@0: michael@0: #define INIT_RSA_SIGN_MECH(mmm) \ michael@0: case CKM_ ## mmm ## _RSA_PKCS: \ michael@0: context->multi = PR_TRUE; \ michael@0: crv = sftk_doSub ## mmm (context); \ michael@0: if (crv != CKR_OK) break; \ michael@0: context->update = (SFTKCipher) sftk_RSAHashSign; \ michael@0: info = PORT_New(SFTKHashSignInfo); \ michael@0: if (info == NULL) { crv = CKR_HOST_MEMORY; break; } \ michael@0: info->hashOid = SEC_OID_ ## mmm ; \ michael@0: goto finish_rsa; michael@0: michael@0: switch(pMechanism->mechanism) { michael@0: INIT_RSA_SIGN_MECH(MD5) michael@0: INIT_RSA_SIGN_MECH(MD2) michael@0: INIT_RSA_SIGN_MECH(SHA1) michael@0: INIT_RSA_SIGN_MECH(SHA224) michael@0: INIT_RSA_SIGN_MECH(SHA256) michael@0: INIT_RSA_SIGN_MECH(SHA384) michael@0: INIT_RSA_SIGN_MECH(SHA512) michael@0: michael@0: case CKM_RSA_PKCS: michael@0: context->update = (SFTKCipher) sftk_RSASign; michael@0: goto finish_rsa; michael@0: case CKM_RSA_X_509: michael@0: context->update = (SFTKCipher) sftk_RSASignRaw; michael@0: finish_rsa: michael@0: if (key_type != CKK_RSA) { michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: context->rsa = PR_TRUE; michael@0: privKey = sftk_GetPrivKey(key,CKK_RSA,&crv); michael@0: if (privKey == NULL) { michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: /* OK, info is allocated only if we're doing hash and sign mechanism. michael@0: * It's necessary to be able to set the correct OID in the final michael@0: * signature. michael@0: */ michael@0: if (info) { michael@0: info->key = privKey; michael@0: context->cipherInfo = info; michael@0: context->destroy = (SFTKDestroy)sftk_Space; michael@0: } else { michael@0: context->cipherInfo = privKey; michael@0: context->destroy = (SFTKDestroy)sftk_Null; michael@0: } michael@0: context->maxLen = nsslowkey_PrivateModulusLen(privKey); michael@0: break; michael@0: case CKM_RSA_PKCS_PSS: michael@0: if (key_type != CKK_RSA) { michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: context->rsa = PR_TRUE; michael@0: if (pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS) || michael@0: !sftk_ValidatePssParams((const CK_RSA_PKCS_PSS_PARAMS*)pMechanism->pParameter)) { michael@0: crv = CKR_MECHANISM_PARAM_INVALID; michael@0: break; michael@0: } michael@0: info = PORT_New(SFTKHashSignInfo); michael@0: if (info == NULL) { michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: info->params = pMechanism->pParameter; michael@0: info->key = sftk_GetPrivKey(key,CKK_RSA,&crv); michael@0: if (info->key == NULL) { michael@0: PORT_Free(info); michael@0: break; michael@0: } michael@0: context->cipherInfo = info; michael@0: context->destroy = (SFTKDestroy) sftk_Space; michael@0: context->update = (SFTKCipher) sftk_RSASignPSS; michael@0: context->maxLen = nsslowkey_PrivateModulusLen(info->key); michael@0: break; michael@0: michael@0: case CKM_DSA_SHA1: michael@0: context->multi = PR_TRUE; michael@0: crv = sftk_doSubSHA1(context); michael@0: if (crv != CKR_OK) break; michael@0: /* fall through */ michael@0: case CKM_DSA: michael@0: if (key_type != CKK_DSA) { michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: privKey = sftk_GetPrivKey(key,CKK_DSA,&crv); michael@0: if (privKey == NULL) { michael@0: break; michael@0: } michael@0: context->cipherInfo = privKey; michael@0: context->update = (SFTKCipher) nsc_DSA_Sign_Stub; michael@0: context->destroy = (privKey == key->objectInfo) ? michael@0: (SFTKDestroy) sftk_Null:(SFTKDestroy)sftk_FreePrivKey; michael@0: context->maxLen = DSA_MAX_SIGNATURE_LEN; michael@0: michael@0: break; michael@0: michael@0: #ifndef NSS_DISABLE_ECC michael@0: case CKM_ECDSA_SHA1: michael@0: context->multi = PR_TRUE; michael@0: crv = sftk_doSubSHA1(context); michael@0: if (crv != CKR_OK) break; michael@0: /* fall through */ michael@0: case CKM_ECDSA: michael@0: if (key_type != CKK_EC) { michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: privKey = sftk_GetPrivKey(key,CKK_EC,&crv); michael@0: if (privKey == NULL) { michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: context->cipherInfo = privKey; michael@0: context->update = (SFTKCipher) nsc_ECDSASignStub; michael@0: context->destroy = (privKey == key->objectInfo) ? michael@0: (SFTKDestroy) sftk_Null:(SFTKDestroy)sftk_FreePrivKey; michael@0: context->maxLen = MAX_ECKEY_LEN * 2; michael@0: michael@0: break; michael@0: #endif /* NSS_DISABLE_ECC */ michael@0: michael@0: #define INIT_HMAC_MECH(mmm) \ michael@0: case CKM_ ## mmm ## _HMAC_GENERAL: \ michael@0: crv = sftk_doHMACInit(context, HASH_Alg ## mmm ,key, \ michael@0: *(CK_ULONG *)pMechanism->pParameter); \ michael@0: break; \ michael@0: case CKM_ ## mmm ## _HMAC: \ michael@0: crv = sftk_doHMACInit(context, HASH_Alg ## mmm ,key, mmm ## _LENGTH); \ michael@0: break; michael@0: michael@0: INIT_HMAC_MECH(MD2) michael@0: INIT_HMAC_MECH(MD5) michael@0: INIT_HMAC_MECH(SHA224) michael@0: INIT_HMAC_MECH(SHA256) michael@0: INIT_HMAC_MECH(SHA384) michael@0: INIT_HMAC_MECH(SHA512) michael@0: michael@0: case CKM_SHA_1_HMAC_GENERAL: michael@0: crv = sftk_doHMACInit(context,HASH_AlgSHA1,key, michael@0: *(CK_ULONG *)pMechanism->pParameter); michael@0: break; michael@0: case CKM_SHA_1_HMAC: michael@0: crv = sftk_doHMACInit(context,HASH_AlgSHA1,key,SHA1_LENGTH); michael@0: break; michael@0: michael@0: case CKM_SSL3_MD5_MAC: michael@0: crv = sftk_doSSLMACInit(context,SEC_OID_MD5,key, michael@0: *(CK_ULONG *)pMechanism->pParameter); michael@0: break; michael@0: case CKM_SSL3_SHA1_MAC: michael@0: crv = sftk_doSSLMACInit(context,SEC_OID_SHA1,key, michael@0: *(CK_ULONG *)pMechanism->pParameter); michael@0: break; michael@0: case CKM_TLS_PRF_GENERAL: michael@0: crv = sftk_TLSPRFInit(context, key, key_type, HASH_AlgNULL); michael@0: break; michael@0: case CKM_NSS_TLS_PRF_GENERAL_SHA256: michael@0: crv = sftk_TLSPRFInit(context, key, key_type, HASH_AlgSHA256); michael@0: break; michael@0: michael@0: case CKM_NSS_HMAC_CONSTANT_TIME: { michael@0: sftk_MACConstantTimeCtx *ctx = michael@0: sftk_HMACConstantTime_New(pMechanism,key); michael@0: CK_ULONG *intpointer; michael@0: michael@0: if (ctx == NULL) { michael@0: crv = CKR_ARGUMENTS_BAD; michael@0: break; michael@0: } michael@0: intpointer = PORT_New(CK_ULONG); michael@0: if (intpointer == NULL) { michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: *intpointer = ctx->hash->length; michael@0: michael@0: context->cipherInfo = intpointer; michael@0: context->hashInfo = ctx; michael@0: context->currentMech = pMechanism->mechanism; michael@0: context->hashUpdate = sftk_HMACConstantTime_Update; michael@0: context->hashdestroy = sftk_MACConstantTime_DestroyContext; michael@0: context->end = sftk_MACConstantTime_EndHash; michael@0: context->update = (SFTKCipher) sftk_SignCopy; michael@0: context->destroy = sftk_Space; michael@0: context->maxLen = 64; michael@0: context->multi = PR_TRUE; michael@0: break; michael@0: } michael@0: michael@0: case CKM_NSS_SSL3_MAC_CONSTANT_TIME: { michael@0: sftk_MACConstantTimeCtx *ctx = michael@0: sftk_SSLv3MACConstantTime_New(pMechanism,key); michael@0: CK_ULONG *intpointer; michael@0: michael@0: if (ctx == NULL) { michael@0: crv = CKR_ARGUMENTS_BAD; michael@0: break; michael@0: } michael@0: intpointer = PORT_New(CK_ULONG); michael@0: if (intpointer == NULL) { michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: *intpointer = ctx->hash->length; michael@0: michael@0: context->cipherInfo = intpointer; michael@0: context->hashInfo = ctx; michael@0: context->currentMech = pMechanism->mechanism; michael@0: context->hashUpdate = sftk_SSLv3MACConstantTime_Update; michael@0: context->hashdestroy = sftk_MACConstantTime_DestroyContext; michael@0: context->end = sftk_MACConstantTime_EndHash; michael@0: context->update = (SFTKCipher) sftk_SignCopy; michael@0: context->destroy = sftk_Space; michael@0: context->maxLen = 64; michael@0: context->multi = PR_TRUE; michael@0: break; michael@0: } michael@0: michael@0: default: michael@0: crv = CKR_MECHANISM_INVALID; michael@0: break; michael@0: } michael@0: michael@0: if (crv != CKR_OK) { michael@0: if (info) PORT_Free(info); michael@0: sftk_FreeContext(context); michael@0: sftk_FreeSession(session); michael@0: return crv; michael@0: } michael@0: sftk_SetContextByType(session, SFTK_SIGN, context); michael@0: sftk_FreeSession(session); michael@0: return CKR_OK; michael@0: } michael@0: michael@0: /** MAC one block of data by block cipher michael@0: */ michael@0: static CK_RV michael@0: sftk_MACBlock( SFTKSessionContext *ctx, void *blk ) michael@0: { michael@0: unsigned int outlen; michael@0: return ( SECSuccess == (ctx->update)( ctx->cipherInfo, ctx->macBuf, &outlen, michael@0: SFTK_MAX_BLOCK_SIZE, blk, ctx->blockSize )) michael@0: ? CKR_OK : sftk_MapCryptError(PORT_GetError()); michael@0: } michael@0: michael@0: /** MAC last (incomplete) block of data by block cipher michael@0: * michael@0: * Call once, then terminate MACing operation. michael@0: */ michael@0: static CK_RV michael@0: sftk_MACFinal( SFTKSessionContext *ctx ) michael@0: { michael@0: unsigned int padLen = ctx->padDataLength; michael@0: /* pad and proceed the residual */ michael@0: if( padLen ) { michael@0: /* shd clr ctx->padLen to make sftk_MACFinal idempotent */ michael@0: PORT_Memset( ctx->padBuf + padLen, 0, ctx->blockSize - padLen ); michael@0: return sftk_MACBlock( ctx, ctx->padBuf ); michael@0: } else michael@0: return CKR_OK; michael@0: } michael@0: michael@0: /** The common implementation for {Sign,Verify}Update. (S/V only vary in their michael@0: * setup and final operations). michael@0: * michael@0: * A call which results in an error terminates the operation [PKCS#11,v2.11] michael@0: */ michael@0: static CK_RV michael@0: sftk_MACUpdate(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pPart, michael@0: CK_ULONG ulPartLen,SFTKContextType type) michael@0: { michael@0: SFTKSession *session; michael@0: SFTKSessionContext *context; michael@0: CK_RV crv; michael@0: michael@0: /* make sure we're legal */ michael@0: crv = sftk_GetContext(hSession,&context,type, PR_TRUE, &session ); michael@0: if (crv != CKR_OK) return crv; michael@0: michael@0: if (context->hashInfo) { michael@0: (*context->hashUpdate)(context->hashInfo, pPart, ulPartLen); michael@0: } else { michael@0: /* must be block cipher MACing */ michael@0: michael@0: unsigned int blkSize = context->blockSize; michael@0: unsigned char *residual = /* free room in context->padBuf */ michael@0: context->padBuf + context->padDataLength; michael@0: unsigned int minInput = /* min input for MACing at least one block */ michael@0: blkSize - context->padDataLength; michael@0: michael@0: /* not enough data even for one block */ michael@0: if( ulPartLen < minInput ) { michael@0: PORT_Memcpy( residual, pPart, ulPartLen ); michael@0: context->padDataLength += ulPartLen; michael@0: goto cleanup; michael@0: } michael@0: /* MACing residual */ michael@0: if( context->padDataLength ) { michael@0: PORT_Memcpy( residual, pPart, minInput ); michael@0: ulPartLen -= minInput; michael@0: pPart += minInput; michael@0: if( CKR_OK != (crv = sftk_MACBlock( context, context->padBuf )) ) michael@0: goto terminate; michael@0: } michael@0: /* MACing full blocks */ michael@0: while( ulPartLen >= blkSize ) michael@0: { michael@0: if( CKR_OK != (crv = sftk_MACBlock( context, pPart )) ) michael@0: goto terminate; michael@0: ulPartLen -= blkSize; michael@0: pPart += blkSize; michael@0: } michael@0: /* save the residual */ michael@0: if( (context->padDataLength = ulPartLen) ) michael@0: PORT_Memcpy( context->padBuf, pPart, ulPartLen ); michael@0: } /* blk cipher MACing */ michael@0: michael@0: goto cleanup; michael@0: michael@0: terminate: michael@0: sftk_TerminateOp( session, type, context ); michael@0: cleanup: michael@0: sftk_FreeSession(session); michael@0: return crv; michael@0: } michael@0: michael@0: /* NSC_SignUpdate continues a multiple-part signature operation, michael@0: * where the signature is (will be) an appendix to the data, michael@0: * and plaintext cannot be recovered from the signature michael@0: * michael@0: * A call which results in an error terminates the operation [PKCS#11,v2.11] michael@0: */ michael@0: CK_RV NSC_SignUpdate(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pPart, michael@0: CK_ULONG ulPartLen) michael@0: { michael@0: CHECK_FORK(); michael@0: return sftk_MACUpdate(hSession, pPart, ulPartLen, SFTK_SIGN); michael@0: } michael@0: michael@0: michael@0: /* NSC_SignFinal finishes a multiple-part signature operation, michael@0: * returning the signature. */ michael@0: CK_RV NSC_SignFinal(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pSignature, michael@0: CK_ULONG_PTR pulSignatureLen) michael@0: { michael@0: SFTKSession *session; michael@0: SFTKSessionContext *context; michael@0: unsigned int outlen; michael@0: unsigned int maxoutlen = *pulSignatureLen; michael@0: CK_RV crv; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: /* make sure we're legal */ michael@0: crv = sftk_GetContext(hSession,&context,SFTK_SIGN,PR_TRUE,&session); michael@0: if (crv != CKR_OK) return crv; michael@0: michael@0: if (context->hashInfo) { michael@0: unsigned int digestLen; michael@0: unsigned char tmpbuf[SFTK_MAX_MAC_LENGTH]; michael@0: michael@0: if( !pSignature ) { michael@0: outlen = context->maxLen; goto finish; michael@0: } michael@0: (*context->end)(context->hashInfo, tmpbuf, &digestLen, sizeof(tmpbuf)); michael@0: if( SECSuccess != (context->update)(context->cipherInfo, pSignature, michael@0: &outlen, maxoutlen, tmpbuf, digestLen)) michael@0: crv = sftk_MapCryptError(PORT_GetError()); michael@0: /* CKR_BUFFER_TOO_SMALL here isn't continuable, let operation terminate. michael@0: * Keeping "too small" CK_RV intact is a standard violation, but allows michael@0: * application read EXACT signature length */ michael@0: } else { michael@0: /* must be block cipher MACing */ michael@0: outlen = context->macSize; michael@0: /* null or "too small" buf doesn't terminate operation [PKCS#11,v2.11]*/ michael@0: if( !pSignature || maxoutlen < outlen ) { michael@0: if( pSignature ) crv = CKR_BUFFER_TOO_SMALL; michael@0: goto finish; michael@0: } michael@0: if( CKR_OK == (crv = sftk_MACFinal( context )) ) michael@0: PORT_Memcpy(pSignature, context->macBuf, outlen ); michael@0: } michael@0: michael@0: sftk_TerminateOp( session, SFTK_SIGN, context ); michael@0: finish: michael@0: *pulSignatureLen = outlen; michael@0: sftk_FreeSession(session); michael@0: return crv; michael@0: } michael@0: michael@0: /* NSC_Sign signs (encrypts with private key) data in a single part, michael@0: * where the signature is (will be) an appendix to the data, michael@0: * and plaintext cannot be recovered from the signature */ michael@0: CK_RV NSC_Sign(CK_SESSION_HANDLE hSession, michael@0: CK_BYTE_PTR pData,CK_ULONG ulDataLen,CK_BYTE_PTR pSignature, michael@0: CK_ULONG_PTR pulSignatureLen) michael@0: { michael@0: SFTKSession *session; michael@0: SFTKSessionContext *context; michael@0: CK_RV crv; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: /* make sure we're legal */ michael@0: crv = sftk_GetContext(hSession,&context,SFTK_SIGN,PR_FALSE,&session); michael@0: if (crv != CKR_OK) return crv; michael@0: michael@0: if (!pSignature) { michael@0: /* see also how C_SignUpdate implements this */ michael@0: *pulSignatureLen = (!context->multi || context->hashInfo) michael@0: ? context->maxLen michael@0: : context->macSize; /* must be block cipher MACing */ michael@0: goto finish; michael@0: } michael@0: michael@0: /* multi part Signing are completely implemented by SignUpdate and michael@0: * sign Final */ michael@0: if (context->multi) { michael@0: /* SignFinal can't follow failed SignUpdate */ michael@0: if( CKR_OK == (crv = NSC_SignUpdate(hSession,pData,ulDataLen) )) michael@0: crv = NSC_SignFinal(hSession, pSignature, pulSignatureLen); michael@0: } else { michael@0: /* single-part PKC signature (e.g. CKM_ECDSA) */ michael@0: unsigned int outlen; michael@0: unsigned int maxoutlen = *pulSignatureLen; michael@0: if( SECSuccess != (*context->update)(context->cipherInfo, pSignature, michael@0: &outlen, maxoutlen, pData, ulDataLen)) michael@0: crv = sftk_MapCryptError(PORT_GetError()); michael@0: *pulSignatureLen = (CK_ULONG) outlen; michael@0: /* "too small" here is certainly continuable */ michael@0: if( crv != CKR_BUFFER_TOO_SMALL ) michael@0: sftk_TerminateOp(session, SFTK_SIGN, context); michael@0: } /* single-part */ michael@0: michael@0: finish: michael@0: sftk_FreeSession(session); michael@0: return crv; michael@0: } michael@0: michael@0: michael@0: /* michael@0: ************** Crypto Functions: Sign Recover ************************ michael@0: */ michael@0: /* NSC_SignRecoverInit initializes a signature operation, michael@0: * where the (digest) data can be recovered from the signature. michael@0: * E.g. encryption with the user's private key */ michael@0: CK_RV NSC_SignRecoverInit(CK_SESSION_HANDLE hSession, michael@0: CK_MECHANISM_PTR pMechanism,CK_OBJECT_HANDLE hKey) michael@0: { michael@0: CHECK_FORK(); michael@0: michael@0: switch (pMechanism->mechanism) { michael@0: case CKM_RSA_PKCS: michael@0: case CKM_RSA_X_509: michael@0: return NSC_SignInit(hSession,pMechanism,hKey); michael@0: default: michael@0: break; michael@0: } michael@0: return CKR_MECHANISM_INVALID; michael@0: } michael@0: michael@0: michael@0: /* NSC_SignRecover signs data in a single operation michael@0: * where the (digest) data can be recovered from the signature. michael@0: * E.g. encryption with the user's private key */ michael@0: CK_RV NSC_SignRecover(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, michael@0: CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) michael@0: { michael@0: CHECK_FORK(); michael@0: michael@0: return NSC_Sign(hSession,pData,ulDataLen,pSignature,pulSignatureLen); michael@0: } michael@0: michael@0: /* michael@0: ************** Crypto Functions: verify ************************ michael@0: */ michael@0: michael@0: /* Handle RSA Signature formatting */ michael@0: static SECStatus michael@0: sftk_hashCheckSign(SFTKHashVerifyInfo *info, const unsigned char *sig, michael@0: unsigned int sigLen, const unsigned char *digest, michael@0: unsigned int digestLen) michael@0: { michael@0: PORT_Assert(info->key->keyType == NSSLOWKEYRSAKey); michael@0: if (info->key->keyType != NSSLOWKEYRSAKey) { michael@0: PORT_SetError(SEC_ERROR_INVALID_KEY); michael@0: return SECFailure; michael@0: } michael@0: michael@0: return RSA_HashCheckSign(info->hashOid, info->key, sig, sigLen, digest, michael@0: digestLen); michael@0: } michael@0: michael@0: SECStatus michael@0: RSA_HashCheckSign(SECOidTag digestOid, NSSLOWKEYPublicKey *key, michael@0: const unsigned char *sig, unsigned int sigLen, michael@0: const unsigned char *digestData, unsigned int digestLen) michael@0: { michael@0: unsigned char *pkcs1DigestInfoData; michael@0: SECItem pkcs1DigestInfo; michael@0: SECItem digest; michael@0: unsigned int bufferSize; michael@0: SECStatus rv; michael@0: michael@0: /* pkcs1DigestInfo.data must be less than key->u.rsa.modulus.len */ michael@0: bufferSize = key->u.rsa.modulus.len; michael@0: pkcs1DigestInfoData = PORT_ZAlloc(bufferSize); michael@0: if (!pkcs1DigestInfoData) { michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: return SECFailure; michael@0: } michael@0: michael@0: pkcs1DigestInfo.data = pkcs1DigestInfoData; michael@0: pkcs1DigestInfo.len = bufferSize; michael@0: michael@0: /* decrypt the block */ michael@0: rv = RSA_CheckSignRecover(&key->u.rsa, pkcs1DigestInfo.data, michael@0: &pkcs1DigestInfo.len, pkcs1DigestInfo.len, michael@0: sig, sigLen); michael@0: if (rv != SECSuccess) { michael@0: PORT_SetError(SEC_ERROR_BAD_SIGNATURE); michael@0: } else { michael@0: digest.data = (PRUint8*) digestData; michael@0: digest.len = digestLen; michael@0: rv = _SGN_VerifyPKCS1DigestInfo( michael@0: digestOid, &digest, &pkcs1DigestInfo, michael@0: PR_TRUE /*XXX: unsafeAllowMissingParameters*/); michael@0: } michael@0: michael@0: PORT_Free(pkcs1DigestInfoData); michael@0: return rv; michael@0: } michael@0: michael@0: static SECStatus michael@0: sftk_RSACheckSign(NSSLOWKEYPublicKey *key, const unsigned char *sig, michael@0: unsigned int sigLen, const unsigned char *digest, michael@0: unsigned int digestLen) michael@0: { michael@0: PORT_Assert(key->keyType == NSSLOWKEYRSAKey); michael@0: if (key->keyType != NSSLOWKEYRSAKey) { michael@0: PORT_SetError(SEC_ERROR_INVALID_KEY); michael@0: return SECFailure; michael@0: } michael@0: michael@0: return RSA_CheckSign(&key->u.rsa, sig, sigLen, digest, digestLen); michael@0: } michael@0: michael@0: static SECStatus michael@0: sftk_RSACheckSignRaw(NSSLOWKEYPublicKey *key, const unsigned char *sig, michael@0: unsigned int sigLen, const unsigned char *digest, michael@0: unsigned int digestLen) michael@0: { michael@0: PORT_Assert(key->keyType == NSSLOWKEYRSAKey); michael@0: if (key->keyType != NSSLOWKEYRSAKey) { michael@0: PORT_SetError(SEC_ERROR_INVALID_KEY); michael@0: return SECFailure; michael@0: } michael@0: michael@0: return RSA_CheckSignRaw(&key->u.rsa, sig, sigLen, digest, digestLen); michael@0: } michael@0: michael@0: static SECStatus michael@0: sftk_RSACheckSignPSS(SFTKHashVerifyInfo *info, const unsigned char *sig, michael@0: unsigned int sigLen, const unsigned char *digest, michael@0: unsigned int digestLen) michael@0: { michael@0: HASH_HashType hashAlg; michael@0: HASH_HashType maskHashAlg; michael@0: CK_RSA_PKCS_PSS_PARAMS *params = (CK_RSA_PKCS_PSS_PARAMS *)info->params; michael@0: michael@0: PORT_Assert(info->key->keyType == NSSLOWKEYRSAKey); michael@0: if (info->key->keyType != NSSLOWKEYRSAKey) { michael@0: PORT_SetError(SEC_ERROR_INVALID_KEY); michael@0: return SECFailure; michael@0: } michael@0: michael@0: hashAlg = GetHashTypeFromMechanism(params->hashAlg); michael@0: maskHashAlg = GetHashTypeFromMechanism(params->mgf); michael@0: michael@0: return RSA_CheckSignPSS(&info->key->u.rsa, hashAlg, maskHashAlg, michael@0: params->sLen, sig, sigLen, digest, digestLen); michael@0: } michael@0: michael@0: /* NSC_VerifyInit initializes a verification operation, michael@0: * where the signature is an appendix to the data, michael@0: * and plaintext cannot be recovered from the signature (e.g. DSA) */ michael@0: CK_RV NSC_VerifyInit(CK_SESSION_HANDLE hSession, michael@0: CK_MECHANISM_PTR pMechanism,CK_OBJECT_HANDLE hKey) michael@0: { michael@0: SFTKSession *session; michael@0: SFTKObject *key; michael@0: SFTKSessionContext *context; michael@0: CK_KEY_TYPE key_type; michael@0: CK_RV crv = CKR_OK; michael@0: NSSLOWKEYPublicKey *pubKey; michael@0: SFTKHashVerifyInfo *info = NULL; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: /* Block Cipher MACing Algorithms use a different Context init method..*/ michael@0: crv = sftk_InitCBCMac(hSession, pMechanism, hKey, CKA_VERIFY, SFTK_VERIFY); michael@0: if (crv != CKR_FUNCTION_NOT_SUPPORTED) return crv; michael@0: michael@0: session = sftk_SessionFromHandle(hSession); michael@0: if (session == NULL) return CKR_SESSION_HANDLE_INVALID; michael@0: crv = sftk_InitGeneric(session,&context,SFTK_VERIFY,&key,hKey,&key_type, michael@0: CKO_PUBLIC_KEY,CKA_VERIFY); michael@0: if (crv != CKR_OK) { michael@0: sftk_FreeSession(session); michael@0: return crv; michael@0: } michael@0: michael@0: context->multi = PR_FALSE; michael@0: michael@0: #define INIT_RSA_VFY_MECH(mmm) \ michael@0: case CKM_ ## mmm ## _RSA_PKCS: \ michael@0: context->multi = PR_TRUE; \ michael@0: crv = sftk_doSub ## mmm (context); \ michael@0: if (crv != CKR_OK) break; \ michael@0: context->verify = (SFTKVerify) sftk_hashCheckSign; \ michael@0: info = PORT_New(SFTKHashVerifyInfo); \ michael@0: if (info == NULL) { crv = CKR_HOST_MEMORY; break; } \ michael@0: info->hashOid = SEC_OID_ ## mmm ; \ michael@0: goto finish_rsa; michael@0: michael@0: switch(pMechanism->mechanism) { michael@0: INIT_RSA_VFY_MECH(MD5) michael@0: INIT_RSA_VFY_MECH(MD2) michael@0: INIT_RSA_VFY_MECH(SHA1) michael@0: INIT_RSA_VFY_MECH(SHA224) michael@0: INIT_RSA_VFY_MECH(SHA256) michael@0: INIT_RSA_VFY_MECH(SHA384) michael@0: INIT_RSA_VFY_MECH(SHA512) michael@0: michael@0: case CKM_RSA_PKCS: michael@0: context->verify = (SFTKVerify) sftk_RSACheckSign; michael@0: goto finish_rsa; michael@0: case CKM_RSA_X_509: michael@0: context->verify = (SFTKVerify) sftk_RSACheckSignRaw; michael@0: finish_rsa: michael@0: if (key_type != CKK_RSA) { michael@0: if (info) PORT_Free(info); michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: context->rsa = PR_TRUE; michael@0: pubKey = sftk_GetPubKey(key,CKK_RSA,&crv); michael@0: if (pubKey == NULL) { michael@0: if (info) PORT_Free(info); michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: if (info) { michael@0: info->key = pubKey; michael@0: context->cipherInfo = info; michael@0: context->destroy = sftk_Space; michael@0: } else { michael@0: context->cipherInfo = pubKey; michael@0: context->destroy = sftk_Null; michael@0: } michael@0: break; michael@0: case CKM_RSA_PKCS_PSS: michael@0: if (key_type != CKK_RSA) { michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: context->rsa = PR_TRUE; michael@0: if (pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS) || michael@0: !sftk_ValidatePssParams((const CK_RSA_PKCS_PSS_PARAMS*)pMechanism->pParameter)) { michael@0: crv = CKR_MECHANISM_PARAM_INVALID; michael@0: break; michael@0: } michael@0: info = PORT_New(SFTKHashVerifyInfo); michael@0: if (info == NULL) { michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: info->params = pMechanism->pParameter; michael@0: info->key = sftk_GetPubKey(key,CKK_RSA,&crv); michael@0: if (info->key == NULL) { michael@0: PORT_Free(info); michael@0: break; michael@0: } michael@0: context->cipherInfo = info; michael@0: context->destroy = (SFTKDestroy) sftk_Space; michael@0: context->verify = (SFTKVerify) sftk_RSACheckSignPSS; michael@0: break; michael@0: case CKM_DSA_SHA1: michael@0: context->multi = PR_TRUE; michael@0: crv = sftk_doSubSHA1(context); michael@0: if (crv != CKR_OK) break; michael@0: /* fall through */ michael@0: case CKM_DSA: michael@0: if (key_type != CKK_DSA) { michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: pubKey = sftk_GetPubKey(key,CKK_DSA,&crv); michael@0: if (pubKey == NULL) { michael@0: break; michael@0: } michael@0: context->cipherInfo = pubKey; michael@0: context->verify = (SFTKVerify) nsc_DSA_Verify_Stub; michael@0: context->destroy = sftk_Null; michael@0: break; michael@0: #ifndef NSS_DISABLE_ECC michael@0: case CKM_ECDSA_SHA1: michael@0: context->multi = PR_TRUE; michael@0: crv = sftk_doSubSHA1(context); michael@0: if (crv != CKR_OK) break; michael@0: /* fall through */ michael@0: case CKM_ECDSA: michael@0: if (key_type != CKK_EC) { michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: pubKey = sftk_GetPubKey(key,CKK_EC,&crv); michael@0: if (pubKey == NULL) { michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: context->cipherInfo = pubKey; michael@0: context->verify = (SFTKVerify) nsc_ECDSAVerifyStub; michael@0: context->destroy = sftk_Null; michael@0: break; michael@0: #endif /* NSS_DISABLE_ECC */ michael@0: michael@0: INIT_HMAC_MECH(MD2) michael@0: INIT_HMAC_MECH(MD5) michael@0: INIT_HMAC_MECH(SHA224) michael@0: INIT_HMAC_MECH(SHA256) michael@0: INIT_HMAC_MECH(SHA384) michael@0: INIT_HMAC_MECH(SHA512) michael@0: michael@0: case CKM_SHA_1_HMAC_GENERAL: michael@0: crv = sftk_doHMACInit(context,HASH_AlgSHA1,key, michael@0: *(CK_ULONG *)pMechanism->pParameter); michael@0: break; michael@0: case CKM_SHA_1_HMAC: michael@0: crv = sftk_doHMACInit(context,HASH_AlgSHA1,key,SHA1_LENGTH); michael@0: break; michael@0: michael@0: case CKM_SSL3_MD5_MAC: michael@0: crv = sftk_doSSLMACInit(context,SEC_OID_MD5,key, michael@0: *(CK_ULONG *)pMechanism->pParameter); michael@0: break; michael@0: case CKM_SSL3_SHA1_MAC: michael@0: crv = sftk_doSSLMACInit(context,SEC_OID_SHA1,key, michael@0: *(CK_ULONG *)pMechanism->pParameter); michael@0: break; michael@0: case CKM_TLS_PRF_GENERAL: michael@0: crv = sftk_TLSPRFInit(context, key, key_type, HASH_AlgNULL); michael@0: break; michael@0: case CKM_NSS_TLS_PRF_GENERAL_SHA256: michael@0: crv = sftk_TLSPRFInit(context, key, key_type, HASH_AlgSHA256); michael@0: break; michael@0: michael@0: default: michael@0: crv = CKR_MECHANISM_INVALID; michael@0: break; michael@0: } michael@0: michael@0: if (crv != CKR_OK) { michael@0: if (info) PORT_Free(info); michael@0: sftk_FreeContext(context); michael@0: sftk_FreeSession(session); michael@0: return crv; michael@0: } michael@0: sftk_SetContextByType(session, SFTK_VERIFY, context); michael@0: sftk_FreeSession(session); michael@0: return CKR_OK; michael@0: } michael@0: michael@0: /* NSC_Verify verifies a signature in a single-part operation, michael@0: * where the signature is an appendix to the data, michael@0: * and plaintext cannot be recovered from the signature */ michael@0: CK_RV NSC_Verify(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, michael@0: CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) michael@0: { michael@0: SFTKSession *session; michael@0: SFTKSessionContext *context; michael@0: CK_RV crv; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: /* make sure we're legal */ michael@0: crv = sftk_GetContext(hSession,&context,SFTK_VERIFY,PR_FALSE,&session); michael@0: if (crv != CKR_OK) return crv; michael@0: michael@0: /* multi part Verifying are completely implemented by VerifyUpdate and michael@0: * VerifyFinal */ michael@0: if (context->multi) { michael@0: /* VerifyFinal can't follow failed VerifyUpdate */ michael@0: if( CKR_OK == (crv = NSC_VerifyUpdate(hSession, pData, ulDataLen))) michael@0: crv = NSC_VerifyFinal(hSession, pSignature, ulSignatureLen); michael@0: } else { michael@0: if (SECSuccess != (*context->verify)(context->cipherInfo,pSignature, michael@0: ulSignatureLen, pData, ulDataLen)) michael@0: crv = sftk_MapCryptError(PORT_GetError()); michael@0: michael@0: sftk_TerminateOp( session, SFTK_VERIFY, context ); michael@0: } michael@0: sftk_FreeSession(session); michael@0: return crv; michael@0: } michael@0: michael@0: michael@0: /* NSC_VerifyUpdate continues a multiple-part verification operation, michael@0: * where the signature is an appendix to the data, michael@0: * and plaintext cannot be recovered from the signature michael@0: * michael@0: * A call which results in an error terminates the operation [PKCS#11,v2.11] michael@0: */ michael@0: CK_RV NSC_VerifyUpdate( CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, michael@0: CK_ULONG ulPartLen) michael@0: { michael@0: CHECK_FORK(); michael@0: return sftk_MACUpdate(hSession, pPart, ulPartLen, SFTK_VERIFY); michael@0: } michael@0: michael@0: michael@0: /* NSC_VerifyFinal finishes a multiple-part verification operation, michael@0: * checking the signature. */ michael@0: CK_RV NSC_VerifyFinal(CK_SESSION_HANDLE hSession, michael@0: CK_BYTE_PTR pSignature,CK_ULONG ulSignatureLen) michael@0: { michael@0: SFTKSession *session; michael@0: SFTKSessionContext *context; michael@0: CK_RV crv; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: if (!pSignature) michael@0: return CKR_ARGUMENTS_BAD; michael@0: michael@0: /* make sure we're legal */ michael@0: crv = sftk_GetContext(hSession,&context,SFTK_VERIFY,PR_TRUE,&session); michael@0: if (crv != CKR_OK) michael@0: return crv; michael@0: michael@0: if (context->hashInfo) { michael@0: unsigned int digestLen; michael@0: unsigned char tmpbuf[SFTK_MAX_MAC_LENGTH]; michael@0: michael@0: (*context->end)(context->hashInfo, tmpbuf, &digestLen, sizeof(tmpbuf)); michael@0: if( SECSuccess != (context->verify)(context->cipherInfo, pSignature, michael@0: ulSignatureLen, tmpbuf, digestLen)) michael@0: crv = sftk_MapCryptError(PORT_GetError()); michael@0: } else if (ulSignatureLen != context->macSize) { michael@0: /* must be block cipher MACing */ michael@0: crv = CKR_SIGNATURE_LEN_RANGE; michael@0: } else if (CKR_OK == (crv = sftk_MACFinal(context))) { michael@0: if (PORT_Memcmp(pSignature, context->macBuf, ulSignatureLen)) michael@0: crv = CKR_SIGNATURE_INVALID; michael@0: } michael@0: michael@0: sftk_TerminateOp( session, SFTK_VERIFY, context ); michael@0: sftk_FreeSession(session); michael@0: return crv; michael@0: michael@0: } michael@0: michael@0: /* michael@0: ************** Crypto Functions: Verify Recover ************************ michael@0: */ michael@0: static SECStatus michael@0: sftk_RSACheckSignRecover(NSSLOWKEYPublicKey *key, unsigned char *data, michael@0: unsigned int *dataLen, unsigned int maxDataLen, michael@0: const unsigned char *sig, unsigned int sigLen) michael@0: { michael@0: PORT_Assert(key->keyType == NSSLOWKEYRSAKey); michael@0: if (key->keyType != NSSLOWKEYRSAKey) { michael@0: PORT_SetError(SEC_ERROR_INVALID_KEY); michael@0: return SECFailure; michael@0: } michael@0: michael@0: return RSA_CheckSignRecover(&key->u.rsa, data, dataLen, maxDataLen, michael@0: sig, sigLen); michael@0: } michael@0: michael@0: static SECStatus michael@0: sftk_RSACheckSignRecoverRaw(NSSLOWKEYPublicKey *key, unsigned char *data, michael@0: unsigned int *dataLen, unsigned int maxDataLen, michael@0: const unsigned char *sig, unsigned int sigLen) michael@0: { michael@0: PORT_Assert(key->keyType == NSSLOWKEYRSAKey); michael@0: if (key->keyType != NSSLOWKEYRSAKey) { michael@0: PORT_SetError(SEC_ERROR_INVALID_KEY); michael@0: return SECFailure; michael@0: } michael@0: michael@0: return RSA_CheckSignRecoverRaw(&key->u.rsa, data, dataLen, maxDataLen, michael@0: sig, sigLen); michael@0: } michael@0: michael@0: /* NSC_VerifyRecoverInit initializes a signature verification operation, michael@0: * where the data is recovered from the signature. michael@0: * E.g. Decryption with the user's public key */ michael@0: CK_RV NSC_VerifyRecoverInit(CK_SESSION_HANDLE hSession, michael@0: CK_MECHANISM_PTR pMechanism,CK_OBJECT_HANDLE hKey) michael@0: { michael@0: SFTKSession *session; michael@0: SFTKObject *key; michael@0: SFTKSessionContext *context; michael@0: CK_KEY_TYPE key_type; michael@0: CK_RV crv = CKR_OK; michael@0: NSSLOWKEYPublicKey *pubKey; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: session = sftk_SessionFromHandle(hSession); michael@0: if (session == NULL) return CKR_SESSION_HANDLE_INVALID; michael@0: crv = sftk_InitGeneric(session,&context,SFTK_VERIFY_RECOVER, michael@0: &key,hKey,&key_type,CKO_PUBLIC_KEY,CKA_VERIFY_RECOVER); michael@0: if (crv != CKR_OK) { michael@0: sftk_FreeSession(session); michael@0: return crv; michael@0: } michael@0: michael@0: context->multi = PR_TRUE; michael@0: michael@0: switch(pMechanism->mechanism) { michael@0: case CKM_RSA_PKCS: michael@0: case CKM_RSA_X_509: michael@0: if (key_type != CKK_RSA) { michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: context->multi = PR_FALSE; michael@0: context->rsa = PR_TRUE; michael@0: pubKey = sftk_GetPubKey(key,CKK_RSA,&crv); michael@0: if (pubKey == NULL) { michael@0: break; michael@0: } michael@0: context->cipherInfo = pubKey; michael@0: context->update = (SFTKCipher) (pMechanism->mechanism == CKM_RSA_X_509 michael@0: ? sftk_RSACheckSignRecoverRaw : sftk_RSACheckSignRecover); michael@0: context->destroy = sftk_Null; michael@0: break; michael@0: default: michael@0: crv = CKR_MECHANISM_INVALID; michael@0: break; michael@0: } michael@0: michael@0: if (crv != CKR_OK) { michael@0: PORT_Free(context); michael@0: sftk_FreeSession(session); michael@0: return crv; michael@0: } michael@0: sftk_SetContextByType(session, SFTK_VERIFY_RECOVER, context); michael@0: sftk_FreeSession(session); michael@0: return CKR_OK; michael@0: } michael@0: michael@0: michael@0: /* NSC_VerifyRecover verifies a signature in a single-part operation, michael@0: * where the data is recovered from the signature. michael@0: * E.g. Decryption with the user's public key */ michael@0: CK_RV NSC_VerifyRecover(CK_SESSION_HANDLE hSession, michael@0: CK_BYTE_PTR pSignature,CK_ULONG ulSignatureLen, michael@0: CK_BYTE_PTR pData,CK_ULONG_PTR pulDataLen) michael@0: { michael@0: SFTKSession *session; michael@0: SFTKSessionContext *context; michael@0: unsigned int outlen; michael@0: unsigned int maxoutlen = *pulDataLen; michael@0: CK_RV crv; michael@0: SECStatus rv; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: /* make sure we're legal */ michael@0: crv = sftk_GetContext(hSession,&context,SFTK_VERIFY_RECOVER, michael@0: PR_FALSE,&session); michael@0: if (crv != CKR_OK) return crv; michael@0: if (pData == NULL) { michael@0: /* to return the actual size, we need to do the decrypt, just return michael@0: * the max size, which is the size of the input signature. */ michael@0: *pulDataLen = ulSignatureLen; michael@0: rv = SECSuccess; michael@0: goto finish; michael@0: } michael@0: michael@0: rv = (*context->update)(context->cipherInfo, pData, &outlen, maxoutlen, michael@0: pSignature, ulSignatureLen); michael@0: *pulDataLen = (CK_ULONG) outlen; michael@0: michael@0: sftk_TerminateOp(session, SFTK_VERIFY_RECOVER, context); michael@0: finish: michael@0: sftk_FreeSession(session); michael@0: return (rv == SECSuccess) ? CKR_OK : sftk_MapVerifyError(PORT_GetError()); michael@0: } michael@0: michael@0: /* michael@0: **************************** Random Functions: ************************ michael@0: */ michael@0: michael@0: /* NSC_SeedRandom mixes additional seed material into the token's random number michael@0: * generator. */ michael@0: CK_RV NSC_SeedRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSeed, michael@0: CK_ULONG ulSeedLen) michael@0: { michael@0: SECStatus rv; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: rv = RNG_RandomUpdate(pSeed, ulSeedLen); michael@0: return (rv == SECSuccess) ? CKR_OK : sftk_MapCryptError(PORT_GetError()); michael@0: } michael@0: michael@0: /* NSC_GenerateRandom generates random data. */ michael@0: CK_RV NSC_GenerateRandom(CK_SESSION_HANDLE hSession, michael@0: CK_BYTE_PTR pRandomData, CK_ULONG ulRandomLen) michael@0: { michael@0: SECStatus rv; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: rv = RNG_GenerateGlobalRandomBytes(pRandomData, ulRandomLen); michael@0: /* michael@0: * This may fail with SEC_ERROR_NEED_RANDOM, which means the RNG isn't michael@0: * seeded with enough entropy. michael@0: */ michael@0: return (rv == SECSuccess) ? CKR_OK : sftk_MapCryptError(PORT_GetError()); michael@0: } michael@0: michael@0: /* michael@0: **************************** Key Functions: ************************ michael@0: */ michael@0: michael@0: michael@0: /* michael@0: * generate a password based encryption key. This code uses michael@0: * PKCS5 to do the work. michael@0: */ michael@0: static CK_RV michael@0: nsc_pbe_key_gen(NSSPKCS5PBEParameter *pkcs5_pbe, CK_MECHANISM_PTR pMechanism, michael@0: void *buf, CK_ULONG *key_length, PRBool faulty3DES) michael@0: { michael@0: SECItem *pbe_key = NULL, iv, pwitem; michael@0: CK_PBE_PARAMS *pbe_params = NULL; michael@0: CK_PKCS5_PBKD2_PARAMS *pbkd2_params = NULL; michael@0: michael@0: *key_length = 0; michael@0: iv.data = NULL; iv.len = 0; michael@0: michael@0: if (pMechanism->mechanism == CKM_PKCS5_PBKD2) { michael@0: pbkd2_params = (CK_PKCS5_PBKD2_PARAMS *)pMechanism->pParameter; michael@0: pwitem.data = (unsigned char *)pbkd2_params->pPassword; michael@0: /* was this a typo in the PKCS #11 spec? */ michael@0: pwitem.len = *pbkd2_params->ulPasswordLen; michael@0: } else { michael@0: pbe_params = (CK_PBE_PARAMS *)pMechanism->pParameter; michael@0: pwitem.data = (unsigned char *)pbe_params->pPassword; michael@0: pwitem.len = pbe_params->ulPasswordLen; michael@0: } michael@0: pbe_key = nsspkcs5_ComputeKeyAndIV(pkcs5_pbe, &pwitem, &iv, faulty3DES); michael@0: if (pbe_key == NULL) { michael@0: return CKR_HOST_MEMORY; michael@0: } michael@0: michael@0: PORT_Memcpy(buf, pbe_key->data, pbe_key->len); michael@0: *key_length = pbe_key->len; michael@0: SECITEM_ZfreeItem(pbe_key, PR_TRUE); michael@0: pbe_key = NULL; michael@0: michael@0: if (iv.data) { michael@0: if (pbe_params && pbe_params->pInitVector != NULL) { michael@0: PORT_Memcpy(pbe_params->pInitVector, iv.data, iv.len); michael@0: } michael@0: PORT_Free(iv.data); michael@0: } michael@0: michael@0: return CKR_OK; michael@0: } michael@0: michael@0: /* michael@0: * this is coded for "full" support. These selections will be limitted to michael@0: * the official subset by freebl. michael@0: */ michael@0: static unsigned int michael@0: sftk_GetSubPrimeFromPrime(unsigned int primeBits) michael@0: { michael@0: if (primeBits <= 1024) { michael@0: return 160; michael@0: } else if (primeBits <= 2048) { michael@0: return 224; michael@0: } else if (primeBits <= 3072) { michael@0: return 256; michael@0: } else if (primeBits <= 7680) { michael@0: return 384; michael@0: } else { michael@0: return 512; michael@0: } michael@0: } michael@0: michael@0: static CK_RV michael@0: nsc_parameter_gen(CK_KEY_TYPE key_type, SFTKObject *key) michael@0: { michael@0: SFTKAttribute *attribute; michael@0: CK_ULONG counter; michael@0: unsigned int seedBits = 0; michael@0: unsigned int subprimeBits = 0; michael@0: unsigned int primeBits; michael@0: unsigned int j = 8; /* default to 1024 bits */ michael@0: CK_RV crv = CKR_OK; michael@0: PQGParams *params = NULL; michael@0: PQGVerify *vfy = NULL; michael@0: SECStatus rv; michael@0: michael@0: attribute = sftk_FindAttribute(key, CKA_PRIME_BITS); michael@0: if (attribute == NULL) { michael@0: return CKR_TEMPLATE_INCOMPLETE; michael@0: } michael@0: primeBits = (unsigned int) *(CK_ULONG *)attribute->attrib.pValue; michael@0: sftk_FreeAttribute(attribute); michael@0: if (primeBits < 1024) { michael@0: j = PQG_PBITS_TO_INDEX(primeBits); michael@0: if (j == (unsigned int)-1) { michael@0: return CKR_ATTRIBUTE_VALUE_INVALID; michael@0: } michael@0: } michael@0: michael@0: attribute = sftk_FindAttribute(key, CKA_NETSCAPE_PQG_SEED_BITS); michael@0: if (attribute != NULL) { michael@0: seedBits = (unsigned int) *(CK_ULONG *)attribute->attrib.pValue; michael@0: sftk_FreeAttribute(attribute); michael@0: } michael@0: michael@0: attribute = sftk_FindAttribute(key, CKA_SUBPRIME_BITS); michael@0: if (attribute != NULL) { michael@0: subprimeBits = (unsigned int) *(CK_ULONG *)attribute->attrib.pValue; michael@0: sftk_FreeAttribute(attribute); michael@0: } michael@0: michael@0: sftk_DeleteAttributeType(key,CKA_PRIME_BITS); michael@0: sftk_DeleteAttributeType(key,CKA_SUBPRIME_BITS); michael@0: sftk_DeleteAttributeType(key,CKA_NETSCAPE_PQG_SEED_BITS); michael@0: michael@0: /* use the old PQG interface if we have old input data */ michael@0: if ((primeBits < 1024) || ((primeBits == 1024) && (subprimeBits == 0))) { michael@0: if (seedBits == 0) { michael@0: rv = PQG_ParamGen(j, ¶ms, &vfy); michael@0: } else { michael@0: rv = PQG_ParamGenSeedLen(j,seedBits/8, ¶ms, &vfy); michael@0: } michael@0: } else { michael@0: if (subprimeBits == 0) { michael@0: subprimeBits = sftk_GetSubPrimeFromPrime(primeBits); michael@0: } michael@0: if (seedBits == 0) { michael@0: seedBits = primeBits; michael@0: } michael@0: rv = PQG_ParamGenV2(primeBits, subprimeBits, seedBits/8, ¶ms, &vfy); michael@0: } michael@0: michael@0: michael@0: michael@0: if (rv != SECSuccess) { michael@0: if (PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { michael@0: sftk_fatalError = PR_TRUE; michael@0: } michael@0: return sftk_MapCryptError(PORT_GetError()); michael@0: } michael@0: crv = sftk_AddAttributeType(key,CKA_PRIME, michael@0: params->prime.data, params->prime.len); michael@0: if (crv != CKR_OK) goto loser; michael@0: crv = sftk_AddAttributeType(key,CKA_SUBPRIME, michael@0: params->subPrime.data, params->subPrime.len); michael@0: if (crv != CKR_OK) goto loser; michael@0: crv = sftk_AddAttributeType(key,CKA_BASE, michael@0: params->base.data, params->base.len); michael@0: if (crv != CKR_OK) goto loser; michael@0: counter = vfy->counter; michael@0: crv = sftk_AddAttributeType(key,CKA_NETSCAPE_PQG_COUNTER, michael@0: &counter, sizeof(counter)); michael@0: crv = sftk_AddAttributeType(key,CKA_NETSCAPE_PQG_SEED, michael@0: vfy->seed.data, vfy->seed.len); michael@0: if (crv != CKR_OK) goto loser; michael@0: crv = sftk_AddAttributeType(key,CKA_NETSCAPE_PQG_H, michael@0: vfy->h.data, vfy->h.len); michael@0: if (crv != CKR_OK) goto loser; michael@0: michael@0: loser: michael@0: PQG_DestroyParams(params); michael@0: michael@0: if (vfy) { michael@0: PQG_DestroyVerify(vfy); michael@0: } michael@0: return crv; michael@0: } michael@0: michael@0: michael@0: static CK_RV michael@0: nsc_SetupBulkKeyGen(CK_MECHANISM_TYPE mechanism, CK_KEY_TYPE *key_type, michael@0: CK_ULONG *key_length) michael@0: { michael@0: CK_RV crv = CKR_OK; michael@0: michael@0: switch (mechanism) { michael@0: case CKM_RC2_KEY_GEN: michael@0: *key_type = CKK_RC2; michael@0: if (*key_length == 0) crv = CKR_TEMPLATE_INCOMPLETE; michael@0: break; michael@0: #if NSS_SOFTOKEN_DOES_RC5 michael@0: case CKM_RC5_KEY_GEN: michael@0: *key_type = CKK_RC5; michael@0: if (*key_length == 0) crv = CKR_TEMPLATE_INCOMPLETE; michael@0: break; michael@0: #endif michael@0: case CKM_RC4_KEY_GEN: michael@0: *key_type = CKK_RC4; michael@0: if (*key_length == 0) crv = CKR_TEMPLATE_INCOMPLETE; michael@0: break; michael@0: case CKM_GENERIC_SECRET_KEY_GEN: michael@0: *key_type = CKK_GENERIC_SECRET; michael@0: if (*key_length == 0) crv = CKR_TEMPLATE_INCOMPLETE; michael@0: break; michael@0: case CKM_CDMF_KEY_GEN: michael@0: *key_type = CKK_CDMF; michael@0: *key_length = 8; michael@0: break; michael@0: case CKM_DES_KEY_GEN: michael@0: *key_type = CKK_DES; michael@0: *key_length = 8; michael@0: break; michael@0: case CKM_DES2_KEY_GEN: michael@0: *key_type = CKK_DES2; michael@0: *key_length = 16; michael@0: break; michael@0: case CKM_DES3_KEY_GEN: michael@0: *key_type = CKK_DES3; michael@0: *key_length = 24; michael@0: break; michael@0: case CKM_SEED_KEY_GEN: michael@0: *key_type = CKK_SEED; michael@0: *key_length = 16; michael@0: break; michael@0: case CKM_CAMELLIA_KEY_GEN: michael@0: *key_type = CKK_CAMELLIA; michael@0: if (*key_length == 0) crv = CKR_TEMPLATE_INCOMPLETE; michael@0: break; michael@0: case CKM_AES_KEY_GEN: michael@0: *key_type = CKK_AES; michael@0: if (*key_length == 0) crv = CKR_TEMPLATE_INCOMPLETE; michael@0: break; michael@0: default: michael@0: PORT_Assert(0); michael@0: crv = CKR_MECHANISM_INVALID; michael@0: break; michael@0: } michael@0: michael@0: return crv; michael@0: } michael@0: michael@0: CK_RV michael@0: nsc_SetupHMACKeyGen(CK_MECHANISM_PTR pMechanism, NSSPKCS5PBEParameter **pbe) michael@0: { michael@0: SECItem salt; michael@0: CK_PBE_PARAMS *pbe_params = NULL; michael@0: NSSPKCS5PBEParameter *params; michael@0: PLArenaPool *arena = NULL; michael@0: SECStatus rv; michael@0: michael@0: *pbe = NULL; michael@0: michael@0: arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); michael@0: if (arena == NULL) { michael@0: return CKR_HOST_MEMORY; michael@0: } michael@0: michael@0: params = (NSSPKCS5PBEParameter *) PORT_ArenaZAlloc(arena, michael@0: sizeof(NSSPKCS5PBEParameter)); michael@0: if (params == NULL) { michael@0: PORT_FreeArena(arena,PR_TRUE); michael@0: return CKR_HOST_MEMORY; michael@0: } michael@0: michael@0: params->poolp = arena; michael@0: params->ivLen = 0; michael@0: params->pbeType = NSSPKCS5_PKCS12_V2; michael@0: params->hashType = HASH_AlgSHA1; michael@0: params->encAlg = SEC_OID_SHA1; /* any invalid value */ michael@0: params->is2KeyDES = PR_FALSE; michael@0: params->keyID = pbeBitGenIntegrityKey; michael@0: pbe_params = (CK_PBE_PARAMS *)pMechanism->pParameter; michael@0: params->iter = pbe_params->ulIteration; michael@0: michael@0: salt.data = (unsigned char *)pbe_params->pSalt; michael@0: salt.len = (unsigned int)pbe_params->ulSaltLen; michael@0: rv = SECITEM_CopyItem(arena,¶ms->salt,&salt); michael@0: if (rv != SECSuccess) { michael@0: PORT_FreeArena(arena,PR_TRUE); michael@0: return CKR_HOST_MEMORY; michael@0: } michael@0: switch (pMechanism->mechanism) { michael@0: case CKM_NETSCAPE_PBE_SHA1_HMAC_KEY_GEN: michael@0: case CKM_PBA_SHA1_WITH_SHA1_HMAC: michael@0: params->hashType = HASH_AlgSHA1; michael@0: params->keyLen = 20; michael@0: break; michael@0: case CKM_NETSCAPE_PBE_MD5_HMAC_KEY_GEN: michael@0: params->hashType = HASH_AlgMD5; michael@0: params->keyLen = 16; michael@0: break; michael@0: case CKM_NETSCAPE_PBE_MD2_HMAC_KEY_GEN: michael@0: params->hashType = HASH_AlgMD2; michael@0: params->keyLen = 16; michael@0: break; michael@0: default: michael@0: PORT_FreeArena(arena,PR_TRUE); michael@0: return CKR_MECHANISM_INVALID; michael@0: } michael@0: *pbe = params; michael@0: return CKR_OK; michael@0: } michael@0: michael@0: /* maybe this should be table driven? */ michael@0: static CK_RV michael@0: nsc_SetupPBEKeyGen(CK_MECHANISM_PTR pMechanism, NSSPKCS5PBEParameter **pbe, michael@0: CK_KEY_TYPE *key_type, CK_ULONG *key_length) michael@0: { michael@0: CK_RV crv = CKR_OK; michael@0: SECOidData *oid; michael@0: CK_PBE_PARAMS *pbe_params = NULL; michael@0: NSSPKCS5PBEParameter *params = NULL; michael@0: CK_PKCS5_PBKD2_PARAMS *pbkd2_params = NULL; michael@0: SECItem salt; michael@0: CK_ULONG iteration = 0; michael@0: michael@0: *pbe = NULL; michael@0: michael@0: oid = SECOID_FindOIDByMechanism(pMechanism->mechanism); michael@0: if (oid == NULL) { michael@0: return CKR_MECHANISM_INVALID; michael@0: } michael@0: michael@0: if (pMechanism->mechanism == CKM_PKCS5_PBKD2) { michael@0: pbkd2_params = (CK_PKCS5_PBKD2_PARAMS *)pMechanism->pParameter; michael@0: if (pbkd2_params->saltSource != CKZ_SALT_SPECIFIED) { michael@0: return CKR_MECHANISM_PARAM_INVALID; michael@0: } michael@0: salt.data = (unsigned char *)pbkd2_params->pSaltSourceData; michael@0: salt.len = (unsigned int)pbkd2_params->ulSaltSourceDataLen; michael@0: iteration = pbkd2_params->iterations; michael@0: } else { michael@0: pbe_params = (CK_PBE_PARAMS *)pMechanism->pParameter; michael@0: salt.data = (unsigned char *)pbe_params->pSalt; michael@0: salt.len = (unsigned int)pbe_params->ulSaltLen; michael@0: iteration = pbe_params->ulIteration; michael@0: } michael@0: params=nsspkcs5_NewParam(oid->offset, &salt, iteration); michael@0: if (params == NULL) { michael@0: return CKR_MECHANISM_INVALID; michael@0: } michael@0: michael@0: switch (params->encAlg) { michael@0: case SEC_OID_DES_CBC: michael@0: *key_type = CKK_DES; michael@0: *key_length = params->keyLen; michael@0: break; michael@0: case SEC_OID_DES_EDE3_CBC: michael@0: *key_type = params->is2KeyDES ? CKK_DES2 : CKK_DES3; michael@0: *key_length = params->keyLen; michael@0: break; michael@0: case SEC_OID_RC2_CBC: michael@0: *key_type = CKK_RC2; michael@0: *key_length = params->keyLen; michael@0: break; michael@0: case SEC_OID_RC4: michael@0: *key_type = CKK_RC4; michael@0: *key_length = params->keyLen; michael@0: break; michael@0: case SEC_OID_PKCS5_PBKDF2: michael@0: /* sigh, PKCS #11 currently only defines SHA1 for the KDF hash type. michael@0: * we do the check here because this where we would handle multiple michael@0: * hash types in the future */ michael@0: if (pbkd2_params == NULL || michael@0: pbkd2_params->prf != CKP_PKCS5_PBKD2_HMAC_SHA1) { michael@0: crv = CKR_MECHANISM_PARAM_INVALID; michael@0: break; michael@0: } michael@0: /* key type must already be set */ michael@0: if (*key_type == CKK_INVALID_KEY_TYPE) { michael@0: crv = CKR_TEMPLATE_INCOMPLETE; michael@0: break; michael@0: } michael@0: /* PBKDF2 needs to calculate the key length from the other parameters michael@0: */ michael@0: if (*key_length == 0) { michael@0: *key_length = sftk_MapKeySize(*key_type); michael@0: } michael@0: if (*key_length == 0) { michael@0: crv = CKR_TEMPLATE_INCOMPLETE; michael@0: break; michael@0: } michael@0: params->keyLen = *key_length; michael@0: break; michael@0: default: michael@0: crv = CKR_MECHANISM_INVALID; michael@0: nsspkcs5_DestroyPBEParameter(params); michael@0: break; michael@0: } michael@0: if (crv == CKR_OK) { michael@0: *pbe = params; michael@0: } michael@0: return crv; michael@0: } michael@0: michael@0: /* NSC_GenerateKey generates a secret key, creating a new key object. */ michael@0: CK_RV NSC_GenerateKey(CK_SESSION_HANDLE hSession, michael@0: CK_MECHANISM_PTR pMechanism,CK_ATTRIBUTE_PTR pTemplate,CK_ULONG ulCount, michael@0: CK_OBJECT_HANDLE_PTR phKey) michael@0: { michael@0: SFTKObject *key; michael@0: SFTKSession *session; michael@0: PRBool checkWeak = PR_FALSE; michael@0: CK_ULONG key_length = 0; michael@0: CK_KEY_TYPE key_type = CKK_INVALID_KEY_TYPE; michael@0: CK_OBJECT_CLASS objclass = CKO_SECRET_KEY; michael@0: CK_RV crv = CKR_OK; michael@0: CK_BBOOL cktrue = CK_TRUE; michael@0: int i; michael@0: SFTKSlot *slot = sftk_SlotFromSessionHandle(hSession); michael@0: unsigned char buf[MAX_KEY_LEN]; michael@0: enum {nsc_pbe, nsc_ssl, nsc_bulk, nsc_param, nsc_jpake} key_gen_type; michael@0: NSSPKCS5PBEParameter *pbe_param; michael@0: SSL3RSAPreMasterSecret *rsa_pms; michael@0: CK_VERSION *version; michael@0: /* in very old versions of NSS, there were implementation errors with key michael@0: * generation methods. We want to beable to read these, but not michael@0: * produce them any more. The affected algorithm was 3DES. michael@0: */ michael@0: PRBool faultyPBE3DES = PR_FALSE; michael@0: HASH_HashType hashType; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: if (!slot) { michael@0: return CKR_SESSION_HANDLE_INVALID; michael@0: } michael@0: /* michael@0: * now lets create an object to hang the attributes off of michael@0: */ michael@0: key = sftk_NewObject(slot); /* fill in the handle later */ michael@0: if (key == NULL) { michael@0: return CKR_HOST_MEMORY; michael@0: } michael@0: michael@0: /* michael@0: * load the template values into the object michael@0: */ michael@0: for (i=0; i < (int) ulCount; i++) { michael@0: if (pTemplate[i].type == CKA_VALUE_LEN) { michael@0: key_length = *(CK_ULONG *)pTemplate[i].pValue; michael@0: continue; michael@0: } michael@0: /* some algorithms need keytype specified */ michael@0: if (pTemplate[i].type == CKA_KEY_TYPE) { michael@0: key_type = *(CK_ULONG *)pTemplate[i].pValue; michael@0: continue; michael@0: } michael@0: michael@0: crv = sftk_AddAttributeType(key,sftk_attr_expand(&pTemplate[i])); michael@0: if (crv != CKR_OK) break; michael@0: } michael@0: if (crv != CKR_OK) { michael@0: sftk_FreeObject(key); michael@0: return crv; michael@0: } michael@0: michael@0: /* make sure we don't have any class, key_type, or value fields */ michael@0: sftk_DeleteAttributeType(key,CKA_CLASS); michael@0: sftk_DeleteAttributeType(key,CKA_KEY_TYPE); michael@0: sftk_DeleteAttributeType(key,CKA_VALUE); michael@0: michael@0: /* Now Set up the parameters to generate the key (based on mechanism) */ michael@0: key_gen_type = nsc_bulk; /* bulk key by default */ michael@0: switch (pMechanism->mechanism) { michael@0: case CKM_CDMF_KEY_GEN: michael@0: case CKM_DES_KEY_GEN: michael@0: case CKM_DES2_KEY_GEN: michael@0: case CKM_DES3_KEY_GEN: michael@0: checkWeak = PR_TRUE; michael@0: /* fall through */ michael@0: case CKM_RC2_KEY_GEN: michael@0: case CKM_RC4_KEY_GEN: michael@0: case CKM_GENERIC_SECRET_KEY_GEN: michael@0: case CKM_SEED_KEY_GEN: michael@0: case CKM_CAMELLIA_KEY_GEN: michael@0: case CKM_AES_KEY_GEN: michael@0: #if NSS_SOFTOKEN_DOES_RC5 michael@0: case CKM_RC5_KEY_GEN: michael@0: #endif michael@0: crv = nsc_SetupBulkKeyGen(pMechanism->mechanism,&key_type,&key_length); michael@0: break; michael@0: case CKM_SSL3_PRE_MASTER_KEY_GEN: michael@0: key_type = CKK_GENERIC_SECRET; michael@0: key_length = 48; michael@0: key_gen_type = nsc_ssl; michael@0: break; michael@0: case CKM_PBA_SHA1_WITH_SHA1_HMAC: michael@0: case CKM_NETSCAPE_PBE_SHA1_HMAC_KEY_GEN: michael@0: case CKM_NETSCAPE_PBE_MD5_HMAC_KEY_GEN: michael@0: case CKM_NETSCAPE_PBE_MD2_HMAC_KEY_GEN: michael@0: key_gen_type = nsc_pbe; michael@0: key_type = CKK_GENERIC_SECRET; michael@0: crv = nsc_SetupHMACKeyGen(pMechanism, &pbe_param); michael@0: break; michael@0: case CKM_NETSCAPE_PBE_SHA1_FAULTY_3DES_CBC: michael@0: faultyPBE3DES = PR_TRUE; michael@0: /* fall through */ michael@0: case CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC: michael@0: case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC: michael@0: case CKM_NETSCAPE_PBE_SHA1_DES_CBC: michael@0: case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC: michael@0: case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4: michael@0: case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC4: michael@0: case CKM_PBE_SHA1_DES3_EDE_CBC: michael@0: case CKM_PBE_SHA1_DES2_EDE_CBC: michael@0: case CKM_PBE_SHA1_RC2_128_CBC: michael@0: case CKM_PBE_SHA1_RC2_40_CBC: michael@0: case CKM_PBE_SHA1_RC4_128: michael@0: case CKM_PBE_SHA1_RC4_40: michael@0: case CKM_PBE_MD5_DES_CBC: michael@0: case CKM_PBE_MD2_DES_CBC: michael@0: case CKM_PKCS5_PBKD2: michael@0: key_gen_type = nsc_pbe; michael@0: crv = nsc_SetupPBEKeyGen(pMechanism,&pbe_param, &key_type, &key_length); michael@0: break; michael@0: case CKM_DSA_PARAMETER_GEN: michael@0: key_gen_type = nsc_param; michael@0: key_type = CKK_DSA; michael@0: objclass = CKO_KG_PARAMETERS; michael@0: crv = CKR_OK; michael@0: break; michael@0: case CKM_NSS_JPAKE_ROUND1_SHA1: hashType = HASH_AlgSHA1; goto jpake1; michael@0: case CKM_NSS_JPAKE_ROUND1_SHA256: hashType = HASH_AlgSHA256; goto jpake1; michael@0: case CKM_NSS_JPAKE_ROUND1_SHA384: hashType = HASH_AlgSHA384; goto jpake1; michael@0: case CKM_NSS_JPAKE_ROUND1_SHA512: hashType = HASH_AlgSHA512; goto jpake1; michael@0: jpake1: michael@0: key_gen_type = nsc_jpake; michael@0: key_type = CKK_NSS_JPAKE_ROUND1; michael@0: objclass = CKO_PRIVATE_KEY; michael@0: if (pMechanism->pParameter == NULL || michael@0: pMechanism->ulParameterLen != sizeof(CK_NSS_JPAKERound1Params)) { michael@0: crv = CKR_MECHANISM_PARAM_INVALID; michael@0: break; michael@0: } michael@0: if (sftk_isTrue(key, CKA_TOKEN)) { michael@0: crv = CKR_TEMPLATE_INCONSISTENT; michael@0: break; michael@0: } michael@0: crv = CKR_OK; michael@0: break; michael@0: default: michael@0: crv = CKR_MECHANISM_INVALID; michael@0: break; michael@0: } michael@0: michael@0: /* make sure we aren't going to overflow the buffer */ michael@0: if (sizeof(buf) < key_length) { michael@0: /* someone is getting pretty optimistic about how big their key can michael@0: * be... */ michael@0: crv = CKR_TEMPLATE_INCONSISTENT; michael@0: } michael@0: michael@0: if (crv != CKR_OK) { sftk_FreeObject(key); return crv; } michael@0: michael@0: /* if there was no error, michael@0: * key_type *MUST* be set in the switch statement above */ michael@0: PORT_Assert( key_type != CKK_INVALID_KEY_TYPE ); michael@0: michael@0: /* michael@0: * now to the actual key gen. michael@0: */ michael@0: switch (key_gen_type) { michael@0: case nsc_pbe: michael@0: crv = nsc_pbe_key_gen(pbe_param, pMechanism, buf, &key_length, michael@0: faultyPBE3DES); michael@0: nsspkcs5_DestroyPBEParameter(pbe_param); michael@0: break; michael@0: case nsc_ssl: michael@0: rsa_pms = (SSL3RSAPreMasterSecret *)buf; michael@0: version = (CK_VERSION *)pMechanism->pParameter; michael@0: rsa_pms->client_version[0] = version->major; michael@0: rsa_pms->client_version[1] = version->minor; michael@0: crv = michael@0: NSC_GenerateRandom(0,&rsa_pms->random[0], sizeof(rsa_pms->random)); michael@0: break; michael@0: case nsc_bulk: michael@0: /* get the key, check for weak keys and repeat if found */ michael@0: do { michael@0: crv = NSC_GenerateRandom(0, buf, key_length); michael@0: } while (crv == CKR_OK && checkWeak && sftk_IsWeakKey(buf,key_type)); michael@0: break; michael@0: case nsc_param: michael@0: /* generate parameters */ michael@0: *buf = 0; michael@0: crv = nsc_parameter_gen(key_type,key); michael@0: break; michael@0: case nsc_jpake: michael@0: crv = jpake_Round1(hashType, michael@0: (CK_NSS_JPAKERound1Params *) pMechanism->pParameter, michael@0: key); michael@0: break; michael@0: } michael@0: michael@0: if (crv != CKR_OK) { sftk_FreeObject(key); return crv; } michael@0: michael@0: /* Add the class, key_type, and value */ michael@0: crv = sftk_AddAttributeType(key,CKA_CLASS,&objclass,sizeof(CK_OBJECT_CLASS)); michael@0: if (crv != CKR_OK) { sftk_FreeObject(key); return crv; } michael@0: crv = sftk_AddAttributeType(key,CKA_KEY_TYPE,&key_type,sizeof(CK_KEY_TYPE)); michael@0: if (crv != CKR_OK) { sftk_FreeObject(key); return crv; } michael@0: if (key_length != 0) { michael@0: crv = sftk_AddAttributeType(key,CKA_VALUE,buf,key_length); michael@0: if (crv != CKR_OK) { sftk_FreeObject(key); return crv; } michael@0: } michael@0: michael@0: /* get the session */ michael@0: session = sftk_SessionFromHandle(hSession); michael@0: if (session == NULL) { michael@0: sftk_FreeObject(key); michael@0: return CKR_SESSION_HANDLE_INVALID; michael@0: } michael@0: michael@0: /* michael@0: * handle the base object stuff michael@0: */ michael@0: crv = sftk_handleObject(key,session); michael@0: sftk_FreeSession(session); michael@0: if (sftk_isTrue(key,CKA_SENSITIVE)) { michael@0: sftk_forceAttribute(key,CKA_ALWAYS_SENSITIVE,&cktrue,sizeof(CK_BBOOL)); michael@0: } michael@0: if (!sftk_isTrue(key,CKA_EXTRACTABLE)) { michael@0: sftk_forceAttribute(key,CKA_NEVER_EXTRACTABLE,&cktrue,sizeof(CK_BBOOL)); michael@0: } michael@0: michael@0: *phKey = key->handle; michael@0: sftk_FreeObject(key); michael@0: return crv; michael@0: } michael@0: michael@0: #define PAIRWISE_DIGEST_LENGTH SHA1_LENGTH /* 160-bits */ michael@0: #define PAIRWISE_MESSAGE_LENGTH 20 /* 160-bits */ michael@0: michael@0: /* michael@0: * FIPS 140-2 pairwise consistency check utilized to validate key pair. michael@0: * michael@0: * This function returns michael@0: * CKR_OK if pairwise consistency check passed michael@0: * CKR_GENERAL_ERROR if pairwise consistency check failed michael@0: * other error codes if paiswise consistency check could not be michael@0: * performed, for example, CKR_HOST_MEMORY. michael@0: */ michael@0: static CK_RV michael@0: sftk_PairwiseConsistencyCheck(CK_SESSION_HANDLE hSession, michael@0: SFTKObject *publicKey, SFTKObject *privateKey, CK_KEY_TYPE keyType) michael@0: { michael@0: /* michael@0: * Key type Mechanism type michael@0: * -------------------------------- michael@0: * For encrypt/decrypt: CKK_RSA => CKM_RSA_PKCS michael@0: * others => CKM_INVALID_MECHANISM michael@0: * michael@0: * For sign/verify: CKK_RSA => CKM_RSA_PKCS michael@0: * CKK_DSA => CKM_DSA michael@0: * CKK_EC => CKM_ECDSA michael@0: * others => CKM_INVALID_MECHANISM michael@0: * michael@0: * None of these mechanisms has a parameter. michael@0: */ michael@0: CK_MECHANISM mech = {0, NULL, 0}; michael@0: michael@0: CK_ULONG modulusLen; michael@0: CK_ULONG subPrimeLen; michael@0: PRBool isEncryptable = PR_FALSE; michael@0: PRBool canSignVerify = PR_FALSE; michael@0: PRBool isDerivable = PR_FALSE; michael@0: CK_RV crv; michael@0: michael@0: /* Variables used for Encrypt/Decrypt functions. */ michael@0: unsigned char *known_message = (unsigned char *)"Known Crypto Message"; michael@0: unsigned char plaintext[PAIRWISE_MESSAGE_LENGTH]; michael@0: CK_ULONG bytes_decrypted; michael@0: unsigned char *ciphertext; michael@0: unsigned char *text_compared; michael@0: CK_ULONG bytes_encrypted; michael@0: CK_ULONG bytes_compared; michael@0: CK_ULONG pairwise_digest_length = PAIRWISE_DIGEST_LENGTH; michael@0: michael@0: /* Variables used for Signature/Verification functions. */ michael@0: /* Must be at least 256 bits for DSA2 digest */ michael@0: unsigned char *known_digest = (unsigned char *) michael@0: "Mozilla Rules the World through NSS!"; michael@0: unsigned char *signature; michael@0: CK_ULONG signature_length; michael@0: michael@0: if (keyType == CKK_RSA) { michael@0: SFTKAttribute *attribute; michael@0: michael@0: /* Get modulus length of private key. */ michael@0: attribute = sftk_FindAttribute(privateKey, CKA_MODULUS); michael@0: if (attribute == NULL) { michael@0: return CKR_DEVICE_ERROR; michael@0: } michael@0: modulusLen = attribute->attrib.ulValueLen; michael@0: if (*(unsigned char *)attribute->attrib.pValue == 0) { michael@0: modulusLen--; michael@0: } michael@0: sftk_FreeAttribute(attribute); michael@0: } else if (keyType == CKK_DSA) { michael@0: SFTKAttribute *attribute; michael@0: michael@0: /* Get subprime length of private key. */ michael@0: attribute = sftk_FindAttribute(privateKey, CKA_SUBPRIME); michael@0: if (attribute == NULL) { michael@0: return CKR_DEVICE_ERROR; michael@0: } michael@0: subPrimeLen = attribute->attrib.ulValueLen; michael@0: if (subPrimeLen > 1 && *(unsigned char *)attribute->attrib.pValue == 0) { michael@0: subPrimeLen--; michael@0: } michael@0: sftk_FreeAttribute(attribute); michael@0: } michael@0: michael@0: /**************************************************/ michael@0: /* Pairwise Consistency Check of Encrypt/Decrypt. */ michael@0: /**************************************************/ michael@0: michael@0: isEncryptable = sftk_isTrue(privateKey, CKA_DECRYPT); michael@0: michael@0: /* michael@0: * If the decryption attribute is set, attempt to encrypt michael@0: * with the public key and decrypt with the private key. michael@0: */ michael@0: if (isEncryptable) { michael@0: if (keyType != CKK_RSA) { michael@0: return CKR_DEVICE_ERROR; michael@0: } michael@0: bytes_encrypted = modulusLen; michael@0: mech.mechanism = CKM_RSA_PKCS; michael@0: michael@0: /* Allocate space for ciphertext. */ michael@0: ciphertext = (unsigned char *) PORT_ZAlloc(bytes_encrypted); michael@0: if (ciphertext == NULL) { michael@0: return CKR_HOST_MEMORY; michael@0: } michael@0: michael@0: /* Prepare for encryption using the public key. */ michael@0: crv = NSC_EncryptInit(hSession, &mech, publicKey->handle); michael@0: if (crv != CKR_OK) { michael@0: PORT_Free(ciphertext); michael@0: return crv; michael@0: } michael@0: michael@0: /* Encrypt using the public key. */ michael@0: crv = NSC_Encrypt(hSession, michael@0: known_message, michael@0: PAIRWISE_MESSAGE_LENGTH, michael@0: ciphertext, michael@0: &bytes_encrypted); michael@0: if (crv != CKR_OK) { michael@0: PORT_Free(ciphertext); michael@0: return crv; michael@0: } michael@0: michael@0: /* Always use the smaller of these two values . . . */ michael@0: bytes_compared = PR_MIN(bytes_encrypted, PAIRWISE_MESSAGE_LENGTH); michael@0: michael@0: /* michael@0: * If there was a failure, the plaintext michael@0: * goes at the end, therefore . . . michael@0: */ michael@0: text_compared = ciphertext + bytes_encrypted - bytes_compared; michael@0: michael@0: /* michael@0: * Check to ensure that ciphertext does michael@0: * NOT EQUAL known input message text michael@0: * per FIPS PUB 140-2 directive. michael@0: */ michael@0: if (PORT_Memcmp(text_compared, known_message, michael@0: bytes_compared) == 0) { michael@0: /* Set error to Invalid PRIVATE Key. */ michael@0: PORT_SetError(SEC_ERROR_INVALID_KEY); michael@0: PORT_Free(ciphertext); michael@0: return CKR_GENERAL_ERROR; michael@0: } michael@0: michael@0: /* Prepare for decryption using the private key. */ michael@0: crv = NSC_DecryptInit(hSession, &mech, privateKey->handle); michael@0: if (crv != CKR_OK) { michael@0: PORT_Free(ciphertext); michael@0: return crv; michael@0: } michael@0: michael@0: memset(plaintext, 0, PAIRWISE_MESSAGE_LENGTH); michael@0: michael@0: /* michael@0: * Initialize bytes decrypted to be the michael@0: * expected PAIRWISE_MESSAGE_LENGTH. michael@0: */ michael@0: bytes_decrypted = PAIRWISE_MESSAGE_LENGTH; michael@0: michael@0: /* michael@0: * Decrypt using the private key. michael@0: * NOTE: No need to reset the michael@0: * value of bytes_encrypted. michael@0: */ michael@0: crv = NSC_Decrypt(hSession, michael@0: ciphertext, michael@0: bytes_encrypted, michael@0: plaintext, michael@0: &bytes_decrypted); michael@0: michael@0: /* Finished with ciphertext; free it. */ michael@0: PORT_Free(ciphertext); michael@0: michael@0: if (crv != CKR_OK) { michael@0: return crv; michael@0: } michael@0: michael@0: /* michael@0: * Check to ensure that the output plaintext michael@0: * does EQUAL known input message text. michael@0: */ michael@0: if ((bytes_decrypted != PAIRWISE_MESSAGE_LENGTH) || michael@0: (PORT_Memcmp(plaintext, known_message, michael@0: PAIRWISE_MESSAGE_LENGTH) != 0)) { michael@0: /* Set error to Bad PUBLIC Key. */ michael@0: PORT_SetError(SEC_ERROR_BAD_KEY); michael@0: return CKR_GENERAL_ERROR; michael@0: } michael@0: } michael@0: michael@0: /**********************************************/ michael@0: /* Pairwise Consistency Check of Sign/Verify. */ michael@0: /**********************************************/ michael@0: michael@0: canSignVerify = sftk_isTrue(privateKey, CKA_SIGN); michael@0: michael@0: if (canSignVerify) { michael@0: /* Determine length of signature. */ michael@0: switch (keyType) { michael@0: case CKK_RSA: michael@0: signature_length = modulusLen; michael@0: mech.mechanism = CKM_RSA_PKCS; michael@0: break; michael@0: case CKK_DSA: michael@0: signature_length = DSA_MAX_SIGNATURE_LEN; michael@0: pairwise_digest_length = subPrimeLen; michael@0: mech.mechanism = CKM_DSA; michael@0: break; michael@0: #ifndef NSS_DISABLE_ECC michael@0: case CKK_EC: michael@0: signature_length = MAX_ECKEY_LEN * 2; michael@0: mech.mechanism = CKM_ECDSA; michael@0: break; michael@0: #endif michael@0: default: michael@0: return CKR_DEVICE_ERROR; michael@0: } michael@0: michael@0: /* Allocate space for signature data. */ michael@0: signature = (unsigned char *) PORT_ZAlloc(signature_length); michael@0: if (signature == NULL) { michael@0: return CKR_HOST_MEMORY; michael@0: } michael@0: michael@0: /* Sign the known hash using the private key. */ michael@0: crv = NSC_SignInit(hSession, &mech, privateKey->handle); michael@0: if (crv != CKR_OK) { michael@0: PORT_Free(signature); michael@0: return crv; michael@0: } michael@0: michael@0: crv = NSC_Sign(hSession, michael@0: known_digest, michael@0: pairwise_digest_length, michael@0: signature, michael@0: &signature_length); michael@0: if (crv != CKR_OK) { michael@0: PORT_Free(signature); michael@0: return crv; michael@0: } michael@0: michael@0: /* Verify the known hash using the public key. */ michael@0: crv = NSC_VerifyInit(hSession, &mech, publicKey->handle); michael@0: if (crv != CKR_OK) { michael@0: PORT_Free(signature); michael@0: return crv; michael@0: } michael@0: michael@0: crv = NSC_Verify(hSession, michael@0: known_digest, michael@0: pairwise_digest_length, michael@0: signature, michael@0: signature_length); michael@0: michael@0: /* Free signature data. */ michael@0: PORT_Free(signature); michael@0: michael@0: if ((crv == CKR_SIGNATURE_LEN_RANGE) || michael@0: (crv == CKR_SIGNATURE_INVALID)) { michael@0: return CKR_GENERAL_ERROR; michael@0: } michael@0: if (crv != CKR_OK) { michael@0: return crv; michael@0: } michael@0: } michael@0: michael@0: /**********************************************/ michael@0: /* Pairwise Consistency Check for Derivation */ michael@0: /**********************************************/ michael@0: michael@0: isDerivable = sftk_isTrue(privateKey, CKA_DERIVE); michael@0: michael@0: if (isDerivable) { michael@0: /* michael@0: * We are not doing consistency check for Diffie-Hellman Key - michael@0: * otherwise it would be here michael@0: * This is also true for Elliptic Curve Diffie-Hellman keys michael@0: * NOTE: EC keys are currently subjected to pairwise michael@0: * consistency check for signing/verification. michael@0: */ michael@0: /* michael@0: * FIPS 140-2 had the following pairwise consistency test for michael@0: * public and private keys used for key agreement: michael@0: * If the keys are used to perform key agreement, then the michael@0: * cryptographic module shall create a second, compatible michael@0: * key pair. The cryptographic module shall perform both michael@0: * sides of the key agreement algorithm and shall compare michael@0: * the resulting shared values. If the shared values are michael@0: * not equal, the test shall fail. michael@0: * This test was removed in Change Notice 3. michael@0: */ michael@0: michael@0: } michael@0: michael@0: return CKR_OK; michael@0: } michael@0: michael@0: /* NSC_GenerateKeyPair generates a public-key/private-key pair, michael@0: * creating new key objects. */ michael@0: CK_RV NSC_GenerateKeyPair (CK_SESSION_HANDLE hSession, michael@0: CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pPublicKeyTemplate, michael@0: CK_ULONG ulPublicKeyAttributeCount, CK_ATTRIBUTE_PTR pPrivateKeyTemplate, michael@0: CK_ULONG ulPrivateKeyAttributeCount, CK_OBJECT_HANDLE_PTR phPublicKey, michael@0: CK_OBJECT_HANDLE_PTR phPrivateKey) michael@0: { michael@0: SFTKObject * publicKey,*privateKey; michael@0: SFTKSession * session; michael@0: CK_KEY_TYPE key_type; michael@0: CK_RV crv = CKR_OK; michael@0: CK_BBOOL cktrue = CK_TRUE; michael@0: SECStatus rv; michael@0: CK_OBJECT_CLASS pubClass = CKO_PUBLIC_KEY; michael@0: CK_OBJECT_CLASS privClass = CKO_PRIVATE_KEY; michael@0: int i; michael@0: SFTKSlot * slot = sftk_SlotFromSessionHandle(hSession); michael@0: unsigned int bitSize; michael@0: michael@0: /* RSA */ michael@0: int public_modulus_bits = 0; michael@0: SECItem pubExp; michael@0: RSAPrivateKey * rsaPriv; michael@0: michael@0: /* DSA */ michael@0: PQGParams pqgParam; michael@0: DHParams dhParam; michael@0: DSAPrivateKey * dsaPriv; michael@0: michael@0: /* Diffie Hellman */ michael@0: int private_value_bits = 0; michael@0: DHPrivateKey * dhPriv; michael@0: michael@0: #ifndef NSS_DISABLE_ECC michael@0: /* Elliptic Curve Cryptography */ michael@0: SECItem ecEncodedParams; /* DER Encoded parameters */ michael@0: ECPrivateKey * ecPriv; michael@0: ECParams * ecParams; michael@0: #endif /* NSS_DISABLE_ECC */ michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: if (!slot) { michael@0: return CKR_SESSION_HANDLE_INVALID; michael@0: } michael@0: /* michael@0: * now lets create an object to hang the attributes off of michael@0: */ michael@0: publicKey = sftk_NewObject(slot); /* fill in the handle later */ michael@0: if (publicKey == NULL) { michael@0: return CKR_HOST_MEMORY; michael@0: } michael@0: michael@0: /* michael@0: * load the template values into the publicKey michael@0: */ michael@0: for (i=0; i < (int) ulPublicKeyAttributeCount; i++) { michael@0: if (pPublicKeyTemplate[i].type == CKA_MODULUS_BITS) { michael@0: public_modulus_bits = *(CK_ULONG *)pPublicKeyTemplate[i].pValue; michael@0: continue; michael@0: } michael@0: michael@0: crv = sftk_AddAttributeType(publicKey, michael@0: sftk_attr_expand(&pPublicKeyTemplate[i])); michael@0: if (crv != CKR_OK) break; michael@0: } michael@0: michael@0: if (crv != CKR_OK) { michael@0: sftk_FreeObject(publicKey); michael@0: return CKR_HOST_MEMORY; michael@0: } michael@0: michael@0: privateKey = sftk_NewObject(slot); /* fill in the handle later */ michael@0: if (privateKey == NULL) { michael@0: sftk_FreeObject(publicKey); michael@0: return CKR_HOST_MEMORY; michael@0: } michael@0: /* michael@0: * now load the private key template michael@0: */ michael@0: for (i=0; i < (int) ulPrivateKeyAttributeCount; i++) { michael@0: if (pPrivateKeyTemplate[i].type == CKA_VALUE_BITS) { michael@0: private_value_bits = *(CK_ULONG *)pPrivateKeyTemplate[i].pValue; michael@0: continue; michael@0: } michael@0: michael@0: crv = sftk_AddAttributeType(privateKey, michael@0: sftk_attr_expand(&pPrivateKeyTemplate[i])); michael@0: if (crv != CKR_OK) break; michael@0: } michael@0: michael@0: if (crv != CKR_OK) { michael@0: sftk_FreeObject(publicKey); michael@0: sftk_FreeObject(privateKey); michael@0: return CKR_HOST_MEMORY; michael@0: } michael@0: sftk_DeleteAttributeType(privateKey,CKA_CLASS); michael@0: sftk_DeleteAttributeType(privateKey,CKA_KEY_TYPE); michael@0: sftk_DeleteAttributeType(privateKey,CKA_VALUE); michael@0: sftk_DeleteAttributeType(publicKey,CKA_CLASS); michael@0: sftk_DeleteAttributeType(publicKey,CKA_KEY_TYPE); michael@0: sftk_DeleteAttributeType(publicKey,CKA_VALUE); michael@0: michael@0: /* Now Set up the parameters to generate the key (based on mechanism) */ michael@0: switch (pMechanism->mechanism) { michael@0: case CKM_RSA_PKCS_KEY_PAIR_GEN: michael@0: /* format the keys */ michael@0: sftk_DeleteAttributeType(publicKey,CKA_MODULUS); michael@0: sftk_DeleteAttributeType(privateKey,CKA_NETSCAPE_DB); michael@0: sftk_DeleteAttributeType(privateKey,CKA_MODULUS); michael@0: sftk_DeleteAttributeType(privateKey,CKA_PRIVATE_EXPONENT); michael@0: sftk_DeleteAttributeType(privateKey,CKA_PUBLIC_EXPONENT); michael@0: sftk_DeleteAttributeType(privateKey,CKA_PRIME_1); michael@0: sftk_DeleteAttributeType(privateKey,CKA_PRIME_2); michael@0: sftk_DeleteAttributeType(privateKey,CKA_EXPONENT_1); michael@0: sftk_DeleteAttributeType(privateKey,CKA_EXPONENT_2); michael@0: sftk_DeleteAttributeType(privateKey,CKA_COEFFICIENT); michael@0: key_type = CKK_RSA; michael@0: if (public_modulus_bits == 0) { michael@0: crv = CKR_TEMPLATE_INCOMPLETE; michael@0: break; michael@0: } michael@0: if (public_modulus_bits < RSA_MIN_MODULUS_BITS) { michael@0: crv = CKR_ATTRIBUTE_VALUE_INVALID; michael@0: break; michael@0: } michael@0: if (public_modulus_bits % 2 != 0) { michael@0: crv = CKR_ATTRIBUTE_VALUE_INVALID; michael@0: break; michael@0: } michael@0: michael@0: /* extract the exponent */ michael@0: crv=sftk_Attribute2SSecItem(NULL,&pubExp,publicKey,CKA_PUBLIC_EXPONENT); michael@0: if (crv != CKR_OK) break; michael@0: bitSize = sftk_GetLengthInBits(pubExp.data, pubExp.len); michael@0: if (bitSize < 2) { michael@0: crv = CKR_ATTRIBUTE_VALUE_INVALID; michael@0: break; michael@0: } michael@0: crv = sftk_AddAttributeType(privateKey,CKA_PUBLIC_EXPONENT, michael@0: sftk_item_expand(&pubExp)); michael@0: if (crv != CKR_OK) { michael@0: PORT_Free(pubExp.data); michael@0: break; michael@0: } michael@0: michael@0: rsaPriv = RSA_NewKey(public_modulus_bits, &pubExp); michael@0: PORT_Free(pubExp.data); michael@0: if (rsaPriv == NULL) { michael@0: if (PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { michael@0: sftk_fatalError = PR_TRUE; michael@0: } michael@0: crv = sftk_MapCryptError(PORT_GetError()); michael@0: break; michael@0: } michael@0: /* now fill in the RSA dependent paramenters in the public key */ michael@0: crv = sftk_AddAttributeType(publicKey,CKA_MODULUS, michael@0: sftk_item_expand(&rsaPriv->modulus)); michael@0: if (crv != CKR_OK) goto kpg_done; michael@0: /* now fill in the RSA dependent paramenters in the private key */ michael@0: crv = sftk_AddAttributeType(privateKey,CKA_NETSCAPE_DB, michael@0: sftk_item_expand(&rsaPriv->modulus)); michael@0: if (crv != CKR_OK) goto kpg_done; michael@0: crv = sftk_AddAttributeType(privateKey,CKA_MODULUS, michael@0: sftk_item_expand(&rsaPriv->modulus)); michael@0: if (crv != CKR_OK) goto kpg_done; michael@0: crv = sftk_AddAttributeType(privateKey,CKA_PRIVATE_EXPONENT, michael@0: sftk_item_expand(&rsaPriv->privateExponent)); michael@0: if (crv != CKR_OK) goto kpg_done; michael@0: crv = sftk_AddAttributeType(privateKey,CKA_PRIME_1, michael@0: sftk_item_expand(&rsaPriv->prime1)); michael@0: if (crv != CKR_OK) goto kpg_done; michael@0: crv = sftk_AddAttributeType(privateKey,CKA_PRIME_2, michael@0: sftk_item_expand(&rsaPriv->prime2)); michael@0: if (crv != CKR_OK) goto kpg_done; michael@0: crv = sftk_AddAttributeType(privateKey,CKA_EXPONENT_1, michael@0: sftk_item_expand(&rsaPriv->exponent1)); michael@0: if (crv != CKR_OK) goto kpg_done; michael@0: crv = sftk_AddAttributeType(privateKey,CKA_EXPONENT_2, michael@0: sftk_item_expand(&rsaPriv->exponent2)); michael@0: if (crv != CKR_OK) goto kpg_done; michael@0: crv = sftk_AddAttributeType(privateKey,CKA_COEFFICIENT, michael@0: sftk_item_expand(&rsaPriv->coefficient)); michael@0: kpg_done: michael@0: /* Should zeroize the contents first, since this func doesn't. */ michael@0: PORT_FreeArena(rsaPriv->arena, PR_TRUE); michael@0: break; michael@0: case CKM_DSA_KEY_PAIR_GEN: michael@0: sftk_DeleteAttributeType(publicKey,CKA_VALUE); michael@0: sftk_DeleteAttributeType(privateKey,CKA_NETSCAPE_DB); michael@0: sftk_DeleteAttributeType(privateKey,CKA_PRIME); michael@0: sftk_DeleteAttributeType(privateKey,CKA_SUBPRIME); michael@0: sftk_DeleteAttributeType(privateKey,CKA_BASE); michael@0: key_type = CKK_DSA; michael@0: michael@0: /* extract the necessary parameters and copy them to the private key */ michael@0: crv=sftk_Attribute2SSecItem(NULL,&pqgParam.prime,publicKey,CKA_PRIME); michael@0: if (crv != CKR_OK) break; michael@0: crv=sftk_Attribute2SSecItem(NULL,&pqgParam.subPrime,publicKey, michael@0: CKA_SUBPRIME); michael@0: if (crv != CKR_OK) { michael@0: PORT_Free(pqgParam.prime.data); michael@0: break; michael@0: } michael@0: crv=sftk_Attribute2SSecItem(NULL,&pqgParam.base,publicKey,CKA_BASE); michael@0: if (crv != CKR_OK) { michael@0: PORT_Free(pqgParam.prime.data); michael@0: PORT_Free(pqgParam.subPrime.data); michael@0: break; michael@0: } michael@0: crv = sftk_AddAttributeType(privateKey,CKA_PRIME, michael@0: sftk_item_expand(&pqgParam.prime)); michael@0: if (crv != CKR_OK) { michael@0: PORT_Free(pqgParam.prime.data); michael@0: PORT_Free(pqgParam.subPrime.data); michael@0: PORT_Free(pqgParam.base.data); michael@0: break; michael@0: } michael@0: crv = sftk_AddAttributeType(privateKey,CKA_SUBPRIME, michael@0: sftk_item_expand(&pqgParam.subPrime)); michael@0: if (crv != CKR_OK) { michael@0: PORT_Free(pqgParam.prime.data); michael@0: PORT_Free(pqgParam.subPrime.data); michael@0: PORT_Free(pqgParam.base.data); michael@0: break; michael@0: } michael@0: crv = sftk_AddAttributeType(privateKey,CKA_BASE, michael@0: sftk_item_expand(&pqgParam.base)); michael@0: if (crv != CKR_OK) { michael@0: PORT_Free(pqgParam.prime.data); michael@0: PORT_Free(pqgParam.subPrime.data); michael@0: PORT_Free(pqgParam.base.data); michael@0: break; michael@0: } michael@0: michael@0: /* michael@0: * these are checked by DSA_NewKey michael@0: */ michael@0: bitSize = sftk_GetLengthInBits(pqgParam.subPrime.data, michael@0: pqgParam.subPrime.len); michael@0: if ((bitSize < DSA_MIN_Q_BITS) || (bitSize > DSA_MAX_Q_BITS)) { michael@0: crv = CKR_TEMPLATE_INCOMPLETE; michael@0: PORT_Free(pqgParam.prime.data); michael@0: PORT_Free(pqgParam.subPrime.data); michael@0: PORT_Free(pqgParam.base.data); michael@0: break; michael@0: } michael@0: bitSize = sftk_GetLengthInBits(pqgParam.prime.data,pqgParam.prime.len); michael@0: if ((bitSize < DSA_MIN_P_BITS) || (bitSize > DSA_MAX_P_BITS)) { michael@0: crv = CKR_TEMPLATE_INCOMPLETE; michael@0: PORT_Free(pqgParam.prime.data); michael@0: PORT_Free(pqgParam.subPrime.data); michael@0: PORT_Free(pqgParam.base.data); michael@0: break; michael@0: } michael@0: bitSize = sftk_GetLengthInBits(pqgParam.base.data,pqgParam.base.len); michael@0: if ((bitSize < 2) || (bitSize > DSA_MAX_P_BITS)) { michael@0: crv = CKR_TEMPLATE_INCOMPLETE; michael@0: PORT_Free(pqgParam.prime.data); michael@0: PORT_Free(pqgParam.subPrime.data); michael@0: PORT_Free(pqgParam.base.data); michael@0: break; michael@0: } michael@0: michael@0: /* Generate the key */ michael@0: rv = DSA_NewKey(&pqgParam, &dsaPriv); michael@0: michael@0: PORT_Free(pqgParam.prime.data); michael@0: PORT_Free(pqgParam.subPrime.data); michael@0: PORT_Free(pqgParam.base.data); michael@0: michael@0: if (rv != SECSuccess) { michael@0: if (PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { michael@0: sftk_fatalError = PR_TRUE; michael@0: } michael@0: crv = sftk_MapCryptError(PORT_GetError()); michael@0: break; michael@0: } michael@0: michael@0: /* store the generated key into the attributes */ michael@0: crv = sftk_AddAttributeType(publicKey,CKA_VALUE, michael@0: sftk_item_expand(&dsaPriv->publicValue)); michael@0: if (crv != CKR_OK) goto dsagn_done; michael@0: michael@0: /* now fill in the RSA dependent paramenters in the private key */ michael@0: crv = sftk_AddAttributeType(privateKey,CKA_NETSCAPE_DB, michael@0: sftk_item_expand(&dsaPriv->publicValue)); michael@0: if (crv != CKR_OK) goto dsagn_done; michael@0: crv = sftk_AddAttributeType(privateKey,CKA_VALUE, michael@0: sftk_item_expand(&dsaPriv->privateValue)); michael@0: michael@0: dsagn_done: michael@0: /* should zeroize, since this function doesn't. */ michael@0: PORT_FreeArena(dsaPriv->params.arena, PR_TRUE); michael@0: break; michael@0: michael@0: case CKM_DH_PKCS_KEY_PAIR_GEN: michael@0: sftk_DeleteAttributeType(privateKey,CKA_PRIME); michael@0: sftk_DeleteAttributeType(privateKey,CKA_BASE); michael@0: sftk_DeleteAttributeType(privateKey,CKA_VALUE); michael@0: sftk_DeleteAttributeType(privateKey,CKA_NETSCAPE_DB); michael@0: key_type = CKK_DH; michael@0: michael@0: /* extract the necessary parameters and copy them to private keys */ michael@0: crv = sftk_Attribute2SSecItem(NULL, &dhParam.prime, publicKey, michael@0: CKA_PRIME); michael@0: if (crv != CKR_OK) break; michael@0: crv = sftk_Attribute2SSecItem(NULL, &dhParam.base, publicKey, CKA_BASE); michael@0: if (crv != CKR_OK) { michael@0: PORT_Free(dhParam.prime.data); michael@0: break; michael@0: } michael@0: crv = sftk_AddAttributeType(privateKey, CKA_PRIME, michael@0: sftk_item_expand(&dhParam.prime)); michael@0: if (crv != CKR_OK) { michael@0: PORT_Free(dhParam.prime.data); michael@0: PORT_Free(dhParam.base.data); michael@0: break; michael@0: } michael@0: crv = sftk_AddAttributeType(privateKey, CKA_BASE, michael@0: sftk_item_expand(&dhParam.base)); michael@0: if (crv != CKR_OK) { michael@0: PORT_Free(dhParam.prime.data); michael@0: PORT_Free(dhParam.base.data); michael@0: break; michael@0: } michael@0: bitSize = sftk_GetLengthInBits(dhParam.prime.data,dhParam.prime.len); michael@0: if ((bitSize < DH_MIN_P_BITS) || (bitSize > DH_MAX_P_BITS)) { michael@0: crv = CKR_TEMPLATE_INCOMPLETE; michael@0: PORT_Free(dhParam.prime.data); michael@0: PORT_Free(dhParam.base.data); michael@0: break; michael@0: } michael@0: bitSize = sftk_GetLengthInBits(dhParam.base.data,dhParam.base.len); michael@0: if ((bitSize < 1) || (bitSize > DH_MAX_P_BITS)) { michael@0: crv = CKR_TEMPLATE_INCOMPLETE; michael@0: PORT_Free(dhParam.prime.data); michael@0: PORT_Free(dhParam.base.data); michael@0: break; michael@0: } michael@0: michael@0: rv = DH_NewKey(&dhParam, &dhPriv); michael@0: PORT_Free(dhParam.prime.data); michael@0: PORT_Free(dhParam.base.data); michael@0: if (rv != SECSuccess) { michael@0: if (PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { michael@0: sftk_fatalError = PR_TRUE; michael@0: } michael@0: crv = sftk_MapCryptError(PORT_GetError()); michael@0: break; michael@0: } michael@0: michael@0: crv=sftk_AddAttributeType(publicKey, CKA_VALUE, michael@0: sftk_item_expand(&dhPriv->publicValue)); michael@0: if (crv != CKR_OK) goto dhgn_done; michael@0: michael@0: crv = sftk_AddAttributeType(privateKey,CKA_NETSCAPE_DB, michael@0: sftk_item_expand(&dhPriv->publicValue)); michael@0: if (crv != CKR_OK) goto dhgn_done; michael@0: michael@0: crv=sftk_AddAttributeType(privateKey, CKA_VALUE, michael@0: sftk_item_expand(&dhPriv->privateValue)); michael@0: michael@0: dhgn_done: michael@0: /* should zeroize, since this function doesn't. */ michael@0: PORT_FreeArena(dhPriv->arena, PR_TRUE); michael@0: break; michael@0: michael@0: #ifndef NSS_DISABLE_ECC michael@0: case CKM_EC_KEY_PAIR_GEN: michael@0: sftk_DeleteAttributeType(privateKey,CKA_EC_PARAMS); michael@0: sftk_DeleteAttributeType(privateKey,CKA_VALUE); michael@0: sftk_DeleteAttributeType(privateKey,CKA_NETSCAPE_DB); michael@0: key_type = CKK_EC; michael@0: michael@0: /* extract the necessary parameters and copy them to private keys */ michael@0: crv = sftk_Attribute2SSecItem(NULL, &ecEncodedParams, publicKey, michael@0: CKA_EC_PARAMS); michael@0: if (crv != CKR_OK) break; michael@0: michael@0: crv = sftk_AddAttributeType(privateKey, CKA_EC_PARAMS, michael@0: sftk_item_expand(&ecEncodedParams)); michael@0: if (crv != CKR_OK) { michael@0: PORT_Free(ecEncodedParams.data); michael@0: break; michael@0: } michael@0: michael@0: /* Decode ec params before calling EC_NewKey */ michael@0: rv = EC_DecodeParams(&ecEncodedParams, &ecParams); michael@0: PORT_Free(ecEncodedParams.data); michael@0: if (rv != SECSuccess) { michael@0: crv = sftk_MapCryptError(PORT_GetError()); michael@0: break; michael@0: } michael@0: rv = EC_NewKey(ecParams, &ecPriv); michael@0: PORT_FreeArena(ecParams->arena, PR_TRUE); michael@0: if (rv != SECSuccess) { michael@0: if (PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { michael@0: sftk_fatalError = PR_TRUE; michael@0: } michael@0: crv = sftk_MapCryptError(PORT_GetError()); michael@0: break; michael@0: } michael@0: michael@0: if (getenv("NSS_USE_DECODED_CKA_EC_POINT")) { michael@0: crv = sftk_AddAttributeType(publicKey, CKA_EC_POINT, michael@0: sftk_item_expand(&ecPriv->publicValue)); michael@0: } else { michael@0: SECItem *pubValue = SEC_ASN1EncodeItem(NULL, NULL, michael@0: &ecPriv->publicValue, michael@0: SEC_ASN1_GET(SEC_OctetStringTemplate)); michael@0: if (!pubValue) { michael@0: crv = CKR_ARGUMENTS_BAD; michael@0: goto ecgn_done; michael@0: } michael@0: crv = sftk_AddAttributeType(publicKey, CKA_EC_POINT, michael@0: sftk_item_expand(pubValue)); michael@0: SECITEM_FreeItem(pubValue, PR_TRUE); michael@0: } michael@0: if (crv != CKR_OK) goto ecgn_done; michael@0: michael@0: crv = sftk_AddAttributeType(privateKey, CKA_VALUE, michael@0: sftk_item_expand(&ecPriv->privateValue)); michael@0: if (crv != CKR_OK) goto ecgn_done; michael@0: michael@0: crv = sftk_AddAttributeType(privateKey,CKA_NETSCAPE_DB, michael@0: sftk_item_expand(&ecPriv->publicValue)); michael@0: ecgn_done: michael@0: /* should zeroize, since this function doesn't. */ michael@0: PORT_FreeArena(ecPriv->ecParams.arena, PR_TRUE); michael@0: break; michael@0: #endif /* NSS_DISABLE_ECC */ michael@0: michael@0: default: michael@0: crv = CKR_MECHANISM_INVALID; michael@0: } michael@0: michael@0: if (crv != CKR_OK) { michael@0: sftk_FreeObject(privateKey); michael@0: sftk_FreeObject(publicKey); michael@0: return crv; michael@0: } michael@0: michael@0: michael@0: /* Add the class, key_type The loop lets us check errors blow out michael@0: * on errors and clean up at the bottom */ michael@0: session = NULL; /* make pedtantic happy... session cannot leave the*/ michael@0: /* loop below NULL unless an error is set... */ michael@0: do { michael@0: crv = sftk_AddAttributeType(privateKey,CKA_CLASS,&privClass, michael@0: sizeof(CK_OBJECT_CLASS)); michael@0: if (crv != CKR_OK) break; michael@0: crv = sftk_AddAttributeType(publicKey,CKA_CLASS,&pubClass, michael@0: sizeof(CK_OBJECT_CLASS)); michael@0: if (crv != CKR_OK) break; michael@0: crv = sftk_AddAttributeType(privateKey,CKA_KEY_TYPE,&key_type, michael@0: sizeof(CK_KEY_TYPE)); michael@0: if (crv != CKR_OK) break; michael@0: crv = sftk_AddAttributeType(publicKey,CKA_KEY_TYPE,&key_type, michael@0: sizeof(CK_KEY_TYPE)); michael@0: if (crv != CKR_OK) break; michael@0: session = sftk_SessionFromHandle(hSession); michael@0: if (session == NULL) crv = CKR_SESSION_HANDLE_INVALID; michael@0: } while (0); michael@0: michael@0: if (crv != CKR_OK) { michael@0: sftk_FreeObject(privateKey); michael@0: sftk_FreeObject(publicKey); michael@0: return crv; michael@0: } michael@0: michael@0: /* michael@0: * handle the base object cleanup for the public Key michael@0: */ michael@0: crv = sftk_handleObject(privateKey,session); michael@0: if (crv != CKR_OK) { michael@0: sftk_FreeSession(session); michael@0: sftk_FreeObject(privateKey); michael@0: sftk_FreeObject(publicKey); michael@0: return crv; michael@0: } michael@0: michael@0: /* michael@0: * handle the base object cleanup for the private Key michael@0: * If we have any problems, we destroy the public Key we've michael@0: * created and linked. michael@0: */ michael@0: crv = sftk_handleObject(publicKey,session); michael@0: sftk_FreeSession(session); michael@0: if (crv != CKR_OK) { michael@0: sftk_FreeObject(publicKey); michael@0: NSC_DestroyObject(hSession,privateKey->handle); michael@0: sftk_FreeObject(privateKey); michael@0: return crv; michael@0: } michael@0: if (sftk_isTrue(privateKey,CKA_SENSITIVE)) { michael@0: sftk_forceAttribute(privateKey,CKA_ALWAYS_SENSITIVE, michael@0: &cktrue,sizeof(CK_BBOOL)); michael@0: } michael@0: if (sftk_isTrue(publicKey,CKA_SENSITIVE)) { michael@0: sftk_forceAttribute(publicKey,CKA_ALWAYS_SENSITIVE, michael@0: &cktrue,sizeof(CK_BBOOL)); michael@0: } michael@0: if (!sftk_isTrue(privateKey,CKA_EXTRACTABLE)) { michael@0: sftk_forceAttribute(privateKey,CKA_NEVER_EXTRACTABLE, michael@0: &cktrue,sizeof(CK_BBOOL)); michael@0: } michael@0: if (!sftk_isTrue(publicKey,CKA_EXTRACTABLE)) { michael@0: sftk_forceAttribute(publicKey,CKA_NEVER_EXTRACTABLE, michael@0: &cktrue,sizeof(CK_BBOOL)); michael@0: } michael@0: michael@0: /* Perform FIPS 140-2 pairwise consistency check. */ michael@0: crv = sftk_PairwiseConsistencyCheck(hSession, michael@0: publicKey, privateKey, key_type); michael@0: if (crv != CKR_OK) { michael@0: NSC_DestroyObject(hSession,publicKey->handle); michael@0: sftk_FreeObject(publicKey); michael@0: NSC_DestroyObject(hSession,privateKey->handle); michael@0: sftk_FreeObject(privateKey); michael@0: if (sftk_audit_enabled) { michael@0: char msg[128]; michael@0: PR_snprintf(msg,sizeof msg, michael@0: "C_GenerateKeyPair(hSession=0x%08lX, " michael@0: "pMechanism->mechanism=0x%08lX)=0x%08lX " michael@0: "self-test: pair-wise consistency test failed", michael@0: (PRUint32)hSession,(PRUint32)pMechanism->mechanism, michael@0: (PRUint32)crv); michael@0: sftk_LogAuditMessage(NSS_AUDIT_ERROR, NSS_AUDIT_SELF_TEST, msg); michael@0: } michael@0: return crv; michael@0: } michael@0: michael@0: *phPrivateKey = privateKey->handle; michael@0: *phPublicKey = publicKey->handle; michael@0: sftk_FreeObject(publicKey); michael@0: sftk_FreeObject(privateKey); michael@0: michael@0: return CKR_OK; michael@0: } michael@0: michael@0: static SECItem *sftk_PackagePrivateKey(SFTKObject *key, CK_RV *crvp) michael@0: { michael@0: NSSLOWKEYPrivateKey *lk = NULL; michael@0: NSSLOWKEYPrivateKeyInfo *pki = NULL; michael@0: SFTKAttribute *attribute = NULL; michael@0: PLArenaPool *arena = NULL; michael@0: SECOidTag algorithm = SEC_OID_UNKNOWN; michael@0: void *dummy, *param = NULL; michael@0: SECStatus rv = SECSuccess; michael@0: SECItem *encodedKey = NULL; michael@0: #ifndef NSS_DISABLE_ECC michael@0: SECItem *fordebug; michael@0: int savelen; michael@0: #endif michael@0: michael@0: if(!key) { michael@0: *crvp = CKR_KEY_HANDLE_INVALID; /* really can't happen */ michael@0: return NULL; michael@0: } michael@0: michael@0: attribute = sftk_FindAttribute(key, CKA_KEY_TYPE); michael@0: if(!attribute) { michael@0: *crvp = CKR_KEY_TYPE_INCONSISTENT; michael@0: return NULL; michael@0: } michael@0: michael@0: lk = sftk_GetPrivKey(key, *(CK_KEY_TYPE *)attribute->attrib.pValue, crvp); michael@0: sftk_FreeAttribute(attribute); michael@0: if(!lk) { michael@0: return NULL; michael@0: } michael@0: michael@0: arena = PORT_NewArena(2048); /* XXX different size? */ michael@0: if(!arena) { michael@0: *crvp = CKR_HOST_MEMORY; michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: michael@0: pki = (NSSLOWKEYPrivateKeyInfo*)PORT_ArenaZAlloc(arena, michael@0: sizeof(NSSLOWKEYPrivateKeyInfo)); michael@0: if(!pki) { michael@0: *crvp = CKR_HOST_MEMORY; michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: pki->arena = arena; michael@0: michael@0: param = NULL; michael@0: switch(lk->keyType) { michael@0: case NSSLOWKEYRSAKey: michael@0: prepare_low_rsa_priv_key_for_asn1(lk); michael@0: dummy = SEC_ASN1EncodeItem(arena, &pki->privateKey, lk, michael@0: nsslowkey_RSAPrivateKeyTemplate); michael@0: algorithm = SEC_OID_PKCS1_RSA_ENCRYPTION; michael@0: break; michael@0: case NSSLOWKEYDSAKey: michael@0: prepare_low_dsa_priv_key_export_for_asn1(lk); michael@0: dummy = SEC_ASN1EncodeItem(arena, &pki->privateKey, lk, michael@0: nsslowkey_DSAPrivateKeyExportTemplate); michael@0: prepare_low_pqg_params_for_asn1(&lk->u.dsa.params); michael@0: param = SEC_ASN1EncodeItem(NULL, NULL, &(lk->u.dsa.params), michael@0: nsslowkey_PQGParamsTemplate); michael@0: algorithm = SEC_OID_ANSIX9_DSA_SIGNATURE; michael@0: break; michael@0: #ifndef NSS_DISABLE_ECC michael@0: case NSSLOWKEYECKey: michael@0: prepare_low_ec_priv_key_for_asn1(lk); michael@0: /* Public value is encoded as a bit string so adjust length michael@0: * to be in bits before ASN encoding and readjust michael@0: * immediately after. michael@0: * michael@0: * Since the SECG specification recommends not including the michael@0: * parameters as part of ECPrivateKey, we zero out the curveOID michael@0: * length before encoding and restore it later. michael@0: */ michael@0: lk->u.ec.publicValue.len <<= 3; michael@0: savelen = lk->u.ec.ecParams.curveOID.len; michael@0: lk->u.ec.ecParams.curveOID.len = 0; michael@0: dummy = SEC_ASN1EncodeItem(arena, &pki->privateKey, lk, michael@0: nsslowkey_ECPrivateKeyTemplate); michael@0: lk->u.ec.ecParams.curveOID.len = savelen; michael@0: lk->u.ec.publicValue.len >>= 3; michael@0: michael@0: fordebug = &pki->privateKey; michael@0: SEC_PRINT("sftk_PackagePrivateKey()", "PrivateKey", lk->keyType, michael@0: fordebug); michael@0: michael@0: param = SECITEM_DupItem(&lk->u.ec.ecParams.DEREncoding); michael@0: michael@0: algorithm = SEC_OID_ANSIX962_EC_PUBLIC_KEY; michael@0: break; michael@0: #endif /* NSS_DISABLE_ECC */ michael@0: case NSSLOWKEYDHKey: michael@0: default: michael@0: dummy = NULL; michael@0: break; michael@0: } michael@0: michael@0: if(!dummy || ((lk->keyType == NSSLOWKEYDSAKey) && !param)) { michael@0: *crvp = CKR_DEVICE_ERROR; /* should map NSS SECError */ michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: michael@0: rv = SECOID_SetAlgorithmID(arena, &pki->algorithm, algorithm, michael@0: (SECItem*)param); michael@0: if(rv != SECSuccess) { michael@0: *crvp = CKR_DEVICE_ERROR; /* should map NSS SECError */ michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: michael@0: dummy = SEC_ASN1EncodeInteger(arena, &pki->version, michael@0: NSSLOWKEY_PRIVATE_KEY_INFO_VERSION); michael@0: if(!dummy) { michael@0: *crvp = CKR_DEVICE_ERROR; /* should map NSS SECError */ michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: michael@0: encodedKey = SEC_ASN1EncodeItem(NULL, NULL, pki, michael@0: nsslowkey_PrivateKeyInfoTemplate); michael@0: *crvp = encodedKey ? CKR_OK : CKR_DEVICE_ERROR; michael@0: michael@0: #ifndef NSS_DISABLE_ECC michael@0: fordebug = encodedKey; michael@0: SEC_PRINT("sftk_PackagePrivateKey()", "PrivateKeyInfo", lk->keyType, michael@0: fordebug); michael@0: #endif michael@0: loser: michael@0: if(arena) { michael@0: PORT_FreeArena(arena, PR_TRUE); michael@0: } michael@0: michael@0: if(lk && (lk != key->objectInfo)) { michael@0: nsslowkey_DestroyPrivateKey(lk); michael@0: } michael@0: michael@0: if(param) { michael@0: SECITEM_ZfreeItem((SECItem*)param, PR_TRUE); michael@0: } michael@0: michael@0: if(rv != SECSuccess) { michael@0: return NULL; michael@0: } michael@0: michael@0: return encodedKey; michael@0: } michael@0: michael@0: /* it doesn't matter yet, since we colapse error conditions in the michael@0: * level above, but we really should map those few key error differences */ michael@0: static CK_RV michael@0: sftk_mapWrap(CK_RV crv) michael@0: { michael@0: switch (crv) { michael@0: case CKR_ENCRYPTED_DATA_INVALID: crv = CKR_WRAPPED_KEY_INVALID; break; michael@0: } michael@0: return crv; michael@0: } michael@0: michael@0: /* NSC_WrapKey wraps (i.e., encrypts) a key. */ michael@0: CK_RV NSC_WrapKey(CK_SESSION_HANDLE hSession, michael@0: CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hWrappingKey, michael@0: CK_OBJECT_HANDLE hKey, CK_BYTE_PTR pWrappedKey, michael@0: CK_ULONG_PTR pulWrappedKeyLen) michael@0: { michael@0: SFTKSession *session; michael@0: SFTKAttribute *attribute; michael@0: SFTKObject *key; michael@0: CK_RV crv; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: session = sftk_SessionFromHandle(hSession); michael@0: if (session == NULL) { michael@0: return CKR_SESSION_HANDLE_INVALID; michael@0: } michael@0: michael@0: key = sftk_ObjectFromHandle(hKey,session); michael@0: sftk_FreeSession(session); michael@0: if (key == NULL) { michael@0: return CKR_KEY_HANDLE_INVALID; michael@0: } michael@0: michael@0: switch(key->objclass) { michael@0: case CKO_SECRET_KEY: michael@0: { michael@0: SFTKSessionContext *context = NULL; michael@0: SECItem pText; michael@0: michael@0: attribute = sftk_FindAttribute(key,CKA_VALUE); michael@0: michael@0: if (attribute == NULL) { michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: crv = sftk_CryptInit(hSession, pMechanism, hWrappingKey, michael@0: CKA_WRAP, CKA_WRAP, SFTK_ENCRYPT, PR_TRUE); michael@0: if (crv != CKR_OK) { michael@0: sftk_FreeAttribute(attribute); michael@0: break; michael@0: } michael@0: michael@0: pText.type = siBuffer; michael@0: pText.data = (unsigned char *)attribute->attrib.pValue; michael@0: pText.len = attribute->attrib.ulValueLen; michael@0: michael@0: /* Find out if this is a block cipher. */ michael@0: crv = sftk_GetContext(hSession,&context,SFTK_ENCRYPT,PR_FALSE,NULL); michael@0: if (crv != CKR_OK || !context) michael@0: break; michael@0: if (context->blockSize > 1) { michael@0: unsigned int remainder = pText.len % context->blockSize; michael@0: if (!context->doPad && remainder) { michael@0: /* When wrapping secret keys with unpadded block ciphers, michael@0: ** the keys are zero padded, if necessary, to fill out michael@0: ** a full block. michael@0: */ michael@0: pText.len += context->blockSize - remainder; michael@0: pText.data = PORT_ZAlloc(pText.len); michael@0: if (pText.data) michael@0: memcpy(pText.data, attribute->attrib.pValue, michael@0: attribute->attrib.ulValueLen); michael@0: else { michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: crv = NSC_Encrypt(hSession, (CK_BYTE_PTR)pText.data, michael@0: pText.len, pWrappedKey, pulWrappedKeyLen); michael@0: /* always force a finalize, both on errors and when michael@0: * we are just getting the size */ michael@0: if (crv != CKR_OK || pWrappedKey == NULL) { michael@0: CK_RV lcrv ; michael@0: lcrv = sftk_GetContext(hSession,&context, michael@0: SFTK_ENCRYPT,PR_FALSE,NULL); michael@0: sftk_SetContextByType(session, SFTK_ENCRYPT, NULL); michael@0: if (lcrv == CKR_OK && context) { michael@0: sftk_FreeContext(context); michael@0: } michael@0: } michael@0: michael@0: if (pText.data != (unsigned char *)attribute->attrib.pValue) michael@0: PORT_ZFree(pText.data, pText.len); michael@0: sftk_FreeAttribute(attribute); michael@0: break; michael@0: } michael@0: michael@0: case CKO_PRIVATE_KEY: michael@0: { michael@0: SECItem *bpki = sftk_PackagePrivateKey(key, &crv); michael@0: SFTKSessionContext *context = NULL; michael@0: michael@0: if(!bpki) { michael@0: break; michael@0: } michael@0: michael@0: crv = sftk_CryptInit(hSession, pMechanism, hWrappingKey, michael@0: CKA_WRAP, CKA_WRAP, SFTK_ENCRYPT, PR_TRUE); michael@0: if(crv != CKR_OK) { michael@0: SECITEM_ZfreeItem(bpki, PR_TRUE); michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: michael@0: crv = NSC_Encrypt(hSession, bpki->data, bpki->len, michael@0: pWrappedKey, pulWrappedKeyLen); michael@0: /* always force a finalize */ michael@0: if (crv != CKR_OK || pWrappedKey == NULL) { michael@0: CK_RV lcrv ; michael@0: lcrv = sftk_GetContext(hSession,&context, michael@0: SFTK_ENCRYPT,PR_FALSE,NULL); michael@0: sftk_SetContextByType(session, SFTK_ENCRYPT, NULL); michael@0: if (lcrv == CKR_OK && context) { michael@0: sftk_FreeContext(context); michael@0: } michael@0: } michael@0: SECITEM_ZfreeItem(bpki, PR_TRUE); michael@0: break; michael@0: } michael@0: michael@0: default: michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: sftk_FreeObject(key); michael@0: michael@0: return sftk_mapWrap(crv); michael@0: } michael@0: michael@0: /* michael@0: * import a pprivate key info into the desired slot michael@0: */ michael@0: static SECStatus michael@0: sftk_unwrapPrivateKey(SFTKObject *key, SECItem *bpki) michael@0: { michael@0: CK_BBOOL cktrue = CK_TRUE; michael@0: CK_KEY_TYPE keyType = CKK_RSA; michael@0: SECStatus rv = SECFailure; michael@0: const SEC_ASN1Template *keyTemplate, *paramTemplate; michael@0: void *paramDest = NULL; michael@0: PLArenaPool *arena; michael@0: NSSLOWKEYPrivateKey *lpk = NULL; michael@0: NSSLOWKEYPrivateKeyInfo *pki = NULL; michael@0: CK_RV crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: michael@0: arena = PORT_NewArena(2048); michael@0: if(!arena) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: pki = (NSSLOWKEYPrivateKeyInfo*)PORT_ArenaZAlloc(arena, michael@0: sizeof(NSSLOWKEYPrivateKeyInfo)); michael@0: if(!pki) { michael@0: PORT_FreeArena(arena, PR_FALSE); michael@0: return SECFailure; michael@0: } michael@0: michael@0: if(SEC_ASN1DecodeItem(arena, pki, nsslowkey_PrivateKeyInfoTemplate, bpki) michael@0: != SECSuccess) { michael@0: PORT_FreeArena(arena, PR_TRUE); michael@0: return SECFailure; michael@0: } michael@0: michael@0: lpk = (NSSLOWKEYPrivateKey *)PORT_ArenaZAlloc(arena, michael@0: sizeof(NSSLOWKEYPrivateKey)); michael@0: if(lpk == NULL) { michael@0: goto loser; michael@0: } michael@0: lpk->arena = arena; michael@0: michael@0: switch(SECOID_GetAlgorithmTag(&pki->algorithm)) { michael@0: case SEC_OID_PKCS1_RSA_ENCRYPTION: michael@0: keyTemplate = nsslowkey_RSAPrivateKeyTemplate; michael@0: paramTemplate = NULL; michael@0: paramDest = NULL; michael@0: lpk->keyType = NSSLOWKEYRSAKey; michael@0: prepare_low_rsa_priv_key_for_asn1(lpk); michael@0: break; michael@0: case SEC_OID_ANSIX9_DSA_SIGNATURE: michael@0: keyTemplate = nsslowkey_DSAPrivateKeyExportTemplate; michael@0: paramTemplate = nsslowkey_PQGParamsTemplate; michael@0: paramDest = &(lpk->u.dsa.params); michael@0: lpk->keyType = NSSLOWKEYDSAKey; michael@0: prepare_low_dsa_priv_key_export_for_asn1(lpk); michael@0: prepare_low_pqg_params_for_asn1(&lpk->u.dsa.params); michael@0: break; michael@0: /* case NSSLOWKEYDHKey: */ michael@0: #ifndef NSS_DISABLE_ECC michael@0: case SEC_OID_ANSIX962_EC_PUBLIC_KEY: michael@0: keyTemplate = nsslowkey_ECPrivateKeyTemplate; michael@0: paramTemplate = NULL; michael@0: paramDest = &(lpk->u.ec.ecParams.DEREncoding); michael@0: lpk->keyType = NSSLOWKEYECKey; michael@0: prepare_low_ec_priv_key_for_asn1(lpk); michael@0: prepare_low_ecparams_for_asn1(&lpk->u.ec.ecParams); michael@0: break; michael@0: #endif /* NSS_DISABLE_ECC */ michael@0: default: michael@0: keyTemplate = NULL; michael@0: paramTemplate = NULL; michael@0: paramDest = NULL; michael@0: break; michael@0: } michael@0: michael@0: if(!keyTemplate) { michael@0: goto loser; michael@0: } michael@0: michael@0: /* decode the private key and any algorithm parameters */ michael@0: rv = SEC_QuickDERDecodeItem(arena, lpk, keyTemplate, &pki->privateKey); michael@0: michael@0: #ifndef NSS_DISABLE_ECC michael@0: if (lpk->keyType == NSSLOWKEYECKey) { michael@0: /* convert length in bits to length in bytes */ michael@0: lpk->u.ec.publicValue.len >>= 3; michael@0: rv = SECITEM_CopyItem(arena, michael@0: &(lpk->u.ec.ecParams.DEREncoding), michael@0: &(pki->algorithm.parameters)); michael@0: if(rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: } michael@0: #endif /* NSS_DISABLE_ECC */ michael@0: michael@0: if(rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: if(paramDest && paramTemplate) { michael@0: rv = SEC_QuickDERDecodeItem(arena, paramDest, paramTemplate, michael@0: &(pki->algorithm.parameters)); michael@0: if(rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: } michael@0: michael@0: rv = SECFailure; michael@0: michael@0: switch (lpk->keyType) { michael@0: case NSSLOWKEYRSAKey: michael@0: keyType = CKK_RSA; michael@0: if(sftk_hasAttribute(key, CKA_NETSCAPE_DB)) { michael@0: sftk_DeleteAttributeType(key, CKA_NETSCAPE_DB); michael@0: } michael@0: crv = sftk_AddAttributeType(key, CKA_KEY_TYPE, &keyType, michael@0: sizeof(keyType)); michael@0: if(crv != CKR_OK) break; michael@0: crv = sftk_AddAttributeType(key, CKA_UNWRAP, &cktrue, michael@0: sizeof(CK_BBOOL)); michael@0: if(crv != CKR_OK) break; michael@0: crv = sftk_AddAttributeType(key, CKA_DECRYPT, &cktrue, michael@0: sizeof(CK_BBOOL)); michael@0: if(crv != CKR_OK) break; michael@0: crv = sftk_AddAttributeType(key, CKA_SIGN, &cktrue, michael@0: sizeof(CK_BBOOL)); michael@0: if(crv != CKR_OK) break; michael@0: crv = sftk_AddAttributeType(key, CKA_SIGN_RECOVER, &cktrue, michael@0: sizeof(CK_BBOOL)); michael@0: if(crv != CKR_OK) break; michael@0: crv = sftk_AddAttributeType(key, CKA_MODULUS, michael@0: sftk_item_expand(&lpk->u.rsa.modulus)); michael@0: if(crv != CKR_OK) break; michael@0: crv = sftk_AddAttributeType(key, CKA_PUBLIC_EXPONENT, michael@0: sftk_item_expand(&lpk->u.rsa.publicExponent)); michael@0: if(crv != CKR_OK) break; michael@0: crv = sftk_AddAttributeType(key, CKA_PRIVATE_EXPONENT, michael@0: sftk_item_expand(&lpk->u.rsa.privateExponent)); michael@0: if(crv != CKR_OK) break; michael@0: crv = sftk_AddAttributeType(key, CKA_PRIME_1, michael@0: sftk_item_expand(&lpk->u.rsa.prime1)); michael@0: if(crv != CKR_OK) break; michael@0: crv = sftk_AddAttributeType(key, CKA_PRIME_2, michael@0: sftk_item_expand(&lpk->u.rsa.prime2)); michael@0: if(crv != CKR_OK) break; michael@0: crv = sftk_AddAttributeType(key, CKA_EXPONENT_1, michael@0: sftk_item_expand(&lpk->u.rsa.exponent1)); michael@0: if(crv != CKR_OK) break; michael@0: crv = sftk_AddAttributeType(key, CKA_EXPONENT_2, michael@0: sftk_item_expand(&lpk->u.rsa.exponent2)); michael@0: if(crv != CKR_OK) break; michael@0: crv = sftk_AddAttributeType(key, CKA_COEFFICIENT, michael@0: sftk_item_expand(&lpk->u.rsa.coefficient)); michael@0: break; michael@0: case NSSLOWKEYDSAKey: michael@0: keyType = CKK_DSA; michael@0: crv = (sftk_hasAttribute(key, CKA_NETSCAPE_DB)) ? CKR_OK : michael@0: CKR_KEY_TYPE_INCONSISTENT; michael@0: if(crv != CKR_OK) break; michael@0: crv = sftk_AddAttributeType(key, CKA_KEY_TYPE, &keyType, michael@0: sizeof(keyType)); michael@0: if(crv != CKR_OK) break; michael@0: crv = sftk_AddAttributeType(key, CKA_SIGN, &cktrue, michael@0: sizeof(CK_BBOOL)); michael@0: if(crv != CKR_OK) break; michael@0: crv = sftk_AddAttributeType(key, CKA_SIGN_RECOVER, &cktrue, michael@0: sizeof(CK_BBOOL)); michael@0: if(crv != CKR_OK) break; michael@0: crv = sftk_AddAttributeType(key, CKA_PRIME, michael@0: sftk_item_expand(&lpk->u.dsa.params.prime)); michael@0: if(crv != CKR_OK) break; michael@0: crv = sftk_AddAttributeType(key, CKA_SUBPRIME, michael@0: sftk_item_expand(&lpk->u.dsa.params.subPrime)); michael@0: if(crv != CKR_OK) break; michael@0: crv = sftk_AddAttributeType(key, CKA_BASE, michael@0: sftk_item_expand(&lpk->u.dsa.params.base)); michael@0: if(crv != CKR_OK) break; michael@0: crv = sftk_AddAttributeType(key, CKA_VALUE, michael@0: sftk_item_expand(&lpk->u.dsa.privateValue)); michael@0: if(crv != CKR_OK) break; michael@0: break; michael@0: #ifdef notdef michael@0: case NSSLOWKEYDHKey: michael@0: template = dhTemplate; michael@0: templateCount = sizeof(dhTemplate)/sizeof(CK_ATTRIBUTE); michael@0: keyType = CKK_DH; michael@0: break; michael@0: #endif michael@0: /* what about fortezza??? */ michael@0: #ifndef NSS_DISABLE_ECC michael@0: case NSSLOWKEYECKey: michael@0: keyType = CKK_EC; michael@0: crv = (sftk_hasAttribute(key, CKA_NETSCAPE_DB)) ? CKR_OK : michael@0: CKR_KEY_TYPE_INCONSISTENT; michael@0: if(crv != CKR_OK) break; michael@0: crv = sftk_AddAttributeType(key, CKA_KEY_TYPE, &keyType, michael@0: sizeof(keyType)); michael@0: if(crv != CKR_OK) break; michael@0: crv = sftk_AddAttributeType(key, CKA_SIGN, &cktrue, michael@0: sizeof(CK_BBOOL)); michael@0: if(crv != CKR_OK) break; michael@0: crv = sftk_AddAttributeType(key, CKA_SIGN_RECOVER, &cktrue, michael@0: sizeof(CK_BBOOL)); michael@0: if(crv != CKR_OK) break; michael@0: crv = sftk_AddAttributeType(key, CKA_DERIVE, &cktrue, michael@0: sizeof(CK_BBOOL)); michael@0: if(crv != CKR_OK) break; michael@0: crv = sftk_AddAttributeType(key, CKA_EC_PARAMS, michael@0: sftk_item_expand(&lpk->u.ec.ecParams.DEREncoding)); michael@0: if(crv != CKR_OK) break; michael@0: crv = sftk_AddAttributeType(key, CKA_VALUE, michael@0: sftk_item_expand(&lpk->u.ec.privateValue)); michael@0: if(crv != CKR_OK) break; michael@0: /* XXX Do we need to decode the EC Params here ?? */ michael@0: break; michael@0: #endif /* NSS_DISABLE_ECC */ michael@0: default: michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: michael@0: loser: michael@0: if(lpk) { michael@0: nsslowkey_DestroyPrivateKey(lpk); michael@0: } michael@0: michael@0: if(crv != CKR_OK) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: return SECSuccess; michael@0: } michael@0: michael@0: michael@0: /* NSC_UnwrapKey unwraps (decrypts) a wrapped key, creating a new key object. */ michael@0: CK_RV NSC_UnwrapKey(CK_SESSION_HANDLE hSession, michael@0: CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hUnwrappingKey, michael@0: CK_BYTE_PTR pWrappedKey, CK_ULONG ulWrappedKeyLen, michael@0: CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, michael@0: CK_OBJECT_HANDLE_PTR phKey) michael@0: { michael@0: SFTKObject *key = NULL; michael@0: SFTKSession *session; michael@0: CK_ULONG key_length = 0; michael@0: unsigned char * buf = NULL; michael@0: CK_RV crv = CKR_OK; michael@0: int i; michael@0: CK_ULONG bsize = ulWrappedKeyLen; michael@0: SFTKSlot *slot = sftk_SlotFromSessionHandle(hSession); michael@0: SECItem bpki; michael@0: CK_OBJECT_CLASS target_type = CKO_SECRET_KEY; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: if (!slot) { michael@0: return CKR_SESSION_HANDLE_INVALID; michael@0: } michael@0: /* michael@0: * now lets create an object to hang the attributes off of michael@0: */ michael@0: key = sftk_NewObject(slot); /* fill in the handle later */ michael@0: if (key == NULL) { michael@0: return CKR_HOST_MEMORY; michael@0: } michael@0: michael@0: /* michael@0: * load the template values into the object michael@0: */ michael@0: for (i=0; i < (int) ulAttributeCount; i++) { michael@0: if (pTemplate[i].type == CKA_VALUE_LEN) { michael@0: key_length = *(CK_ULONG *)pTemplate[i].pValue; michael@0: continue; michael@0: } michael@0: if (pTemplate[i].type == CKA_CLASS) { michael@0: target_type = *(CK_OBJECT_CLASS *)pTemplate[i].pValue; michael@0: } michael@0: crv = sftk_AddAttributeType(key,sftk_attr_expand(&pTemplate[i])); michael@0: if (crv != CKR_OK) break; michael@0: } michael@0: if (crv != CKR_OK) { michael@0: sftk_FreeObject(key); michael@0: return crv; michael@0: } michael@0: michael@0: crv = sftk_CryptInit(hSession,pMechanism,hUnwrappingKey,CKA_UNWRAP, michael@0: CKA_UNWRAP, SFTK_DECRYPT, PR_FALSE); michael@0: if (crv != CKR_OK) { michael@0: sftk_FreeObject(key); michael@0: return sftk_mapWrap(crv); michael@0: } michael@0: michael@0: /* allocate the buffer to decrypt into michael@0: * this assumes the unwrapped key is never larger than the michael@0: * wrapped key. For all the mechanisms we support this is true */ michael@0: buf = (unsigned char *)PORT_Alloc( ulWrappedKeyLen); michael@0: bsize = ulWrappedKeyLen; michael@0: michael@0: crv = NSC_Decrypt(hSession, pWrappedKey, ulWrappedKeyLen, buf, &bsize); michael@0: if (crv != CKR_OK) { michael@0: sftk_FreeObject(key); michael@0: PORT_Free(buf); michael@0: return sftk_mapWrap(crv); michael@0: } michael@0: michael@0: switch(target_type) { michael@0: case CKO_SECRET_KEY: michael@0: if (!sftk_hasAttribute(key,CKA_KEY_TYPE)) { michael@0: crv = CKR_TEMPLATE_INCOMPLETE; michael@0: break; michael@0: } michael@0: michael@0: if (key_length == 0 || key_length > bsize) { michael@0: key_length = bsize; michael@0: } michael@0: if (key_length > MAX_KEY_LEN) { michael@0: crv = CKR_TEMPLATE_INCONSISTENT; michael@0: break; michael@0: } michael@0: michael@0: /* add the value */ michael@0: crv = sftk_AddAttributeType(key,CKA_VALUE,buf,key_length); michael@0: break; michael@0: case CKO_PRIVATE_KEY: michael@0: bpki.data = (unsigned char *)buf; michael@0: bpki.len = bsize; michael@0: crv = CKR_OK; michael@0: if(sftk_unwrapPrivateKey(key, &bpki) != SECSuccess) { michael@0: crv = CKR_TEMPLATE_INCOMPLETE; michael@0: } michael@0: break; michael@0: default: michael@0: crv = CKR_TEMPLATE_INCONSISTENT; michael@0: break; michael@0: } michael@0: michael@0: PORT_ZFree(buf, bsize); michael@0: if (crv != CKR_OK) { sftk_FreeObject(key); return crv; } michael@0: michael@0: /* get the session */ michael@0: session = sftk_SessionFromHandle(hSession); michael@0: if (session == NULL) { michael@0: sftk_FreeObject(key); michael@0: return CKR_SESSION_HANDLE_INVALID; michael@0: } michael@0: michael@0: /* michael@0: * handle the base object stuff michael@0: */ michael@0: crv = sftk_handleObject(key,session); michael@0: *phKey = key->handle; michael@0: sftk_FreeSession(session); michael@0: sftk_FreeObject(key); michael@0: michael@0: return crv; michael@0: michael@0: } michael@0: michael@0: /* michael@0: * The SSL key gen mechanism create's lots of keys. This function handles the michael@0: * details of each of these key creation. michael@0: */ michael@0: static CK_RV michael@0: sftk_buildSSLKey(CK_SESSION_HANDLE hSession, SFTKObject *baseKey, michael@0: PRBool isMacKey, unsigned char *keyBlock, unsigned int keySize, michael@0: CK_OBJECT_HANDLE *keyHandle) michael@0: { michael@0: SFTKObject *key; michael@0: SFTKSession *session; michael@0: CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; michael@0: CK_BBOOL cktrue = CK_TRUE; michael@0: CK_BBOOL ckfalse = CK_FALSE; michael@0: CK_RV crv = CKR_HOST_MEMORY; michael@0: michael@0: /* michael@0: * now lets create an object to hang the attributes off of michael@0: */ michael@0: *keyHandle = CK_INVALID_HANDLE; michael@0: key = sftk_NewObject(baseKey->slot); michael@0: if (key == NULL) return CKR_HOST_MEMORY; michael@0: sftk_narrowToSessionObject(key)->wasDerived = PR_TRUE; michael@0: michael@0: crv = sftk_CopyObject(key,baseKey); michael@0: if (crv != CKR_OK) goto loser; michael@0: if (isMacKey) { michael@0: crv = sftk_forceAttribute(key,CKA_KEY_TYPE,&keyType,sizeof(keyType)); michael@0: if (crv != CKR_OK) goto loser; michael@0: crv = sftk_forceAttribute(key,CKA_DERIVE,&cktrue,sizeof(CK_BBOOL)); michael@0: if (crv != CKR_OK) goto loser; michael@0: crv = sftk_forceAttribute(key,CKA_ENCRYPT,&ckfalse,sizeof(CK_BBOOL)); michael@0: if (crv != CKR_OK) goto loser; michael@0: crv = sftk_forceAttribute(key,CKA_DECRYPT,&ckfalse,sizeof(CK_BBOOL)); michael@0: if (crv != CKR_OK) goto loser; michael@0: crv = sftk_forceAttribute(key,CKA_SIGN,&cktrue,sizeof(CK_BBOOL)); michael@0: if (crv != CKR_OK) goto loser; michael@0: crv = sftk_forceAttribute(key,CKA_VERIFY,&cktrue,sizeof(CK_BBOOL)); michael@0: if (crv != CKR_OK) goto loser; michael@0: crv = sftk_forceAttribute(key,CKA_WRAP,&ckfalse,sizeof(CK_BBOOL)); michael@0: if (crv != CKR_OK) goto loser; michael@0: crv = sftk_forceAttribute(key,CKA_UNWRAP,&ckfalse,sizeof(CK_BBOOL)); michael@0: if (crv != CKR_OK) goto loser; michael@0: } michael@0: crv = sftk_forceAttribute(key,CKA_VALUE,keyBlock,keySize); michael@0: if (crv != CKR_OK) goto loser; michael@0: michael@0: /* get the session */ michael@0: crv = CKR_HOST_MEMORY; michael@0: session = sftk_SessionFromHandle(hSession); michael@0: if (session == NULL) { goto loser; } michael@0: michael@0: crv = sftk_handleObject(key,session); michael@0: sftk_FreeSession(session); michael@0: *keyHandle = key->handle; michael@0: loser: michael@0: if (key) sftk_FreeObject(key); michael@0: return crv; michael@0: } michael@0: michael@0: /* michael@0: * if there is an error, we need to free the keys we already created in SSL michael@0: * This is the routine that will do it.. michael@0: */ michael@0: static void michael@0: sftk_freeSSLKeys(CK_SESSION_HANDLE session, michael@0: CK_SSL3_KEY_MAT_OUT *returnedMaterial ) michael@0: { michael@0: if (returnedMaterial->hClientMacSecret != CK_INVALID_HANDLE) { michael@0: NSC_DestroyObject(session,returnedMaterial->hClientMacSecret); michael@0: } michael@0: if (returnedMaterial->hServerMacSecret != CK_INVALID_HANDLE) { michael@0: NSC_DestroyObject(session, returnedMaterial->hServerMacSecret); michael@0: } michael@0: if (returnedMaterial->hClientKey != CK_INVALID_HANDLE) { michael@0: NSC_DestroyObject(session, returnedMaterial->hClientKey); michael@0: } michael@0: if (returnedMaterial->hServerKey != CK_INVALID_HANDLE) { michael@0: NSC_DestroyObject(session, returnedMaterial->hServerKey); michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * when deriving from sensitive and extractable keys, we need to preserve some michael@0: * of the semantics in the derived key. This helper routine maintains these michael@0: * semantics. michael@0: */ michael@0: static CK_RV michael@0: sftk_DeriveSensitiveCheck(SFTKObject *baseKey,SFTKObject *destKey) michael@0: { michael@0: PRBool hasSensitive; michael@0: PRBool sensitive = PR_FALSE; michael@0: PRBool hasExtractable; michael@0: PRBool extractable = PR_TRUE; michael@0: CK_RV crv = CKR_OK; michael@0: SFTKAttribute *att; michael@0: michael@0: hasSensitive = PR_FALSE; michael@0: att = sftk_FindAttribute(destKey,CKA_SENSITIVE); michael@0: if (att) { michael@0: hasSensitive = PR_TRUE; michael@0: sensitive = (PRBool) *(CK_BBOOL *)att->attrib.pValue; michael@0: sftk_FreeAttribute(att); michael@0: } michael@0: michael@0: hasExtractable = PR_FALSE; michael@0: att = sftk_FindAttribute(destKey,CKA_EXTRACTABLE); michael@0: if (att) { michael@0: hasExtractable = PR_TRUE; michael@0: extractable = (PRBool) *(CK_BBOOL *)att->attrib.pValue; michael@0: sftk_FreeAttribute(att); michael@0: } michael@0: michael@0: michael@0: /* don't make a key more accessible */ michael@0: if (sftk_isTrue(baseKey,CKA_SENSITIVE) && hasSensitive && michael@0: (sensitive == PR_FALSE)) { michael@0: return CKR_KEY_FUNCTION_NOT_PERMITTED; michael@0: } michael@0: if (!sftk_isTrue(baseKey,CKA_EXTRACTABLE) && hasExtractable && michael@0: (extractable == PR_TRUE)) { michael@0: return CKR_KEY_FUNCTION_NOT_PERMITTED; michael@0: } michael@0: michael@0: /* inherit parent's sensitivity */ michael@0: if (!hasSensitive) { michael@0: att = sftk_FindAttribute(baseKey,CKA_SENSITIVE); michael@0: if (att == NULL) return CKR_KEY_TYPE_INCONSISTENT; michael@0: crv = sftk_defaultAttribute(destKey,sftk_attr_expand(&att->attrib)); michael@0: sftk_FreeAttribute(att); michael@0: if (crv != CKR_OK) return crv; michael@0: } michael@0: if (!hasExtractable) { michael@0: att = sftk_FindAttribute(baseKey,CKA_EXTRACTABLE); michael@0: if (att == NULL) return CKR_KEY_TYPE_INCONSISTENT; michael@0: crv = sftk_defaultAttribute(destKey,sftk_attr_expand(&att->attrib)); michael@0: sftk_FreeAttribute(att); michael@0: if (crv != CKR_OK) return crv; michael@0: } michael@0: michael@0: /* we should inherit the parent's always extractable/ never sensitive info, michael@0: * but handleObject always forces this attributes, so we would need to do michael@0: * something special. */ michael@0: return CKR_OK; michael@0: } michael@0: michael@0: /* michael@0: * make known fixed PKCS #11 key types to their sizes in bytes michael@0: */ michael@0: unsigned long michael@0: sftk_MapKeySize(CK_KEY_TYPE keyType) michael@0: { michael@0: switch (keyType) { michael@0: case CKK_CDMF: michael@0: return 8; michael@0: case CKK_DES: michael@0: return 8; michael@0: case CKK_DES2: michael@0: return 16; michael@0: case CKK_DES3: michael@0: return 24; michael@0: /* IDEA and CAST need to be added */ michael@0: default: michael@0: break; michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: #ifndef NSS_DISABLE_ECC michael@0: /* Inputs: michael@0: * key_len: Length of derived key to be generated. michael@0: * SharedSecret: a shared secret that is the output of a key agreement primitive. michael@0: * SharedInfo: (Optional) some data shared by the entities computing the secret key. michael@0: * SharedInfoLen: the length in octets of SharedInfo michael@0: * Hash: The hash function to be used in the KDF michael@0: * HashLen: the length in octets of the output of Hash michael@0: * Output: michael@0: * key: Pointer to a buffer containing derived key, if return value is SECSuccess. michael@0: */ michael@0: static CK_RV sftk_compute_ANSI_X9_63_kdf(CK_BYTE **key, CK_ULONG key_len, SECItem *SharedSecret, michael@0: CK_BYTE_PTR SharedInfo, CK_ULONG SharedInfoLen, michael@0: SECStatus Hash(unsigned char *, const unsigned char *, PRUint32), michael@0: CK_ULONG HashLen) michael@0: { michael@0: unsigned char *buffer = NULL, *output_buffer = NULL; michael@0: PRUint32 buffer_len, max_counter, i; michael@0: SECStatus rv; michael@0: CK_RV crv; michael@0: michael@0: /* Check that key_len isn't too long. The maximum key length could be michael@0: * greatly increased if the code below did not limit the 4-byte counter michael@0: * to a maximum value of 255. */ michael@0: if (key_len > 254 * HashLen) michael@0: return CKR_ARGUMENTS_BAD; michael@0: michael@0: if (SharedInfo == NULL) michael@0: SharedInfoLen = 0; michael@0: michael@0: buffer_len = SharedSecret->len + 4 + SharedInfoLen; michael@0: buffer = (CK_BYTE *)PORT_Alloc(buffer_len); michael@0: if (buffer == NULL) { michael@0: crv = CKR_HOST_MEMORY; michael@0: goto loser; michael@0: } michael@0: michael@0: max_counter = key_len/HashLen; michael@0: if (key_len > max_counter * HashLen) michael@0: max_counter++; michael@0: michael@0: output_buffer = (CK_BYTE *)PORT_Alloc(max_counter * HashLen); michael@0: if (output_buffer == NULL) { michael@0: crv = CKR_HOST_MEMORY; michael@0: goto loser; michael@0: } michael@0: michael@0: /* Populate buffer with SharedSecret || Counter || [SharedInfo] michael@0: * where Counter is 0x00000001 */ michael@0: PORT_Memcpy(buffer, SharedSecret->data, SharedSecret->len); michael@0: buffer[SharedSecret->len] = 0; michael@0: buffer[SharedSecret->len + 1] = 0; michael@0: buffer[SharedSecret->len + 2] = 0; michael@0: buffer[SharedSecret->len + 3] = 1; michael@0: if (SharedInfo) { michael@0: PORT_Memcpy(&buffer[SharedSecret->len + 4], SharedInfo, SharedInfoLen); michael@0: } michael@0: michael@0: for(i=0; i < max_counter; i++) { michael@0: rv = Hash(&output_buffer[i * HashLen], buffer, buffer_len); michael@0: if (rv != SECSuccess) { michael@0: /* 'Hash' should not fail. */ michael@0: crv = CKR_FUNCTION_FAILED; michael@0: goto loser; michael@0: } michael@0: michael@0: /* Increment counter (assumes max_counter < 255) */ michael@0: buffer[SharedSecret->len + 3]++; michael@0: } michael@0: michael@0: PORT_ZFree(buffer, buffer_len); michael@0: if (key_len < max_counter * HashLen) { michael@0: PORT_Memset(output_buffer + key_len, 0, max_counter * HashLen - key_len); michael@0: } michael@0: *key = output_buffer; michael@0: michael@0: return CKR_OK; michael@0: michael@0: loser: michael@0: if (buffer) { michael@0: PORT_ZFree(buffer, buffer_len); michael@0: } michael@0: if (output_buffer) { michael@0: PORT_ZFree(output_buffer, max_counter * HashLen); michael@0: } michael@0: return crv; michael@0: } michael@0: michael@0: static CK_RV sftk_ANSI_X9_63_kdf(CK_BYTE **key, CK_ULONG key_len, michael@0: SECItem *SharedSecret, michael@0: CK_BYTE_PTR SharedInfo, CK_ULONG SharedInfoLen, michael@0: CK_EC_KDF_TYPE kdf) michael@0: { michael@0: if (kdf == CKD_SHA1_KDF) michael@0: return sftk_compute_ANSI_X9_63_kdf(key, key_len, SharedSecret, SharedInfo, michael@0: SharedInfoLen, SHA1_HashBuf, SHA1_LENGTH); michael@0: else if (kdf == CKD_SHA224_KDF) michael@0: return sftk_compute_ANSI_X9_63_kdf(key, key_len, SharedSecret, SharedInfo, michael@0: SharedInfoLen, SHA224_HashBuf, SHA224_LENGTH); michael@0: else if (kdf == CKD_SHA256_KDF) michael@0: return sftk_compute_ANSI_X9_63_kdf(key, key_len, SharedSecret, SharedInfo, michael@0: SharedInfoLen, SHA256_HashBuf, SHA256_LENGTH); michael@0: else if (kdf == CKD_SHA384_KDF) michael@0: return sftk_compute_ANSI_X9_63_kdf(key, key_len, SharedSecret, SharedInfo, michael@0: SharedInfoLen, SHA384_HashBuf, SHA384_LENGTH); michael@0: else if (kdf == CKD_SHA512_KDF) michael@0: return sftk_compute_ANSI_X9_63_kdf(key, key_len, SharedSecret, SharedInfo, michael@0: SharedInfoLen, SHA512_HashBuf, SHA512_LENGTH); michael@0: else michael@0: return CKR_MECHANISM_INVALID; michael@0: } michael@0: #endif /* NSS_DISABLE_ECC */ michael@0: michael@0: /* michael@0: * SSL Key generation given pre master secret michael@0: */ michael@0: #define NUM_MIXERS 9 michael@0: static const char * const mixers[NUM_MIXERS] = { michael@0: "A", michael@0: "BB", michael@0: "CCC", michael@0: "DDDD", michael@0: "EEEEE", michael@0: "FFFFFF", michael@0: "GGGGGGG", michael@0: "HHHHHHHH", michael@0: "IIIIIIIII" }; michael@0: #define SSL3_PMS_LENGTH 48 michael@0: #define SSL3_MASTER_SECRET_LENGTH 48 michael@0: #define SSL3_RANDOM_LENGTH 32 michael@0: michael@0: michael@0: /* NSC_DeriveKey derives a key from a base key, creating a new key object. */ michael@0: CK_RV NSC_DeriveKey( CK_SESSION_HANDLE hSession, michael@0: CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hBaseKey, michael@0: CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, michael@0: CK_OBJECT_HANDLE_PTR phKey) michael@0: { michael@0: SFTKSession * session; michael@0: SFTKSlot * slot = sftk_SlotFromSessionHandle(hSession); michael@0: SFTKObject * key; michael@0: SFTKObject * sourceKey; michael@0: SFTKAttribute * att = NULL; michael@0: SFTKAttribute * att2 = NULL; michael@0: unsigned char * buf; michael@0: SHA1Context * sha; michael@0: MD5Context * md5; michael@0: MD2Context * md2; michael@0: CK_ULONG macSize; michael@0: CK_ULONG tmpKeySize; michael@0: CK_ULONG IVSize; michael@0: CK_ULONG keySize = 0; michael@0: CK_RV crv = CKR_OK; michael@0: CK_BBOOL cktrue = CK_TRUE; michael@0: CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; michael@0: CK_OBJECT_CLASS classType = CKO_SECRET_KEY; michael@0: CK_KEY_DERIVATION_STRING_DATA *stringPtr; michael@0: PRBool isTLS = PR_FALSE; michael@0: PRBool isSHA256 = PR_FALSE; michael@0: PRBool isDH = PR_FALSE; michael@0: SECStatus rv; michael@0: int i; michael@0: unsigned int outLen; michael@0: unsigned char sha_out[SHA1_LENGTH]; michael@0: unsigned char key_block[NUM_MIXERS * MD5_LENGTH]; michael@0: unsigned char key_block2[MD5_LENGTH]; michael@0: PRBool isFIPS; michael@0: HASH_HashType hashType; michael@0: PRBool extractValue = PR_TRUE; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: if (!slot) { michael@0: return CKR_SESSION_HANDLE_INVALID; michael@0: } michael@0: /* michael@0: * now lets create an object to hang the attributes off of michael@0: */ michael@0: if (phKey) *phKey = CK_INVALID_HANDLE; michael@0: michael@0: key = sftk_NewObject(slot); /* fill in the handle later */ michael@0: if (key == NULL) { michael@0: return CKR_HOST_MEMORY; michael@0: } michael@0: isFIPS = (slot->slotID == FIPS_SLOT_ID); michael@0: michael@0: /* michael@0: * load the template values into the object michael@0: */ michael@0: for (i=0; i < (int) ulAttributeCount; i++) { michael@0: crv = sftk_AddAttributeType(key,sftk_attr_expand(&pTemplate[i])); michael@0: if (crv != CKR_OK) break; michael@0: michael@0: if (pTemplate[i].type == CKA_KEY_TYPE) { michael@0: keyType = *(CK_KEY_TYPE *)pTemplate[i].pValue; michael@0: } michael@0: if (pTemplate[i].type == CKA_VALUE_LEN) { michael@0: keySize = *(CK_ULONG *)pTemplate[i].pValue; michael@0: } michael@0: } michael@0: if (crv != CKR_OK) { sftk_FreeObject(key); return crv; } michael@0: michael@0: if (keySize == 0) { michael@0: keySize = sftk_MapKeySize(keyType); michael@0: } michael@0: michael@0: switch (pMechanism->mechanism) { michael@0: case CKM_NSS_JPAKE_ROUND2_SHA1: /* fall through */ michael@0: case CKM_NSS_JPAKE_ROUND2_SHA256: /* fall through */ michael@0: case CKM_NSS_JPAKE_ROUND2_SHA384: /* fall through */ michael@0: case CKM_NSS_JPAKE_ROUND2_SHA512: michael@0: extractValue = PR_FALSE; michael@0: classType = CKO_PRIVATE_KEY; michael@0: break; michael@0: case CKM_NSS_JPAKE_FINAL_SHA1: /* fall through */ michael@0: case CKM_NSS_JPAKE_FINAL_SHA256: /* fall through */ michael@0: case CKM_NSS_JPAKE_FINAL_SHA384: /* fall through */ michael@0: case CKM_NSS_JPAKE_FINAL_SHA512: michael@0: extractValue = PR_FALSE; michael@0: /* fall through */ michael@0: default: michael@0: classType = CKO_SECRET_KEY; michael@0: } michael@0: michael@0: crv = sftk_forceAttribute (key,CKA_CLASS,&classType,sizeof(classType)); michael@0: if (crv != CKR_OK) { michael@0: sftk_FreeObject(key); michael@0: return crv; michael@0: } michael@0: michael@0: /* look up the base key we're deriving with */ michael@0: session = sftk_SessionFromHandle(hSession); michael@0: if (session == NULL) { michael@0: sftk_FreeObject(key); michael@0: return CKR_SESSION_HANDLE_INVALID; michael@0: } michael@0: michael@0: sourceKey = sftk_ObjectFromHandle(hBaseKey,session); michael@0: sftk_FreeSession(session); michael@0: if (sourceKey == NULL) { michael@0: sftk_FreeObject(key); michael@0: return CKR_KEY_HANDLE_INVALID; michael@0: } michael@0: michael@0: if (extractValue) { michael@0: /* get the value of the base key */ michael@0: att = sftk_FindAttribute(sourceKey,CKA_VALUE); michael@0: if (att == NULL) { michael@0: sftk_FreeObject(key); michael@0: sftk_FreeObject(sourceKey); michael@0: return CKR_KEY_HANDLE_INVALID; michael@0: } michael@0: } michael@0: michael@0: switch (pMechanism->mechanism) { michael@0: /* michael@0: * generate the master secret michael@0: */ michael@0: case CKM_NSS_TLS_MASTER_KEY_DERIVE_SHA256: michael@0: case CKM_NSS_TLS_MASTER_KEY_DERIVE_DH_SHA256: michael@0: isSHA256 = PR_TRUE; michael@0: /* fall thru */ michael@0: case CKM_TLS_MASTER_KEY_DERIVE: michael@0: case CKM_TLS_MASTER_KEY_DERIVE_DH: michael@0: isTLS = PR_TRUE; michael@0: /* fall thru */ michael@0: case CKM_SSL3_MASTER_KEY_DERIVE: michael@0: case CKM_SSL3_MASTER_KEY_DERIVE_DH: michael@0: { michael@0: CK_SSL3_MASTER_KEY_DERIVE_PARAMS *ssl3_master; michael@0: SSL3RSAPreMasterSecret * rsa_pms; michael@0: unsigned char crsrdata[SSL3_RANDOM_LENGTH * 2]; michael@0: michael@0: if ((pMechanism->mechanism == CKM_SSL3_MASTER_KEY_DERIVE_DH) || michael@0: (pMechanism->mechanism == CKM_TLS_MASTER_KEY_DERIVE_DH) || michael@0: (pMechanism->mechanism == CKM_NSS_TLS_MASTER_KEY_DERIVE_DH_SHA256)) michael@0: isDH = PR_TRUE; michael@0: michael@0: /* first do the consistancy checks */ michael@0: if (!isDH && (att->attrib.ulValueLen != SSL3_PMS_LENGTH)) { michael@0: crv = CKR_KEY_TYPE_INCONSISTENT; michael@0: break; michael@0: } michael@0: att2 = sftk_FindAttribute(sourceKey,CKA_KEY_TYPE); michael@0: if ((att2 == NULL) || (*(CK_KEY_TYPE *)att2->attrib.pValue != michael@0: CKK_GENERIC_SECRET)) { michael@0: if (att2) sftk_FreeAttribute(att2); michael@0: crv = CKR_KEY_FUNCTION_NOT_PERMITTED; michael@0: break; michael@0: } michael@0: sftk_FreeAttribute(att2); michael@0: if (keyType != CKK_GENERIC_SECRET) { michael@0: crv = CKR_KEY_FUNCTION_NOT_PERMITTED; michael@0: break; michael@0: } michael@0: if ((keySize != 0) && (keySize != SSL3_MASTER_SECRET_LENGTH)) { michael@0: crv = CKR_KEY_FUNCTION_NOT_PERMITTED; michael@0: break; michael@0: } michael@0: michael@0: /* finally do the key gen */ michael@0: ssl3_master = (CK_SSL3_MASTER_KEY_DERIVE_PARAMS *) michael@0: pMechanism->pParameter; michael@0: michael@0: PORT_Memcpy(crsrdata, michael@0: ssl3_master->RandomInfo.pClientRandom, SSL3_RANDOM_LENGTH); michael@0: PORT_Memcpy(crsrdata + SSL3_RANDOM_LENGTH, michael@0: ssl3_master->RandomInfo.pServerRandom, SSL3_RANDOM_LENGTH); michael@0: michael@0: if (ssl3_master->pVersion) { michael@0: SFTKSessionObject *sessKey = sftk_narrowToSessionObject(key); michael@0: rsa_pms = (SSL3RSAPreMasterSecret *) att->attrib.pValue; michael@0: /* don't leak more key material then necessary for SSL to work */ michael@0: if ((sessKey == NULL) || sessKey->wasDerived) { michael@0: ssl3_master->pVersion->major = 0xff; michael@0: ssl3_master->pVersion->minor = 0xff; michael@0: } else { michael@0: ssl3_master->pVersion->major = rsa_pms->client_version[0]; michael@0: ssl3_master->pVersion->minor = rsa_pms->client_version[1]; michael@0: } michael@0: } michael@0: if (ssl3_master->RandomInfo.ulClientRandomLen != SSL3_RANDOM_LENGTH) { michael@0: crv = CKR_MECHANISM_PARAM_INVALID; michael@0: break; michael@0: } michael@0: if (ssl3_master->RandomInfo.ulServerRandomLen != SSL3_RANDOM_LENGTH) { michael@0: crv = CKR_MECHANISM_PARAM_INVALID; michael@0: break; michael@0: } michael@0: michael@0: if (isTLS) { michael@0: SECStatus status; michael@0: SECItem crsr = { siBuffer, NULL, 0 }; michael@0: SECItem master = { siBuffer, NULL, 0 }; michael@0: SECItem pms = { siBuffer, NULL, 0 }; michael@0: michael@0: crsr.data = crsrdata; michael@0: crsr.len = sizeof crsrdata; michael@0: master.data = key_block; michael@0: master.len = SSL3_MASTER_SECRET_LENGTH; michael@0: pms.data = (unsigned char*)att->attrib.pValue; michael@0: pms.len = att->attrib.ulValueLen; michael@0: michael@0: if (isSHA256) { michael@0: status = TLS_P_hash(HASH_AlgSHA256, &pms, "master secret", michael@0: &crsr, &master, isFIPS); michael@0: } else { michael@0: status = TLS_PRF(&pms, "master secret", &crsr, &master, isFIPS); michael@0: } michael@0: if (status != SECSuccess) { michael@0: crv = CKR_FUNCTION_FAILED; michael@0: break; michael@0: } michael@0: } else { michael@0: /* now allocate the hash contexts */ michael@0: md5 = MD5_NewContext(); michael@0: if (md5 == NULL) { michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: sha = SHA1_NewContext(); michael@0: if (sha == NULL) { michael@0: PORT_Free(md5); michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: for (i = 0; i < 3; i++) { michael@0: SHA1_Begin(sha); michael@0: SHA1_Update(sha, (unsigned char*) mixers[i], strlen(mixers[i])); michael@0: SHA1_Update(sha, (const unsigned char*)att->attrib.pValue, michael@0: att->attrib.ulValueLen); michael@0: SHA1_Update(sha, crsrdata, sizeof crsrdata); michael@0: SHA1_End(sha, sha_out, &outLen, SHA1_LENGTH); michael@0: PORT_Assert(outLen == SHA1_LENGTH); michael@0: michael@0: MD5_Begin(md5); michael@0: MD5_Update(md5, (const unsigned char*)att->attrib.pValue, michael@0: att->attrib.ulValueLen); michael@0: MD5_Update(md5, sha_out, outLen); michael@0: MD5_End(md5, &key_block[i*MD5_LENGTH], &outLen, MD5_LENGTH); michael@0: PORT_Assert(outLen == MD5_LENGTH); michael@0: } michael@0: PORT_Free(md5); michael@0: PORT_Free(sha); michael@0: } michael@0: michael@0: /* store the results */ michael@0: crv = sftk_forceAttribute michael@0: (key,CKA_VALUE,key_block,SSL3_MASTER_SECRET_LENGTH); michael@0: if (crv != CKR_OK) break; michael@0: keyType = CKK_GENERIC_SECRET; michael@0: crv = sftk_forceAttribute (key,CKA_KEY_TYPE,&keyType,sizeof(keyType)); michael@0: if (isTLS) { michael@0: /* TLS's master secret is used to "sign" finished msgs with PRF. */ michael@0: /* XXX This seems like a hack. But SFTK_Derive only accepts michael@0: * one "operation" argument. */ michael@0: crv = sftk_forceAttribute(key,CKA_SIGN, &cktrue,sizeof(CK_BBOOL)); michael@0: if (crv != CKR_OK) break; michael@0: crv = sftk_forceAttribute(key,CKA_VERIFY,&cktrue,sizeof(CK_BBOOL)); michael@0: if (crv != CKR_OK) break; michael@0: /* While we're here, we might as well force this, too. */ michael@0: crv = sftk_forceAttribute(key,CKA_DERIVE,&cktrue,sizeof(CK_BBOOL)); michael@0: if (crv != CKR_OK) break; michael@0: } michael@0: break; michael@0: } michael@0: michael@0: case CKM_NSS_TLS_KEY_AND_MAC_DERIVE_SHA256: michael@0: isSHA256 = PR_TRUE; michael@0: /* fall thru */ michael@0: case CKM_TLS_KEY_AND_MAC_DERIVE: michael@0: isTLS = PR_TRUE; michael@0: /* fall thru */ michael@0: case CKM_SSL3_KEY_AND_MAC_DERIVE: michael@0: { michael@0: CK_SSL3_KEY_MAT_PARAMS *ssl3_keys; michael@0: CK_SSL3_KEY_MAT_OUT * ssl3_keys_out; michael@0: CK_ULONG effKeySize; michael@0: unsigned int block_needed; michael@0: unsigned char srcrdata[SSL3_RANDOM_LENGTH * 2]; michael@0: unsigned char crsrdata[SSL3_RANDOM_LENGTH * 2]; michael@0: michael@0: crv = sftk_DeriveSensitiveCheck(sourceKey,key); michael@0: if (crv != CKR_OK) break; michael@0: michael@0: if (att->attrib.ulValueLen != SSL3_MASTER_SECRET_LENGTH) { michael@0: crv = CKR_KEY_FUNCTION_NOT_PERMITTED; michael@0: break; michael@0: } michael@0: att2 = sftk_FindAttribute(sourceKey,CKA_KEY_TYPE); michael@0: if ((att2 == NULL) || (*(CK_KEY_TYPE *)att2->attrib.pValue != michael@0: CKK_GENERIC_SECRET)) { michael@0: if (att2) sftk_FreeAttribute(att2); michael@0: crv = CKR_KEY_FUNCTION_NOT_PERMITTED; michael@0: break; michael@0: } michael@0: sftk_FreeAttribute(att2); michael@0: md5 = MD5_NewContext(); michael@0: if (md5 == NULL) { michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: sha = SHA1_NewContext(); michael@0: if (sha == NULL) { michael@0: PORT_Free(md5); michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: ssl3_keys = (CK_SSL3_KEY_MAT_PARAMS *) pMechanism->pParameter; michael@0: michael@0: PORT_Memcpy(srcrdata, michael@0: ssl3_keys->RandomInfo.pServerRandom, SSL3_RANDOM_LENGTH); michael@0: PORT_Memcpy(srcrdata + SSL3_RANDOM_LENGTH, michael@0: ssl3_keys->RandomInfo.pClientRandom, SSL3_RANDOM_LENGTH); michael@0: michael@0: PORT_Memcpy(crsrdata, michael@0: ssl3_keys->RandomInfo.pClientRandom, SSL3_RANDOM_LENGTH); michael@0: PORT_Memcpy(crsrdata + SSL3_RANDOM_LENGTH, michael@0: ssl3_keys->RandomInfo.pServerRandom, SSL3_RANDOM_LENGTH); michael@0: michael@0: /* michael@0: * clear out our returned keys so we can recover on failure michael@0: */ michael@0: ssl3_keys_out = ssl3_keys->pReturnedKeyMaterial; michael@0: ssl3_keys_out->hClientMacSecret = CK_INVALID_HANDLE; michael@0: ssl3_keys_out->hServerMacSecret = CK_INVALID_HANDLE; michael@0: ssl3_keys_out->hClientKey = CK_INVALID_HANDLE; michael@0: ssl3_keys_out->hServerKey = CK_INVALID_HANDLE; michael@0: michael@0: /* michael@0: * How much key material do we need? michael@0: */ michael@0: macSize = ssl3_keys->ulMacSizeInBits/8; michael@0: effKeySize = ssl3_keys->ulKeySizeInBits/8; michael@0: IVSize = ssl3_keys->ulIVSizeInBits/8; michael@0: if (keySize == 0) { michael@0: effKeySize = keySize; michael@0: } michael@0: block_needed = 2 * (macSize + effKeySize + michael@0: ((!ssl3_keys->bIsExport) * IVSize)); michael@0: PORT_Assert(block_needed <= sizeof key_block); michael@0: if (block_needed > sizeof key_block) michael@0: block_needed = sizeof key_block; michael@0: michael@0: /* michael@0: * generate the key material: This looks amazingly similar to the michael@0: * PMS code, and is clearly crying out for a function to provide it. michael@0: */ michael@0: if (isTLS) { michael@0: SECStatus status; michael@0: SECItem srcr = { siBuffer, NULL, 0 }; michael@0: SECItem keyblk = { siBuffer, NULL, 0 }; michael@0: SECItem master = { siBuffer, NULL, 0 }; michael@0: michael@0: srcr.data = srcrdata; michael@0: srcr.len = sizeof srcrdata; michael@0: keyblk.data = key_block; michael@0: keyblk.len = block_needed; michael@0: master.data = (unsigned char*)att->attrib.pValue; michael@0: master.len = att->attrib.ulValueLen; michael@0: michael@0: if (isSHA256) { michael@0: status = TLS_P_hash(HASH_AlgSHA256, &master, "key expansion", michael@0: &srcr, &keyblk, isFIPS); michael@0: } else { michael@0: status = TLS_PRF(&master, "key expansion", &srcr, &keyblk, michael@0: isFIPS); michael@0: } michael@0: if (status != SECSuccess) { michael@0: goto key_and_mac_derive_fail; michael@0: } michael@0: } else { michael@0: unsigned int block_bytes = 0; michael@0: /* key_block = michael@0: * MD5(master_secret + SHA('A' + master_secret + michael@0: * ServerHello.random + ClientHello.random)) + michael@0: * MD5(master_secret + SHA('BB' + master_secret + michael@0: * ServerHello.random + ClientHello.random)) + michael@0: * MD5(master_secret + SHA('CCC' + master_secret + michael@0: * ServerHello.random + ClientHello.random)) + michael@0: * [...]; michael@0: */ michael@0: for (i = 0; i < NUM_MIXERS && block_bytes < block_needed; i++) { michael@0: SHA1_Begin(sha); michael@0: SHA1_Update(sha, (unsigned char*) mixers[i], strlen(mixers[i])); michael@0: SHA1_Update(sha, (const unsigned char*)att->attrib.pValue, michael@0: att->attrib.ulValueLen); michael@0: SHA1_Update(sha, srcrdata, sizeof srcrdata); michael@0: SHA1_End(sha, sha_out, &outLen, SHA1_LENGTH); michael@0: PORT_Assert(outLen == SHA1_LENGTH); michael@0: MD5_Begin(md5); michael@0: MD5_Update(md5, (const unsigned char*)att->attrib.pValue, michael@0: att->attrib.ulValueLen); michael@0: MD5_Update(md5, sha_out, outLen); michael@0: MD5_End(md5, &key_block[i*MD5_LENGTH], &outLen, MD5_LENGTH); michael@0: PORT_Assert(outLen == MD5_LENGTH); michael@0: block_bytes += outLen; michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * Put the key material where it goes. michael@0: */ michael@0: i = 0; /* now shows how much consumed */ michael@0: michael@0: /* michael@0: * The key_block is partitioned as follows: michael@0: * client_write_MAC_secret[CipherSpec.hash_size] michael@0: */ michael@0: crv = sftk_buildSSLKey(hSession,key,PR_TRUE,&key_block[i],macSize, michael@0: &ssl3_keys_out->hClientMacSecret); michael@0: if (crv != CKR_OK) michael@0: goto key_and_mac_derive_fail; michael@0: michael@0: i += macSize; michael@0: michael@0: /* michael@0: * server_write_MAC_secret[CipherSpec.hash_size] michael@0: */ michael@0: crv = sftk_buildSSLKey(hSession,key,PR_TRUE,&key_block[i],macSize, michael@0: &ssl3_keys_out->hServerMacSecret); michael@0: if (crv != CKR_OK) { michael@0: goto key_and_mac_derive_fail; michael@0: } michael@0: i += macSize; michael@0: michael@0: if (keySize) { michael@0: if (!ssl3_keys->bIsExport) { michael@0: /* michael@0: ** Generate Domestic write keys and IVs. michael@0: ** client_write_key[CipherSpec.key_material] michael@0: */ michael@0: crv = sftk_buildSSLKey(hSession,key,PR_FALSE,&key_block[i], michael@0: keySize, &ssl3_keys_out->hClientKey); michael@0: if (crv != CKR_OK) { michael@0: goto key_and_mac_derive_fail; michael@0: } michael@0: i += keySize; michael@0: michael@0: /* michael@0: ** server_write_key[CipherSpec.key_material] michael@0: */ michael@0: crv = sftk_buildSSLKey(hSession,key,PR_FALSE,&key_block[i], michael@0: keySize, &ssl3_keys_out->hServerKey); michael@0: if (crv != CKR_OK) { michael@0: goto key_and_mac_derive_fail; michael@0: } michael@0: i += keySize; michael@0: michael@0: /* michael@0: ** client_write_IV[CipherSpec.IV_size] michael@0: */ michael@0: if (IVSize > 0) { michael@0: PORT_Memcpy(ssl3_keys_out->pIVClient, michael@0: &key_block[i], IVSize); michael@0: i += IVSize; michael@0: } michael@0: michael@0: /* michael@0: ** server_write_IV[CipherSpec.IV_size] michael@0: */ michael@0: if (IVSize > 0) { michael@0: PORT_Memcpy(ssl3_keys_out->pIVServer, michael@0: &key_block[i], IVSize); michael@0: i += IVSize; michael@0: } michael@0: PORT_Assert(i <= sizeof key_block); michael@0: michael@0: } else if (!isTLS) { michael@0: michael@0: /* michael@0: ** Generate SSL3 Export write keys and IVs. michael@0: ** client_write_key[CipherSpec.key_material] michael@0: ** final_client_write_key = MD5(client_write_key + michael@0: ** ClientHello.random + ServerHello.random); michael@0: */ michael@0: MD5_Begin(md5); michael@0: MD5_Update(md5, &key_block[i], effKeySize); michael@0: MD5_Update(md5, crsrdata, sizeof crsrdata); michael@0: MD5_End(md5, key_block2, &outLen, MD5_LENGTH); michael@0: i += effKeySize; michael@0: crv = sftk_buildSSLKey(hSession,key,PR_FALSE,key_block2, michael@0: keySize,&ssl3_keys_out->hClientKey); michael@0: if (crv != CKR_OK) { michael@0: goto key_and_mac_derive_fail; michael@0: } michael@0: michael@0: /* michael@0: ** server_write_key[CipherSpec.key_material] michael@0: ** final_server_write_key = MD5(server_write_key + michael@0: ** ServerHello.random + ClientHello.random); michael@0: */ michael@0: MD5_Begin(md5); michael@0: MD5_Update(md5, &key_block[i], effKeySize); michael@0: MD5_Update(md5, srcrdata, sizeof srcrdata); michael@0: MD5_End(md5, key_block2, &outLen, MD5_LENGTH); michael@0: i += effKeySize; michael@0: crv = sftk_buildSSLKey(hSession,key,PR_FALSE,key_block2, michael@0: keySize,&ssl3_keys_out->hServerKey); michael@0: if (crv != CKR_OK) { michael@0: goto key_and_mac_derive_fail; michael@0: } michael@0: michael@0: /* michael@0: ** client_write_IV = michael@0: ** MD5(ClientHello.random + ServerHello.random); michael@0: */ michael@0: MD5_Begin(md5); michael@0: MD5_Update(md5, crsrdata, sizeof crsrdata); michael@0: MD5_End(md5, key_block2, &outLen, MD5_LENGTH); michael@0: PORT_Memcpy(ssl3_keys_out->pIVClient, key_block2, IVSize); michael@0: michael@0: /* michael@0: ** server_write_IV = michael@0: ** MD5(ServerHello.random + ClientHello.random); michael@0: */ michael@0: MD5_Begin(md5); michael@0: MD5_Update(md5, srcrdata, sizeof srcrdata); michael@0: MD5_End(md5, key_block2, &outLen, MD5_LENGTH); michael@0: PORT_Memcpy(ssl3_keys_out->pIVServer, key_block2, IVSize); michael@0: michael@0: } else { michael@0: michael@0: /* michael@0: ** Generate TLS 1.0 Export write keys and IVs. michael@0: */ michael@0: SECStatus status; michael@0: SECItem secret = { siBuffer, NULL, 0 }; michael@0: SECItem crsr = { siBuffer, NULL, 0 }; michael@0: SECItem keyblk = { siBuffer, NULL, 0 }; michael@0: michael@0: /* michael@0: ** client_write_key[CipherSpec.key_material] michael@0: ** final_client_write_key = PRF(client_write_key, michael@0: ** "client write key", michael@0: ** client_random + server_random); michael@0: */ michael@0: secret.data = &key_block[i]; michael@0: secret.len = effKeySize; michael@0: i += effKeySize; michael@0: crsr.data = crsrdata; michael@0: crsr.len = sizeof crsrdata; michael@0: keyblk.data = key_block2; michael@0: keyblk.len = sizeof key_block2; michael@0: status = TLS_PRF(&secret, "client write key", &crsr, &keyblk, michael@0: isFIPS); michael@0: if (status != SECSuccess) { michael@0: goto key_and_mac_derive_fail; michael@0: } michael@0: crv = sftk_buildSSLKey(hSession, key, PR_FALSE, key_block2, michael@0: keySize, &ssl3_keys_out->hClientKey); michael@0: if (crv != CKR_OK) { michael@0: goto key_and_mac_derive_fail; michael@0: } michael@0: michael@0: /* michael@0: ** server_write_key[CipherSpec.key_material] michael@0: ** final_server_write_key = PRF(server_write_key, michael@0: ** "server write key", michael@0: ** client_random + server_random); michael@0: */ michael@0: secret.data = &key_block[i]; michael@0: secret.len = effKeySize; michael@0: i += effKeySize; michael@0: keyblk.data = key_block2; michael@0: keyblk.len = sizeof key_block2; michael@0: status = TLS_PRF(&secret, "server write key", &crsr, &keyblk, michael@0: isFIPS); michael@0: if (status != SECSuccess) { michael@0: goto key_and_mac_derive_fail; michael@0: } michael@0: crv = sftk_buildSSLKey(hSession, key, PR_FALSE, key_block2, michael@0: keySize, &ssl3_keys_out->hServerKey); michael@0: if (crv != CKR_OK) { michael@0: goto key_and_mac_derive_fail; michael@0: } michael@0: michael@0: /* michael@0: ** iv_block = PRF("", "IV block", michael@0: ** client_random + server_random); michael@0: ** client_write_IV[SecurityParameters.IV_size] michael@0: ** server_write_IV[SecurityParameters.IV_size] michael@0: */ michael@0: if (IVSize) { michael@0: secret.data = NULL; michael@0: secret.len = 0; michael@0: keyblk.data = &key_block[i]; michael@0: keyblk.len = 2 * IVSize; michael@0: status = TLS_PRF(&secret, "IV block", &crsr, &keyblk, michael@0: isFIPS); michael@0: if (status != SECSuccess) { michael@0: goto key_and_mac_derive_fail; michael@0: } michael@0: PORT_Memcpy(ssl3_keys_out->pIVClient, keyblk.data, IVSize); michael@0: PORT_Memcpy(ssl3_keys_out->pIVServer, keyblk.data + IVSize, michael@0: IVSize); michael@0: } michael@0: } michael@0: } michael@0: michael@0: crv = CKR_OK; michael@0: michael@0: if (0) { michael@0: key_and_mac_derive_fail: michael@0: if (crv == CKR_OK) michael@0: crv = CKR_FUNCTION_FAILED; michael@0: sftk_freeSSLKeys(hSession, ssl3_keys_out); michael@0: } michael@0: MD5_DestroyContext(md5, PR_TRUE); michael@0: SHA1_DestroyContext(sha, PR_TRUE); michael@0: sftk_FreeObject(key); michael@0: key = NULL; michael@0: break; michael@0: } michael@0: michael@0: case CKM_CONCATENATE_BASE_AND_KEY: michael@0: { michael@0: SFTKObject *newKey; michael@0: michael@0: crv = sftk_DeriveSensitiveCheck(sourceKey,key); michael@0: if (crv != CKR_OK) break; michael@0: michael@0: session = sftk_SessionFromHandle(hSession); michael@0: if (session == NULL) { michael@0: crv = CKR_SESSION_HANDLE_INVALID; michael@0: break; michael@0: } michael@0: michael@0: newKey = sftk_ObjectFromHandle(*(CK_OBJECT_HANDLE *) michael@0: pMechanism->pParameter,session); michael@0: sftk_FreeSession(session); michael@0: if ( newKey == NULL) { michael@0: crv = CKR_KEY_HANDLE_INVALID; michael@0: break; michael@0: } michael@0: michael@0: if (sftk_isTrue(newKey,CKA_SENSITIVE)) { michael@0: crv = sftk_forceAttribute(newKey,CKA_SENSITIVE,&cktrue, michael@0: sizeof(CK_BBOOL)); michael@0: if (crv != CKR_OK) { michael@0: sftk_FreeObject(newKey); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: att2 = sftk_FindAttribute(newKey,CKA_VALUE); michael@0: if (att2 == NULL) { michael@0: sftk_FreeObject(newKey); michael@0: crv = CKR_KEY_HANDLE_INVALID; michael@0: break; michael@0: } michael@0: tmpKeySize = att->attrib.ulValueLen+att2->attrib.ulValueLen; michael@0: if (keySize == 0) keySize = tmpKeySize; michael@0: if (keySize > tmpKeySize) { michael@0: sftk_FreeObject(newKey); michael@0: sftk_FreeAttribute(att2); michael@0: crv = CKR_TEMPLATE_INCONSISTENT; michael@0: break; michael@0: } michael@0: buf = (unsigned char*)PORT_Alloc(tmpKeySize); michael@0: if (buf == NULL) { michael@0: sftk_FreeAttribute(att2); michael@0: sftk_FreeObject(newKey); michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: michael@0: PORT_Memcpy(buf,att->attrib.pValue,att->attrib.ulValueLen); michael@0: PORT_Memcpy(buf+att->attrib.ulValueLen, michael@0: att2->attrib.pValue,att2->attrib.ulValueLen); michael@0: michael@0: crv = sftk_forceAttribute (key,CKA_VALUE,buf,keySize); michael@0: PORT_ZFree(buf,tmpKeySize); michael@0: sftk_FreeAttribute(att2); michael@0: sftk_FreeObject(newKey); michael@0: break; michael@0: } michael@0: michael@0: case CKM_CONCATENATE_BASE_AND_DATA: michael@0: crv = sftk_DeriveSensitiveCheck(sourceKey,key); michael@0: if (crv != CKR_OK) break; michael@0: michael@0: stringPtr = (CK_KEY_DERIVATION_STRING_DATA *) pMechanism->pParameter; michael@0: tmpKeySize = att->attrib.ulValueLen+stringPtr->ulLen; michael@0: if (keySize == 0) keySize = tmpKeySize; michael@0: if (keySize > tmpKeySize) { michael@0: crv = CKR_TEMPLATE_INCONSISTENT; michael@0: break; michael@0: } michael@0: buf = (unsigned char*)PORT_Alloc(tmpKeySize); michael@0: if (buf == NULL) { michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: michael@0: PORT_Memcpy(buf,att->attrib.pValue,att->attrib.ulValueLen); michael@0: PORT_Memcpy(buf+att->attrib.ulValueLen,stringPtr->pData, michael@0: stringPtr->ulLen); michael@0: michael@0: crv = sftk_forceAttribute (key,CKA_VALUE,buf,keySize); michael@0: PORT_ZFree(buf,tmpKeySize); michael@0: break; michael@0: case CKM_CONCATENATE_DATA_AND_BASE: michael@0: crv = sftk_DeriveSensitiveCheck(sourceKey,key); michael@0: if (crv != CKR_OK) break; michael@0: michael@0: stringPtr = (CK_KEY_DERIVATION_STRING_DATA *)pMechanism->pParameter; michael@0: tmpKeySize = att->attrib.ulValueLen+stringPtr->ulLen; michael@0: if (keySize == 0) keySize = tmpKeySize; michael@0: if (keySize > tmpKeySize) { michael@0: crv = CKR_TEMPLATE_INCONSISTENT; michael@0: break; michael@0: } michael@0: buf = (unsigned char*)PORT_Alloc(tmpKeySize); michael@0: if (buf == NULL) { michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: michael@0: PORT_Memcpy(buf,stringPtr->pData,stringPtr->ulLen); michael@0: PORT_Memcpy(buf+stringPtr->ulLen,att->attrib.pValue, michael@0: att->attrib.ulValueLen); michael@0: michael@0: crv = sftk_forceAttribute (key,CKA_VALUE,buf,keySize); michael@0: PORT_ZFree(buf,tmpKeySize); michael@0: break; michael@0: case CKM_XOR_BASE_AND_DATA: michael@0: crv = sftk_DeriveSensitiveCheck(sourceKey,key); michael@0: if (crv != CKR_OK) break; michael@0: michael@0: stringPtr = (CK_KEY_DERIVATION_STRING_DATA *)pMechanism->pParameter; michael@0: tmpKeySize = PR_MIN(att->attrib.ulValueLen,stringPtr->ulLen); michael@0: if (keySize == 0) keySize = tmpKeySize; michael@0: if (keySize > tmpKeySize) { michael@0: crv = CKR_TEMPLATE_INCONSISTENT; michael@0: break; michael@0: } michael@0: buf = (unsigned char*)PORT_Alloc(keySize); michael@0: if (buf == NULL) { michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: michael@0: michael@0: PORT_Memcpy(buf,att->attrib.pValue,keySize); michael@0: for (i=0; i < (int)keySize; i++) { michael@0: buf[i] ^= stringPtr->pData[i]; michael@0: } michael@0: michael@0: crv = sftk_forceAttribute (key,CKA_VALUE,buf,keySize); michael@0: PORT_ZFree(buf,keySize); michael@0: break; michael@0: michael@0: case CKM_EXTRACT_KEY_FROM_KEY: michael@0: { michael@0: /* the following assumes 8 bits per byte */ michael@0: CK_ULONG extract = *(CK_EXTRACT_PARAMS *)pMechanism->pParameter; michael@0: CK_ULONG shift = extract & 0x7; /* extract mod 8 the fast way */ michael@0: CK_ULONG offset = extract >> 3; /* extract div 8 the fast way */ michael@0: michael@0: crv = sftk_DeriveSensitiveCheck(sourceKey,key); michael@0: if (crv != CKR_OK) break; michael@0: michael@0: if (keySize == 0) { michael@0: crv = CKR_TEMPLATE_INCOMPLETE; michael@0: break; michael@0: } michael@0: /* make sure we have enough bits in the original key */ michael@0: if (att->attrib.ulValueLen < michael@0: (offset + keySize + ((shift != 0)? 1 :0)) ) { michael@0: crv = CKR_MECHANISM_PARAM_INVALID; michael@0: break; michael@0: } michael@0: buf = (unsigned char*)PORT_Alloc(keySize); michael@0: if (buf == NULL) { michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: michael@0: /* copy the bits we need into the new key */ michael@0: for (i=0; i < (int)keySize; i++) { michael@0: unsigned char *value = michael@0: ((unsigned char *)att->attrib.pValue)+offset+i; michael@0: if (shift) { michael@0: buf[i] = (value[0] << (shift)) | (value[1] >> (8 - shift)); michael@0: } else { michael@0: buf[i] = value[0]; michael@0: } michael@0: } michael@0: michael@0: crv = sftk_forceAttribute (key,CKA_VALUE,buf,keySize); michael@0: PORT_ZFree(buf,keySize); michael@0: break; michael@0: } michael@0: case CKM_MD2_KEY_DERIVATION: michael@0: if (keySize == 0) keySize = MD2_LENGTH; michael@0: if (keySize > MD2_LENGTH) { michael@0: crv = CKR_TEMPLATE_INCONSISTENT; michael@0: break; michael@0: } michael@0: /* now allocate the hash contexts */ michael@0: md2 = MD2_NewContext(); michael@0: if (md2 == NULL) { michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: MD2_Begin(md2); michael@0: MD2_Update(md2,(const unsigned char*)att->attrib.pValue, michael@0: att->attrib.ulValueLen); michael@0: MD2_End(md2,key_block,&outLen,MD2_LENGTH); michael@0: MD2_DestroyContext(md2, PR_TRUE); michael@0: michael@0: crv = sftk_forceAttribute (key,CKA_VALUE,key_block,keySize); michael@0: break; michael@0: case CKM_MD5_KEY_DERIVATION: michael@0: if (keySize == 0) keySize = MD5_LENGTH; michael@0: if (keySize > MD5_LENGTH) { michael@0: crv = CKR_TEMPLATE_INCONSISTENT; michael@0: break; michael@0: } michael@0: MD5_HashBuf(key_block,(const unsigned char*)att->attrib.pValue, michael@0: att->attrib.ulValueLen); michael@0: michael@0: crv = sftk_forceAttribute (key,CKA_VALUE,key_block,keySize); michael@0: break; michael@0: case CKM_SHA1_KEY_DERIVATION: michael@0: if (keySize == 0) keySize = SHA1_LENGTH; michael@0: if (keySize > SHA1_LENGTH) { michael@0: crv = CKR_TEMPLATE_INCONSISTENT; michael@0: break; michael@0: } michael@0: SHA1_HashBuf(key_block,(const unsigned char*)att->attrib.pValue, michael@0: att->attrib.ulValueLen); michael@0: michael@0: crv = sftk_forceAttribute(key,CKA_VALUE,key_block,keySize); michael@0: break; michael@0: michael@0: case CKM_SHA224_KEY_DERIVATION: michael@0: if (keySize == 0) keySize = SHA224_LENGTH; michael@0: if (keySize > SHA224_LENGTH) { michael@0: crv = CKR_TEMPLATE_INCONSISTENT; michael@0: break; michael@0: } michael@0: SHA224_HashBuf(key_block,(const unsigned char*)att->attrib.pValue, michael@0: att->attrib.ulValueLen); michael@0: michael@0: crv = sftk_forceAttribute(key,CKA_VALUE,key_block,keySize); michael@0: break; michael@0: michael@0: case CKM_SHA256_KEY_DERIVATION: michael@0: if (keySize == 0) keySize = SHA256_LENGTH; michael@0: if (keySize > SHA256_LENGTH) { michael@0: crv = CKR_TEMPLATE_INCONSISTENT; michael@0: break; michael@0: } michael@0: SHA256_HashBuf(key_block,(const unsigned char*)att->attrib.pValue, michael@0: att->attrib.ulValueLen); michael@0: michael@0: crv = sftk_forceAttribute(key,CKA_VALUE,key_block,keySize); michael@0: break; michael@0: michael@0: case CKM_SHA384_KEY_DERIVATION: michael@0: if (keySize == 0) keySize = SHA384_LENGTH; michael@0: if (keySize > SHA384_LENGTH) { michael@0: crv = CKR_TEMPLATE_INCONSISTENT; michael@0: break; michael@0: } michael@0: SHA384_HashBuf(key_block,(const unsigned char*)att->attrib.pValue, michael@0: att->attrib.ulValueLen); michael@0: michael@0: crv = sftk_forceAttribute(key,CKA_VALUE,key_block,keySize); michael@0: break; michael@0: michael@0: case CKM_SHA512_KEY_DERIVATION: michael@0: if (keySize == 0) keySize = SHA512_LENGTH; michael@0: if (keySize > SHA512_LENGTH) { michael@0: crv = CKR_TEMPLATE_INCONSISTENT; michael@0: break; michael@0: } michael@0: SHA512_HashBuf(key_block,(const unsigned char*)att->attrib.pValue, michael@0: att->attrib.ulValueLen); michael@0: michael@0: crv = sftk_forceAttribute(key,CKA_VALUE,key_block,keySize); michael@0: break; michael@0: michael@0: case CKM_DH_PKCS_DERIVE: michael@0: { michael@0: SECItem derived, dhPublic; michael@0: SECItem dhPrime, dhValue; michael@0: /* sourceKey - values for the local existing low key */ michael@0: /* get prime and value attributes */ michael@0: crv = sftk_Attribute2SecItem(NULL, &dhPrime, sourceKey, CKA_PRIME); michael@0: if (crv != SECSuccess) break; michael@0: crv = sftk_Attribute2SecItem(NULL, &dhValue, sourceKey, CKA_VALUE); michael@0: if (crv != SECSuccess) { michael@0: PORT_Free(dhPrime.data); michael@0: break; michael@0: } michael@0: michael@0: dhPublic.data = pMechanism->pParameter; michael@0: dhPublic.len = pMechanism->ulParameterLen; michael@0: michael@0: /* calculate private value - oct */ michael@0: rv = DH_Derive(&dhPublic, &dhPrime, &dhValue, &derived, keySize); michael@0: michael@0: PORT_Free(dhPrime.data); michael@0: PORT_Free(dhValue.data); michael@0: michael@0: if (rv == SECSuccess) { michael@0: sftk_forceAttribute(key, CKA_VALUE, derived.data, derived.len); michael@0: PORT_ZFree(derived.data, derived.len); michael@0: } else michael@0: crv = CKR_HOST_MEMORY; michael@0: michael@0: break; michael@0: } michael@0: michael@0: #ifndef NSS_DISABLE_ECC michael@0: case CKM_ECDH1_DERIVE: michael@0: case CKM_ECDH1_COFACTOR_DERIVE: michael@0: { michael@0: SECItem ecScalar, ecPoint; michael@0: SECItem tmp; michael@0: PRBool withCofactor = PR_FALSE; michael@0: unsigned char *secret; michael@0: unsigned char *keyData = NULL; michael@0: int secretlen, curveLen, pubKeyLen; michael@0: CK_ECDH1_DERIVE_PARAMS *mechParams; michael@0: NSSLOWKEYPrivateKey *privKey; michael@0: PLArenaPool *arena = NULL; michael@0: michael@0: /* Check mechanism parameters */ michael@0: mechParams = (CK_ECDH1_DERIVE_PARAMS *) pMechanism->pParameter; michael@0: if ((pMechanism->ulParameterLen != sizeof(CK_ECDH1_DERIVE_PARAMS)) || michael@0: ((mechParams->kdf == CKD_NULL) && michael@0: ((mechParams->ulSharedDataLen != 0) || michael@0: (mechParams->pSharedData != NULL)))) { michael@0: crv = CKR_MECHANISM_PARAM_INVALID; michael@0: break; michael@0: } michael@0: michael@0: privKey = sftk_GetPrivKey(sourceKey, CKK_EC, &crv); michael@0: if (privKey == NULL) { michael@0: break; michael@0: } michael@0: michael@0: /* Now we are working with a non-NULL private key */ michael@0: SECITEM_CopyItem(NULL, &ecScalar, &privKey->u.ec.privateValue); michael@0: michael@0: ecPoint.data = mechParams->pPublicData; michael@0: ecPoint.len = mechParams->ulPublicDataLen; michael@0: michael@0: curveLen = (privKey->u.ec.ecParams.fieldID.size +7)/8; michael@0: pubKeyLen = (2*curveLen) + 1; michael@0: michael@0: /* if the len is too small, can't be a valid point */ michael@0: if (ecPoint.len < pubKeyLen) { michael@0: goto ec_loser; michael@0: } michael@0: /* if the len is too large, must be an encoded point (length is michael@0: * equal case just falls through */ michael@0: if (ecPoint.len > pubKeyLen) { michael@0: SECItem newPoint; michael@0: michael@0: arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); michael@0: if (arena == NULL) { michael@0: goto ec_loser; michael@0: } michael@0: michael@0: rv = SEC_QuickDERDecodeItem(arena, &newPoint, michael@0: SEC_ASN1_GET(SEC_OctetStringTemplate), michael@0: &ecPoint); michael@0: if (rv != SECSuccess) { michael@0: goto ec_loser; michael@0: } michael@0: ecPoint = newPoint; michael@0: } michael@0: michael@0: if (pMechanism->mechanism == CKM_ECDH1_COFACTOR_DERIVE) { michael@0: withCofactor = PR_TRUE; michael@0: } else { michael@0: /* When not using cofactor derivation, one should michael@0: * validate the public key to avoid small subgroup michael@0: * attacks. michael@0: */ michael@0: if (EC_ValidatePublicKey(&privKey->u.ec.ecParams, &ecPoint) michael@0: != SECSuccess) { michael@0: goto ec_loser; michael@0: } michael@0: } michael@0: michael@0: rv = ECDH_Derive(&ecPoint, &privKey->u.ec.ecParams, &ecScalar, michael@0: withCofactor, &tmp); michael@0: PORT_Free(ecScalar.data); michael@0: ecScalar.data = NULL; michael@0: if (privKey != sourceKey->objectInfo) { michael@0: nsslowkey_DestroyPrivateKey(privKey); michael@0: privKey=NULL; michael@0: } michael@0: if (arena) { michael@0: PORT_FreeArena(arena,PR_FALSE); michael@0: arena=NULL; michael@0: } michael@0: michael@0: if (rv != SECSuccess) { michael@0: crv = sftk_MapCryptError(PORT_GetError()); michael@0: break; michael@0: } michael@0: michael@0: michael@0: /* michael@0: * apply the kdf function. michael@0: */ michael@0: if (mechParams->kdf == CKD_NULL) { michael@0: /* michael@0: * tmp is the raw data created by ECDH_Derive, michael@0: * secret and secretlen are the values we will michael@0: * eventually pass as our generated key. michael@0: */ michael@0: secret = tmp.data; michael@0: secretlen = tmp.len; michael@0: } else { michael@0: secretlen = keySize; michael@0: crv = sftk_ANSI_X9_63_kdf(&secret, keySize, michael@0: &tmp, mechParams->pSharedData, michael@0: mechParams->ulSharedDataLen, mechParams->kdf); michael@0: PORT_ZFree(tmp.data, tmp.len); michael@0: if (crv != CKR_OK) { michael@0: break; michael@0: } michael@0: tmp.data = secret; michael@0: tmp.len = secretlen; michael@0: } michael@0: michael@0: /* michael@0: * if keySize is supplied, then we are generating a key of a specific michael@0: * length. This is done by taking the least significant 'keySize' michael@0: * bytes from the unsigned value calculated by ECDH. Note: this may michael@0: * mean padding temp with extra leading zeros from what ECDH_Derive michael@0: * already returned (which itself may contain leading zeros). michael@0: */ michael@0: if (keySize) { michael@0: if (secretlen < keySize) { michael@0: keyData = PORT_ZAlloc(keySize); michael@0: if (!keyData) { michael@0: PORT_ZFree(tmp.data, tmp.len); michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: PORT_Memcpy(&keyData[keySize-secretlen],secret,secretlen); michael@0: secret = keyData; michael@0: } else { michael@0: secret += (secretlen - keySize); michael@0: } michael@0: secretlen = keySize; michael@0: } michael@0: michael@0: sftk_forceAttribute(key, CKA_VALUE, secret, secretlen); michael@0: PORT_ZFree(tmp.data, tmp.len); michael@0: if (keyData) { michael@0: PORT_ZFree(keyData, keySize); michael@0: } michael@0: break; michael@0: michael@0: ec_loser: michael@0: crv = CKR_ARGUMENTS_BAD; michael@0: PORT_Free(ecScalar.data); michael@0: if (privKey != sourceKey->objectInfo) michael@0: nsslowkey_DestroyPrivateKey(privKey); michael@0: if (arena) { michael@0: PORT_FreeArena(arena, PR_FALSE); michael@0: } michael@0: break; michael@0: michael@0: } michael@0: #endif /* NSS_DISABLE_ECC */ michael@0: michael@0: /* See RFC 5869 and CK_NSS_HKDFParams for documentation. */ michael@0: case CKM_NSS_HKDF_SHA1: hashType = HASH_AlgSHA1; goto hkdf; michael@0: case CKM_NSS_HKDF_SHA256: hashType = HASH_AlgSHA256; goto hkdf; michael@0: case CKM_NSS_HKDF_SHA384: hashType = HASH_AlgSHA384; goto hkdf; michael@0: case CKM_NSS_HKDF_SHA512: hashType = HASH_AlgSHA512; goto hkdf; michael@0: hkdf: { michael@0: const CK_NSS_HKDFParams * params = michael@0: (const CK_NSS_HKDFParams *) pMechanism->pParameter; michael@0: const SECHashObject * rawHash; michael@0: unsigned hashLen; michael@0: CK_BYTE buf[HASH_LENGTH_MAX]; michael@0: CK_BYTE * prk; /* psuedo-random key */ michael@0: CK_ULONG prkLen; michael@0: CK_BYTE * okm; /* output keying material */ michael@0: michael@0: rawHash = HASH_GetRawHashObject(hashType); michael@0: if (rawHash == NULL || rawHash->length > sizeof buf) { michael@0: crv = CKR_FUNCTION_FAILED; michael@0: break; michael@0: } michael@0: hashLen = rawHash->length; michael@0: michael@0: if (pMechanism->ulParameterLen != sizeof(CK_NSS_HKDFParams) || michael@0: !params || (!params->bExpand && !params->bExtract) || michael@0: (params->bExtract && params->ulSaltLen > 0 && !params->pSalt) || michael@0: (params->bExpand && params->ulInfoLen > 0 && !params->pInfo)) { michael@0: crv = CKR_MECHANISM_PARAM_INVALID; michael@0: break; michael@0: } michael@0: if (keySize == 0 || keySize > sizeof key_block || michael@0: (!params->bExpand && keySize > hashLen) || michael@0: (params->bExpand && keySize > 255 * hashLen)) { michael@0: crv = CKR_TEMPLATE_INCONSISTENT; michael@0: break; michael@0: } michael@0: crv = sftk_DeriveSensitiveCheck(sourceKey, key); michael@0: if (crv != CKR_OK) michael@0: break; michael@0: michael@0: /* HKDF-Extract(salt, base key value) */ michael@0: if (params->bExtract) { michael@0: CK_BYTE * salt; michael@0: CK_ULONG saltLen; michael@0: HMACContext * hmac; michael@0: unsigned int bufLen; michael@0: michael@0: salt = params->pSalt; michael@0: saltLen = params->ulSaltLen; michael@0: if (salt == NULL) { michael@0: saltLen = hashLen; michael@0: salt = buf; michael@0: memset(salt, 0, saltLen); michael@0: } michael@0: hmac = HMAC_Create(rawHash, salt, saltLen, isFIPS); michael@0: if (!hmac) { michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: HMAC_Begin(hmac); michael@0: HMAC_Update(hmac, (const unsigned char*) att->attrib.pValue, michael@0: att->attrib.ulValueLen); michael@0: HMAC_Finish(hmac, buf, &bufLen, sizeof(buf)); michael@0: HMAC_Destroy(hmac, PR_TRUE); michael@0: PORT_Assert(bufLen == rawHash->length); michael@0: prk = buf; michael@0: prkLen = bufLen; michael@0: } else { michael@0: /* PRK = base key value */ michael@0: prk = (CK_BYTE*) att->attrib.pValue; michael@0: prkLen = att->attrib.ulValueLen; michael@0: } michael@0: michael@0: /* HKDF-Expand */ michael@0: if (!params->bExpand) { michael@0: okm = prk; michael@0: } else { michael@0: /* T(1) = HMAC-Hash(prk, "" | info | 0x01) michael@0: * T(n) = HMAC-Hash(prk, T(n-1) | info | n michael@0: * key material = T(1) | ... | T(n) michael@0: */ michael@0: HMACContext * hmac; michael@0: CK_BYTE i; michael@0: unsigned iterations = PR_ROUNDUP(keySize, hashLen) / hashLen; michael@0: hmac = HMAC_Create(rawHash, prk, prkLen, isFIPS); michael@0: if (hmac == NULL) { michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: for (i = 1; i <= iterations; ++i) { michael@0: unsigned len; michael@0: HMAC_Begin(hmac); michael@0: if (i > 1) { michael@0: HMAC_Update(hmac, key_block + ((i-2) * hashLen), hashLen); michael@0: } michael@0: if (params->ulInfoLen != 0) { michael@0: HMAC_Update(hmac, params->pInfo, params->ulInfoLen); michael@0: } michael@0: HMAC_Update(hmac, &i, 1); michael@0: HMAC_Finish(hmac, key_block + ((i-1) * hashLen), &len, michael@0: hashLen); michael@0: PORT_Assert(len == hashLen); michael@0: } michael@0: HMAC_Destroy(hmac, PR_TRUE); michael@0: okm = key_block; michael@0: } michael@0: /* key material = prk */ michael@0: crv = sftk_forceAttribute(key, CKA_VALUE, okm, keySize); michael@0: break; michael@0: } /* end of CKM_NSS_HKDF_* */ michael@0: michael@0: case CKM_NSS_JPAKE_ROUND2_SHA1: hashType = HASH_AlgSHA1; goto jpake2; michael@0: case CKM_NSS_JPAKE_ROUND2_SHA256: hashType = HASH_AlgSHA256; goto jpake2; michael@0: case CKM_NSS_JPAKE_ROUND2_SHA384: hashType = HASH_AlgSHA384; goto jpake2; michael@0: case CKM_NSS_JPAKE_ROUND2_SHA512: hashType = HASH_AlgSHA512; goto jpake2; michael@0: jpake2: michael@0: if (pMechanism->pParameter == NULL || michael@0: pMechanism->ulParameterLen != sizeof(CK_NSS_JPAKERound2Params)) michael@0: crv = CKR_MECHANISM_PARAM_INVALID; michael@0: if (crv == CKR_OK && sftk_isTrue(key, CKA_TOKEN)) michael@0: crv = CKR_TEMPLATE_INCONSISTENT; michael@0: if (crv == CKR_OK) michael@0: crv = sftk_DeriveSensitiveCheck(sourceKey, key); michael@0: if (crv == CKR_OK) michael@0: crv = jpake_Round2(hashType, michael@0: (CK_NSS_JPAKERound2Params *) pMechanism->pParameter, michael@0: sourceKey, key); michael@0: break; michael@0: michael@0: case CKM_NSS_JPAKE_FINAL_SHA1: hashType = HASH_AlgSHA1; goto jpakeFinal; michael@0: case CKM_NSS_JPAKE_FINAL_SHA256: hashType = HASH_AlgSHA256; goto jpakeFinal; michael@0: case CKM_NSS_JPAKE_FINAL_SHA384: hashType = HASH_AlgSHA384; goto jpakeFinal; michael@0: case CKM_NSS_JPAKE_FINAL_SHA512: hashType = HASH_AlgSHA512; goto jpakeFinal; michael@0: jpakeFinal: michael@0: if (pMechanism->pParameter == NULL || michael@0: pMechanism->ulParameterLen != sizeof(CK_NSS_JPAKEFinalParams)) michael@0: crv = CKR_MECHANISM_PARAM_INVALID; michael@0: /* We purposely do not do the derive sensitivity check; we want to be michael@0: able to derive non-sensitive keys while allowing the ROUND1 and michael@0: ROUND2 keys to be sensitive (which they always are, since they are michael@0: in the CKO_PRIVATE_KEY class). The caller must include CKA_SENSITIVE michael@0: in the template in order for the resultant keyblock key to be michael@0: sensitive. michael@0: */ michael@0: if (crv == CKR_OK) michael@0: crv = jpake_Final(hashType, michael@0: (CK_NSS_JPAKEFinalParams *) pMechanism->pParameter, michael@0: sourceKey, key); michael@0: break; michael@0: michael@0: default: michael@0: crv = CKR_MECHANISM_INVALID; michael@0: } michael@0: if (att) { michael@0: sftk_FreeAttribute(att); michael@0: } michael@0: sftk_FreeObject(sourceKey); michael@0: if (crv != CKR_OK) { michael@0: if (key) sftk_FreeObject(key); michael@0: return crv; michael@0: } michael@0: michael@0: /* link the key object into the list */ michael@0: if (key) { michael@0: SFTKSessionObject *sessKey = sftk_narrowToSessionObject(key); michael@0: PORT_Assert(sessKey); michael@0: /* get the session */ michael@0: sessKey->wasDerived = PR_TRUE; michael@0: session = sftk_SessionFromHandle(hSession); michael@0: if (session == NULL) { michael@0: sftk_FreeObject(key); michael@0: return CKR_HOST_MEMORY; michael@0: } michael@0: michael@0: crv = sftk_handleObject(key,session); michael@0: sftk_FreeSession(session); michael@0: *phKey = key->handle; michael@0: sftk_FreeObject(key); michael@0: } michael@0: return crv; michael@0: } michael@0: michael@0: michael@0: /* NSC_GetFunctionStatus obtains an updated status of a function running michael@0: * in parallel with an application. */ michael@0: CK_RV NSC_GetFunctionStatus(CK_SESSION_HANDLE hSession) michael@0: { michael@0: CHECK_FORK(); michael@0: michael@0: return CKR_FUNCTION_NOT_PARALLEL; michael@0: } michael@0: michael@0: /* NSC_CancelFunction cancels a function running in parallel */ michael@0: CK_RV NSC_CancelFunction(CK_SESSION_HANDLE hSession) michael@0: { michael@0: CHECK_FORK(); michael@0: michael@0: return CKR_FUNCTION_NOT_PARALLEL; michael@0: } michael@0: michael@0: /* NSC_GetOperationState saves the state of the cryptographic michael@0: *operation in a session. michael@0: * NOTE: This code only works for digest functions for now. eventually need michael@0: * to add full flatten/resurect to our state stuff so that all types of state michael@0: * can be saved */ michael@0: CK_RV NSC_GetOperationState(CK_SESSION_HANDLE hSession, michael@0: CK_BYTE_PTR pOperationState, CK_ULONG_PTR pulOperationStateLen) michael@0: { michael@0: SFTKSessionContext *context; michael@0: SFTKSession *session; michael@0: CK_RV crv; michael@0: CK_ULONG pOSLen = *pulOperationStateLen; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: /* make sure we're legal */ michael@0: crv = sftk_GetContext(hSession, &context, SFTK_HASH, PR_TRUE, &session); michael@0: if (crv != CKR_OK) return crv; michael@0: michael@0: *pulOperationStateLen = context->cipherInfoLen + sizeof(CK_MECHANISM_TYPE) michael@0: + sizeof(SFTKContextType); michael@0: if (pOperationState == NULL) { michael@0: sftk_FreeSession(session); michael@0: return CKR_OK; michael@0: } else { michael@0: if (pOSLen < *pulOperationStateLen) { michael@0: return CKR_BUFFER_TOO_SMALL; michael@0: } michael@0: } michael@0: PORT_Memcpy(pOperationState,&context->type,sizeof(SFTKContextType)); michael@0: pOperationState += sizeof(SFTKContextType); michael@0: PORT_Memcpy(pOperationState,&context->currentMech, michael@0: sizeof(CK_MECHANISM_TYPE)); michael@0: pOperationState += sizeof(CK_MECHANISM_TYPE); michael@0: PORT_Memcpy(pOperationState,context->cipherInfo,context->cipherInfoLen); michael@0: sftk_FreeSession(session); michael@0: return CKR_OK; michael@0: } michael@0: michael@0: michael@0: #define sftk_Decrement(stateSize,len) \ michael@0: stateSize = ((stateSize) > (CK_ULONG)(len)) ? \ michael@0: ((stateSize) - (CK_ULONG)(len)) : 0; michael@0: michael@0: /* NSC_SetOperationState restores the state of the cryptographic michael@0: * operation in a session. This is coded like it can restore lots of michael@0: * states, but it only works for truly flat cipher structures. */ michael@0: CK_RV NSC_SetOperationState(CK_SESSION_HANDLE hSession, michael@0: CK_BYTE_PTR pOperationState, CK_ULONG ulOperationStateLen, michael@0: CK_OBJECT_HANDLE hEncryptionKey, CK_OBJECT_HANDLE hAuthenticationKey) michael@0: { michael@0: SFTKSessionContext *context; michael@0: SFTKSession *session; michael@0: SFTKContextType type; michael@0: CK_MECHANISM mech; michael@0: CK_RV crv = CKR_OK; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: while (ulOperationStateLen != 0) { michael@0: /* get what type of state we're dealing with... */ michael@0: PORT_Memcpy(&type,pOperationState, sizeof(SFTKContextType)); michael@0: michael@0: /* fix up session contexts based on type */ michael@0: session = sftk_SessionFromHandle(hSession); michael@0: if (session == NULL) return CKR_SESSION_HANDLE_INVALID; michael@0: context = sftk_ReturnContextByType(session, type); michael@0: sftk_SetContextByType(session, type, NULL); michael@0: if (context) { michael@0: sftk_FreeContext(context); michael@0: } michael@0: pOperationState += sizeof(SFTKContextType); michael@0: sftk_Decrement(ulOperationStateLen,sizeof(SFTKContextType)); michael@0: michael@0: michael@0: /* get the mechanism structure */ michael@0: PORT_Memcpy(&mech.mechanism,pOperationState,sizeof(CK_MECHANISM_TYPE)); michael@0: pOperationState += sizeof(CK_MECHANISM_TYPE); michael@0: sftk_Decrement(ulOperationStateLen, sizeof(CK_MECHANISM_TYPE)); michael@0: /* should be filled in... but not necessary for hash */ michael@0: mech.pParameter = NULL; michael@0: mech.ulParameterLen = 0; michael@0: switch (type) { michael@0: case SFTK_HASH: michael@0: crv = NSC_DigestInit(hSession,&mech); michael@0: if (crv != CKR_OK) break; michael@0: crv = sftk_GetContext(hSession, &context, SFTK_HASH, PR_TRUE, michael@0: NULL); michael@0: if (crv != CKR_OK) break; michael@0: PORT_Memcpy(context->cipherInfo,pOperationState, michael@0: context->cipherInfoLen); michael@0: pOperationState += context->cipherInfoLen; michael@0: sftk_Decrement(ulOperationStateLen,context->cipherInfoLen); michael@0: break; michael@0: default: michael@0: /* do sign/encrypt/decrypt later */ michael@0: crv = CKR_SAVED_STATE_INVALID; michael@0: } michael@0: sftk_FreeSession(session); michael@0: if (crv != CKR_OK) break; michael@0: } michael@0: return crv; michael@0: } michael@0: michael@0: /* Dual-function cryptographic operations */ michael@0: michael@0: /* NSC_DigestEncryptUpdate continues a multiple-part digesting and encryption michael@0: * operation. */ michael@0: CK_RV NSC_DigestEncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, michael@0: CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, michael@0: CK_ULONG_PTR pulEncryptedPartLen) michael@0: { michael@0: CK_RV crv; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: crv = NSC_EncryptUpdate(hSession,pPart,ulPartLen, pEncryptedPart, michael@0: pulEncryptedPartLen); michael@0: if (crv != CKR_OK) return crv; michael@0: crv = NSC_DigestUpdate(hSession,pPart,ulPartLen); michael@0: michael@0: return crv; michael@0: } michael@0: michael@0: michael@0: /* NSC_DecryptDigestUpdate continues a multiple-part decryption and michael@0: * digesting operation. */ michael@0: CK_RV NSC_DecryptDigestUpdate(CK_SESSION_HANDLE hSession, michael@0: CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, michael@0: CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) michael@0: { michael@0: CK_RV crv; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: crv = NSC_DecryptUpdate(hSession,pEncryptedPart, ulEncryptedPartLen, michael@0: pPart, pulPartLen); michael@0: if (crv != CKR_OK) return crv; michael@0: crv = NSC_DigestUpdate(hSession,pPart,*pulPartLen); michael@0: michael@0: return crv; michael@0: } michael@0: michael@0: michael@0: /* NSC_SignEncryptUpdate continues a multiple-part signing and michael@0: * encryption operation. */ michael@0: CK_RV NSC_SignEncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, michael@0: CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, michael@0: CK_ULONG_PTR pulEncryptedPartLen) michael@0: { michael@0: CK_RV crv; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: crv = NSC_EncryptUpdate(hSession,pPart,ulPartLen, pEncryptedPart, michael@0: pulEncryptedPartLen); michael@0: if (crv != CKR_OK) return crv; michael@0: crv = NSC_SignUpdate(hSession,pPart,ulPartLen); michael@0: michael@0: return crv; michael@0: } michael@0: michael@0: michael@0: /* NSC_DecryptVerifyUpdate continues a multiple-part decryption michael@0: * and verify operation. */ michael@0: CK_RV NSC_DecryptVerifyUpdate(CK_SESSION_HANDLE hSession, michael@0: CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, michael@0: CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) michael@0: { michael@0: CK_RV crv; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: crv = NSC_DecryptUpdate(hSession,pEncryptedData, ulEncryptedDataLen, michael@0: pData, pulDataLen); michael@0: if (crv != CKR_OK) return crv; michael@0: crv = NSC_VerifyUpdate(hSession, pData, *pulDataLen); michael@0: michael@0: return crv; michael@0: } michael@0: michael@0: /* NSC_DigestKey continues a multi-part message-digesting operation, michael@0: * by digesting the value of a secret key as part of the data already digested. michael@0: */ michael@0: CK_RV NSC_DigestKey(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hKey) michael@0: { michael@0: SFTKSession *session = NULL; michael@0: SFTKObject *key = NULL; michael@0: SFTKAttribute *att; michael@0: CK_RV crv; michael@0: michael@0: CHECK_FORK(); michael@0: michael@0: session = sftk_SessionFromHandle(hSession); michael@0: if (session == NULL) return CKR_SESSION_HANDLE_INVALID; michael@0: michael@0: key = sftk_ObjectFromHandle(hKey,session); michael@0: sftk_FreeSession(session); michael@0: if (key == NULL) return CKR_KEY_HANDLE_INVALID; michael@0: michael@0: /* PUT ANY DIGEST KEY RESTRICTION CHECKS HERE */ michael@0: michael@0: /* make sure it's a valid key for this operation */ michael@0: if (key->objclass != CKO_SECRET_KEY) { michael@0: sftk_FreeObject(key); michael@0: return CKR_KEY_TYPE_INCONSISTENT; michael@0: } michael@0: /* get the key value */ michael@0: att = sftk_FindAttribute(key,CKA_VALUE); michael@0: sftk_FreeObject(key); michael@0: if (!att) { michael@0: return CKR_KEY_HANDLE_INVALID; michael@0: } michael@0: crv = NSC_DigestUpdate(hSession,(CK_BYTE_PTR)att->attrib.pValue, michael@0: att->attrib.ulValueLen); michael@0: sftk_FreeAttribute(att); michael@0: return crv; michael@0: }