#include <Security/KeyItem.h>
#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
#include <Security/cssmtype.h>
#include <Security/Access.h>
#include <Security/Keychains.h>
#include <Security/KeyItem.h>
#include <Security/wrapkey.h>
#include <Security/globals.h>
static CSSM_DB_NAME_ATTR(kSecKeyLabel, 6, "Label", 0, NULL, BLOB);
static CSSM_DB_NAME_ATTR(kSecKeyPrintName, 1, "PrintName", 0, NULL, BLOB);
using namespace KeychainCore;
KeyItem::KeyItem(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId) :
ItemImpl(keychain, primaryKey, uniqueId),
mKey(NULL)
{
}
KeyItem::KeyItem(const Keychain &keychain, const PrimaryKey &primaryKey) :
ItemImpl(keychain, primaryKey),
mKey(NULL)
{
}
KeyItem::KeyItem(KeyItem &keyItem) :
ItemImpl(keyItem),
mKey(NULL)
{
}
KeyItem::~KeyItem()
{
if (mKey)
{
CssmClient::SSDbUniqueRecord uniqueId(ssDbUniqueRecord());
uniqueId->database()->csp()->freeKey(*mKey);
uniqueId->allocator().free(mKey);
}
}
void
KeyItem::update()
{
MacOSError::throwMe(unimpErr);
}
Item
KeyItem::copyTo(const Keychain &keychain)
{
MacOSError::throwMe(unimpErr);
}
void
KeyItem::didModify()
{
}
PrimaryKey
KeyItem::add(Keychain &keychain)
{
MacOSError::throwMe(unimpErr);
}
CssmClient::SSDbUniqueRecord
KeyItem::ssDbUniqueRecord()
{
DbUniqueRecordImpl *impl = &*dbUniqueRecord();
return CssmClient::SSDbUniqueRecord(safe_cast<Security::CssmClient::SSDbUniqueRecordImpl *>(impl));
}
const CssmKey &
KeyItem::cssmKey()
{
if (!mKey)
{
CssmClient::SSDbUniqueRecord uniqueId(ssDbUniqueRecord());
CssmDataContainer dataBlob(uniqueId->allocator());
uniqueId->get(NULL, &dataBlob);
mKey = reinterpret_cast<CssmKey *>(dataBlob.Data);
dataBlob.Data = NULL;
dataBlob.Length = 0;
}
return *mKey;
}
const AccessCredentials *
KeyItem::getCredentials(
CSSM_ACL_AUTHORIZATION_TAG operation,
SecCredentialType credentialType)
{
AclFactory factory;
switch (credentialType)
{
case kSecCredentialTypeDefault:
return globals().credentials();
case kSecCredentialTypeWithUI:
return factory.promptCred();
case kSecCredentialTypeNoUI:
return factory.nullCred();
default:
MacOSError::throwMe(paramErr);
}
}
void
KeyItem::createPair(
Keychain keychain,
CSSM_ALGORITHMS algorithm,
uint32 keySizeInBits,
CSSM_CC_HANDLE contextHandle,
CSSM_KEYUSE publicKeyUsage,
uint32 publicKeyAttr,
CSSM_KEYUSE privateKeyUsage,
uint32 privateKeyAttr,
RefPointer<Access> initialAccess,
RefPointer<KeyItem> &outPublicKey,
RefPointer<KeyItem> &outPrivateKey)
{
bool freeKeys = false;
bool deleteContext = false;
if (!keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP)
MacOSError::throwMe(errSecInvalidKeychain);
SSDb ssDb(safe_cast<SSDbImpl *>(&(*keychain->database())));
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, 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;
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, kSecKeyLabel, 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, kSecKeyLabel, 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;
std::string description(initialAccess->promptDescription());
pubDbAttributes.add(kSecKeyLabel, pubKeyHash);
pubDbAttributes.add(kSecKeyPrintName, description);
pubUniqueId->modify(CSSM_DL_DB_RECORD_PUBLIC_KEY, &pubDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
privDbAttributes.add(kSecKeyLabel, pubKeyHash);
privDbAttributes.add(kSecKeyPrintName, description);
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);
Access pubKeyAccess;
pubKeyAccess.setAccess(*publicKey, maker);
outPublicKey = safe_cast<KeyItem*>(&(*keychain->item(CSSM_DL_DB_RECORD_PUBLIC_KEY, pubUniqueId)));
outPrivateKey = safe_cast<KeyItem*>(&(*keychain->item(CSSM_DL_DB_RECORD_PRIVATE_KEY, privUniqueId)));
}
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);
}
void
KeyItem::importPair(
Keychain keychain,
const CSSM_KEY &publicWrappedKey,
const CSSM_KEY &privateWrappedKey,
RefPointer<Access> initialAccess,
RefPointer<KeyItem> &outPublicKey,
RefPointer<KeyItem> &outPrivateKey)
{
bool freePublicKey = false;
bool freePrivateKey = false;
bool deleteContext = false;
if (!keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP)
MacOSError::throwMe(errSecInvalidKeychain);
SSDb ssDb(safe_cast<SSDbImpl *>(&(*keychain->database())));
CssmClient::CSP csp(keychain->csp());
CssmClient::CSP appleCsp(gGuidAppleCSP);
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;
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.AlgorithmId, 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);
status = CSSM_UnwrapKey(
ccHandle,
NULL,
&publicWrappedKey,
publicWrappedKey.KeyHeader.KeyUsage,
publicWrappedKey.KeyHeader.KeyAttr,
&pubKeyHash,
&rcc,
&publicCssmKey,
NULL);
if (status)
CssmError::throwMe(status);
freePublicKey = true;
status = CSSM_UnwrapKey(
ccHandle,
NULL,
&privateWrappedKey,
privateWrappedKey.KeyHeader.KeyUsage,
privateWrappedKey.KeyHeader.KeyAttr,
&pubKeyHash,
&rcc,
&privateCssmKey,
NULL);
if (status)
CssmError::throwMe(status);
freePrivateKey = true;
DbAttributes pubDbAttributes;
DbUniqueRecord pubUniqueId;
SSDbCursor dbPubCursor(ssDb, 1);
dbPubCursor->recordType(CSSM_DL_DB_RECORD_PUBLIC_KEY);
dbPubCursor->add(CSSM_DB_EQUAL, kSecKeyLabel, 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, kSecKeyLabel, pubKeyHash);
CssmClient::Key privateKey;
if (!dbPrivCursor->nextKey(&privDbAttributes, privateKey, privUniqueId))
MacOSError::throwMe(errSecItemNotFound);
csp.allocator().free(cssmData->Data);
csp.allocator().free(cssmData);
std::string description(initialAccess->promptDescription());
pubDbAttributes.add(kSecKeyPrintName, description);
pubUniqueId->modify(CSSM_DL_DB_RECORD_PUBLIC_KEY, &pubDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
privDbAttributes.add(kSecKeyPrintName, description);
privUniqueId->modify(CSSM_DL_DB_RECORD_PRIVATE_KEY, &privDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
initialAccess->setAccess(*privateKey, maker);
Access pubKeyAccess;
pubKeyAccess.setAccess(*publicKey, maker);
outPublicKey = safe_cast<KeyItem*>(&(*keychain->item(CSSM_DL_DB_RECORD_PUBLIC_KEY, pubUniqueId)));
outPrivateKey = safe_cast<KeyItem*>(&(*keychain->item(CSSM_DL_DB_RECORD_PRIVATE_KEY, privUniqueId)));
}
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);
}