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