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