diff options
Diffstat (limited to 'security/nss/lib/ckfw/capi/cobject.c')
-rw-r--r-- | security/nss/lib/ckfw/capi/cobject.c | 2226 |
1 files changed, 2226 insertions, 0 deletions
diff --git a/security/nss/lib/ckfw/capi/cobject.c b/security/nss/lib/ckfw/capi/cobject.c new file mode 100644 index 000000000..c4b77d27a --- /dev/null +++ b/security/nss/lib/ckfw/capi/cobject.c @@ -0,0 +1,2226 @@ +/* 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); +} |