summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/ckfw/nssmkey/mobject.c
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /security/nss/lib/ckfw/nssmkey/mobject.c
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'security/nss/lib/ckfw/nssmkey/mobject.c')
-rw-r--r--security/nss/lib/ckfw/nssmkey/mobject.c1861
1 files changed, 1861 insertions, 0 deletions
diff --git a/security/nss/lib/ckfw/nssmkey/mobject.c b/security/nss/lib/ckfw/nssmkey/mobject.c
new file mode 100644
index 000000000..b19a8fdbd
--- /dev/null
+++ b/security/nss/lib/ckfw/nssmkey/mobject.c
@@ -0,0 +1,1861 @@
+/* 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);
+}