1/*
2 * Copyright (c) 2002-2004,2011-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24//
25// KeyItem.cpp
26//
27#include <security_keychain/KeyItem.h>
28#include <Security/cssmtype.h>
29#include <security_keychain/Access.h>
30#include <security_keychain/Keychains.h>
31#include <security_keychain/KeyItem.h>
32#include <security_cdsa_client/wrapkey.h>
33#include <security_cdsa_client/genkey.h>
34#include <security_cdsa_client/signclient.h>
35#include <security_cdsa_client/cryptoclient.h>
36
37#include <security_keychain/Globals.h>
38#include "KCEventNotifier.h"
39#include <CommonCrypto/CommonDigest.h>
40#include <SecBase.h>
41
42// @@@ This needs to be shared.
43#pragma clang diagnostic push
44#pragma clang diagnostic ignored "-Wunused-const-variable"
45static CSSM_DB_NAME_ATTR(kInfoKeyPrintName, kSecKeyPrintName, (char*) "PrintName", 0, NULL, BLOB);
46static CSSM_DB_NAME_ATTR(kInfoKeyLabel, kSecKeyLabel, (char*) "Label", 0, NULL, BLOB);
47static CSSM_DB_NAME_ATTR(kInfoKeyApplicationTag, kSecKeyApplicationTag, (char*) "ApplicationTag", 0, NULL, BLOB);
48#pragma clang diagnostic pop
49
50using namespace KeychainCore;
51using namespace CssmClient;
52
53KeyItem::KeyItem(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId) :
54	ItemImpl(keychain, primaryKey, uniqueId),
55	mKey(),
56	algid(NULL),
57	mPubKeyHash(Allocator::standard())
58{
59}
60
61KeyItem::KeyItem(const Keychain &keychain, const PrimaryKey &primaryKey)  :
62	ItemImpl(keychain, primaryKey),
63	mKey(),
64	algid(NULL),
65	mPubKeyHash(Allocator::standard())
66{
67}
68
69KeyItem* KeyItem::make(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId)
70{
71	KeyItem* k = new KeyItem(keychain, primaryKey, uniqueId);
72	keychain->addItem(primaryKey, k);
73	return k;
74}
75
76
77
78KeyItem* KeyItem::make(const Keychain &keychain, const PrimaryKey &primaryKey)
79{
80	KeyItem* k = new KeyItem(keychain, primaryKey);
81	keychain->addItem(primaryKey, k);
82	return k;
83}
84
85
86
87KeyItem::KeyItem(KeyItem &keyItem) :
88	ItemImpl(keyItem),
89	mKey(),
90	algid(NULL),
91	mPubKeyHash(Allocator::standard())
92{
93	// @@@ this doesn't work for keys that are not in a keychain.
94}
95
96KeyItem::KeyItem(const CssmClient::Key &key) :
97    ItemImpl(key->keyClass() + CSSM_DL_DB_RECORD_PUBLIC_KEY, (OSType)0, (UInt32)0, (const void*)NULL),
98	mKey(key),
99	algid(NULL),
100	mPubKeyHash(Allocator::standard())
101{
102	if (key->keyClass() > CSSM_KEYCLASS_SESSION_KEY)
103		MacOSError::throwMe(errSecParam);
104}
105
106KeyItem::~KeyItem()
107{
108}
109
110void
111KeyItem::update()
112{
113	ItemImpl::update();
114}
115
116Item
117KeyItem::copyTo(const Keychain &keychain, Access *newAccess)
118{
119	if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
120		MacOSError::throwMe(errSecInvalidKeychain);
121
122	/* Get the destination keychain's db. */
123	SSDbImpl* dbImpl = dynamic_cast<SSDbImpl*>(&(*keychain->database()));
124	if (dbImpl == NULL)
125	{
126		CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
127	}
128
129	SSDb ssDb(dbImpl);
130
131	/* Make sure mKey is valid. */
132	const CSSM_KEY *cssmKey = key();
133	if (cssmKey && (0==(cssmKey->KeyHeader.KeyAttr & CSSM_KEYATTR_EXTRACTABLE)))
134	{
135		MacOSError::throwMe(errSecDataNotAvailable);
136	}
137
138	// Generate a random label to use initially
139	CssmClient::CSP appleCsp(gGuidAppleCSP);
140	CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW);
141	uint8 labelBytes[20];
142	CssmData label(labelBytes, sizeof(labelBytes));
143	random.generate(label, (uint32)label.Length);
144
145	/* Set up the ACL for the new key. */
146	SecPointer<Access> access;
147	if (newAccess)
148		access = newAccess;
149	else
150		access = new Access(*mKey);
151
152	/* Generate a random 3DES wrapping Key. */
153	CssmClient::GenerateKey genKey(csp(), CSSM_ALGID_3DES_3KEY, 192);
154	CssmClient::Key wrappingKey(genKey(KeySpec(CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP,
155		CSSM_KEYATTR_EXTRACTABLE /* | CSSM_KEYATTR_RETURN_DATA */)));
156
157	/* make a random IV */
158	uint8 ivBytes[8];
159	CssmData iv(ivBytes, sizeof(ivBytes));
160	random.generate(iv, (uint32)iv.length());
161
162	/* Extract the key by wrapping it with the wrapping key. */
163	CssmClient::WrapKey wrap(csp(), CSSM_ALGID_3DES_3KEY_EDE);
164	wrap.key(wrappingKey);
165	wrap.cred(getCredentials(CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED, kSecCredentialTypeDefault));
166	wrap.mode(CSSM_ALGMODE_ECBPad);
167	wrap.padding(CSSM_PADDING_PKCS7);
168	wrap.initVector(iv);
169	CssmClient::Key wrappedKey(wrap(mKey));
170
171	/* Unwrap the new key into the new Keychain. */
172	CssmClient::UnwrapKey unwrap(keychain->csp(), CSSM_ALGID_3DES_3KEY_EDE);
173	unwrap.key(wrappingKey);
174	unwrap.mode(CSSM_ALGMODE_ECBPad);
175	unwrap.padding(CSSM_PADDING_PKCS7);
176	unwrap.initVector(iv);
177
178	/* Setup the dldbHandle in the context. */
179	unwrap.add(CSSM_ATTRIBUTE_DL_DB_HANDLE, ssDb->handle());
180
181	/* Set up an initial aclEntry so we can change it after the unwrap. */
182	Access::Maker maker(Allocator::standard(), Access::Maker::kAnyMakerType);
183	ResourceControlContext rcc;
184	maker.initialOwner(rcc, NULL);
185	unwrap.owner(rcc.input());
186
187	/* Unwrap the key. */
188	uint32 usage = mKey->usage();
189	/* Work around csp brokeness where it sets all usage bits in the Keyheader when CSSM_KEYUSE_ANY is set. */
190	if (usage & CSSM_KEYUSE_ANY)
191		usage = CSSM_KEYUSE_ANY;
192
193	CssmClient::Key unwrappedKey(unwrap(wrappedKey, KeySpec(usage,
194		(mKey->attributes() | CSSM_KEYATTR_PERMANENT) & ~(CSSM_KEYATTR_ALWAYS_SENSITIVE | CSSM_KEYATTR_NEVER_EXTRACTABLE),
195		label)));
196
197	/* Look up unwrapped key in the DLDB. */
198	DbUniqueRecord uniqueId;
199	SSDbCursor dbCursor(ssDb, 1);
200	dbCursor->recordType(recordType());
201	dbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label);
202	CssmClient::Key copiedKey;
203	if (!dbCursor->nextKey(NULL, copiedKey, uniqueId))
204		MacOSError::throwMe(errSecItemNotFound);
205
206	/* Copy the Label, PrintName and ApplicationTag attributes from the old key to the new one. */
207	dbUniqueRecord();
208	DbAttributes oldDbAttributes(mUniqueId->database(), 3);
209	oldDbAttributes.add(kInfoKeyLabel);
210	oldDbAttributes.add(kInfoKeyPrintName);
211	oldDbAttributes.add(kInfoKeyApplicationTag);
212	mUniqueId->get(&oldDbAttributes, NULL);
213	try
214	{
215		uniqueId->modify(recordType(), &oldDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
216	}
217	catch (CssmError e)
218	{
219		// clean up after trying to insert a duplicate key
220		uniqueId->deleteRecord ();
221		throw;
222	}
223
224	/* Set the acl and owner on the unwrapped key. */
225	access->setAccess(*unwrappedKey, maker);
226
227	/* Return a keychain item which represents the new key.  */
228	Item item(keychain->item(recordType(), uniqueId));
229
230    KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, item);
231
232	return item;
233}
234
235Item
236KeyItem::importTo(const Keychain &keychain, Access *newAccess, SecKeychainAttributeList *attrList)
237{
238	if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
239		MacOSError::throwMe(errSecInvalidKeychain);
240
241	/* Get the destination keychain's db. */
242	SSDbImpl* dbImpl = dynamic_cast<SSDbImpl*>(&(*keychain->database()));
243	if (dbImpl == NULL)
244		CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
245
246	SSDb ssDb(dbImpl);
247
248	/* Make sure mKey is valid. */
249	/* We can't call key() here, since we won't have a unique record id yet */
250	if (!mKey)
251		CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
252
253	// Generate a random label to use initially
254	CssmClient::CSP appleCsp(gGuidAppleCSP);
255	CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW);
256	uint8 labelBytes[20];
257	CssmData label(labelBytes, sizeof(labelBytes));
258	random.generate(label, (uint32)label.Length);
259
260	/* Set up the ACL for the new key. */
261	SecPointer<Access> access;
262	if (newAccess)
263		access = newAccess;
264	else
265		access = new Access(*mKey);
266
267	/* Generate a random 3DES wrapping Key. */
268	CssmClient::GenerateKey genKey(csp(), CSSM_ALGID_3DES_3KEY, 192);
269	CssmClient::Key wrappingKey(genKey(KeySpec(CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP,
270		CSSM_KEYATTR_EXTRACTABLE /* | CSSM_KEYATTR_RETURN_DATA */)));
271
272	/* make a random IV */
273	uint8 ivBytes[8];
274	CssmData iv(ivBytes, sizeof(ivBytes));
275	random.generate(iv, (uint32)iv.length());
276
277	/* Extract the key by wrapping it with the wrapping key. */
278	CssmClient::WrapKey wrap(csp(), CSSM_ALGID_3DES_3KEY_EDE);
279	wrap.key(wrappingKey);
280	wrap.cred(getCredentials(CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED, kSecCredentialTypeDefault));
281	wrap.mode(CSSM_ALGMODE_ECBPad);
282	wrap.padding(CSSM_PADDING_PKCS7);
283	wrap.initVector(iv);
284	CssmClient::Key wrappedKey(wrap(mKey));
285
286	/* Unwrap the new key into the new Keychain. */
287	CssmClient::UnwrapKey unwrap(keychain->csp(), CSSM_ALGID_3DES_3KEY_EDE);
288	unwrap.key(wrappingKey);
289	unwrap.mode(CSSM_ALGMODE_ECBPad);
290	unwrap.padding(CSSM_PADDING_PKCS7);
291	unwrap.initVector(iv);
292
293	/* Setup the dldbHandle in the context. */
294	unwrap.add(CSSM_ATTRIBUTE_DL_DB_HANDLE, ssDb->handle());
295
296	/* Set up an initial aclEntry so we can change it after the unwrap. */
297	Access::Maker maker(Allocator::standard(), Access::Maker::kAnyMakerType);
298	ResourceControlContext rcc;
299	maker.initialOwner(rcc, NULL);
300	unwrap.owner(rcc.input());
301
302	/* Unwrap the key. */
303	uint32 usage = mKey->usage();
304	/* Work around csp brokeness where it sets all usage bits in the Keyheader when CSSM_KEYUSE_ANY is set. */
305	if (usage & CSSM_KEYUSE_ANY)
306		usage = CSSM_KEYUSE_ANY;
307
308	CssmClient::Key unwrappedKey(unwrap(wrappedKey, KeySpec(usage,
309		(mKey->attributes() | CSSM_KEYATTR_PERMANENT) & ~(CSSM_KEYATTR_ALWAYS_SENSITIVE | CSSM_KEYATTR_NEVER_EXTRACTABLE),
310		label)));
311
312	/* Look up unwrapped key in the DLDB. */
313	DbUniqueRecord uniqueId;
314	SSDbCursor dbCursor(ssDb, 1);
315	dbCursor->recordType(recordType());
316	dbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label);
317	CssmClient::Key copiedKey;
318	if (!dbCursor->nextKey(NULL, copiedKey, uniqueId))
319		MacOSError::throwMe(errSecItemNotFound);
320
321	// Set the initial label, application label, and application tag (if provided)
322	if (attrList) {
323		DbAttributes newDbAttributes;
324		SSDbCursor otherDbCursor(ssDb, 1);
325		otherDbCursor->recordType(recordType());
326		bool checkForDuplicates = false;
327
328		for (UInt32 index=0; index < attrList->count; index++) {
329			SecKeychainAttribute attr = attrList->attr[index];
330			CssmData attrData(attr.data, attr.length);
331			if (attr.tag == kSecKeyPrintName) {
332				newDbAttributes.add(kInfoKeyPrintName, attrData);
333			}
334			if (attr.tag == kSecKeyLabel) {
335				newDbAttributes.add(kInfoKeyLabel, attrData);
336				otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, attrData);
337				checkForDuplicates = true;
338			}
339			if (attr.tag == kSecKeyApplicationTag) {
340				newDbAttributes.add(kInfoKeyApplicationTag, attrData);
341				otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyApplicationTag, attrData);
342				checkForDuplicates = true;
343			}
344		}
345
346		DbAttributes otherDbAttributes;
347		DbUniqueRecord otherUniqueId;
348		CssmClient::Key otherKey;
349		try
350		{
351			if (checkForDuplicates && otherDbCursor->nextKey(&otherDbAttributes, otherKey, otherUniqueId))
352				MacOSError::throwMe(errSecDuplicateItem);
353
354			uniqueId->modify(recordType(), &newDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
355		}
356		catch (CssmError e)
357		{
358			// clean up after trying to insert a duplicate key
359			uniqueId->deleteRecord ();
360			throw;
361		}
362	}
363
364	/* Set the acl and owner on the unwrapped key. */
365	access->setAccess(*unwrappedKey, maker);
366
367	/* Return a keychain item which represents the new key.  */
368	Item item(keychain->item(recordType(), uniqueId));
369
370    KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, item);
371
372	return item;
373}
374
375void
376KeyItem::didModify()
377{
378}
379
380PrimaryKey
381KeyItem::add(Keychain &keychain)
382{
383	MacOSError::throwMe(errSecUnimplemented);
384}
385
386CssmClient::SSDbUniqueRecord
387KeyItem::ssDbUniqueRecord()
388{
389	DbUniqueRecordImpl *impl = &*dbUniqueRecord();
390	Security::CssmClient::SSDbUniqueRecordImpl *simpl = dynamic_cast<Security::CssmClient::SSDbUniqueRecordImpl *>(impl);
391	if (simpl == NULL)
392	{
393		CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
394	}
395
396	return CssmClient::SSDbUniqueRecord(simpl);
397}
398
399CssmClient::Key &
400KeyItem::key()
401{
402	if (!mKey)
403	{
404		CssmClient::SSDbUniqueRecord uniqueId(ssDbUniqueRecord());
405		CssmDataContainer dataBlob(uniqueId->allocator());
406		uniqueId->get(NULL, &dataBlob);
407		mKey = CssmClient::Key(uniqueId->database()->csp(), *reinterpret_cast<CssmKey *>(dataBlob.Data));
408	}
409
410	return mKey;
411}
412
413CssmClient::CSP
414KeyItem::csp()
415{
416	return key()->csp();
417}
418
419
420const CSSM_X509_ALGORITHM_IDENTIFIER&
421KeyItem::algorithmIdentifier()
422{
423#if 0
424	CssmKey *mKey;
425	CSSM_KEY_TYPE algorithm
426		CSSM_KEY_PTR cssmKey =  (CSSM_KEY_PTR)thisData->Data;
427cssmKey->KeyHeader
428	static void printKeyHeader(
429	const CSSM_KEYHEADER &hdr)
430{
431	printf("   Algorithm       : ");
432	switch(hdr.AlgorithmId) {
433CSSM_X509_ALGORITHM_IDENTIFIER algID;
434
435CSSM_OID *CL_algToOid(
436	CSSM_ALGORITHMS algId)
437typedef struct cssm_x509_algorithm_identifier {
438    CSSM_OID algorithm;
439    CSSM_DATA parameters;
440} CSSM_X509_ALGORITHM_IDENTIFIER, *CSSM_X509_ALGORITHM_IDENTIFIER_PTR;
441#endif
442
443	abort();
444}
445
446/*
447 * itemID, used to locate Extended Attributes, is the public key hash for keys.
448 */
449const CssmData &KeyItem::itemID()
450{
451	if(mPubKeyHash.length() == 0) {
452		/*
453		 * Fetch the attribute from disk.
454		 */
455		UInt32 tag = kSecKeyLabel;
456		UInt32 format = 0;
457		SecKeychainAttributeInfo attrInfo = {1, &tag, &format};
458		SecKeychainAttributeList *attrList = NULL;
459		getAttributesAndData(&attrInfo, NULL, &attrList, NULL, NULL);
460		if((attrList == NULL) || (attrList->count != 1)) {
461			MacOSError::throwMe(errSecNoSuchAttr);
462		}
463		mPubKeyHash.copy(attrList->attr->data, attrList->attr->length);
464		freeAttributesAndData(attrList, NULL);
465	}
466	return mPubKeyHash;
467}
468
469
470unsigned int
471KeyItem::strengthInBits(const CSSM_X509_ALGORITHM_IDENTIFIER *algid)
472{
473	// @@@ Make a context with key based on algid and use that to get the effective keysize and not just the logical one.
474	CSSM_KEY_SIZE keySize = {};
475	CSSM_RETURN rv = CSSM_QueryKeySizeInBits (csp()->handle(),
476                         CSSM_INVALID_HANDLE,
477                         key(),
478                         &keySize);
479	if (rv)
480		return 0;
481
482	return keySize.LogicalKeySizeInBits;
483}
484
485const AccessCredentials *
486KeyItem::getCredentials(
487	CSSM_ACL_AUTHORIZATION_TAG operation,
488	SecCredentialType credentialType)
489{
490	// @@@ Fix this to actually examine the ACL for this key and consider operation and do the right thing.
491	//AutoAclEntryInfoList aclInfos;
492	//key()->getAcl(aclInfos);
493
494	bool smartcard = keychain() != NULL ? (keychain()->database()->dl()->guid() == gGuidAppleSdCSPDL) : false;
495
496	AclFactory factory;
497	switch (credentialType)
498	{
499	case kSecCredentialTypeDefault:
500		return smartcard?globals().smartcardItemCredentials():globals().itemCredentials();
501	case kSecCredentialTypeWithUI:
502		return smartcard?globals().smartcardItemCredentials():factory.promptCred();
503	case kSecCredentialTypeNoUI:
504		return factory.nullCred();
505	default:
506		MacOSError::throwMe(errSecParam);
507	}
508}
509
510bool
511KeyItem::operator == (KeyItem &other)
512{
513	if (mKey && *mKey)
514	{
515		// Pointer compare
516		return this == &other;
517	}
518
519	// If keychains are different, then keys are different
520	Keychain otherKeychain = other.keychain();
521	return (mKeychain && otherKeychain && (*mKeychain == *otherKeychain));
522}
523
524void
525KeyItem::createPair(
526	Keychain keychain,
527	CSSM_ALGORITHMS algorithm,
528	uint32 keySizeInBits,
529	CSSM_CC_HANDLE contextHandle,
530	CSSM_KEYUSE publicKeyUsage,
531	uint32 publicKeyAttr,
532	CSSM_KEYUSE privateKeyUsage,
533	uint32 privateKeyAttr,
534	SecPointer<Access> initialAccess,
535	SecPointer<KeyItem> &outPublicKey,
536	SecPointer<KeyItem> &outPrivateKey)
537{
538	bool freeKeys = false;
539	bool deleteContext = false;
540
541	if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
542		MacOSError::throwMe(errSecInvalidKeychain);
543
544	SSDbImpl* impl = dynamic_cast<SSDbImpl*>(&(*keychain->database()));
545	if (impl == NULL)
546	{
547		CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
548	}
549
550	SSDb ssDb(impl);
551	CssmClient::CSP csp(keychain->csp());
552	CssmClient::CSP appleCsp(gGuidAppleCSP);
553
554	// Generate a random label to use initially
555	CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW);
556	uint8 labelBytes[20];
557	CssmData label(labelBytes, sizeof(labelBytes));
558	random.generate(label, (uint32)label.Length);
559
560	// Create a Access::Maker for the initial owner of the private key.
561	ResourceControlContext rcc;
562	memset(&rcc, 0, sizeof(rcc));
563	Access::Maker maker;
564	// @@@ Potentially provide a credential argument which allows us to generate keys in the csp.  Currently the CSP let's anyone do this, but we might restrict this in the future, f.e. a smartcard could require out of band pin entry before a key can be generated.
565	maker.initialOwner(rcc);
566	// Create the cred we need to manipulate the keys until we actually set a new access control for them.
567	const AccessCredentials *cred = maker.cred();
568
569	CSSM_KEY publicCssmKey, privateCssmKey;
570	memset(&publicCssmKey, 0, sizeof(publicCssmKey));
571	memset(&privateCssmKey, 0, sizeof(privateCssmKey));
572
573	CSSM_CC_HANDLE ccHandle = 0;
574
575	Item publicKeyItem, privateKeyItem;
576	try
577	{
578		CSSM_RETURN status;
579		if (contextHandle)
580				ccHandle = contextHandle;
581		else
582		{
583			status = CSSM_CSP_CreateKeyGenContext(csp->handle(), algorithm, keySizeInBits, NULL, NULL, NULL, NULL, NULL, &ccHandle);
584			if (status)
585				CssmError::throwMe(status);
586			deleteContext = true;
587		}
588
589		CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle();
590		CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle;
591		CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } };
592		status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes);
593		if (status)
594			CssmError::throwMe(status);
595
596		// Generate the keypair
597		status = CSSM_GenerateKeyPair(ccHandle, publicKeyUsage, publicKeyAttr, &label, &publicCssmKey, privateKeyUsage, privateKeyAttr, &label, &rcc, &privateCssmKey);
598		if (status)
599			CssmError::throwMe(status);
600		freeKeys = true;
601
602		// Find the keys we just generated in the DL to get SecKeyRef's to them
603		// so we can change the label to be the hash of the public key, and
604		// fix up other attributes.
605
606		// Look up public key in the DLDB.
607		DbAttributes pubDbAttributes;
608		DbUniqueRecord pubUniqueId;
609		SSDbCursor dbPubCursor(ssDb, 1);
610		dbPubCursor->recordType(CSSM_DL_DB_RECORD_PUBLIC_KEY);
611		dbPubCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label);
612		CssmClient::Key publicKey;
613		if (!dbPubCursor->nextKey(&pubDbAttributes, publicKey, pubUniqueId))
614			MacOSError::throwMe(errSecItemNotFound);
615
616		// Look up private key in the DLDB.
617		DbAttributes privDbAttributes;
618		DbUniqueRecord privUniqueId;
619		SSDbCursor dbPrivCursor(ssDb, 1);
620		dbPrivCursor->recordType(CSSM_DL_DB_RECORD_PRIVATE_KEY);
621		dbPrivCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label);
622		CssmClient::Key privateKey;
623		if (!dbPrivCursor->nextKey(&privDbAttributes, privateKey, privUniqueId))
624			MacOSError::throwMe(errSecItemNotFound);
625
626		// Convert reference public key to a raw key so we can use it
627		// in the appleCsp.
628		CssmClient::WrapKey wrap(csp, CSSM_ALGID_NONE);
629		wrap.cred(cred);
630		CssmClient::Key rawPubKey = wrap(publicKey);
631
632		// Calculate the hash of the public key using the appleCSP.
633		CssmClient::PassThrough passThrough(appleCsp);
634		void *outData;
635		CssmData *cssmData;
636
637		/* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the
638		* associated key blob.
639		* Key is specified in CSSM_CSP_CreatePassThroughContext.
640		* Hash is allocated bythe CSP, in the App's memory, and returned
641		* in *outData. */
642		passThrough.key(rawPubKey);
643		passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData);
644		cssmData = reinterpret_cast<CssmData *>(outData);
645		CssmData &pubKeyHash = *cssmData;
646
647		auto_ptr<string>privDescription;
648		auto_ptr<string>pubDescription;
649		try {
650			privDescription.reset(new string(initialAccess->promptDescription()));
651			pubDescription.reset(new string(initialAccess->promptDescription()));
652		}
653		catch(...) {
654			/* this path taken if no promptDescription available, e.g., for complex ACLs */
655			privDescription.reset(new string("Private key"));
656			pubDescription.reset(new string("Public key"));
657		}
658
659		// Set the label of the public key to the public key hash.
660		// Set the PrintName of the public key to the description in the acl.
661		pubDbAttributes.add(kInfoKeyLabel, pubKeyHash);
662		pubDbAttributes.add(kInfoKeyPrintName, *pubDescription);
663		pubUniqueId->modify(CSSM_DL_DB_RECORD_PUBLIC_KEY, &pubDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
664
665		// Set the label of the private key to the public key hash.
666		// Set the PrintName of the private key to the description in the acl.
667		privDbAttributes.add(kInfoKeyLabel, pubKeyHash);
668		privDbAttributes.add(kInfoKeyPrintName, *privDescription);
669		privUniqueId->modify(CSSM_DL_DB_RECORD_PRIVATE_KEY, &privDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
670
671		// @@@ Not exception safe!
672		csp.allocator().free(cssmData->Data);
673		csp.allocator().free(cssmData);
674
675		// Finally fix the acl and owner of the private key to the specified access control settings.
676		initialAccess->setAccess(*privateKey, maker);
677
678		if(publicKeyAttr & CSSM_KEYATTR_PUBLIC_KEY_ENCRYPT) {
679			/*
680			 * Make the public key acl completely open.
681			 * If the key was not encrypted, it already has a wide-open
682			 * ACL (though that is a feature of securityd; it's not
683			 * CDSA-specified behavior).
684			 */
685			SecPointer<Access> pubKeyAccess(new Access());
686			pubKeyAccess->setAccess(*publicKey, maker);
687		}
688
689		// Create keychain items which will represent the keys.
690		publicKeyItem = keychain->item(CSSM_DL_DB_RECORD_PUBLIC_KEY, pubUniqueId);
691		privateKeyItem = keychain->item(CSSM_DL_DB_RECORD_PRIVATE_KEY, privUniqueId);
692
693		KeyItem* impl = dynamic_cast<KeyItem*>(&(*publicKeyItem));
694		if (impl == NULL)
695		{
696			CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
697		}
698
699		outPublicKey = impl;
700
701		impl = dynamic_cast<KeyItem*>(&(*privateKeyItem));
702		if (impl == NULL)
703		{
704			CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
705		}
706
707		outPrivateKey = impl;
708	}
709	catch (...)
710	{
711		if (freeKeys)
712		{
713			// Delete the keys if something goes wrong so we don't end up with inaccessible keys in the database.
714			CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, TRUE);
715			CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, TRUE);
716		}
717
718		if (deleteContext)
719			CSSM_DeleteContext(ccHandle);
720
721		throw;
722	}
723
724	if (freeKeys)
725	{
726		CSSM_FreeKey(csp->handle(), NULL, &publicCssmKey, FALSE);
727		CSSM_FreeKey(csp->handle(), NULL, &privateCssmKey, FALSE);
728	}
729
730	if (deleteContext)
731		CSSM_DeleteContext(ccHandle);
732
733	if (keychain && publicKeyItem && privateKeyItem)
734	{
735		keychain->postEvent(kSecAddEvent, publicKeyItem);
736		keychain->postEvent(kSecAddEvent, privateKeyItem);
737	}
738}
739
740void
741KeyItem::importPair(
742	Keychain keychain,
743	const CSSM_KEY &publicWrappedKey,
744	const CSSM_KEY &privateWrappedKey,
745	SecPointer<Access> initialAccess,
746	SecPointer<KeyItem> &outPublicKey,
747	SecPointer<KeyItem> &outPrivateKey)
748{
749	bool freePublicKey = false;
750	bool freePrivateKey = false;
751	bool deleteContext = false;
752
753	if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
754		MacOSError::throwMe(errSecInvalidKeychain);
755
756	SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*keychain->database()));
757	if (impl == NULL)
758	{
759		CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
760	}
761
762	SSDb ssDb(impl);
763	CssmClient::CSP csp(keychain->csp());
764	CssmClient::CSP appleCsp(gGuidAppleCSP);
765
766	// Create a Access::Maker for the initial owner of the private key.
767	ResourceControlContext rcc;
768	memset(&rcc, 0, sizeof(rcc));
769	Access::Maker maker(Allocator::standard(), Access::Maker::kAnyMakerType);
770	// @@@ Potentially provide a credential argument which allows us to unwrap keys in the csp.
771	// Currently the CSP lets anyone do this, but we might restrict this in the future, e.g.
772	// a smartcard could require out of band pin entry before a key can be generated.
773	maker.initialOwner(rcc);
774	// Create the cred we need to manipulate the keys until we actually set a new access control for them.
775	const AccessCredentials *cred = maker.cred();
776
777	CSSM_KEY publicCssmKey, privateCssmKey;
778	memset(&publicCssmKey, 0, sizeof(publicCssmKey));
779	memset(&privateCssmKey, 0, sizeof(privateCssmKey));
780
781	CSSM_CC_HANDLE ccHandle = 0;
782
783	Item publicKeyItem, privateKeyItem;
784	try
785	{
786		CSSM_RETURN status;
787
788		// Calculate the hash of the public key using the appleCSP.
789		CssmClient::PassThrough passThrough(appleCsp);
790		void *outData;
791		CssmData *cssmData;
792
793		/* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the
794		* associated key blob.
795		* Key is specified in CSSM_CSP_CreatePassThroughContext.
796		* Hash is allocated bythe CSP, in the App's memory, and returned
797		* in *outData. */
798		passThrough.key(&publicWrappedKey);
799		passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData);
800		cssmData = reinterpret_cast<CssmData *>(outData);
801		CssmData &pubKeyHash = *cssmData;
802
803		status = CSSM_CSP_CreateSymmetricContext(csp->handle(), publicWrappedKey.KeyHeader.WrapAlgorithmId, CSSM_ALGMODE_NONE, NULL, NULL, NULL, CSSM_PADDING_NONE, NULL, &ccHandle);
804		if (status)
805			CssmError::throwMe(status);
806		deleteContext = true;
807
808		CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle();
809		CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle;
810		CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } };
811		status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes);
812		if (status)
813			CssmError::throwMe(status);
814
815		// Unwrap the the keys
816		CSSM_DATA descriptiveData = {0, NULL};
817
818		status = CSSM_UnwrapKey(
819			ccHandle,
820			NULL,
821			&publicWrappedKey,
822			publicWrappedKey.KeyHeader.KeyUsage,
823			publicWrappedKey.KeyHeader.KeyAttr | CSSM_KEYATTR_PERMANENT,
824			&pubKeyHash,
825			&rcc,
826			&publicCssmKey,
827			&descriptiveData);
828
829		if (status)
830			CssmError::throwMe(status);
831		freePublicKey = true;
832
833		if (descriptiveData.Data != NULL)
834			free (descriptiveData.Data);
835
836		status = CSSM_UnwrapKey(
837			ccHandle,
838			NULL,
839			&privateWrappedKey,
840			privateWrappedKey.KeyHeader.KeyUsage,
841			privateWrappedKey.KeyHeader.KeyAttr | CSSM_KEYATTR_PERMANENT,
842			&pubKeyHash,
843			&rcc,
844			&privateCssmKey,
845			&descriptiveData);
846
847		if (status)
848			CssmError::throwMe(status);
849
850		if (descriptiveData.Data != NULL)
851			free (descriptiveData.Data);
852
853		freePrivateKey = true;
854
855		// Find the keys we just generated in the DL to get SecKeyRefs to them
856		// so we can change the label to be the hash of the public key, and
857		// fix up other attributes.
858
859		// Look up public key in the DLDB.
860		DbAttributes pubDbAttributes;
861		DbUniqueRecord pubUniqueId;
862		SSDbCursor dbPubCursor(ssDb, 1);
863		dbPubCursor->recordType(CSSM_DL_DB_RECORD_PUBLIC_KEY);
864		dbPubCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, pubKeyHash);
865		CssmClient::Key publicKey;
866		if (!dbPubCursor->nextKey(&pubDbAttributes, publicKey, pubUniqueId))
867			MacOSError::throwMe(errSecItemNotFound);
868
869		// Look up private key in the DLDB.
870		DbAttributes privDbAttributes;
871		DbUniqueRecord privUniqueId;
872		SSDbCursor dbPrivCursor(ssDb, 1);
873		dbPrivCursor->recordType(CSSM_DL_DB_RECORD_PRIVATE_KEY);
874		dbPrivCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, pubKeyHash);
875		CssmClient::Key privateKey;
876		if (!dbPrivCursor->nextKey(&privDbAttributes, privateKey, privUniqueId))
877			MacOSError::throwMe(errSecItemNotFound);
878
879		// @@@ Not exception safe!
880		csp.allocator().free(cssmData->Data);
881		csp.allocator().free(cssmData);
882
883		auto_ptr<string>privDescription;
884		auto_ptr<string>pubDescription;
885		try {
886			privDescription.reset(new string(initialAccess->promptDescription()));
887			pubDescription.reset(new string(initialAccess->promptDescription()));
888		}
889		catch(...) {
890			/* this path taken if no promptDescription available, e.g., for complex ACLs */
891			privDescription.reset(new string("Private key"));
892			pubDescription.reset(new string("Public key"));
893		}
894
895		// Set the label of the public key to the public key hash.
896		// Set the PrintName of the public key to the description in the acl.
897		pubDbAttributes.add(kInfoKeyPrintName, *pubDescription);
898		pubUniqueId->modify(CSSM_DL_DB_RECORD_PUBLIC_KEY, &pubDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
899
900		// Set the label of the private key to the public key hash.
901		// Set the PrintName of the private key to the description in the acl.
902		privDbAttributes.add(kInfoKeyPrintName, *privDescription);
903		privUniqueId->modify(CSSM_DL_DB_RECORD_PRIVATE_KEY, &privDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
904
905		// Finally fix the acl and owner of the private key to the specified access control settings.
906		initialAccess->setAccess(*privateKey, maker);
907
908		// Make the public key acl completely open
909		SecPointer<Access> pubKeyAccess(new Access());
910		pubKeyAccess->setAccess(*publicKey, maker);
911
912		// Create keychain items which will represent the keys.
913		publicKeyItem = keychain->item(CSSM_DL_DB_RECORD_PUBLIC_KEY, pubUniqueId);
914		privateKeyItem = keychain->item(CSSM_DL_DB_RECORD_PRIVATE_KEY, privUniqueId);
915
916		KeyItem* impl = dynamic_cast<KeyItem*>(&(*publicKeyItem));
917		if (impl == NULL)
918		{
919			CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
920		}
921
922		outPublicKey = impl;
923
924		impl = dynamic_cast<KeyItem*>(&(*privateKeyItem));
925		if (impl == NULL)
926		{
927			CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
928		}
929		outPrivateKey = impl;
930	}
931	catch (...)
932	{
933		if (freePublicKey)
934			CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, TRUE);
935		if (freePrivateKey)
936			CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, TRUE);
937
938		if (deleteContext)
939			CSSM_DeleteContext(ccHandle);
940
941		throw;
942	}
943
944	if (freePublicKey)
945		CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, FALSE);
946	if (freePrivateKey)
947		CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, FALSE);
948
949	if (deleteContext)
950		CSSM_DeleteContext(ccHandle);
951
952	if (keychain && publicKeyItem && privateKeyItem)
953	{
954		KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, publicKeyItem);
955		KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, privateKeyItem);
956	}
957}
958
959SecPointer<KeyItem>
960KeyItem::generateWithAttributes(const SecKeychainAttributeList *attrList,
961	Keychain keychain,
962	CSSM_ALGORITHMS algorithm,
963	uint32 keySizeInBits,
964	CSSM_CC_HANDLE contextHandle,
965	CSSM_KEYUSE keyUsage,
966	uint32 keyAttr,
967	SecPointer<Access> initialAccess)
968{
969	CssmClient::CSP appleCsp(gGuidAppleCSP);
970	CssmClient::CSP csp(NULL);
971	SSDb ssDb(NULL);
972	uint8 labelBytes[20];
973	CssmData label(labelBytes, sizeof(labelBytes));
974	bool freeKey = false;
975	bool deleteContext = false;
976	const CSSM_DATA *plabel = NULL;
977
978	if (keychain)
979	{
980		if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
981			MacOSError::throwMe(errSecInvalidKeychain);
982
983		SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*keychain->database()));
984		if (impl == NULL)
985		{
986			CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
987		}
988
989		ssDb = SSDb(impl);
990		csp = keychain->csp();
991
992		// Generate a random label to use initially
993		CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW);
994		random.generate(label, (uint32)label.Length);
995		plabel = &label;
996	}
997	else
998	{
999		// Not a persistent key so create it in the regular csp
1000		csp = appleCsp;
1001	}
1002
1003	// Create a Access::Maker for the initial owner of the private key.
1004	ResourceControlContext *prcc = NULL, rcc;
1005	const AccessCredentials *cred = NULL;
1006	Access::Maker maker;
1007	if (keychain && initialAccess)
1008	{
1009		memset(&rcc, 0, sizeof(rcc));
1010		// @@@ Potentially provide a credential argument which allows us to generate keys in the csp.
1011		// Currently the CSP lets anyone do this, but we might restrict this in the future, e.g. a smartcard
1012		// could require out-of-band pin entry before a key can be generated.
1013		maker.initialOwner(rcc);
1014		// Create the cred we need to manipulate the keys until we actually set a new access control for them.
1015		cred = maker.cred();
1016		prcc = &rcc;
1017	}
1018
1019	CSSM_KEY cssmKey;
1020
1021	CSSM_CC_HANDLE ccHandle = 0;
1022
1023	Item keyItem;
1024	try
1025	{
1026		CSSM_RETURN status;
1027		if (contextHandle)
1028			ccHandle = contextHandle;
1029		else
1030		{
1031			status = CSSM_CSP_CreateKeyGenContext(csp->handle(), algorithm, keySizeInBits, NULL, NULL, NULL, NULL, NULL, &ccHandle);
1032			if (status)
1033				CssmError::throwMe(status);
1034			deleteContext = true;
1035		}
1036
1037		if (ssDb)
1038		{
1039			CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle();
1040			CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle;
1041			CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } };
1042			status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes);
1043			if (status)
1044				CssmError::throwMe(status);
1045
1046			keyAttr |= CSSM_KEYATTR_PERMANENT;
1047		}
1048
1049		// Generate the key
1050		status = CSSM_GenerateKey(ccHandle, keyUsage, keyAttr, plabel, prcc, &cssmKey);
1051		if (status)
1052			CssmError::throwMe(status);
1053
1054		if (ssDb)
1055		{
1056			freeKey = true;
1057			// Find the key we just generated in the DL and get a SecKeyRef
1058			// so we can specify the label attribute(s) and initial ACL.
1059
1060			// Look up key in the DLDB.
1061			DbAttributes dbAttributes;
1062			DbUniqueRecord uniqueId;
1063			SSDbCursor dbCursor(ssDb, 1);
1064			dbCursor->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY);
1065			dbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label);
1066			CssmClient::Key key;
1067			if (!dbCursor->nextKey(&dbAttributes, key, uniqueId))
1068				MacOSError::throwMe(errSecItemNotFound);
1069
1070			// Set the initial label, application label, and application tag (if provided)
1071			if (attrList) {
1072				DbAttributes newDbAttributes;
1073				SSDbCursor otherDbCursor(ssDb, 1);
1074				otherDbCursor->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY);
1075				bool checkForDuplicates = false;
1076
1077				for (UInt32 index=0; index < attrList->count; index++) {
1078					SecKeychainAttribute attr = attrList->attr[index];
1079					CssmData attrData(attr.data, attr.length);
1080					if (attr.tag == kSecKeyPrintName) {
1081						newDbAttributes.add(kInfoKeyPrintName, attrData);
1082					}
1083					if (attr.tag == kSecKeyLabel) {
1084						newDbAttributes.add(kInfoKeyLabel, attrData);
1085						otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, attrData);
1086						checkForDuplicates = true;
1087					}
1088					if (attr.tag == kSecKeyApplicationTag) {
1089						newDbAttributes.add(kInfoKeyApplicationTag, attrData);
1090						otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyApplicationTag, attrData);
1091						checkForDuplicates = true;
1092					}
1093				}
1094
1095				DbAttributes otherDbAttributes;
1096				DbUniqueRecord otherUniqueId;
1097				CssmClient::Key otherKey;
1098				if (checkForDuplicates && otherDbCursor->nextKey(&otherDbAttributes, otherKey, otherUniqueId))
1099					MacOSError::throwMe(errSecDuplicateItem);
1100
1101				uniqueId->modify(CSSM_DL_DB_RECORD_SYMMETRIC_KEY, &newDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
1102			}
1103
1104			// Finally, fix the acl and owner of the key to the specified access control settings.
1105			if (initialAccess)
1106				initialAccess->setAccess(*key, maker);
1107
1108			// Create keychain item which will represent the key.
1109			keyItem = keychain->item(CSSM_DL_DB_RECORD_SYMMETRIC_KEY, uniqueId);
1110		}
1111		else
1112		{
1113			CssmClient::Key tempKey(csp, cssmKey);
1114			keyItem = new KeyItem(tempKey);
1115		}
1116	}
1117	catch (...)
1118	{
1119		if (freeKey)
1120		{
1121			// Delete the key if something goes wrong so we don't end up with inaccessible keys in the database.
1122			CSSM_FreeKey(csp->handle(), cred, &cssmKey, TRUE);
1123		}
1124
1125		if (deleteContext)
1126			CSSM_DeleteContext(ccHandle);
1127
1128		throw;
1129	}
1130
1131	if (freeKey)
1132	{
1133		CSSM_FreeKey(csp->handle(), NULL, &cssmKey, FALSE);
1134	}
1135
1136	if (deleteContext)
1137		CSSM_DeleteContext(ccHandle);
1138
1139	if (keychain && keyItem)
1140		keychain->postEvent(kSecAddEvent, keyItem);
1141
1142	KeyItem* item = dynamic_cast<KeyItem*>(&*keyItem);
1143	if (item == NULL)
1144	{
1145		CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
1146	}
1147
1148	return item;
1149}
1150
1151SecPointer<KeyItem>
1152KeyItem::generate(Keychain keychain,
1153	CSSM_ALGORITHMS algorithm,
1154	uint32 keySizeInBits,
1155	CSSM_CC_HANDLE contextHandle,
1156	CSSM_KEYUSE keyUsage,
1157	uint32 keyAttr,
1158	SecPointer<Access> initialAccess)
1159{
1160	return KeyItem::generateWithAttributes(NULL, keychain,
1161		algorithm, keySizeInBits, contextHandle,
1162		keyUsage, keyAttr, initialAccess);
1163}
1164
1165
1166void KeyItem::RawSign(SecPadding padding, CSSM_DATA dataToSign, const AccessCredentials *credentials, CSSM_DATA& signature)
1167{
1168	CSSM_ALGORITHMS baseAlg = key()->header().algorithm();
1169
1170    if ((baseAlg != CSSM_ALGID_RSA) && (baseAlg != CSSM_ALGID_ECDSA))
1171	{
1172		MacOSError::throwMe(errSecParam);
1173	}
1174
1175	CSSM_ALGORITHMS paddingAlg = CSSM_PADDING_PKCS1;
1176
1177	switch (padding)
1178	{
1179		case kSecPaddingPKCS1:
1180		{
1181			paddingAlg = CSSM_PADDING_PKCS1;
1182			break;
1183		}
1184
1185		case kSecPaddingPKCS1MD2:
1186		{
1187			baseAlg = CSSM_ALGID_MD2WithRSA;
1188			break;
1189		}
1190
1191		case kSecPaddingPKCS1MD5:
1192		{
1193			baseAlg = CSSM_ALGID_MD5WithRSA;
1194			break;
1195		}
1196
1197		case kSecPaddingPKCS1SHA1:
1198		{
1199			baseAlg = CSSM_ALGID_SHA1WithRSA;
1200			break;
1201		}
1202
1203		default:
1204		{
1205			paddingAlg = CSSM_PADDING_NONE;
1206			break;
1207		}
1208	}
1209
1210	Sign signContext(csp(), baseAlg);
1211	signContext.key(key());
1212	signContext.set(CSSM_ATTRIBUTE_PADDING, paddingAlg);
1213	signContext.cred(credentials);
1214
1215	CssmData data(dataToSign.Data, dataToSign.Length);
1216	signContext.sign(data);
1217
1218    CssmData sig(signature.Data, signature.Length);
1219	signContext(sig); // yes, this is an accessor.  Believe it, or not.
1220    signature.Length = sig.length();
1221}
1222
1223
1224
1225void KeyItem::RawVerify(SecPadding padding, CSSM_DATA dataToVerify, const AccessCredentials *credentials, CSSM_DATA sig)
1226{
1227	CSSM_ALGORITHMS baseAlg = key()->header().algorithm();
1228    if ((baseAlg != CSSM_ALGID_RSA) && (baseAlg != CSSM_ALGID_ECDSA))
1229	{
1230		MacOSError::throwMe(errSecParam);
1231	}
1232
1233	CSSM_ALGORITHMS paddingAlg = CSSM_PADDING_PKCS1;
1234
1235	switch (padding)
1236	{
1237		case kSecPaddingPKCS1:
1238		{
1239			paddingAlg = CSSM_PADDING_PKCS1;
1240			break;
1241		}
1242
1243		case kSecPaddingPKCS1MD2:
1244		{
1245			baseAlg = CSSM_ALGID_MD2WithRSA;
1246			break;
1247		}
1248
1249		case kSecPaddingPKCS1MD5:
1250		{
1251			baseAlg = CSSM_ALGID_MD5WithRSA;
1252			break;
1253		}
1254
1255		case kSecPaddingPKCS1SHA1:
1256		{
1257			baseAlg = CSSM_ALGID_SHA1WithRSA;
1258			break;
1259		}
1260
1261		default:
1262		{
1263			paddingAlg = CSSM_PADDING_NONE;
1264			break;
1265		}
1266	}
1267
1268	Verify verifyContext(csp(), baseAlg);
1269	verifyContext.key(key());
1270	verifyContext.set(CSSM_ATTRIBUTE_PADDING, paddingAlg);
1271	verifyContext.cred(credentials);
1272
1273	CssmData data(dataToVerify.Data, dataToVerify.Length);
1274	CssmData signature(sig.Data, sig.Length);
1275	verifyContext.verify(data, signature);
1276}
1277
1278
1279
1280void KeyItem::Encrypt(SecPadding padding, CSSM_DATA dataToEncrypt, const AccessCredentials *credentials, CSSM_DATA& encryptedData)
1281{
1282	CSSM_ALGORITHMS baseAlg = key()->header().algorithm();
1283	if (baseAlg != CSSM_ALGID_RSA)
1284	{
1285		MacOSError::throwMe(errSecParam);
1286	}
1287
1288	CSSM_ALGORITHMS paddingAlg = CSSM_PADDING_PKCS1;
1289
1290	switch (padding)
1291	{
1292		case kSecPaddingPKCS1:
1293		{
1294			paddingAlg = CSSM_PADDING_PKCS1;
1295			break;
1296		}
1297
1298		default:
1299		{
1300			paddingAlg = CSSM_PADDING_NONE;
1301			break;
1302		}
1303	}
1304
1305	CssmClient::Encrypt encryptContext(csp(), baseAlg);
1306	encryptContext.key(key());
1307	encryptContext.padding(paddingAlg);
1308	encryptContext.cred(credentials);
1309
1310	CssmData inData(dataToEncrypt.Data, dataToEncrypt.Length);
1311	CssmData outData(encryptedData.Data, encryptedData.Length);
1312	CssmData remData((void*) NULL, 0);
1313
1314	encryptedData.Length = encryptContext.encrypt(inData, outData, remData);
1315}
1316
1317
1318
1319void KeyItem::Decrypt(SecPadding padding, CSSM_DATA dataToDecrypt, const AccessCredentials *credentials, CSSM_DATA& decryptedData)
1320{
1321	CSSM_ALGORITHMS baseAlg = key()->header().algorithm();
1322	if (baseAlg != CSSM_ALGID_RSA)
1323	{
1324		MacOSError::throwMe(errSecParam);
1325	}
1326
1327	CSSM_ALGORITHMS paddingAlg = CSSM_PADDING_PKCS1;
1328
1329	switch (padding)
1330	{
1331		case kSecPaddingPKCS1:
1332		{
1333			paddingAlg = CSSM_PADDING_PKCS1;
1334			break;
1335		}
1336
1337
1338		default:
1339		{
1340			paddingAlg = CSSM_PADDING_NONE;
1341			break;
1342		}
1343	}
1344
1345	CssmClient::Decrypt decryptContext(csp(), baseAlg);
1346	decryptContext.key(key());
1347	decryptContext.padding(paddingAlg);
1348	decryptContext.cred(credentials);
1349
1350	CssmData inData(dataToDecrypt.Data, dataToDecrypt.Length);
1351	CssmData outData(decryptedData.Data, decryptedData.Length);
1352	CssmData remData((void*) NULL, 0);
1353	decryptedData.Length = decryptContext.decrypt(inData, outData, remData);
1354    if (remData.Data != NULL)
1355    {
1356        free(remData.Data);
1357    }
1358}
1359
1360CFHashCode KeyItem::hash()
1361{
1362	CFHashCode result = 0;
1363	const CSSM_KEY *cssmKey = key();
1364	if (NULL != cssmKey)
1365	{
1366		unsigned char digest[CC_SHA256_DIGEST_LENGTH];
1367
1368		CFIndex size_of_data = sizeof(CSSM_KEYHEADER) +  cssmKey->KeyData.Length;
1369
1370		CFMutableDataRef temp_cfdata = CFDataCreateMutable(kCFAllocatorDefault, size_of_data);
1371		if (NULL == temp_cfdata)
1372		{
1373			return result;
1374		}
1375
1376		CFDataAppendBytes(temp_cfdata, (const UInt8 *)cssmKey, sizeof(CSSM_KEYHEADER));
1377		CFDataAppendBytes(temp_cfdata, cssmKey->KeyData.Data, cssmKey->KeyData.Length);
1378
1379		if (size_of_data < 80)
1380		{
1381			// If it is less than 80 bytes then CFData can be used
1382			result = CFHash(temp_cfdata);
1383			CFRelease(temp_cfdata);
1384		}
1385		// CFData truncates its hash value to 80 bytes. ????
1386		// In order to do the 'right thing' a SHA 256 hash will be used to
1387		// include all of the data
1388		else
1389		{
1390			memset(digest, 0, CC_SHA256_DIGEST_LENGTH);
1391
1392			CC_SHA256((const void *)CFDataGetBytePtr(temp_cfdata), (CC_LONG)CFDataGetLength(temp_cfdata), digest);
1393
1394			CFDataRef data_to_hash = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
1395				(const UInt8 *)digest, CC_SHA256_DIGEST_LENGTH, kCFAllocatorNull);
1396			result = CFHash(data_to_hash);
1397			CFRelease(data_to_hash);
1398			CFRelease(temp_cfdata);
1399		}
1400	}
1401	return result;
1402}
1403
1404