/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ckcapi.h" #include "secdert.h" #define SSL3_SHAMD5_HASH_SIZE 36 /* LEN_MD5 (16) + LEN_SHA1 (20) */ /* * ckcapi/crsa.c * * This file implements the NSSCKMDMechnaism and NSSCKMDCryptoOperation objects * for the RSA operation on the CAPI cryptoki module. */ /* * write a Decimal value to a string */ static char * putDecimalString(char *cstr, unsigned long value) { unsigned long tenpower; int first = 1; for (tenpower = 10000000; tenpower; tenpower /= 10) { unsigned char digit = (unsigned char)(value / tenpower); value = value % tenpower; /* drop leading zeros */ if (first && (0 == digit)) { continue; } first = 0; *cstr++ = digit + '0'; } /* if value was zero, put one of them out */ if (first) { *cstr++ = '0'; } return cstr; } /* * Create a Capi OID string value from a DER OID */ static char * nss_ckcapi_GetOidString( unsigned char *oidTag, unsigned int oidTagSize, CK_RV *pError) { unsigned char *oid; char *oidStr; char *cstr; unsigned long value; unsigned int oidSize; if (DER_OBJECT_ID != *oidTag) { /* wasn't an oid */ *pError = CKR_DATA_INVALID; return NULL; } oid = nss_ckcapi_DERUnwrap(oidTag, oidTagSize, &oidSize, NULL); if (oidSize < 2) { *pError = CKR_DATA_INVALID; return NULL; } oidStr = nss_ZNEWARRAY(NULL, char, oidSize * 4); if ((char *)NULL == oidStr) { *pError = CKR_HOST_MEMORY; return NULL; } cstr = oidStr; cstr = putDecimalString(cstr, (*oid) / 40); *cstr++ = '.'; cstr = putDecimalString(cstr, (*oid) % 40); oidSize--; value = 0; while (oidSize--) { oid++; value = (value << 7) + (*oid & 0x7f); if (0 == (*oid & 0x80)) { *cstr++ = '.'; cstr = putDecimalString(cstr, value); value = 0; } } *cstr = 0; /* NULL terminate */ if (value != 0) { nss_ZFreeIf(oidStr); *pError = CKR_DATA_INVALID; return NULL; } return oidStr; } /* * PKCS #11 sign for RSA expects to take a fully DER-encoded hash value, * which includes the hash OID. CAPI expects to take a Hash Context. While * CAPI does have the capability of setting a raw hash value, it does not * have the ability to sign an arbitrary value. This function tries to * reduce the passed in data into something that CAPI could actually sign. */ static CK_RV ckcapi_GetRawHash( const NSSItem *input, NSSItem *hash, ALG_ID *hashAlg) { unsigned char *current; unsigned char *algid; unsigned char *oid; unsigned char *hashData; char *oidStr; CK_RV error; unsigned int oidSize; unsigned int size; /* * there are 2 types of hashes NSS typically tries to sign, regular * RSA signature format (with encoded DER_OIDS), and SSL3 Signed hashes. * CAPI knows not to add any oids to SSL3_Signed hashes, so if we have any * random hash that is exactly the same size as an SSL3 hash, then we can * just pass the data through. CAPI has know way of knowing if the value * is really a combined hash or some other arbitrary data, so it's safe to * handle this case first. */ if (SSL3_SHAMD5_HASH_SIZE == input->size) { hash->data = input->data; hash->size = input->size; *hashAlg = CALG_SSL3_SHAMD5; return CKR_OK; } current = (unsigned char *)input->data; /* make sure we have a sequence tag */ if ((DER_SEQUENCE | DER_CONSTRUCTED) != *current) { return CKR_DATA_INVALID; } /* parse the input block to get 1) the hash oid, and 2) the raw hash value. * unfortunatly CAPI doesn't have a builtin function to do this work, so * we go ahead and do it by hand here. * * format is: * SEQUENCE { * SECQUENCE { // algid * OID {} // oid * ANY {} // optional params * } * OCTECT {} // hash */ /* unwrap */ algid = nss_ckcapi_DERUnwrap(current, input->size, &size, NULL); if (algid + size != current + input->size) { /* make sure there is not extra data at the end */ return CKR_DATA_INVALID; } if ((DER_SEQUENCE | DER_CONSTRUCTED) != *algid) { /* wasn't an algid */ return CKR_DATA_INVALID; } oid = nss_ckcapi_DERUnwrap(algid, size, &oidSize, &hashData); if (DER_OCTET_STRING != *hashData) { /* wasn't a hash */ return CKR_DATA_INVALID; } /* get the real hash */ current = hashData; size = size - (hashData - algid); hash->data = nss_ckcapi_DERUnwrap(current, size, &hash->size, NULL); /* get the real oid as a string. Again, Microsoft does not * export anything that does this for us */ oidStr = nss_ckcapi_GetOidString(oid, oidSize, &error); if ((char *)NULL == oidStr) { return error; } /* look up the hash alg from the oid (fortunately CAPI does to this) */ *hashAlg = CertOIDToAlgId(oidStr); nss_ZFreeIf(oidStr); if (0 == *hashAlg) { return CKR_HOST_MEMORY; } /* hash looks reasonably consistent, we should be able to sign it now */ return CKR_OK; } /* * So everyone else in the worlds stores their bignum data MSB first, but not * Microsoft, we need to byte swap everything coming into and out of CAPI. */ void ckcapi_ReverseData(NSSItem *item) { int end = (item->size) - 1; int middle = (item->size) / 2; unsigned char *buf = item->data; int i; for (i = 0; i < middle; i++) { unsigned char tmp = buf[i]; buf[i] = buf[end - i]; buf[end - i] = tmp; } return; } typedef struct ckcapiInternalCryptoOperationRSAPrivStr ckcapiInternalCryptoOperationRSAPriv; struct ckcapiInternalCryptoOperationRSAPrivStr { NSSCKMDCryptoOperation mdOperation; NSSCKMDMechanism *mdMechanism; ckcapiInternalObject *iKey; HCRYPTPROV hProv; DWORD keySpec; HCRYPTKEY hKey; NSSItem *buffer; }; /* * ckcapi_mdCryptoOperationRSAPriv_Create */ static NSSCKMDCryptoOperation * ckcapi_mdCryptoOperationRSAPriv_Create( const NSSCKMDCryptoOperation *proto, NSSCKMDMechanism *mdMechanism, NSSCKMDObject *mdKey, CK_RV *pError) { ckcapiInternalObject *iKey = (ckcapiInternalObject *)mdKey->etc; const NSSItem *classItem = nss_ckcapi_FetchAttribute(iKey, CKA_CLASS); const NSSItem *keyType = nss_ckcapi_FetchAttribute(iKey, CKA_KEY_TYPE); ckcapiInternalCryptoOperationRSAPriv *iOperation; CK_RV error; HCRYPTPROV hProv; DWORD keySpec; HCRYPTKEY hKey; /* make sure we have the right objects */ if (((const NSSItem *)NULL == classItem) || (sizeof(CK_OBJECT_CLASS) != classItem->size) || (CKO_PRIVATE_KEY != *(CK_OBJECT_CLASS *)classItem->data) || ((const NSSItem *)NULL == keyType) || (sizeof(CK_KEY_TYPE) != keyType->size) || (CKK_RSA != *(CK_KEY_TYPE *)keyType->data)) { *pError = CKR_KEY_TYPE_INCONSISTENT; return (NSSCKMDCryptoOperation *)NULL; } error = nss_ckcapi_FetchKeyContainer(iKey, &hProv, &keySpec, &hKey); if (error != CKR_OK) { *pError = error; return (NSSCKMDCryptoOperation *)NULL; } iOperation = nss_ZNEW(NULL, ckcapiInternalCryptoOperationRSAPriv); if ((ckcapiInternalCryptoOperationRSAPriv *)NULL == iOperation) { *pError = CKR_HOST_MEMORY; return (NSSCKMDCryptoOperation *)NULL; } iOperation->mdMechanism = mdMechanism; iOperation->iKey = iKey; iOperation->hProv = hProv; iOperation->keySpec = keySpec; iOperation->hKey = hKey; nsslibc_memcpy(&iOperation->mdOperation, proto, sizeof(NSSCKMDCryptoOperation)); iOperation->mdOperation.etc = iOperation; return &iOperation->mdOperation; } static CK_RV ckcapi_mdCryptoOperationRSAPriv_Destroy( NSSCKMDCryptoOperation *mdOperation, NSSCKFWCryptoOperation *fwOperation, NSSCKMDInstance *mdInstance, NSSCKFWInstance *fwInstance) { ckcapiInternalCryptoOperationRSAPriv *iOperation = (ckcapiInternalCryptoOperationRSAPriv *)mdOperation->etc; if (iOperation->hKey) { CryptDestroyKey(iOperation->hKey); } if (iOperation->buffer) { nssItem_Destroy(iOperation->buffer); } nss_ZFreeIf(iOperation); return CKR_OK; } static CK_ULONG ckcapi_mdCryptoOperationRSA_GetFinalLength( NSSCKMDCryptoOperation *mdOperation, NSSCKFWCryptoOperation *fwOperation, NSSCKMDSession *mdSession, NSSCKFWSession *fwSession, NSSCKMDToken *mdToken, NSSCKFWToken *fwToken, NSSCKMDInstance *mdInstance, NSSCKFWInstance *fwInstance, CK_RV *pError) { ckcapiInternalCryptoOperationRSAPriv *iOperation = (ckcapiInternalCryptoOperationRSAPriv *)mdOperation->etc; const NSSItem *modulus = nss_ckcapi_FetchAttribute(iOperation->iKey, CKA_MODULUS); return modulus->size; } /* * ckcapi_mdCryptoOperationRSADecrypt_GetOperationLength * we won't know the length until we actually decrypt the * input block. Since we go to all the work to decrypt the * the block, we'll save if for when the block is asked for */ static CK_ULONG ckcapi_mdCryptoOperationRSADecrypt_GetOperationLength( NSSCKMDCryptoOperation *mdOperation, NSSCKFWCryptoOperation *fwOperation, NSSCKMDSession *mdSession, NSSCKFWSession *fwSession, NSSCKMDToken *mdToken, NSSCKFWToken *fwToken, NSSCKMDInstance *mdInstance, NSSCKFWInstance *fwInstance, const NSSItem *input, CK_RV *pError) { ckcapiInternalCryptoOperationRSAPriv *iOperation = (ckcapiInternalCryptoOperationRSAPriv *)mdOperation->etc; BOOL rc; /* Microsoft's Decrypt operation works in place. Since we don't want * to trash our input buffer, we make a copy of it */ iOperation->buffer = nssItem_Duplicate((NSSItem *)input, NULL, NULL); if ((NSSItem *)NULL == iOperation->buffer) { *pError = CKR_HOST_MEMORY; return 0; } /* Sigh, reverse it */ ckcapi_ReverseData(iOperation->buffer); rc = CryptDecrypt(iOperation->hKey, 0, TRUE, 0, iOperation->buffer->data, &iOperation->buffer->size); if (!rc) { DWORD msError = GetLastError(); switch (msError) { case NTE_BAD_DATA: *pError = CKR_ENCRYPTED_DATA_INVALID; break; case NTE_FAIL: case NTE_BAD_UID: *pError = CKR_DEVICE_ERROR; break; default: *pError = CKR_GENERAL_ERROR; } return 0; } return iOperation->buffer->size; } /* * ckcapi_mdCryptoOperationRSADecrypt_UpdateFinal * * NOTE: ckcapi_mdCryptoOperationRSADecrypt_GetOperationLength is presumed to * have been called previously. */ static CK_RV ckcapi_mdCryptoOperationRSADecrypt_UpdateFinal( NSSCKMDCryptoOperation *mdOperation, NSSCKFWCryptoOperation *fwOperation, NSSCKMDSession *mdSession, NSSCKFWSession *fwSession, NSSCKMDToken *mdToken, NSSCKFWToken *fwToken, NSSCKMDInstance *mdInstance, NSSCKFWInstance *fwInstance, const NSSItem *input, NSSItem *output) { ckcapiInternalCryptoOperationRSAPriv *iOperation = (ckcapiInternalCryptoOperationRSAPriv *)mdOperation->etc; NSSItem *buffer = iOperation->buffer; if ((NSSItem *)NULL == buffer) { return CKR_GENERAL_ERROR; } nsslibc_memcpy(output->data, buffer->data, buffer->size); output->size = buffer->size; return CKR_OK; } /* * ckcapi_mdCryptoOperationRSASign_UpdateFinal * */ static CK_RV ckcapi_mdCryptoOperationRSASign_UpdateFinal( NSSCKMDCryptoOperation *mdOperation, NSSCKFWCryptoOperation *fwOperation, NSSCKMDSession *mdSession, NSSCKFWSession *fwSession, NSSCKMDToken *mdToken, NSSCKFWToken *fwToken, NSSCKMDInstance *mdInstance, NSSCKFWInstance *fwInstance, const NSSItem *input, NSSItem *output) { ckcapiInternalCryptoOperationRSAPriv *iOperation = (ckcapiInternalCryptoOperationRSAPriv *)mdOperation->etc; CK_RV error = CKR_OK; DWORD msError; NSSItem hash; HCRYPTHASH hHash = 0; ALG_ID hashAlg; DWORD hashSize; DWORD len; /* temp length value we throw away */ BOOL rc; /* * PKCS #11 sign for RSA expects to take a fully DER-encoded hash value, * which includes the hash OID. CAPI expects to take a Hash Context. While * CAPI does have the capability of setting a raw hash value, it does not * have the ability to sign an arbitrary value. This function tries to * reduce the passed in data into something that CAPI could actually sign. */ error = ckcapi_GetRawHash(input, &hash, &hashAlg); if (CKR_OK != error) { goto loser; } rc = CryptCreateHash(iOperation->hProv, hashAlg, 0, 0, &hHash); if (!rc) { goto loser; } /* make sure the hash lens match before we set it */ len = sizeof(DWORD); rc = CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE *)&hashSize, &len, 0); if (!rc) { goto loser; } if (hash.size != hashSize) { /* The input must have been bad for this to happen */ error = CKR_DATA_INVALID; goto loser; } /* we have an explicit hash, set it, note that the length is * implicit by the hashAlg used in create */ rc = CryptSetHashParam(hHash, HP_HASHVAL, hash.data, 0); if (!rc) { goto loser; } /* OK, we have the data in a hash structure, sign it! */ rc = CryptSignHash(hHash, iOperation->keySpec, NULL, 0, output->data, &output->size); if (!rc) { goto loser; } /* Don't return a signature that might have been broken because of a cosmic * ray, or a broken processor, verify that it is valid... */ rc = CryptVerifySignature(hHash, output->data, output->size, iOperation->hKey, NULL, 0); if (!rc) { goto loser; } /* OK, Microsoft likes to do things completely differently than anyone * else. We need to reverse the data we received here */ ckcapi_ReverseData(output); CryptDestroyHash(hHash); return CKR_OK; loser: /* map the microsoft error */ if (CKR_OK == error) { msError = GetLastError(); switch (msError) { case ERROR_NOT_ENOUGH_MEMORY: error = CKR_HOST_MEMORY; break; case NTE_NO_MEMORY: error = CKR_DEVICE_MEMORY; break; case ERROR_MORE_DATA: return CKR_BUFFER_TOO_SMALL; case ERROR_INVALID_PARAMETER: /* these params were derived from the */ case ERROR_INVALID_HANDLE: /* inputs, so if they are bad, the input */ case NTE_BAD_ALGID: /* data is bad */ case NTE_BAD_HASH: error = CKR_DATA_INVALID; break; case ERROR_BUSY: case NTE_FAIL: case NTE_BAD_UID: error = CKR_DEVICE_ERROR; break; default: error = CKR_GENERAL_ERROR; break; } } if (hHash) { CryptDestroyHash(hHash); } return error; } NSS_IMPLEMENT_DATA const NSSCKMDCryptoOperation ckcapi_mdCryptoOperationRSADecrypt_proto = { NULL, /* etc */ ckcapi_mdCryptoOperationRSAPriv_Destroy, NULL, /* GetFinalLengh - not needed for one shot Decrypt/Encrypt */ ckcapi_mdCryptoOperationRSADecrypt_GetOperationLength, NULL, /* Final - not needed for one shot operation */ NULL, /* Update - not needed for one shot operation */ NULL, /* DigetUpdate - not needed for one shot operation */ ckcapi_mdCryptoOperationRSADecrypt_UpdateFinal, NULL, /* UpdateCombo - not needed for one shot operation */ NULL, /* DigetKey - not needed for one shot operation */ (void *)NULL /* null terminator */ }; NSS_IMPLEMENT_DATA const NSSCKMDCryptoOperation ckcapi_mdCryptoOperationRSASign_proto = { NULL, /* etc */ ckcapi_mdCryptoOperationRSAPriv_Destroy, ckcapi_mdCryptoOperationRSA_GetFinalLength, NULL, /* GetOperationLengh - not needed for one shot Sign/Verify */ NULL, /* Final - not needed for one shot operation */ NULL, /* Update - not needed for one shot operation */ NULL, /* DigetUpdate - not needed for one shot operation */ ckcapi_mdCryptoOperationRSASign_UpdateFinal, NULL, /* UpdateCombo - not needed for one shot operation */ NULL, /* DigetKey - not needed for one shot operation */ (void *)NULL /* null terminator */ }; /********** NSSCKMDMechansim functions ***********************/ /* * ckcapi_mdMechanismRSA_Destroy */ static void ckcapi_mdMechanismRSA_Destroy( NSSCKMDMechanism *mdMechanism, NSSCKFWMechanism *fwMechanism, NSSCKMDInstance *mdInstance, NSSCKFWInstance *fwInstance) { nss_ZFreeIf(fwMechanism); } /* * ckcapi_mdMechanismRSA_GetMinKeySize */ static CK_ULONG ckcapi_mdMechanismRSA_GetMinKeySize( NSSCKMDMechanism *mdMechanism, NSSCKFWMechanism *fwMechanism, NSSCKMDToken *mdToken, NSSCKFWToken *fwToken, NSSCKMDInstance *mdInstance, NSSCKFWInstance *fwInstance, CK_RV *pError) { return 384; } /* * ckcapi_mdMechanismRSA_GetMaxKeySize */ static CK_ULONG ckcapi_mdMechanismRSA_GetMaxKeySize( NSSCKMDMechanism *mdMechanism, NSSCKFWMechanism *fwMechanism, NSSCKMDToken *mdToken, NSSCKFWToken *fwToken, NSSCKMDInstance *mdInstance, NSSCKFWInstance *fwInstance, CK_RV *pError) { return 16384; } /* * ckcapi_mdMechanismRSA_DecryptInit */ static NSSCKMDCryptoOperation * ckcapi_mdMechanismRSA_DecryptInit( NSSCKMDMechanism *mdMechanism, NSSCKFWMechanism *fwMechanism, CK_MECHANISM *pMechanism, NSSCKMDSession *mdSession, NSSCKFWSession *fwSession, NSSCKMDToken *mdToken, NSSCKFWToken *fwToken, NSSCKMDInstance *mdInstance, NSSCKFWInstance *fwInstance, NSSCKMDObject *mdKey, NSSCKFWObject *fwKey, CK_RV *pError) { return ckcapi_mdCryptoOperationRSAPriv_Create( &ckcapi_mdCryptoOperationRSADecrypt_proto, mdMechanism, mdKey, pError); } /* * ckcapi_mdMechanismRSA_SignInit */ static NSSCKMDCryptoOperation * ckcapi_mdMechanismRSA_SignInit( NSSCKMDMechanism *mdMechanism, NSSCKFWMechanism *fwMechanism, CK_MECHANISM *pMechanism, NSSCKMDSession *mdSession, NSSCKFWSession *fwSession, NSSCKMDToken *mdToken, NSSCKFWToken *fwToken, NSSCKMDInstance *mdInstance, NSSCKFWInstance *fwInstance, NSSCKMDObject *mdKey, NSSCKFWObject *fwKey, CK_RV *pError) { return ckcapi_mdCryptoOperationRSAPriv_Create( &ckcapi_mdCryptoOperationRSASign_proto, mdMechanism, mdKey, pError); } NSS_IMPLEMENT_DATA const NSSCKMDMechanism nss_ckcapi_mdMechanismRSA = { (void *)NULL, /* etc */ ckcapi_mdMechanismRSA_Destroy, ckcapi_mdMechanismRSA_GetMinKeySize, ckcapi_mdMechanismRSA_GetMaxKeySize, NULL, /* GetInHardware - default false */ NULL, /* EncryptInit - default errs */ ckcapi_mdMechanismRSA_DecryptInit, NULL, /* DigestInit - default errs*/ ckcapi_mdMechanismRSA_SignInit, NULL, /* VerifyInit - default errs */ ckcapi_mdMechanismRSA_SignInit, /* SignRecoverInit */ NULL, /* VerifyRecoverInit - default errs */ NULL, /* GenerateKey - default errs */ NULL, /* GenerateKeyPair - default errs */ NULL, /* GetWrapKeyLength - default errs */ NULL, /* WrapKey - default errs */ NULL, /* UnwrapKey - default errs */ NULL, /* DeriveKey - default errs */ (void *)NULL /* null terminator */ };