| /* 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 "nssbase.h" |
| |
| /* |
| * ckcapi/cobject.c |
| * |
| * This file implements the NSSCKMDObject object for the |
| * "nss to capi objects" cryptoki module. |
| */ |
| |
| const CK_ATTRIBUTE_TYPE certAttrs[] = { |
| CKA_CLASS, |
| CKA_TOKEN, |
| CKA_PRIVATE, |
| CKA_MODIFIABLE, |
| CKA_LABEL, |
| CKA_CERTIFICATE_TYPE, |
| CKA_SUBJECT, |
| CKA_ISSUER, |
| CKA_SERIAL_NUMBER, |
| CKA_VALUE |
| }; |
| const PRUint32 certAttrsCount = NSS_CKCAPI_ARRAY_SIZE(certAttrs); |
| |
| /* private keys, for now only support RSA */ |
| const CK_ATTRIBUTE_TYPE privKeyAttrs[] = { |
| CKA_CLASS, |
| CKA_TOKEN, |
| CKA_PRIVATE, |
| CKA_MODIFIABLE, |
| CKA_LABEL, |
| CKA_KEY_TYPE, |
| CKA_DERIVE, |
| CKA_LOCAL, |
| CKA_SUBJECT, |
| CKA_SENSITIVE, |
| CKA_DECRYPT, |
| CKA_SIGN, |
| CKA_SIGN_RECOVER, |
| CKA_UNWRAP, |
| CKA_EXTRACTABLE, |
| CKA_ALWAYS_SENSITIVE, |
| CKA_NEVER_EXTRACTABLE, |
| CKA_MODULUS, |
| CKA_PUBLIC_EXPONENT, |
| }; |
| const PRUint32 privKeyAttrsCount = NSS_CKCAPI_ARRAY_SIZE(privKeyAttrs); |
| |
| /* public keys, for now only support RSA */ |
| const CK_ATTRIBUTE_TYPE pubKeyAttrs[] = { |
| CKA_CLASS, |
| CKA_TOKEN, |
| CKA_PRIVATE, |
| CKA_MODIFIABLE, |
| CKA_LABEL, |
| CKA_KEY_TYPE, |
| CKA_DERIVE, |
| CKA_LOCAL, |
| CKA_SUBJECT, |
| CKA_ENCRYPT, |
| CKA_VERIFY, |
| CKA_VERIFY_RECOVER, |
| CKA_WRAP, |
| CKA_MODULUS, |
| CKA_PUBLIC_EXPONENT, |
| }; |
| const PRUint32 pubKeyAttrsCount = NSS_CKCAPI_ARRAY_SIZE(pubKeyAttrs); |
| static const CK_BBOOL ck_true = CK_TRUE; |
| static const CK_BBOOL ck_false = CK_FALSE; |
| static const CK_CERTIFICATE_TYPE ckc_x509 = CKC_X_509; |
| static const CK_KEY_TYPE ckk_rsa = CKK_RSA; |
| static const CK_OBJECT_CLASS cko_certificate = CKO_CERTIFICATE; |
| static const CK_OBJECT_CLASS cko_private_key = CKO_PRIVATE_KEY; |
| static const CK_OBJECT_CLASS cko_public_key = CKO_PUBLIC_KEY; |
| static const NSSItem ckcapi_trueItem = { |
| (void *)&ck_true, (PRUint32)sizeof(CK_BBOOL) |
| }; |
| static const NSSItem ckcapi_falseItem = { |
| (void *)&ck_false, (PRUint32)sizeof(CK_BBOOL) |
| }; |
| static const NSSItem ckcapi_x509Item = { |
| (void *)&ckc_x509, (PRUint32)sizeof(CK_CERTIFICATE_TYPE) |
| }; |
| static const NSSItem ckcapi_rsaItem = { |
| (void *)&ckk_rsa, (PRUint32)sizeof(CK_KEY_TYPE) |
| }; |
| static const NSSItem ckcapi_certClassItem = { |
| (void *)&cko_certificate, (PRUint32)sizeof(CK_OBJECT_CLASS) |
| }; |
| static const NSSItem ckcapi_privKeyClassItem = { |
| (void *)&cko_private_key, (PRUint32)sizeof(CK_OBJECT_CLASS) |
| }; |
| static const NSSItem ckcapi_pubKeyClassItem = { |
| (void *)&cko_public_key, (PRUint32)sizeof(CK_OBJECT_CLASS) |
| }; |
| static const NSSItem ckcapi_emptyItem = { |
| (void *)&ck_true, 0 |
| }; |
| |
| /* |
| * these are utilities. The chould be moved to a new utilities file. |
| */ |
| |
| /* |
| * unwrap a single DER value |
| */ |
| unsigned char * |
| nss_ckcapi_DERUnwrap( |
| unsigned char *src, |
| unsigned int size, |
| unsigned int *outSize, |
| unsigned char **next) |
| { |
| unsigned char *start = src; |
| unsigned char *end = src + size; |
| unsigned int len = 0; |
| |
| /* initialize error condition return values */ |
| *outSize = 0; |
| if (next) { |
| *next = src; |
| } |
| |
| if (size < 2) { |
| return start; |
| } |
| src++; /* skip the tag -- should check it against an expected value! */ |
| len = (unsigned)*src++; |
| if (len & 0x80) { |
| unsigned int count = len & 0x7f; |
| len = 0; |
| |
| if (count + 2 > size) { |
| return start; |
| } |
| while (count-- > 0) { |
| len = (len << 8) | (unsigned)*src++; |
| } |
| } |
| if (len + (src - start) > size) { |
| return start; |
| } |
| if (next) { |
| *next = src + len; |
| } |
| *outSize = len; |
| |
| return src; |
| } |
| |
| /* |
| * convert a PKCS #11 bytestrin into a CK_ULONG, the byte stream must be |
| * less than sizeof (CK_ULONG). |
| */ |
| CK_ULONG |
| nss_ckcapi_DataToInt( |
| NSSItem *data, |
| CK_RV *pError) |
| { |
| CK_ULONG value = 0; |
| unsigned long count = data->size; |
| unsigned char *dataPtr = data->data; |
| unsigned long size = 0; |
| |
| *pError = CKR_OK; |
| |
| while (count--) { |
| value = value << 8; |
| value = value + *dataPtr++; |
| if (size || value) { |
| size++; |
| } |
| } |
| if (size > sizeof(CK_ULONG)) { |
| *pError = CKR_ATTRIBUTE_VALUE_INVALID; |
| } |
| return value; |
| } |
| |
| /* |
| * convert a CK_ULONG to a bytestream. Data is stored in the buffer 'buf' |
| * and must be at least CK_ULONG. Caller must provide buf. |
| */ |
| CK_ULONG |
| nss_ckcapi_IntToData( |
| CK_ULONG value, |
| NSSItem *data, |
| unsigned char *dataPtr, |
| CK_RV *pError) |
| { |
| unsigned long count = 0; |
| unsigned long i; |
| #define SHIFT ((sizeof(CK_ULONG) - 1) * 8) |
| PRBool first = 0; |
| |
| *pError = CKR_OK; |
| |
| data->data = dataPtr; |
| for (i = 0; i < sizeof(CK_ULONG); i++) { |
| unsigned char digit = (unsigned char)((value >> SHIFT) & 0xff); |
| |
| value = value << 8; |
| |
| /* drop leading zero bytes */ |
| if (first && (0 == digit)) { |
| continue; |
| } |
| *dataPtr++ = digit; |
| count++; |
| } |
| data->size = count; |
| return count; |
| } |
| |
| /* |
| * get an attribute from a template. Value is returned in NSS item. |
| * data for the item is owned by the template. |
| */ |
| CK_RV |
| nss_ckcapi_GetAttribute( |
| CK_ATTRIBUTE_TYPE type, |
| CK_ATTRIBUTE *template, |
| CK_ULONG templateSize, |
| NSSItem *item) |
| { |
| CK_ULONG i; |
| |
| for (i = 0; i < templateSize; i++) { |
| if (template[i].type == type) { |
| item->data = template[i].pValue; |
| item->size = template[i].ulValueLen; |
| return CKR_OK; |
| } |
| } |
| return CKR_TEMPLATE_INCOMPLETE; |
| } |
| |
| /* |
| * get an attribute which is type CK_ULONG. |
| */ |
| CK_ULONG |
| nss_ckcapi_GetULongAttribute( |
| CK_ATTRIBUTE_TYPE type, |
| CK_ATTRIBUTE *template, |
| CK_ULONG templateSize, |
| CK_RV *pError) |
| { |
| NSSItem item; |
| |
| *pError = nss_ckcapi_GetAttribute(type, template, templateSize, &item); |
| if (CKR_OK != *pError) { |
| return (CK_ULONG)0; |
| } |
| if (item.size != sizeof(CK_ULONG)) { |
| *pError = CKR_ATTRIBUTE_VALUE_INVALID; |
| return (CK_ULONG)0; |
| } |
| return *(CK_ULONG *)item.data; |
| } |
| |
| /* |
| * get an attribute which is type CK_BBOOL. |
| */ |
| CK_BBOOL |
| nss_ckcapi_GetBoolAttribute( |
| CK_ATTRIBUTE_TYPE type, |
| CK_ATTRIBUTE *template, |
| CK_ULONG templateSize, |
| CK_RV *pError) |
| { |
| NSSItem item; |
| |
| *pError = nss_ckcapi_GetAttribute(type, template, templateSize, &item); |
| if (CKR_OK != *pError) { |
| return (CK_BBOOL)0; |
| } |
| if (item.size != sizeof(CK_BBOOL)) { |
| *pError = CKR_ATTRIBUTE_VALUE_INVALID; |
| return (CK_BBOOL)0; |
| } |
| return *(CK_BBOOL *)item.data; |
| } |
| |
| /* |
| * get an attribute which is type CK_BBOOL. |
| */ |
| char * |
| nss_ckcapi_GetStringAttribute( |
| CK_ATTRIBUTE_TYPE type, |
| CK_ATTRIBUTE *template, |
| CK_ULONG templateSize, |
| CK_RV *pError) |
| { |
| NSSItem item; |
| char *str; |
| |
| /* get the attribute */ |
| *pError = nss_ckcapi_GetAttribute(type, template, templateSize, &item); |
| if (CKR_OK != *pError) { |
| return (char *)NULL; |
| } |
| /* make sure it is null terminated */ |
| str = nss_ZNEWARRAY(NULL, char, item.size + 1); |
| if ((char *)NULL == str) { |
| *pError = CKR_HOST_MEMORY; |
| return (char *)NULL; |
| } |
| |
| nsslibc_memcpy(str, item.data, item.size); |
| str[item.size] = 0; |
| |
| return str; |
| } |
| |
| /* |
| * Return the size in bytes of a wide string, including the terminating null |
| * character |
| */ |
| int |
| nss_ckcapi_WideSize( |
| LPCWSTR wide) |
| { |
| DWORD size; |
| |
| if ((LPWSTR)NULL == wide) { |
| return 0; |
| } |
| size = wcslen(wide) + 1; |
| return size * sizeof(WCHAR); |
| } |
| |
| /* |
| * Covert a Unicode wide character string to a UTF8 string |
| */ |
| char * |
| nss_ckcapi_WideToUTF8( |
| LPCWSTR wide) |
| { |
| DWORD size; |
| char *buf; |
| |
| if ((LPWSTR)NULL == wide) { |
| return (char *)NULL; |
| } |
| |
| size = WideCharToMultiByte(CP_UTF8, 0, wide, -1, NULL, 0, NULL, 0); |
| if (size == 0) { |
| return (char *)NULL; |
| } |
| buf = nss_ZNEWARRAY(NULL, char, size); |
| size = WideCharToMultiByte(CP_UTF8, 0, wide, -1, buf, size, NULL, 0); |
| if (size == 0) { |
| nss_ZFreeIf(buf); |
| return (char *)NULL; |
| } |
| return buf; |
| } |
| |
| /* |
| * Return a Wide String duplicated with nss allocated memory. |
| */ |
| LPWSTR |
| nss_ckcapi_WideDup( |
| LPCWSTR wide) |
| { |
| DWORD len; |
| LPWSTR buf; |
| |
| if ((LPWSTR)NULL == wide) { |
| return (LPWSTR)NULL; |
| } |
| |
| len = wcslen(wide) + 1; |
| |
| buf = nss_ZNEWARRAY(NULL, WCHAR, len); |
| if ((LPWSTR)NULL == buf) { |
| return buf; |
| } |
| nsslibc_memcpy(buf, wide, len * sizeof(WCHAR)); |
| return buf; |
| } |
| |
| /* |
| * Covert a UTF8 string to Unicode wide character |
| */ |
| LPWSTR |
| nss_ckcapi_UTF8ToWide( |
| char *buf) |
| { |
| DWORD size; |
| LPWSTR wide; |
| |
| if ((char *)NULL == buf) { |
| return (LPWSTR)NULL; |
| } |
| |
| size = MultiByteToWideChar(CP_UTF8, 0, buf, -1, NULL, 0); |
| if (size == 0) { |
| return (LPWSTR)NULL; |
| } |
| wide = nss_ZNEWARRAY(NULL, WCHAR, size); |
| size = MultiByteToWideChar(CP_UTF8, 0, buf, -1, wide, size); |
| if (size == 0) { |
| nss_ZFreeIf(wide); |
| return (LPWSTR)NULL; |
| } |
| return wide; |
| } |
| |
| /* |
| * keep all the knowlege of how the internalObject is laid out in this function |
| * |
| * nss_ckcapi_FetchKeyContainer |
| * |
| * fetches the Provider container and info as well as a key handle for a |
| * private key. If something other than a private key is passed in, |
| * this function fails with CKR_KEY_TYPE_INCONSISTENT |
| */ |
| NSS_EXTERN CK_RV |
| nss_ckcapi_FetchKeyContainer( |
| ckcapiInternalObject *iKey, |
| HCRYPTPROV *hProv, |
| DWORD *keySpec, |
| HCRYPTKEY *hKey) |
| { |
| ckcapiCertObject *co; |
| ckcapiKeyObject *ko; |
| BOOL rc, dummy; |
| DWORD msError; |
| |
| switch (iKey->type) { |
| default: |
| case ckcapiRaw: |
| /* can't have raw private keys */ |
| return CKR_KEY_TYPE_INCONSISTENT; |
| case ckcapiCert: |
| if (iKey->objClass != CKO_PRIVATE_KEY) { |
| /* Only private keys have private key provider handles */ |
| return CKR_KEY_TYPE_INCONSISTENT; |
| } |
| co = &iKey->u.cert; |
| |
| /* OK, get the Provider */ |
| rc = CryptAcquireCertificatePrivateKey(co->certContext, |
| CRYPT_ACQUIRE_CACHE_FLAG | |
| CRYPT_ACQUIRE_COMPARE_KEY_FLAG, |
| NULL, hProv, |
| keySpec, &dummy); |
| if (!rc) { |
| goto loser; |
| } |
| break; |
| case ckcapiBareKey: |
| if (iKey->objClass != CKO_PRIVATE_KEY) { |
| /* Only private keys have private key provider handles */ |
| return CKR_KEY_TYPE_INCONSISTENT; |
| } |
| ko = &iKey->u.key; |
| |
| /* OK, get the Provider */ |
| if (0 == ko->hProv) { |
| rc = |
| CryptAcquireContext(hProv, |
| ko->containerName, |
| ko->provName, |
| ko->provInfo.dwProvType, 0); |
| if (!rc) { |
| goto loser; |
| } |
| } else { |
| *hProv = |
| ko->hProv; |
| } |
| *keySpec = ko->provInfo.dwKeySpec; |
| break; |
| } |
| |
| /* and get the crypto handle */ |
| rc = CryptGetUserKey(*hProv, *keySpec, hKey); |
| if (!rc) { |
| goto loser; |
| } |
| return CKR_OK; |
| loser: |
| /* map the microsoft error before leaving */ |
| msError = GetLastError(); |
| switch (msError) { |
| case ERROR_INVALID_HANDLE: |
| case ERROR_INVALID_PARAMETER: |
| case NTE_BAD_KEY: |
| case NTE_NO_KEY: |
| case NTE_BAD_PUBLIC_KEY: |
| case NTE_BAD_KEYSET: |
| case NTE_KEYSET_NOT_DEF: |
| return CKR_KEY_TYPE_INCONSISTENT; |
| case NTE_BAD_UID: |
| case NTE_KEYSET_ENTRY_BAD: |
| return CKR_DEVICE_ERROR; |
| } |
| return CKR_GENERAL_ERROR; |
| } |
| |
| /* |
| * take a DER PUBLIC Key block and return the modulus and exponent |
| */ |
| static void |
| ckcapi_CertPopulateModulusExponent( |
| ckcapiInternalObject *io) |
| { |
| ckcapiKeyParams *kp = &io->u.cert.key; |
| PCCERT_CONTEXT certContext = io->u.cert.certContext; |
| unsigned char *pkData = |
| certContext->pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData; |
| unsigned int size = |
| certContext->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData; |
| unsigned int newSize; |
| unsigned char *ptr, *newptr; |
| |
| /* find the start of the modulus -- this will not give good results if |
| * the key isn't an rsa key! */ |
| ptr = nss_ckcapi_DERUnwrap(pkData, size, &newSize, NULL); |
| kp->modulus.data = nss_ckcapi_DERUnwrap(ptr, newSize, |
| &kp->modulus.size, &newptr); |
| /* changed from signed to unsigned int */ |
| if (0 == *(char *)kp->modulus.data) { |
| kp->modulus.data = ((char *)kp->modulus.data) + 1; |
| kp->modulus.size = kp->modulus.size - 1; |
| } |
| /* changed from signed to unsigned int */ |
| kp->exponent.data = nss_ckcapi_DERUnwrap(newptr, (newptr - ptr) + newSize, |
| &kp->exponent.size, NULL); |
| if (0 == *(char *)kp->exponent.data) { |
| kp->exponent.data = ((char *)kp->exponent.data) + 1; |
| kp->exponent.size = kp->exponent.size - 1; |
| } |
| return; |
| } |
| |
| typedef struct _CAPI_RSA_KEY_BLOB { |
| PUBLICKEYSTRUC header; |
| RSAPUBKEY rsa; |
| char data[1]; |
| } CAPI_RSA_KEY_BLOB; |
| |
| #define CAPI_MODULUS_OFFSET(modSize) 0 |
| #define CAPI_PRIME_1_OFFSET(modSize) (modSize) |
| #define CAPI_PRIME_2_OFFSET(modSize) ((modSize) + (modSize) / 2) |
| #define CAPI_EXPONENT_1_OFFSET(modSize) ((modSize)*2) |
| #define CAPI_EXPONENT_2_OFFSET(modSize) ((modSize)*2 + (modSize) / 2) |
| #define CAPI_COEFFICIENT_OFFSET(modSize) ((modSize)*3) |
| #define CAPI_PRIVATE_EXP_OFFSET(modSize) ((modSize)*3 + (modSize) / 2) |
| |
| void |
| ckcapi_FetchPublicKey( |
| ckcapiInternalObject *io) |
| { |
| ckcapiKeyParams *kp; |
| HCRYPTPROV hProv; |
| DWORD keySpec; |
| HCRYPTKEY hKey = 0; |
| CK_RV error; |
| DWORD bufLen; |
| BOOL rc; |
| unsigned long modulus; |
| char *buf = NULL; |
| CAPI_RSA_KEY_BLOB *blob; |
| |
| error = nss_ckcapi_FetchKeyContainer(io, &hProv, &keySpec, &hKey); |
| if (CKR_OK != error) { |
| goto loser; |
| } |
| kp = (ckcapiCert == io->type) ? &io->u.cert.key : &io->u.key.key; |
| |
| rc = CryptExportKey(hKey, 0, PUBLICKEYBLOB, 0, buf, &bufLen); |
| if (!rc) { |
| goto loser; |
| } |
| buf = nss_ZNEWARRAY(NULL, char, bufLen); |
| rc = CryptExportKey(hKey, 0, PUBLICKEYBLOB, 0, buf, &bufLen); |
| if (!rc) { |
| goto loser; |
| } |
| /* validate the blob */ |
| blob = (CAPI_RSA_KEY_BLOB *)buf; |
| if ((PUBLICKEYBLOB != blob->header.bType) || |
| (0x02 != blob->header.bVersion) || |
| (0x31415352 != blob->rsa.magic)) { |
| goto loser; |
| } |
| modulus = blob->rsa.bitlen / 8; |
| kp->pubKey = buf; |
| buf = NULL; |
| |
| kp->modulus.data = &blob->data[CAPI_MODULUS_OFFSET(modulus)]; |
| kp->modulus.size = modulus; |
| ckcapi_ReverseData(&kp->modulus); |
| nss_ckcapi_IntToData(blob->rsa.pubexp, &kp->exponent, |
| kp->publicExponentData, &error); |
| |
| loser: |
| nss_ZFreeIf(buf); |
| if (0 != hKey) { |
| CryptDestroyKey(hKey); |
| } |
| return; |
| } |
| |
| void |
| ckcapi_FetchPrivateKey( |
| ckcapiInternalObject *io) |
| { |
| ckcapiKeyParams *kp; |
| HCRYPTPROV hProv; |
| DWORD keySpec; |
| HCRYPTKEY hKey = 0; |
| CK_RV error; |
| DWORD bufLen; |
| BOOL rc; |
| unsigned long modulus; |
| char *buf = NULL; |
| CAPI_RSA_KEY_BLOB *blob; |
| |
| error = nss_ckcapi_FetchKeyContainer(io, &hProv, &keySpec, &hKey); |
| if (CKR_OK != error) { |
| goto loser; |
| } |
| kp = (ckcapiCert == io->type) ? &io->u.cert.key : &io->u.key.key; |
| |
| rc = CryptExportKey(hKey, 0, PRIVATEKEYBLOB, 0, buf, &bufLen); |
| if (!rc) { |
| goto loser; |
| } |
| buf = nss_ZNEWARRAY(NULL, char, bufLen); |
| rc = CryptExportKey(hKey, 0, PRIVATEKEYBLOB, 0, buf, &bufLen); |
| if (!rc) { |
| goto loser; |
| } |
| /* validate the blob */ |
| blob = (CAPI_RSA_KEY_BLOB *)buf; |
| if ((PRIVATEKEYBLOB != blob->header.bType) || |
| (0x02 != blob->header.bVersion) || |
| (0x32415352 != blob->rsa.magic)) { |
| goto loser; |
| } |
| modulus = blob->rsa.bitlen / 8; |
| kp->privateKey = buf; |
| buf = NULL; |
| |
| kp->privateExponent.data = &blob->data[CAPI_PRIVATE_EXP_OFFSET(modulus)]; |
| kp->privateExponent.size = modulus; |
| ckcapi_ReverseData(&kp->privateExponent); |
| kp->prime1.data = &blob->data[CAPI_PRIME_1_OFFSET(modulus)]; |
| kp->prime1.size = modulus / 2; |
| ckcapi_ReverseData(&kp->prime1); |
| kp->prime2.data = &blob->data[CAPI_PRIME_2_OFFSET(modulus)]; |
| kp->prime2.size = modulus / 2; |
| ckcapi_ReverseData(&kp->prime2); |
| kp->exponent1.data = &blob->data[CAPI_EXPONENT_1_OFFSET(modulus)]; |
| kp->exponent1.size = modulus / 2; |
| ckcapi_ReverseData(&kp->exponent1); |
| kp->exponent2.data = &blob->data[CAPI_EXPONENT_2_OFFSET(modulus)]; |
| kp->exponent2.size = modulus / 2; |
| ckcapi_ReverseData(&kp->exponent2); |
| kp->coefficient.data = &blob->data[CAPI_COEFFICIENT_OFFSET(modulus)]; |
| kp->coefficient.size = modulus / 2; |
| ckcapi_ReverseData(&kp->coefficient); |
| |
| loser: |
| nss_ZFreeIf(buf); |
| if (0 != hKey) { |
| CryptDestroyKey(hKey); |
| } |
| return; |
| } |
| |
| void |
| ckcapi_PopulateModulusExponent( |
| ckcapiInternalObject *io) |
| { |
| if (ckcapiCert == io->type) { |
| ckcapi_CertPopulateModulusExponent(io); |
| } else { |
| ckcapi_FetchPublicKey(io); |
| } |
| return; |
| } |
| |
| /* |
| * fetch the friendly name attribute. |
| * can only be called with ckcapiCert type objects! |
| */ |
| void |
| ckcapi_FetchLabel( |
| ckcapiInternalObject *io) |
| { |
| ckcapiCertObject *co = &io->u.cert; |
| char *label; |
| PCCERT_CONTEXT certContext = io->u.cert.certContext; |
| char labelDataUTF16[128]; |
| DWORD size = sizeof(labelDataUTF16); |
| DWORD size8 = sizeof(co->labelData); |
| BOOL rv; |
| |
| rv = CertGetCertificateContextProperty(certContext, |
| CERT_FRIENDLY_NAME_PROP_ID, labelDataUTF16, &size); |
| if (rv) { |
| co->labelData = nss_ckcapi_WideToUTF8((LPCWSTR)labelDataUTF16); |
| if ((CHAR *)NULL == co->labelData) { |
| rv = 0; |
| } else { |
| size = strlen(co->labelData); |
| } |
| } |
| label = co->labelData; |
| /* we are presuming a user cert, make sure it has a nickname, even if |
| * Microsoft never gave it one */ |
| if (!rv && co->hasID) { |
| DWORD mserror = GetLastError(); |
| #define DEFAULT_NICKNAME "no Microsoft nickname" |
| label = DEFAULT_NICKNAME; |
| size = sizeof(DEFAULT_NICKNAME); |
| rv = 1; |
| } |
| |
| if (rv) { |
| co->label.data = label; |
| co->label.size = size; |
| } |
| return; |
| } |
| |
| void |
| ckcapi_FetchSerial( |
| ckcapiInternalObject *io) |
| { |
| ckcapiCertObject *co = &io->u.cert; |
| PCCERT_CONTEXT certContext = io->u.cert.certContext; |
| DWORD size = sizeof(co->derSerial); |
| |
| BOOL rc = CryptEncodeObject(X509_ASN_ENCODING, |
| X509_MULTI_BYTE_INTEGER, |
| &certContext->pCertInfo->SerialNumber, |
| co->derSerial, |
| &size); |
| if (rc) { |
| co->serial.data = co->derSerial; |
| co->serial.size = size; |
| } |
| return; |
| } |
| |
| /* |
| * fetch the key ID. |
| */ |
| void |
| ckcapi_FetchID( |
| ckcapiInternalObject *io) |
| { |
| PCCERT_CONTEXT certContext = io->u.cert.certContext; |
| DWORD size = 0; |
| BOOL rc; |
| |
| rc = CertGetCertificateContextProperty(certContext, |
| CERT_KEY_IDENTIFIER_PROP_ID, NULL, &size); |
| if (!rc) { |
| return; |
| } |
| io->idData = nss_ZNEWARRAY(NULL, char, size); |
| if (io->idData == NULL) { |
| return; |
| } |
| |
| rc = CertGetCertificateContextProperty(certContext, |
| CERT_KEY_IDENTIFIER_PROP_ID, io->idData, &size); |
| if (!rc) { |
| nss_ZFreeIf(io->idData); |
| io->idData = NULL; |
| return; |
| } |
| io->id.data = io->idData; |
| io->id.size = size; |
| return; |
| } |
| |
| /* |
| * fetch the hash key. |
| */ |
| void |
| ckcapi_CertFetchHashKey( |
| ckcapiInternalObject *io) |
| { |
| ckcapiCertObject *co = &io->u.cert; |
| PCCERT_CONTEXT certContext = io->u.cert.certContext; |
| DWORD size = certContext->cbCertEncoded; |
| DWORD max = sizeof(io->hashKeyData) - 1; |
| DWORD offset = 0; |
| |
| /* make sure we don't over flow. NOTE: cutting the top of a cert is |
| * not a big issue because the signature for will be unique for the cert */ |
| if (size > max) { |
| offset = size - max; |
| size = max; |
| } |
| |
| nsslibc_memcpy(io->hashKeyData, certContext->pbCertEncoded + offset, size); |
| io->hashKeyData[size] = (char)(io->objClass & 0xff); |
| |
| io->hashKey.data = io->hashKeyData; |
| io->hashKey.size = size + 1; |
| return; |
| } |
| |
| /* |
| * fetch the hash key. |
| */ |
| void |
| ckcapi_KeyFetchHashKey( |
| ckcapiInternalObject *io) |
| { |
| ckcapiKeyObject *ko = &io->u.key; |
| DWORD size; |
| DWORD max = sizeof(io->hashKeyData) - 2; |
| DWORD offset = 0; |
| DWORD provLen = strlen(ko->provName); |
| DWORD containerLen = strlen(ko->containerName); |
| |
| size = provLen + containerLen; |
| |
| /* make sure we don't overflow, try to keep things unique */ |
| if (size > max) { |
| DWORD diff = ((size - max) + 1) / 2; |
| provLen -= diff; |
| containerLen -= diff; |
| size = provLen + containerLen; |
| } |
| |
| nsslibc_memcpy(io->hashKeyData, ko->provName, provLen); |
| nsslibc_memcpy(&io->hashKeyData[provLen], |
| ko->containerName, |
| containerLen); |
| io->hashKeyData[size] = (char)(io->objClass & 0xff); |
| io->hashKeyData[size + 1] = (char)(ko->provInfo.dwKeySpec & 0xff); |
| |
| io->hashKey.data = io->hashKeyData; |
| io->hashKey.size = size + 2; |
| return; |
| } |
| |
| /* |
| * fetch the hash key. |
| */ |
| void |
| ckcapi_FetchHashKey( |
| ckcapiInternalObject *io) |
| { |
| if (ckcapiCert == io->type) { |
| ckcapi_CertFetchHashKey(io); |
| } else { |
| ckcapi_KeyFetchHashKey(io); |
| } |
| return; |
| } |
| |
| const NSSItem * |
| ckcapi_FetchCertAttribute( |
| ckcapiInternalObject *io, |
| CK_ATTRIBUTE_TYPE type) |
| { |
| PCCERT_CONTEXT certContext = io->u.cert.certContext; |
| switch (type) { |
| case CKA_CLASS: |
| return &ckcapi_certClassItem; |
| case CKA_TOKEN: |
| return &ckcapi_trueItem; |
| case CKA_MODIFIABLE: |
| case CKA_PRIVATE: |
| return &ckcapi_falseItem; |
| case CKA_CERTIFICATE_TYPE: |
| return &ckcapi_x509Item; |
| case CKA_LABEL: |
| if (0 == io->u.cert.label.size) { |
| ckcapi_FetchLabel(io); |
| } |
| return &io->u.cert.label; |
| case CKA_SUBJECT: |
| if (0 == io->u.cert.subject.size) { |
| io->u.cert.subject.data = |
| certContext->pCertInfo->Subject.pbData; |
| io->u.cert.subject.size = |
| certContext->pCertInfo->Subject.cbData; |
| } |
| return &io->u.cert.subject; |
| case CKA_ISSUER: |
| if (0 == io->u.cert.issuer.size) { |
| io->u.cert.issuer.data = |
| certContext->pCertInfo->Issuer.pbData; |
| io->u.cert.issuer.size = |
| certContext->pCertInfo->Issuer.cbData; |
| } |
| return &io->u.cert.issuer; |
| case CKA_SERIAL_NUMBER: |
| if (0 == io->u.cert.serial.size) { |
| /* not exactly right. This should be the encoded serial number, but |
| * it's the decoded serial number! */ |
| ckcapi_FetchSerial(io); |
| } |
| return &io->u.cert.serial; |
| case CKA_VALUE: |
| if (0 == io->u.cert.derCert.size) { |
| io->u.cert.derCert.data = |
| io->u.cert.certContext->pbCertEncoded; |
| io->u.cert.derCert.size = |
| io->u.cert.certContext->cbCertEncoded; |
| } |
| return &io->u.cert.derCert; |
| case CKA_ID: |
| if (!io->u.cert.hasID) { |
| return NULL; |
| } |
| if (0 == io->id.size) { |
| ckcapi_FetchID(io); |
| } |
| return &io->id; |
| default: |
| break; |
| } |
| return NULL; |
| } |
| |
| const NSSItem * |
| ckcapi_FetchPubKeyAttribute( |
| ckcapiInternalObject *io, |
| CK_ATTRIBUTE_TYPE type) |
| { |
| PRBool isCertType = (ckcapiCert == io->type); |
| ckcapiKeyParams *kp = isCertType ? &io->u.cert.key : &io->u.key.key; |
| |
| switch (type) { |
| case CKA_CLASS: |
| return &ckcapi_pubKeyClassItem; |
| case CKA_TOKEN: |
| case CKA_LOCAL: |
| case CKA_ENCRYPT: |
| case CKA_VERIFY: |
| case CKA_VERIFY_RECOVER: |
| return &ckcapi_trueItem; |
| case CKA_PRIVATE: |
| case CKA_MODIFIABLE: |
| case CKA_DERIVE: |
| case CKA_WRAP: |
| return &ckcapi_falseItem; |
| case CKA_KEY_TYPE: |
| return &ckcapi_rsaItem; |
| case CKA_LABEL: |
| if (!isCertType) { |
| return &ckcapi_emptyItem; |
| } |
| if (0 == io->u.cert.label.size) { |
| ckcapi_FetchLabel(io); |
| } |
| return &io->u.cert.label; |
| case CKA_SUBJECT: |
| if (!isCertType) { |
| return &ckcapi_emptyItem; |
| } |
| if (0 == io->u.cert.subject.size) { |
| PCCERT_CONTEXT certContext = |
| io->u.cert.certContext; |
| io->u.cert.subject.data = |
| certContext->pCertInfo->Subject.pbData; |
| io->u.cert.subject.size = |
| certContext->pCertInfo->Subject.cbData; |
| } |
| return &io->u.cert.subject; |
| case CKA_MODULUS: |
| if (0 == kp->modulus.size) { |
| ckcapi_PopulateModulusExponent(io); |
| } |
| return &kp->modulus; |
| case CKA_PUBLIC_EXPONENT: |
| if (0 == kp->modulus.size) { |
| ckcapi_PopulateModulusExponent(io); |
| } |
| return &kp->exponent; |
| case CKA_ID: |
| if (0 == io->id.size) { |
| ckcapi_FetchID(io); |
| } |
| return &io->id; |
| default: |
| break; |
| } |
| return NULL; |
| } |
| |
| const NSSItem * |
| ckcapi_FetchPrivKeyAttribute( |
| ckcapiInternalObject *io, |
| CK_ATTRIBUTE_TYPE type) |
| { |
| PRBool isCertType = (ckcapiCert == io->type); |
| ckcapiKeyParams *kp = isCertType ? &io->u.cert.key : &io->u.key.key; |
| |
| switch (type) { |
| case CKA_CLASS: |
| return &ckcapi_privKeyClassItem; |
| case CKA_TOKEN: |
| case CKA_LOCAL: |
| case CKA_SIGN: |
| case CKA_DECRYPT: |
| case CKA_SIGN_RECOVER: |
| return &ckcapi_trueItem; |
| case CKA_SENSITIVE: |
| case CKA_PRIVATE: /* should move in the future */ |
| case CKA_MODIFIABLE: |
| case CKA_DERIVE: |
| case CKA_UNWRAP: |
| case CKA_EXTRACTABLE: /* will probably move in the future */ |
| case CKA_ALWAYS_SENSITIVE: |
| case CKA_NEVER_EXTRACTABLE: |
| return &ckcapi_falseItem; |
| case CKA_KEY_TYPE: |
| return &ckcapi_rsaItem; |
| case CKA_LABEL: |
| if (!isCertType) { |
| return &ckcapi_emptyItem; |
| } |
| if (0 == io->u.cert.label.size) { |
| ckcapi_FetchLabel(io); |
| } |
| return &io->u.cert.label; |
| case CKA_SUBJECT: |
| if (!isCertType) { |
| return &ckcapi_emptyItem; |
| } |
| if (0 == io->u.cert.subject.size) { |
| PCCERT_CONTEXT certContext = |
| io->u.cert.certContext; |
| io->u.cert.subject.data = |
| certContext->pCertInfo->Subject.pbData; |
| io->u.cert.subject.size = |
| certContext->pCertInfo->Subject.cbData; |
| } |
| return &io->u.cert.subject; |
| case CKA_MODULUS: |
| if (0 == kp->modulus.size) { |
| ckcapi_PopulateModulusExponent(io); |
| } |
| return &kp->modulus; |
| case CKA_PUBLIC_EXPONENT: |
| if (0 == kp->modulus.size) { |
| ckcapi_PopulateModulusExponent(io); |
| } |
| return &kp->exponent; |
| case CKA_PRIVATE_EXPONENT: |
| if (0 == kp->privateExponent.size) { |
| ckcapi_FetchPrivateKey(io); |
| } |
| return &kp->privateExponent; |
| case CKA_PRIME_1: |
| if (0 == kp->privateExponent.size) { |
| ckcapi_FetchPrivateKey(io); |
| } |
| return &kp->prime1; |
| case CKA_PRIME_2: |
| if (0 == kp->privateExponent.size) { |
| ckcapi_FetchPrivateKey(io); |
| } |
| return &kp->prime2; |
| case CKA_EXPONENT_1: |
| if (0 == kp->privateExponent.size) { |
| ckcapi_FetchPrivateKey(io); |
| } |
| return &kp->exponent1; |
| case CKA_EXPONENT_2: |
| if (0 == kp->privateExponent.size) { |
| ckcapi_FetchPrivateKey(io); |
| } |
| return &kp->exponent2; |
| case CKA_COEFFICIENT: |
| if (0 == kp->privateExponent.size) { |
| ckcapi_FetchPrivateKey(io); |
| } |
| return &kp->coefficient; |
| case CKA_ID: |
| if (0 == io->id.size) { |
| ckcapi_FetchID(io); |
| } |
| return &io->id; |
| default: |
| return NULL; |
| } |
| } |
| |
| const NSSItem * |
| nss_ckcapi_FetchAttribute( |
| ckcapiInternalObject *io, |
| CK_ATTRIBUTE_TYPE type) |
| { |
| CK_ULONG i; |
| |
| if (io->type == ckcapiRaw) { |
| for (i = 0; i < io->u.raw.n; i++) { |
| if (type == io->u.raw.types[i]) { |
| return &io->u.raw.items[i]; |
| } |
| } |
| return NULL; |
| } |
| /* deal with the common attributes */ |
| switch (io->objClass) { |
| case CKO_CERTIFICATE: |
| return ckcapi_FetchCertAttribute(io, type); |
| case CKO_PRIVATE_KEY: |
| return ckcapi_FetchPrivKeyAttribute(io, type); |
| case CKO_PUBLIC_KEY: |
| return ckcapi_FetchPubKeyAttribute(io, type); |
| } |
| return NULL; |
| } |
| |
| /* |
| * check to see if the certificate already exists |
| */ |
| static PRBool |
| ckcapi_cert_exists( |
| NSSItem *value, |
| ckcapiInternalObject **io) |
| { |
| int count, i; |
| PRUint32 size = 0; |
| ckcapiInternalObject **listp = NULL; |
| CK_ATTRIBUTE myTemplate[2]; |
| CK_OBJECT_CLASS cert_class = CKO_CERTIFICATE; |
| CK_ULONG templateCount = 2; |
| CK_RV error; |
| PRBool found = PR_FALSE; |
| |
| myTemplate[0].type = CKA_CLASS; |
| myTemplate[0].pValue = &cert_class; |
| myTemplate[0].ulValueLen = sizeof(cert_class); |
| myTemplate[1].type = CKA_VALUE; |
| myTemplate[1].pValue = value->data; |
| myTemplate[1].ulValueLen = value->size; |
| |
| count = nss_ckcapi_collect_all_certs(myTemplate, templateCount, &listp, |
| &size, 0, &error); |
| |
| /* free them */ |
| if (count > 1) { |
| *io = listp[0]; |
| found = PR_TRUE; |
| } |
| |
| for (i = 1; i < count; i++) { |
| nss_ckcapi_DestroyInternalObject(listp[i]); |
| } |
| nss_ZFreeIf(listp); |
| return found; |
| } |
| |
| static PRBool |
| ckcapi_cert_hasEmail( |
| PCCERT_CONTEXT certContext) |
| { |
| int count; |
| |
| count = CertGetNameString(certContext, CERT_NAME_EMAIL_TYPE, |
| 0, NULL, NULL, 0); |
| |
| return count > 1 ? PR_TRUE : PR_FALSE; |
| } |
| |
| static PRBool |
| ckcapi_cert_isRoot( |
| PCCERT_CONTEXT certContext) |
| { |
| return CertCompareCertificateName(certContext->dwCertEncodingType, |
| &certContext->pCertInfo->Issuer, &certContext->pCertInfo->Subject); |
| } |
| |
| static PRBool |
| ckcapi_cert_isCA( |
| PCCERT_CONTEXT certContext) |
| { |
| PCERT_EXTENSION extension; |
| CERT_BASIC_CONSTRAINTS2_INFO basicInfo; |
| DWORD size = sizeof(basicInfo); |
| BOOL rc; |
| |
| extension = CertFindExtension(szOID_BASIC_CONSTRAINTS, |
| certContext->pCertInfo->cExtension, |
| certContext->pCertInfo->rgExtension); |
| if ((PCERT_EXTENSION)NULL == extension) { |
| return PR_FALSE; |
| } |
| rc = CryptDecodeObject(X509_ASN_ENCODING, szOID_BASIC_CONSTRAINTS2, |
| extension->Value.pbData, extension->Value.cbData, |
| 0, &basicInfo, &size); |
| if (!rc) { |
| return PR_FALSE; |
| } |
| return (PRBool)basicInfo.fCA; |
| } |
| |
| static CRYPT_KEY_PROV_INFO * |
| ckcapi_cert_getPrivateKeyInfo( |
| PCCERT_CONTEXT certContext, |
| NSSItem *keyID) |
| { |
| BOOL rc; |
| CRYPT_HASH_BLOB msKeyID; |
| DWORD size = 0; |
| CRYPT_KEY_PROV_INFO *prov = NULL; |
| |
| msKeyID.cbData = keyID->size; |
| msKeyID.pbData = keyID->data; |
| |
| rc = CryptGetKeyIdentifierProperty( |
| &msKeyID, |
| CERT_KEY_PROV_INFO_PROP_ID, |
| 0, NULL, NULL, NULL, &size); |
| if (!rc) { |
| return (CRYPT_KEY_PROV_INFO *)NULL; |
| } |
| prov = (CRYPT_KEY_PROV_INFO *)nss_ZAlloc(NULL, size); |
| if ((CRYPT_KEY_PROV_INFO *)prov == NULL) { |
| return (CRYPT_KEY_PROV_INFO *)NULL; |
| } |
| rc = CryptGetKeyIdentifierProperty( |
| &msKeyID, |
| CERT_KEY_PROV_INFO_PROP_ID, |
| 0, NULL, NULL, prov, &size); |
| if (!rc) { |
| nss_ZFreeIf(prov); |
| return (CRYPT_KEY_PROV_INFO *)NULL; |
| } |
| |
| return prov; |
| } |
| |
| static CRYPT_KEY_PROV_INFO * |
| ckcapi_cert_getProvInfo( |
| ckcapiInternalObject *io) |
| { |
| BOOL rc; |
| DWORD size = 0; |
| CRYPT_KEY_PROV_INFO *prov = NULL; |
| |
| rc = CertGetCertificateContextProperty( |
| io->u.cert.certContext, |
| CERT_KEY_PROV_INFO_PROP_ID, |
| NULL, &size); |
| if (!rc) { |
| return (CRYPT_KEY_PROV_INFO *)NULL; |
| } |
| prov = (CRYPT_KEY_PROV_INFO *)nss_ZAlloc(NULL, size); |
| if ((CRYPT_KEY_PROV_INFO *)prov == NULL) { |
| return (CRYPT_KEY_PROV_INFO *)NULL; |
| } |
| rc = CertGetCertificateContextProperty( |
| io->u.cert.certContext, |
| CERT_KEY_PROV_INFO_PROP_ID, |
| prov, &size); |
| if (!rc) { |
| nss_ZFreeIf(prov); |
| return (CRYPT_KEY_PROV_INFO *)NULL; |
| } |
| |
| return prov; |
| } |
| |
| /* forward declaration */ |
| static void |
| ckcapi_removeObjectFromHash( |
| ckcapiInternalObject *io); |
| |
| /* |
| * Finalize - unneeded |
| * Destroy |
| * IsTokenObject - CK_TRUE |
| * GetAttributeCount |
| * GetAttributeTypes |
| * GetAttributeSize |
| * GetAttribute |
| * SetAttribute |
| * GetObjectSize |
| */ |
| |
| static CK_RV |
| ckcapi_mdObject_Destroy( |
| NSSCKMDObject *mdObject, |
| NSSCKFWObject *fwObject, |
| NSSCKMDSession *mdSession, |
| NSSCKFWSession *fwSession, |
| NSSCKMDToken *mdToken, |
| NSSCKFWToken *fwToken, |
| NSSCKMDInstance *mdInstance, |
| NSSCKFWInstance *fwInstance) |
| { |
| ckcapiInternalObject *io = (ckcapiInternalObject *)mdObject->etc; |
| CK_OBJECT_CLASS objClass; |
| BOOL rc; |
| DWORD provType; |
| DWORD msError; |
| PRBool isCertType = (PRBool)(ckcapiCert == io->type); |
| HCERTSTORE hStore = 0; |
| |
| if (ckcapiRaw == io->type) { |
| /* there is not 'object write protected' error, use the next best thing */ |
| return CKR_TOKEN_WRITE_PROTECTED; |
| } |
| |
| objClass = io->objClass; |
| if (CKO_CERTIFICATE == objClass) { |
| PCCERT_CONTEXT certContext; |
| |
| /* get the store */ |
| hStore = CertOpenSystemStore(0, io->u.cert.certStore); |
| if (0 == hStore) { |
| rc = 0; |
| goto loser; |
| } |
| certContext = CertFindCertificateInStore(hStore, X509_ASN_ENCODING, 0, |
| CERT_FIND_EXISTING, io->u.cert.certContext, NULL); |
| if ((PCCERT_CONTEXT)NULL == certContext) { |
| rc = 0; |
| goto loser; |
| } |
| rc = CertDeleteCertificateFromStore(certContext); |
| } else { |
| char *provName = NULL; |
| char *containerName = NULL; |
| HCRYPTPROV hProv; |
| CRYPT_HASH_BLOB msKeyID; |
| |
| if (0 == io->id.size) { |
| ckcapi_FetchID(io); |
| } |
| |
| if (isCertType) { |
| CRYPT_KEY_PROV_INFO *provInfo = ckcapi_cert_getProvInfo(io); |
| provName = nss_ckcapi_WideToUTF8(provInfo->pwszProvName); |
| containerName = nss_ckcapi_WideToUTF8(provInfo->pwszContainerName); |
| provType = provInfo->dwProvType; |
| nss_ZFreeIf(provInfo); |
| } else { |
| provName = io->u.key.provName; |
| containerName = io->u.key.containerName; |
| provType = io->u.key.provInfo.dwProvType; |
| io->u.key.provName = NULL; |
| io->u.key.containerName = NULL; |
| } |
| /* first remove the key id pointer */ |
| msKeyID.cbData = io->id.size; |
| msKeyID.pbData = io->id.data; |
| rc = CryptSetKeyIdentifierProperty(&msKeyID, |
| CERT_KEY_PROV_INFO_PROP_ID, CRYPT_KEYID_DELETE_FLAG, NULL, NULL, NULL); |
| if (rc) { |
| rc = CryptAcquireContext(&hProv, containerName, provName, provType, |
| CRYPT_DELETEKEYSET); |
| } |
| nss_ZFreeIf(provName); |
| nss_ZFreeIf(containerName); |
| } |
| loser: |
| |
| if (hStore) { |
| CertCloseStore(hStore, 0); |
| } |
| if (!rc) { |
| msError = GetLastError(); |
| return CKR_GENERAL_ERROR; |
| } |
| |
| /* remove it from the hash */ |
| ckcapi_removeObjectFromHash(io); |
| |
| /* free the puppy.. */ |
| nss_ckcapi_DestroyInternalObject(io); |
| return CKR_OK; |
| } |
| |
| static CK_BBOOL |
| ckcapi_mdObject_IsTokenObject( |
| NSSCKMDObject *mdObject, |
| NSSCKFWObject *fwObject, |
| NSSCKMDSession *mdSession, |
| NSSCKFWSession *fwSession, |
| NSSCKMDToken *mdToken, |
| NSSCKFWToken *fwToken, |
| NSSCKMDInstance *mdInstance, |
| NSSCKFWInstance *fwInstance) |
| { |
| return CK_TRUE; |
| } |
| |
| static CK_ULONG |
| ckcapi_mdObject_GetAttributeCount( |
| NSSCKMDObject *mdObject, |
| NSSCKFWObject *fwObject, |
| NSSCKMDSession *mdSession, |
| NSSCKFWSession *fwSession, |
| NSSCKMDToken *mdToken, |
| NSSCKFWToken *fwToken, |
| NSSCKMDInstance *mdInstance, |
| NSSCKFWInstance *fwInstance, |
| CK_RV *pError) |
| { |
| ckcapiInternalObject *io = (ckcapiInternalObject *)mdObject->etc; |
| |
| if (ckcapiRaw == io->type) { |
| return io->u.raw.n; |
| } |
| switch (io->objClass) { |
| case CKO_CERTIFICATE: |
| return certAttrsCount; |
| case CKO_PUBLIC_KEY: |
| return pubKeyAttrsCount; |
| case CKO_PRIVATE_KEY: |
| return privKeyAttrsCount; |
| default: |
| break; |
| } |
| return 0; |
| } |
| |
| static CK_RV |
| ckcapi_mdObject_GetAttributeTypes( |
| NSSCKMDObject *mdObject, |
| NSSCKFWObject *fwObject, |
| NSSCKMDSession *mdSession, |
| NSSCKFWSession *fwSession, |
| NSSCKMDToken *mdToken, |
| NSSCKFWToken *fwToken, |
| NSSCKMDInstance *mdInstance, |
| NSSCKFWInstance *fwInstance, |
| CK_ATTRIBUTE_TYPE_PTR typeArray, |
| CK_ULONG ulCount) |
| { |
| ckcapiInternalObject *io = (ckcapiInternalObject *)mdObject->etc; |
| CK_ULONG i; |
| CK_RV error = CKR_OK; |
| const CK_ATTRIBUTE_TYPE *attrs = NULL; |
| CK_ULONG size = ckcapi_mdObject_GetAttributeCount( |
| mdObject, fwObject, mdSession, fwSession, |
| mdToken, fwToken, mdInstance, fwInstance, &error); |
| |
| if (size != ulCount) { |
| return CKR_BUFFER_TOO_SMALL; |
| } |
| if (io->type == ckcapiRaw) { |
| attrs = io->u.raw.types; |
| } else |
| switch (io->objClass) { |
| case CKO_CERTIFICATE: |
| attrs = |
| certAttrs; |
| break; |
| case CKO_PUBLIC_KEY: |
| attrs = |
| pubKeyAttrs; |
| break; |
| case CKO_PRIVATE_KEY: |
| attrs = |
| privKeyAttrs; |
| break; |
| default: |
| return CKR_OK; |
| } |
| |
| for (i = 0; i < size; i++) { |
| typeArray[i] = attrs[i]; |
| } |
| |
| return CKR_OK; |
| } |
| |
| static CK_ULONG |
| ckcapi_mdObject_GetAttributeSize( |
| NSSCKMDObject *mdObject, |
| NSSCKFWObject *fwObject, |
| NSSCKMDSession *mdSession, |
| NSSCKFWSession *fwSession, |
| NSSCKMDToken *mdToken, |
| NSSCKFWToken *fwToken, |
| NSSCKMDInstance *mdInstance, |
| NSSCKFWInstance *fwInstance, |
| CK_ATTRIBUTE_TYPE attribute, |
| CK_RV *pError) |
| { |
| ckcapiInternalObject *io = (ckcapiInternalObject *)mdObject->etc; |
| |
| const NSSItem *b; |
| |
| b = nss_ckcapi_FetchAttribute(io, attribute); |
| |
| if ((const NSSItem *)NULL == b) { |
| *pError = CKR_ATTRIBUTE_TYPE_INVALID; |
| return 0; |
| } |
| return b->size; |
| } |
| |
| static CK_RV |
| ckcapi_mdObject_SetAttribute( |
| NSSCKMDObject *mdObject, |
| NSSCKFWObject *fwObject, |
| NSSCKMDSession *mdSession, |
| NSSCKFWSession *fwSession, |
| NSSCKMDToken *mdToken, |
| NSSCKFWToken *fwToken, |
| NSSCKMDInstance *mdInstance, |
| NSSCKFWInstance *fwInstance, |
| CK_ATTRIBUTE_TYPE attribute, |
| NSSItem *value) |
| { |
| return CKR_OK; |
| } |
| |
| static NSSCKFWItem |
| ckcapi_mdObject_GetAttribute( |
| NSSCKMDObject *mdObject, |
| NSSCKFWObject *fwObject, |
| NSSCKMDSession *mdSession, |
| NSSCKFWSession *fwSession, |
| NSSCKMDToken *mdToken, |
| NSSCKFWToken *fwToken, |
| NSSCKMDInstance *mdInstance, |
| NSSCKFWInstance *fwInstance, |
| CK_ATTRIBUTE_TYPE attribute, |
| CK_RV *pError) |
| { |
| NSSCKFWItem mdItem; |
| ckcapiInternalObject *io = (ckcapiInternalObject *)mdObject->etc; |
| |
| mdItem.needsFreeing = PR_FALSE; |
| mdItem.item = (NSSItem *)nss_ckcapi_FetchAttribute(io, attribute); |
| |
| if ((NSSItem *)NULL == mdItem.item) { |
| *pError = CKR_ATTRIBUTE_TYPE_INVALID; |
| } |
| |
| return mdItem; |
| } |
| |
| static CK_ULONG |
| ckcapi_mdObject_GetObjectSize( |
| NSSCKMDObject *mdObject, |
| NSSCKFWObject *fwObject, |
| NSSCKMDSession *mdSession, |
| NSSCKFWSession *fwSession, |
| NSSCKMDToken *mdToken, |
| NSSCKFWToken *fwToken, |
| NSSCKMDInstance *mdInstance, |
| NSSCKFWInstance *fwInstance, |
| CK_RV *pError) |
| { |
| ckcapiInternalObject *io = (ckcapiInternalObject *)mdObject->etc; |
| CK_ULONG rv = 1; |
| |
| /* size is irrelevant to this token */ |
| return rv; |
| } |
| |
| static const NSSCKMDObject |
| ckcapi_prototype_mdObject = { |
| (void *)NULL, /* etc */ |
| NULL, /* Finalize */ |
| ckcapi_mdObject_Destroy, |
| ckcapi_mdObject_IsTokenObject, |
| ckcapi_mdObject_GetAttributeCount, |
| ckcapi_mdObject_GetAttributeTypes, |
| ckcapi_mdObject_GetAttributeSize, |
| ckcapi_mdObject_GetAttribute, |
| NULL, /* FreeAttribute */ |
| ckcapi_mdObject_SetAttribute, |
| ckcapi_mdObject_GetObjectSize, |
| (void *)NULL /* null terminator */ |
| }; |
| |
| static nssHash *ckcapiInternalObjectHash = NULL; |
| |
| NSS_IMPLEMENT NSSCKMDObject * |
| nss_ckcapi_CreateMDObject( |
| NSSArena *arena, |
| ckcapiInternalObject *io, |
| CK_RV *pError) |
| { |
| if ((nssHash *)NULL == ckcapiInternalObjectHash) { |
| ckcapiInternalObjectHash = nssHash_CreateItem(NULL, 10); |
| } |
| if (ckcapiCert == io->type) { |
| /* the hash key, not a cryptographic key */ |
| NSSItem *key = &io->hashKey; |
| ckcapiInternalObject *old_o = NULL; |
| |
| if (key->size == 0) { |
| ckcapi_FetchHashKey(io); |
| } |
| old_o = (ckcapiInternalObject *) |
| nssHash_Lookup(ckcapiInternalObjectHash, key); |
| if (!old_o) { |
| nssHash_Add(ckcapiInternalObjectHash, key, io); |
| } else if (old_o != io) { |
| nss_ckcapi_DestroyInternalObject(io); |
| io = old_o; |
| } |
| } |
| |
| if ((void *)NULL == io->mdObject.etc) { |
| (void)nsslibc_memcpy(&io->mdObject, &ckcapi_prototype_mdObject, |
| sizeof(ckcapi_prototype_mdObject)); |
| io->mdObject.etc = (void *)io; |
| } |
| return &io->mdObject; |
| } |
| |
| static void |
| ckcapi_removeObjectFromHash( |
| ckcapiInternalObject *io) |
| { |
| NSSItem *key = &io->hashKey; |
| |
| if ((nssHash *)NULL == ckcapiInternalObjectHash) { |
| return; |
| } |
| if (key->size == 0) { |
| ckcapi_FetchHashKey(io); |
| } |
| nssHash_Remove(ckcapiInternalObjectHash, key); |
| return; |
| } |
| |
| void |
| nss_ckcapi_DestroyInternalObject( |
| ckcapiInternalObject *io) |
| { |
| switch (io->type) { |
| case ckcapiRaw: |
| return; |
| case ckcapiCert: |
| CertFreeCertificateContext(io->u.cert.certContext); |
| nss_ZFreeIf(io->u.cert.labelData); |
| nss_ZFreeIf(io->u.cert.key.privateKey); |
| nss_ZFreeIf(io->u.cert.key.pubKey); |
| nss_ZFreeIf(io->idData); |
| break; |
| case ckcapiBareKey: |
| nss_ZFreeIf(io->u.key.provInfo.pwszContainerName); |
| nss_ZFreeIf(io->u.key.provInfo.pwszProvName); |
| nss_ZFreeIf(io->u.key.provName); |
| nss_ZFreeIf(io->u.key.containerName); |
| nss_ZFreeIf(io->u.key.key.privateKey); |
| nss_ZFreeIf(io->u.key.key.pubKey); |
| if (0 != io->u.key.hProv) { |
| CryptReleaseContext(io->u.key.hProv, 0); |
| } |
| nss_ZFreeIf(io->idData); |
| break; |
| } |
| nss_ZFreeIf(io); |
| return; |
| } |
| |
| static ckcapiInternalObject * |
| nss_ckcapi_CreateCertificate( |
| NSSCKFWSession *fwSession, |
| CK_ATTRIBUTE_PTR pTemplate, |
| CK_ULONG ulAttributeCount, |
| CK_RV *pError) |
| { |
| NSSItem value; |
| NSSItem keyID; |
| char *storeStr; |
| ckcapiInternalObject *io = NULL; |
| PCCERT_CONTEXT certContext = NULL; |
| PCCERT_CONTEXT storedCertContext = NULL; |
| CRYPT_KEY_PROV_INFO *prov_info = NULL; |
| char *nickname = NULL; |
| HCERTSTORE hStore = 0; |
| DWORD msError = 0; |
| PRBool hasID; |
| CK_RV dummy; |
| BOOL rc; |
| |
| *pError = nss_ckcapi_GetAttribute(CKA_VALUE, pTemplate, |
| ulAttributeCount, &value); |
| |
| if (CKR_OK != *pError) { |
| return (ckcapiInternalObject *)NULL; |
| } |
| |
| *pError = nss_ckcapi_GetAttribute(CKA_ID, pTemplate, |
| ulAttributeCount, &keyID); |
| |
| if (CKR_OK != *pError) { |
| return (ckcapiInternalObject *)NULL; |
| } |
| |
| if (ckcapi_cert_exists(&value, &io)) { |
| return io; |
| } |
| |
| /* OK, we are creating a new one, figure out what store it belongs to.. |
| * first get a certContext handle.. */ |
| certContext = CertCreateCertificateContext(X509_ASN_ENCODING, |
| value.data, value.size); |
| if ((PCCERT_CONTEXT)NULL == certContext) { |
| msError = GetLastError(); |
| *pError = CKR_ATTRIBUTE_VALUE_INVALID; |
| goto loser; |
| } |
| |
| /* do we have a private key laying around... */ |
| prov_info = ckcapi_cert_getPrivateKeyInfo(certContext, &keyID); |
| if (prov_info) { |
| CRYPT_DATA_BLOB msKeyID; |
| storeStr = "My"; |
| hasID = PR_TRUE; |
| rc = CertSetCertificateContextProperty(certContext, |
| CERT_KEY_PROV_INFO_PROP_ID, |
| 0, prov_info); |
| nss_ZFreeIf(prov_info); |
| if (!rc) { |
| msError = GetLastError(); |
| *pError = CKR_DEVICE_ERROR; |
| goto loser; |
| } |
| msKeyID.cbData = keyID.size; |
| msKeyID.pbData = keyID.data; |
| rc = CertSetCertificateContextProperty(certContext, |
| CERT_KEY_IDENTIFIER_PROP_ID, |
| 0, &msKeyID); |
| if (!rc) { |
| msError = GetLastError(); |
| *pError = CKR_DEVICE_ERROR; |
| goto loser; |
| } |
| |
| /* does it look like a CA */ |
| } else if (ckcapi_cert_isCA(certContext)) { |
| storeStr = ckcapi_cert_isRoot(certContext) ? "CA" : "Root"; |
| /* does it look like an S/MIME cert */ |
| } else if (ckcapi_cert_hasEmail(certContext)) { |
| storeStr = "AddressBook"; |
| } else { |
| /* just pick a store */ |
| storeStr = "CA"; |
| } |
| |
| /* get the nickname, not an error if we can't find it */ |
| nickname = nss_ckcapi_GetStringAttribute(CKA_LABEL, pTemplate, |
| ulAttributeCount, &dummy); |
| if (nickname) { |
| LPWSTR nicknameUTF16 = NULL; |
| CRYPT_DATA_BLOB nicknameBlob; |
| |
| nicknameUTF16 = nss_ckcapi_UTF8ToWide(nickname); |
| nss_ZFreeIf(nickname); |
| nickname = NULL; |
| if ((LPWSTR)NULL == nicknameUTF16) { |
| *pError = CKR_HOST_MEMORY; |
| goto loser; |
| } |
| nicknameBlob.cbData = nss_ckcapi_WideSize(nicknameUTF16); |
| nicknameBlob.pbData = (BYTE *)nicknameUTF16; |
| rc = CertSetCertificateContextProperty(certContext, |
| CERT_FRIENDLY_NAME_PROP_ID, 0, &nicknameBlob); |
| nss_ZFreeIf(nicknameUTF16); |
| if (!rc) { |
| msError = GetLastError(); |
| *pError = CKR_DEVICE_ERROR; |
| goto loser; |
| } |
| } |
| |
| hStore = CertOpenSystemStore((HCRYPTPROV)NULL, storeStr); |
| if (0 == hStore) { |
| msError = GetLastError(); |
| *pError = CKR_DEVICE_ERROR; |
| goto loser; |
| } |
| |
| rc = CertAddCertificateContextToStore(hStore, certContext, |
| CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES, &storedCertContext); |
| CertFreeCertificateContext(certContext); |
| certContext = NULL; |
| CertCloseStore(hStore, 0); |
| hStore = 0; |
| if (!rc) { |
| msError = GetLastError(); |
| *pError = CKR_DEVICE_ERROR; |
| goto loser; |
| } |
| |
| io = nss_ZNEW(NULL, ckcapiInternalObject); |
| if ((ckcapiInternalObject *)NULL == io) { |
| *pError = CKR_HOST_MEMORY; |
| goto loser; |
| } |
| io->type = ckcapiCert; |
| io->objClass = CKO_CERTIFICATE; |
| io->u.cert.certContext = storedCertContext; |
| io->u.cert.hasID = hasID; |
| return io; |
| |
| loser: |
| if (certContext) { |
| CertFreeCertificateContext(certContext); |
| certContext = NULL; |
| } |
| if (storedCertContext) { |
| CertFreeCertificateContext(storedCertContext); |
| storedCertContext = NULL; |
| } |
| if (0 != hStore) { |
| CertCloseStore(hStore, 0); |
| } |
| return (ckcapiInternalObject *)NULL; |
| } |
| |
| static char * |
| ckcapi_getDefaultProvider( |
| CK_RV *pError) |
| { |
| char *name = NULL; |
| BOOL rc; |
| DWORD nameLength = 0; |
| |
| rc = CryptGetDefaultProvider(PROV_RSA_FULL, NULL, CRYPT_USER_DEFAULT, NULL, |
| &nameLength); |
| if (!rc) { |
| return (char *)NULL; |
| } |
| |
| name = nss_ZNEWARRAY(NULL, char, nameLength); |
| if ((char *)NULL == name) { |
| return (char *)NULL; |
| } |
| rc = CryptGetDefaultProvider(PROV_RSA_FULL, NULL, CRYPT_USER_DEFAULT, name, |
| &nameLength); |
| if (!rc) { |
| nss_ZFreeIf(name); |
| return (char *)NULL; |
| } |
| |
| return name; |
| } |
| |
| static char * |
| ckcapi_getContainer( |
| CK_RV *pError, |
| NSSItem *id) |
| { |
| RPC_STATUS rstat; |
| UUID uuid; |
| char *uuidStr; |
| char *container; |
| |
| rstat = UuidCreate(&uuid); |
| rstat = UuidToString(&uuid, &uuidStr); |
| |
| /* convert it from rcp memory to our own */ |
| container = nssUTF8_Duplicate(uuidStr, NULL); |
| RpcStringFree(&uuidStr); |
| |
| return container; |
| } |
| |
| static CK_RV |
| ckcapi_buildPrivateKeyBlob( |
| NSSItem *keyBlob, |
| NSSItem *modulus, |
| NSSItem *publicExponent, |
| NSSItem *privateExponent, |
| NSSItem *prime1, |
| NSSItem *prime2, |
| NSSItem *exponent1, |
| NSSItem *exponent2, |
| NSSItem *coefficient, |
| PRBool isKeyExchange) |
| { |
| CAPI_RSA_KEY_BLOB *keyBlobData = NULL; |
| unsigned char *target; |
| unsigned long modSize = modulus->size; |
| unsigned long dataSize; |
| CK_RV error = CKR_OK; |
| |
| /* validate extras */ |
| if (privateExponent->size != modSize) { |
| error = CKR_ATTRIBUTE_VALUE_INVALID; |
| goto loser; |
| } |
| if (prime1->size != modSize / 2) { |
| error = CKR_ATTRIBUTE_VALUE_INVALID; |
| goto loser; |
| } |
| if (prime2->size != modSize / 2) { |
| error = CKR_ATTRIBUTE_VALUE_INVALID; |
| goto loser; |
| } |
| if (exponent1->size != modSize / 2) { |
| error = CKR_ATTRIBUTE_VALUE_INVALID; |
| goto loser; |
| } |
| if (exponent2->size != modSize / 2) { |
| error = CKR_ATTRIBUTE_VALUE_INVALID; |
| goto loser; |
| } |
| if (coefficient->size != modSize / 2) { |
| error = CKR_ATTRIBUTE_VALUE_INVALID; |
| goto loser; |
| } |
| dataSize = (modSize * 4) + (modSize / 2) + sizeof(CAPI_RSA_KEY_BLOB); |
| keyBlobData = (CAPI_RSA_KEY_BLOB *)nss_ZAlloc(NULL, dataSize); |
| if ((CAPI_RSA_KEY_BLOB *)NULL == keyBlobData) { |
| error = CKR_HOST_MEMORY; |
| goto loser; |
| } |
| |
| keyBlobData->header.bType = PRIVATEKEYBLOB; |
| keyBlobData->header.bVersion = 0x02; |
| keyBlobData->header.reserved = 0x00; |
| keyBlobData->header.aiKeyAlg = isKeyExchange ? CALG_RSA_KEYX : CALG_RSA_SIGN; |
| keyBlobData->rsa.magic = 0x32415352; |
| keyBlobData->rsa.bitlen = modSize * 8; |
| keyBlobData->rsa.pubexp = nss_ckcapi_DataToInt(publicExponent, &error); |
| if (CKR_OK != error) { |
| goto loser; |
| } |
| |
| target = &keyBlobData->data[CAPI_MODULUS_OFFSET(modSize)]; |
| nsslibc_memcpy(target, modulus->data, modulus->size); |
| modulus->data = target; |
| ckcapi_ReverseData(modulus); |
| |
| target = &keyBlobData->data[CAPI_PRIVATE_EXP_OFFSET(modSize)]; |
| nsslibc_memcpy(target, privateExponent->data, privateExponent->size); |
| privateExponent->data = target; |
| ckcapi_ReverseData(privateExponent); |
| |
| target = &keyBlobData->data[CAPI_PRIME_1_OFFSET(modSize)]; |
| nsslibc_memcpy(target, prime1->data, prime1->size); |
| prime1->data = target; |
| ckcapi_ReverseData(prime1); |
| |
| target = &keyBlobData->data[CAPI_PRIME_2_OFFSET(modSize)]; |
| nsslibc_memcpy(target, prime2->data, prime2->size); |
| prime2->data = target; |
| ckcapi_ReverseData(prime2); |
| |
| target = &keyBlobData->data[CAPI_EXPONENT_1_OFFSET(modSize)]; |
| nsslibc_memcpy(target, exponent1->data, exponent1->size); |
| exponent1->data = target; |
| ckcapi_ReverseData(exponent1); |
| |
| target = &keyBlobData->data[CAPI_EXPONENT_2_OFFSET(modSize)]; |
| nsslibc_memcpy(target, exponent2->data, exponent2->size); |
| exponent2->data = target; |
| ckcapi_ReverseData(exponent2); |
| |
| target = &keyBlobData->data[CAPI_COEFFICIENT_OFFSET(modSize)]; |
| nsslibc_memcpy(target, coefficient->data, coefficient->size); |
| coefficient->data = target; |
| ckcapi_ReverseData(coefficient); |
| |
| keyBlob->data = keyBlobData; |
| keyBlob->size = dataSize; |
| |
| return CKR_OK; |
| |
| loser: |
| nss_ZFreeIf(keyBlobData); |
| return error; |
| } |
| |
| static ckcapiInternalObject * |
| nss_ckcapi_CreatePrivateKey( |
| NSSCKFWSession *fwSession, |
| CK_ATTRIBUTE_PTR pTemplate, |
| CK_ULONG ulAttributeCount, |
| CK_RV *pError) |
| { |
| NSSItem modulus; |
| NSSItem publicExponent; |
| NSSItem privateExponent; |
| NSSItem exponent1; |
| NSSItem exponent2; |
| NSSItem prime1; |
| NSSItem prime2; |
| NSSItem coefficient; |
| NSSItem keyID; |
| NSSItem keyBlob; |
| ckcapiInternalObject *io = NULL; |
| char *providerName = NULL; |
| char *containerName = NULL; |
| char *idData = NULL; |
| CRYPT_KEY_PROV_INFO provInfo; |
| CRYPT_HASH_BLOB msKeyID; |
| CK_KEY_TYPE keyType; |
| HCRYPTPROV hProv = 0; |
| HCRYPTKEY hKey = 0; |
| PRBool decrypt; |
| DWORD keySpec; |
| DWORD msError; |
| BOOL rc; |
| |
| keyType = nss_ckcapi_GetULongAttribute(CKA_KEY_TYPE, pTemplate, ulAttributeCount, pError); |
| if (CKR_OK != *pError) { |
| return (ckcapiInternalObject *)NULL; |
| } |
| if (CKK_RSA != keyType) { |
| *pError = CKR_ATTRIBUTE_VALUE_INVALID; |
| return (ckcapiInternalObject *)NULL; |
| } |
| |
| decrypt = nss_ckcapi_GetBoolAttribute(CKA_DECRYPT, |
| pTemplate, ulAttributeCount, pError); |
| if (CKR_TEMPLATE_INCOMPLETE == *pError) { |
| decrypt = PR_TRUE; /* default to true */ |
| } |
| decrypt = decrypt || nss_ckcapi_GetBoolAttribute(CKA_UNWRAP, |
| pTemplate, ulAttributeCount, pError); |
| if (CKR_TEMPLATE_INCOMPLETE == *pError) { |
| decrypt = PR_TRUE; /* default to true */ |
| } |
| keySpec = decrypt ? AT_KEYEXCHANGE : AT_SIGNATURE; |
| |
| *pError = nss_ckcapi_GetAttribute(CKA_MODULUS, pTemplate, |
| ulAttributeCount, &modulus); |
| if (CKR_OK != *pError) { |
| return (ckcapiInternalObject *)NULL; |
| } |
| *pError = nss_ckcapi_GetAttribute(CKA_PUBLIC_EXPONENT, pTemplate, |
| ulAttributeCount, &publicExponent); |
| if (CKR_OK != *pError) { |
| return (ckcapiInternalObject *)NULL; |
| } |
| *pError = nss_ckcapi_GetAttribute(CKA_PRIVATE_EXPONENT, pTemplate, |
| ulAttributeCount, &privateExponent); |
| if (CKR_OK != *pError) { |
| return (ckcapiInternalObject *)NULL; |
| } |
| *pError = nss_ckcapi_GetAttribute(CKA_PRIME_1, pTemplate, |
| ulAttributeCount, &prime1); |
| if (CKR_OK != *pError) { |
| return (ckcapiInternalObject *)NULL; |
| } |
| *pError = nss_ckcapi_GetAttribute(CKA_PRIME_2, pTemplate, |
| ulAttributeCount, &prime2); |
| if (CKR_OK != *pError) { |
| return (ckcapiInternalObject *)NULL; |
| } |
| *pError = nss_ckcapi_GetAttribute(CKA_EXPONENT_1, pTemplate, |
| ulAttributeCount, &exponent1); |
| if (CKR_OK != *pError) { |
| return (ckcapiInternalObject *)NULL; |
| } |
| *pError = nss_ckcapi_GetAttribute(CKA_EXPONENT_2, pTemplate, |
| ulAttributeCount, &exponent2); |
| if (CKR_OK != *pError) { |
| return (ckcapiInternalObject *)NULL; |
| } |
| *pError = nss_ckcapi_GetAttribute(CKA_COEFFICIENT, pTemplate, |
| ulAttributeCount, &coefficient); |
| if (CKR_OK != *pError) { |
| return (ckcapiInternalObject *)NULL; |
| } |
| *pError = nss_ckcapi_GetAttribute(CKA_ID, pTemplate, |
| ulAttributeCount, &keyID); |
| if (CKR_OK != *pError) { |
| return (ckcapiInternalObject *)NULL; |
| } |
| providerName = ckcapi_getDefaultProvider(pError); |
| if ((char *)NULL == providerName) { |
| return (ckcapiInternalObject *)NULL; |
| } |
| containerName = ckcapi_getContainer(pError, &keyID); |
| if ((char *)NULL == containerName) { |
| goto loser; |
| } |
| rc = CryptAcquireContext(&hProv, containerName, providerName, |
| PROV_RSA_FULL, CRYPT_NEWKEYSET); |
| if (!rc) { |
| msError = GetLastError(); |
| *pError = CKR_DEVICE_ERROR; |
| goto loser; |
| } |
| |
| *pError = ckcapi_buildPrivateKeyBlob( |
| &keyBlob, |
| &modulus, |
| &publicExponent, |
| &privateExponent, |
| &prime1, |
| &prime2, |
| &exponent1, |
| &exponent2, |
| &coefficient, |
| decrypt); |
| if (CKR_OK != *pError) { |
| goto loser; |
| } |
| |
| rc = CryptImportKey(hProv, keyBlob.data, keyBlob.size, |
| 0, CRYPT_EXPORTABLE, &hKey); |
| if (!rc) { |
| msError = GetLastError(); |
| *pError = CKR_DEVICE_ERROR; |
| goto loser; |
| } |
| |
| idData = nss_ZNEWARRAY(NULL, char, keyID.size); |
| if ((void *)NULL == idData) { |
| *pError = CKR_HOST_MEMORY; |
| goto loser; |
| } |
| nsslibc_memcpy(idData, keyID.data, keyID.size); |
| |
| provInfo.pwszContainerName = nss_ckcapi_UTF8ToWide(containerName); |
| provInfo.pwszProvName = nss_ckcapi_UTF8ToWide(providerName); |
| provInfo.dwProvType = PROV_RSA_FULL; |
| provInfo.dwFlags = 0; |
| provInfo.cProvParam = 0; |
| provInfo.rgProvParam = NULL; |
| provInfo.dwKeySpec = keySpec; |
| |
| msKeyID.cbData = keyID.size; |
| msKeyID.pbData = keyID.data; |
| |
| rc = CryptSetKeyIdentifierProperty(&msKeyID, CERT_KEY_PROV_INFO_PROP_ID, |
| 0, NULL, NULL, &provInfo); |
| if (!rc) { |
| goto loser; |
| } |
| |
| /* handle error here */ |
| io = nss_ZNEW(NULL, ckcapiInternalObject); |
| if ((ckcapiInternalObject *)NULL == io) { |
| *pError = CKR_HOST_MEMORY; |
| goto loser; |
| } |
| io->type = ckcapiBareKey; |
| io->objClass = CKO_PRIVATE_KEY; |
| io->u.key.provInfo = provInfo; |
| io->u.key.provName = providerName; |
| io->u.key.containerName = containerName; |
| io->u.key.hProv = hProv; /* save the handle */ |
| io->idData = idData; |
| io->id.data = idData; |
| io->id.size = keyID.size; |
| /* done with the key handle */ |
| CryptDestroyKey(hKey); |
| return io; |
| |
| loser: |
| nss_ZFreeIf(containerName); |
| nss_ZFreeIf(providerName); |
| nss_ZFreeIf(idData); |
| if (0 != hProv) { |
| CryptReleaseContext(hProv, 0); |
| } |
| if (0 != hKey) { |
| CryptDestroyKey(hKey); |
| } |
| return (ckcapiInternalObject *)NULL; |
| } |
| |
| NSS_EXTERN NSSCKMDObject * |
| nss_ckcapi_CreateObject( |
| NSSCKFWSession *fwSession, |
| CK_ATTRIBUTE_PTR pTemplate, |
| CK_ULONG ulAttributeCount, |
| CK_RV *pError) |
| { |
| CK_OBJECT_CLASS objClass; |
| ckcapiInternalObject *io = NULL; |
| CK_BBOOL isToken; |
| |
| /* |
| * only create token objects |
| */ |
| isToken = nss_ckcapi_GetBoolAttribute(CKA_TOKEN, pTemplate, |
| ulAttributeCount, pError); |
| if (CKR_OK != *pError) { |
| return (NSSCKMDObject *)NULL; |
| } |
| if (!isToken) { |
| *pError = CKR_ATTRIBUTE_VALUE_INVALID; |
| return (NSSCKMDObject *)NULL; |
| } |
| |
| /* |
| * only create keys and certs. |
| */ |
| objClass = nss_ckcapi_GetULongAttribute(CKA_CLASS, pTemplate, |
| ulAttributeCount, pError); |
| if (CKR_OK != *pError) { |
| return (NSSCKMDObject *)NULL; |
| } |
| #ifdef notdef |
| if (objClass == CKO_PUBLIC_KEY) { |
| return CKR_OK; /* fake public key creation, happens as a side effect of |
| * private key creation */ |
| } |
| #endif |
| if (objClass == CKO_CERTIFICATE) { |
| io = nss_ckcapi_CreateCertificate(fwSession, pTemplate, |
| ulAttributeCount, pError); |
| } else if (objClass == CKO_PRIVATE_KEY) { |
| io = nss_ckcapi_CreatePrivateKey(fwSession, pTemplate, |
| ulAttributeCount, pError); |
| } else { |
| *pError = CKR_ATTRIBUTE_VALUE_INVALID; |
| } |
| |
| if ((ckcapiInternalObject *)NULL == io) { |
| return (NSSCKMDObject *)NULL; |
| } |
| return nss_ckcapi_CreateMDObject(NULL, io, pError); |
| } |