blob: c4b77d27aebf885131d7c8269a55983df486339d [file] [log] [blame]
/* 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);
}