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