mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-15 04:00:12 +01:00
1962 lines
52 KiB
C
1962 lines
52 KiB
C
|
/* ***** BEGIN LICENSE BLOCK *****
|
||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||
|
*
|
||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||
|
* the License. You may obtain a copy of the License at
|
||
|
* http://www.mozilla.org/MPL/
|
||
|
*
|
||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||
|
* for the specific language governing rights and limitations under the
|
||
|
* License.
|
||
|
*
|
||
|
* The Original Code is the Netscape security libraries.
|
||
|
*
|
||
|
* The Initial Developer of the Original Code is
|
||
|
* Netscape Communications Corporation.
|
||
|
* Portions created by the Initial Developer are Copyright (C) 1994-2000
|
||
|
* the Initial Developer. All Rights Reserved.
|
||
|
* Portions created by Red Hat, Inc, are Copyright (C) 2005
|
||
|
*
|
||
|
* Contributor(s):
|
||
|
* Bob Relyea (rrelyea@redhat.com)
|
||
|
*
|
||
|
* Alternatively, the contents of this file may be used under the terms of
|
||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||
|
* of those above. If you wish to allow use of your version of this file only
|
||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||
|
* use your version of this file under the terms of the MPL, indicate your
|
||
|
* decision by deleting the provisions above and replace them with the notice
|
||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||
|
* the provisions above, a recipient may use your version of this file under
|
||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||
|
*
|
||
|
* ***** END LICENSE BLOCK ***** */
|
||
|
#ifdef DEBUG
|
||
|
static const char CVS_ID[] = "@(#) $RCSfile: mobject.c,v $ $Revision: 1.4 $ $Date: 2009/02/25 18:38:04 $";
|
||
|
#endif /* DEBUG */
|
||
|
|
||
|
#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 implment 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;
|
||
|
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);
|
||
|
}
|