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