#include <security_keychain/KeyItem.h>
#include <Security/cssmtype.h>
#include <security_keychain/Access.h>
#include <security_keychain/Keychains.h>
#include <security_keychain/KeyItem.h>
#include <security_cdsa_client/wrapkey.h>
#include <security_cdsa_client/genkey.h>
#include <security_cdsa_client/signclient.h>
#include <security_cdsa_client/cryptoclient.h>
#include <security_keychain/Globals.h>
#include "KCEventNotifier.h"
#include <CommonCrypto/CommonDigest.h>
#include <SecBase.h>
static CSSM_DB_NAME_ATTR(kInfoKeyPrintName, kSecKeyPrintName, (char*) "PrintName", 0, NULL, BLOB);
static CSSM_DB_NAME_ATTR(kInfoKeyLabel, kSecKeyLabel, (char*) "Label", 0, NULL, BLOB);
static CSSM_DB_NAME_ATTR(kInfoKeyApplicationTag, kSecKeyApplicationTag, (char*) "ApplicationTag", 0, NULL, BLOB);
using namespace KeychainCore;
using namespace CssmClient;
KeyItem::KeyItem(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId) :
ItemImpl(keychain, primaryKey, uniqueId),
mKey(),
algid(NULL),
mPubKeyHash(Allocator::standard())
{
}
KeyItem::KeyItem(const Keychain &keychain, const PrimaryKey &primaryKey) :
ItemImpl(keychain, primaryKey),
mKey(),
algid(NULL),
mPubKeyHash(Allocator::standard())
{
}
KeyItem* KeyItem::make(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId)
{
KeyItem* k = new KeyItem(keychain, primaryKey, uniqueId);
keychain->addItem(primaryKey, k);
return k;
}
KeyItem* KeyItem::make(const Keychain &keychain, const PrimaryKey &primaryKey)
{
KeyItem* k = new KeyItem(keychain, primaryKey);
keychain->addItem(primaryKey, k);
return k;
}
KeyItem::KeyItem(KeyItem &keyItem) :
ItemImpl(keyItem),
mKey(),
algid(NULL),
mPubKeyHash(Allocator::standard())
{
}
KeyItem::KeyItem(const CssmClient::Key &key) :
ItemImpl(key->keyClass() + CSSM_DL_DB_RECORD_PUBLIC_KEY, (OSType)0, (UInt32)0, (const void*)NULL),
mKey(key),
algid(NULL),
mPubKeyHash(Allocator::standard())
{
if (key->keyClass() > CSSM_KEYCLASS_SESSION_KEY)
MacOSError::throwMe(errSecParam);
}
KeyItem::~KeyItem()
{
}
void
KeyItem::update()
{
ItemImpl::update();
}
Item
KeyItem::copyTo(const Keychain &keychain, Access *newAccess)
{
if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
MacOSError::throwMe(errSecInvalidKeychain);
SSDbImpl* dbImpl = dynamic_cast<SSDbImpl*>(&(*keychain->database()));
if (dbImpl == NULL)
{
CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
}
SSDb ssDb(dbImpl);
const CSSM_KEY *cssmKey = key();
if (cssmKey && (0==(cssmKey->KeyHeader.KeyAttr & CSSM_KEYATTR_EXTRACTABLE)))
{
MacOSError::throwMe(errSecDataNotAvailable);
}
CssmClient::CSP appleCsp(gGuidAppleCSP);
CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW);
uint8 labelBytes[20];
CssmData label(labelBytes, sizeof(labelBytes));
random.generate(label, (uint32)label.Length);
SecPointer<Access> access;
if (newAccess)
access = newAccess;
else
access = new Access(*mKey);
CssmClient::GenerateKey genKey(csp(), CSSM_ALGID_3DES_3KEY, 192);
CssmClient::Key wrappingKey(genKey(KeySpec(CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP,
CSSM_KEYATTR_EXTRACTABLE )));
uint8 ivBytes[8];
CssmData iv(ivBytes, sizeof(ivBytes));
random.generate(iv, (uint32)iv.length());
CssmClient::WrapKey wrap(csp(), CSSM_ALGID_3DES_3KEY_EDE);
wrap.key(wrappingKey);
wrap.cred(getCredentials(CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED, kSecCredentialTypeDefault));
wrap.mode(CSSM_ALGMODE_ECBPad);
wrap.padding(CSSM_PADDING_PKCS7);
wrap.initVector(iv);
CssmClient::Key wrappedKey(wrap(mKey));
CssmClient::UnwrapKey unwrap(keychain->csp(), CSSM_ALGID_3DES_3KEY_EDE);
unwrap.key(wrappingKey);
unwrap.mode(CSSM_ALGMODE_ECBPad);
unwrap.padding(CSSM_PADDING_PKCS7);
unwrap.initVector(iv);
unwrap.add(CSSM_ATTRIBUTE_DL_DB_HANDLE, ssDb->handle());
Access::Maker maker(Allocator::standard(), Access::Maker::kAnyMakerType);
ResourceControlContext rcc;
maker.initialOwner(rcc, NULL);
unwrap.owner(rcc.input());
uint32 usage = mKey->usage();
if (usage & CSSM_KEYUSE_ANY)
usage = CSSM_KEYUSE_ANY;
CssmClient::Key unwrappedKey(unwrap(wrappedKey, KeySpec(usage,
(mKey->attributes() | CSSM_KEYATTR_PERMANENT) & ~(CSSM_KEYATTR_ALWAYS_SENSITIVE | CSSM_KEYATTR_NEVER_EXTRACTABLE),
label)));
DbUniqueRecord uniqueId;
SSDbCursor dbCursor(ssDb, 1);
dbCursor->recordType(recordType());
dbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label);
CssmClient::Key copiedKey;
if (!dbCursor->nextKey(NULL, copiedKey, uniqueId))
MacOSError::throwMe(errSecItemNotFound);
dbUniqueRecord();
DbAttributes oldDbAttributes(mUniqueId->database(), 3);
oldDbAttributes.add(kInfoKeyLabel);
oldDbAttributes.add(kInfoKeyPrintName);
oldDbAttributes.add(kInfoKeyApplicationTag);
mUniqueId->get(&oldDbAttributes, NULL);
try
{
uniqueId->modify(recordType(), &oldDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
}
catch (CssmError e)
{
uniqueId->deleteRecord ();
throw;
}
access->setAccess(*unwrappedKey, maker);
Item item(keychain->item(recordType(), uniqueId));
KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, item);
return item;
}
Item
KeyItem::importTo(const Keychain &keychain, Access *newAccess, SecKeychainAttributeList *attrList)
{
if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
MacOSError::throwMe(errSecInvalidKeychain);
SSDbImpl* dbImpl = dynamic_cast<SSDbImpl*>(&(*keychain->database()));
if (dbImpl == NULL)
CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
SSDb ssDb(dbImpl);
if (!mKey)
CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
CssmClient::CSP appleCsp(gGuidAppleCSP);
CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW);
uint8 labelBytes[20];
CssmData label(labelBytes, sizeof(labelBytes));
random.generate(label, (uint32)label.Length);
SecPointer<Access> access;
if (newAccess)
access = newAccess;
else
access = new Access(*mKey);
CssmClient::GenerateKey genKey(csp(), CSSM_ALGID_3DES_3KEY, 192);
CssmClient::Key wrappingKey(genKey(KeySpec(CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP,
CSSM_KEYATTR_EXTRACTABLE )));
uint8 ivBytes[8];
CssmData iv(ivBytes, sizeof(ivBytes));
random.generate(iv, (uint32)iv.length());
CssmClient::WrapKey wrap(csp(), CSSM_ALGID_3DES_3KEY_EDE);
wrap.key(wrappingKey);
wrap.cred(getCredentials(CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED, kSecCredentialTypeDefault));
wrap.mode(CSSM_ALGMODE_ECBPad);
wrap.padding(CSSM_PADDING_PKCS7);
wrap.initVector(iv);
CssmClient::Key wrappedKey(wrap(mKey));
CssmClient::UnwrapKey unwrap(keychain->csp(), CSSM_ALGID_3DES_3KEY_EDE);
unwrap.key(wrappingKey);
unwrap.mode(CSSM_ALGMODE_ECBPad);
unwrap.padding(CSSM_PADDING_PKCS7);
unwrap.initVector(iv);
unwrap.add(CSSM_ATTRIBUTE_DL_DB_HANDLE, ssDb->handle());
Access::Maker maker(Allocator::standard(), Access::Maker::kAnyMakerType);
ResourceControlContext rcc;
maker.initialOwner(rcc, NULL);
unwrap.owner(rcc.input());
uint32 usage = mKey->usage();
if (usage & CSSM_KEYUSE_ANY)
usage = CSSM_KEYUSE_ANY;
CssmClient::Key unwrappedKey(unwrap(wrappedKey, KeySpec(usage,
(mKey->attributes() | CSSM_KEYATTR_PERMANENT) & ~(CSSM_KEYATTR_ALWAYS_SENSITIVE | CSSM_KEYATTR_NEVER_EXTRACTABLE),
label)));
DbUniqueRecord uniqueId;
SSDbCursor dbCursor(ssDb, 1);
dbCursor->recordType(recordType());
dbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label);
CssmClient::Key copiedKey;
if (!dbCursor->nextKey(NULL, copiedKey, uniqueId))
MacOSError::throwMe(errSecItemNotFound);
if (attrList) {
DbAttributes newDbAttributes;
SSDbCursor otherDbCursor(ssDb, 1);
otherDbCursor->recordType(recordType());
bool checkForDuplicates = false;
for (UInt32 index=0; index < attrList->count; index++) {
SecKeychainAttribute attr = attrList->attr[index];
CssmData attrData(attr.data, attr.length);
if (attr.tag == kSecKeyPrintName) {
newDbAttributes.add(kInfoKeyPrintName, attrData);
}
if (attr.tag == kSecKeyLabel) {
newDbAttributes.add(kInfoKeyLabel, attrData);
otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, attrData);
checkForDuplicates = true;
}
if (attr.tag == kSecKeyApplicationTag) {
newDbAttributes.add(kInfoKeyApplicationTag, attrData);
otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyApplicationTag, attrData);
checkForDuplicates = true;
}
}
DbAttributes otherDbAttributes;
DbUniqueRecord otherUniqueId;
CssmClient::Key otherKey;
try
{
if (checkForDuplicates && otherDbCursor->nextKey(&otherDbAttributes, otherKey, otherUniqueId))
MacOSError::throwMe(errSecDuplicateItem);
uniqueId->modify(recordType(), &newDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
}
catch (CssmError e)
{
uniqueId->deleteRecord ();
throw;
}
}
access->setAccess(*unwrappedKey, maker);
Item item(keychain->item(recordType(), uniqueId));
KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, item);
return item;
}
void
KeyItem::didModify()
{
}
PrimaryKey
KeyItem::add(Keychain &keychain)
{
MacOSError::throwMe(errSecUnimplemented);
}
CssmClient::SSDbUniqueRecord
KeyItem::ssDbUniqueRecord()
{
DbUniqueRecordImpl *impl = &*dbUniqueRecord();
Security::CssmClient::SSDbUniqueRecordImpl *simpl = dynamic_cast<Security::CssmClient::SSDbUniqueRecordImpl *>(impl);
if (simpl == NULL)
{
CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
}
return CssmClient::SSDbUniqueRecord(simpl);
}
CssmClient::Key &
KeyItem::key()
{
if (!mKey)
{
CssmClient::SSDbUniqueRecord uniqueId(ssDbUniqueRecord());
CssmDataContainer dataBlob(uniqueId->allocator());
uniqueId->get(NULL, &dataBlob);
mKey = CssmClient::Key(uniqueId->database()->csp(), *reinterpret_cast<CssmKey *>(dataBlob.Data));
}
return mKey;
}
CssmClient::CSP
KeyItem::csp()
{
return key()->csp();
}
const CSSM_X509_ALGORITHM_IDENTIFIER&
KeyItem::algorithmIdentifier()
{
#if 0
CssmKey *mKey;
CSSM_KEY_TYPE algorithm
CSSM_KEY_PTR cssmKey = (CSSM_KEY_PTR)thisData->Data;
cssmKey->KeyHeader
static void printKeyHeader(
const CSSM_KEYHEADER &hdr)
{
printf(" Algorithm : ");
switch(hdr.AlgorithmId) {
CSSM_X509_ALGORITHM_IDENTIFIER algID;
CSSM_OID *CL_algToOid(
CSSM_ALGORITHMS algId)
typedef struct cssm_x509_algorithm_identifier {
CSSM_OID algorithm;
CSSM_DATA parameters;
} CSSM_X509_ALGORITHM_IDENTIFIER, *CSSM_X509_ALGORITHM_IDENTIFIER_PTR;
#endif
abort();
}
const CssmData &KeyItem::itemID()
{
if(mPubKeyHash.length() == 0) {
UInt32 tag = kSecKeyLabel;
UInt32 format = 0;
SecKeychainAttributeInfo attrInfo = {1, &tag, &format};
SecKeychainAttributeList *attrList = NULL;
getAttributesAndData(&attrInfo, NULL, &attrList, NULL, NULL);
if((attrList == NULL) || (attrList->count != 1)) {
MacOSError::throwMe(errSecNoSuchAttr);
}
mPubKeyHash.copy(attrList->attr->data, attrList->attr->length);
freeAttributesAndData(attrList, NULL);
}
return mPubKeyHash;
}
unsigned int
KeyItem::strengthInBits(const CSSM_X509_ALGORITHM_IDENTIFIER *algid)
{
CSSM_KEY_SIZE keySize = {};
CSSM_RETURN rv = CSSM_QueryKeySizeInBits (csp()->handle(),
CSSM_INVALID_HANDLE,
key(),
&keySize);
if (rv)
return 0;
return keySize.LogicalKeySizeInBits;
}
const AccessCredentials *
KeyItem::getCredentials(
CSSM_ACL_AUTHORIZATION_TAG operation,
SecCredentialType credentialType)
{
bool smartcard = keychain() != NULL ? (keychain()->database()->dl()->guid() == gGuidAppleSdCSPDL) : false;
AclFactory factory;
switch (credentialType)
{
case kSecCredentialTypeDefault:
return smartcard?globals().smartcardItemCredentials():globals().itemCredentials();
case kSecCredentialTypeWithUI:
return smartcard?globals().smartcardItemCredentials():factory.promptCred();
case kSecCredentialTypeNoUI:
return factory.nullCred();
default:
MacOSError::throwMe(errSecParam);
}
}
bool
KeyItem::operator == (KeyItem &other)
{
if (mKey && *mKey)
{
return this == &other;
}
Keychain otherKeychain = other.keychain();
return (mKeychain && otherKeychain && (*mKeychain == *otherKeychain));
}
void
KeyItem::createPair(
Keychain keychain,
CSSM_ALGORITHMS algorithm,
uint32 keySizeInBits,
CSSM_CC_HANDLE contextHandle,
CSSM_KEYUSE publicKeyUsage,
uint32 publicKeyAttr,
CSSM_KEYUSE privateKeyUsage,
uint32 privateKeyAttr,
SecPointer<Access> initialAccess,
SecPointer<KeyItem> &outPublicKey,
SecPointer<KeyItem> &outPrivateKey)
{
bool freeKeys = false;
bool deleteContext = false;
if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
MacOSError::throwMe(errSecInvalidKeychain);
SSDbImpl* impl = dynamic_cast<SSDbImpl*>(&(*keychain->database()));
if (impl == NULL)
{
CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
}
SSDb ssDb(impl);
CssmClient::CSP csp(keychain->csp());
CssmClient::CSP appleCsp(gGuidAppleCSP);
CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW);
uint8 labelBytes[20];
CssmData label(labelBytes, sizeof(labelBytes));
random.generate(label, (uint32)label.Length);
ResourceControlContext rcc;
memset(&rcc, 0, sizeof(rcc));
Access::Maker maker;
maker.initialOwner(rcc);
const AccessCredentials *cred = maker.cred();
CSSM_KEY publicCssmKey, privateCssmKey;
memset(&publicCssmKey, 0, sizeof(publicCssmKey));
memset(&privateCssmKey, 0, sizeof(privateCssmKey));
CSSM_CC_HANDLE ccHandle = 0;
Item publicKeyItem, privateKeyItem;
try
{
CSSM_RETURN status;
if (contextHandle)
ccHandle = contextHandle;
else
{
status = CSSM_CSP_CreateKeyGenContext(csp->handle(), algorithm, keySizeInBits, NULL, NULL, NULL, NULL, NULL, &ccHandle);
if (status)
CssmError::throwMe(status);
deleteContext = true;
}
CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle();
CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle;
CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } };
status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes);
if (status)
CssmError::throwMe(status);
status = CSSM_GenerateKeyPair(ccHandle, publicKeyUsage, publicKeyAttr, &label, &publicCssmKey, privateKeyUsage, privateKeyAttr, &label, &rcc, &privateCssmKey);
if (status)
CssmError::throwMe(status);
freeKeys = true;
DbAttributes pubDbAttributes;
DbUniqueRecord pubUniqueId;
SSDbCursor dbPubCursor(ssDb, 1);
dbPubCursor->recordType(CSSM_DL_DB_RECORD_PUBLIC_KEY);
dbPubCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label);
CssmClient::Key publicKey;
if (!dbPubCursor->nextKey(&pubDbAttributes, publicKey, pubUniqueId))
MacOSError::throwMe(errSecItemNotFound);
DbAttributes privDbAttributes;
DbUniqueRecord privUniqueId;
SSDbCursor dbPrivCursor(ssDb, 1);
dbPrivCursor->recordType(CSSM_DL_DB_RECORD_PRIVATE_KEY);
dbPrivCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label);
CssmClient::Key privateKey;
if (!dbPrivCursor->nextKey(&privDbAttributes, privateKey, privUniqueId))
MacOSError::throwMe(errSecItemNotFound);
CssmClient::WrapKey wrap(csp, CSSM_ALGID_NONE);
wrap.cred(cred);
CssmClient::Key rawPubKey = wrap(publicKey);
CssmClient::PassThrough passThrough(appleCsp);
void *outData;
CssmData *cssmData;
passThrough.key(rawPubKey);
passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData);
cssmData = reinterpret_cast<CssmData *>(outData);
CssmData &pubKeyHash = *cssmData;
auto_ptr<string>privDescription;
auto_ptr<string>pubDescription;
try {
privDescription.reset(new string(initialAccess->promptDescription()));
pubDescription.reset(new string(initialAccess->promptDescription()));
}
catch(...) {
privDescription.reset(new string("Private key"));
pubDescription.reset(new string("Public key"));
}
pubDbAttributes.add(kInfoKeyLabel, pubKeyHash);
pubDbAttributes.add(kInfoKeyPrintName, *pubDescription);
pubUniqueId->modify(CSSM_DL_DB_RECORD_PUBLIC_KEY, &pubDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
privDbAttributes.add(kInfoKeyLabel, pubKeyHash);
privDbAttributes.add(kInfoKeyPrintName, *privDescription);
privUniqueId->modify(CSSM_DL_DB_RECORD_PRIVATE_KEY, &privDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
csp.allocator().free(cssmData->Data);
csp.allocator().free(cssmData);
initialAccess->setAccess(*privateKey, maker);
if(publicKeyAttr & CSSM_KEYATTR_PUBLIC_KEY_ENCRYPT) {
SecPointer<Access> pubKeyAccess(new Access());
pubKeyAccess->setAccess(*publicKey, maker);
}
publicKeyItem = keychain->item(CSSM_DL_DB_RECORD_PUBLIC_KEY, pubUniqueId);
privateKeyItem = keychain->item(CSSM_DL_DB_RECORD_PRIVATE_KEY, privUniqueId);
KeyItem* impl = dynamic_cast<KeyItem*>(&(*publicKeyItem));
if (impl == NULL)
{
CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
}
outPublicKey = impl;
impl = dynamic_cast<KeyItem*>(&(*privateKeyItem));
if (impl == NULL)
{
CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
}
outPrivateKey = impl;
}
catch (...)
{
if (freeKeys)
{
CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, TRUE);
CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, TRUE);
}
if (deleteContext)
CSSM_DeleteContext(ccHandle);
throw;
}
if (freeKeys)
{
CSSM_FreeKey(csp->handle(), NULL, &publicCssmKey, FALSE);
CSSM_FreeKey(csp->handle(), NULL, &privateCssmKey, FALSE);
}
if (deleteContext)
CSSM_DeleteContext(ccHandle);
if (keychain && publicKeyItem && privateKeyItem)
{
keychain->postEvent(kSecAddEvent, publicKeyItem);
keychain->postEvent(kSecAddEvent, privateKeyItem);
}
}
void
KeyItem::importPair(
Keychain keychain,
const CSSM_KEY &publicWrappedKey,
const CSSM_KEY &privateWrappedKey,
SecPointer<Access> initialAccess,
SecPointer<KeyItem> &outPublicKey,
SecPointer<KeyItem> &outPrivateKey)
{
bool freePublicKey = false;
bool freePrivateKey = false;
bool deleteContext = false;
if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
MacOSError::throwMe(errSecInvalidKeychain);
SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*keychain->database()));
if (impl == NULL)
{
CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
}
SSDb ssDb(impl);
CssmClient::CSP csp(keychain->csp());
CssmClient::CSP appleCsp(gGuidAppleCSP);
ResourceControlContext rcc;
memset(&rcc, 0, sizeof(rcc));
Access::Maker maker(Allocator::standard(), Access::Maker::kAnyMakerType);
maker.initialOwner(rcc);
const AccessCredentials *cred = maker.cred();
CSSM_KEY publicCssmKey, privateCssmKey;
memset(&publicCssmKey, 0, sizeof(publicCssmKey));
memset(&privateCssmKey, 0, sizeof(privateCssmKey));
CSSM_CC_HANDLE ccHandle = 0;
Item publicKeyItem, privateKeyItem;
try
{
CSSM_RETURN status;
CssmClient::PassThrough passThrough(appleCsp);
void *outData;
CssmData *cssmData;
passThrough.key(&publicWrappedKey);
passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData);
cssmData = reinterpret_cast<CssmData *>(outData);
CssmData &pubKeyHash = *cssmData;
status = CSSM_CSP_CreateSymmetricContext(csp->handle(), publicWrappedKey.KeyHeader.WrapAlgorithmId, CSSM_ALGMODE_NONE, NULL, NULL, NULL, CSSM_PADDING_NONE, NULL, &ccHandle);
if (status)
CssmError::throwMe(status);
deleteContext = true;
CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle();
CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle;
CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } };
status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes);
if (status)
CssmError::throwMe(status);
CSSM_DATA descriptiveData = {0, NULL};
status = CSSM_UnwrapKey(
ccHandle,
NULL,
&publicWrappedKey,
publicWrappedKey.KeyHeader.KeyUsage,
publicWrappedKey.KeyHeader.KeyAttr | CSSM_KEYATTR_PERMANENT,
&pubKeyHash,
&rcc,
&publicCssmKey,
&descriptiveData);
if (status)
CssmError::throwMe(status);
freePublicKey = true;
if (descriptiveData.Data != NULL)
free (descriptiveData.Data);
status = CSSM_UnwrapKey(
ccHandle,
NULL,
&privateWrappedKey,
privateWrappedKey.KeyHeader.KeyUsage,
privateWrappedKey.KeyHeader.KeyAttr | CSSM_KEYATTR_PERMANENT,
&pubKeyHash,
&rcc,
&privateCssmKey,
&descriptiveData);
if (status)
CssmError::throwMe(status);
if (descriptiveData.Data != NULL)
free (descriptiveData.Data);
freePrivateKey = true;
DbAttributes pubDbAttributes;
DbUniqueRecord pubUniqueId;
SSDbCursor dbPubCursor(ssDb, 1);
dbPubCursor->recordType(CSSM_DL_DB_RECORD_PUBLIC_KEY);
dbPubCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, pubKeyHash);
CssmClient::Key publicKey;
if (!dbPubCursor->nextKey(&pubDbAttributes, publicKey, pubUniqueId))
MacOSError::throwMe(errSecItemNotFound);
DbAttributes privDbAttributes;
DbUniqueRecord privUniqueId;
SSDbCursor dbPrivCursor(ssDb, 1);
dbPrivCursor->recordType(CSSM_DL_DB_RECORD_PRIVATE_KEY);
dbPrivCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, pubKeyHash);
CssmClient::Key privateKey;
if (!dbPrivCursor->nextKey(&privDbAttributes, privateKey, privUniqueId))
MacOSError::throwMe(errSecItemNotFound);
csp.allocator().free(cssmData->Data);
csp.allocator().free(cssmData);
auto_ptr<string>privDescription;
auto_ptr<string>pubDescription;
try {
privDescription.reset(new string(initialAccess->promptDescription()));
pubDescription.reset(new string(initialAccess->promptDescription()));
}
catch(...) {
privDescription.reset(new string("Private key"));
pubDescription.reset(new string("Public key"));
}
pubDbAttributes.add(kInfoKeyPrintName, *pubDescription);
pubUniqueId->modify(CSSM_DL_DB_RECORD_PUBLIC_KEY, &pubDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
privDbAttributes.add(kInfoKeyPrintName, *privDescription);
privUniqueId->modify(CSSM_DL_DB_RECORD_PRIVATE_KEY, &privDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
initialAccess->setAccess(*privateKey, maker);
SecPointer<Access> pubKeyAccess(new Access());
pubKeyAccess->setAccess(*publicKey, maker);
publicKeyItem = keychain->item(CSSM_DL_DB_RECORD_PUBLIC_KEY, pubUniqueId);
privateKeyItem = keychain->item(CSSM_DL_DB_RECORD_PRIVATE_KEY, privUniqueId);
KeyItem* impl = dynamic_cast<KeyItem*>(&(*publicKeyItem));
if (impl == NULL)
{
CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
}
outPublicKey = impl;
impl = dynamic_cast<KeyItem*>(&(*privateKeyItem));
if (impl == NULL)
{
CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
}
outPrivateKey = impl;
}
catch (...)
{
if (freePublicKey)
CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, TRUE);
if (freePrivateKey)
CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, TRUE);
if (deleteContext)
CSSM_DeleteContext(ccHandle);
throw;
}
if (freePublicKey)
CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, FALSE);
if (freePrivateKey)
CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, FALSE);
if (deleteContext)
CSSM_DeleteContext(ccHandle);
if (keychain && publicKeyItem && privateKeyItem)
{
KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, publicKeyItem);
KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, privateKeyItem);
}
}
SecPointer<KeyItem>
KeyItem::generateWithAttributes(const SecKeychainAttributeList *attrList,
Keychain keychain,
CSSM_ALGORITHMS algorithm,
uint32 keySizeInBits,
CSSM_CC_HANDLE contextHandle,
CSSM_KEYUSE keyUsage,
uint32 keyAttr,
SecPointer<Access> initialAccess)
{
CssmClient::CSP appleCsp(gGuidAppleCSP);
CssmClient::CSP csp(NULL);
SSDb ssDb(NULL);
uint8 labelBytes[20];
CssmData label(labelBytes, sizeof(labelBytes));
bool freeKey = false;
bool deleteContext = false;
const CSSM_DATA *plabel = NULL;
if (keychain)
{
if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
MacOSError::throwMe(errSecInvalidKeychain);
SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*keychain->database()));
if (impl == NULL)
{
CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
}
ssDb = SSDb(impl);
csp = keychain->csp();
CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW);
random.generate(label, (uint32)label.Length);
plabel = &label;
}
else
{
csp = appleCsp;
}
ResourceControlContext *prcc = NULL, rcc;
const AccessCredentials *cred = NULL;
Access::Maker maker;
if (keychain && initialAccess)
{
memset(&rcc, 0, sizeof(rcc));
maker.initialOwner(rcc);
cred = maker.cred();
prcc = &rcc;
}
CSSM_KEY cssmKey;
CSSM_CC_HANDLE ccHandle = 0;
Item keyItem;
try
{
CSSM_RETURN status;
if (contextHandle)
ccHandle = contextHandle;
else
{
status = CSSM_CSP_CreateKeyGenContext(csp->handle(), algorithm, keySizeInBits, NULL, NULL, NULL, NULL, NULL, &ccHandle);
if (status)
CssmError::throwMe(status);
deleteContext = true;
}
if (ssDb)
{
CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle();
CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle;
CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } };
status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes);
if (status)
CssmError::throwMe(status);
keyAttr |= CSSM_KEYATTR_PERMANENT;
}
status = CSSM_GenerateKey(ccHandle, keyUsage, keyAttr, plabel, prcc, &cssmKey);
if (status)
CssmError::throwMe(status);
if (ssDb)
{
freeKey = true;
DbAttributes dbAttributes;
DbUniqueRecord uniqueId;
SSDbCursor dbCursor(ssDb, 1);
dbCursor->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY);
dbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label);
CssmClient::Key key;
if (!dbCursor->nextKey(&dbAttributes, key, uniqueId))
MacOSError::throwMe(errSecItemNotFound);
if (attrList) {
DbAttributes newDbAttributes;
SSDbCursor otherDbCursor(ssDb, 1);
otherDbCursor->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY);
bool checkForDuplicates = false;
for (UInt32 index=0; index < attrList->count; index++) {
SecKeychainAttribute attr = attrList->attr[index];
CssmData attrData(attr.data, attr.length);
if (attr.tag == kSecKeyPrintName) {
newDbAttributes.add(kInfoKeyPrintName, attrData);
}
if (attr.tag == kSecKeyLabel) {
newDbAttributes.add(kInfoKeyLabel, attrData);
otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, attrData);
checkForDuplicates = true;
}
if (attr.tag == kSecKeyApplicationTag) {
newDbAttributes.add(kInfoKeyApplicationTag, attrData);
otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyApplicationTag, attrData);
checkForDuplicates = true;
}
}
DbAttributes otherDbAttributes;
DbUniqueRecord otherUniqueId;
CssmClient::Key otherKey;
if (checkForDuplicates && otherDbCursor->nextKey(&otherDbAttributes, otherKey, otherUniqueId))
MacOSError::throwMe(errSecDuplicateItem);
uniqueId->modify(CSSM_DL_DB_RECORD_SYMMETRIC_KEY, &newDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
}
if (initialAccess)
initialAccess->setAccess(*key, maker);
keyItem = keychain->item(CSSM_DL_DB_RECORD_SYMMETRIC_KEY, uniqueId);
}
else
{
CssmClient::Key tempKey(csp, cssmKey);
keyItem = new KeyItem(tempKey);
}
}
catch (...)
{
if (freeKey)
{
CSSM_FreeKey(csp->handle(), cred, &cssmKey, TRUE);
}
if (deleteContext)
CSSM_DeleteContext(ccHandle);
throw;
}
if (freeKey)
{
CSSM_FreeKey(csp->handle(), NULL, &cssmKey, FALSE);
}
if (deleteContext)
CSSM_DeleteContext(ccHandle);
if (keychain && keyItem)
keychain->postEvent(kSecAddEvent, keyItem);
KeyItem* item = dynamic_cast<KeyItem*>(&*keyItem);
if (item == NULL)
{
CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
}
return item;
}
SecPointer<KeyItem>
KeyItem::generate(Keychain keychain,
CSSM_ALGORITHMS algorithm,
uint32 keySizeInBits,
CSSM_CC_HANDLE contextHandle,
CSSM_KEYUSE keyUsage,
uint32 keyAttr,
SecPointer<Access> initialAccess)
{
return KeyItem::generateWithAttributes(NULL, keychain,
algorithm, keySizeInBits, contextHandle,
keyUsage, keyAttr, initialAccess);
}
void KeyItem::RawSign(SecPadding padding, CSSM_DATA dataToSign, const AccessCredentials *credentials, CSSM_DATA& signature)
{
CSSM_ALGORITHMS baseAlg = key()->header().algorithm();
if ((baseAlg != CSSM_ALGID_RSA) && (baseAlg != CSSM_ALGID_ECDSA))
{
MacOSError::throwMe(errSecParam);
}
CSSM_ALGORITHMS paddingAlg = CSSM_PADDING_PKCS1;
switch (padding)
{
case kSecPaddingPKCS1:
{
paddingAlg = CSSM_PADDING_PKCS1;
break;
}
case kSecPaddingPKCS1MD2:
{
baseAlg = CSSM_ALGID_MD2WithRSA;
break;
}
case kSecPaddingPKCS1MD5:
{
baseAlg = CSSM_ALGID_MD5WithRSA;
break;
}
case kSecPaddingPKCS1SHA1:
{
baseAlg = CSSM_ALGID_SHA1WithRSA;
break;
}
default:
{
paddingAlg = CSSM_PADDING_NONE;
break;
}
}
Sign signContext(csp(), baseAlg);
signContext.key(key());
signContext.set(CSSM_ATTRIBUTE_PADDING, paddingAlg);
signContext.cred(credentials);
CssmData data(dataToSign.Data, dataToSign.Length);
signContext.sign(data);
CssmData sig(signature.Data, signature.Length);
signContext(sig); signature.Length = sig.length();
}
void KeyItem::RawVerify(SecPadding padding, CSSM_DATA dataToVerify, const AccessCredentials *credentials, CSSM_DATA sig)
{
CSSM_ALGORITHMS baseAlg = key()->header().algorithm();
if ((baseAlg != CSSM_ALGID_RSA) && (baseAlg != CSSM_ALGID_ECDSA))
{
MacOSError::throwMe(errSecParam);
}
CSSM_ALGORITHMS paddingAlg = CSSM_PADDING_PKCS1;
switch (padding)
{
case kSecPaddingPKCS1:
{
paddingAlg = CSSM_PADDING_PKCS1;
break;
}
case kSecPaddingPKCS1MD2:
{
baseAlg = CSSM_ALGID_MD2WithRSA;
break;
}
case kSecPaddingPKCS1MD5:
{
baseAlg = CSSM_ALGID_MD5WithRSA;
break;
}
case kSecPaddingPKCS1SHA1:
{
baseAlg = CSSM_ALGID_SHA1WithRSA;
break;
}
default:
{
paddingAlg = CSSM_PADDING_NONE;
break;
}
}
Verify verifyContext(csp(), baseAlg);
verifyContext.key(key());
verifyContext.set(CSSM_ATTRIBUTE_PADDING, paddingAlg);
verifyContext.cred(credentials);
CssmData data(dataToVerify.Data, dataToVerify.Length);
CssmData signature(sig.Data, sig.Length);
verifyContext.verify(data, signature);
}
void KeyItem::Encrypt(SecPadding padding, CSSM_DATA dataToEncrypt, const AccessCredentials *credentials, CSSM_DATA& encryptedData)
{
CSSM_ALGORITHMS baseAlg = key()->header().algorithm();
if (baseAlg != CSSM_ALGID_RSA)
{
MacOSError::throwMe(errSecParam);
}
CSSM_ALGORITHMS paddingAlg = CSSM_PADDING_PKCS1;
switch (padding)
{
case kSecPaddingPKCS1:
{
paddingAlg = CSSM_PADDING_PKCS1;
break;
}
default:
{
paddingAlg = CSSM_PADDING_NONE;
break;
}
}
CssmClient::Encrypt encryptContext(csp(), baseAlg);
encryptContext.key(key());
encryptContext.padding(paddingAlg);
encryptContext.cred(credentials);
CssmData inData(dataToEncrypt.Data, dataToEncrypt.Length);
CssmData outData(encryptedData.Data, encryptedData.Length);
CssmData remData((void*) NULL, 0);
encryptedData.Length = encryptContext.encrypt(inData, outData, remData);
}
void KeyItem::Decrypt(SecPadding padding, CSSM_DATA dataToDecrypt, const AccessCredentials *credentials, CSSM_DATA& decryptedData)
{
CSSM_ALGORITHMS baseAlg = key()->header().algorithm();
if (baseAlg != CSSM_ALGID_RSA)
{
MacOSError::throwMe(errSecParam);
}
CSSM_ALGORITHMS paddingAlg = CSSM_PADDING_PKCS1;
switch (padding)
{
case kSecPaddingPKCS1:
{
paddingAlg = CSSM_PADDING_PKCS1;
break;
}
default:
{
paddingAlg = CSSM_PADDING_NONE;
break;
}
}
CssmClient::Decrypt decryptContext(csp(), baseAlg);
decryptContext.key(key());
decryptContext.padding(paddingAlg);
decryptContext.cred(credentials);
CssmData inData(dataToDecrypt.Data, dataToDecrypt.Length);
CssmData outData(decryptedData.Data, decryptedData.Length);
CssmData remData((void*) NULL, 0);
decryptedData.Length = decryptContext.decrypt(inData, outData, remData);
if (remData.Data != NULL)
{
free(remData.Data);
}
}
CFHashCode KeyItem::hash()
{
CFHashCode result = 0;
const CSSM_KEY *cssmKey = key();
if (NULL != cssmKey)
{
unsigned char digest[CC_SHA256_DIGEST_LENGTH];
CFIndex size_of_data = sizeof(CSSM_KEYHEADER) + cssmKey->KeyData.Length;
CFMutableDataRef temp_cfdata = CFDataCreateMutable(kCFAllocatorDefault, size_of_data);
if (NULL == temp_cfdata)
{
return result;
}
CFDataAppendBytes(temp_cfdata, (const UInt8 *)cssmKey, sizeof(CSSM_KEYHEADER));
CFDataAppendBytes(temp_cfdata, cssmKey->KeyData.Data, cssmKey->KeyData.Length);
if (size_of_data < 80)
{
result = CFHash(temp_cfdata);
CFRelease(temp_cfdata);
}
else
{
memset(digest, 0, CC_SHA256_DIGEST_LENGTH);
CC_SHA256((const void *)CFDataGetBytePtr(temp_cfdata), (CC_LONG)CFDataGetLength(temp_cfdata), digest);
CFDataRef data_to_hash = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
(const UInt8 *)digest, CC_SHA256_DIGEST_LENGTH, kCFAllocatorNull);
result = CFHash(data_to_hash);
CFRelease(data_to_hash);
CFRelease(temp_cfdata);
}
}
return result;
}