/* 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); } /*** UTF16<-->UTF-8 functions minicking WideCharToMultiByte/MultiByteToWideChar ***/ int utf8GetMaskIndex(unsigned char n) { if((unsigned char)(n + 2) < 0xc2) return 1; // 00~10111111, fe, ff if(n < 0xe0) return 2; // 110xxxxx if(n < 0xf0) return 3; // 1110xxxx if(n < 0xf8) return 4; // 11110xxx if(n < 0xfc) return 5; // 111110xx return 6; // 1111110x } int wc2Utf8Len(wchar_t ** n, int *len) { wchar_t *ch = *n, ch2; int qch; if((0xD800 <= *ch && *ch <= 0xDBFF) && *len) { ch2 = *(ch + 1); if(0xDC00 <= ch2 && ch2 <= 0xDFFF) { qch = 0x10000 + (((*ch - 0xD800) & 0x3ff) << 10) + ((ch2 - 0xDC00) & 0x3ff); (*n)++; (*len)--; } } else qch = (int) *ch; if (qch <= 0x7f) return 1; else if (qch <= 0x7ff) return 2; else if (qch <= 0xffff) return 3; else if (qch <= 0x1fffff) return 4; else if (qch <= 0x3ffffff) return 5; else return 6; } int Utf8ToWideChar(unsigned int unused1, unsigned long unused2, char *sb, int ss, wchar_t * wb, int ws) { static const unsigned char utf8mask[] = { 0, 0xff, 0x1f, 0x0f, 0x07, 0x03, 0x01 }; char *p = (char *)(sb); char *e = (char *)(sb + ss); wchar_t *w = wb; int cnt = 0, t, qch; if (ss < 1) { ss = lstrlenA(sb); e = (char *)(sb + ss); } if (wb && ws) { for (; p < e; ++w) { t = utf8GetMaskIndex(*p); qch = (*p++ & utf8mask[t]); while(p < e && --t) qch <<= 6, qch |= (*p++) & 0x3f; if(qch < 0x10000) { if(cnt <= ws) *w = (wchar_t) qch; cnt++; } else { if (cnt + 2 <= ws) { *w++ = (wchar_t) (0xD800 + (((qch - 0x10000) >> 10) & 0x3ff)), *w = (wchar_t) (0xDC00 + (((qch - 0x10000)) & 0x3ff)); } cnt += 2; } } return (cnt <= ws) ? cnt : ws; } else { for (t; p < e;) { t = utf8GetMaskIndex(*p); qch = (*p++ & utf8mask[t]); while (p < e && --t) qch <<= 6, qch |= (*p++) & 0x3f; if (qch < 0x10000) cnt++; else cnt += 2; } return cnt; } } int WideCharToUtf8(unsigned int unused1, unsigned long unused2, wchar_t * wb, int ws, char *sb, int ss) { wchar_t *p = (wchar_t *)(wb); wchar_t *e = (wchar_t *)(wb + ws); wchar_t *oldp; char *s = sb; int cnt = 0, qch, t; if (ws < 1) { ws = lstrlenW(wb); e = (wchar_t *)(wb + ws); } if (sb && ss) { for (t; p < e; ++p) { oldp = p; t = wc2Utf8Len(&p, &ws); if (p != oldp) { /* unicode surrogates encountered */ qch = 0x10000 + (((*oldp - 0xD800) & 0x3ff) << 10) + ((*p - 0xDC00) & 0x3ff); } else qch = *p; if (qch <= 0x7f) *s++ = (char) (qch), cnt++; else if (qch <= 0x7ff) *s++ = 0xc0 | (char) (qch >> 6), *s++ = 0x80 | (char) (qch & 0x3f), cnt += 2; else if (qch <= 0xffff) *s++ = 0xe0 | (char) (qch >> 12), *s++ = 0x80 | (char) ((qch >> 6) & 0x3f), *s++ = 0x80 | (char) (qch & 0x3f), cnt += 3; else if (qch <= 0x1fffff) *s++ = 0xf0 | (char) (qch >> 18), *s++ = 0x80 | (char) ((qch >> 12) & 0x3f), *s++ = 0x80 | (char) ((qch >> 6) & 0x3f), *s++ = 0x80 | (char) (qch & 0x3f), cnt += 4; else if (qch <= 0x3ffffff) *s++ = 0xf8 | (char) (qch >> 24), *s++ = 0x80 | (char) ((qch >> 18) & 0x3f), *s++ = 0x80 | (char) ((qch >> 12) & 0x3f), *s++ = 0x80 | (char) ((qch >> 6) & 0x3f), *s++ = 0x80 | (char) (qch & 0x3f), cnt += 5; else *s++ = 0xfc | (char) (qch >> 30), *s++ = 0x80 | (char) ((qch >> 24) & 0x3f), *s++ = 0x80 | (char) ((qch >> 18) & 0x3f), *s++ = 0x80 | (char) ((qch >> 12) & 0x3f), *s++ = 0x80 | (char) ((qch >> 6) & 0x3f), *s++ = 0x80 | (char) (qch & 0x3f), cnt += 6; } return (cnt <= ss) ? cnt : ss; } else { for (t; p < e; ++p) { t = wc2Utf8Len(&p, &ws); cnt += t; } return cnt; } } /*** UTF16<-->UTF-8 functions ends ***/ /* * 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 = WideCharToUtf8(CP_UTF8, 0, wide, -1, NULL, 0, NULL); if (size == 0) { return (char *)NULL; } buf = nss_ZNEWARRAY(NULL, char, size); size = WideCharToUtf8(CP_UTF8, 0, wide, -1, buf, size, NULL); 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 = Utf8ToWideChar(CP_UTF8, 0, buf, -1, NULL, 0); if (size == 0) { return (LPWSTR) NULL; } wide = nss_ZNEWARRAY(NULL, WCHAR, size); size = Utf8ToWideChar(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 == providerName ) { 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); }