diff options
Diffstat (limited to 'security/nss/lib/softoken/pkcs11c.c')
-rw-r--r-- | security/nss/lib/softoken/pkcs11c.c | 5349 |
1 files changed, 5349 insertions, 0 deletions
diff --git a/security/nss/lib/softoken/pkcs11c.c b/security/nss/lib/softoken/pkcs11c.c new file mode 100644 index 000000000..ad598cb95 --- /dev/null +++ b/security/nss/lib/softoken/pkcs11c.c @@ -0,0 +1,5349 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1994-2000 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): + * Dr Stephen Henson <stephen.henson@gemplus.com> + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ +/* + * This file implements PKCS 11 on top of our existing security modules + * + * For more information about PKCS 11 See PKCS 11 Token Inteface Standard. + * This implementation has two slots: + * slot 1 is our generic crypto support. It does not require login. + * It supports Public Key ops, and all they bulk ciphers and hashes. + * It can also support Private Key ops for imported Private keys. It does + * not have any token storage. + * slot 2 is our private key support. It requires a login before use. It + * can store Private Keys and Certs as token objects. Currently only private + * keys and their associated Certificates are saved on the token. + * + * In this implementation, session objects are only visible to the session + * that created or generated them. + */ +#include "seccomon.h" +#include "secitem.h" +#include "secport.h" +#include "blapi.h" +#include "pkcs11.h" +#include "pkcs11i.h" +#include "lowkeyi.h" +#include "pcert.h" +#include "sechash.h" +#include "secder.h" +#include "secdig.h" +#include "lowpbe.h" /* We do PBE below */ +#include "pkcs11t.h" +#include "secoid.h" +#include "alghmac.h" +#include "softoken.h" +#include "secasn1.h" +/*#include "secmodi.h" */ + +#include "pcert.h" +#include "ssl3prot.h" /* for SSL3_RANDOM_LENGTH */ + +#define __PASTE(x,y) x##y + +/* + * we renamed all our internal functions, get the correct + * definitions for them... + */ +#undef CK_PKCS11_FUNCTION_INFO +#undef CK_NEED_ARG_LIST + +#define CK_EXTERN extern +#define CK_PKCS11_FUNCTION_INFO(func) \ + CK_RV __PASTE(NS,func) +#define CK_NEED_ARG_LIST 1 + +#include "pkcs11f.h" + + +/* forward static declaration. */ +static SECStatus pk11_PRF(const SECItem *secret, const char *label, + SECItem *seed, SECItem *result); + +#define PK11_OFFSETOF(str, memb) ((PRPtrdiff)(&(((str *)0)->memb))) + +static void pk11_Null(void *data, PRBool freeit) +{ + return; +} + +/* + * free routines.... Free local type allocated data, and convert + * other free routines to the destroy signature. + */ +static void +pk11_FreePrivKey(NSSLOWKEYPrivateKey *key, PRBool freeit) +{ + nsslowkey_DestroyPrivateKey(key); +} + +static void +pk11_HMAC_Destroy(HMACContext *context, PRBool freeit) +{ + HMAC_Destroy(context); +} + +static void +pk11_Space(void *data, PRBool freeit) +{ + PORT_Free(data); +} + + +/* + * turn a CDMF key into a des key. CDMF is an old IBM scheme to export DES by + * Deprecating a full des key to 40 bit key strenth. + */ +static CK_RV +pk11_cdmf2des(unsigned char *cdmfkey, unsigned char *deskey) +{ + unsigned char key1[8] = { 0xc4, 0x08, 0xb0, 0x54, 0x0b, 0xa1, 0xe0, 0xae }; + unsigned char key2[8] = { 0xef, 0x2c, 0x04, 0x1c, 0xe6, 0x38, 0x2f, 0xe6 }; + unsigned char enc_src[8]; + unsigned char enc_dest[8]; + unsigned int leng,i; + DESContext *descx; + SECStatus rv; + + + /* zero the parity bits */ + for (i=0; i < 8; i++) { + enc_src[i] = cdmfkey[i] & 0xfe; + } + + /* encrypt with key 1 */ + descx = DES_CreateContext(key1, NULL, NSS_DES, PR_TRUE); + if (descx == NULL) return CKR_HOST_MEMORY; + rv = DES_Encrypt(descx, enc_dest, &leng, 8, enc_src, 8); + DES_DestroyContext(descx,PR_TRUE); + if (rv != SECSuccess) return CKR_DEVICE_ERROR; + + /* xor source with des, zero the parity bits and depricate the key*/ + for (i=0; i < 8; i++) { + if (i & 1) { + enc_src[i] = (enc_src[i] ^ enc_dest[i]) & 0xfe; + } else { + enc_src[i] = (enc_src[i] ^ enc_dest[i]) & 0x0e; + } + } + + /* encrypt with key 2 */ + descx = DES_CreateContext(key2, NULL, NSS_DES, PR_TRUE); + if (descx == NULL) return CKR_HOST_MEMORY; + rv = DES_Encrypt(descx, deskey, &leng, 8, enc_src, 8); + DES_DestroyContext(descx,PR_TRUE); + if (rv != SECSuccess) return CKR_DEVICE_ERROR; + + /* set the corret parity on our new des key */ + pk11_FormatDESKey(deskey, 8); + return CKR_OK; +} + + +static CK_RV +pk11_CryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey, CK_ATTRIBUTE_TYPE etype, + PK11ContextType contextType, PRBool isEncrypt); +/* + * Calculate a Lynx checksum for CKM_LYNX_WRAP mechanism. + */ +static CK_RV +pk11_calcLynxChecksum(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hWrapKey, + unsigned char *checksum, unsigned char *key, CK_ULONG len) +{ + + CK_BYTE E[10]; + CK_ULONG Elen = sizeof (E); + CK_BYTE C[8]; + CK_ULONG Clen = sizeof (C); + unsigned short sum1 = 0, sum2 = 0; + CK_MECHANISM mech = { CKM_DES_ECB, NULL, 0 }; + int i; + CK_RV crv; + + if (len != 8) return CKR_WRAPPED_KEY_LEN_RANGE; + + /* zero the parity bits */ + for (i=0; i < 8; i++) { + sum1 = sum1 + key[i]; + sum2 = sum2 + sum1; + } + + /* encrypt with key 1 */ + + crv = pk11_CryptInit(hSession,&mech,hWrapKey,CKA_WRAP, PK11_ENCRYPT, + PR_TRUE); + if (crv != CKR_OK) return crv; + + crv = NSC_Encrypt(hSession,key,len,E,&Elen); + if (crv != CKR_OK) return crv; + + E[8] = (sum2 >> 8) & 0xff; + E[9] = sum2 & 0xff; + + crv = pk11_CryptInit(hSession,&mech,hWrapKey,CKA_WRAP, PK11_ENCRYPT, + PR_TRUE); + if (crv != CKR_OK) return crv; + + crv = NSC_Encrypt(hSession,&E[2],len,C,&Clen); + if (crv != CKR_OK) return crv; + + checksum[0] = C[6]; + checksum[1] = C[7]; + + return CKR_OK; +} +/* NSC_DestroyObject destroys an object. */ +CK_RV +NSC_DestroyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject) +{ + PK11Slot *slot = pk11_SlotFromSessionHandle(hSession); + PK11Session *session; + PK11Object *object; + PK11FreeStatus status; + + /* + * This whole block just makes sure we really can destroy the + * requested object. + */ + session = pk11_SessionFromHandle(hSession); + if (session == NULL) { + return CKR_SESSION_HANDLE_INVALID; + } + + object = pk11_ObjectFromHandle(hObject,session); + if (object == NULL) { + pk11_FreeSession(session); + return CKR_OBJECT_HANDLE_INVALID; + } + + /* don't destroy a private object if we aren't logged in */ + if ((!slot->isLoggedIn) && (slot->needLogin) && + (pk11_isTrue(object,CKA_PRIVATE))) { + pk11_FreeSession(session); + pk11_FreeObject(object); + return CKR_USER_NOT_LOGGED_IN; + } + + /* don't destroy a token object if we aren't in a rw session */ + + if (((session->info.flags & CKF_RW_SESSION) == 0) && + (pk11_isTrue(object,CKA_TOKEN))) { + pk11_FreeSession(session); + pk11_FreeObject(object); + return CKR_SESSION_READ_ONLY; + } + + pk11_DeleteObject(session,object); + + pk11_FreeSession(session); + + /* + * get some indication if the object is destroyed. Note: this is not + * 100%. Someone may have an object reference outstanding (though that + * should not be the case by here. Also note that the object is "half" + * destroyed. Our internal representation is destroyed, but it may still + * be in the data base. + */ + status = pk11_FreeObject(object); + + return (status != PK11_DestroyFailure) ? CKR_OK : CKR_DEVICE_ERROR; +} + + +/* + ************** Crypto Functions: Utilities ************************ + */ + + +/* + * return a context based on the PK11Context type. + */ +PK11SessionContext * +pk11_ReturnContextByType(PK11Session *session, PK11ContextType type) +{ + switch (type) { + case PK11_ENCRYPT: + case PK11_DECRYPT: + return session->enc_context; + case PK11_HASH: + return session->hash_context; + case PK11_SIGN: + case PK11_SIGN_RECOVER: + case PK11_VERIFY: + case PK11_VERIFY_RECOVER: + return session->hash_context; + } + return NULL; +} + +/* + * change a context based on the PK11Context type. + */ +void +pk11_SetContextByType(PK11Session *session, PK11ContextType type, + PK11SessionContext *context) +{ + switch (type) { + case PK11_ENCRYPT: + case PK11_DECRYPT: + session->enc_context = context; + break; + case PK11_HASH: + session->hash_context = context; + break; + case PK11_SIGN: + case PK11_SIGN_RECOVER: + case PK11_VERIFY: + case PK11_VERIFY_RECOVER: + session->hash_context = context; + break; + } + return; +} + +/* + * code to grab the context. Needed by every C_XXXUpdate, C_XXXFinal, + * and C_XXX function. The function takes a session handle, the context type, + * and wether or not the session needs to be multipart. It returns the context, + * and optionally returns the session pointer (if sessionPtr != NULL) if session + * pointer is returned, the caller is responsible for freeing it. + */ +static CK_RV +pk11_GetContext(CK_SESSION_HANDLE handle,PK11SessionContext **contextPtr, + PK11ContextType type, PRBool needMulti, PK11Session **sessionPtr) +{ + PK11Session *session; + PK11SessionContext *context; + + session = pk11_SessionFromHandle(handle); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + context = pk11_ReturnContextByType(session,type); + /* make sure the context is valid */ + if((context==NULL)||(context->type!=type)||(needMulti&&!(context->multi))){ + pk11_FreeSession(session); + return CKR_OPERATION_NOT_INITIALIZED; + } + *contextPtr = context; + if (sessionPtr != NULL) { + *sessionPtr = session; + } else { + pk11_FreeSession(session); + } + return CKR_OK; +} + +/* + ************** Crypto Functions: Encrypt ************************ + */ + +/* + * All the NSC_InitXXX functions have a set of common checks and processing they + * all need to do at the beginning. This is done here. + */ +static CK_RV +pk11_InitGeneric(PK11Session *session,PK11SessionContext **contextPtr, + PK11ContextType ctype,PK11Object **keyPtr, + CK_OBJECT_HANDLE hKey, CK_KEY_TYPE *keyTypePtr, + CK_OBJECT_CLASS pubKeyType, CK_ATTRIBUTE_TYPE operation) +{ + PK11Object *key = NULL; + PK11Attribute *att; + PK11SessionContext *context; + + /* We can only init if there is not current context active */ + if (pk11_ReturnContextByType(session,ctype) != NULL) { + return CKR_OPERATION_ACTIVE; + } + + /* find the key */ + if (keyPtr) { + unsigned int size; + key = pk11_ObjectFromHandle(hKey,session); + if (key == NULL) { + return CKR_KEY_HANDLE_INVALID; + } + + /* make sure it's a valid key for this operation */ + if (((key->objclass != CKO_SECRET_KEY) && (key->objclass != pubKeyType)) + || !pk11_isTrue(key,operation)) { + pk11_FreeObject(key); + return CKR_KEY_TYPE_INCONSISTENT; + } + /* get the key type */ + att = pk11_FindAttribute(key,CKA_KEY_TYPE); + PORT_Assert(att != NULL); + size = sizeof(CK_KEY_TYPE); + if (att->attrib.ulValueLen < size) { + CK_KEY_TYPE kt; + int i; + int shift; +#ifdef IS_LITTLE_ENDIAN + shift = 0; +#else + shift = (att->attrib.ulValueLen - 1) * 8; +#endif + kt = 0; + for (i=att->attrib.ulValueLen - 1; i>=0; i--) { + kt |= ((unsigned char *)att->attrib.pValue)[i] << shift; +#ifdef IS_LITTLE_ENDIAN + shift += 8; +#else + shift -= 8; +#endif + } + *keyTypePtr = kt; + } else { + *keyTypePtr = *((CK_KEY_TYPE *)att->attrib.pValue); + } + pk11_FreeAttribute(att); + *keyPtr = key; + } + + /* allocate the context structure */ + context = (PK11SessionContext *)PORT_Alloc(sizeof(PK11SessionContext)); + if (context == NULL) { + if (key) pk11_FreeObject(key); + return CKR_HOST_MEMORY; + } + context->type = ctype; + context->multi = PR_TRUE; + context->cipherInfo = NULL; + context->hashInfo = NULL; + context->doPad = PR_FALSE; + context->padDataLength = 0; + context->key = key; + context->blockSize = 0; + + *contextPtr = context; + return CKR_OK; +} + +/* NSC_CryptInit initializes an encryption/Decryption operation. */ +/* This function is used by NSC_EncryptInit and NSC_WrapKey. The only difference + * in their uses if whether or not etype is CKA_ENCRYPT or CKA_WRAP */ +static CK_RV +pk11_CryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey, CK_ATTRIBUTE_TYPE etype, + PK11ContextType contextType, PRBool isEncrypt) +{ + PK11Session *session; + PK11Object *key; + PK11SessionContext *context; + PK11Attribute *att; + CK_RC2_CBC_PARAMS *rc2_param; +#if NSS_SOFTOKEN_DOES_RC5 + CK_RC5_CBC_PARAMS *rc5_param; + SECItem rc5Key; +#endif + CK_KEY_TYPE key_type; + CK_RV crv = CKR_OK; + unsigned effectiveKeyLength; + unsigned char newdeskey[8]; + PRBool useNewKey=PR_FALSE; + int t; + + session = pk11_SessionFromHandle(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + crv = pk11_InitGeneric(session,&context,contextType,&key,hKey,&key_type, + isEncrypt ?CKO_PUBLIC_KEY:CKO_PRIVATE_KEY, etype); + + if (crv != CKR_OK) { + pk11_FreeSession(session); + return crv; + } + + context->doPad = PR_FALSE; + switch(pMechanism->mechanism) { + case CKM_RSA_PKCS: + case CKM_RSA_X_509: + if (key_type != CKK_RSA) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + context->multi = PR_FALSE; + context->cipherInfo = isEncrypt ? + (void *)pk11_GetPubKey(key,CKK_RSA) : + (void *)pk11_GetPrivKey(key,CKK_RSA); + if (context->cipherInfo == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + if (isEncrypt) { + context->update = (PK11Cipher) + (pMechanism->mechanism == CKM_RSA_X_509 + ? RSA_EncryptRaw : RSA_EncryptBlock); + } else { + context->update = (PK11Cipher) + (pMechanism->mechanism == CKM_RSA_X_509 + ? RSA_DecryptRaw : RSA_DecryptBlock); + } + context->destroy = pk11_Null; + break; + case CKM_RC2_CBC_PAD: + context->doPad = PR_TRUE; + context->blockSize = 8; + /* fall thru */ + case CKM_RC2_ECB: + case CKM_RC2_CBC: + if (key_type != CKK_RC2) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + att = pk11_FindAttribute(key,CKA_VALUE); + if (att == NULL) { + crv = CKR_KEY_HANDLE_INVALID; + break; + } + rc2_param = (CK_RC2_CBC_PARAMS *)pMechanism->pParameter; + effectiveKeyLength = (rc2_param->ulEffectiveBits+7)/8; + context->cipherInfo = + RC2_CreateContext((unsigned char*)att->attrib.pValue, + att->attrib.ulValueLen, rc2_param->iv, + pMechanism->mechanism == CKM_RC2_ECB ? NSS_RC2 : + NSS_RC2_CBC,effectiveKeyLength); + pk11_FreeAttribute(att); + if (context->cipherInfo == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + context->update = (PK11Cipher) (isEncrypt ? RC2_Encrypt : RC2_Decrypt); + context->destroy = (PK11Destroy) RC2_DestroyContext; + break; +#if NSS_SOFTOKEN_DOES_RC5 + case CKM_RC5_CBC_PAD: + context->doPad = PR_TRUE; + /* fall thru */ + case CKM_RC5_ECB: + case CKM_RC5_CBC: + if (key_type != CKK_RC5) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + att = pk11_FindAttribute(key,CKA_VALUE); + if (att == NULL) { + crv = CKR_KEY_HANDLE_INVALID; + break; + } + rc5_param = (CK_RC5_CBC_PARAMS *)pMechanism->pParameter; + if (context->doPad) { + context->blockSize = rc5_param->ulWordsize*2; + } + rc5Key.data = (unsigned char*)att->attrib.pValue; + rc5Key.len = att->attrib.ulValueLen; + context->cipherInfo = RC5_CreateContext(&rc5Key,rc5_param->ulRounds, + rc5_param->ulWordsize,rc5_param->pIv, + pMechanism->mechanism == CKM_RC5_ECB ? NSS_RC5 : NSS_RC5_CBC); + pk11_FreeAttribute(att); + if (context->cipherInfo == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + context->update = (PK11Cipher) (isEncrypt ? RC5_Encrypt : RC5_Decrypt); + context->destroy = (PK11Destroy) RC5_DestroyContext; + break; +#endif + case CKM_RC4: + if (key_type != CKK_RC4) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + att = pk11_FindAttribute(key,CKA_VALUE); + if (att == NULL) { + crv = CKR_KEY_HANDLE_INVALID; + break; + } + context->cipherInfo = + RC4_CreateContext((unsigned char*)att->attrib.pValue, + att->attrib.ulValueLen); + pk11_FreeAttribute(att); + if (context->cipherInfo == NULL) { + crv = CKR_HOST_MEMORY; /* WRONG !!! */ + break; + } + context->update = (PK11Cipher) (isEncrypt ? RC4_Encrypt : RC4_Decrypt); + context->destroy = (PK11Destroy) RC4_DestroyContext; + break; + case CKM_CDMF_CBC_PAD: + context->doPad = PR_TRUE; + context->blockSize = 8; + /* fall thru */ + case CKM_CDMF_ECB: + case CKM_CDMF_CBC: + if (key_type != CKK_CDMF) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + t = (pMechanism->mechanism == CKM_CDMF_ECB) ? NSS_DES : NSS_DES_CBC; + useNewKey=PR_TRUE; + if (crv != CKR_OK) break; + goto finish_des; + case CKM_DES_ECB: + if (key_type != CKK_DES) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + t = NSS_DES; + goto finish_des; + case CKM_DES_CBC_PAD: + context->doPad = PR_TRUE; + context->blockSize = 8; + /* fall thru */ + case CKM_DES_CBC: + if (key_type != CKK_DES) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + t = NSS_DES_CBC; + goto finish_des; + case CKM_DES3_ECB: + if ((key_type != CKK_DES2) && (key_type != CKK_DES3)) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + t = NSS_DES_EDE3; + goto finish_des; + case CKM_DES3_CBC_PAD: + context->doPad = PR_TRUE; + context->blockSize = 8; + /* fall thru */ + case CKM_DES3_CBC: + if ((key_type != CKK_DES2) && (key_type != CKK_DES3)) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + t = NSS_DES_EDE3_CBC; +finish_des: + att = pk11_FindAttribute(key,CKA_VALUE); + if (att == NULL) { + crv = CKR_KEY_HANDLE_INVALID; + break; + } + if (useNewKey) { + crv = pk11_cdmf2des((unsigned char*)att->attrib.pValue,newdeskey); + if (crv != CKR_OK) { + pk11_FreeAttribute(att); + break; + } + } + context->cipherInfo = DES_CreateContext( + useNewKey ? newdeskey : (unsigned char*)att->attrib.pValue, + (unsigned char*)pMechanism->pParameter,t, isEncrypt); + pk11_FreeAttribute(att); + if (context->cipherInfo == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + context->update = (PK11Cipher) (isEncrypt ? DES_Encrypt : DES_Decrypt); + context->destroy = (PK11Destroy) DES_DestroyContext; + + break; + case CKM_AES_CBC_PAD: + context->doPad = PR_TRUE; + context->blockSize = 16; + /* fall thru */ + case CKM_AES_ECB: + case CKM_AES_CBC: + if (key_type != CKK_AES) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + att = pk11_FindAttribute(key,CKA_VALUE); + if (att == NULL) { + crv = CKR_KEY_HANDLE_INVALID; + break; + } + context->cipherInfo = AES_CreateContext( + (unsigned char*)att->attrib.pValue, + (unsigned char*)pMechanism->pParameter, + pMechanism->mechanism == CKM_AES_ECB ? NSS_AES : NSS_AES_CBC, + isEncrypt, att->attrib.ulValueLen, 16); + pk11_FreeAttribute(att); + if (context->cipherInfo == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + context->update = (PK11Cipher) (isEncrypt ? AES_Encrypt : AES_Decrypt); + context->destroy = (PK11Destroy) AES_DestroyContext; + + break; + default: + crv = CKR_MECHANISM_INVALID; + break; + } + + if (crv != CKR_OK) { + pk11_FreeContext(context); + pk11_FreeSession(session); + return crv; + } + pk11_SetContextByType(session, contextType, context); + pk11_FreeSession(session); + return CKR_OK; +} + +/* NSC_EncryptInit initializes an encryption operation. */ +CK_RV NSC_EncryptInit(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) +{ + return pk11_CryptInit(hSession, pMechanism, hKey, CKA_ENCRYPT, + PK11_ENCRYPT, PR_TRUE); +} + +/* NSC_EncryptUpdate continues a multiple-part encryption operation. */ +CK_RV NSC_EncryptUpdate(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, + CK_ULONG_PTR pulEncryptedPartLen) +{ + PK11SessionContext *context; + unsigned int outlen,i; + unsigned int padoutlen = 0; + unsigned int maxout = *pulEncryptedPartLen; + CK_RV crv; + SECStatus rv; + + /* make sure we're legal */ + crv = pk11_GetContext(hSession,&context,PK11_ENCRYPT,PR_TRUE,NULL); + if (crv != CKR_OK) return crv; + + /* do padding */ + if (context->doPad) { + /* deal with previous buffered data */ + if (context->padDataLength != 0) { + /* fill in the padded to a full block size */ + for (i=context->padDataLength; + (ulPartLen != 0) && i < context->blockSize; i++) { + context->padBuf[i] = *pPart++; + ulPartLen--; + context->padDataLength++; + } + + /* not enough data to encrypt yet? then return */ + if (context->padDataLength != context->blockSize) { + *pulEncryptedPartLen = 0; + return CKR_OK; + } + /* encrypt the current padded data */ + rv = (*context->update)(context->cipherInfo,pEncryptedPart, + &outlen,context->blockSize,context->padBuf,context->blockSize); + if (rv != SECSuccess) return CKR_DEVICE_ERROR; + pEncryptedPart += padoutlen; + maxout -= padoutlen; + } + /* save the residual */ + context->padDataLength = ulPartLen % context->blockSize; + if (context->padDataLength) { + PORT_Memcpy(context->padBuf, + &pPart[ulPartLen-context->padDataLength], + context->padDataLength); + ulPartLen -= context->padDataLength; + } + /* if we've exhausted our new buffer, we're done */ + if (ulPartLen == 0) { + *pulEncryptedPartLen = padoutlen; + return CKR_OK; + } + } + + + + /* do it: NOTE: this assumes buf size in is >= buf size out! */ + rv = (*context->update)(context->cipherInfo,pEncryptedPart, + &outlen, maxout, pPart, ulPartLen); + *pulEncryptedPartLen = (CK_ULONG) (outlen + padoutlen); + return (rv == SECSuccess) ? CKR_OK : CKR_DEVICE_ERROR; +} + + +/* NSC_EncryptFinal finishes a multiple-part encryption operation. */ +CK_RV NSC_EncryptFinal(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pLastEncryptedPart, CK_ULONG_PTR pulLastEncryptedPartLen) +{ + PK11Session *session; + PK11SessionContext *context; + unsigned int outlen,i; + unsigned int maxout = *pulLastEncryptedPartLen; + CK_RV crv; + SECStatus rv = SECSuccess; + PRBool contextFinished = PR_TRUE; + + /* make sure we're legal */ + crv = pk11_GetContext(hSession,&context,PK11_ENCRYPT,PR_TRUE,&session); + if (crv != CKR_OK) return crv; + + *pulLastEncryptedPartLen = 0; + if (!pLastEncryptedPart) { + /* caller is checking the amount of remaining data */ + if (context->blockSize > 0) { + *pulLastEncryptedPartLen = context->blockSize; + contextFinished = PR_FALSE; /* still have padding to go */ + } + goto finish; + } + + /* do padding */ + if (context->doPad) { + unsigned char padbyte = (unsigned char) + (context->blockSize - context->padDataLength); + /* fill out rest of pad buffer with pad magic*/ + for (i=context->padDataLength; i < context->blockSize; i++) { + context->padBuf[i] = padbyte; + } + rv = (*context->update)(context->cipherInfo,pLastEncryptedPart, + &outlen, maxout, context->padBuf, context->blockSize); + if (rv == SECSuccess) *pulLastEncryptedPartLen = (CK_ULONG) outlen; + } + +finish: + if (contextFinished) { + pk11_SetContextByType(session, PK11_ENCRYPT, NULL); + pk11_FreeContext(context); + } + pk11_FreeSession(session); + return (rv == SECSuccess) ? CKR_OK : CKR_DEVICE_ERROR; +} + +/* NSC_Encrypt encrypts single-part data. */ +CK_RV NSC_Encrypt (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, + CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData, + CK_ULONG_PTR pulEncryptedDataLen) +{ + PK11Session *session; + PK11SessionContext *context; + unsigned int outlen; + unsigned int maxoutlen = *pulEncryptedDataLen; + CK_RV crv; + CK_RV crv2; + SECStatus rv; + + /* make sure we're legal */ + crv = pk11_GetContext(hSession,&context,PK11_ENCRYPT,PR_FALSE,&session); + if (crv != CKR_OK) return crv; + + if (!pEncryptedData) { + *pulEncryptedDataLen = ulDataLen + 2 * context->blockSize; + goto finish; + } + + if (context->doPad) { + CK_ULONG finalLen; + /* padding is fairly complicated, have the update and final + * code deal with it */ + pk11_FreeSession(session); + crv = NSC_EncryptUpdate(hSession,pData,ulDataLen,pEncryptedData, + pulEncryptedDataLen); + if (crv != CKR_OK) *pulEncryptedDataLen = 0; + maxoutlen -= *pulEncryptedDataLen; + pEncryptedData += *pulEncryptedDataLen; + finalLen = maxoutlen; + crv2 = NSC_EncryptFinal(hSession, pEncryptedData, &finalLen); + if (crv2 == CKR_OK) { *pulEncryptedDataLen += finalLen; } + return crv == CKR_OK ? crv2 :crv; + } + + + /* do it: NOTE: this assumes buf size is big enough. */ + rv = (*context->update)(context->cipherInfo, pEncryptedData, + &outlen, maxoutlen, pData, ulDataLen); + *pulEncryptedDataLen = (CK_ULONG) outlen; + pk11_SetContextByType(session, PK11_ENCRYPT, NULL); + pk11_FreeContext(context); +finish: + pk11_FreeSession(session); + + return (rv == SECSuccess) ? CKR_OK : CKR_DEVICE_ERROR; +} + + +/* + ************** Crypto Functions: Decrypt ************************ + */ + +/* NSC_DecryptInit initializes a decryption operation. */ +CK_RV NSC_DecryptInit( CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) +{ + return pk11_CryptInit(hSession, pMechanism, hKey, CKA_DECRYPT, + PK11_DECRYPT, PR_FALSE); +} + +/* NSC_DecryptUpdate continues a multiple-part decryption operation. */ +CK_RV NSC_DecryptUpdate(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, + CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) +{ + PK11SessionContext *context; + unsigned int padoutlen = 0; + unsigned int outlen; + unsigned int maxout = *pulPartLen; + CK_RV crv; + SECStatus rv; + + /* make sure we're legal */ + crv = pk11_GetContext(hSession,&context,PK11_DECRYPT,PR_TRUE,NULL); + if (crv != CKR_OK) return crv; + + if (context->doPad) { + /* first decrypt our saved buffer */ + if (context->padDataLength != 0) { + rv = (*context->update)(context->cipherInfo, pPart, &padoutlen, + maxout, context->padBuf, context->blockSize); + if (rv != SECSuccess) return CKR_DEVICE_ERROR; + pPart += padoutlen; + maxout -= padoutlen; + } + /* now save the final block for the next decrypt or the final */ + PORT_Memcpy(context->padBuf,&pEncryptedPart[ulEncryptedPartLen - + context->blockSize], context->blockSize); + context->padDataLength = context->blockSize; + ulEncryptedPartLen -= context->padDataLength; + } + + /* do it: NOTE: this assumes buf size in is >= buf size out! */ + rv = (*context->update)(context->cipherInfo,pPart, &outlen, + maxout, pEncryptedPart, ulEncryptedPartLen); + *pulPartLen = (CK_ULONG) (outlen + padoutlen); + return (rv == SECSuccess) ? CKR_OK : CKR_DEVICE_ERROR; +} + + +/* NSC_DecryptFinal finishes a multiple-part decryption operation. */ +CK_RV NSC_DecryptFinal(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pLastPart, CK_ULONG_PTR pulLastPartLen) +{ + PK11Session *session; + PK11SessionContext *context; + unsigned int outlen; + unsigned int maxout = *pulLastPartLen; + CK_RV crv; + SECStatus rv = SECSuccess; + PRBool contextFinished = PR_TRUE; + + /* make sure we're legal */ + crv = pk11_GetContext(hSession,&context,PK11_DECRYPT,PR_TRUE,&session); + if (crv != CKR_OK) return crv; + + *pulLastPartLen = 0; + if (!pLastPart) { + /* caller is checking the amount of remaining data */ + if (context->padDataLength > 0) { + *pulLastPartLen = 2 * context->blockSize; + contextFinished = PR_FALSE; /* still have padding to go */ + } + goto finish; + } + + if (context->doPad) { + /* decrypt our saved buffer */ + if (context->padDataLength != 0) { + /* this assumes that pLastPart is big enough to hold the *whole* + * buffer!!! */ + rv = (*context->update)(context->cipherInfo, pLastPart, &outlen, + maxout, context->padBuf, context->blockSize); + if (rv == SECSuccess) { + unsigned int padSize = + (unsigned int) pLastPart[context->blockSize-1]; + if ((padSize > context->blockSize) || (padSize == 0)) { + rv = SECFailure; + } else { + *pulLastPartLen = outlen - padSize; + } + } + } + } + +finish: + if (contextFinished) { + pk11_SetContextByType(session, PK11_DECRYPT, NULL); + pk11_FreeContext(context); + } + pk11_FreeSession(session); + return (rv == SECSuccess) ? CKR_OK : CKR_DEVICE_ERROR; +} + +/* NSC_Decrypt decrypts encrypted data in a single part. */ +CK_RV NSC_Decrypt(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pEncryptedData,CK_ULONG ulEncryptedDataLen,CK_BYTE_PTR pData, + CK_ULONG_PTR pulDataLen) +{ + PK11Session *session; + PK11SessionContext *context; + unsigned int outlen; + unsigned int maxoutlen = *pulDataLen; + CK_RV crv; + CK_RV crv2; + SECStatus rv; + + /* make sure we're legal */ + crv = pk11_GetContext(hSession,&context,PK11_DECRYPT,PR_FALSE,&session); + if (crv != CKR_OK) return crv; + + if (!pData) { + *pulDataLen = ulEncryptedDataLen + context->blockSize; + goto finish; + } + + if (context->doPad) { + CK_ULONG finalLen; + /* padding is fairly complicated, have the update and final + * code deal with it */ + pk11_FreeSession(session); + crv = NSC_DecryptUpdate(hSession,pEncryptedData,ulEncryptedDataLen, + pData, pulDataLen); + if (crv != CKR_OK) *pulDataLen = 0; + maxoutlen -= *pulDataLen; + pData += *pulDataLen; + finalLen = maxoutlen; + crv2 = NSC_DecryptFinal(hSession, pData, &finalLen); + if (crv2 == CKR_OK) { *pulDataLen += finalLen; } + return crv == CKR_OK ? crv2 :crv; + } + + rv = (*context->update)(context->cipherInfo, pData, &outlen, maxoutlen, + pEncryptedData, ulEncryptedDataLen); + *pulDataLen = (CK_ULONG) outlen; + pk11_SetContextByType(session, PK11_DECRYPT, NULL); + pk11_FreeContext(context); +finish: + pk11_FreeSession(session); + return (rv == SECSuccess) ? CKR_OK : CKR_DEVICE_ERROR; +} + + + +/* + ************** Crypto Functions: Digest (HASH) ************************ + */ + +/* NSC_DigestInit initializes a message-digesting operation. */ +CK_RV NSC_DigestInit(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism) +{ + PK11Session *session; + PK11SessionContext *context; + MD2Context *md2_context; + MD5Context *md5_context; + SHA1Context *sha1_context; + CK_RV crv = CKR_OK; + + session = pk11_SessionFromHandle(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + crv = pk11_InitGeneric(session,&context,PK11_HASH,NULL,0,NULL, 0, 0); + if (crv != CKR_OK) { + pk11_FreeSession(session); + return crv; + } + + switch(pMechanism->mechanism) { + case CKM_MD2: + md2_context = MD2_NewContext(); + context->cipherInfo = (void *)md2_context; + context->cipherInfoLen = MD2_FlattenSize(md2_context); + context->currentMech = CKM_MD2; + if (context->cipherInfo == NULL) { + crv= CKR_HOST_MEMORY; + + } + context->hashUpdate = (PK11Hash) MD2_Update; + context->end = (PK11End) MD2_End; + context->destroy = (PK11Destroy) MD2_DestroyContext; + context->maxLen = MD2_LENGTH; + MD2_Begin(md2_context); + break; + case CKM_MD5: + md5_context = MD5_NewContext(); + context->cipherInfo = (void *)md5_context; + context->cipherInfoLen = MD5_FlattenSize(md5_context); + context->currentMech = CKM_MD5; + if (context->cipherInfo == NULL) { + crv= CKR_HOST_MEMORY; + + } + context->hashUpdate = (PK11Hash) MD5_Update; + context->end = (PK11End) MD5_End; + context->destroy = (PK11Destroy) MD5_DestroyContext; + context->maxLen = MD5_LENGTH; + MD5_Begin(md5_context); + break; + case CKM_SHA_1: + sha1_context = SHA1_NewContext(); + context->cipherInfo = (void *)sha1_context; + context->cipherInfoLen = SHA1_FlattenSize(sha1_context); + context->currentMech = CKM_SHA_1; + if (context->cipherInfo == NULL) { + crv= CKR_HOST_MEMORY; + break; + } + context->hashUpdate = (PK11Hash) SHA1_Update; + context->end = (PK11End) SHA1_End; + context->destroy = (PK11Destroy) SHA1_DestroyContext; + SHA1_Begin(sha1_context); + context->maxLen = SHA1_LENGTH; + break; + default: + crv = CKR_MECHANISM_INVALID; + break; + } + + if (crv != CKR_OK) { + pk11_FreeContext(context); + pk11_FreeSession(session); + return crv; + } + pk11_SetContextByType(session, PK11_HASH, context); + pk11_FreeSession(session); + return CKR_OK; +} + + +/* NSC_Digest digests data in a single part. */ +CK_RV NSC_Digest(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pDigest, + CK_ULONG_PTR pulDigestLen) +{ + PK11Session *session; + PK11SessionContext *context; + unsigned int digestLen; + unsigned int maxout = *pulDigestLen; + CK_RV crv; + + /* make sure we're legal */ + crv = pk11_GetContext(hSession,&context,PK11_HASH,PR_FALSE,&session); + if (crv != CKR_OK) return crv; + + if (pDigest == NULL) { + *pulDigestLen = context->maxLen; + goto finish; + } + + /* do it: */ + (*context->hashUpdate)(context->cipherInfo, pData, ulDataLen); + /* NOTE: this assumes buf size is bigenough for the algorithm */ + (*context->end)(context->cipherInfo, pDigest, &digestLen,maxout); + *pulDigestLen = digestLen; + + pk11_SetContextByType(session, PK11_HASH, NULL); + pk11_FreeContext(context); +finish: + pk11_FreeSession(session); + return CKR_OK; +} + + +/* NSC_DigestUpdate continues a multiple-part message-digesting operation. */ +CK_RV NSC_DigestUpdate(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pPart, + CK_ULONG ulPartLen) +{ + PK11SessionContext *context; + CK_RV crv; + + /* make sure we're legal */ + crv = pk11_GetContext(hSession,&context,PK11_HASH,PR_TRUE,NULL); + if (crv != CKR_OK) return crv; + /* do it: */ + (*context->hashUpdate)(context->cipherInfo, pPart, ulPartLen); + return CKR_OK; +} + + +/* NSC_DigestFinal finishes a multiple-part message-digesting operation. */ +CK_RV NSC_DigestFinal(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pDigest, + CK_ULONG_PTR pulDigestLen) +{ + PK11Session *session; + PK11SessionContext *context; + unsigned int maxout = *pulDigestLen; + unsigned int digestLen; + CK_RV crv; + + /* make sure we're legal */ + crv = pk11_GetContext(hSession, &context, PK11_HASH, PR_TRUE, &session); + if (crv != CKR_OK) return crv; + + if (pDigest != NULL) { + (*context->end)(context->cipherInfo, pDigest, &digestLen, maxout); + *pulDigestLen = digestLen; + pk11_SetContextByType(session, PK11_HASH, NULL); + pk11_FreeContext(context); + } else { + *pulDigestLen = context->maxLen; + } + + pk11_FreeSession(session); + return CKR_OK; +} + +/* + * this helper functions are used by Generic Macing and Signing functions + * that use hashes as part of their operations. + */ +static CK_RV +pk11_doSubMD2(PK11SessionContext *context) { + MD2Context *md2_context = MD2_NewContext(); + context->hashInfo = (void *)md2_context; + if (context->hashInfo == NULL) { + return CKR_HOST_MEMORY; + } + context->hashUpdate = (PK11Hash) MD2_Update; + context->end = (PK11End) MD2_End; + context->hashdestroy = (PK11Destroy) MD2_DestroyContext; + MD2_Begin(md2_context); + return CKR_OK; +} + +static CK_RV +pk11_doSubMD5(PK11SessionContext *context) { + MD5Context *md5_context = MD5_NewContext(); + context->hashInfo = (void *)md5_context; + if (context->hashInfo == NULL) { + return CKR_HOST_MEMORY; + } + context->hashUpdate = (PK11Hash) MD5_Update; + context->end = (PK11End) MD5_End; + context->hashdestroy = (PK11Destroy) MD5_DestroyContext; + MD5_Begin(md5_context); + return CKR_OK; +} + +static CK_RV +pk11_doSubSHA1(PK11SessionContext *context) { + SHA1Context *sha1_context = SHA1_NewContext(); + context->hashInfo = (void *)sha1_context; + if (context->hashInfo == NULL) { + return CKR_HOST_MEMORY; + } + context->hashUpdate = (PK11Hash) SHA1_Update; + context->end = (PK11End) SHA1_End; + context->hashdestroy = (PK11Destroy) SHA1_DestroyContext; + SHA1_Begin(sha1_context); + return CKR_OK; +} + +/* + * HMAC General copies only a portion of the result. This update routine likes + * the final HMAC output with the signature. + */ +static SECStatus +pk11_HMACCopy(CK_ULONG *copyLen,unsigned char *sig,unsigned int *sigLen, + unsigned int maxLen,unsigned char *hash, unsigned int hashLen) +{ + if (maxLen < *copyLen) return SECFailure; + PORT_Memcpy(sig,hash,*copyLen); + *sigLen = *copyLen; + return SECSuccess; +} + +/* Verify is just a compare for HMAC */ +static SECStatus +pk11_HMACCmp(CK_ULONG *copyLen,unsigned char *sig,unsigned int sigLen, + unsigned char *hash, unsigned int hashLen) +{ + return PORT_Memcmp(sig,hash,*copyLen) ? SECSuccess : SECFailure ; +} + +/* + * common HMAC initalization routine + */ +static CK_RV +pk11_doHMACInit(PK11SessionContext *context,HASH_HashType hash, + PK11Object *key, CK_ULONG mac_size) +{ + PK11Attribute *keyval; + HMACContext *HMACcontext; + CK_ULONG *intpointer; + const SECHashObject *hashObj = &SECRawHashObjects[hash]; + + keyval = pk11_FindAttribute(key,CKA_VALUE); + if (keyval == NULL) return CKR_KEY_SIZE_RANGE; + + HMACcontext = HMAC_Create(hashObj, + (const unsigned char*)keyval->attrib.pValue, + keyval->attrib.ulValueLen); + context->hashInfo = HMACcontext; + context->multi = PR_TRUE; + pk11_FreeAttribute(keyval); + if (context->hashInfo == NULL) { + return CKR_HOST_MEMORY; + } + context->hashUpdate = (PK11Hash) HMAC_Update; + context->end = (PK11End) HMAC_Finish; + + context->hashdestroy = (PK11Destroy) pk11_HMAC_Destroy; + intpointer = (CK_ULONG *) PORT_Alloc(sizeof(CK_ULONG)); + if (intpointer == NULL) { + return CKR_HOST_MEMORY; + } + *intpointer = mac_size; + context->cipherInfo = (void *) intpointer; + context->destroy = (PK11Destroy) pk11_Space; + context->update = (PK11Cipher) pk11_HMACCopy; + context->verify = (PK11Verify) pk11_HMACCmp; + context->maxLen = hashObj->length; + HMAC_Begin(HMACcontext); + return CKR_OK; +} + +/* + * SSL Macing support. SSL Macs are inited, then update with the base + * hashing algorithm, then finalized in sign and verify + */ + +/* + * FROM SSL: + * 60 bytes is 3 times the maximum length MAC size that is supported. + * We probably should have one copy of this table. We still need this table + * in ssl to 'sign' the handshake hashes. + */ +static unsigned char ssl_pad_1 [60] = { + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36 +}; +static unsigned char ssl_pad_2 [60] = { + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c +}; + +static SECStatus +pk11_SSLMACSign(PK11SSLMACInfo *info,unsigned char *sig,unsigned int *sigLen, + unsigned int maxLen,unsigned char *hash, unsigned int hashLen) +{ + unsigned char tmpBuf[PK11_MAX_MAC_LENGTH]; + unsigned int out; + + info->begin(info->hashContext); + info->update(info->hashContext,info->key,info->keySize); + info->update(info->hashContext,ssl_pad_2,info->padSize); + info->update(info->hashContext,hash,hashLen); + info->end(info->hashContext,tmpBuf,&out,PK11_MAX_MAC_LENGTH); + PORT_Memcpy(sig,tmpBuf,info->macSize); + *sigLen = info->macSize; + return SECSuccess; +} + +static SECStatus +pk11_SSLMACVerify(PK11SSLMACInfo *info,unsigned char *sig,unsigned int sigLen, + unsigned char *hash, unsigned int hashLen) +{ + unsigned char tmpBuf[PK11_MAX_MAC_LENGTH]; + unsigned int out; + + info->begin(info->hashContext); + info->update(info->hashContext,info->key,info->keySize); + info->update(info->hashContext,ssl_pad_2,info->padSize); + info->update(info->hashContext,hash,hashLen); + info->end(info->hashContext,tmpBuf,&out,PK11_MAX_MAC_LENGTH); + return (PORT_Memcmp(sig,tmpBuf,info->macSize) == 0) ? + SECSuccess : SECFailure; +} + +/* + * common HMAC initalization routine + */ +static CK_RV +pk11_doSSLMACInit(PK11SessionContext *context,SECOidTag oid, + PK11Object *key, CK_ULONG mac_size) +{ + PK11Attribute *keyval; + PK11Begin begin; + int padSize; + PK11SSLMACInfo *sslmacinfo; + CK_RV crv = CKR_MECHANISM_INVALID; + + if (oid == SEC_OID_SHA1) { + crv = pk11_doSubSHA1(context); + begin = (PK11Begin) SHA1_Begin; + if (crv != CKR_OK) return crv; + padSize = 40; + } else { + crv = pk11_doSubMD5(context); + if (crv != CKR_OK) return crv; + begin = (PK11Begin) MD5_Begin; + padSize = 48; + } + context->multi = PR_TRUE; + + keyval = pk11_FindAttribute(key,CKA_VALUE); + if (keyval == NULL) return CKR_KEY_SIZE_RANGE; + + context->hashUpdate(context->hashInfo,keyval->attrib.pValue, + keyval->attrib.ulValueLen); + context->hashUpdate(context->hashInfo,ssl_pad_1,padSize); + sslmacinfo = (PK11SSLMACInfo *) PORT_Alloc(sizeof(PK11SSLMACInfo)); + if (sslmacinfo == NULL) { + pk11_FreeAttribute(keyval); + return CKR_HOST_MEMORY; + } + sslmacinfo->macSize = mac_size; + sslmacinfo->hashContext = context->hashInfo; + PORT_Memcpy(sslmacinfo->key,keyval->attrib.pValue, + keyval->attrib.ulValueLen); + sslmacinfo->keySize = keyval->attrib.ulValueLen; + sslmacinfo->begin = begin; + sslmacinfo->end = context->end; + sslmacinfo->update = context->hashUpdate; + sslmacinfo->padSize = padSize; + pk11_FreeAttribute(keyval); + context->cipherInfo = (void *) sslmacinfo; + context->destroy = (PK11Destroy) pk11_Space; + context->update = (PK11Cipher) pk11_SSLMACSign; + context->verify = (PK11Verify) pk11_SSLMACVerify; + context->maxLen = mac_size; + return CKR_OK; +} + +typedef struct { + PRUint32 cxSize; /* size of allocated block, in bytes. */ + PRUint32 cxKeyLen; /* number of bytes of cxBuf containing key. */ + PRUint32 cxDataLen; /* number of bytes of cxBuf containing data. */ + SECStatus cxRv; /* records failure of void functions. */ + unsigned char cxBuf[512]; /* actual size may be larger than 512. */ +} TLSPRFContext; + +static void +pk11_TLSPRFHashUpdate(TLSPRFContext *cx, const unsigned char *data, + unsigned int data_len) +{ + PRUint32 bytesUsed = PK11_OFFSETOF(TLSPRFContext, cxBuf) + + cx->cxKeyLen + cx->cxDataLen; + + if (cx->cxRv != SECSuccess) /* function has previously failed. */ + return; + if (bytesUsed + data_len > cx->cxSize) { + /* We don't use realloc here because + ** (a) realloc doesn't zero out the old block, and + ** (b) if realloc fails, we lose the old block. + */ + PRUint32 blockSize = bytesUsed + data_len + 512; + TLSPRFContext *new_cx = (TLSPRFContext *)PORT_Alloc(blockSize); + if (!new_cx) { + cx->cxRv = SECFailure; + return; + } + PORT_Memcpy(new_cx, cx, cx->cxSize); + new_cx->cxSize = blockSize; + + PORT_ZFree(cx, cx->cxSize); + cx = new_cx; + } + PORT_Memcpy(cx->cxBuf + cx->cxKeyLen + cx->cxDataLen, data, data_len); + cx->cxDataLen += data_len; +} + +static void +pk11_TLSPRFEnd(TLSPRFContext *ctx, unsigned char *hashout, + unsigned int *pDigestLen, unsigned int maxDigestLen) +{ + *pDigestLen = 0; /* tells Verify that no data has been input yet. */ +} + +/* Compute the PRF values from the data previously input. */ +static SECStatus +pk11_TLSPRFUpdate(TLSPRFContext *cx, + unsigned char *sig, /* output goes here. */ + unsigned int * sigLen, /* how much output. */ + unsigned int maxLen, /* output buffer size */ + unsigned char *hash, /* unused. */ + unsigned int hashLen) /* unused. */ +{ + SECStatus rv; + SECItem sigItem; + SECItem seedItem; + SECItem secretItem; + + if (cx->cxRv != SECSuccess) + return cx->cxRv; + + secretItem.data = cx->cxBuf; + secretItem.len = cx->cxKeyLen; + + seedItem.data = cx->cxBuf + cx->cxKeyLen; + seedItem.len = cx->cxDataLen; + + sigItem.data = sig; + sigItem.len = maxLen; + + rv = pk11_PRF(&secretItem, NULL, &seedItem, &sigItem); + if (rv == SECSuccess && sigLen != NULL) + *sigLen = sigItem.len; + return rv; + +} + +static SECStatus +pk11_TLSPRFVerify(TLSPRFContext *cx, + unsigned char *sig, /* input, for comparison. */ + unsigned int sigLen, /* length of sig. */ + unsigned char *hash, /* data to be verified. */ + unsigned int hashLen) /* size of hash data. */ +{ + unsigned char * tmp = (unsigned char *)PORT_Alloc(sigLen); + unsigned int tmpLen = sigLen; + SECStatus rv; + + if (!tmp) + return SECFailure; + if (hashLen) { + /* hashLen is non-zero when the user does a one-step verify. + ** In this case, none of the data has been input yet. + */ + pk11_TLSPRFHashUpdate(cx, hash, hashLen); + } + rv = pk11_TLSPRFUpdate(cx, tmp, &tmpLen, sigLen, NULL, 0); + if (rv == SECSuccess) { + rv = (SECStatus)(1 - !PORT_Memcmp(tmp, sig, sigLen)); + } + PORT_ZFree(tmp, sigLen); + return rv; +} + +static void +pk11_TLSPRFHashDestroy(TLSPRFContext *cx, PRBool freeit) +{ + if (freeit) + PORT_ZFree(cx, cx->cxSize); +} + +static CK_RV +pk11_TLSPRFInit(PK11SessionContext *context, + PK11Object * key, + CK_KEY_TYPE key_type) +{ + PK11Attribute * keyVal; + TLSPRFContext * prf_cx; + CK_RV crv = CKR_HOST_MEMORY; + PRUint32 keySize; + PRUint32 blockSize; + + if (key_type != CKK_GENERIC_SECRET) + return CKR_KEY_TYPE_INCONSISTENT; /* CKR_KEY_FUNCTION_NOT_PERMITTED */ + + context->multi = PR_TRUE; + + keyVal = pk11_FindAttribute(key, CKA_VALUE); + keySize = (!keyVal) ? 0 : keyVal->attrib.ulValueLen; + blockSize = keySize + sizeof(TLSPRFContext); + prf_cx = (TLSPRFContext *)PORT_Alloc(blockSize); + if (!prf_cx) + goto done; + prf_cx->cxSize = blockSize; + prf_cx->cxKeyLen = keySize; + prf_cx->cxDataLen = 0; + prf_cx->cxRv = SECSuccess; + if (keySize) + PORT_Memcpy(prf_cx->cxBuf, keyVal->attrib.pValue, keySize); + + context->hashInfo = (void *) prf_cx; + context->cipherInfo = (void *) prf_cx; + context->hashUpdate = (PK11Hash) pk11_TLSPRFHashUpdate; + context->end = (PK11End) pk11_TLSPRFEnd; + context->update = (PK11Cipher) pk11_TLSPRFUpdate; + context->verify = (PK11Verify) pk11_TLSPRFVerify; + context->destroy = (PK11Destroy) pk11_Null; + context->hashdestroy = (PK11Destroy) pk11_TLSPRFHashDestroy; + crv = CKR_OK; + +done: + if (keyVal) + pk11_FreeAttribute(keyVal); + return crv; +} + +/* + ************** Crypto Functions: Sign ************************ + */ + +/* + * Check if We're using CBCMacing and initialize the session context if we are. + */ +static CK_RV +pk11_InitCBCMac(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey, CK_ATTRIBUTE_TYPE keyUsage, + PK11ContextType contextType) + +{ + CK_MECHANISM cbc_mechanism; + CK_ULONG mac_bytes = PK11_INVALID_MAC_SIZE; + CK_RC2_CBC_PARAMS rc2_params; +#if NSS_SOFTOKEN_DOES_RC5 + CK_RC5_CBC_PARAMS rc5_params; + CK_RC5_MAC_GENERAL_PARAMS *rc5_mac; +#endif + unsigned char ivBlock[PK11_MAX_BLOCK_SIZE]; + PK11SessionContext *context; + CK_RV crv; + int blockSize; + + switch (pMechanism->mechanism) { + case CKM_RC2_MAC_GENERAL: + mac_bytes = + ((CK_RC2_MAC_GENERAL_PARAMS *)pMechanism->pParameter)->ulMacLength; + /* fall through */ + case CKM_RC2_MAC: + /* this works because ulEffectiveBits is in the same place in both the + * CK_RC2_MAC_GENERAL_PARAMS and CK_RC2_CBC_PARAMS */ + rc2_params.ulEffectiveBits = ((CK_RC2_MAC_GENERAL_PARAMS *) + pMechanism->pParameter)->ulEffectiveBits; + PORT_Memset(rc2_params.iv,0,sizeof(rc2_params.iv)); + cbc_mechanism.mechanism = CKM_RC2_CBC; + cbc_mechanism.pParameter = &rc2_params; + cbc_mechanism.ulParameterLen = sizeof(rc2_params); + blockSize = 8; + break; +#if NSS_SOFTOKEN_DOES_RC5 + case CKM_RC5_MAC_GENERAL: + mac_bytes = + ((CK_RC5_MAC_GENERAL_PARAMS *)pMechanism->pParameter)->ulMacLength; + /* fall through */ + case CKM_RC5_MAC: + /* this works because ulEffectiveBits is in the same place in both the + * CK_RC5_MAC_GENERAL_PARAMS and CK_RC5_CBC_PARAMS */ + rc5_mac = (CK_RC5_MAC_GENERAL_PARAMS *)pMechanism->pParameter; + rc5_params.ulWordsize = rc5_mac->ulWordsize; + rc5_params.ulRounds = rc5_mac->ulRounds; + rc5_params.pIv = ivBlock; + blockSize = rc5_mac->ulWordsize*2; + rc5_params.ulIvLen = blockSize; + PORT_Memset(ivBlock,0,blockSize); + cbc_mechanism.mechanism = CKM_RC5_CBC; + cbc_mechanism.pParameter = &rc5_params; + cbc_mechanism.ulParameterLen = sizeof(rc5_params); + break; +#endif + /* add cast and idea later */ + case CKM_DES_MAC_GENERAL: + mac_bytes = *(CK_ULONG *)pMechanism->pParameter; + /* fall through */ + case CKM_DES_MAC: + blockSize = 8; + PORT_Memset(ivBlock,0,blockSize); + cbc_mechanism.mechanism = CKM_DES_CBC; + cbc_mechanism.pParameter = &ivBlock; + cbc_mechanism.ulParameterLen = blockSize; + break; + case CKM_DES3_MAC_GENERAL: + mac_bytes = *(CK_ULONG *)pMechanism->pParameter; + /* fall through */ + case CKM_DES3_MAC: + blockSize = 8; + PORT_Memset(ivBlock,0,blockSize); + cbc_mechanism.mechanism = CKM_DES3_CBC; + cbc_mechanism.pParameter = &ivBlock; + cbc_mechanism.ulParameterLen = blockSize; + break; + case CKM_CDMF_MAC_GENERAL: + mac_bytes = *(CK_ULONG *)pMechanism->pParameter; + /* fall through */ + case CKM_CDMF_MAC: + blockSize = 8; + PORT_Memset(ivBlock,0,blockSize); + cbc_mechanism.mechanism = CKM_CDMF_CBC; + cbc_mechanism.pParameter = &ivBlock; + cbc_mechanism.ulParameterLen = blockSize; + break; + case CKM_AES_MAC_GENERAL: + mac_bytes = *(CK_ULONG *)pMechanism->pParameter; + /* fall through */ + case CKM_AES_MAC: + blockSize = 16; + PORT_Memset(ivBlock,0,blockSize); + cbc_mechanism.mechanism = CKM_AES_CBC; + cbc_mechanism.pParameter = &ivBlock; + cbc_mechanism.ulParameterLen = blockSize; + break; + default: + return CKR_FUNCTION_NOT_SUPPORTED; + } + + crv = pk11_CryptInit(hSession, &cbc_mechanism, hKey, keyUsage, + contextType, PR_TRUE); + if (crv != CKR_OK) return crv; + crv = pk11_GetContext(hSession,&context,contextType,PR_TRUE,NULL); + + /* this shouldn't happen! */ + PORT_Assert(crv == CKR_OK); + if (crv != CKR_OK) return crv; + context->blockSize = blockSize; + if (mac_bytes == PK11_INVALID_MAC_SIZE) mac_bytes = blockSize/2; + context->macSize = mac_bytes; + return CKR_OK; +} + +/* + * encode RSA PKCS #1 Signature data before signing... + */ +static SECStatus +pk11_HashSign(PK11HashSignInfo *info,unsigned char *sig,unsigned int *sigLen, + unsigned int maxLen,unsigned char *hash, unsigned int hashLen) +{ + + SECStatus rv = SECFailure; + SECItem digder; + PLArenaPool *arena = NULL; + SGNDigestInfo *di = NULL; + + digder.data = NULL; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if ( !arena ) { goto loser; } + + /* Construct digest info */ + di = SGN_CreateDigestInfo(info->hashOid, hash, hashLen); + if (!di) { goto loser; } + + /* Der encode the digest as a DigestInfo */ + rv = DER_Encode(arena, &digder, SGNDigestInfoTemplate, di); + if (rv != SECSuccess) { + goto loser; + } + + /* + ** Encrypt signature after constructing appropriate PKCS#1 signature + ** block + */ + rv = RSA_Sign(info->key,sig,sigLen,maxLen,digder.data,digder.len); + + loser: + SGN_DestroyDigestInfo(di); + if (arena != NULL) { + PORT_FreeArena(arena, PR_FALSE); + } + return rv; +} + +static SECStatus +nsc_DSA_Verify_Stub(void *ctx, void *sigBuf, unsigned int sigLen, + void *dataBuf, unsigned int dataLen) +{ + SECItem signature, digest; + NSSLOWKEYPublicKey *key = (NSSLOWKEYPublicKey *)ctx; + + signature.data = (unsigned char *)sigBuf; + signature.len = sigLen; + digest.data = (unsigned char *)dataBuf; + digest.len = dataLen; + return DSA_VerifyDigest(&(key->u.dsa), &signature, &digest); +} + +static SECStatus +nsc_DSA_Sign_Stub(void *ctx, void *sigBuf, + unsigned int *sigLen, unsigned int maxSigLen, + void *dataBuf, unsigned int dataLen) +{ + SECItem signature = { 0 }, digest; + SECStatus rv; + NSSLOWKEYPrivateKey *key = (NSSLOWKEYPrivateKey *)ctx; + + (void)SECITEM_AllocItem(NULL, &signature, maxSigLen); + digest.data = (unsigned char *)dataBuf; + digest.len = dataLen; + rv = DSA_SignDigest(&(key->u.dsa), &signature, &digest); + *sigLen = signature.len; + PORT_Memcpy(sigBuf, signature.data, signature.len); + SECITEM_FreeItem(&signature, PR_FALSE); + return rv; +} + +/* NSC_SignInit setups up the signing operations. There are three basic + * types of signing: + * (1) the tradition single part, where "Raw RSA" or "Raw DSA" is applied + * to data in a single Sign operation (which often looks a lot like an + * encrypt, with data coming in and data going out). + * (2) Hash based signing, where we continually hash the data, then apply + * some sort of signature to the end. + * (3) Block Encryption CBC MAC's, where the Data is encrypted with a key, + * and only the final block is part of the mac. + * + * For case number 3, we initialize a context much like the Encryption Context + * (in fact we share code). We detect case 3 in C_SignUpdate, C_Sign, and + * C_Final by the following method... if it's not multi-part, and it's doesn't + * have a hash context, it must be a block Encryption CBC MAC. + * + * For case number 2, we initialize a hash structure, as well as make it + * multi-part. Updates are simple calls to the hash update function. Final + * calls the hashend, then passes the result to the 'update' function (which + * operates as a final signature function). In some hash based MAC'ing (as + * opposed to hash base signatures), the update function is can be simply a + * copy (as is the case with HMAC). + */ +CK_RV NSC_SignInit(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) +{ + PK11Session *session; + PK11Object *key; + PK11SessionContext *context; + CK_KEY_TYPE key_type; + CK_RV crv = CKR_OK; + NSSLOWKEYPrivateKey *privKey; + PK11HashSignInfo *info = NULL; + + /* Block Cipher MACing Algorithms use a different Context init method..*/ + crv = pk11_InitCBCMac(hSession, pMechanism, hKey, CKA_SIGN, PK11_SIGN); + if (crv != CKR_FUNCTION_NOT_SUPPORTED) return crv; + + /* we're not using a block cipher mac */ + session = pk11_SessionFromHandle(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + crv = pk11_InitGeneric(session,&context,PK11_SIGN,&key,hKey,&key_type, + CKO_PRIVATE_KEY,CKA_SIGN); + if (crv != CKR_OK) { + pk11_FreeSession(session); + return crv; + } + + context->multi = PR_FALSE; + + switch(pMechanism->mechanism) { + case CKM_MD5_RSA_PKCS: + context->multi = PR_TRUE; + crv = pk11_doSubMD5(context); + if (crv != CKR_OK) break; + context->update = (PK11Cipher) pk11_HashSign; + info = (PK11HashSignInfo *)PORT_Alloc(sizeof(PK11HashSignInfo)); + if (info == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + info->hashOid = SEC_OID_MD5; + goto finish_rsa; + case CKM_MD2_RSA_PKCS: + context->multi = PR_TRUE; + crv = pk11_doSubMD2(context); + if (crv != CKR_OK) break; + context->update = (PK11Cipher) pk11_HashSign; + info = (PK11HashSignInfo *)PORT_Alloc(sizeof(PK11HashSignInfo)); + if (info == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + info->hashOid = SEC_OID_MD2; + goto finish_rsa; + case CKM_SHA1_RSA_PKCS: + context->multi = PR_TRUE; + crv = pk11_doSubSHA1(context); + if (crv != CKR_OK) break; + context->update = (PK11Cipher) pk11_HashSign; + info = (PK11HashSignInfo *)PORT_Alloc(sizeof(PK11HashSignInfo)); + if (info == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + info->hashOid = SEC_OID_SHA1; + goto finish_rsa; + case CKM_RSA_PKCS: + context->update = (PK11Cipher) RSA_Sign; + goto finish_rsa; + case CKM_RSA_X_509: + context->update = (PK11Cipher) RSA_SignRaw; +finish_rsa: + if (key_type != CKK_RSA) { + if (info) PORT_Free(info); + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + context->multi = PR_FALSE; + privKey = pk11_GetPrivKey(key,CKK_RSA); + if (privKey == NULL) { + if (info) PORT_Free(info); + crv = CKR_HOST_MEMORY; + break; + } + /* OK, info is allocated only if we're doing hash and sign mechanism. + * It's necessary to be able to set the correct OID in the final + * signature. + */ + if (info) { + info->key = privKey; + context->cipherInfo = info; + context->destroy = (PK11Destroy)pk11_Space; + } else { + context->cipherInfo = privKey; + context->destroy = (PK11Destroy)pk11_Null; + } + context->maxLen = nsslowkey_PrivateModulusLen(privKey); + break; + + case CKM_DSA_SHA1: + context->multi = PR_TRUE; + crv = pk11_doSubSHA1(context); + if (crv != CKR_OK) break; + /* fall through */ + case CKM_DSA: + if (key_type != CKK_DSA) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + privKey = pk11_GetPrivKey(key,CKK_DSA); + if (privKey == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + context->cipherInfo = privKey; + context->update = (PK11Cipher) nsc_DSA_Sign_Stub; + context->destroy = (privKey == key->objectInfo) ? + (PK11Destroy) pk11_Null:(PK11Destroy)pk11_FreePrivKey; + context->maxLen = DSA_SIGNATURE_LEN; + + break; + case CKM_MD2_HMAC_GENERAL: + crv = pk11_doHMACInit(context,HASH_AlgMD2,key, + *(CK_ULONG *)pMechanism->pParameter); + break; + case CKM_MD2_HMAC: + crv = pk11_doHMACInit(context,HASH_AlgMD2,key,MD2_LENGTH); + break; + case CKM_MD5_HMAC_GENERAL: + crv = pk11_doHMACInit(context,HASH_AlgMD5,key, + *(CK_ULONG *)pMechanism->pParameter); + break; + case CKM_MD5_HMAC: + crv = pk11_doHMACInit(context,HASH_AlgMD5,key,MD5_LENGTH); + break; + case CKM_SHA_1_HMAC_GENERAL: + crv = pk11_doHMACInit(context,HASH_AlgSHA1,key, + *(CK_ULONG *)pMechanism->pParameter); + break; + case CKM_SHA_1_HMAC: + crv = pk11_doHMACInit(context,HASH_AlgSHA1,key,SHA1_LENGTH); + break; + case CKM_SSL3_MD5_MAC: + crv = pk11_doSSLMACInit(context,SEC_OID_MD5,key, + *(CK_ULONG *)pMechanism->pParameter); + break; + case CKM_SSL3_SHA1_MAC: + crv = pk11_doSSLMACInit(context,SEC_OID_SHA1,key, + *(CK_ULONG *)pMechanism->pParameter); + break; + case CKM_TLS_PRF_GENERAL: + crv = pk11_TLSPRFInit(context, key, key_type); + break; + default: + crv = CKR_MECHANISM_INVALID; + break; + } + + if (crv != CKR_OK) { + pk11_FreeContext(context); + pk11_FreeSession(session); + return crv; + } + pk11_SetContextByType(session, PK11_SIGN, context); + pk11_FreeSession(session); + return CKR_OK; +} + + +/* MACUpdate is the common implementation for SignUpdate and VerifyUpdate. + * (sign and verify only very in their setup and final operations) */ +static CK_RV +pk11_MACUpdate(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pPart, + CK_ULONG ulPartLen,PK11ContextType type) +{ + unsigned int outlen; + PK11SessionContext *context; + CK_RV crv; + SECStatus rv; + + /* make sure we're legal */ + crv = pk11_GetContext(hSession,&context,type,PR_FALSE,NULL); + if (crv != CKR_OK) return crv; + + if (context->hashInfo) { + (*context->hashUpdate)(context->hashInfo, pPart, ulPartLen); + return CKR_OK; + } + + /* must be block cipher macing */ + + /* deal with previous buffered data */ + if (context->padDataLength != 0) { + int i; + /* fill in the padded to a full block size */ + for (i=context->padDataLength; (ulPartLen != 0) && + i < (int)context->blockSize; i++) { + context->padBuf[i] = *pPart++; + ulPartLen--; + context->padDataLength++; + } + + /* not enough data to encrypt yet? then return */ + if (context->padDataLength != context->blockSize) return CKR_OK; + /* encrypt the current padded data */ + rv = (*context->update)(context->cipherInfo,context->macBuf,&outlen, + PK11_MAX_BLOCK_SIZE,context->padBuf,context->blockSize); + if (rv != SECSuccess) return CKR_DEVICE_ERROR; + } + + /* save the residual */ + context->padDataLength = ulPartLen % context->blockSize; + if (context->padDataLength) { + PORT_Memcpy(context->padBuf, + &pPart[ulPartLen-context->padDataLength], + context->padDataLength); + ulPartLen -= context->padDataLength; + } + + /* if we've exhausted our new buffer, we're done */ + if (ulPartLen == 0) { return CKR_OK; } + + /* run the data through out encrypter */ + while (ulPartLen) { + rv = (*context->update)(context->cipherInfo, context->padBuf, &outlen, + PK11_MAX_BLOCK_SIZE, pPart, context->blockSize); + if (rv != SECSuccess) return CKR_DEVICE_ERROR; + /* paranoia.. make sure we exit the loop */ + PORT_Assert(ulPartLen >= context->blockSize); + if (ulPartLen < context->blockSize) break; + ulPartLen -= context->blockSize; + } + + return CKR_OK; + +} + +/* NSC_SignUpdate continues a multiple-part signature operation, + * where the signature is (will be) an appendix to the data, + * and plaintext cannot be recovered from the signature */ +CK_RV NSC_SignUpdate(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pPart, + CK_ULONG ulPartLen) +{ + return pk11_MACUpdate(hSession, pPart, ulPartLen, PK11_SIGN); +} + + +/* NSC_SignFinal finishes a multiple-part signature operation, + * returning the signature. */ +CK_RV NSC_SignFinal(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pSignature, + CK_ULONG_PTR pulSignatureLen) +{ + PK11Session *session; + PK11SessionContext *context; + unsigned int outlen; + unsigned int digestLen; + unsigned int maxoutlen = *pulSignatureLen; + unsigned char tmpbuf[PK11_MAX_MAC_LENGTH]; + CK_RV crv; + SECStatus rv = SECSuccess; + + /* make sure we're legal */ + *pulSignatureLen = 0; + crv = pk11_GetContext(hSession,&context,PK11_SIGN,PR_TRUE,&session); + if (crv != CKR_OK) return crv; + + if (!pSignature) { + *pulSignatureLen = context->maxLen; + goto finish; + } else if (context->hashInfo) { + (*context->end)(context->hashInfo, tmpbuf, &digestLen, sizeof(tmpbuf)); + rv = (*context->update)(context->cipherInfo, pSignature, + &outlen, maxoutlen, tmpbuf, digestLen); + *pulSignatureLen = (CK_ULONG) outlen; + } else { + /* deal with the last block if any residual */ + if (context->padDataLength) { + /* fill out rest of pad buffer with pad magic*/ + int i; + for (i=context->padDataLength; i < (int)context->blockSize; i++) { + context->padBuf[i] = 0; + } + rv = (*context->update)(context->cipherInfo,context->macBuf, + &outlen,PK11_MAX_BLOCK_SIZE,context->padBuf,context->blockSize); + } + if (rv == SECSuccess) { + PORT_Memcpy(pSignature,context->macBuf,context->macSize); + *pulSignatureLen = context->macSize; + } + } + + pk11_FreeContext(context); + pk11_SetContextByType(session, PK11_SIGN, NULL); + +finish: + pk11_FreeSession(session); + + return (rv == SECSuccess) ? CKR_OK : CKR_DEVICE_ERROR; +} + +/* NSC_Sign signs (encrypts with private key) data in a single part, + * where the signature is (will be) an appendix to the data, + * and plaintext cannot be recovered from the signature */ +CK_RV NSC_Sign(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pData,CK_ULONG ulDataLen,CK_BYTE_PTR pSignature, + CK_ULONG_PTR pulSignatureLen) +{ + PK11Session *session; + PK11SessionContext *context; + unsigned int outlen; + unsigned int maxoutlen = *pulSignatureLen; + CK_RV crv,crv2; + SECStatus rv; + + /* make sure we're legal */ + crv = pk11_GetContext(hSession,&context,PK11_SIGN,PR_FALSE,&session); + if (crv != CKR_OK) return crv; + + if (!pSignature) { + *pulSignatureLen = context->maxLen; + goto finish; + } + + /* multi part Signing are completely implemented by SignUpdate and + * sign Final */ + if (context->multi) { + pk11_FreeSession(session); + crv = NSC_SignUpdate(hSession,pData,ulDataLen); + if (crv != CKR_OK) *pulSignatureLen = 0; + crv2 = NSC_SignFinal(hSession, pSignature, pulSignatureLen); + return crv == CKR_OK ? crv2 :crv; + } + + rv = (*context->update)(context->cipherInfo, pSignature, + &outlen, maxoutlen, pData, ulDataLen); + *pulSignatureLen = (CK_ULONG) outlen; + pk11_FreeContext(context); + pk11_SetContextByType(session, PK11_SIGN, NULL); + +finish: + pk11_FreeSession(session); + + return (rv == SECSuccess) ? CKR_OK : CKR_DEVICE_ERROR; +} + + +/* + ************** Crypto Functions: Sign Recover ************************ + */ +/* NSC_SignRecoverInit initializes a signature operation, + * where the (digest) data can be recovered from the signature. + * E.g. encryption with the user's private key */ +CK_RV NSC_SignRecoverInit(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism,CK_OBJECT_HANDLE hKey) +{ + switch (pMechanism->mechanism) { + case CKM_RSA_PKCS: + case CKM_RSA_X_509: + return NSC_SignInit(hSession,pMechanism,hKey); + default: + break; + } + return CKR_MECHANISM_INVALID; +} + + +/* NSC_SignRecover signs data in a single operation + * where the (digest) data can be recovered from the signature. + * E.g. encryption with the user's private key */ +CK_RV NSC_SignRecover(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, + CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) +{ + return NSC_Sign(hSession,pData,ulDataLen,pSignature,pulSignatureLen); +} + +/* + ************** Crypto Functions: verify ************************ + */ + +/* Handle RSA Signature formating */ +static SECStatus +pk11_hashCheckSign(PK11HashVerifyInfo *info, unsigned char *sig, + unsigned int sigLen, unsigned char *digest, unsigned int digestLen) +{ + + SECItem it; + SGNDigestInfo *di = NULL; + SECStatus rv = SECSuccess; + + it.data = NULL; + + if (info->key == NULL) goto loser; + + it.len = nsslowkey_PublicModulusLen(info->key); + if (!it.len) goto loser; + + it.data = (unsigned char *) PORT_Alloc(it.len); + if (it.data == NULL) goto loser; + + /* decrypt the block */ + rv = RSA_CheckSignRecover(info->key, it.data, &it.len, it.len, sig, sigLen); + if (rv != SECSuccess) goto loser; + + di = SGN_DecodeDigestInfo(&it); + if (di == NULL) goto loser; + if (di->digest.len != digestLen) goto loser; + + /* make sure the tag is OK */ + if (SECOID_GetAlgorithmTag(&di->digestAlgorithm) != info->hashOid) { + goto loser; + } + /* Now check the signature */ + if (PORT_Memcmp(digest, di->digest.data, di->digest.len) == 0) { + goto done; + } + + loser: + rv = SECFailure; + + done: + if (it.data != NULL) PORT_Free(it.data); + if (di != NULL) SGN_DestroyDigestInfo(di); + + return rv; +} + +/* NSC_VerifyInit initializes a verification operation, + * where the signature is an appendix to the data, + * and plaintext cannot be recovered from the signature (e.g. DSA) */ +CK_RV NSC_VerifyInit(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism,CK_OBJECT_HANDLE hKey) +{ + PK11Session *session; + PK11Object *key; + PK11SessionContext *context; + CK_KEY_TYPE key_type; + CK_RV crv = CKR_OK; + NSSLOWKEYPublicKey *pubKey; + PK11HashVerifyInfo *info = NULL; + + /* Block Cipher MACing Algorithms use a different Context init method..*/ + crv = pk11_InitCBCMac(hSession, pMechanism, hKey, CKA_VERIFY, PK11_VERIFY); + if (crv != CKR_FUNCTION_NOT_SUPPORTED) return crv; + + session = pk11_SessionFromHandle(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + crv = pk11_InitGeneric(session,&context,PK11_VERIFY,&key,hKey,&key_type, + CKO_PUBLIC_KEY,CKA_VERIFY); + if (crv != CKR_OK) { + pk11_FreeSession(session); + return crv; + } + + context->multi = PR_FALSE; + + switch(pMechanism->mechanism) { + case CKM_MD5_RSA_PKCS: + context->multi = PR_TRUE; + crv = pk11_doSubMD5(context); + if (crv != CKR_OK) break; + context->verify = (PK11Verify) pk11_hashCheckSign; + info = (PK11HashVerifyInfo *)PORT_Alloc(sizeof(PK11HashVerifyInfo)); + if (info == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + info->hashOid = SEC_OID_MD5; + goto finish_rsa; + case CKM_MD2_RSA_PKCS: + context->multi = PR_TRUE; + crv = pk11_doSubMD2(context); + if (crv != CKR_OK) break; + context->verify = (PK11Verify) pk11_hashCheckSign; + info = (PK11HashVerifyInfo *)PORT_Alloc(sizeof(PK11HashVerifyInfo)); + if (info == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + info->hashOid = SEC_OID_MD2; + goto finish_rsa; + case CKM_SHA1_RSA_PKCS: + context->multi = PR_TRUE; + crv = pk11_doSubSHA1(context); + if (crv != CKR_OK) break; + context->verify = (PK11Verify) pk11_hashCheckSign; + info = (PK11HashVerifyInfo *)PORT_Alloc(sizeof(PK11HashVerifyInfo)); + if (info == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + info->hashOid = SEC_OID_SHA1; + goto finish_rsa; + case CKM_RSA_PKCS: + context->verify = (PK11Verify) RSA_CheckSign; + goto finish_rsa; + case CKM_RSA_X_509: + context->verify = (PK11Verify) RSA_CheckSignRaw; +finish_rsa: + if (key_type != CKK_RSA) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + pubKey = pk11_GetPubKey(key,CKK_RSA); + if (pubKey == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + if (info) { + info->key = pubKey; + context->cipherInfo = info; + context->destroy = pk11_Space; + } else { + context->cipherInfo = pubKey; + context->destroy = pk11_Null; + } + break; + case CKM_DSA_SHA1: + context->multi = PR_TRUE; + crv = pk11_doSubSHA1(context); + if (crv != CKR_OK) break; + /* fall through */ + case CKM_DSA: + if (key_type != CKK_DSA) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + context->multi = PR_FALSE; + pubKey = pk11_GetPubKey(key,CKK_DSA); + if (pubKey == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + context->cipherInfo = pubKey; + context->verify = (PK11Verify) nsc_DSA_Verify_Stub; + context->destroy = pk11_Null; + break; + + case CKM_MD2_HMAC_GENERAL: + crv = pk11_doHMACInit(context,HASH_AlgMD2,key, + *(CK_ULONG *)pMechanism->pParameter); + break; + case CKM_MD2_HMAC: + crv = pk11_doHMACInit(context,HASH_AlgMD2,key,MD2_LENGTH); + break; + case CKM_MD5_HMAC_GENERAL: + crv = pk11_doHMACInit(context,HASH_AlgMD5,key, + *(CK_ULONG *)pMechanism->pParameter); + break; + case CKM_MD5_HMAC: + crv = pk11_doHMACInit(context,HASH_AlgMD5,key,MD5_LENGTH); + break; + case CKM_SHA_1_HMAC_GENERAL: + crv = pk11_doHMACInit(context,HASH_AlgSHA1,key, + *(CK_ULONG *)pMechanism->pParameter); + break; + case CKM_SHA_1_HMAC: + crv = pk11_doHMACInit(context,HASH_AlgSHA1,key,SHA1_LENGTH); + break; + case CKM_SSL3_MD5_MAC: + crv = pk11_doSSLMACInit(context,SEC_OID_MD5,key, + *(CK_ULONG *)pMechanism->pParameter); + break; + case CKM_SSL3_SHA1_MAC: + crv = pk11_doSSLMACInit(context,SEC_OID_SHA1,key, + *(CK_ULONG *)pMechanism->pParameter); + break; + case CKM_TLS_PRF_GENERAL: + crv = pk11_TLSPRFInit(context, key, key_type); + + default: + crv = CKR_MECHANISM_INVALID; + break; + } + + if (crv != CKR_OK) { + PORT_Free(context); + pk11_FreeSession(session); + return crv; + } + pk11_SetContextByType(session, PK11_VERIFY, context); + pk11_FreeSession(session); + return CKR_OK; +} + +/* NSC_Verify verifies a signature in a single-part operation, + * where the signature is an appendix to the data, + * and plaintext cannot be recovered from the signature */ +CK_RV NSC_Verify(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, + CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) +{ + PK11Session *session; + PK11SessionContext *context; + CK_RV crv; + SECStatus rv; + + /* make sure we're legal */ + crv = pk11_GetContext(hSession,&context,PK11_VERIFY,PR_FALSE,&session); + if (crv != CKR_OK) return crv; + + rv = (*context->verify)(context->cipherInfo,pSignature, ulSignatureLen, + pData, ulDataLen); + pk11_FreeContext(context); + pk11_SetContextByType(session, PK11_VERIFY, NULL); + pk11_FreeSession(session); + + return (rv == SECSuccess) ? CKR_OK : CKR_SIGNATURE_INVALID; + +} + + +/* NSC_VerifyUpdate continues a multiple-part verification operation, + * where the signature is an appendix to the data, + * and plaintext cannot be recovered from the signature */ +CK_RV NSC_VerifyUpdate( CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, + CK_ULONG ulPartLen) +{ + return pk11_MACUpdate(hSession, pPart, ulPartLen, PK11_VERIFY); +} + + +/* NSC_VerifyFinal finishes a multiple-part verification operation, + * checking the signature. */ +CK_RV NSC_VerifyFinal(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pSignature,CK_ULONG ulSignatureLen) +{ + PK11Session *session; + PK11SessionContext *context; + unsigned int outlen; + unsigned int digestLen; + unsigned char tmpbuf[PK11_MAX_MAC_LENGTH]; + CK_RV crv; + SECStatus rv = SECSuccess; + + /* make sure we're legal */ + crv = pk11_GetContext(hSession,&context,PK11_VERIFY,PR_TRUE,&session); + if (crv != CKR_OK) return crv; + + if (context->hashInfo) { + (*context->end)(context->hashInfo, tmpbuf, &digestLen, sizeof(tmpbuf)); + rv = (*context->verify)(context->cipherInfo, pSignature, + ulSignatureLen, tmpbuf, digestLen); + } else { + if (context->padDataLength) { + /* fill out rest of pad buffer with pad magic*/ + int i; + for (i=context->padDataLength; i < (int)context->blockSize; i++) { + context->padBuf[i] = 0; + } + rv = (*context->update)(context->cipherInfo,context->macBuf, + &outlen,PK11_MAX_BLOCK_SIZE,context->padBuf,context->blockSize); + } + if (rv == SECSuccess) { + rv =(PORT_Memcmp(pSignature,context->macBuf,context->macSize) == 0) + ? SECSuccess : SECFailure; + } + } + + pk11_FreeContext(context); + pk11_SetContextByType(session, PK11_VERIFY, NULL); + pk11_FreeSession(session); + return (rv == SECSuccess) ? CKR_OK : CKR_SIGNATURE_INVALID; + +} + +/* + ************** Crypto Functions: Verify Recover ************************ + */ + +/* NSC_VerifyRecoverInit initializes a signature verification operation, + * where the data is recovered from the signature. + * E.g. Decryption with the user's public key */ +CK_RV NSC_VerifyRecoverInit(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism,CK_OBJECT_HANDLE hKey) +{ + PK11Session *session; + PK11Object *key; + PK11SessionContext *context; + CK_KEY_TYPE key_type; + CK_RV crv = CKR_OK; + NSSLOWKEYPublicKey *pubKey; + + session = pk11_SessionFromHandle(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + crv = pk11_InitGeneric(session,&context,PK11_VERIFY_RECOVER, + &key,hKey,&key_type,CKO_PUBLIC_KEY,CKA_VERIFY_RECOVER); + if (crv != CKR_OK) { + pk11_FreeSession(session); + return crv; + } + + context->multi = PR_TRUE; + + switch(pMechanism->mechanism) { + case CKM_RSA_PKCS: + case CKM_RSA_X_509: + if (key_type != CKK_RSA) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + context->multi = PR_FALSE; + pubKey = pk11_GetPubKey(key,CKK_RSA); + if (pubKey == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + context->cipherInfo = pubKey; + context->update = (PK11Cipher) (pMechanism->mechanism == CKM_RSA_X_509 + ? RSA_CheckSignRecoverRaw : RSA_CheckSignRecover); + context->destroy = pk11_Null; + break; + default: + crv = CKR_MECHANISM_INVALID; + break; + } + + if (crv != CKR_OK) { + PORT_Free(context); + pk11_FreeSession(session); + return crv; + } + pk11_SetContextByType(session, PK11_VERIFY_RECOVER, context); + pk11_FreeSession(session); + return CKR_OK; +} + + +/* NSC_VerifyRecover verifies a signature in a single-part operation, + * where the data is recovered from the signature. + * E.g. Decryption with the user's public key */ +CK_RV NSC_VerifyRecover(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pSignature,CK_ULONG ulSignatureLen, + CK_BYTE_PTR pData,CK_ULONG_PTR pulDataLen) +{ + PK11Session *session; + PK11SessionContext *context; + unsigned int outlen; + unsigned int maxoutlen = *pulDataLen; + CK_RV crv; + SECStatus rv; + + /* make sure we're legal */ + crv = pk11_GetContext(hSession,&context,PK11_VERIFY_RECOVER, + PR_FALSE,&session); + if (crv != CKR_OK) return crv; + + rv = (*context->update)(context->cipherInfo, pData, &outlen, maxoutlen, + pSignature, ulSignatureLen); + *pulDataLen = (CK_ULONG) outlen; + pk11_FreeContext(context); + pk11_SetContextByType(session, PK11_VERIFY_RECOVER, NULL); + pk11_FreeSession(session); + return (rv == SECSuccess) ? CKR_OK : CKR_DEVICE_ERROR; +} + +/* + **************************** Random Functions: ************************ + */ + +/* NSC_SeedRandom mixes additional seed material into the token's random number + * generator. */ +CK_RV NSC_SeedRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSeed, + CK_ULONG ulSeedLen) +{ + SECStatus rv; + + rv = RNG_RandomUpdate(pSeed, ulSeedLen); + return (rv == SECSuccess) ? CKR_OK : CKR_DEVICE_ERROR; +} + +/* NSC_GenerateRandom generates random data. */ +CK_RV NSC_GenerateRandom(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pRandomData, CK_ULONG ulRandomLen) +{ + SECStatus rv; + + rv = RNG_GenerateGlobalRandomBytes(pRandomData, ulRandomLen); + return (rv == SECSuccess) ? CKR_OK : CKR_DEVICE_ERROR; +} + +/* + **************************** Key Functions: ************************ + */ + + +/* + * generate a password based encryption key. This code uses + * PKCS5 to do the work. + */ +static CK_RV +nsc_pbe_key_gen(NSSPKCS5PBEParameter *pkcs5_pbe, CK_MECHANISM_PTR pMechanism, + char *buf, CK_ULONG *key_length, PRBool faulty3DES) +{ + SECItem *pbe_key = NULL, iv, pwitem; + CK_PBE_PARAMS *pbe_params = NULL; + + *key_length = 0; + iv.data = NULL; iv.len = 0; + + pbe_params = (CK_PBE_PARAMS *)pMechanism->pParameter; + + pwitem.data = (unsigned char *)pbe_params->pPassword; + pwitem.len = (unsigned int)pbe_params->ulPasswordLen; + pbe_key = nsspkcs5_ComputeKeyAndIV(pkcs5_pbe, &pwitem, &iv, faulty3DES); + if (pbe_key == NULL) { + return CKR_HOST_MEMORY; + } + + PORT_Memcpy(buf, pbe_key->data, pbe_key->len); + *key_length = pbe_key->len; + SECITEM_ZfreeItem(pbe_key, PR_TRUE); + pbe_key = NULL; + + if (iv.data && pbe_params->pInitVector != NULL) { + PORT_Memcpy(pbe_params->pInitVector, iv.data, iv.len); + } + return CKR_OK; +} +static CK_RV +nsc_parameter_gen(CK_KEY_TYPE key_type, PK11Object *key) +{ + PK11Attribute *attribute; + CK_ULONG counter; + unsigned int seedBits = 0; + unsigned int primeBits; + CK_RV crv = CKR_OK; + PQGParams *params = NULL; + PQGVerify *vfy = NULL; + SECStatus rv; + + attribute = pk11_FindAttribute(key, CKA_PRIME_BITS); + if (attribute == NULL) { + return CKR_TEMPLATE_INCOMPLETE; + } + primeBits = (unsigned int) *(CK_ULONG *)attribute->attrib.pValue; + pk11_FreeAttribute(attribute); + + attribute = pk11_FindAttribute(key, CKA_NETSCAPE_PQG_SEED_BITS); + if (attribute != NULL) { + seedBits = (unsigned int) *(CK_ULONG *)attribute->attrib.pValue; + pk11_FreeAttribute(attribute); + } + + pk11_DeleteAttributeType(key,CKA_PRIME_BITS); + pk11_DeleteAttributeType(key,CKA_NETSCAPE_PQG_SEED_BITS); + + if (seedBits == 0) { + rv = PQG_ParamGen(primeBits, ¶ms, &vfy); + } else { + rv = PQG_ParamGenSeedLen(primeBits,seedBits/8, ¶ms, &vfy); + } + + if (rv != SECSuccess) { + return CKR_DEVICE_ERROR; + } + crv = pk11_AddAttributeType(key,CKA_PRIME, + params->prime.data, params->prime.len); + if (crv != CKR_OK) goto loser; + crv = pk11_AddAttributeType(key,CKA_SUBPRIME, + params->subPrime.data, params->subPrime.len); + if (crv != CKR_OK) goto loser; + crv = pk11_AddAttributeType(key,CKA_BASE, + params->base.data, params->base.len); + if (crv != CKR_OK) goto loser; + counter = vfy->counter; + crv = pk11_AddAttributeType(key,CKA_NETSCAPE_PQG_COUNTER, + &counter, sizeof(counter)); + crv = pk11_AddAttributeType(key,CKA_NETSCAPE_PQG_SEED, + vfy->seed.data, vfy->seed.len); + if (crv != CKR_OK) goto loser; + crv = pk11_AddAttributeType(key,CKA_NETSCAPE_PQG_H, + vfy->h.data, vfy->h.len); + if (crv != CKR_OK) goto loser; + +loser: + if (params) { + PQG_DestroyParams(params); + } + if (vfy) { + PQG_DestroyVerify(vfy); + } + return crv; +} + + + + + + + +static CK_RV +nsc_SetupBulkKeyGen(CK_MECHANISM_TYPE mechanism, CK_KEY_TYPE *key_type, + CK_ULONG *key_length) +{ + CK_RV crv = CKR_OK; + + switch (mechanism) { + case CKM_RC2_KEY_GEN: + *key_type = CKK_RC2; + if (*key_length == 0) crv = CKR_TEMPLATE_INCOMPLETE; + break; +#if NSS_SOFTOKEN_DOES_RC5 + case CKM_RC5_KEY_GEN: + *key_type = CKK_RC5; + if (*key_length == 0) crv = CKR_TEMPLATE_INCOMPLETE; + break; +#endif + case CKM_RC4_KEY_GEN: + *key_type = CKK_RC4; + if (*key_length == 0) crv = CKR_TEMPLATE_INCOMPLETE; + break; + case CKM_GENERIC_SECRET_KEY_GEN: + *key_type = CKK_GENERIC_SECRET; + if (*key_length == 0) crv = CKR_TEMPLATE_INCOMPLETE; + break; + case CKM_CDMF_KEY_GEN: + *key_type = CKK_CDMF; + *key_length = 8; + break; + case CKM_DES_KEY_GEN: + *key_type = CKK_DES; + *key_length = 8; + break; + case CKM_DES2_KEY_GEN: + *key_type = CKK_DES2; + *key_length = 16; + break; + case CKM_DES3_KEY_GEN: + *key_type = CKK_DES3; + *key_length = 24; + break; + case CKM_AES_KEY_GEN: + *key_type = CKK_AES; + if (*key_length == 0) crv = CKR_TEMPLATE_INCOMPLETE; + break; + default: + PORT_Assert(0); + crv = CKR_MECHANISM_INVALID; + break; + } + + return crv; +} + +CK_RV +nsc_SetupHMACKeyGen(CK_MECHANISM_PTR pMechanism, NSSPKCS5PBEParameter **pbe) +{ + SECItem salt; + CK_PBE_PARAMS *pbe_params = NULL; + NSSPKCS5PBEParameter *params; + PRArenaPool *arena = NULL; + SECStatus rv; + + *pbe = NULL; + + arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); + if (arena == NULL) { + return CKR_HOST_MEMORY; + } + + params = (NSSPKCS5PBEParameter *) PORT_ArenaZAlloc(arena, + sizeof(NSSPKCS5PBEParameter)); + if (params == NULL) { + PORT_FreeArena(arena,PR_TRUE); + return CKR_HOST_MEMORY; + } + + params->poolp = arena; + params->ivLen = 0; + params->pbeType = NSSPKCS5_PKCS12_V2; + params->hashType = HASH_AlgSHA1; + params->encAlg = SEC_OID_SHA1; /* any invalid value */ + params->is2KeyDES = PR_FALSE; + params->keyID = pbeBitGenIntegrityKey; + pbe_params = (CK_PBE_PARAMS *)pMechanism->pParameter; + params->iter = pbe_params->ulIteration; + + salt.data = (unsigned char *)pbe_params->pSalt; + salt.len = (unsigned int)pbe_params->ulSaltLen; + rv = SECITEM_CopyItem(arena,¶ms->salt,&salt); + if (rv != SECSuccess) { + PORT_FreeArena(arena,PR_TRUE); + return CKR_HOST_MEMORY; + } + switch (pMechanism->mechanism) { + case CKM_NETSCAPE_PBE_SHA1_HMAC_KEY_GEN: + case CKM_PBA_SHA1_WITH_SHA1_HMAC: + params->hashType = HASH_AlgSHA1; + params->keyLen = 20; + break; + case CKM_NETSCAPE_PBE_MD5_HMAC_KEY_GEN: + params->hashType = HASH_AlgMD5; + params->keyLen = 16; + break; + case CKM_NETSCAPE_PBE_MD2_HMAC_KEY_GEN: + params->hashType = HASH_AlgMD2; + params->keyLen = 16; + break; + default: + PORT_FreeArena(arena,PR_TRUE); + return CKR_MECHANISM_INVALID; + } + *pbe = params; + return CKR_OK; +} +/* maybe this should be table driven? */ +static CK_RV +nsc_SetupPBEKeyGen(CK_MECHANISM_PTR pMechanism, NSSPKCS5PBEParameter **pbe, + CK_KEY_TYPE *key_type) +{ + CK_RV crv = CKR_OK; + SECOidData *oid; + CK_PBE_PARAMS *pbe_params; + NSSPKCS5PBEParameter *params; + SECItem salt; + + *pbe = NULL; + + oid = SECOID_FindOIDByMechanism(pMechanism->mechanism); + if (oid == NULL) { + return CKR_MECHANISM_INVALID; + } + + pbe_params = (CK_PBE_PARAMS *)pMechanism->pParameter; + salt.data = (unsigned char *)pbe_params->pSalt; + salt.len = (unsigned int)pbe_params->ulSaltLen; + + params=nsspkcs5_NewParam(oid->offset, &salt, pbe_params->ulIteration); + if (params == NULL) { + return CKR_MECHANISM_INVALID; + } + + + switch (params->encAlg) { + case SEC_OID_DES_CBC: + *key_type = CKK_DES; + break; + case SEC_OID_DES_EDE3_CBC: + *key_type = params->is2KeyDES ? CKK_DES2 : CKK_DES3; + break; + case SEC_OID_RC2_CBC: + *key_type = CKK_RC2; + break; + case SEC_OID_RC4: + *key_type = CKK_RC4; + break; + default: + crv = CKR_MECHANISM_INVALID; + nsspkcs5_DestroyPBEParameter(params); + break; + } + if (crv == CKR_OK) { + *pbe = params; + } + return crv; +} + +/* NSC_GenerateKey generates a secret key, creating a new key object. */ +CK_RV NSC_GenerateKey(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism,CK_ATTRIBUTE_PTR pTemplate,CK_ULONG ulCount, + CK_OBJECT_HANDLE_PTR phKey) +{ + PK11Object *key; + PK11Session *session; + PRBool checkWeak = PR_FALSE; + CK_ULONG key_length = 0; + CK_KEY_TYPE key_type = CKK_INVALID_KEY_TYPE; + CK_OBJECT_CLASS objclass = CKO_SECRET_KEY; + CK_RV crv = CKR_OK; + CK_BBOOL cktrue = CK_TRUE; + int i; + PK11Slot *slot = pk11_SlotFromSessionHandle(hSession); + char buf[MAX_KEY_LEN]; + enum {nsc_pbe, nsc_ssl, nsc_bulk, nsc_param} key_gen_type; + NSSPKCS5PBEParameter *pbe_param; + SSL3RSAPreMasterSecret *rsa_pms; + CK_VERSION *version; + /* in very old versions of NSS, there were implementation errors with key + * generation methods. We want to beable to read these, but not + * produce them any more. The affected algorithm was 3DES. + */ + PRBool faultyPBE3DES = PR_FALSE; + + + /* + * now lets create an object to hang the attributes off of + */ + key = pk11_NewObject(slot); /* fill in the handle later */ + if (key == NULL) { + return CKR_HOST_MEMORY; + } + + /* + * load the template values into the object + */ + for (i=0; i < (int) ulCount; i++) { + if (pTemplate[i].type == CKA_VALUE_LEN) { + key_length = *(CK_ULONG *)pTemplate[i].pValue; + continue; + } + + crv = pk11_AddAttributeType(key,pk11_attr_expand(&pTemplate[i])); + if (crv != CKR_OK) break; + } + if (crv != CKR_OK) { + pk11_FreeObject(key); + return crv; + } + + /* make sure we don't have any class, key_type, or value fields */ + pk11_DeleteAttributeType(key,CKA_CLASS); + pk11_DeleteAttributeType(key,CKA_KEY_TYPE); + pk11_DeleteAttributeType(key,CKA_VALUE); + + /* Now Set up the parameters to generate the key (based on mechanism) */ + key_gen_type = nsc_bulk; /* bulk key by default */ + switch (pMechanism->mechanism) { + case CKM_CDMF_KEY_GEN: + case CKM_DES_KEY_GEN: + case CKM_DES2_KEY_GEN: + case CKM_DES3_KEY_GEN: + checkWeak = PR_TRUE; + case CKM_RC2_KEY_GEN: + case CKM_RC4_KEY_GEN: + case CKM_GENERIC_SECRET_KEY_GEN: + case CKM_AES_KEY_GEN: +#if NSS_SOFTOKEN_DOES_RC5 + case CKM_RC5_KEY_GEN: +#endif + crv = nsc_SetupBulkKeyGen(pMechanism->mechanism,&key_type,&key_length); + break; + case CKM_SSL3_PRE_MASTER_KEY_GEN: + key_type = CKK_GENERIC_SECRET; + key_length = 48; + key_gen_type = nsc_ssl; + break; + case CKM_PBA_SHA1_WITH_SHA1_HMAC: + case CKM_NETSCAPE_PBE_SHA1_HMAC_KEY_GEN: + case CKM_NETSCAPE_PBE_MD5_HMAC_KEY_GEN: + case CKM_NETSCAPE_PBE_MD2_HMAC_KEY_GEN: + key_gen_type = nsc_pbe; + key_type = CKK_GENERIC_SECRET; + crv = nsc_SetupHMACKeyGen(pMechanism, &pbe_param); + break; + case CKM_NETSCAPE_PBE_SHA1_FAULTY_3DES_CBC: + faultyPBE3DES = PR_TRUE; + case CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC: + case CKM_NETSCAPE_PBE_SHA1_DES_CBC: + case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC: + case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4: + case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC4: + case CKM_PBE_SHA1_DES3_EDE_CBC: + case CKM_PBE_SHA1_DES2_EDE_CBC: + case CKM_PBE_SHA1_RC2_128_CBC: + case CKM_PBE_SHA1_RC2_40_CBC: + case CKM_PBE_SHA1_RC4_128: + case CKM_PBE_SHA1_RC4_40: + case CKM_PBE_MD5_DES_CBC: + case CKM_PBE_MD2_DES_CBC: + key_gen_type = nsc_pbe; + crv = nsc_SetupPBEKeyGen(pMechanism,&pbe_param, &key_type); + break; + case CKM_DSA_PARAMETER_GEN: + key_gen_type = nsc_param; + key_type = CKK_DSA; + objclass = CKO_KG_PARAMETERS; + crv = CKR_OK; + break; + default: + crv = CKR_MECHANISM_INVALID; + break; + } + + /* make sure we aren't going to overflow the buffer */ + if (sizeof(buf) < key_length) { + /* someone is getting pretty optimistic about how big their key can + * be... */ + crv = CKR_TEMPLATE_INCONSISTENT; + } + + if (crv != CKR_OK) { pk11_FreeObject(key); return crv; } + + /* if there was no error, + * key_type *MUST* be set in the switch statement above */ + PORT_Assert( key_type != CKK_INVALID_KEY_TYPE ); + + /* + * now to the actual key gen. + */ + switch (key_gen_type) { + case nsc_pbe: + crv = nsc_pbe_key_gen(pbe_param, pMechanism, buf, &key_length, + faultyPBE3DES); + nsspkcs5_DestroyPBEParameter(pbe_param); + break; + case nsc_ssl: + rsa_pms = (SSL3RSAPreMasterSecret *)buf; + version = (CK_VERSION *)pMechanism->pParameter; + rsa_pms->client_version[0] = version->major; + rsa_pms->client_version[1] = version->minor; + crv = + NSC_GenerateRandom(0,&rsa_pms->random[0], sizeof(rsa_pms->random)); + break; + case nsc_bulk: + /* get the key, check for weak keys and repeat if found */ + do { + crv = NSC_GenerateRandom(0, (unsigned char *)buf, key_length); + } while (crv == CKR_OK && checkWeak && + pk11_IsWeakKey((unsigned char *)buf,key_type)); + break; + case nsc_param: + /* generate parameters */ + *buf = 0; + crv = nsc_parameter_gen(key_type,key); + break; + } + + if (crv != CKR_OK) { pk11_FreeObject(key); return crv; } + + /* Add the class, key_type, and value */ + crv = pk11_AddAttributeType(key,CKA_CLASS,&objclass,sizeof(CK_OBJECT_CLASS)); + if (crv != CKR_OK) { pk11_FreeObject(key); return crv; } + crv = pk11_AddAttributeType(key,CKA_KEY_TYPE,&key_type,sizeof(CK_KEY_TYPE)); + if (crv != CKR_OK) { pk11_FreeObject(key); return crv; } + if (key_length != 0) { + crv = pk11_AddAttributeType(key,CKA_VALUE,buf,key_length); + if (crv != CKR_OK) { pk11_FreeObject(key); return crv; } + } + + /* get the session */ + session = pk11_SessionFromHandle(hSession); + if (session == NULL) { + pk11_FreeObject(key); + return CKR_SESSION_HANDLE_INVALID; + } + + /* + * handle the base object stuff + */ + crv = pk11_handleObject(key,session); + pk11_FreeSession(session); + if (pk11_isTrue(key,CKA_SENSITIVE)) { + pk11_forceAttribute(key,CKA_ALWAYS_SENSITIVE,&cktrue,sizeof(CK_BBOOL)); + } + if (!pk11_isTrue(key,CKA_EXTRACTABLE)) { + pk11_forceAttribute(key,CKA_NEVER_EXTRACTABLE,&cktrue,sizeof(CK_BBOOL)); + } + + *phKey = key->handle; + pk11_FreeObject(key); + return crv; +} + + + +/* NSC_GenerateKeyPair generates a public-key/private-key pair, + * creating new key objects. */ +CK_RV NSC_GenerateKeyPair (CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pPublicKeyTemplate, + CK_ULONG ulPublicKeyAttributeCount, CK_ATTRIBUTE_PTR pPrivateKeyTemplate, + CK_ULONG ulPrivateKeyAttributeCount, CK_OBJECT_HANDLE_PTR phPublicKey, + CK_OBJECT_HANDLE_PTR phPrivateKey) +{ + PK11Object * publicKey,*privateKey; + PK11Session * session; + CK_KEY_TYPE key_type; + CK_RV crv = CKR_OK; + CK_BBOOL cktrue = CK_TRUE; + SECStatus rv; + CK_OBJECT_CLASS pubClass = CKO_PUBLIC_KEY; + CK_OBJECT_CLASS privClass = CKO_PRIVATE_KEY; + int i; + PK11Slot * slot = pk11_SlotFromSessionHandle(hSession); + + /* RSA */ + int public_modulus_bits = 0; + SECItem pubExp; + RSAPrivateKey * rsaPriv; + + /* DSA */ + PQGParams pqgParam; + DHParams dhParam; + DSAPrivateKey * dsaPriv; + + /* Diffie Hellman */ + int private_value_bits = 0; + DHPrivateKey * dhPriv; + + /* + * now lets create an object to hang the attributes off of + */ + publicKey = pk11_NewObject(slot); /* fill in the handle later */ + if (publicKey == NULL) { + return CKR_HOST_MEMORY; + } + + /* + * load the template values into the publicKey + */ + for (i=0; i < (int) ulPublicKeyAttributeCount; i++) { + if (pPublicKeyTemplate[i].type == CKA_MODULUS_BITS) { + public_modulus_bits = *(CK_ULONG *)pPublicKeyTemplate[i].pValue; + continue; + } + + crv = pk11_AddAttributeType(publicKey, + pk11_attr_expand(&pPublicKeyTemplate[i])); + if (crv != CKR_OK) break; + } + + if (crv != CKR_OK) { + pk11_FreeObject(publicKey); + return CKR_HOST_MEMORY; + } + + privateKey = pk11_NewObject(slot); /* fill in the handle later */ + if (privateKey == NULL) { + pk11_FreeObject(publicKey); + return CKR_HOST_MEMORY; + } + /* + * now load the private key template + */ + for (i=0; i < (int) ulPrivateKeyAttributeCount; i++) { + if (pPrivateKeyTemplate[i].type == CKA_VALUE_BITS) { + private_value_bits = *(CK_ULONG *)pPrivateKeyTemplate[i].pValue; + continue; + } + + crv = pk11_AddAttributeType(privateKey, + pk11_attr_expand(&pPrivateKeyTemplate[i])); + if (crv != CKR_OK) break; + } + + if (crv != CKR_OK) { + pk11_FreeObject(publicKey); + pk11_FreeObject(privateKey); + return CKR_HOST_MEMORY; + } + pk11_DeleteAttributeType(privateKey,CKA_CLASS); + pk11_DeleteAttributeType(privateKey,CKA_KEY_TYPE); + pk11_DeleteAttributeType(privateKey,CKA_VALUE); + pk11_DeleteAttributeType(publicKey,CKA_CLASS); + pk11_DeleteAttributeType(publicKey,CKA_KEY_TYPE); + pk11_DeleteAttributeType(publicKey,CKA_VALUE); + + /* Now Set up the parameters to generate the key (based on mechanism) */ + switch (pMechanism->mechanism) { + case CKM_RSA_PKCS_KEY_PAIR_GEN: + /* format the keys */ + pk11_DeleteAttributeType(publicKey,CKA_MODULUS); + pk11_DeleteAttributeType(privateKey,CKA_NETSCAPE_DB); + pk11_DeleteAttributeType(privateKey,CKA_MODULUS); + pk11_DeleteAttributeType(privateKey,CKA_PRIVATE_EXPONENT); + pk11_DeleteAttributeType(privateKey,CKA_PUBLIC_EXPONENT); + pk11_DeleteAttributeType(privateKey,CKA_PRIME_1); + pk11_DeleteAttributeType(privateKey,CKA_PRIME_2); + pk11_DeleteAttributeType(privateKey,CKA_EXPONENT_1); + pk11_DeleteAttributeType(privateKey,CKA_EXPONENT_2); + pk11_DeleteAttributeType(privateKey,CKA_COEFFICIENT); + key_type = CKK_RSA; + if (public_modulus_bits == 0) { + crv = CKR_TEMPLATE_INCOMPLETE; + break; + } + + /* extract the exponent */ + crv=pk11_Attribute2SSecItem(NULL,&pubExp,publicKey,CKA_PUBLIC_EXPONENT); + if (crv != CKR_OK) break; + crv = pk11_AddAttributeType(privateKey,CKA_PUBLIC_EXPONENT, + pk11_item_expand(&pubExp)); + if (crv != CKR_OK) { + PORT_Free(pubExp.data); + break; + } + + rsaPriv = RSA_NewKey(public_modulus_bits, &pubExp); + PORT_Free(pubExp.data); + if (rsaPriv == NULL) { + crv = CKR_DEVICE_ERROR; + break; + } + /* now fill in the RSA dependent paramenters in the public key */ + crv = pk11_AddAttributeType(publicKey,CKA_MODULUS, + pk11_item_expand(&rsaPriv->modulus)); + if (crv != CKR_OK) goto kpg_done; + /* now fill in the RSA dependent paramenters in the private key */ + crv = pk11_AddAttributeType(privateKey,CKA_NETSCAPE_DB, + pk11_item_expand(&rsaPriv->modulus)); + if (crv != CKR_OK) goto kpg_done; + crv = pk11_AddAttributeType(privateKey,CKA_MODULUS, + pk11_item_expand(&rsaPriv->modulus)); + if (crv != CKR_OK) goto kpg_done; + crv = pk11_AddAttributeType(privateKey,CKA_PRIVATE_EXPONENT, + pk11_item_expand(&rsaPriv->privateExponent)); + if (crv != CKR_OK) goto kpg_done; + crv = pk11_AddAttributeType(privateKey,CKA_PRIME_1, + pk11_item_expand(&rsaPriv->prime1)); + if (crv != CKR_OK) goto kpg_done; + crv = pk11_AddAttributeType(privateKey,CKA_PRIME_2, + pk11_item_expand(&rsaPriv->prime2)); + if (crv != CKR_OK) goto kpg_done; + crv = pk11_AddAttributeType(privateKey,CKA_EXPONENT_1, + pk11_item_expand(&rsaPriv->exponent1)); + if (crv != CKR_OK) goto kpg_done; + crv = pk11_AddAttributeType(privateKey,CKA_EXPONENT_2, + pk11_item_expand(&rsaPriv->exponent2)); + if (crv != CKR_OK) goto kpg_done; + crv = pk11_AddAttributeType(privateKey,CKA_COEFFICIENT, + pk11_item_expand(&rsaPriv->coefficient)); +kpg_done: + /* Should zeroize the contents first, since this func doesn't. */ + PORT_FreeArena(rsaPriv->arena, PR_TRUE); + break; + case CKM_DSA_KEY_PAIR_GEN: + pk11_DeleteAttributeType(publicKey,CKA_VALUE); + pk11_DeleteAttributeType(privateKey,CKA_NETSCAPE_DB); + pk11_DeleteAttributeType(privateKey,CKA_PRIME); + pk11_DeleteAttributeType(privateKey,CKA_SUBPRIME); + pk11_DeleteAttributeType(privateKey,CKA_BASE); + key_type = CKK_DSA; + + /* extract the necessary paramters and copy them to the private key */ + crv=pk11_Attribute2SSecItem(NULL,&pqgParam.prime,publicKey,CKA_PRIME); + if (crv != CKR_OK) break; + crv=pk11_Attribute2SSecItem(NULL,&pqgParam.subPrime,publicKey, + CKA_SUBPRIME); + if (crv != CKR_OK) { + PORT_Free(pqgParam.prime.data); + break; + } + crv=pk11_Attribute2SSecItem(NULL,&pqgParam.base,publicKey,CKA_BASE); + if (crv != CKR_OK) { + PORT_Free(pqgParam.prime.data); + PORT_Free(pqgParam.subPrime.data); + break; + } + crv = pk11_AddAttributeType(privateKey,CKA_PRIME, + pk11_item_expand(&pqgParam.prime)); + if (crv != CKR_OK) { + PORT_Free(pqgParam.prime.data); + PORT_Free(pqgParam.subPrime.data); + PORT_Free(pqgParam.base.data); + break; + } + crv = pk11_AddAttributeType(privateKey,CKA_SUBPRIME, + pk11_item_expand(&pqgParam.subPrime)); + if (crv != CKR_OK) { + PORT_Free(pqgParam.prime.data); + PORT_Free(pqgParam.subPrime.data); + PORT_Free(pqgParam.base.data); + break; + } + crv = pk11_AddAttributeType(privateKey,CKA_BASE, + pk11_item_expand(&pqgParam.base)); + if (crv != CKR_OK) { + PORT_Free(pqgParam.prime.data); + PORT_Free(pqgParam.subPrime.data); + PORT_Free(pqgParam.base.data); + break; + } + + /* Generate the key */ + rv = DSA_NewKey(&pqgParam, &dsaPriv); + + PORT_Free(pqgParam.prime.data); + PORT_Free(pqgParam.subPrime.data); + PORT_Free(pqgParam.base.data); + + if (rv != SECSuccess) { crv = CKR_DEVICE_ERROR; break; } + + /* store the generated key into the attributes */ + crv = pk11_AddAttributeType(publicKey,CKA_VALUE, + pk11_item_expand(&dsaPriv->publicValue)); + if (crv != CKR_OK) goto dsagn_done; + + /* now fill in the RSA dependent paramenters in the private key */ + crv = pk11_AddAttributeType(privateKey,CKA_NETSCAPE_DB, + pk11_item_expand(&dsaPriv->publicValue)); + if (crv != CKR_OK) goto dsagn_done; + crv = pk11_AddAttributeType(privateKey,CKA_VALUE, + pk11_item_expand(&dsaPriv->privateValue)); + +dsagn_done: + /* should zeroize, since this function doesn't. */ + PORT_FreeArena(dsaPriv->params.arena, PR_TRUE); + break; + + case CKM_DH_PKCS_KEY_PAIR_GEN: + pk11_DeleteAttributeType(privateKey,CKA_PRIME); + pk11_DeleteAttributeType(privateKey,CKA_BASE); + pk11_DeleteAttributeType(privateKey,CKA_VALUE); + key_type = CKK_DH; + + /* extract the necessary parameters and copy them to private keys */ + crv = pk11_Attribute2SSecItem(NULL, &dhParam.prime, publicKey, + CKA_PRIME); + if (crv != CKR_OK) break; + crv = pk11_Attribute2SSecItem(NULL, &dhParam.base, publicKey, CKA_BASE); + if (crv != CKR_OK) { + PORT_Free(dhParam.prime.data); + break; + } + crv = pk11_AddAttributeType(privateKey, CKA_PRIME, + pk11_item_expand(&dhParam.prime)); + if (crv != CKR_OK) { + PORT_Free(dhParam.prime.data); + PORT_Free(dhParam.base.data); + break; + } + crv = pk11_AddAttributeType(privateKey, CKA_BASE, + pk11_item_expand(&dhParam.base)); + if (crv != CKR_OK) goto dhgn_done; + + rv = DH_NewKey(&dhParam, &dhPriv); + PORT_Free(dhParam.prime.data); + PORT_Free(dhParam.base.data); + if (rv != SECSuccess) { + crv = CKR_DEVICE_ERROR; + break; + } + + crv=pk11_AddAttributeType(publicKey, CKA_VALUE, + pk11_item_expand(&dhPriv->publicValue)); + if (crv != CKR_OK) goto dhgn_done; + + crv=pk11_AddAttributeType(privateKey, CKA_VALUE, + pk11_item_expand(&dhPriv->privateValue)); + +dhgn_done: + /* should zeroize, since this function doesn't. */ + PORT_FreeArena(dhPriv->arena, PR_TRUE); + break; + + default: + crv = CKR_MECHANISM_INVALID; + } + + if (crv != CKR_OK) { + pk11_FreeObject(privateKey); + pk11_FreeObject(publicKey); + return crv; + } + + + /* Add the class, key_type The loop lets us check errors blow out + * on errors and clean up at the bottom */ + session = NULL; /* make pedtantic happy... session cannot leave the*/ + /* loop below NULL unless an error is set... */ + do { + crv = pk11_AddAttributeType(privateKey,CKA_CLASS,&privClass, + sizeof(CK_OBJECT_CLASS)); + if (crv != CKR_OK) break; + crv = pk11_AddAttributeType(publicKey,CKA_CLASS,&pubClass, + sizeof(CK_OBJECT_CLASS)); + if (crv != CKR_OK) break; + crv = pk11_AddAttributeType(privateKey,CKA_KEY_TYPE,&key_type, + sizeof(CK_KEY_TYPE)); + if (crv != CKR_OK) break; + crv = pk11_AddAttributeType(publicKey,CKA_KEY_TYPE,&key_type, + sizeof(CK_KEY_TYPE)); + if (crv != CKR_OK) break; + session = pk11_SessionFromHandle(hSession); + if (session == NULL) crv = CKR_SESSION_HANDLE_INVALID; + } while (0); + + if (crv != CKR_OK) { + pk11_FreeObject(privateKey); + pk11_FreeObject(publicKey); + return crv; + } + + /* + * handle the base object cleanup for the public Key + */ + crv = pk11_handleObject(privateKey,session); + if (crv != CKR_OK) { + pk11_FreeSession(session); + pk11_FreeObject(privateKey); + pk11_FreeObject(publicKey); + return crv; + } + + /* + * handle the base object cleanup for the private Key + * If we have any problems, we destroy the public Key we've + * created and linked. + */ + crv = pk11_handleObject(publicKey,session); + pk11_FreeSession(session); + if (crv != CKR_OK) { + pk11_FreeObject(publicKey); + NSC_DestroyObject(hSession,privateKey->handle); + pk11_FreeObject(privateKey); + return crv; + } + if (pk11_isTrue(privateKey,CKA_SENSITIVE)) { + pk11_forceAttribute(privateKey,CKA_ALWAYS_SENSITIVE, + &cktrue,sizeof(CK_BBOOL)); + } + if (pk11_isTrue(publicKey,CKA_SENSITIVE)) { + pk11_forceAttribute(publicKey,CKA_ALWAYS_SENSITIVE, + &cktrue,sizeof(CK_BBOOL)); + } + if (!pk11_isTrue(privateKey,CKA_EXTRACTABLE)) { + pk11_forceAttribute(privateKey,CKA_NEVER_EXTRACTABLE, + &cktrue,sizeof(CK_BBOOL)); + } + if (!pk11_isTrue(publicKey,CKA_EXTRACTABLE)) { + pk11_forceAttribute(publicKey,CKA_NEVER_EXTRACTABLE, + &cktrue,sizeof(CK_BBOOL)); + } + *phPrivateKey = privateKey->handle; + *phPublicKey = publicKey->handle; + pk11_FreeObject(publicKey); + pk11_FreeObject(privateKey); + + return CKR_OK; +} + +static SECItem *pk11_PackagePrivateKey(PK11Object *key) +{ + NSSLOWKEYPrivateKey *lk = NULL; + NSSLOWKEYPrivateKeyInfo *pki = NULL; + PK11Attribute *attribute = NULL; + PLArenaPool *arena = NULL; + SECOidTag algorithm = SEC_OID_UNKNOWN; + void *dummy, *param = NULL; + SECStatus rv = SECSuccess; + SECItem *encodedKey = NULL; + + if(!key) { + return NULL; + } + + attribute = pk11_FindAttribute(key, CKA_KEY_TYPE); + if(!attribute) { + return NULL; + } + + lk = pk11_GetPrivKey(key, *(CK_KEY_TYPE *)attribute->attrib.pValue); + pk11_FreeAttribute(attribute); + if(!lk) { + return NULL; + } + + arena = PORT_NewArena(2048); /* XXX different size? */ + if(!arena) { + rv = SECFailure; + goto loser; + } + + pki = (NSSLOWKEYPrivateKeyInfo*)PORT_ArenaZAlloc(arena, + sizeof(NSSLOWKEYPrivateKeyInfo)); + if(!pki) { + rv = SECFailure; + goto loser; + } + pki->arena = arena; + + param = NULL; + switch(lk->keyType) { + case NSSLOWKEYRSAKey: + prepare_low_rsa_priv_key_for_asn1(lk); + dummy = SEC_ASN1EncodeItem(arena, &pki->privateKey, lk, + nsslowkey_RSAPrivateKeyTemplate); + algorithm = SEC_OID_PKCS1_RSA_ENCRYPTION; + break; + case NSSLOWKEYDSAKey: + prepare_low_dsa_priv_key_export_for_asn1(lk); + dummy = SEC_ASN1EncodeItem(arena, &pki->privateKey, lk, + nsslowkey_DSAPrivateKeyExportTemplate); + prepare_low_pqg_params_for_asn1(&lk->u.dsa.params); + param = SEC_ASN1EncodeItem(NULL, NULL, &(lk->u.dsa.params), + nsslowkey_PQGParamsTemplate); + algorithm = SEC_OID_ANSIX9_DSA_SIGNATURE; + break; + case NSSLOWKEYDHKey: + default: + dummy = NULL; + break; + } + + if(!dummy || ((lk->keyType == NSSLOWKEYDSAKey) && !param)) { + goto loser; + } + + rv = SECOID_SetAlgorithmID(arena, &pki->algorithm, algorithm, + (SECItem*)param); + if(rv != SECSuccess) { + rv = SECFailure; + goto loser; + } + + dummy = SEC_ASN1EncodeInteger(arena, &pki->version, + NSSLOWKEY_PRIVATE_KEY_INFO_VERSION); + if(!dummy) { + rv = SECFailure; + goto loser; + } + + encodedKey = SEC_ASN1EncodeItem(NULL, NULL, pki, + nsslowkey_PrivateKeyInfoTemplate); + +loser: + if(arena) { + PORT_FreeArena(arena, PR_TRUE); + } + + if(lk && (lk != key->objectInfo)) { + nsslowkey_DestroyPrivateKey(lk); + } + + if(param) { + SECITEM_ZfreeItem((SECItem*)param, PR_TRUE); + } + + if(rv != SECSuccess) { + return NULL; + } + + return encodedKey; +} + +/* it doesn't matter yet, since we colapse error conditions in the + * level above, but we really should map those few key error differences */ +CK_RV pk11_mapWrap(CK_RV crv) { return crv; } + +/* NSC_WrapKey wraps (i.e., encrypts) a key. */ +CK_RV NSC_WrapKey(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hWrappingKey, + CK_OBJECT_HANDLE hKey, CK_BYTE_PTR pWrappedKey, + CK_ULONG_PTR pulWrappedKeyLen) +{ + PK11Session *session; + PK11Attribute *attribute; + PK11Object *key; + CK_RV crv; + PRBool isLynks = PR_FALSE; + CK_ULONG len = 0; + + session = pk11_SessionFromHandle(hSession); + if (session == NULL) { + return CKR_SESSION_HANDLE_INVALID; + } + + key = pk11_ObjectFromHandle(hKey,session); + pk11_FreeSession(session); + if (key == NULL) { + return CKR_KEY_HANDLE_INVALID; + } + + switch(key->objclass) { + case CKO_SECRET_KEY: + attribute = pk11_FindAttribute(key,CKA_VALUE); + + if (attribute == NULL) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + if (pMechanism->mechanism == CKM_KEY_WRAP_LYNKS) { + isLynks = PR_TRUE; + pMechanism->mechanism = CKM_DES_ECB; + len = *pulWrappedKeyLen; + } + + crv = pk11_CryptInit(hSession, pMechanism, hWrappingKey, + CKA_WRAP, PK11_ENCRYPT, PR_TRUE); + if (crv != CKR_OK) { + pk11_FreeAttribute(attribute); + break; + } + crv = NSC_Encrypt(hSession, (CK_BYTE_PTR)attribute->attrib.pValue, + attribute->attrib.ulValueLen,pWrappedKey,pulWrappedKeyLen); + + if (isLynks && (crv == CKR_OK)) { + unsigned char buf[2]; + crv = pk11_calcLynxChecksum(hSession,hWrappingKey,buf, + (unsigned char*)attribute->attrib.pValue, + attribute->attrib.ulValueLen); + if (len >= 10) { + pWrappedKey[8] = buf[0]; + pWrappedKey[9] = buf[1]; + *pulWrappedKeyLen = 10; + } + } + pk11_FreeAttribute(attribute); + break; + + case CKO_PRIVATE_KEY: + { + SECItem *bpki = pk11_PackagePrivateKey(key); + + if(!bpki) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + + crv = pk11_CryptInit(hSession, pMechanism, hWrappingKey, + CKA_WRAP, PK11_ENCRYPT, PR_TRUE); + if(crv != CKR_OK) { + SECITEM_ZfreeItem(bpki, PR_TRUE); + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + + crv = NSC_Encrypt(hSession, bpki->data, bpki->len, + pWrappedKey, pulWrappedKeyLen); + SECITEM_ZfreeItem(bpki, PR_TRUE); + break; + } + + default: + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + pk11_FreeObject(key); + + return pk11_mapWrap(crv); +} + +/* + * import a pprivate key info into the desired slot + */ +static SECStatus +pk11_unwrapPrivateKey(PK11Object *key, SECItem *bpki) +{ + CK_BBOOL cktrue = CK_TRUE; + CK_KEY_TYPE keyType = CKK_RSA; + SECStatus rv = SECFailure; + const SEC_ASN1Template *keyTemplate, *paramTemplate; + void *paramDest = NULL; + PLArenaPool *arena; + NSSLOWKEYPrivateKey *lpk = NULL; + NSSLOWKEYPrivateKeyInfo *pki = NULL; + SECItem *ck_id = NULL; + CK_RV crv = CKR_KEY_TYPE_INCONSISTENT; + + arena = PORT_NewArena(2048); + if(!arena) { + return SECFailure; + } + + pki = (NSSLOWKEYPrivateKeyInfo*)PORT_ArenaZAlloc(arena, + sizeof(NSSLOWKEYPrivateKeyInfo)); + if(!pki) { + PORT_FreeArena(arena, PR_TRUE); + return SECFailure; + } + + if(SEC_ASN1DecodeItem(arena, pki, nsslowkey_PrivateKeyInfoTemplate, bpki) + != SECSuccess) { + PORT_FreeArena(arena, PR_FALSE); + return SECFailure; + } + + lpk = (NSSLOWKEYPrivateKey *)PORT_ArenaZAlloc(arena, + sizeof(NSSLOWKEYPrivateKey)); + if(lpk == NULL) { + goto loser; + } + lpk->arena = arena; + + switch(SECOID_GetAlgorithmTag(&pki->algorithm)) { + case SEC_OID_PKCS1_RSA_ENCRYPTION: + keyTemplate = nsslowkey_RSAPrivateKeyTemplate; + paramTemplate = NULL; + paramDest = NULL; + lpk->keyType = NSSLOWKEYRSAKey; + prepare_low_rsa_priv_key_for_asn1(lpk); + break; + case SEC_OID_ANSIX9_DSA_SIGNATURE: + keyTemplate = nsslowkey_DSAPrivateKeyExportTemplate; + paramTemplate = nsslowkey_PQGParamsTemplate; + paramDest = &(lpk->u.dsa.params); + lpk->keyType = NSSLOWKEYDSAKey; + prepare_low_dsa_priv_key_export_for_asn1(lpk); + prepare_low_pqg_params_for_asn1(&lpk->u.dsa.params); + break; + /* case NSSLOWKEYDHKey: */ + default: + keyTemplate = NULL; + paramTemplate = NULL; + paramDest = NULL; + break; + } + + if(!keyTemplate) { + goto loser; + } + + /* decode the private key and any algorithm parameters */ + rv = SEC_ASN1DecodeItem(arena, lpk, keyTemplate, &pki->privateKey); + if(rv != SECSuccess) { + goto loser; + } + if(paramDest && paramTemplate) { + rv = SEC_ASN1DecodeItem(arena, paramDest, paramTemplate, + &(pki->algorithm.parameters)); + if(rv != SECSuccess) { + goto loser; + } + } + + rv = SECFailure; + + switch (lpk->keyType) { + case NSSLOWKEYRSAKey: + keyType = CKK_RSA; + if(pk11_hasAttribute(key, CKA_NETSCAPE_DB)) { + pk11_DeleteAttributeType(key, CKA_NETSCAPE_DB); + } + crv = pk11_AddAttributeType(key, CKA_KEY_TYPE, &keyType, + sizeof(keyType)); + if(crv != CKR_OK) break; + crv = pk11_AddAttributeType(key, CKA_UNWRAP, &cktrue, + sizeof(CK_BBOOL)); + if(crv != CKR_OK) break; + crv = pk11_AddAttributeType(key, CKA_DECRYPT, &cktrue, + sizeof(CK_BBOOL)); + if(crv != CKR_OK) break; + crv = pk11_AddAttributeType(key, CKA_SIGN, &cktrue, + sizeof(CK_BBOOL)); + if(crv != CKR_OK) break; + crv = pk11_AddAttributeType(key, CKA_SIGN_RECOVER, &cktrue, + sizeof(CK_BBOOL)); + if(crv != CKR_OK) break; + crv = pk11_AddAttributeType(key, CKA_MODULUS, + pk11_item_expand(&lpk->u.rsa.modulus)); + if(crv != CKR_OK) break; + crv = pk11_AddAttributeType(key, CKA_PUBLIC_EXPONENT, + pk11_item_expand(&lpk->u.rsa.publicExponent)); + if(crv != CKR_OK) break; + crv = pk11_AddAttributeType(key, CKA_PRIVATE_EXPONENT, + pk11_item_expand(&lpk->u.rsa.privateExponent)); + if(crv != CKR_OK) break; + crv = pk11_AddAttributeType(key, CKA_PRIME_1, + pk11_item_expand(&lpk->u.rsa.prime1)); + if(crv != CKR_OK) break; + crv = pk11_AddAttributeType(key, CKA_PRIME_2, + pk11_item_expand(&lpk->u.rsa.prime2)); + if(crv != CKR_OK) break; + crv = pk11_AddAttributeType(key, CKA_EXPONENT_1, + pk11_item_expand(&lpk->u.rsa.exponent1)); + if(crv != CKR_OK) break; + crv = pk11_AddAttributeType(key, CKA_EXPONENT_2, + pk11_item_expand(&lpk->u.rsa.exponent2)); + if(crv != CKR_OK) break; + crv = pk11_AddAttributeType(key, CKA_COEFFICIENT, + pk11_item_expand(&lpk->u.rsa.coefficient)); + break; + case NSSLOWKEYDSAKey: + keyType = CKK_DSA; + crv = (pk11_hasAttribute(key, CKA_NETSCAPE_DB)) ? CKR_OK : + CKR_KEY_TYPE_INCONSISTENT; + if(crv != CKR_OK) break; + crv = pk11_AddAttributeType(key, CKA_KEY_TYPE, &keyType, + sizeof(keyType)); + if(crv != CKR_OK) break; + crv = pk11_AddAttributeType(key, CKA_SIGN, &cktrue, + sizeof(CK_BBOOL)); + if(crv != CKR_OK) break; + crv = pk11_AddAttributeType(key, CKA_SIGN_RECOVER, &cktrue, + sizeof(CK_BBOOL)); + if(crv != CKR_OK) break; + crv = pk11_AddAttributeType(key, CKA_PRIME, + pk11_item_expand(&lpk->u.dsa.params.prime)); + if(crv != CKR_OK) break; + crv = pk11_AddAttributeType(key, CKA_SUBPRIME, + pk11_item_expand(&lpk->u.dsa.params.subPrime)); + if(crv != CKR_OK) break; + crv = pk11_AddAttributeType(key, CKA_BASE, + pk11_item_expand(&lpk->u.dsa.params.base)); + if(crv != CKR_OK) break; + crv = pk11_AddAttributeType(key, CKA_VALUE, + pk11_item_expand(&lpk->u.dsa.privateValue)); + if(crv != CKR_OK) break; + break; +#ifdef notdef + case NSSLOWKEYDHKey: + template = dhTemplate; + templateCount = sizeof(dhTemplate)/sizeof(CK_ATTRIBUTE); + keyType = CKK_DH; + break; +#endif + /* what about fortezza??? */ + default: + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + +loser: + if(ck_id) { + SECITEM_ZfreeItem(ck_id, PR_TRUE); + } + + if(lpk) { + nsslowkey_DestroyPrivateKey(lpk); + } + + if(crv != CKR_OK) { + return SECFailure; + } + + return SECSuccess; +} + + +/* NSC_UnwrapKey unwraps (decrypts) a wrapped key, creating a new key object. */ +CK_RV NSC_UnwrapKey(CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hUnwrappingKey, + CK_BYTE_PTR pWrappedKey, CK_ULONG ulWrappedKeyLen, + CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, + CK_OBJECT_HANDLE_PTR phKey) +{ + PK11Object *key = NULL; + PK11Session *session; + int key_length = 0; + unsigned char * buf = NULL; + CK_RV crv = CKR_OK; + int i; + CK_ULONG bsize = ulWrappedKeyLen; + PK11Slot *slot = pk11_SlotFromSessionHandle(hSession); + SECItem bpki; + CK_OBJECT_CLASS target_type = CKO_SECRET_KEY; + PRBool isLynks = PR_FALSE; + + /* + * now lets create an object to hang the attributes off of + */ + key = pk11_NewObject(slot); /* fill in the handle later */ + if (key == NULL) { + return CKR_HOST_MEMORY; + } + + /* + * load the template values into the object + */ + for (i=0; i < (int) ulAttributeCount; i++) { + if (pTemplate[i].type == CKA_VALUE_LEN) { + key_length = *(CK_ULONG *)pTemplate[i].pValue; + continue; + } + if (pTemplate[i].type == CKA_CLASS) { + target_type = *(CK_OBJECT_CLASS *)pTemplate[i].pValue; + } + crv = pk11_AddAttributeType(key,pk11_attr_expand(&pTemplate[i])); + if (crv != CKR_OK) break; + } + if (crv != CKR_OK) { + pk11_FreeObject(key); + return crv; + } + + /* LYNKS is a special key wrapping mechanism */ + if (pMechanism->mechanism == CKM_KEY_WRAP_LYNKS) { + isLynks = PR_TRUE; + pMechanism->mechanism = CKM_DES_ECB; + ulWrappedKeyLen -= 2; /* don't decrypt the checksum */ + } + + crv = pk11_CryptInit(hSession,pMechanism,hUnwrappingKey,CKA_UNWRAP, + PK11_DECRYPT, PR_FALSE); + if (crv != CKR_OK) { + pk11_FreeObject(key); + return pk11_mapWrap(crv); + } + + /* allocate the buffer to decrypt into + * this assumes the unwrapped key is never larger than the + * wrapped key. For all the mechanisms we support this is true */ + buf = (unsigned char *)PORT_Alloc( ulWrappedKeyLen); + bsize = ulWrappedKeyLen; + + crv = NSC_Decrypt(hSession, pWrappedKey, ulWrappedKeyLen, buf, &bsize); + if (crv != CKR_OK) { + pk11_FreeObject(key); + PORT_Free(buf); + return pk11_mapWrap(crv); + } + + switch(target_type) { + case CKO_SECRET_KEY: + if (!pk11_hasAttribute(key,CKA_KEY_TYPE)) { + crv = CKR_TEMPLATE_INCOMPLETE; + break; + } + + /* verify the Lynx checksum */ + if (isLynks) { + unsigned char checkSum[2]; + crv = pk11_calcLynxChecksum(hSession,hUnwrappingKey,checkSum, + buf,bsize); + if (crv != CKR_OK) break; + if ((ulWrappedKeyLen != 8) || (pWrappedKey[8] != checkSum[0]) + || (pWrappedKey[9] != checkSum[1])) { + crv = CKR_WRAPPED_KEY_INVALID; + break; + } + } + + if(key_length == 0) { + key_length = bsize; + } + if (key_length > MAX_KEY_LEN) { + crv = CKR_TEMPLATE_INCONSISTENT; + break; + } + + /* add the value */ + crv = pk11_AddAttributeType(key,CKA_VALUE,buf,key_length); + break; + case CKO_PRIVATE_KEY: + bpki.data = (unsigned char *)buf; + bpki.len = bsize; + crv = CKR_OK; + if(pk11_unwrapPrivateKey(key, &bpki) != SECSuccess) { + crv = CKR_TEMPLATE_INCOMPLETE; + } + break; + default: + crv = CKR_TEMPLATE_INCONSISTENT; + break; + } + + PORT_ZFree(buf, bsize); + if (crv != CKR_OK) { pk11_FreeObject(key); return crv; } + + /* get the session */ + session = pk11_SessionFromHandle(hSession); + if (session == NULL) { + pk11_FreeObject(key); + return CKR_SESSION_HANDLE_INVALID; + } + + /* + * handle the base object stuff + */ + crv = pk11_handleObject(key,session); + *phKey = key->handle; + pk11_FreeSession(session); + pk11_FreeObject(key); + + return crv; + +} + +/* + * The SSL key gen mechanism create's lots of keys. This function handles the + * details of each of these key creation. + */ +static CK_RV +pk11_buildSSLKey(CK_SESSION_HANDLE hSession, PK11Object *baseKey, + PRBool isMacKey, unsigned char *keyBlock, unsigned int keySize, + CK_OBJECT_HANDLE *keyHandle) +{ + PK11Object *key; + PK11Session *session; + CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; + CK_BBOOL cktrue = CK_TRUE; + CK_BBOOL ckfalse = CK_FALSE; + CK_RV crv = CKR_HOST_MEMORY; + + /* + * now lets create an object to hang the attributes off of + */ + *keyHandle = CK_INVALID_HANDLE; + key = pk11_NewObject(baseKey->slot); + if (key == NULL) return CKR_HOST_MEMORY; + pk11_narrowToSessionObject(key)->wasDerived = PR_TRUE; + + crv = pk11_CopyObject(key,baseKey); + if (crv != CKR_OK) goto loser; + if (isMacKey) { + crv = pk11_forceAttribute(key,CKA_KEY_TYPE,&keyType,sizeof(keyType)); + if (crv != CKR_OK) goto loser; + crv = pk11_forceAttribute(key,CKA_DERIVE,&cktrue,sizeof(CK_BBOOL)); + if (crv != CKR_OK) goto loser; + crv = pk11_forceAttribute(key,CKA_ENCRYPT,&ckfalse,sizeof(CK_BBOOL)); + if (crv != CKR_OK) goto loser; + crv = pk11_forceAttribute(key,CKA_DECRYPT,&ckfalse,sizeof(CK_BBOOL)); + if (crv != CKR_OK) goto loser; + crv = pk11_forceAttribute(key,CKA_SIGN,&cktrue,sizeof(CK_BBOOL)); + if (crv != CKR_OK) goto loser; + crv = pk11_forceAttribute(key,CKA_VERIFY,&cktrue,sizeof(CK_BBOOL)); + if (crv != CKR_OK) goto loser; + crv = pk11_forceAttribute(key,CKA_WRAP,&ckfalse,sizeof(CK_BBOOL)); + if (crv != CKR_OK) goto loser; + crv = pk11_forceAttribute(key,CKA_UNWRAP,&ckfalse,sizeof(CK_BBOOL)); + if (crv != CKR_OK) goto loser; + } + crv = pk11_forceAttribute(key,CKA_VALUE,keyBlock,keySize); + if (crv != CKR_OK) goto loser; + + /* get the session */ + crv = CKR_HOST_MEMORY; + session = pk11_SessionFromHandle(hSession); + if (session == NULL) { goto loser; } + + crv = pk11_handleObject(key,session); + pk11_FreeSession(session); + *keyHandle = key->handle; +loser: + if (key) pk11_FreeObject(key); + return crv; +} + +/* + * if there is an error, we need to free the keys we already created in SSL + * This is the routine that will do it.. + */ +static void +pk11_freeSSLKeys(CK_SESSION_HANDLE session, + CK_SSL3_KEY_MAT_OUT *returnedMaterial ) { + if (returnedMaterial->hClientMacSecret != CK_INVALID_HANDLE) { + NSC_DestroyObject(session,returnedMaterial->hClientMacSecret); + } + if (returnedMaterial->hServerMacSecret != CK_INVALID_HANDLE) { + NSC_DestroyObject(session, returnedMaterial->hServerMacSecret); + } + if (returnedMaterial->hClientKey != CK_INVALID_HANDLE) { + NSC_DestroyObject(session, returnedMaterial->hClientKey); + } + if (returnedMaterial->hServerKey != CK_INVALID_HANDLE) { + NSC_DestroyObject(session, returnedMaterial->hServerKey); + } +} + +/* + * when deriving from sensitive and extractable keys, we need to preserve some + * of the semantics in the derived key. This helper routine maintains these + * semantics. + */ +static CK_RV +pk11_DeriveSensitiveCheck(PK11Object *baseKey,PK11Object *destKey) { + PRBool hasSensitive; + PRBool sensitive = PR_FALSE; + PRBool hasExtractable; + PRBool extractable = PR_TRUE; + CK_RV crv = CKR_OK; + PK11Attribute *att; + + hasSensitive = PR_FALSE; + att = pk11_FindAttribute(destKey,CKA_SENSITIVE); + if (att) { + hasSensitive = PR_TRUE; + sensitive = (PRBool) *(CK_BBOOL *)att->attrib.pValue; + pk11_FreeAttribute(att); + } + + hasExtractable = PR_FALSE; + att = pk11_FindAttribute(destKey,CKA_EXTRACTABLE); + if (att) { + hasExtractable = PR_TRUE; + extractable = (PRBool) *(CK_BBOOL *)att->attrib.pValue; + pk11_FreeAttribute(att); + } + + + /* don't make a key more accessible */ + if (pk11_isTrue(baseKey,CKA_SENSITIVE) && hasSensitive && + (sensitive == PR_FALSE)) { + return CKR_KEY_FUNCTION_NOT_PERMITTED; + } + if (!pk11_isTrue(baseKey,CKA_EXTRACTABLE) && hasExtractable && + (extractable == PR_TRUE)) { + return CKR_KEY_FUNCTION_NOT_PERMITTED; + } + + /* inherit parent's sensitivity */ + if (!hasSensitive) { + att = pk11_FindAttribute(baseKey,CKA_SENSITIVE); + if (att == NULL) return CKR_KEY_TYPE_INCONSISTENT; + crv = pk11_defaultAttribute(destKey,pk11_attr_expand(&att->attrib)); + pk11_FreeAttribute(att); + if (crv != CKR_OK) return crv; + } + if (!hasExtractable) { + att = pk11_FindAttribute(baseKey,CKA_EXTRACTABLE); + if (att == NULL) return CKR_KEY_TYPE_INCONSISTENT; + crv = pk11_defaultAttribute(destKey,pk11_attr_expand(&att->attrib)); + pk11_FreeAttribute(att); + if (crv != CKR_OK) return crv; + } + + /* we should inherit the parent's always extractable/ never sensitive info, + * but handleObject always forces this attributes, so we would need to do + * something special. */ + return CKR_OK; +} + +/* + * make known fixed PKCS #11 key types to their sizes in bytes + */ +static unsigned long +pk11_MapKeySize(CK_KEY_TYPE keyType) { + switch (keyType) { + case CKK_CDMF: + return 8; + case CKK_DES: + return 8; + case CKK_DES2: + return 16; + case CKK_DES3: + return 24; + /* IDEA and CAST need to be added */ + default: + break; + } + return 0; +} + +#define PHASH_STATE_MAX_LEN 20 + +/* TLS P_hash function */ +static SECStatus +pk11_P_hash(HASH_HashType hashType, const SECItem *secret, const char *label, + SECItem *seed, SECItem *result) +{ + unsigned char state[PHASH_STATE_MAX_LEN]; + unsigned char outbuf[PHASH_STATE_MAX_LEN]; + unsigned int state_len = 0, label_len = 0, outbuf_len = 0, chunk_size; + unsigned int remaining; + unsigned char *res; + SECStatus status; + HMACContext *cx; + SECStatus rv = SECFailure; + const SECHashObject *hashObj = &SECRawHashObjects[hashType]; + + PORT_Assert((secret != NULL) && (secret->data != NULL || !secret->len)); + PORT_Assert((seed != NULL) && (seed->data != NULL)); + PORT_Assert((result != NULL) && (result->data != NULL)); + + remaining = result->len; + res = result->data; + + if (label != NULL) + label_len = PORT_Strlen(label); + + cx = HMAC_Create(hashObj, secret->data, secret->len); + if (cx == NULL) + goto loser; + + /* initialize the state = A(1) = HMAC_hash(secret, seed) */ + HMAC_Begin(cx); + HMAC_Update(cx, (unsigned char *)label, label_len); + HMAC_Update(cx, seed->data, seed->len); + status = HMAC_Finish(cx, state, &state_len, PHASH_STATE_MAX_LEN); + if (status != SECSuccess) + goto loser; + + /* generate a block at a time until we're done */ + while (remaining > 0) { + + HMAC_Begin(cx); + HMAC_Update(cx, state, state_len); + if (label_len) + HMAC_Update(cx, (unsigned char *)label, label_len); + HMAC_Update(cx, seed->data, seed->len); + status = HMAC_Finish(cx, outbuf, &outbuf_len, PHASH_STATE_MAX_LEN); + if (status != SECSuccess) + goto loser; + + /* Update the state = A(i) = HMAC_hash(secret, A(i-1)) */ + HMAC_Begin(cx); + HMAC_Update(cx, state, state_len); + status = HMAC_Finish(cx, state, &state_len, PHASH_STATE_MAX_LEN); + if (status != SECSuccess) + goto loser; + + chunk_size = PR_MIN(outbuf_len, remaining); + PORT_Memcpy(res, &outbuf, chunk_size); + res += chunk_size; + remaining -= chunk_size; + } + + rv = SECSuccess; + +loser: + /* if (cx) HMAC_Destroy(cx); */ + /* clear out state so it's not left on the stack */ + if (cx) HMAC_Destroy(cx); + PORT_Memset(state, 0, sizeof(state)); + PORT_Memset(outbuf, 0, sizeof(outbuf)); + return rv; +} + +static SECStatus +pk11_PRF(const SECItem *secret, const char *label, SECItem *seed, + SECItem *result) +{ + SECStatus rv = SECFailure, status; + unsigned int i; + SECItem tmp = { siBuffer, NULL, 0}; + SECItem S1; + SECItem S2; + + PORT_Assert((secret != NULL) && (secret->data != NULL || !secret->len)); + PORT_Assert((seed != NULL) && (seed->data != NULL)); + PORT_Assert((result != NULL) && (result->data != NULL)); + + S1.type = siBuffer; + S1.len = (secret->len / 2) + (secret->len & 1); + S1.data = secret->data; + + S2.type = siBuffer; + S2.len = S1.len; + S2.data = secret->data + (secret->len - S2.len); + + tmp.data = (unsigned char*)PORT_Alloc(result->len); + if (tmp.data == NULL) + goto loser; + tmp.len = result->len; + + status = pk11_P_hash(HASH_AlgMD5, &S1, label, seed, result); + if (status != SECSuccess) + goto loser; + + status = pk11_P_hash(HASH_AlgSHA1, &S2, label, seed, &tmp); + if (status != SECSuccess) + goto loser; + + for (i = 0; i < result->len; i++) + result->data[i] ^= tmp.data[i]; + + rv = SECSuccess; + +loser: + if (tmp.data != NULL) + PORT_ZFree(tmp.data, tmp.len); + return rv; +} + +/* + * SSL Key generation given pre master secret + */ +#define NUM_MIXERS 9 +static const char * const mixers[NUM_MIXERS] = { + "A", + "BB", + "CCC", + "DDDD", + "EEEEE", + "FFFFFF", + "GGGGGGG", + "HHHHHHHH", + "IIIIIIIII" }; +#define SSL3_PMS_LENGTH 48 +#define SSL3_MASTER_SECRET_LENGTH 48 + + +/* NSC_DeriveKey derives a key from a base key, creating a new key object. */ +CK_RV NSC_DeriveKey( CK_SESSION_HANDLE hSession, + CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hBaseKey, + CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, + CK_OBJECT_HANDLE_PTR phKey) +{ + PK11Session * session; + PK11Slot * slot = pk11_SlotFromSessionHandle(hSession); + PK11Object * key; + PK11Object * sourceKey; + PK11Attribute * att; + PK11Attribute * att2; + unsigned char * buf; + SHA1Context * sha; + MD5Context * md5; + MD2Context * md2; + CK_ULONG macSize; + CK_ULONG tmpKeySize; + CK_ULONG IVSize; + CK_ULONG keySize = 0; + CK_RV crv = CKR_OK; + CK_BBOOL cktrue = CK_TRUE; + CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; + CK_OBJECT_CLASS classType = CKO_SECRET_KEY; + CK_KEY_DERIVATION_STRING_DATA *stringPtr; + PRBool isTLS = PR_FALSE; + PRBool isDH = PR_FALSE; + SECStatus rv; + int i; + unsigned int outLen; + unsigned char sha_out[SHA1_LENGTH]; + unsigned char key_block[NUM_MIXERS * MD5_LENGTH]; + unsigned char key_block2[MD5_LENGTH]; + + /* + * now lets create an object to hang the attributes off of + */ + if (phKey) *phKey = CK_INVALID_HANDLE; + + key = pk11_NewObject(slot); /* fill in the handle later */ + if (key == NULL) { + return CKR_HOST_MEMORY; + } + + /* + * load the template values into the object + */ + for (i=0; i < (int) ulAttributeCount; i++) { + crv = pk11_AddAttributeType(key,pk11_attr_expand(&pTemplate[i])); + if (crv != CKR_OK) break; + + if (pTemplate[i].type == CKA_KEY_TYPE) { + keyType = *(CK_KEY_TYPE *)pTemplate[i].pValue; + } + if (pTemplate[i].type == CKA_VALUE_LEN) { + keySize = *(CK_ULONG *)pTemplate[i].pValue; + } + } + if (crv != CKR_OK) { pk11_FreeObject(key); return crv; } + + if (keySize == 0) { + keySize = pk11_MapKeySize(keyType); + } + + /* Derive can only create SECRET KEY's currently... */ + classType = CKO_SECRET_KEY; + crv = pk11_forceAttribute (key,CKA_CLASS,&classType,sizeof(classType)); + if (crv != CKR_OK) { + pk11_FreeObject(key); + return crv; + } + + /* look up the base key we're deriving with */ + session = pk11_SessionFromHandle(hSession); + if (session == NULL) { + pk11_FreeObject(key); + return CKR_SESSION_HANDLE_INVALID; + } + + sourceKey = pk11_ObjectFromHandle(hBaseKey,session); + pk11_FreeSession(session); + if (sourceKey == NULL) { + pk11_FreeObject(key); + return CKR_KEY_HANDLE_INVALID; + } + + /* don't use key derive to expose sensitive keys */ + crv = pk11_DeriveSensitiveCheck(sourceKey,key); + if (crv != CKR_OK) { + pk11_FreeObject(key); + pk11_FreeObject(sourceKey); + return crv; + } + + /* get the value of the base key */ + att = pk11_FindAttribute(sourceKey,CKA_VALUE); + if (att == NULL) { + pk11_FreeObject(key); + pk11_FreeObject(sourceKey); + return CKR_KEY_HANDLE_INVALID; + } + + switch (pMechanism->mechanism) { + /* + * generate the master secret + */ + case CKM_TLS_MASTER_KEY_DERIVE: + case CKM_TLS_MASTER_KEY_DERIVE_DH: + isTLS = PR_TRUE; + /* fall thru */ + case CKM_SSL3_MASTER_KEY_DERIVE: + case CKM_SSL3_MASTER_KEY_DERIVE_DH: + { + CK_SSL3_MASTER_KEY_DERIVE_PARAMS *ssl3_master; + SSL3RSAPreMasterSecret *rsa_pms; + if ((pMechanism->mechanism == CKM_SSL3_MASTER_KEY_DERIVE_DH) || + (pMechanism->mechanism == CKM_TLS_MASTER_KEY_DERIVE_DH)) + isDH = PR_TRUE; + + /* first do the consistancy checks */ + if (!isDH && (att->attrib.ulValueLen != SSL3_PMS_LENGTH)) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + att2 = pk11_FindAttribute(sourceKey,CKA_KEY_TYPE); + if ((att2 == NULL) || (*(CK_KEY_TYPE *)att2->attrib.pValue != + CKK_GENERIC_SECRET)) { + if (att2) pk11_FreeAttribute(att2); + crv = CKR_KEY_FUNCTION_NOT_PERMITTED; + break; + } + pk11_FreeAttribute(att2); + if (keyType != CKK_GENERIC_SECRET) { + crv = CKR_KEY_FUNCTION_NOT_PERMITTED; + break; + } + if ((keySize != 0) && (keySize != SSL3_MASTER_SECRET_LENGTH)) { + crv = CKR_KEY_FUNCTION_NOT_PERMITTED; + break; + } + + + /* finally do the key gen */ + ssl3_master = (CK_SSL3_MASTER_KEY_DERIVE_PARAMS *) + pMechanism->pParameter; + if (ssl3_master->pVersion) { + PK11SessionObject *sessKey = pk11_narrowToSessionObject(key); + rsa_pms = (SSL3RSAPreMasterSecret *) att->attrib.pValue; + /* don't leak more key material then necessary for SSL to work */ + if ((sessKey == NULL) || sessKey->wasDerived) { + ssl3_master->pVersion->major = 0xff; + ssl3_master->pVersion->minor = 0xff; + } else { + ssl3_master->pVersion->major = rsa_pms->client_version[0]; + ssl3_master->pVersion->minor = rsa_pms->client_version[1]; + } + } + if (ssl3_master->RandomInfo.ulClientRandomLen != SSL3_RANDOM_LENGTH) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + if (ssl3_master->RandomInfo.ulServerRandomLen != SSL3_RANDOM_LENGTH) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + + if (isTLS) { + unsigned char crsrdata[SSL3_RANDOM_LENGTH * 2]; + SECItem crsr = { siBuffer, NULL, 0 }; + SECItem master = { siBuffer, NULL, 0 }; + SECItem pms = { siBuffer, NULL, 0 }; + SECStatus status; + + pms.data = (unsigned char*)att->attrib.pValue; + pms.len = att->attrib.ulValueLen; + master.data = key_block; + master.len = SSL3_MASTER_SECRET_LENGTH; + crsr.data = crsrdata; + crsr.len = sizeof(crsrdata); + + PORT_Memcpy(crsrdata, ssl3_master->RandomInfo.pClientRandom, + SSL3_RANDOM_LENGTH); + PORT_Memcpy(crsrdata + SSL3_RANDOM_LENGTH, + ssl3_master->RandomInfo.pServerRandom, SSL3_RANDOM_LENGTH); + + status = pk11_PRF(&pms, "master secret", &crsr, &master); + if (status != SECSuccess) { + crv = CKR_FUNCTION_FAILED; + break; + } + } else { + /* now allocate the hash contexts */ + md5 = MD5_NewContext(); + if (md5 == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + sha = SHA1_NewContext(); + if (sha == NULL) { + PORT_Free(md5); + crv = CKR_HOST_MEMORY; + break; + } + for (i = 0; i < 3; i++) { + SHA1_Begin(sha); + SHA1_Update(sha, (unsigned char*) mixers[i], strlen(mixers[i])); + SHA1_Update(sha, (const unsigned char*)att->attrib.pValue, + att->attrib.ulValueLen); + SHA1_Update(sha, ssl3_master->RandomInfo.pClientRandom, + ssl3_master->RandomInfo.ulClientRandomLen); + SHA1_Update(sha, ssl3_master->RandomInfo.pServerRandom, + ssl3_master->RandomInfo.ulServerRandomLen); + SHA1_End(sha, sha_out, &outLen, SHA1_LENGTH); + PORT_Assert(outLen == SHA1_LENGTH); + MD5_Begin(md5); + MD5_Update(md5, (const unsigned char*)att->attrib.pValue, + att->attrib.ulValueLen); + MD5_Update(md5, sha_out, outLen); + MD5_End(md5, &key_block[i*MD5_LENGTH], &outLen, MD5_LENGTH); + PORT_Assert(outLen == MD5_LENGTH); + } + PORT_Free(md5); + PORT_Free(sha); + } + + /* store the results */ + crv = pk11_forceAttribute + (key,CKA_VALUE,key_block,SSL3_MASTER_SECRET_LENGTH); + if (crv != CKR_OK) break; + keyType = CKK_GENERIC_SECRET; + crv = pk11_forceAttribute (key,CKA_KEY_TYPE,&keyType,sizeof(keyType)); + if (isTLS) { + /* TLS's master secret is used to "sign" finished msgs with PRF. */ + /* XXX This seems like a hack. But PK11_Derive only accepts + * one "operation" argument. */ + crv = pk11_forceAttribute(key,CKA_SIGN, &cktrue,sizeof(CK_BBOOL)); + if (crv != CKR_OK) break; + crv = pk11_forceAttribute(key,CKA_VERIFY,&cktrue,sizeof(CK_BBOOL)); + if (crv != CKR_OK) break; + /* While we're here, we might as well force this, too. */ + crv = pk11_forceAttribute(key,CKA_DERIVE,&cktrue,sizeof(CK_BBOOL)); + if (crv != CKR_OK) break; + } + break; + } + + case CKM_TLS_KEY_AND_MAC_DERIVE: + isTLS = PR_TRUE; + /* fall thru */ + case CKM_SSL3_KEY_AND_MAC_DERIVE: + { + CK_SSL3_KEY_MAT_PARAMS *ssl3_keys; + CK_SSL3_KEY_MAT_OUT * ssl3_keys_out; + CK_ULONG effKeySize; + + crv = pk11_DeriveSensitiveCheck(sourceKey,key); + if (crv != CKR_OK) break; + + if (att->attrib.ulValueLen != SSL3_MASTER_SECRET_LENGTH) { + crv = CKR_KEY_FUNCTION_NOT_PERMITTED; + break; + } + att2 = pk11_FindAttribute(sourceKey,CKA_KEY_TYPE); + if ((att2 == NULL) || (*(CK_KEY_TYPE *)att2->attrib.pValue != + CKK_GENERIC_SECRET)) { + if (att2) pk11_FreeAttribute(att2); + crv = CKR_KEY_FUNCTION_NOT_PERMITTED; + break; + } + pk11_FreeAttribute(att2); + md5 = MD5_NewContext(); + if (md5 == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + sha = SHA1_NewContext(); + if (sha == NULL) { + PORT_Free(md5); + crv = CKR_HOST_MEMORY; + break; + } + ssl3_keys = (CK_SSL3_KEY_MAT_PARAMS *) pMechanism->pParameter; + /* + * clear out our returned keys so we can recover on failure + */ + ssl3_keys_out = ssl3_keys->pReturnedKeyMaterial; + ssl3_keys_out->hClientMacSecret = CK_INVALID_HANDLE; + ssl3_keys_out->hServerMacSecret = CK_INVALID_HANDLE; + ssl3_keys_out->hClientKey = CK_INVALID_HANDLE; + ssl3_keys_out->hServerKey = CK_INVALID_HANDLE; + + /* + * generate the key material: This looks amazingly similar to the + * PMS code, and is clearly crying out for a function to provide it. + */ + if (isTLS) { + SECStatus status; + SECItem master = { siBuffer, NULL, 0 }; + SECItem srcr = { siBuffer, NULL, 0 }; + SECItem keyblk = { siBuffer, NULL, 0 }; + unsigned char srcrdata[SSL3_RANDOM_LENGTH * 2]; + + master.data = (unsigned char*)att->attrib.pValue; + master.len = att->attrib.ulValueLen; + srcr.data = srcrdata; + srcr.len = sizeof srcrdata; + keyblk.data = key_block; + keyblk.len = sizeof key_block; + + PORT_Memcpy(srcrdata, + ssl3_keys->RandomInfo.pServerRandom, + SSL3_RANDOM_LENGTH); + PORT_Memcpy(srcrdata + SSL3_RANDOM_LENGTH, + ssl3_keys->RandomInfo.pClientRandom, + SSL3_RANDOM_LENGTH); + + status = pk11_PRF(&master, "key expansion", &srcr, &keyblk); + if (status != SECSuccess) { + goto key_and_mac_derive_fail; + } + } else { + /* key_block = + * MD5(master_secret + SHA('A' + master_secret + + * ServerHello.random + ClientHello.random)) + + * MD5(master_secret + SHA('BB' + master_secret + + * ServerHello.random + ClientHello.random)) + + * MD5(master_secret + SHA('CCC' + master_secret + + * ServerHello.random + ClientHello.random)) + + * [...]; + */ + for (i = 0; i < NUM_MIXERS; i++) { + SHA1_Begin(sha); + SHA1_Update(sha, (unsigned char*) mixers[i], strlen(mixers[i])); + SHA1_Update(sha, (const unsigned char*)att->attrib.pValue, + att->attrib.ulValueLen); + SHA1_Update(sha, ssl3_keys->RandomInfo.pServerRandom, + ssl3_keys->RandomInfo.ulServerRandomLen); + SHA1_Update(sha, ssl3_keys->RandomInfo.pClientRandom, + ssl3_keys->RandomInfo.ulClientRandomLen); + SHA1_End(sha, sha_out, &outLen, SHA1_LENGTH); + PORT_Assert(outLen == SHA1_LENGTH); + MD5_Begin(md5); + MD5_Update(md5, (const unsigned char*)att->attrib.pValue, + att->attrib.ulValueLen); + MD5_Update(md5, sha_out, outLen); + MD5_End(md5, &key_block[i*MD5_LENGTH], &outLen, MD5_LENGTH); + PORT_Assert(outLen == MD5_LENGTH); + } + } + + /* + * Put the key material where it goes. + */ + i = 0; /* now shows how much consumed */ + macSize = ssl3_keys->ulMacSizeInBits/8; + effKeySize = ssl3_keys->ulKeySizeInBits/8; + IVSize = ssl3_keys->ulIVSizeInBits/8; + if (keySize == 0) { + effKeySize = keySize; + } + + /* + * The key_block is partitioned as follows: + * client_write_MAC_secret[CipherSpec.hash_size] + */ + crv = pk11_buildSSLKey(hSession,key,PR_TRUE,&key_block[i],macSize, + &ssl3_keys_out->hClientMacSecret); + if (crv != CKR_OK) + goto key_and_mac_derive_fail; + + i += macSize; + + /* + * server_write_MAC_secret[CipherSpec.hash_size] + */ + crv = pk11_buildSSLKey(hSession,key,PR_TRUE,&key_block[i],macSize, + &ssl3_keys_out->hServerMacSecret); + if (crv != CKR_OK) { + goto key_and_mac_derive_fail; + } + i += macSize; + + if (keySize) { + if (!ssl3_keys->bIsExport) { + /* + ** Generate Domestic write keys and IVs. + ** client_write_key[CipherSpec.key_material] + */ + crv = pk11_buildSSLKey(hSession,key,PR_FALSE,&key_block[i], + keySize, &ssl3_keys_out->hClientKey); + if (crv != CKR_OK) { + goto key_and_mac_derive_fail; + } + i += keySize; + + /* + ** server_write_key[CipherSpec.key_material] + */ + crv = pk11_buildSSLKey(hSession,key,PR_FALSE,&key_block[i], + keySize, &ssl3_keys_out->hServerKey); + if (crv != CKR_OK) { + goto key_and_mac_derive_fail; + } + i += keySize; + + /* + ** client_write_IV[CipherSpec.IV_size] + */ + PORT_Memcpy(ssl3_keys_out->pIVClient, &key_block[i], IVSize); + i += IVSize; + + /* + ** server_write_IV[CipherSpec.IV_size] + */ + PORT_Memcpy(ssl3_keys_out->pIVServer, &key_block[i], IVSize); + i += IVSize; + PORT_Assert(i <= sizeof key_block); + + } else if (!isTLS) { + + /* + ** Generate SSL3 Export write keys and IVs. + ** client_write_key[CipherSpec.key_material] + ** final_client_write_key = MD5(client_write_key + + ** ClientHello.random + ServerHello.random); + */ + MD5_Begin(md5); + MD5_Update(md5, &key_block[i], effKeySize); + MD5_Update(md5, ssl3_keys->RandomInfo.pClientRandom, + ssl3_keys->RandomInfo.ulClientRandomLen); + MD5_Update(md5, ssl3_keys->RandomInfo.pServerRandom, + ssl3_keys->RandomInfo.ulServerRandomLen); + MD5_End(md5, key_block2, &outLen, MD5_LENGTH); + i += effKeySize; + crv = pk11_buildSSLKey(hSession,key,PR_FALSE,key_block2, + keySize,&ssl3_keys_out->hClientKey); + if (crv != CKR_OK) { + goto key_and_mac_derive_fail; + } + + /* + ** server_write_key[CipherSpec.key_material] + ** final_server_write_key = MD5(server_write_key + + ** ServerHello.random + ClientHello.random); + */ + MD5_Begin(md5); + MD5_Update(md5, &key_block[i], effKeySize); + MD5_Update(md5, ssl3_keys->RandomInfo.pServerRandom, + ssl3_keys->RandomInfo.ulServerRandomLen); + MD5_Update(md5, ssl3_keys->RandomInfo.pClientRandom, + ssl3_keys->RandomInfo.ulClientRandomLen); + MD5_End(md5, key_block2, &outLen, MD5_LENGTH); + i += effKeySize; + crv = pk11_buildSSLKey(hSession,key,PR_FALSE,key_block2, + keySize,&ssl3_keys_out->hServerKey); + if (crv != CKR_OK) { + goto key_and_mac_derive_fail; + } + + /* + ** client_write_IV = + ** MD5(ClientHello.random + ServerHello.random); + */ + MD5_Begin(md5); + MD5_Update(md5, ssl3_keys->RandomInfo.pClientRandom, + ssl3_keys->RandomInfo.ulClientRandomLen); + MD5_Update(md5, ssl3_keys->RandomInfo.pServerRandom, + ssl3_keys->RandomInfo.ulServerRandomLen); + MD5_End(md5, key_block2, &outLen, MD5_LENGTH); + PORT_Memcpy(ssl3_keys_out->pIVClient, key_block2, IVSize); + + /* + ** server_write_IV = + ** MD5(ServerHello.random + ClientHello.random); + */ + MD5_Begin(md5); + MD5_Update(md5, ssl3_keys->RandomInfo.pServerRandom, + ssl3_keys->RandomInfo.ulServerRandomLen); + MD5_Update(md5, ssl3_keys->RandomInfo.pClientRandom, + ssl3_keys->RandomInfo.ulClientRandomLen); + MD5_End(md5, key_block2, &outLen, MD5_LENGTH); + PORT_Memcpy(ssl3_keys_out->pIVServer, key_block2, IVSize); + + } else { + + /* + ** Generate TLS Export write keys and IVs. + */ + SECStatus status; + SECItem secret = { siBuffer, NULL, 0 }; + SECItem crsr = { siBuffer, NULL, 0 }; + SECItem keyblk = { siBuffer, NULL, 0 }; + unsigned char crsrdata[SSL3_RANDOM_LENGTH * 2]; + + crsr.data = crsrdata; + crsr.len = sizeof crsrdata; + + PORT_Memcpy(crsrdata, + ssl3_keys->RandomInfo.pClientRandom, + SSL3_RANDOM_LENGTH); + PORT_Memcpy(crsrdata + SSL3_RANDOM_LENGTH, + ssl3_keys->RandomInfo.pServerRandom, + SSL3_RANDOM_LENGTH); + + + /* + ** client_write_key[CipherSpec.key_material] + ** final_client_write_key = PRF(client_write_key, + ** "client write key", + ** client_random + server_random); + */ + secret.data = &key_block[i]; + secret.len = effKeySize; + i += effKeySize; + keyblk.data = key_block2; + keyblk.len = sizeof key_block2; + status = pk11_PRF(&secret, "client write key", &crsr, &keyblk); + if (status != SECSuccess) { + goto key_and_mac_derive_fail; + } + crv = pk11_buildSSLKey(hSession, key, PR_FALSE, key_block2, + keySize, &ssl3_keys_out->hClientKey); + if (crv != CKR_OK) { + goto key_and_mac_derive_fail; + } + + /* + ** server_write_key[CipherSpec.key_material] + ** final_server_write_key = PRF(server_write_key, + ** "server write key", + ** client_random + server_random); + */ + secret.data = &key_block[i]; + secret.len = effKeySize; + i += effKeySize; + keyblk.data = key_block2; + keyblk.len = sizeof key_block2; + status = pk11_PRF(&secret, "server write key", &crsr, &keyblk); + if (status != SECSuccess) { + goto key_and_mac_derive_fail; + } + crv = pk11_buildSSLKey(hSession, key, PR_FALSE, key_block2, + keySize, &ssl3_keys_out->hServerKey); + if (crv != CKR_OK) { + goto key_and_mac_derive_fail; + } + + /* + ** iv_block = PRF("", "IV block", + ** client_random + server_random); + ** client_write_IV[SecurityParameters.IV_size] + ** server_write_IV[SecurityParameters.IV_size] + */ + if (IVSize) { + secret.data = NULL; + secret.len = 0; + keyblk.data = &key_block[i]; + keyblk.len = 2 * IVSize; + status = pk11_PRF(&secret, "IV block", &crsr, &keyblk); + if (status != SECSuccess) { + goto key_and_mac_derive_fail; + } + PORT_Memcpy(ssl3_keys_out->pIVClient, keyblk.data, IVSize); + PORT_Memcpy(ssl3_keys_out->pIVServer, keyblk.data + IVSize, + IVSize); + } + } + } + + crv = CKR_OK; + + if (0) { +key_and_mac_derive_fail: + if (crv == CKR_OK) + crv = CKR_FUNCTION_FAILED; + pk11_freeSSLKeys(hSession, ssl3_keys_out); + } + MD5_DestroyContext(md5, PR_TRUE); + SHA1_DestroyContext(sha, PR_TRUE); + pk11_FreeObject(key); + key = NULL; + break; + } + + case CKM_CONCATENATE_BASE_AND_KEY: + { + PK11Object *newKey; + + crv = pk11_DeriveSensitiveCheck(sourceKey,key); + if (crv != CKR_OK) break; + + session = pk11_SessionFromHandle(hSession); + if (session == NULL) { + crv = CKR_SESSION_HANDLE_INVALID; + break; + } + + newKey = pk11_ObjectFromHandle(*(CK_OBJECT_HANDLE *) + pMechanism->pParameter,session); + pk11_FreeSession(session); + if ( newKey == NULL) { + crv = CKR_KEY_HANDLE_INVALID; + break; + } + + if (pk11_isTrue(newKey,CKA_SENSITIVE)) { + crv = pk11_forceAttribute(newKey,CKA_SENSITIVE,&cktrue, + sizeof(CK_BBOOL)); + if (crv != CKR_OK) { + pk11_FreeObject(newKey); + break; + } + } + + att2 = pk11_FindAttribute(newKey,CKA_VALUE); + if (att2 == NULL) { + pk11_FreeObject(newKey); + crv = CKR_KEY_HANDLE_INVALID; + break; + } + tmpKeySize = att->attrib.ulValueLen+att2->attrib.ulValueLen; + if (keySize == 0) keySize = tmpKeySize; + if (keySize > tmpKeySize) { + pk11_FreeObject(newKey); + pk11_FreeAttribute(att2); + crv = CKR_TEMPLATE_INCONSISTENT; + break; + } + buf = (unsigned char*)PORT_Alloc(tmpKeySize); + if (buf == NULL) { + pk11_FreeAttribute(att2); + pk11_FreeObject(newKey); + crv = CKR_HOST_MEMORY; + break; + } + + PORT_Memcpy(buf,att->attrib.pValue,att->attrib.ulValueLen); + PORT_Memcpy(buf+att->attrib.ulValueLen, + att2->attrib.pValue,att2->attrib.ulValueLen); + + crv = pk11_forceAttribute (key,CKA_VALUE,buf,keySize); + PORT_ZFree(buf,tmpKeySize); + pk11_FreeAttribute(att2); + pk11_FreeObject(newKey); + break; + } + + case CKM_CONCATENATE_BASE_AND_DATA: + stringPtr = (CK_KEY_DERIVATION_STRING_DATA *) pMechanism->pParameter; + tmpKeySize = att->attrib.ulValueLen+stringPtr->ulLen; + if (keySize == 0) keySize = tmpKeySize; + if (keySize > tmpKeySize) { + crv = CKR_TEMPLATE_INCONSISTENT; + break; + } + buf = (unsigned char*)PORT_Alloc(tmpKeySize); + if (buf == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + + PORT_Memcpy(buf,att->attrib.pValue,att->attrib.ulValueLen); + PORT_Memcpy(buf+att->attrib.ulValueLen,stringPtr->pData, + stringPtr->ulLen); + + crv = pk11_forceAttribute (key,CKA_VALUE,buf,keySize); + PORT_ZFree(buf,tmpKeySize); + break; + case CKM_CONCATENATE_DATA_AND_BASE: + stringPtr = (CK_KEY_DERIVATION_STRING_DATA *)pMechanism->pParameter; + tmpKeySize = att->attrib.ulValueLen+stringPtr->ulLen; + if (keySize == 0) keySize = tmpKeySize; + if (keySize > tmpKeySize) { + crv = CKR_TEMPLATE_INCONSISTENT; + break; + } + buf = (unsigned char*)PORT_Alloc(tmpKeySize); + if (buf == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + + PORT_Memcpy(buf,stringPtr->pData,stringPtr->ulLen); + PORT_Memcpy(buf+stringPtr->ulLen,att->attrib.pValue, + att->attrib.ulValueLen); + + crv = pk11_forceAttribute (key,CKA_VALUE,buf,keySize); + PORT_ZFree(buf,tmpKeySize); + break; + case CKM_XOR_BASE_AND_DATA: + stringPtr = (CK_KEY_DERIVATION_STRING_DATA *)pMechanism->pParameter; + tmpKeySize = PR_MIN(att->attrib.ulValueLen,stringPtr->ulLen); + if (keySize == 0) keySize = tmpKeySize; + if (keySize > tmpKeySize) { + crv = CKR_TEMPLATE_INCONSISTENT; + break; + } + buf = (unsigned char*)PORT_Alloc(keySize); + if (buf == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + + + PORT_Memcpy(buf,att->attrib.pValue,keySize); + for (i=0; i < (int)keySize; i++) { + buf[i] ^= stringPtr->pData[i]; + } + + crv = pk11_forceAttribute (key,CKA_VALUE,buf,keySize); + PORT_ZFree(buf,keySize); + break; + + case CKM_EXTRACT_KEY_FROM_KEY: + { + /* the following assumes 8 bits per byte */ + CK_ULONG extract = *(CK_EXTRACT_PARAMS *)pMechanism->pParameter; + CK_ULONG shift = extract & 0x7; /* extract mod 8 the fast way */ + CK_ULONG offset = extract >> 3; /* extract div 8 the fast way */ + + if (keySize == 0) { + crv = CKR_TEMPLATE_INCOMPLETE; + break; + } + /* make sure we have enough bits in the original key */ + if (att->attrib.ulValueLen < + (offset + keySize + ((shift != 0)? 1 :0)) ) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + buf = (unsigned char*)PORT_Alloc(keySize); + if (buf == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + + /* copy the bits we need into the new key */ + for (i=0; i < (int)keySize; i++) { + unsigned char *value = + ((unsigned char *)att->attrib.pValue)+offset+i; + if (shift) { + buf[i] = (value[0] << (shift)) | (value[1] >> (8 - shift)); + } else { + buf[i] = value[0]; + } + } + + crv = pk11_forceAttribute (key,CKA_VALUE,buf,keySize); + PORT_ZFree(buf,keySize); + break; + } + case CKM_MD2_KEY_DERIVATION: + if (keySize == 0) keySize = MD2_LENGTH; + if (keySize > MD2_LENGTH) { + crv = CKR_TEMPLATE_INCONSISTENT; + break; + } + /* now allocate the hash contexts */ + md2 = MD2_NewContext(); + if (md2 == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + MD2_Begin(md2); + MD2_Update(md2,(const unsigned char*)att->attrib.pValue, + att->attrib.ulValueLen); + MD2_End(md2,key_block,&outLen,MD2_LENGTH); + MD2_DestroyContext(md2, PR_TRUE); + + crv = pk11_forceAttribute (key,CKA_VALUE,key_block,keySize); + break; + case CKM_MD5_KEY_DERIVATION: + if (keySize == 0) keySize = MD5_LENGTH; + if (keySize > MD5_LENGTH) { + crv = CKR_TEMPLATE_INCONSISTENT; + break; + } + /* now allocate the hash contexts */ + md5 = MD5_NewContext(); + if (md5 == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + MD5_Begin(md5); + MD5_Update(md5,(const unsigned char*)att->attrib.pValue, + att->attrib.ulValueLen); + MD5_End(md5,key_block,&outLen,MD5_LENGTH); + MD5_DestroyContext(md5, PR_TRUE); + + crv = pk11_forceAttribute (key,CKA_VALUE,key_block,keySize); + break; + case CKM_SHA1_KEY_DERIVATION: + if (keySize == 0) keySize = SHA1_LENGTH; + if (keySize > SHA1_LENGTH) { + crv = CKR_TEMPLATE_INCONSISTENT; + break; + } + /* now allocate the hash contexts */ + sha = SHA1_NewContext(); + if (sha == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + SHA1_Begin(sha); + SHA1_Update(sha,(const unsigned char*)att->attrib.pValue, + att->attrib.ulValueLen); + SHA1_End(sha,key_block,&outLen,SHA1_LENGTH); + SHA1_DestroyContext(sha, PR_TRUE); + + crv = pk11_forceAttribute(key,CKA_VALUE,key_block,keySize); + break; + + case CKM_DH_PKCS_DERIVE: + { + SECItem derived, dhPublic; + SECItem dhPrime, dhValue; + /* sourceKey - values for the local existing low key */ + /* get prime and value attributes */ + crv = pk11_Attribute2SecItem(NULL, &dhPrime, sourceKey, CKA_PRIME); + if (crv != SECSuccess) break; + crv = pk11_Attribute2SecItem(NULL, &dhValue, sourceKey, CKA_VALUE); + if (crv != SECSuccess) { + PORT_Free(dhPrime.data); + break; + } + + dhPublic.data = pMechanism->pParameter; + dhPublic.len = pMechanism->ulParameterLen; + + /* calculate private value - oct */ + rv = DH_Derive(&dhPublic, &dhPrime, &dhValue, &derived, keySize); + + PORT_Free(dhPrime.data); + PORT_Free(dhValue.data); + + if (rv == SECSuccess) { + pk11_forceAttribute(key, CKA_VALUE, derived.data, derived.len); + PORT_ZFree(derived.data, derived.len); + } else + crv = CKR_HOST_MEMORY; + + break; + } + + default: + crv = CKR_MECHANISM_INVALID; + } + pk11_FreeAttribute(att); + pk11_FreeObject(sourceKey); + if (crv != CKR_OK) { + if (key) pk11_FreeObject(key); + return crv; + } + + /* link the key object into the list */ + if (key) { + PK11SessionObject *sessKey = pk11_narrowToSessionObject(key); + PORT_Assert(sessKey); + /* get the session */ + sessKey->wasDerived = PR_TRUE; + session = pk11_SessionFromHandle(hSession); + if (session == NULL) { + pk11_FreeObject(key); + return CKR_HOST_MEMORY; + } + + crv = pk11_handleObject(key,session); + pk11_FreeSession(session); + *phKey = key->handle; + pk11_FreeObject(key); + } + return crv; +} + + +/* NSC_GetFunctionStatus obtains an updated status of a function running + * in parallel with an application. */ +CK_RV NSC_GetFunctionStatus(CK_SESSION_HANDLE hSession) +{ + return CKR_FUNCTION_NOT_PARALLEL; +} + +/* NSC_CancelFunction cancels a function running in parallel */ +CK_RV NSC_CancelFunction(CK_SESSION_HANDLE hSession) +{ + return CKR_FUNCTION_NOT_PARALLEL; +} + +/* NSC_GetOperationState saves the state of the cryptographic + *operation in a session. + * NOTE: This code only works for digest functions for now. eventually need + * to add full flatten/resurect to our state stuff so that all types of state + * can be saved */ +CK_RV NSC_GetOperationState(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pOperationState, CK_ULONG_PTR pulOperationStateLen) +{ + PK11SessionContext *context; + PK11Session *session; + CK_RV crv; + CK_ULONG pOSLen = *pulOperationStateLen; + + /* make sure we're legal */ + crv = pk11_GetContext(hSession, &context, PK11_HASH, PR_TRUE, &session); + if (crv != CKR_OK) return crv; + + *pulOperationStateLen = context->cipherInfoLen + sizeof(CK_MECHANISM_TYPE) + + sizeof(PK11ContextType); + if (pOperationState == NULL) { + pk11_FreeSession(session); + return CKR_OK; + } else { + if (pOSLen < *pulOperationStateLen) { + return CKR_BUFFER_TOO_SMALL; + } + } + PORT_Memcpy(pOperationState,&context->type,sizeof(PK11ContextType)); + pOperationState += sizeof(PK11ContextType); + PORT_Memcpy(pOperationState,&context->currentMech, + sizeof(CK_MECHANISM_TYPE)); + pOperationState += sizeof(CK_MECHANISM_TYPE); + PORT_Memcpy(pOperationState,context->cipherInfo,context->cipherInfoLen); + pk11_FreeSession(session); + return CKR_OK; +} + + +#define pk11_Decrement(stateSize,len) \ + stateSize = ((stateSize) > (CK_ULONG)(len)) ? \ + ((stateSize) - (CK_ULONG)(len)) : 0; + +/* NSC_SetOperationState restores the state of the cryptographic + * operation in a session. This is coded like it can restore lots of + * states, but it only works for truly flat cipher structures. */ +CK_RV NSC_SetOperationState(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pOperationState, CK_ULONG ulOperationStateLen, + CK_OBJECT_HANDLE hEncryptionKey, CK_OBJECT_HANDLE hAuthenticationKey) +{ + PK11SessionContext *context; + PK11Session *session; + PK11ContextType type; + CK_MECHANISM mech; + CK_RV crv = CKR_OK; + + while (ulOperationStateLen != 0) { + /* get what type of state we're dealing with... */ + PORT_Memcpy(&type,pOperationState, sizeof(PK11ContextType)); + + /* fix up session contexts based on type */ + session = pk11_SessionFromHandle(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + context = pk11_ReturnContextByType(session, type); + pk11_SetContextByType(session, type, NULL); + if (context) { + pk11_FreeContext(context); + } + pOperationState += sizeof(PK11ContextType); + pk11_Decrement(ulOperationStateLen,sizeof(PK11ContextType)); + + + /* get the mechanism structure */ + PORT_Memcpy(&mech.mechanism,pOperationState,sizeof(CK_MECHANISM_TYPE)); + pOperationState += sizeof(CK_MECHANISM_TYPE); + pk11_Decrement(ulOperationStateLen, sizeof(CK_MECHANISM_TYPE)); + /* should be filled in... but not necessary for hash */ + mech.pParameter = NULL; + mech.ulParameterLen = 0; + switch (type) { + case PK11_HASH: + crv = NSC_DigestInit(hSession,&mech); + if (crv != CKR_OK) break; + crv = pk11_GetContext(hSession, &context, PK11_HASH, PR_TRUE, + NULL); + if (crv != CKR_OK) break; + PORT_Memcpy(context->cipherInfo,pOperationState, + context->cipherInfoLen); + pOperationState += context->cipherInfoLen; + pk11_Decrement(ulOperationStateLen,context->cipherInfoLen); + break; + default: + /* do sign/encrypt/decrypt later */ + crv = CKR_SAVED_STATE_INVALID; + } + pk11_FreeSession(session); + if (crv != CKR_OK) break; + } + return crv; +} + +/* Dual-function cryptographic operations */ + +/* NSC_DigestEncryptUpdate continues a multiple-part digesting and encryption + * operation. */ +CK_RV NSC_DigestEncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, + CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, + CK_ULONG_PTR pulEncryptedPartLen) +{ + CK_RV crv; + + crv = NSC_EncryptUpdate(hSession,pPart,ulPartLen, pEncryptedPart, + pulEncryptedPartLen); + if (crv != CKR_OK) return crv; + crv = NSC_DigestUpdate(hSession,pPart,ulPartLen); + + return crv; +} + + +/* NSC_DecryptDigestUpdate continues a multiple-part decryption and + * digesting operation. */ +CK_RV NSC_DecryptDigestUpdate(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, + CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) +{ + CK_RV crv; + + crv = NSC_DecryptUpdate(hSession,pEncryptedPart, ulEncryptedPartLen, + pPart, pulPartLen); + if (crv != CKR_OK) return crv; + crv = NSC_DigestUpdate(hSession,pPart,*pulPartLen); + + return crv; +} + + +/* NSC_SignEncryptUpdate continues a multiple-part signing and + * encryption operation. */ +CK_RV NSC_SignEncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, + CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, + CK_ULONG_PTR pulEncryptedPartLen) +{ + CK_RV crv; + + crv = NSC_EncryptUpdate(hSession,pPart,ulPartLen, pEncryptedPart, + pulEncryptedPartLen); + if (crv != CKR_OK) return crv; + crv = NSC_SignUpdate(hSession,pPart,ulPartLen); + + return crv; +} + + +/* NSC_DecryptVerifyUpdate continues a multiple-part decryption + * and verify operation. */ +CK_RV NSC_DecryptVerifyUpdate(CK_SESSION_HANDLE hSession, + CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, + CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) +{ + CK_RV crv; + + crv = NSC_DecryptUpdate(hSession,pEncryptedData, ulEncryptedDataLen, + pData, pulDataLen); + if (crv != CKR_OK) return crv; + crv = NSC_VerifyUpdate(hSession, pData, *pulDataLen); + + return crv; +} + +/* NSC_DigestKey continues a multi-part message-digesting operation, + * by digesting the value of a secret key as part of the data already digested. + */ +CK_RV NSC_DigestKey(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hKey) +{ + PK11Session *session = NULL; + PK11Object *key = NULL; + PK11Attribute *att; + CK_RV crv; + + session = pk11_SessionFromHandle(hSession); + if (session == NULL) return CKR_SESSION_HANDLE_INVALID; + + key = pk11_ObjectFromHandle(hKey,session); + pk11_FreeSession(session); + if (key == NULL) return CKR_KEY_HANDLE_INVALID; + + /* PUT ANY DIGEST KEY RESTRICTION CHECKS HERE */ + + /* make sure it's a valid key for this operation */ + if (key->objclass != CKO_SECRET_KEY) { + pk11_FreeObject(key); + return CKR_KEY_TYPE_INCONSISTENT; + } + /* get the key value */ + att = pk11_FindAttribute(key,CKA_VALUE); + pk11_FreeObject(key); + + crv = NSC_DigestUpdate(hSession,(CK_BYTE_PTR)att->attrib.pValue, + att->attrib.ulValueLen); + pk11_FreeAttribute(att); + return crv; +} |