mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-15 12:00:38 +01:00
a572ea8ca3
bug1095307, bug1073330(backout), bug1084986, bug1050069, bug942172, bug1054547, bug532081, bug1096348, bug1058870, bug1093940, bug1102985, bug1112461, bug1094492, bug112029, bug1119983, bug1120685, bug1120691, bug1113632, bug863076, bug1082973, bug1124539, bug1117617, bug1117621, bug1121273, bug753136, bug921684, bug1132818, bug1125375, bug647690, bug1055441, bug1134455, bug975010, bug950369, bug1128367, bug1129573, bug1136095, bug1117897, bug1113453, bug1061725, bug1073330, bug1111901, bug1083900, bug1136095, bug1138820, bug1096741, bug1134548, bug345725, bug950348, bug950344, bug1151037, bug991783, bug1153994
1925 lines
50 KiB
C
1925 lines
50 KiB
C
/* 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 "ckmk.h"
|
|
#include "nssbase.h"
|
|
|
|
#include "secdert.h" /* for DER_INTEGER */
|
|
#include "string.h"
|
|
|
|
/* asn1 encoder (to build pkcs#8 blobs) */
|
|
#include <seccomon.h>
|
|
#include <secitem.h>
|
|
#include <blapit.h>
|
|
#include <secoid.h>
|
|
#include <secasn1.h>
|
|
|
|
/* for importing the keys */
|
|
#include <CoreFoundation/CoreFoundation.h>
|
|
#include <security/SecImportExport.h>
|
|
|
|
/*
|
|
* nssmkey/mobject.c
|
|
*
|
|
* This file implements the NSSCKMDObject object for the
|
|
* "nssmkey" 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_CKMK_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_CKMK_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_CKMK_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 ckmk_trueItem = {
|
|
(void *)&ck_true, (PRUint32)sizeof(CK_BBOOL) };
|
|
static const NSSItem ckmk_falseItem = {
|
|
(void *)&ck_false, (PRUint32)sizeof(CK_BBOOL) };
|
|
static const NSSItem ckmk_x509Item = {
|
|
(void *)&ckc_x509, (PRUint32)sizeof(CK_CERTIFICATE_TYPE) };
|
|
static const NSSItem ckmk_rsaItem = {
|
|
(void *)&ckk_rsa, (PRUint32)sizeof(CK_KEY_TYPE) };
|
|
static const NSSItem ckmk_certClassItem = {
|
|
(void *)&cko_certificate, (PRUint32)sizeof(CK_OBJECT_CLASS) };
|
|
static const NSSItem ckmk_privKeyClassItem = {
|
|
(void *)&cko_private_key, (PRUint32)sizeof(CK_OBJECT_CLASS) };
|
|
static const NSSItem ckmk_pubKeyClassItem = {
|
|
(void *)&cko_public_key, (PRUint32)sizeof(CK_OBJECT_CLASS) };
|
|
static const NSSItem ckmk_emptyItem = {
|
|
(void *)&ck_true, 0};
|
|
|
|
/*
|
|
* these are utilities. The chould be moved to a new utilities file.
|
|
*/
|
|
#ifdef DEBUG
|
|
static void
|
|
itemdump(char *str, void *data, int size, CK_RV error)
|
|
{
|
|
unsigned char *ptr = (unsigned char *)data;
|
|
int i;
|
|
fprintf(stderr,str);
|
|
for (i=0; i < size; i++) {
|
|
fprintf(stderr,"%02x ",(unsigned int) ptr[i]);
|
|
}
|
|
fprintf(stderr," (error = %d)\n", (int ) error);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* unwrap a single DER value
|
|
* now that we have util linked in, we should probably use
|
|
* the ANS1_Decoder for this work...
|
|
*/
|
|
unsigned char *
|
|
nss_ckmk_DERUnwrap
|
|
(
|
|
unsigned char *src,
|
|
int size,
|
|
int *outSize,
|
|
unsigned char **next
|
|
)
|
|
{
|
|
unsigned char *start = src;
|
|
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) {
|
|
int count = len & 0x7f;
|
|
len =0;
|
|
|
|
if (count+2 > size) {
|
|
return start;
|
|
}
|
|
while (count-- > 0) {
|
|
len = (len << 8) | (unsigned) *src++;
|
|
}
|
|
}
|
|
if (len + (src-start) > (unsigned int)size) {
|
|
return start;
|
|
}
|
|
if (next) {
|
|
*next = src+len;
|
|
}
|
|
*outSize = len;
|
|
|
|
return src;
|
|
}
|
|
|
|
/*
|
|
* get an attribute from a template. Value is returned in NSS item.
|
|
* data for the item is owned by the template.
|
|
*/
|
|
CK_RV
|
|
nss_ckmk_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_ckmk_GetULongAttribute
|
|
(
|
|
CK_ATTRIBUTE_TYPE type,
|
|
CK_ATTRIBUTE *template,
|
|
CK_ULONG templateSize,
|
|
CK_RV *pError
|
|
)
|
|
{
|
|
NSSItem item;
|
|
|
|
*pError = nss_ckmk_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_ckmk_GetBoolAttribute
|
|
(
|
|
CK_ATTRIBUTE_TYPE type,
|
|
CK_ATTRIBUTE *template,
|
|
CK_ULONG templateSize,
|
|
CK_BBOOL defaultBool
|
|
)
|
|
{
|
|
NSSItem item;
|
|
CK_RV error;
|
|
|
|
error = nss_ckmk_GetAttribute(type, template, templateSize, &item);
|
|
if (CKR_OK != error) {
|
|
return defaultBool;
|
|
}
|
|
if (item.size != sizeof(CK_BBOOL)) {
|
|
return defaultBool;
|
|
}
|
|
return *(CK_BBOOL *)item.data;
|
|
}
|
|
|
|
/*
|
|
* get an attribute as a NULL terminated string. Caller is responsible to
|
|
* free the string.
|
|
*/
|
|
char *
|
|
nss_ckmk_GetStringAttribute
|
|
(
|
|
CK_ATTRIBUTE_TYPE type,
|
|
CK_ATTRIBUTE *template,
|
|
CK_ULONG templateSize,
|
|
CK_RV *pError
|
|
)
|
|
{
|
|
NSSItem item;
|
|
char *str;
|
|
|
|
/* get the attribute */
|
|
*pError = nss_ckmk_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;
|
|
}
|
|
|
|
/*
|
|
* Apple doesn't seem to have a public interface to the DER encoder,
|
|
* wip out a quick one for integers only (anything more complicated,
|
|
* we should use one of the 3 in lib/util). -- especially since we
|
|
* now link with it.
|
|
*/
|
|
static CK_RV
|
|
ckmk_encodeInt(NSSItem *dest, void *src, int srcLen)
|
|
{
|
|
int dataLen = srcLen;
|
|
int lenLen = 1;
|
|
int encLen;
|
|
int isSigned = 0;
|
|
int offset = 0;
|
|
unsigned char *data = NULL;
|
|
int i;
|
|
|
|
if (*(unsigned char *)src & 0x80) {
|
|
dataLen++;
|
|
isSigned = 1;
|
|
}
|
|
|
|
/* calculate the length of the length specifier */
|
|
/* (NOTE: destroys dataLen value) */
|
|
if (dataLen > 0x7f) {
|
|
do {
|
|
lenLen++;
|
|
dataLen >>= 8;
|
|
} while (dataLen);
|
|
}
|
|
|
|
/* calculate our total length */
|
|
dataLen = isSigned + srcLen;
|
|
encLen = 1 + lenLen + dataLen;
|
|
data = nss_ZNEWARRAY(NULL, unsigned char, encLen);
|
|
if ((unsigned char *)NULL == data) {
|
|
return CKR_HOST_MEMORY;
|
|
}
|
|
data[0] = DER_INTEGER;
|
|
if (1 == lenLen) {
|
|
data[1] = dataLen;
|
|
} else {
|
|
data[1] = 0x80 + lenLen;
|
|
for (i=0; i < lenLen; i++) {
|
|
data[i+1] = ((dataLen >> ((lenLen-i-1)*8)) & 0xff);
|
|
}
|
|
}
|
|
offset = lenLen+1;
|
|
|
|
if (isSigned) {
|
|
data[offset++] = 0;
|
|
}
|
|
nsslibc_memcpy(&data[offset], src, srcLen);
|
|
dest->data = data;
|
|
dest->size = encLen;
|
|
return CKR_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get a Keyring attribute. If content is set to true, then we get the
|
|
* content, not the attribute.
|
|
*/
|
|
static CK_RV
|
|
ckmk_GetCommonAttribute
|
|
(
|
|
ckmkInternalObject *io,
|
|
SecItemAttr itemAttr,
|
|
PRBool content,
|
|
NSSItem *item,
|
|
char *dbString
|
|
)
|
|
{
|
|
SecKeychainAttributeList *attrList = NULL;
|
|
SecKeychainAttributeInfo attrInfo;
|
|
PRUint32 len = 0;
|
|
PRUint32 dataLen = 0;
|
|
PRUint32 attrFormat = 0;
|
|
void *dataVal = 0;
|
|
void *out = NULL;
|
|
CK_RV error = CKR_OK;
|
|
OSStatus macErr;
|
|
|
|
attrInfo.count = 1;
|
|
attrInfo.tag = &itemAttr;
|
|
attrInfo.format = &attrFormat;
|
|
|
|
macErr = SecKeychainItemCopyAttributesAndData(io->u.item.itemRef,
|
|
&attrInfo, NULL, &attrList, &len, &out);
|
|
if (noErr != macErr) {
|
|
CKMK_MACERR(dbString, macErr);
|
|
return CKR_ATTRIBUTE_TYPE_INVALID;
|
|
}
|
|
dataLen = content ? len : attrList->attr->length;
|
|
dataVal = content ? out : attrList->attr->data;
|
|
|
|
/* Apple's documentation says this value is DER Encoded, but it clearly isn't
|
|
* der encode it before we ship it back off to NSS
|
|
*/
|
|
if ( kSecSerialNumberItemAttr == itemAttr ) {
|
|
error = ckmk_encodeInt(item, dataVal, dataLen);
|
|
goto loser; /* logically 'done' if error == CKR_OK */
|
|
}
|
|
item->data = nss_ZNEWARRAY(NULL, char, dataLen);
|
|
if (NULL == item->data) {
|
|
error = CKR_HOST_MEMORY;
|
|
goto loser;
|
|
}
|
|
nsslibc_memcpy(item->data, dataVal, dataLen);
|
|
item->size = dataLen;
|
|
|
|
loser:
|
|
SecKeychainItemFreeAttributesAndData(attrList, out);
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* change an attribute (does not operate on the content).
|
|
*/
|
|
static CK_RV
|
|
ckmk_updateAttribute
|
|
(
|
|
SecKeychainItemRef itemRef,
|
|
SecItemAttr itemAttr,
|
|
void *data,
|
|
PRUint32 len,
|
|
char *dbString
|
|
)
|
|
{
|
|
SecKeychainAttributeList attrList;
|
|
SecKeychainAttribute attrAttr;
|
|
OSStatus macErr;
|
|
CK_RV error = CKR_OK;
|
|
|
|
attrList.count = 1;
|
|
attrList.attr = &attrAttr;
|
|
attrAttr.tag = itemAttr;
|
|
attrAttr.data = data;
|
|
attrAttr.length = len;
|
|
macErr = SecKeychainItemModifyAttributesAndData(itemRef, &attrList, 0, NULL);
|
|
if (noErr != macErr) {
|
|
CKMK_MACERR(dbString, macErr);
|
|
error = CKR_ATTRIBUTE_TYPE_INVALID;
|
|
}
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* get an attribute (does not operate on the content)
|
|
*/
|
|
static CK_RV
|
|
ckmk_GetDataAttribute
|
|
(
|
|
ckmkInternalObject *io,
|
|
SecItemAttr itemAttr,
|
|
NSSItem *item,
|
|
char *dbString
|
|
)
|
|
{
|
|
return ckmk_GetCommonAttribute(io, itemAttr, PR_FALSE, item, dbString);
|
|
}
|
|
|
|
/*
|
|
* get an attribute we know is a BOOL.
|
|
*/
|
|
static CK_RV
|
|
ckmk_GetBoolAttribute
|
|
(
|
|
ckmkInternalObject *io,
|
|
SecItemAttr itemAttr,
|
|
NSSItem *item,
|
|
char *dbString
|
|
)
|
|
{
|
|
SecKeychainAttribute attr;
|
|
SecKeychainAttributeList attrList;
|
|
CK_BBOOL *boolp = NULL;
|
|
PRUint32 len = 0;;
|
|
void *out = NULL;
|
|
CK_RV error = CKR_OK;
|
|
OSStatus macErr;
|
|
|
|
attr.tag = itemAttr;
|
|
attr.length = 0;
|
|
attr.data = NULL;
|
|
attrList.count = 1;
|
|
attrList.attr = &attr;
|
|
|
|
boolp = nss_ZNEW(NULL, CK_BBOOL);
|
|
if ((CK_BBOOL *)NULL == boolp) {
|
|
error = CKR_HOST_MEMORY;
|
|
goto loser;
|
|
}
|
|
|
|
macErr = SecKeychainItemCopyContent(io->u.item.itemRef, NULL,
|
|
&attrList, &len, &out);
|
|
if (noErr != macErr) {
|
|
CKMK_MACERR(dbString, macErr);
|
|
error = CKR_ATTRIBUTE_TYPE_INVALID;
|
|
goto loser;
|
|
}
|
|
if (sizeof(PRUint32) != attr.length) {
|
|
error = CKR_ATTRIBUTE_TYPE_INVALID;
|
|
goto loser;
|
|
}
|
|
*boolp = *(PRUint32 *)attr.data ? 1 : 0;
|
|
item->data = boolp;
|
|
boolp = NULL;
|
|
item->size = sizeof(CK_BBOOL);
|
|
|
|
loser:
|
|
nss_ZFreeIf(boolp);
|
|
SecKeychainItemFreeContent(&attrList, out);
|
|
return error;
|
|
}
|
|
|
|
|
|
/*
|
|
* macros for fetching attributes into a cache and returning the
|
|
* appropriate value. These operate inside switch statements
|
|
*/
|
|
#define CKMK_HANDLE_ITEM(func, io, type, loc, item, error, str) \
|
|
if (0 == (item)->loc.size) { \
|
|
error = func(io, type, &(item)->loc, str); \
|
|
} \
|
|
return (CKR_OK == (error)) ? &(item)->loc : NULL;
|
|
|
|
#define CKMK_HANDLE_OPT_ITEM(func, io, type, loc, item, error, str) \
|
|
if (0 == (item)->loc.size) { \
|
|
(void) func(io, type, &(item)->loc, str); \
|
|
} \
|
|
return &(item)->loc ;
|
|
|
|
#define CKMK_HANDLE_BOOL_ITEM(io, type, loc, item, error, str) \
|
|
CKMK_HANDLE_ITEM(ckmk_GetBoolAttribute, io, type, loc, item, error, str)
|
|
#define CKMK_HANDLE_DATA_ITEM(io, type, loc, item, error, str) \
|
|
CKMK_HANDLE_ITEM(ckmk_GetDataAttribute, io, type, loc, item, error, str)
|
|
#define CKMK_HANDLE_OPT_DATA_ITEM(io, type, loc, item, error, str) \
|
|
CKMK_HANDLE_OPT_ITEM(ckmk_GetDataAttribute, io, type, loc, item, error, str)
|
|
|
|
/*
|
|
* fetch the unique identifier for each object type.
|
|
*/
|
|
static void
|
|
ckmk_FetchHashKey
|
|
(
|
|
ckmkInternalObject *io
|
|
)
|
|
{
|
|
NSSItem *key = &io->hashKey;
|
|
|
|
if (io->objClass == CKO_CERTIFICATE) {
|
|
ckmk_GetCommonAttribute(io, kSecCertEncodingItemAttr,
|
|
PR_TRUE, key, "Fetching HashKey (cert)");
|
|
} else {
|
|
ckmk_GetCommonAttribute(io, kSecKeyLabel,
|
|
PR_FALSE, key, "Fetching HashKey (key)");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Apple mucks with the actual subject and issuer, so go fetch
|
|
* the real ones ourselves.
|
|
*/
|
|
static void
|
|
ckmk_fetchCert
|
|
(
|
|
ckmkInternalObject *io
|
|
)
|
|
{
|
|
CK_RV error;
|
|
unsigned char * cert, *next;
|
|
int certSize, thisEntrySize;
|
|
|
|
error = ckmk_GetCommonAttribute(io, kSecCertEncodingItemAttr, PR_TRUE,
|
|
&io->u.item.derCert, "Fetching Value (cert)");
|
|
if (CKR_OK != error) {
|
|
return;
|
|
}
|
|
/* unwrap the cert bundle */
|
|
cert = nss_ckmk_DERUnwrap((unsigned char *)io->u.item.derCert.data,
|
|
io->u.item.derCert.size,
|
|
&certSize, NULL);
|
|
/* unwrap the cert itself */
|
|
/* cert == certdata */
|
|
cert = nss_ckmk_DERUnwrap(cert, certSize, &certSize, NULL);
|
|
|
|
/* skip the optional version */
|
|
if ((cert[0] & 0xa0) == 0xa0) {
|
|
nss_ckmk_DERUnwrap(cert, certSize, &thisEntrySize, &next);
|
|
certSize -= next - cert;
|
|
cert = next;
|
|
}
|
|
/* skip the serial number */
|
|
nss_ckmk_DERUnwrap(cert, certSize, &thisEntrySize, &next);
|
|
certSize -= next - cert;
|
|
cert = next;
|
|
|
|
/* skip the OID */
|
|
nss_ckmk_DERUnwrap(cert, certSize, &thisEntrySize, &next);
|
|
certSize -= next - cert;
|
|
cert = next;
|
|
|
|
/* save the (wrapped) issuer */
|
|
io->u.item.issuer.data = cert;
|
|
nss_ckmk_DERUnwrap(cert, certSize, &thisEntrySize, &next);
|
|
io->u.item.issuer.size = next - cert;
|
|
certSize -= io->u.item.issuer.size;
|
|
cert = next;
|
|
|
|
/* skip the OID */
|
|
nss_ckmk_DERUnwrap(cert, certSize, &thisEntrySize, &next);
|
|
certSize -= next - cert;
|
|
cert = next;
|
|
|
|
/* save the (wrapped) subject */
|
|
io->u.item.subject.data = cert;
|
|
nss_ckmk_DERUnwrap(cert, certSize, &thisEntrySize, &next);
|
|
io->u.item.subject.size = next - cert;
|
|
certSize -= io->u.item.subject.size;
|
|
cert = next;
|
|
}
|
|
|
|
static void
|
|
ckmk_fetchModulus
|
|
(
|
|
ckmkInternalObject *io
|
|
)
|
|
{
|
|
NSSItem item;
|
|
PRInt32 modLen;
|
|
CK_RV error;
|
|
|
|
/* we can't reliably get the modulus for private keys through CSSM (sigh).
|
|
* For NSS this is OK because we really only use this to get the modulus
|
|
* length (unless we are trying to get a public key from a private keys,
|
|
* something CSSM ALSO does not do!).
|
|
*/
|
|
error = ckmk_GetDataAttribute(io, kSecKeyKeySizeInBits, &item,
|
|
"Key Fetch Modulus");
|
|
if (CKR_OK != error) {
|
|
return;
|
|
}
|
|
|
|
modLen = *(PRInt32 *)item.data;
|
|
modLen = modLen/8; /* convert from bits to bytes */
|
|
|
|
nss_ZFreeIf(item.data);
|
|
io->u.item.modulus.data = nss_ZNEWARRAY(NULL, char, modLen);
|
|
if (NULL == io->u.item.modulus.data) {
|
|
return;
|
|
}
|
|
*(char *)io->u.item.modulus.data = 0x80; /* fake NSS out or it will
|
|
* drop the first byte */
|
|
io->u.item.modulus.size = modLen;
|
|
return;
|
|
}
|
|
|
|
const NSSItem *
|
|
ckmk_FetchCertAttribute
|
|
(
|
|
ckmkInternalObject *io,
|
|
CK_ATTRIBUTE_TYPE type,
|
|
CK_RV *pError
|
|
)
|
|
{
|
|
ckmkItemObject *item = &io->u.item;
|
|
*pError = CKR_OK;
|
|
switch(type) {
|
|
case CKA_CLASS:
|
|
return &ckmk_certClassItem;
|
|
case CKA_TOKEN:
|
|
case CKA_MODIFIABLE:
|
|
return &ckmk_trueItem;
|
|
case CKA_PRIVATE:
|
|
return &ckmk_falseItem;
|
|
case CKA_CERTIFICATE_TYPE:
|
|
return &ckmk_x509Item;
|
|
case CKA_LABEL:
|
|
CKMK_HANDLE_OPT_DATA_ITEM(io, kSecLabelItemAttr, label, item, *pError,
|
|
"Cert:Label attr")
|
|
case CKA_SUBJECT:
|
|
/* OK, well apple does provide an subject and issuer attribute, but they
|
|
* decided to cannonicalize that value. Probably a good move for them,
|
|
* but makes it useless for most users of PKCS #11.. Get the real subject
|
|
* from the certificate */
|
|
if (0 == item->derCert.size) {
|
|
ckmk_fetchCert(io);
|
|
}
|
|
return &item->subject;
|
|
case CKA_ISSUER:
|
|
if (0 == item->derCert.size) {
|
|
ckmk_fetchCert(io);
|
|
}
|
|
return &item->issuer;
|
|
case CKA_SERIAL_NUMBER:
|
|
CKMK_HANDLE_DATA_ITEM(io, kSecSerialNumberItemAttr, serial, item, *pError,
|
|
"Cert:Serial Number attr")
|
|
case CKA_VALUE:
|
|
if (0 == item->derCert.size) {
|
|
ckmk_fetchCert(io);
|
|
}
|
|
return &item->derCert;
|
|
case CKA_ID:
|
|
CKMK_HANDLE_OPT_DATA_ITEM(io, kSecPublicKeyHashItemAttr, id, item, *pError,
|
|
"Cert:ID attr")
|
|
default:
|
|
*pError = CKR_ATTRIBUTE_TYPE_INVALID;
|
|
break;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
const NSSItem *
|
|
ckmk_FetchPubKeyAttribute
|
|
(
|
|
ckmkInternalObject *io,
|
|
CK_ATTRIBUTE_TYPE type,
|
|
CK_RV *pError
|
|
)
|
|
{
|
|
ckmkItemObject *item = &io->u.item;
|
|
*pError = CKR_OK;
|
|
|
|
switch(type) {
|
|
case CKA_CLASS:
|
|
return &ckmk_pubKeyClassItem;
|
|
case CKA_TOKEN:
|
|
case CKA_LOCAL:
|
|
return &ckmk_trueItem;
|
|
case CKA_KEY_TYPE:
|
|
return &ckmk_rsaItem;
|
|
case CKA_LABEL:
|
|
CKMK_HANDLE_OPT_DATA_ITEM(io, kSecKeyPrintName, label, item, *pError,
|
|
"PubKey:Label attr")
|
|
case CKA_ENCRYPT:
|
|
CKMK_HANDLE_BOOL_ITEM(io, kSecKeyEncrypt, encrypt, item, *pError,
|
|
"PubKey:Encrypt attr")
|
|
case CKA_VERIFY:
|
|
CKMK_HANDLE_BOOL_ITEM(io, kSecKeyVerify, verify, item, *pError,
|
|
"PubKey:Verify attr")
|
|
case CKA_VERIFY_RECOVER:
|
|
CKMK_HANDLE_BOOL_ITEM(io, kSecKeyVerifyRecover, verifyRecover,
|
|
item, *pError, "PubKey:VerifyRecover attr")
|
|
case CKA_PRIVATE:
|
|
CKMK_HANDLE_BOOL_ITEM(io, kSecKeyPrivate, private, item, *pError,
|
|
"PubKey:Private attr")
|
|
case CKA_MODIFIABLE:
|
|
CKMK_HANDLE_BOOL_ITEM(io, kSecKeyModifiable, modify, item, *pError,
|
|
"PubKey:Modify attr")
|
|
case CKA_DERIVE:
|
|
CKMK_HANDLE_BOOL_ITEM(io, kSecKeyDerive, derive, item, *pError,
|
|
"PubKey:Derive attr")
|
|
case CKA_WRAP:
|
|
CKMK_HANDLE_BOOL_ITEM(io, kSecKeyWrap, wrap, item, *pError,
|
|
"PubKey:Wrap attr")
|
|
case CKA_SUBJECT:
|
|
CKMK_HANDLE_OPT_DATA_ITEM(io, kSecSubjectItemAttr, subject, item, *pError,
|
|
"PubKey:Subect attr")
|
|
case CKA_MODULUS:
|
|
return &ckmk_emptyItem;
|
|
case CKA_PUBLIC_EXPONENT:
|
|
return &ckmk_emptyItem;
|
|
case CKA_ID:
|
|
CKMK_HANDLE_OPT_DATA_ITEM(io, kSecKeyLabel, id, item, *pError,
|
|
"PubKey:ID attr")
|
|
default:
|
|
*pError = CKR_ATTRIBUTE_TYPE_INVALID;
|
|
break;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
const NSSItem *
|
|
ckmk_FetchPrivKeyAttribute
|
|
(
|
|
ckmkInternalObject *io,
|
|
CK_ATTRIBUTE_TYPE type,
|
|
CK_RV *pError
|
|
)
|
|
{
|
|
ckmkItemObject *item = &io->u.item;
|
|
*pError = CKR_OK;
|
|
|
|
switch(type) {
|
|
case CKA_CLASS:
|
|
return &ckmk_privKeyClassItem;
|
|
case CKA_TOKEN:
|
|
case CKA_LOCAL:
|
|
return &ckmk_trueItem;
|
|
case CKA_SENSITIVE:
|
|
case CKA_EXTRACTABLE: /* will probably move in the future */
|
|
case CKA_ALWAYS_SENSITIVE:
|
|
case CKA_NEVER_EXTRACTABLE:
|
|
return &ckmk_falseItem;
|
|
case CKA_KEY_TYPE:
|
|
return &ckmk_rsaItem;
|
|
case CKA_LABEL:
|
|
CKMK_HANDLE_OPT_DATA_ITEM(io, kSecKeyPrintName, label, item, *pError,
|
|
"PrivateKey:Label attr")
|
|
case CKA_DECRYPT:
|
|
CKMK_HANDLE_BOOL_ITEM(io, kSecKeyDecrypt, decrypt, item, *pError,
|
|
"PrivateKey:Decrypt attr")
|
|
case CKA_SIGN:
|
|
CKMK_HANDLE_BOOL_ITEM(io, kSecKeySign, sign, item, *pError,
|
|
"PrivateKey:Sign attr")
|
|
case CKA_SIGN_RECOVER:
|
|
CKMK_HANDLE_BOOL_ITEM(io, kSecKeySignRecover, signRecover, item, *pError,
|
|
"PrivateKey:Sign Recover attr")
|
|
case CKA_PRIVATE:
|
|
CKMK_HANDLE_BOOL_ITEM(io, kSecKeyPrivate, private, item, *pError,
|
|
"PrivateKey:Private attr")
|
|
case CKA_MODIFIABLE:
|
|
CKMK_HANDLE_BOOL_ITEM(io, kSecKeyModifiable, modify, item, *pError,
|
|
"PrivateKey:Modify attr")
|
|
case CKA_DERIVE:
|
|
CKMK_HANDLE_BOOL_ITEM(io, kSecKeyDerive, derive, item, *pError,
|
|
"PrivateKey:Derive attr")
|
|
case CKA_UNWRAP:
|
|
CKMK_HANDLE_BOOL_ITEM(io, kSecKeyUnwrap, unwrap, item, *pError,
|
|
"PrivateKey:Unwrap attr")
|
|
case CKA_SUBJECT:
|
|
CKMK_HANDLE_OPT_DATA_ITEM(io, kSecSubjectItemAttr, subject, item, *pError,
|
|
"PrivateKey:Subject attr")
|
|
case CKA_MODULUS:
|
|
if (0 == item->modulus.size) {
|
|
ckmk_fetchModulus(io);
|
|
}
|
|
return &item->modulus;
|
|
case CKA_PUBLIC_EXPONENT:
|
|
return &ckmk_emptyItem;
|
|
#ifdef notdef
|
|
/* the following are sensitive attributes. We could implement them for
|
|
* sensitive keys using the key export function, but it's better to
|
|
* just support wrap through this token. That will more reliably allow us
|
|
* to export any private key that is truly exportable.
|
|
*/
|
|
case CKA_PRIVATE_EXPONENT:
|
|
CKMK_HANDLE_DATA_ITEM(io, kSecPrivateExponentItemAttr, privateExponent,
|
|
item, *pError)
|
|
case CKA_PRIME_1:
|
|
CKMK_HANDLE_DATA_ITEM(io, kSecPrime1ItemAttr, prime1, item, *pError)
|
|
case CKA_PRIME_2:
|
|
CKMK_HANDLE_DATA_ITEM(io, kSecPrime2ItemAttr, prime2, item, *pError)
|
|
case CKA_EXPONENT_1:
|
|
CKMK_HANDLE_DATA_ITEM(io, kSecExponent1ItemAttr, exponent1, item, *pError)
|
|
case CKA_EXPONENT_2:
|
|
CKMK_HANDLE_DATA_ITEM(io, kSecExponent2ItemAttr, exponent2, item, *pError)
|
|
case CKA_COEFFICIENT:
|
|
CKMK_HANDLE_DATA_ITEM(io, kSecCoefficientItemAttr, coefficient,
|
|
item, *pError)
|
|
#endif
|
|
case CKA_ID:
|
|
CKMK_HANDLE_OPT_DATA_ITEM(io, kSecKeyLabel, id, item, *pError,
|
|
"PrivateKey:ID attr")
|
|
default:
|
|
*pError = CKR_ATTRIBUTE_TYPE_INVALID;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
const NSSItem *
|
|
nss_ckmk_FetchAttribute
|
|
(
|
|
ckmkInternalObject *io,
|
|
CK_ATTRIBUTE_TYPE type,
|
|
CK_RV *pError
|
|
)
|
|
{
|
|
CK_ULONG i;
|
|
const NSSItem * value = NULL;
|
|
|
|
if (io->type == ckmkRaw) {
|
|
for( i = 0; i < io->u.raw.n; i++ ) {
|
|
if( type == io->u.raw.types[i] ) {
|
|
return &io->u.raw.items[i];
|
|
}
|
|
}
|
|
*pError = CKR_ATTRIBUTE_TYPE_INVALID;
|
|
return NULL;
|
|
}
|
|
/* deal with the common attributes */
|
|
switch (io->objClass) {
|
|
case CKO_CERTIFICATE:
|
|
value = ckmk_FetchCertAttribute(io, type, pError);
|
|
break;
|
|
case CKO_PRIVATE_KEY:
|
|
value = ckmk_FetchPrivKeyAttribute(io, type, pError);
|
|
break;
|
|
case CKO_PUBLIC_KEY:
|
|
value = ckmk_FetchPubKeyAttribute(io, type, pError);
|
|
break;
|
|
default:
|
|
*pError = CKR_OBJECT_HANDLE_INVALID;
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if (CKA_ID == type) {
|
|
itemdump("id: ", value->data, value->size, *pError);
|
|
}
|
|
#endif
|
|
return value;
|
|
}
|
|
|
|
static void
|
|
ckmk_removeObjectFromHash
|
|
(
|
|
ckmkInternalObject *io
|
|
);
|
|
|
|
/*
|
|
*
|
|
* These are the MSObject functions we need to implement
|
|
*
|
|
* Finalize - unneeded (actually we should clean up the hashtables)
|
|
* Destroy
|
|
* IsTokenObject - CK_TRUE
|
|
* GetAttributeCount
|
|
* GetAttributeTypes
|
|
* GetAttributeSize
|
|
* GetAttribute
|
|
* SetAttribute
|
|
* GetObjectSize
|
|
*/
|
|
|
|
static CK_RV
|
|
ckmk_mdObject_Destroy
|
|
(
|
|
NSSCKMDObject *mdObject,
|
|
NSSCKFWObject *fwObject,
|
|
NSSCKMDSession *mdSession,
|
|
NSSCKFWSession *fwSession,
|
|
NSSCKMDToken *mdToken,
|
|
NSSCKFWToken *fwToken,
|
|
NSSCKMDInstance *mdInstance,
|
|
NSSCKFWInstance *fwInstance
|
|
)
|
|
{
|
|
ckmkInternalObject *io = (ckmkInternalObject *)mdObject->etc;
|
|
OSStatus macErr;
|
|
|
|
if (ckmkRaw == io->type) {
|
|
/* there is not 'object write protected' error, use the next best thing */
|
|
return CKR_TOKEN_WRITE_PROTECTED;
|
|
}
|
|
|
|
/* This API is done well. The following 4 lines are the complete apple
|
|
* specific part of this implementation */
|
|
macErr = SecKeychainItemDelete(io->u.item.itemRef);
|
|
if (noErr != macErr) {
|
|
CKMK_MACERR("Delete object", macErr);
|
|
}
|
|
|
|
/* remove it from the hash */
|
|
ckmk_removeObjectFromHash(io);
|
|
|
|
/* free the puppy.. */
|
|
nss_ckmk_DestroyInternalObject(io);
|
|
|
|
return CKR_OK;
|
|
}
|
|
|
|
static CK_BBOOL
|
|
ckmk_mdObject_IsTokenObject
|
|
(
|
|
NSSCKMDObject *mdObject,
|
|
NSSCKFWObject *fwObject,
|
|
NSSCKMDSession *mdSession,
|
|
NSSCKFWSession *fwSession,
|
|
NSSCKMDToken *mdToken,
|
|
NSSCKFWToken *fwToken,
|
|
NSSCKMDInstance *mdInstance,
|
|
NSSCKFWInstance *fwInstance
|
|
)
|
|
{
|
|
return CK_TRUE;
|
|
}
|
|
|
|
static CK_ULONG
|
|
ckmk_mdObject_GetAttributeCount
|
|
(
|
|
NSSCKMDObject *mdObject,
|
|
NSSCKFWObject *fwObject,
|
|
NSSCKMDSession *mdSession,
|
|
NSSCKFWSession *fwSession,
|
|
NSSCKMDToken *mdToken,
|
|
NSSCKFWToken *fwToken,
|
|
NSSCKMDInstance *mdInstance,
|
|
NSSCKFWInstance *fwInstance,
|
|
CK_RV *pError
|
|
)
|
|
{
|
|
ckmkInternalObject *io = (ckmkInternalObject *)mdObject->etc;
|
|
|
|
if (ckmkRaw == 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
|
|
ckmk_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
|
|
)
|
|
{
|
|
ckmkInternalObject *io = (ckmkInternalObject *)mdObject->etc;
|
|
CK_ULONG i;
|
|
CK_RV error = CKR_OK;
|
|
const CK_ATTRIBUTE_TYPE *attrs = NULL;
|
|
CK_ULONG size = ckmk_mdObject_GetAttributeCount(
|
|
mdObject, fwObject, mdSession, fwSession,
|
|
mdToken, fwToken, mdInstance, fwInstance, &error);
|
|
|
|
if( size != ulCount ) {
|
|
return CKR_BUFFER_TOO_SMALL;
|
|
}
|
|
if (io->type == ckmkRaw) {
|
|
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
|
|
ckmk_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
|
|
)
|
|
{
|
|
ckmkInternalObject *io = (ckmkInternalObject *)mdObject->etc;
|
|
|
|
const NSSItem *b;
|
|
|
|
b = nss_ckmk_FetchAttribute(io, attribute, pError);
|
|
|
|
if ((const NSSItem *)NULL == b) {
|
|
return 0;
|
|
}
|
|
return b->size;
|
|
}
|
|
|
|
static CK_RV
|
|
ckmk_mdObject_SetAttribute
|
|
(
|
|
NSSCKMDObject *mdObject,
|
|
NSSCKFWObject *fwObject,
|
|
NSSCKMDSession *mdSession,
|
|
NSSCKFWSession *fwSession,
|
|
NSSCKMDToken *mdToken,
|
|
NSSCKFWToken *fwToken,
|
|
NSSCKMDInstance *mdInstance,
|
|
NSSCKFWInstance *fwInstance,
|
|
CK_ATTRIBUTE_TYPE attribute,
|
|
NSSItem *value
|
|
)
|
|
{
|
|
ckmkInternalObject *io = (ckmkInternalObject *)mdObject->etc;
|
|
SecKeychainItemRef itemRef;
|
|
|
|
if (io->type == ckmkRaw) {
|
|
return CKR_TOKEN_WRITE_PROTECTED;
|
|
}
|
|
itemRef = io->u.item.itemRef;
|
|
|
|
switch (io->objClass) {
|
|
case CKO_PRIVATE_KEY:
|
|
case CKO_PUBLIC_KEY:
|
|
switch (attribute) {
|
|
case CKA_ID:
|
|
ckmk_updateAttribute(itemRef, kSecKeyLabel,
|
|
value->data, value->size, "Set Attr Key ID");
|
|
#ifdef DEBUG
|
|
itemdump("key id: ", value->data, value->size, CKR_OK);
|
|
#endif
|
|
break;
|
|
case CKA_LABEL:
|
|
ckmk_updateAttribute(itemRef, kSecKeyPrintName, value->data,
|
|
value->size, "Set Attr Key Label");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case CKO_CERTIFICATE:
|
|
switch (attribute) {
|
|
case CKA_ID:
|
|
ckmk_updateAttribute(itemRef, kSecPublicKeyHashItemAttr,
|
|
value->data, value->size, "Set Attr Cert ID");
|
|
break;
|
|
case CKA_LABEL:
|
|
ckmk_updateAttribute(itemRef, kSecLabelItemAttr, value->data,
|
|
value->size, "Set Attr Cert Label");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return CKR_OK;
|
|
}
|
|
|
|
static NSSCKFWItem
|
|
ckmk_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;
|
|
ckmkInternalObject *io = (ckmkInternalObject *)mdObject->etc;
|
|
|
|
mdItem.needsFreeing = PR_FALSE;
|
|
mdItem.item = (NSSItem*)nss_ckmk_FetchAttribute(io, attribute, pError);
|
|
|
|
|
|
return mdItem;
|
|
}
|
|
|
|
static CK_ULONG
|
|
ckmk_mdObject_GetObjectSize
|
|
(
|
|
NSSCKMDObject *mdObject,
|
|
NSSCKFWObject *fwObject,
|
|
NSSCKMDSession *mdSession,
|
|
NSSCKFWSession *fwSession,
|
|
NSSCKMDToken *mdToken,
|
|
NSSCKFWToken *fwToken,
|
|
NSSCKMDInstance *mdInstance,
|
|
NSSCKFWInstance *fwInstance,
|
|
CK_RV *pError
|
|
)
|
|
{
|
|
CK_ULONG rv = 1;
|
|
|
|
/* size is irrelevant to this token */
|
|
return rv;
|
|
}
|
|
|
|
static const NSSCKMDObject
|
|
ckmk_prototype_mdObject = {
|
|
(void *)NULL, /* etc */
|
|
NULL, /* Finalize */
|
|
ckmk_mdObject_Destroy,
|
|
ckmk_mdObject_IsTokenObject,
|
|
ckmk_mdObject_GetAttributeCount,
|
|
ckmk_mdObject_GetAttributeTypes,
|
|
ckmk_mdObject_GetAttributeSize,
|
|
ckmk_mdObject_GetAttribute,
|
|
NULL, /* FreeAttribute */
|
|
ckmk_mdObject_SetAttribute,
|
|
ckmk_mdObject_GetObjectSize,
|
|
(void *)NULL /* null terminator */
|
|
};
|
|
|
|
static nssHash *ckmkInternalObjectHash = NULL;
|
|
|
|
NSS_IMPLEMENT NSSCKMDObject *
|
|
nss_ckmk_CreateMDObject
|
|
(
|
|
NSSArena *arena,
|
|
ckmkInternalObject *io,
|
|
CK_RV *pError
|
|
)
|
|
{
|
|
if ((nssHash *)NULL == ckmkInternalObjectHash) {
|
|
ckmkInternalObjectHash = nssHash_CreateItem(NULL, 10);
|
|
}
|
|
if (ckmkItem == io->type) {
|
|
/* the hash key, not a cryptographic key */
|
|
NSSItem *key = &io->hashKey;
|
|
ckmkInternalObject *old_o = NULL;
|
|
|
|
if (key->size == 0) {
|
|
ckmk_FetchHashKey(io);
|
|
}
|
|
old_o = (ckmkInternalObject *)
|
|
nssHash_Lookup(ckmkInternalObjectHash, key);
|
|
if (!old_o) {
|
|
nssHash_Add(ckmkInternalObjectHash, key, io);
|
|
} else if (old_o != io) {
|
|
nss_ckmk_DestroyInternalObject(io);
|
|
io = old_o;
|
|
}
|
|
}
|
|
|
|
if ( (void*)NULL == io->mdObject.etc) {
|
|
(void) nsslibc_memcpy(&io->mdObject,&ckmk_prototype_mdObject,
|
|
sizeof(ckmk_prototype_mdObject));
|
|
io->mdObject.etc = (void *)io;
|
|
}
|
|
return &io->mdObject;
|
|
}
|
|
|
|
static void
|
|
ckmk_removeObjectFromHash
|
|
(
|
|
ckmkInternalObject *io
|
|
)
|
|
{
|
|
NSSItem *key = &io->hashKey;
|
|
|
|
if ((nssHash *)NULL == ckmkInternalObjectHash) {
|
|
return;
|
|
}
|
|
if (key->size == 0) {
|
|
ckmk_FetchHashKey(io);
|
|
}
|
|
nssHash_Remove(ckmkInternalObjectHash, key);
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
nss_ckmk_DestroyInternalObject
|
|
(
|
|
ckmkInternalObject *io
|
|
)
|
|
{
|
|
switch (io->type) {
|
|
case ckmkRaw:
|
|
return;
|
|
case ckmkItem:
|
|
nss_ZFreeIf(io->u.item.modify.data);
|
|
nss_ZFreeIf(io->u.item.private.data);
|
|
nss_ZFreeIf(io->u.item.encrypt.data);
|
|
nss_ZFreeIf(io->u.item.decrypt.data);
|
|
nss_ZFreeIf(io->u.item.derive.data);
|
|
nss_ZFreeIf(io->u.item.sign.data);
|
|
nss_ZFreeIf(io->u.item.signRecover.data);
|
|
nss_ZFreeIf(io->u.item.verify.data);
|
|
nss_ZFreeIf(io->u.item.verifyRecover.data);
|
|
nss_ZFreeIf(io->u.item.wrap.data);
|
|
nss_ZFreeIf(io->u.item.unwrap.data);
|
|
nss_ZFreeIf(io->u.item.label.data);
|
|
/*nss_ZFreeIf(io->u.item.subject.data); */
|
|
/*nss_ZFreeIf(io->u.item.issuer.data); */
|
|
nss_ZFreeIf(io->u.item.serial.data);
|
|
nss_ZFreeIf(io->u.item.modulus.data);
|
|
nss_ZFreeIf(io->u.item.exponent.data);
|
|
nss_ZFreeIf(io->u.item.privateExponent.data);
|
|
nss_ZFreeIf(io->u.item.prime1.data);
|
|
nss_ZFreeIf(io->u.item.prime2.data);
|
|
nss_ZFreeIf(io->u.item.exponent1.data);
|
|
nss_ZFreeIf(io->u.item.exponent2.data);
|
|
nss_ZFreeIf(io->u.item.coefficient.data);
|
|
break;
|
|
}
|
|
nss_ZFreeIf(io);
|
|
return;
|
|
}
|
|
|
|
|
|
static ckmkInternalObject *
|
|
nss_ckmk_NewInternalObject
|
|
(
|
|
CK_OBJECT_CLASS objClass,
|
|
SecKeychainItemRef itemRef,
|
|
SecItemClass itemClass,
|
|
CK_RV *pError
|
|
)
|
|
{
|
|
ckmkInternalObject *io = nss_ZNEW(NULL, ckmkInternalObject);
|
|
|
|
if ((ckmkInternalObject *)NULL == io) {
|
|
*pError = CKR_HOST_MEMORY;
|
|
return io;
|
|
}
|
|
io->type = ckmkItem;
|
|
io->objClass = objClass;
|
|
io->u.item.itemRef = itemRef;
|
|
io->u.item.itemClass = itemClass;
|
|
return io;
|
|
}
|
|
|
|
/*
|
|
* Apple doesn't alway have a default keyChain set by the OS, use the
|
|
* SearchList to try to find one.
|
|
*/
|
|
static CK_RV
|
|
ckmk_GetSafeDefaultKeychain
|
|
(
|
|
SecKeychainRef *keychainRef
|
|
)
|
|
{
|
|
OSStatus macErr;
|
|
CFArrayRef searchList = 0;
|
|
CK_RV error = CKR_OK;
|
|
|
|
macErr = SecKeychainCopyDefault(keychainRef);
|
|
if (noErr != macErr) {
|
|
int searchCount = 0;
|
|
if (errSecNoDefaultKeychain != macErr) {
|
|
CKMK_MACERR("Getting default key chain", macErr);
|
|
error = CKR_GENERAL_ERROR;
|
|
goto loser;
|
|
}
|
|
/* ok, we don't have a default key chain, find one */
|
|
macErr = SecKeychainCopySearchList(&searchList);
|
|
if (noErr != macErr) {
|
|
CKMK_MACERR("failed to find a keyring searchList", macErr);
|
|
error = CKR_DEVICE_REMOVED;
|
|
goto loser;
|
|
}
|
|
searchCount = CFArrayGetCount(searchList);
|
|
if (searchCount < 1) {
|
|
error = CKR_DEVICE_REMOVED;
|
|
goto loser;
|
|
}
|
|
*keychainRef =
|
|
(SecKeychainRef)CFRetain(CFArrayGetValueAtIndex(searchList, 0));
|
|
if (0 == *keychainRef) {
|
|
error = CKR_DEVICE_REMOVED;
|
|
goto loser;
|
|
}
|
|
/* should we set it as default? */
|
|
}
|
|
loser:
|
|
if (0 != searchList) {
|
|
CFRelease(searchList);
|
|
}
|
|
return error;
|
|
}
|
|
static ckmkInternalObject *
|
|
nss_ckmk_CreateCertificate
|
|
(
|
|
NSSCKFWSession *fwSession,
|
|
CK_ATTRIBUTE_PTR pTemplate,
|
|
CK_ULONG ulAttributeCount,
|
|
CK_RV *pError
|
|
)
|
|
{
|
|
NSSItem value;
|
|
ckmkInternalObject *io = NULL;
|
|
OSStatus macErr;
|
|
SecCertificateRef certRef;
|
|
SecKeychainItemRef itemRef;
|
|
SecKeychainRef keychainRef;
|
|
CSSM_DATA certData;
|
|
|
|
*pError = nss_ckmk_GetAttribute(CKA_VALUE, pTemplate,
|
|
ulAttributeCount, &value);
|
|
if (CKR_OK != *pError) {
|
|
goto loser;
|
|
}
|
|
|
|
certData.Data = value.data;
|
|
certData.Length = value.size;
|
|
macErr = SecCertificateCreateFromData(&certData, CSSM_CERT_X_509v3,
|
|
CSSM_CERT_ENCODING_BER, &certRef);
|
|
if (noErr != macErr) {
|
|
CKMK_MACERR("Create cert from data Failed", macErr);
|
|
*pError = CKR_GENERAL_ERROR; /* need to map macErr */
|
|
goto loser;
|
|
}
|
|
|
|
*pError = ckmk_GetSafeDefaultKeychain(&keychainRef);
|
|
if (CKR_OK != *pError) {
|
|
goto loser;
|
|
}
|
|
|
|
macErr = SecCertificateAddToKeychain( certRef, keychainRef);
|
|
itemRef = (SecKeychainItemRef) certRef;
|
|
if (errSecDuplicateItem != macErr) {
|
|
NSSItem keyID = { NULL, 0 };
|
|
char *nickname = NULL;
|
|
CK_RV dummy;
|
|
|
|
if (noErr != macErr) {
|
|
CKMK_MACERR("Add cert to keychain Failed", macErr);
|
|
*pError = CKR_GENERAL_ERROR; /* need to map macErr */
|
|
goto loser;
|
|
}
|
|
/* these two are optional */
|
|
nickname = nss_ckmk_GetStringAttribute(CKA_LABEL, pTemplate,
|
|
ulAttributeCount, &dummy);
|
|
/* we've added a new one, update the attributes in the key ring */
|
|
if (nickname) {
|
|
ckmk_updateAttribute(itemRef, kSecLabelItemAttr, nickname,
|
|
strlen(nickname)+1, "Modify Cert Label");
|
|
nss_ZFreeIf(nickname);
|
|
}
|
|
dummy = nss_ckmk_GetAttribute(CKA_ID, pTemplate,
|
|
ulAttributeCount, &keyID);
|
|
if (CKR_OK == dummy) {
|
|
dummy = ckmk_updateAttribute(itemRef, kSecPublicKeyHashItemAttr,
|
|
keyID.data, keyID.size, "Modify Cert ID");
|
|
}
|
|
}
|
|
|
|
io = nss_ckmk_NewInternalObject(CKO_CERTIFICATE, itemRef,
|
|
kSecCertificateItemClass, pError);
|
|
if ((ckmkInternalObject *)NULL != io) {
|
|
itemRef = 0;
|
|
}
|
|
|
|
loser:
|
|
if (0 != itemRef) {
|
|
CFRelease(itemRef);
|
|
}
|
|
if (0 != keychainRef) {
|
|
CFRelease(keychainRef);
|
|
}
|
|
|
|
return io;
|
|
}
|
|
|
|
/*
|
|
* PKCS #8 attributes
|
|
*/
|
|
struct ckmk_AttributeStr {
|
|
SECItem attrType;
|
|
SECItem *attrValue;
|
|
};
|
|
typedef struct ckmk_AttributeStr ckmk_Attribute;
|
|
|
|
/*
|
|
** A PKCS#8 private key info object
|
|
*/
|
|
struct PrivateKeyInfoStr {
|
|
PLArenaPool *arena;
|
|
SECItem version;
|
|
SECAlgorithmID algorithm;
|
|
SECItem privateKey;
|
|
ckmk_Attribute **attributes;
|
|
};
|
|
typedef struct PrivateKeyInfoStr PrivateKeyInfo;
|
|
|
|
const SEC_ASN1Template ckmk_RSAPrivateKeyTemplate[] = {
|
|
{ SEC_ASN1_SEQUENCE, 0, NULL, sizeof(RSAPrivateKey) },
|
|
{ SEC_ASN1_INTEGER, offsetof(RSAPrivateKey,version) },
|
|
{ SEC_ASN1_INTEGER, offsetof(RSAPrivateKey,modulus) },
|
|
{ SEC_ASN1_INTEGER, offsetof(RSAPrivateKey,publicExponent) },
|
|
{ SEC_ASN1_INTEGER, offsetof(RSAPrivateKey,privateExponent) },
|
|
{ SEC_ASN1_INTEGER, offsetof(RSAPrivateKey,prime1) },
|
|
{ SEC_ASN1_INTEGER, offsetof(RSAPrivateKey,prime2) },
|
|
{ SEC_ASN1_INTEGER, offsetof(RSAPrivateKey,exponent1) },
|
|
{ SEC_ASN1_INTEGER, offsetof(RSAPrivateKey,exponent2) },
|
|
{ SEC_ASN1_INTEGER, offsetof(RSAPrivateKey,coefficient) },
|
|
{ 0 }
|
|
};
|
|
|
|
const SEC_ASN1Template ckmk_AttributeTemplate[] = {
|
|
{ SEC_ASN1_SEQUENCE, 0, NULL, sizeof(ckmk_Attribute) },
|
|
{ SEC_ASN1_OBJECT_ID, offsetof(ckmk_Attribute, attrType) },
|
|
{ SEC_ASN1_SET_OF, offsetof(ckmk_Attribute, attrValue),
|
|
SEC_AnyTemplate },
|
|
{ 0 }
|
|
};
|
|
|
|
const SEC_ASN1Template ckmk_SetOfAttributeTemplate[] = {
|
|
{ SEC_ASN1_SET_OF, 0, ckmk_AttributeTemplate },
|
|
};
|
|
|
|
SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
|
|
|
|
/* ASN1 Templates for new decoder/encoder */
|
|
const SEC_ASN1Template ckmk_PrivateKeyInfoTemplate[] = {
|
|
{ SEC_ASN1_SEQUENCE, 0, NULL, sizeof(PrivateKeyInfo) },
|
|
{ SEC_ASN1_INTEGER, offsetof(PrivateKeyInfo,version) },
|
|
{ SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(PrivateKeyInfo,algorithm),
|
|
SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
|
|
{ SEC_ASN1_OCTET_STRING, offsetof(PrivateKeyInfo,privateKey) },
|
|
{ SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
|
|
offsetof(PrivateKeyInfo, attributes), ckmk_SetOfAttributeTemplate },
|
|
{ 0 }
|
|
};
|
|
|
|
#define CKMK_PRIVATE_KEY_INFO_VERSION 0
|
|
static CK_RV
|
|
ckmk_CreateRSAKeyBlob
|
|
(
|
|
RSAPrivateKey *lk,
|
|
NSSItem *keyBlob
|
|
)
|
|
{
|
|
PrivateKeyInfo *pki = NULL;
|
|
PLArenaPool *arena = NULL;
|
|
SECOidTag algorithm = SEC_OID_UNKNOWN;
|
|
void *dummy;
|
|
SECStatus rv;
|
|
SECItem *encodedKey = NULL;
|
|
CK_RV error = CKR_OK;
|
|
|
|
arena = PORT_NewArena(2048); /* XXX different size? */
|
|
if(!arena) {
|
|
error = CKR_HOST_MEMORY;
|
|
goto loser;
|
|
}
|
|
|
|
pki = (PrivateKeyInfo*)PORT_ArenaZAlloc(arena, sizeof(PrivateKeyInfo));
|
|
if(!pki) {
|
|
error = CKR_HOST_MEMORY;
|
|
goto loser;
|
|
}
|
|
pki->arena = arena;
|
|
|
|
dummy = SEC_ASN1EncodeItem(arena, &pki->privateKey, lk,
|
|
ckmk_RSAPrivateKeyTemplate);
|
|
algorithm = SEC_OID_PKCS1_RSA_ENCRYPTION;
|
|
|
|
if (!dummy) {
|
|
error = CKR_DEVICE_ERROR; /* should map NSS SECError */
|
|
goto loser;
|
|
}
|
|
|
|
rv = SECOID_SetAlgorithmID(arena, &pki->algorithm, algorithm,
|
|
(SECItem*)NULL);
|
|
if (rv != SECSuccess) {
|
|
error = CKR_DEVICE_ERROR; /* should map NSS SECError */
|
|
goto loser;
|
|
}
|
|
|
|
dummy = SEC_ASN1EncodeInteger(arena, &pki->version,
|
|
CKMK_PRIVATE_KEY_INFO_VERSION);
|
|
if (!dummy) {
|
|
error = CKR_DEVICE_ERROR; /* should map NSS SECError */
|
|
goto loser;
|
|
}
|
|
|
|
encodedKey = SEC_ASN1EncodeItem(NULL, NULL, pki,
|
|
ckmk_PrivateKeyInfoTemplate);
|
|
if (!encodedKey) {
|
|
error = CKR_DEVICE_ERROR;
|
|
goto loser;
|
|
}
|
|
|
|
keyBlob->data = nss_ZNEWARRAY(NULL, char, encodedKey->len);
|
|
if (NULL == keyBlob->data) {
|
|
error = CKR_HOST_MEMORY;
|
|
goto loser;
|
|
}
|
|
nsslibc_memcpy(keyBlob->data, encodedKey->data, encodedKey->len);
|
|
keyBlob->size = encodedKey->len;
|
|
|
|
loser:
|
|
if(arena) {
|
|
PORT_FreeArena(arena, PR_TRUE);
|
|
}
|
|
if (encodedKey) {
|
|
SECITEM_FreeItem(encodedKey, PR_TRUE);
|
|
}
|
|
|
|
return error;
|
|
}
|
|
/*
|
|
* There MUST be a better way to do this. For now, find the key based on the
|
|
* default name Apple gives it once we import.
|
|
*/
|
|
#define IMPORTED_NAME "Imported Private Key"
|
|
static CK_RV
|
|
ckmk_FindImportedKey
|
|
(
|
|
SecKeychainRef keychainRef,
|
|
SecItemClass itemClass,
|
|
SecKeychainItemRef *outItemRef
|
|
)
|
|
{
|
|
OSStatus macErr;
|
|
SecKeychainSearchRef searchRef = 0;
|
|
SecKeychainItemRef newItemRef;
|
|
|
|
macErr = SecKeychainSearchCreateFromAttributes(keychainRef, itemClass,
|
|
NULL, &searchRef);
|
|
if (noErr != macErr) {
|
|
CKMK_MACERR("Can't search for Key", macErr);
|
|
return CKR_GENERAL_ERROR;
|
|
}
|
|
while (noErr == SecKeychainSearchCopyNext(searchRef, &newItemRef)) {
|
|
SecKeychainAttributeList *attrList = NULL;
|
|
SecKeychainAttributeInfo attrInfo;
|
|
SecItemAttr itemAttr = kSecKeyPrintName;
|
|
PRUint32 attrFormat = 0;
|
|
OSStatus macErr;
|
|
|
|
attrInfo.count = 1;
|
|
attrInfo.tag = &itemAttr;
|
|
attrInfo.format = &attrFormat;
|
|
|
|
macErr = SecKeychainItemCopyAttributesAndData(newItemRef,
|
|
&attrInfo, NULL, &attrList, NULL, NULL);
|
|
if (noErr == macErr) {
|
|
if (nsslibc_memcmp(attrList->attr->data, IMPORTED_NAME,
|
|
attrList->attr->length, NULL) == 0) {
|
|
*outItemRef = newItemRef;
|
|
CFRelease (searchRef);
|
|
SecKeychainItemFreeAttributesAndData(attrList, NULL);
|
|
return CKR_OK;
|
|
}
|
|
SecKeychainItemFreeAttributesAndData(attrList, NULL);
|
|
}
|
|
CFRelease(newItemRef);
|
|
}
|
|
CFRelease (searchRef);
|
|
return CKR_GENERAL_ERROR; /* we can come up with something better! */
|
|
}
|
|
|
|
static ckmkInternalObject *
|
|
nss_ckmk_CreatePrivateKey
|
|
(
|
|
NSSCKFWSession *fwSession,
|
|
CK_ATTRIBUTE_PTR pTemplate,
|
|
CK_ULONG ulAttributeCount,
|
|
CK_RV *pError
|
|
)
|
|
{
|
|
NSSItem attribute;
|
|
RSAPrivateKey lk;
|
|
NSSItem keyID;
|
|
char *nickname = NULL;
|
|
ckmkInternalObject *io = NULL;
|
|
CK_KEY_TYPE keyType;
|
|
OSStatus macErr;
|
|
SecKeychainItemRef itemRef = 0;
|
|
NSSItem keyBlob = { NULL, 0 };
|
|
CFDataRef dataRef = 0;
|
|
SecExternalFormat inputFormat = kSecFormatBSAFE;
|
|
/*SecExternalFormat inputFormat = kSecFormatOpenSSL; */
|
|
SecExternalItemType itemType = kSecItemTypePrivateKey;
|
|
SecKeyImportExportParameters keyParams ;
|
|
SecKeychainRef targetKeychain = 0;
|
|
unsigned char zero = 0;
|
|
CK_RV error;
|
|
|
|
keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
|
|
keyParams.flags = 0;
|
|
keyParams.passphrase = 0;
|
|
keyParams.alertTitle = 0;
|
|
keyParams.alertPrompt = 0;
|
|
keyParams.accessRef = 0; /* default */
|
|
keyParams.keyUsage = 0; /* will get filled in */
|
|
keyParams.keyAttributes = CSSM_KEYATTR_PERMANENT; /* will get filled in */
|
|
keyType = nss_ckmk_GetULongAttribute
|
|
(CKA_KEY_TYPE, pTemplate, ulAttributeCount, pError);
|
|
if (CKR_OK != *pError) {
|
|
return (ckmkInternalObject *)NULL;
|
|
}
|
|
if (CKK_RSA != keyType) {
|
|
*pError = CKR_ATTRIBUTE_VALUE_INVALID;
|
|
return (ckmkInternalObject *)NULL;
|
|
}
|
|
if (nss_ckmk_GetBoolAttribute(CKA_DECRYPT,
|
|
pTemplate, ulAttributeCount, CK_TRUE)) {
|
|
keyParams.keyUsage |= CSSM_KEYUSE_DECRYPT;
|
|
}
|
|
if (nss_ckmk_GetBoolAttribute(CKA_UNWRAP,
|
|
pTemplate, ulAttributeCount, CK_TRUE)) {
|
|
keyParams.keyUsage |= CSSM_KEYUSE_UNWRAP;
|
|
}
|
|
if (nss_ckmk_GetBoolAttribute(CKA_SIGN,
|
|
pTemplate, ulAttributeCount, CK_TRUE)) {
|
|
keyParams.keyUsage |= CSSM_KEYUSE_SIGN;
|
|
}
|
|
if (nss_ckmk_GetBoolAttribute(CKA_DERIVE,
|
|
pTemplate, ulAttributeCount, CK_FALSE)) {
|
|
keyParams.keyUsage |= CSSM_KEYUSE_DERIVE;
|
|
}
|
|
if (nss_ckmk_GetBoolAttribute(CKA_SENSITIVE,
|
|
pTemplate, ulAttributeCount, CK_TRUE)) {
|
|
keyParams.keyAttributes |= CSSM_KEYATTR_SENSITIVE;
|
|
}
|
|
if (nss_ckmk_GetBoolAttribute(CKA_EXTRACTABLE,
|
|
pTemplate, ulAttributeCount, CK_TRUE)) {
|
|
keyParams.keyAttributes |= CSSM_KEYATTR_EXTRACTABLE;
|
|
}
|
|
|
|
lk.version.type = siUnsignedInteger;
|
|
lk.version.data = &zero;
|
|
lk.version.len = 1;
|
|
|
|
*pError = nss_ckmk_GetAttribute(CKA_MODULUS, pTemplate,
|
|
ulAttributeCount, &attribute);
|
|
if (CKR_OK != *pError) {
|
|
return (ckmkInternalObject *)NULL;
|
|
}
|
|
lk.modulus.type = siUnsignedInteger;
|
|
lk.modulus.data = attribute.data;
|
|
lk.modulus.len = attribute.size;
|
|
|
|
*pError = nss_ckmk_GetAttribute(CKA_PUBLIC_EXPONENT, pTemplate,
|
|
ulAttributeCount, &attribute);
|
|
if (CKR_OK != *pError) {
|
|
return (ckmkInternalObject *)NULL;
|
|
}
|
|
lk.publicExponent.type = siUnsignedInteger;
|
|
lk.publicExponent.data = attribute.data;
|
|
lk.publicExponent.len = attribute.size;
|
|
|
|
*pError = nss_ckmk_GetAttribute(CKA_PRIVATE_EXPONENT, pTemplate,
|
|
ulAttributeCount, &attribute);
|
|
if (CKR_OK != *pError) {
|
|
return (ckmkInternalObject *)NULL;
|
|
}
|
|
lk.privateExponent.type = siUnsignedInteger;
|
|
lk.privateExponent.data = attribute.data;
|
|
lk.privateExponent.len = attribute.size;
|
|
|
|
*pError = nss_ckmk_GetAttribute(CKA_PRIME_1, pTemplate,
|
|
ulAttributeCount, &attribute);
|
|
if (CKR_OK != *pError) {
|
|
return (ckmkInternalObject *)NULL;
|
|
}
|
|
lk.prime1.type = siUnsignedInteger;
|
|
lk.prime1.data = attribute.data;
|
|
lk.prime1.len = attribute.size;
|
|
|
|
*pError = nss_ckmk_GetAttribute(CKA_PRIME_2, pTemplate,
|
|
ulAttributeCount, &attribute);
|
|
if (CKR_OK != *pError) {
|
|
return (ckmkInternalObject *)NULL;
|
|
}
|
|
lk.prime2.type = siUnsignedInteger;
|
|
lk.prime2.data = attribute.data;
|
|
lk.prime2.len = attribute.size;
|
|
|
|
*pError = nss_ckmk_GetAttribute(CKA_EXPONENT_1, pTemplate,
|
|
ulAttributeCount, &attribute);
|
|
if (CKR_OK != *pError) {
|
|
return (ckmkInternalObject *)NULL;
|
|
}
|
|
lk.exponent1.type = siUnsignedInteger;
|
|
lk.exponent1.data = attribute.data;
|
|
lk.exponent1.len = attribute.size;
|
|
|
|
*pError = nss_ckmk_GetAttribute(CKA_EXPONENT_2, pTemplate,
|
|
ulAttributeCount, &attribute);
|
|
if (CKR_OK != *pError) {
|
|
return (ckmkInternalObject *)NULL;
|
|
}
|
|
lk.exponent2.type = siUnsignedInteger;
|
|
lk.exponent2.data = attribute.data;
|
|
lk.exponent2.len = attribute.size;
|
|
|
|
*pError = nss_ckmk_GetAttribute(CKA_COEFFICIENT, pTemplate,
|
|
ulAttributeCount, &attribute);
|
|
if (CKR_OK != *pError) {
|
|
return (ckmkInternalObject *)NULL;
|
|
}
|
|
lk.coefficient.type = siUnsignedInteger;
|
|
lk.coefficient.data = attribute.data;
|
|
lk.coefficient.len = attribute.size;
|
|
|
|
/* ASN1 Encode the pkcs8 structure... look at softoken to see how this
|
|
* is done... */
|
|
error = ckmk_CreateRSAKeyBlob(&lk, &keyBlob);
|
|
if (CKR_OK != error) {
|
|
goto loser;
|
|
}
|
|
|
|
dataRef = CFDataCreate(NULL, (UInt8 *)keyBlob.data, keyBlob.size);
|
|
if (0 == dataRef) {
|
|
*pError = CKR_HOST_MEMORY;
|
|
goto loser;
|
|
}
|
|
|
|
*pError == ckmk_GetSafeDefaultKeychain(&targetKeychain);
|
|
if (CKR_OK != *pError) {
|
|
goto loser;
|
|
}
|
|
|
|
|
|
/* the itemArray that is returned is useless. the item does not
|
|
* is 'not on the key chain' so none of the modify calls work on it.
|
|
* It also has a key that isn't the same key as the one in the actual
|
|
* key chain. In short it isn't the item we want, and it gives us zero
|
|
* information about the item we want, so don't even bother with it...
|
|
*/
|
|
macErr = SecKeychainItemImport(dataRef, NULL, &inputFormat, &itemType, 0,
|
|
&keyParams, targetKeychain, NULL);
|
|
if (noErr != macErr) {
|
|
CKMK_MACERR("Import Private Key", macErr);
|
|
*pError = CKR_GENERAL_ERROR;
|
|
goto loser;
|
|
}
|
|
|
|
*pError = ckmk_FindImportedKey(targetKeychain,
|
|
CSSM_DL_DB_RECORD_PRIVATE_KEY,
|
|
&itemRef);
|
|
if (CKR_OK != *pError) {
|
|
#ifdef DEBUG
|
|
fprintf(stderr,"couldn't find key in keychain \n");
|
|
#endif
|
|
goto loser;
|
|
}
|
|
|
|
|
|
/* set the CKA_ID and the CKA_LABEL */
|
|
error = nss_ckmk_GetAttribute(CKA_ID, pTemplate,
|
|
ulAttributeCount, &keyID);
|
|
if (CKR_OK == error) {
|
|
error = ckmk_updateAttribute(itemRef, kSecKeyLabel,
|
|
keyID.data, keyID.size, "Modify Key ID");
|
|
#ifdef DEBUG
|
|
itemdump("key id: ", keyID.data, keyID.size, error);
|
|
#endif
|
|
}
|
|
nickname = nss_ckmk_GetStringAttribute(CKA_LABEL, pTemplate,
|
|
ulAttributeCount, &error);
|
|
if (nickname) {
|
|
ckmk_updateAttribute(itemRef, kSecKeyPrintName, nickname,
|
|
strlen(nickname)+1, "Modify Key Label");
|
|
} else {
|
|
#define DEFAULT_NICKNAME "NSS Imported Key"
|
|
ckmk_updateAttribute(itemRef, kSecKeyPrintName, DEFAULT_NICKNAME,
|
|
sizeof(DEFAULT_NICKNAME), "Modify Key Label");
|
|
}
|
|
|
|
io = nss_ckmk_NewInternalObject(CKO_PRIVATE_KEY, itemRef,
|
|
CSSM_DL_DB_RECORD_PRIVATE_KEY, pError);
|
|
if ((ckmkInternalObject *)NULL == io) {
|
|
CFRelease(itemRef);
|
|
}
|
|
|
|
return io;
|
|
|
|
loser:
|
|
/* free the key blob */
|
|
if (keyBlob.data) {
|
|
nss_ZFreeIf(keyBlob.data);
|
|
}
|
|
if (0 != targetKeychain) {
|
|
CFRelease(targetKeychain);
|
|
}
|
|
if (0 != dataRef) {
|
|
CFRelease(dataRef);
|
|
}
|
|
return io;
|
|
}
|
|
|
|
|
|
NSS_EXTERN NSSCKMDObject *
|
|
nss_ckmk_CreateObject
|
|
(
|
|
NSSCKFWSession *fwSession,
|
|
CK_ATTRIBUTE_PTR pTemplate,
|
|
CK_ULONG ulAttributeCount,
|
|
CK_RV *pError
|
|
)
|
|
{
|
|
CK_OBJECT_CLASS objClass;
|
|
ckmkInternalObject *io = NULL;
|
|
CK_BBOOL isToken;
|
|
|
|
/*
|
|
* only create token objects
|
|
*/
|
|
isToken = nss_ckmk_GetBoolAttribute(CKA_TOKEN, pTemplate,
|
|
ulAttributeCount, CK_FALSE);
|
|
if (!isToken) {
|
|
*pError = CKR_ATTRIBUTE_VALUE_INVALID;
|
|
return (NSSCKMDObject *) NULL;
|
|
}
|
|
|
|
/*
|
|
* only create keys and certs.
|
|
*/
|
|
objClass = nss_ckmk_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_ckmk_CreateCertificate(fwSession, pTemplate,
|
|
ulAttributeCount, pError);
|
|
} else if (objClass == CKO_PRIVATE_KEY) {
|
|
io = nss_ckmk_CreatePrivateKey(fwSession, pTemplate,
|
|
ulAttributeCount, pError);
|
|
} else {
|
|
*pError = CKR_ATTRIBUTE_VALUE_INVALID;
|
|
}
|
|
|
|
if ((ckmkInternalObject *)NULL == io) {
|
|
return (NSSCKMDObject *) NULL;
|
|
}
|
|
return nss_ckmk_CreateMDObject(NULL, io, pError);
|
|
}
|