]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/KeyItem.cpp
Security-57740.60.18.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 unwrap.key(wrappingKey);
355 unwrap.mode(CSSM_ALGMODE_ECBPad);
356 unwrap.padding(CSSM_PADDING_PKCS7);
357 unwrap.initVector(iv);
358
359 /* Setup the dldbHandle in the context. */
360 unwrap.add(CSSM_ATTRIBUTE_DL_DB_HANDLE, ssDb->handle());
361
362 /* Set up an initial aclEntry so we can change it after the unwrap. */
363 Access::Maker maker(Allocator::standard(), Access::Maker::kAnyMakerType);
364 ResourceControlContext rcc;
365 maker.initialOwner(rcc, NULL);
366 unwrap.owner(rcc.input());
367
368 /* Unwrap the key. */
369 uint32 usage = mKey->usage();
370 /* Work around csp brokeness where it sets all usage bits in the Keyheader when CSSM_KEYUSE_ANY is set. */
371 if (usage & CSSM_KEYUSE_ANY)
372 usage = CSSM_KEYUSE_ANY;
373
374 CssmClient::Key unwrappedKey(unwrap(wrappedKey, KeySpec(usage,
375 (mKey->attributes() | CSSM_KEYATTR_PERMANENT) & ~(CSSM_KEYATTR_ALWAYS_SENSITIVE | CSSM_KEYATTR_NEVER_EXTRACTABLE),
376 label)));
377
378 /* Look up unwrapped key in the DLDB. */
379 DbUniqueRecord uniqueId;
380 SSDbCursor dbCursor(ssDb, 1);
381 dbCursor->recordType(recordType());
382 dbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label);
383 CssmClient::Key copiedKey;
384 if (!dbCursor->nextKey(NULL, copiedKey, uniqueId))
385 MacOSError::throwMe(errSecItemNotFound);
386
387 // Set the initial label, application label, and application tag (if provided)
388 if (attrList) {
389 DbAttributes newDbAttributes;
390
391 for (UInt32 index=0; index < attrList->count; index++) {
392 SecKeychainAttribute attr = attrList->attr[index];
393 CssmData attrData(attr.data, attr.length);
394 if (attr.tag == kSecKeyPrintName) {
395 newDbAttributes.add(kInfoKeyPrintName, attrData);
396 }
397 if (attr.tag == kSecKeyLabel) {
398 newDbAttributes.add(kInfoKeyLabel, attrData);
399 }
400 if (attr.tag == kSecKeyApplicationTag) {
401 newDbAttributes.add(kInfoKeyApplicationTag, attrData);
402 }
403 }
404
405 modifyUniqueId(keychain, ssDb, uniqueId, newDbAttributes, recordType());
406 }
407
408 /* Set the acl and owner on the unwrapped key. */
409 addIntegrity(*access);
410 access->setAccess(*unwrappedKey, maker);
411
412 /* Return a keychain item which represents the new key. */
413 Item item(keychain->item(recordType(), uniqueId));
414
415 KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, item);
416
417 return item;
418 }
419
420 void
421 KeyItem::didModify()
422 {
423 }
424
425 PrimaryKey
426 KeyItem::add(Keychain &keychain)
427 {
428 MacOSError::throwMe(errSecUnimplemented);
429 }
430
431 CssmClient::SSDbUniqueRecord
432 KeyItem::ssDbUniqueRecord()
433 {
434 DbUniqueRecordImpl *impl = &*dbUniqueRecord();
435 Security::CssmClient::SSDbUniqueRecordImpl *simpl = dynamic_cast<Security::CssmClient::SSDbUniqueRecordImpl *>(impl);
436 if (simpl == NULL)
437 {
438 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
439 }
440
441 return CssmClient::SSDbUniqueRecord(simpl);
442 }
443
444 CssmKey::Header
445 KeyItem::unverifiedKeyHeader() {
446 return unverifiedKey()->header();
447 }
448
449 CssmClient::Key
450 KeyItem::unverifiedKey()
451 {
452 StLock<Mutex>_(mMutex);
453 if (!mKey)
454 {
455 CssmClient::SSDbUniqueRecord uniqueId(ssDbUniqueRecord());
456 CssmDataContainer dataBlob(uniqueId->allocator());
457 uniqueId->get(NULL, &dataBlob);
458 return CssmClient::Key(uniqueId->database()->csp(), *reinterpret_cast<CssmKey *>(dataBlob.Data));
459 }
460
461 return mKey;
462 }
463
464 CssmClient::Key &
465 KeyItem::key()
466 {
467 StLock<Mutex>_(mMutex);
468 if (!mKey)
469 {
470 mKey = unverifiedKey();
471
472 try {
473 if(!ItemImpl::checkIntegrity(*mKey)) {
474 secnotice("integrity", "key has no integrity, denying access");
475 mKey.release();
476 CssmError::throwMe(errSecInvalidItemRef);
477 }
478 } catch(CssmError cssme) {
479 mKey.release();
480 secnotice("integrity", "error while checking integrity, denying access: %s", cssme.what());
481 throw cssme;
482 }
483 }
484
485 return mKey;
486 }
487
488 CssmClient::CSP
489 KeyItem::csp()
490 {
491 return key()->csp();
492 }
493
494
495 const CSSM_X509_ALGORITHM_IDENTIFIER&
496 KeyItem::algorithmIdentifier()
497 {
498 #if 0
499 CssmKey *mKey;
500 CSSM_KEY_TYPE algorithm
501 CSSM_KEY_PTR cssmKey = (CSSM_KEY_PTR)thisData->Data;
502 cssmKey->KeyHeader
503 static void printKeyHeader(
504 const CSSM_KEYHEADER &hdr)
505 {
506 printf(" Algorithm : ");
507 switch(hdr.AlgorithmId) {
508 CSSM_X509_ALGORITHM_IDENTIFIER algID;
509
510 CSSM_OID *CL_algToOid(
511 CSSM_ALGORITHMS algId)
512 typedef struct cssm_x509_algorithm_identifier {
513 CSSM_OID algorithm;
514 CSSM_DATA parameters;
515 } CSSM_X509_ALGORITHM_IDENTIFIER, *CSSM_X509_ALGORITHM_IDENTIFIER_PTR;
516 #endif
517
518 abort();
519 }
520
521 /*
522 * itemID, used to locate Extended Attributes, is the public key hash for keys.
523 */
524 const CssmData &KeyItem::itemID()
525 {
526 if(mPubKeyHash.length() == 0) {
527 /*
528 * Fetch the attribute from disk.
529 */
530 UInt32 tag = kSecKeyLabel;
531 UInt32 format = 0;
532 SecKeychainAttributeInfo attrInfo = {1, &tag, &format};
533 SecKeychainAttributeList *attrList = NULL;
534 getAttributesAndData(&attrInfo, NULL, &attrList, NULL, NULL);
535 if((attrList == NULL) || (attrList->count != 1)) {
536 MacOSError::throwMe(errSecNoSuchAttr);
537 }
538 mPubKeyHash.copy(attrList->attr->data, attrList->attr->length);
539 freeAttributesAndData(attrList, NULL);
540 }
541 return mPubKeyHash;
542 }
543
544
545 unsigned int
546 KeyItem::strengthInBits(const CSSM_X509_ALGORITHM_IDENTIFIER *algid)
547 {
548 // @@@ Make a context with key based on algid and use that to get the effective keysize and not just the logical one.
549 CSSM_KEY_SIZE keySize = {};
550 CSSM_RETURN rv = CSSM_QueryKeySizeInBits (csp()->handle(),
551 CSSM_INVALID_HANDLE,
552 key(),
553 &keySize);
554 if (rv)
555 return 0;
556
557 return keySize.LogicalKeySizeInBits;
558 }
559
560 const AccessCredentials *
561 KeyItem::getCredentials(
562 CSSM_ACL_AUTHORIZATION_TAG operation,
563 SecCredentialType credentialType)
564 {
565 // @@@ Fix this to actually examine the ACL for this key and consider operation and do the right thing.
566 //AutoAclEntryInfoList aclInfos;
567 //key()->getAcl(aclInfos);
568
569 bool smartcard = keychain() != NULL ? (keychain()->database()->dl()->guid() == gGuidAppleSdCSPDL) : false;
570
571 AclFactory factory;
572 switch (credentialType)
573 {
574 case kSecCredentialTypeDefault:
575 return smartcard?globals().smartcardItemCredentials():globals().itemCredentials();
576 case kSecCredentialTypeWithUI:
577 return smartcard?globals().smartcardItemCredentials():factory.promptCred();
578 case kSecCredentialTypeNoUI:
579 return factory.nullCred();
580 default:
581 MacOSError::throwMe(errSecParam);
582 }
583 }
584
585 CssmClient::Key
586 KeyItem::publicKey() {
587 return mPublicKey;
588 }
589
590 bool
591 KeyItem::operator == (KeyItem &other)
592 {
593 if (mKey && *mKey)
594 {
595 // Pointer compare
596 return this == &other;
597 }
598
599 // If keychains are different, then keys are different
600 Keychain otherKeychain = other.keychain();
601 return (mKeychain && otherKeychain && (*mKeychain == *otherKeychain));
602 }
603
604 void
605 KeyItem::createPair(
606 Keychain keychain,
607 CSSM_ALGORITHMS algorithm,
608 uint32 keySizeInBits,
609 CSSM_CC_HANDLE contextHandle,
610 CSSM_KEYUSE publicKeyUsage,
611 uint32 publicKeyAttr,
612 CSSM_KEYUSE privateKeyUsage,
613 uint32 privateKeyAttr,
614 SecPointer<Access> initialAccess,
615 SecPointer<KeyItem> &outPublicKey,
616 SecPointer<KeyItem> &outPrivateKey)
617 {
618 SSDb ssDb(NULL);
619 Access::Maker maker;
620 const AccessCredentials *cred = NULL;
621 CssmClient::CSP appleCsp(gGuidAppleCSP);
622 CssmClient::CSP csp = appleCsp;
623 ResourceControlContext rcc;
624 memset(&rcc, 0, sizeof(rcc));
625 CssmData label;
626 uint8 labelBytes[20];
627
628 if (keychain) {
629 if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
630 MacOSError::throwMe(errSecInvalidKeychain);
631
632 SSDbImpl* impl = dynamic_cast<SSDbImpl*>(&(*keychain->database()));
633 if (impl == NULL)
634 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
635
636 ssDb = SSDb(impl);
637 csp = CssmClient::CSP(keychain->csp());
638
639 // Generate a random label to use initially
640 CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW);
641 label = CssmData(labelBytes, sizeof(labelBytes));
642 random.generate(label, (uint32)label.length());
643
644 // Create a Access::Maker for the initial owner of the private key.
645 // @@@ 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.
646 maker.initialOwner(rcc);
647 // Create the cred we need to manipulate the keys until we actually set a new access control for them.
648 cred = maker.cred();
649 }
650
651 CssmKey publicCssmKey, privateCssmKey;
652 CSSM_CC_HANDLE ccHandle = 0;
653
654 bool freePublicKey = false;
655 bool freePrivateKey = false;
656 bool deleteContext = false;
657 bool permanentPubKey = false;
658 bool permanentPrivKey = false;
659
660 SecPointer<KeyItem> publicKeyItem, privateKeyItem;
661 try {
662 CSSM_RETURN status;
663 if (contextHandle) {
664 ccHandle = contextHandle;
665 } else {
666 status = CSSM_CSP_CreateKeyGenContext(csp->handle(), algorithm, keySizeInBits, NULL, NULL, NULL, NULL, NULL, &ccHandle);
667 if (status)
668 CssmError::throwMe(status);
669 deleteContext = true;
670 }
671
672 if (ssDb) {
673 CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle();
674 CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle;
675 CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } };
676 status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes);
677 if (status)
678 CssmError::throwMe(status);
679 }
680
681 // Generate the keypair
682 status = CSSM_GenerateKeyPair(ccHandle, publicKeyUsage, publicKeyAttr, &label, &publicCssmKey, privateKeyUsage, privateKeyAttr, &label, &rcc, &privateCssmKey);
683 if (status)
684 CssmError::throwMe(status);
685 if ((publicKeyAttr & CSSM_KEYATTR_PERMANENT) != 0) {
686 permanentPubKey = true;
687 freePublicKey = true;
688 }
689 if ((privateKeyAttr & CSSM_KEYATTR_PERMANENT) != 0) {
690 permanentPrivKey = true;
691 freePrivateKey = true;
692 }
693
694 // 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
695 // fix up other attributes.
696
697 // Look up public key in the DLDB.
698 CssmClient::Key publicKey;
699 DbAttributes pubDbAttributes;
700 DbUniqueRecord pubUniqueId;
701 if (permanentPubKey) {
702 SSDbCursor dbPubCursor(ssDb, 1);
703 dbPubCursor->recordType(CSSM_DL_DB_RECORD_PUBLIC_KEY);
704 dbPubCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label);
705 if (!dbPubCursor->nextKey(&pubDbAttributes, publicKey, pubUniqueId))
706 MacOSError::throwMe(errSecItemNotFound);
707 } else {
708 publicKey = CssmClient::Key(appleCsp, publicCssmKey);
709 outPublicKey = new KeyItem(publicKey);
710 freePublicKey = false;
711 }
712
713 // Look up private key in the DLDB.
714 CssmClient::Key privateKey;
715 DbAttributes privDbAttributes;
716 DbUniqueRecord privUniqueId;
717 if (permanentPrivKey) {
718 SSDbCursor dbPrivCursor(ssDb, 1);
719 dbPrivCursor->recordType(CSSM_DL_DB_RECORD_PRIVATE_KEY);
720 dbPrivCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label);
721 if (!dbPrivCursor->nextKey(&privDbAttributes, privateKey, privUniqueId))
722 MacOSError::throwMe(errSecItemNotFound);
723 } else {
724 privateKey = CssmClient::Key(appleCsp, privateCssmKey);
725 outPrivateKey = new KeyItem(privateKey);
726 freePrivateKey = false;
727 }
728
729 if (ssDb) {
730 // Convert reference public key to a raw key so we can use it in the appleCsp.
731 CssmClient::WrapKey wrap(csp, CSSM_ALGID_NONE);
732 wrap.cred(cred);
733 CssmClient::Key rawPubKey = wrap(publicKey);
734
735 // Calculate the hash of the public key using the appleCSP.
736 CssmClient::PassThrough passThrough(appleCsp);
737
738 /* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the
739 * associated key blob.
740 * Key is specified in CSSM_CSP_CreatePassThroughContext.
741 * Hash is allocated by the CSP, in the App's memory, and returned
742 * in *outData. */
743 passThrough.key(rawPubKey);
744 CssmData *pubKeyHashData;
745 passThrough(CSSM_APPLECSP_KEYDIGEST, (const void *)NULL, &pubKeyHashData);
746 CssmAutoData pubKeyHash(passThrough.allocator());
747 pubKeyHash.set(*pubKeyHashData);
748 passThrough.allocator().free(pubKeyHashData);
749
750 auto_ptr<string>privDescription;
751 auto_ptr<string>pubDescription;
752 try {
753 privDescription.reset(new string(initialAccess->promptDescription()));
754 pubDescription.reset(new string(initialAccess->promptDescription()));
755 }
756 catch (...) {
757 /* this path taken if no promptDescription available, e.g., for complex ACLs */
758 privDescription.reset(new string("Private key"));
759 pubDescription.reset(new string("Public key"));
760 }
761
762 if (permanentPubKey) {
763 // Set the label of the public key to the public key hash.
764 // Set the PrintName of the public key to the description in the acl.
765 pubDbAttributes.add(kInfoKeyLabel, pubKeyHash.get());
766 pubDbAttributes.add(kInfoKeyPrintName, *pubDescription);
767 modifyUniqueId(keychain, ssDb, pubUniqueId, pubDbAttributes, CSSM_DL_DB_RECORD_PUBLIC_KEY);
768
769 // Create keychain item which will represent the public key.
770 publicKeyItem = dynamic_cast<KeyItem*>(keychain->item(CSSM_DL_DB_RECORD_PUBLIC_KEY, pubUniqueId).get());
771 if (!publicKeyItem) {
772 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
773 }
774
775 if (publicKeyAttr & CSSM_KEYATTR_PUBLIC_KEY_ENCRYPT) {
776 /*
777 * Make the public key acl completely open.
778 * If the key was not encrypted, it already has a wide-open
779 * ACL (though that is a feature of securityd; it's not
780 * CDSA-specified behavior).
781 */
782 SecPointer<Access> pubKeyAccess(new Access());
783 publicKeyItem->addIntegrity(*pubKeyAccess);
784 pubKeyAccess->setAccess(*publicKey, maker);
785 }
786 outPublicKey = publicKeyItem;
787 }
788
789 if (permanentPrivKey) {
790 // Set the label of the private key to the public key hash.
791 // Set the PrintName of the private key to the description in the acl.
792 privDbAttributes.add(kInfoKeyLabel, pubKeyHash.get());
793 privDbAttributes.add(kInfoKeyPrintName, *privDescription);
794 modifyUniqueId(keychain, ssDb, privUniqueId, privDbAttributes, CSSM_DL_DB_RECORD_PRIVATE_KEY);
795
796 // Create keychain item which will represent the private key.
797 privateKeyItem = dynamic_cast<KeyItem*>(keychain->item(CSSM_DL_DB_RECORD_PRIVATE_KEY, privUniqueId).get());
798 if (!privateKeyItem) {
799 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
800 }
801
802 // Finally fix the acl and owner of the private key to the specified access control settings.
803 privateKeyItem->addIntegrity(*initialAccess);
804 initialAccess->setAccess(*privateKey, maker);
805 outPrivateKey = privateKeyItem;
806 }
807 }
808 outPrivateKey->mPublicKey = publicKey;
809 }
810 catch (...)
811 {
812 // Delete the keys if something goes wrong so we don't end up with inaccessible keys in the database.
813 if (freePublicKey) {
814 CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, permanentPubKey);
815 }
816 if (freePrivateKey) {
817 CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, permanentPrivKey);
818 }
819
820 if (deleteContext)
821 CSSM_DeleteContext(ccHandle);
822
823 throw;
824 }
825
826 if (freePublicKey) {
827 CSSM_FreeKey(csp->handle(), NULL, &publicCssmKey, FALSE);
828 }
829 if (freePrivateKey) {
830 CSSM_FreeKey(csp->handle(), NULL, &privateCssmKey, FALSE);
831 }
832
833 if (deleteContext)
834 CSSM_DeleteContext(ccHandle);
835
836 if (keychain) {
837 if (permanentPubKey) {
838 keychain->postEvent(kSecAddEvent, publicKeyItem);
839 }
840 if (permanentPrivKey) {
841 keychain->postEvent(kSecAddEvent, privateKeyItem);
842 }
843 }
844 }
845
846 void
847 KeyItem::importPair(
848 Keychain keychain,
849 const CSSM_KEY &publicWrappedKey,
850 const CSSM_KEY &privateWrappedKey,
851 SecPointer<Access> initialAccess,
852 SecPointer<KeyItem> &outPublicKey,
853 SecPointer<KeyItem> &outPrivateKey)
854 {
855 bool freePublicKey = false;
856 bool freePrivateKey = false;
857 bool deleteContext = false;
858
859 if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
860 MacOSError::throwMe(errSecInvalidKeychain);
861
862 SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*keychain->database()));
863 if (impl == NULL)
864 {
865 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
866 }
867
868 SSDb ssDb(impl);
869 CssmClient::CSP csp(keychain->csp());
870 CssmClient::CSP appleCsp(gGuidAppleCSP);
871
872 // Create a Access::Maker for the initial owner of the private key.
873 ResourceControlContext rcc;
874 memset(&rcc, 0, sizeof(rcc));
875 Access::Maker maker(Allocator::standard(), Access::Maker::kAnyMakerType);
876 // @@@ Potentially provide a credential argument which allows us to unwrap keys in the csp.
877 // Currently the CSP lets anyone do this, but we might restrict this in the future, e.g.
878 // a smartcard could require out of band pin entry before a key can be generated.
879 maker.initialOwner(rcc);
880 // Create the cred we need to manipulate the keys until we actually set a new access control for them.
881 const AccessCredentials *cred = maker.cred();
882
883 CSSM_KEY publicCssmKey, privateCssmKey;
884 memset(&publicCssmKey, 0, sizeof(publicCssmKey));
885 memset(&privateCssmKey, 0, sizeof(privateCssmKey));
886
887 CSSM_CC_HANDLE ccHandle = 0;
888
889 SecPointer<KeyItem> publicKeyItem, privateKeyItem;
890 try
891 {
892 CSSM_RETURN status;
893
894 // Calculate the hash of the public key using the appleCSP.
895 CssmClient::PassThrough passThrough(appleCsp);
896 void *outData;
897 CssmData *cssmData;
898
899 /* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the
900 * associated key blob.
901 * Key is specified in CSSM_CSP_CreatePassThroughContext.
902 * Hash is allocated bythe CSP, in the App's memory, and returned
903 * in *outData. */
904 passThrough.key(&publicWrappedKey);
905 passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData);
906 cssmData = reinterpret_cast<CssmData *>(outData);
907 CssmData &pubKeyHash = *cssmData;
908
909 status = CSSM_CSP_CreateSymmetricContext(csp->handle(), publicWrappedKey.KeyHeader.WrapAlgorithmId, CSSM_ALGMODE_NONE, NULL, NULL, NULL, CSSM_PADDING_NONE, NULL, &ccHandle);
910 if (status)
911 CssmError::throwMe(status);
912 deleteContext = true;
913
914 CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle();
915 CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle;
916 CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } };
917 status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes);
918 if (status)
919 CssmError::throwMe(status);
920
921 // Unwrap the the keys
922 CSSM_DATA descriptiveData = {0, NULL};
923
924 status = CSSM_UnwrapKey(
925 ccHandle,
926 NULL,
927 &publicWrappedKey,
928 publicWrappedKey.KeyHeader.KeyUsage,
929 publicWrappedKey.KeyHeader.KeyAttr | CSSM_KEYATTR_PERMANENT,
930 &pubKeyHash,
931 &rcc,
932 &publicCssmKey,
933 &descriptiveData);
934
935 if (status)
936 CssmError::throwMe(status);
937 freePublicKey = true;
938
939 if (descriptiveData.Data != NULL)
940 free (descriptiveData.Data);
941
942 status = CSSM_UnwrapKey(
943 ccHandle,
944 NULL,
945 &privateWrappedKey,
946 privateWrappedKey.KeyHeader.KeyUsage,
947 privateWrappedKey.KeyHeader.KeyAttr | CSSM_KEYATTR_PERMANENT,
948 &pubKeyHash,
949 &rcc,
950 &privateCssmKey,
951 &descriptiveData);
952
953 if (status)
954 CssmError::throwMe(status);
955
956 if (descriptiveData.Data != NULL)
957 free (descriptiveData.Data);
958
959 freePrivateKey = true;
960
961 // Find the keys we just generated in the DL to get SecKeyRefs to them
962 // so we can change the label to be the hash of the public key, and
963 // fix up other attributes.
964
965 // Look up public key in the DLDB.
966 DbAttributes pubDbAttributes;
967 DbUniqueRecord pubUniqueId;
968 SSDbCursor dbPubCursor(ssDb, 1);
969 dbPubCursor->recordType(CSSM_DL_DB_RECORD_PUBLIC_KEY);
970 dbPubCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, pubKeyHash);
971 CssmClient::Key publicKey;
972 if (!dbPubCursor->nextKey(&pubDbAttributes, publicKey, pubUniqueId))
973 MacOSError::throwMe(errSecItemNotFound);
974
975 // Look up private key in the DLDB.
976 DbAttributes privDbAttributes;
977 DbUniqueRecord privUniqueId;
978 SSDbCursor dbPrivCursor(ssDb, 1);
979 dbPrivCursor->recordType(CSSM_DL_DB_RECORD_PRIVATE_KEY);
980 dbPrivCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, pubKeyHash);
981 CssmClient::Key privateKey;
982 if (!dbPrivCursor->nextKey(&privDbAttributes, privateKey, privUniqueId))
983 MacOSError::throwMe(errSecItemNotFound);
984
985 // @@@ Not exception safe!
986 csp.allocator().free(cssmData->Data);
987 csp.allocator().free(cssmData);
988
989 auto_ptr<string>privDescription;
990 auto_ptr<string>pubDescription;
991 try {
992 privDescription.reset(new string(initialAccess->promptDescription()));
993 pubDescription.reset(new string(initialAccess->promptDescription()));
994 }
995 catch(...) {
996 /* this path taken if no promptDescription available, e.g., for complex ACLs */
997 privDescription.reset(new string("Private key"));
998 pubDescription.reset(new string("Public key"));
999 }
1000
1001 // Set the label of the public key to the public key hash.
1002 // Set the PrintName of the public key to the description in the acl.
1003 pubDbAttributes.add(kInfoKeyPrintName, *pubDescription);
1004 modifyUniqueId(keychain, ssDb, pubUniqueId, pubDbAttributes, CSSM_DL_DB_RECORD_PUBLIC_KEY);
1005
1006 // Set the label of the private key to the public key hash.
1007 // Set the PrintName of the private key to the description in the acl.
1008 privDbAttributes.add(kInfoKeyPrintName, *privDescription);
1009 modifyUniqueId(keychain, ssDb, privUniqueId, privDbAttributes, CSSM_DL_DB_RECORD_PRIVATE_KEY);
1010
1011 // Create keychain items which will represent the keys.
1012 publicKeyItem = dynamic_cast<KeyItem*>(keychain->item(CSSM_DL_DB_RECORD_PUBLIC_KEY, pubUniqueId).get());
1013 privateKeyItem = dynamic_cast<KeyItem*>(keychain->item(CSSM_DL_DB_RECORD_PRIVATE_KEY, privUniqueId).get());
1014
1015 if (!publicKeyItem || !privateKeyItem)
1016 {
1017 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
1018 }
1019
1020 // Finally fix the acl and owner of the private key to the specified access control settings.
1021 privateKeyItem->addIntegrity(*initialAccess);
1022 initialAccess->setAccess(*privateKey, maker);
1023
1024 // Make the public key acl completely open
1025 SecPointer<Access> pubKeyAccess(new Access());
1026 publicKeyItem->addIntegrity(*pubKeyAccess);
1027 pubKeyAccess->setAccess(*publicKey, maker);
1028
1029 outPublicKey = publicKeyItem;
1030 outPrivateKey = privateKeyItem;
1031 }
1032 catch (...)
1033 {
1034 if (freePublicKey)
1035 CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, TRUE);
1036 if (freePrivateKey)
1037 CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, TRUE);
1038
1039 if (deleteContext)
1040 CSSM_DeleteContext(ccHandle);
1041
1042 throw;
1043 }
1044
1045 if (freePublicKey)
1046 CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, FALSE);
1047 if (freePrivateKey)
1048 CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, FALSE);
1049
1050 if (deleteContext)
1051 CSSM_DeleteContext(ccHandle);
1052
1053 if (keychain && publicKeyItem && privateKeyItem)
1054 {
1055 KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, Item(publicKeyItem));
1056 KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, Item(privateKeyItem));
1057 }
1058 }
1059
1060 SecPointer<KeyItem>
1061 KeyItem::generateWithAttributes(const SecKeychainAttributeList *attrList,
1062 Keychain keychain,
1063 CSSM_ALGORITHMS algorithm,
1064 uint32 keySizeInBits,
1065 CSSM_CC_HANDLE contextHandle,
1066 CSSM_KEYUSE keyUsage,
1067 uint32 keyAttr,
1068 SecPointer<Access> initialAccess)
1069 {
1070 CssmClient::CSP appleCsp(gGuidAppleCSP);
1071 CssmClient::CSP csp(NULL);
1072 SSDb ssDb(NULL);
1073 uint8 labelBytes[20];
1074 CssmData label(labelBytes, sizeof(labelBytes));
1075 bool freeKey = false;
1076 bool deleteContext = false;
1077 const CSSM_DATA *plabel = NULL;
1078
1079 if (keychain)
1080 {
1081 if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
1082 MacOSError::throwMe(errSecInvalidKeychain);
1083
1084 SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*keychain->database()));
1085 if (impl == NULL)
1086 {
1087 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
1088 }
1089
1090 ssDb = SSDb(impl);
1091 csp = keychain->csp();
1092
1093 // Generate a random label to use initially
1094 CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW);
1095 random.generate(label, (uint32)label.Length);
1096 plabel = &label;
1097 }
1098 else
1099 {
1100 // Not a persistent key so create it in the regular csp
1101 csp = appleCsp;
1102 }
1103
1104 // Create a Access::Maker for the initial owner of the private key.
1105 ResourceControlContext *prcc = NULL, rcc;
1106 const AccessCredentials *cred = NULL;
1107 Access::Maker maker;
1108
1109
1110
1111 if (keychain)
1112 {
1113 memset(&rcc, 0, sizeof(rcc));
1114 // @@@ Potentially provide a credential argument which allows us to generate keys in the csp.
1115 // Currently the CSP lets anyone do this, but we might restrict this in the future, e.g. a smartcard
1116 // could require out-of-band pin entry before a key can be generated.
1117 maker.initialOwner(rcc);
1118 // Create the cred we need to manipulate the keys until we actually set a new access control for them.
1119 cred = maker.cred();
1120 prcc = &rcc;
1121
1122 if (!initialAccess) {
1123 // We don't have an access, but we need to set integrity. Make an Access.
1124 initialAccess = new Access(string(label));
1125 }
1126 }
1127
1128 CSSM_KEY cssmKey;
1129
1130 CSSM_CC_HANDLE ccHandle = 0;
1131
1132 SecPointer<KeyItem> keyItem;
1133 try
1134 {
1135 CSSM_RETURN status;
1136 if (contextHandle)
1137 ccHandle = contextHandle;
1138 else
1139 {
1140 status = CSSM_CSP_CreateKeyGenContext(csp->handle(), algorithm, keySizeInBits, NULL, NULL, NULL, NULL, NULL, &ccHandle);
1141 if (status)
1142 CssmError::throwMe(status);
1143 deleteContext = true;
1144 }
1145
1146 if (ssDb)
1147 {
1148 CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle();
1149 CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle;
1150 CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } };
1151 status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes);
1152 if (status)
1153 CssmError::throwMe(status);
1154
1155 keyAttr |= CSSM_KEYATTR_PERMANENT;
1156 }
1157
1158 // Generate the key
1159 status = CSSM_GenerateKey(ccHandle, keyUsage, keyAttr, plabel, prcc, &cssmKey);
1160 if (status)
1161 CssmError::throwMe(status);
1162
1163 if (ssDb)
1164 {
1165 freeKey = true;
1166 // Find the key we just generated in the DL and get a SecKeyRef
1167 // so we can specify the label attribute(s) and initial ACL.
1168
1169 // Look up key in the DLDB.
1170 DbAttributes dbAttributes;
1171 DbUniqueRecord uniqueId;
1172 SSDbCursor dbCursor(ssDb, 1);
1173 dbCursor->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY);
1174 dbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label);
1175 CssmClient::Key key;
1176 if (!dbCursor->nextKey(&dbAttributes, key, uniqueId))
1177 MacOSError::throwMe(errSecItemNotFound);
1178
1179 // Set the initial label, application label, and application tag (if provided)
1180 if (attrList) {
1181 DbAttributes newDbAttributes;
1182
1183 for (UInt32 index=0; index < attrList->count; index++) {
1184 SecKeychainAttribute attr = attrList->attr[index];
1185 CssmData attrData(attr.data, attr.length);
1186 if (attr.tag == kSecKeyPrintName) {
1187 newDbAttributes.add(kInfoKeyPrintName, attrData);
1188 }
1189 if (attr.tag == kSecKeyLabel) {
1190 newDbAttributes.add(kInfoKeyLabel, attrData);
1191 }
1192 if (attr.tag == kSecKeyApplicationTag) {
1193 newDbAttributes.add(kInfoKeyApplicationTag, attrData);
1194 }
1195 }
1196
1197 modifyUniqueId(keychain, ssDb, uniqueId, newDbAttributes, CSSM_DL_DB_RECORD_SYMMETRIC_KEY);
1198 }
1199
1200 // Create keychain item which will represent the key.
1201 keyItem = dynamic_cast<KeyItem *>(keychain->item(CSSM_DL_DB_RECORD_SYMMETRIC_KEY, uniqueId).get());
1202
1203 // Finally, fix the acl and owner of the key to the specified access control settings.
1204 keyItem->addIntegrity(*initialAccess);
1205 initialAccess->setAccess(*key, maker);
1206 }
1207 else
1208 {
1209 CssmClient::Key tempKey(csp, cssmKey);
1210 keyItem = new KeyItem(tempKey);
1211 }
1212 }
1213 catch (...)
1214 {
1215 if (freeKey)
1216 {
1217 // Delete the key if something goes wrong so we don't end up with inaccessible keys in the database.
1218 CSSM_FreeKey(csp->handle(), cred, &cssmKey, TRUE);
1219 }
1220
1221 if (deleteContext)
1222 CSSM_DeleteContext(ccHandle);
1223
1224 throw;
1225 }
1226
1227 if (freeKey)
1228 {
1229 CSSM_FreeKey(csp->handle(), NULL, &cssmKey, FALSE);
1230 }
1231
1232 if (deleteContext)
1233 CSSM_DeleteContext(ccHandle);
1234
1235 if (keychain && keyItem)
1236 keychain->postEvent(kSecAddEvent, keyItem);
1237
1238 KeyItem* item = dynamic_cast<KeyItem*>(&*keyItem);
1239 if (item == NULL)
1240 {
1241 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
1242 }
1243
1244 return item;
1245 }
1246
1247 SecPointer<KeyItem>
1248 KeyItem::generate(Keychain keychain,
1249 CSSM_ALGORITHMS algorithm,
1250 uint32 keySizeInBits,
1251 CSSM_CC_HANDLE contextHandle,
1252 CSSM_KEYUSE keyUsage,
1253 uint32 keyAttr,
1254 SecPointer<Access> initialAccess)
1255 {
1256 return KeyItem::generateWithAttributes(NULL, keychain,
1257 algorithm, keySizeInBits, contextHandle,
1258 keyUsage, keyAttr, initialAccess);
1259 }
1260
1261
1262 CFHashCode KeyItem::hash()
1263 {
1264 CFHashCode result = 0;
1265 const CSSM_KEY *cssmKey = key();
1266 if (NULL != cssmKey)
1267 {
1268 unsigned char digest[CC_SHA256_DIGEST_LENGTH];
1269
1270 CFIndex size_of_data = sizeof(CSSM_KEYHEADER) + cssmKey->KeyData.Length;
1271
1272 CFMutableDataRef temp_cfdata = CFDataCreateMutable(kCFAllocatorDefault, size_of_data);
1273 if (NULL == temp_cfdata)
1274 {
1275 return result;
1276 }
1277
1278 CFDataAppendBytes(temp_cfdata, (const UInt8 *)cssmKey, sizeof(CSSM_KEYHEADER));
1279 CFDataAppendBytes(temp_cfdata, cssmKey->KeyData.Data, cssmKey->KeyData.Length);
1280
1281 if (size_of_data < 80)
1282 {
1283 // If it is less than 80 bytes then CFData can be used
1284 result = CFHash(temp_cfdata);
1285 CFRelease(temp_cfdata);
1286 }
1287 // CFData truncates its hash value to 80 bytes. ????
1288 // In order to do the 'right thing' a SHA 256 hash will be used to
1289 // include all of the data
1290 else
1291 {
1292 memset(digest, 0, CC_SHA256_DIGEST_LENGTH);
1293
1294 CC_SHA256((const void *)CFDataGetBytePtr(temp_cfdata), (CC_LONG)CFDataGetLength(temp_cfdata), digest);
1295
1296 CFDataRef data_to_hash = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
1297 (const UInt8 *)digest, CC_SHA256_DIGEST_LENGTH, kCFAllocatorNull);
1298 result = CFHash(data_to_hash);
1299 CFRelease(data_to_hash);
1300 CFRelease(temp_cfdata);
1301 }
1302 }
1303 return result;
1304 }
1305
1306 void KeyItem::setIntegrity(bool force) {
1307 ItemImpl::setIntegrity(*unverifiedKey(), force);
1308 }
1309
1310 bool KeyItem::checkIntegrity() {
1311 if(!isPersistent()) {
1312 return true;
1313 }
1314
1315 try {
1316 // key() checks integrity of itself, and throws if there's a problem.
1317 key();
1318 return true;
1319 } catch (CssmError cssme) {
1320 return false;
1321 }
1322 }
1323
1324 void KeyItem::removeIntegrity(const AccessCredentials *cred) {
1325 ItemImpl::removeIntegrity(*key(), cred);
1326 }
1327
1328 // KeyItems are a little bit special: the only modifications you can do to them
1329 // is to change their Print Name, Label, or Application Tag.
1330 //
1331 // When we do this modification, we need to look ahead to see if there's an item
1332 // that's already there. If there are, we're going to throw a errSecDuplicateItem.
1333 //
1334 // Unless that item doesn't pass the integrity check, in which case we delete it
1335 // and continue with the add.
1336 void KeyItem::modifyUniqueId(Keychain keychain, SSDb ssDb, DbUniqueRecord& uniqueId, DbAttributes& newDbAttributes, CSSM_DB_RECORDTYPE recordType) {
1337 SSDbCursor otherDbCursor(ssDb, 1);
1338 otherDbCursor->recordType(recordType);
1339
1340 bool checkForDuplicates = false;
1341 // Set up the ssdb cursor
1342 CssmDbAttributeData* label = newDbAttributes.findAttribute(kInfoKeyLabel);
1343 if(label) {
1344 otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label->at(0));
1345 checkForDuplicates = true;
1346 }
1347 CssmDbAttributeData* apptag = newDbAttributes.findAttribute(kInfoKeyApplicationTag);
1348 if(apptag) {
1349 otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyApplicationTag, apptag->at(0));
1350 checkForDuplicates = true;
1351 }
1352
1353 // KeyItems only have integrity if the keychain supports it; otherwise,
1354 // don't pre-check for duplicates
1355 if((!keychain) || !keychain->hasIntegrityProtection()) {
1356 secnotice("integrity", "key skipping duplicate integrity check due to keychain version");
1357 checkForDuplicates = false;
1358 }
1359
1360 if (checkForDuplicates) {
1361 secnotice("integrity", "looking for duplicates");
1362 // If there are duplicates that are invalid, delete it and
1363 // continue. Otherwise, if there are duplicates, throw errSecDuplicateItem.
1364 DbAttributes otherDbAttributes;
1365 DbUniqueRecord otherUniqueId;
1366 CssmClient::Key otherKey;
1367
1368 while(otherDbCursor->nextKey(&otherDbAttributes, otherKey, otherUniqueId)) {
1369 secnotice("integrity", "found a duplicate, checking integrity");
1370
1371 PrimaryKey pk = keychain->makePrimaryKey(recordType, otherUniqueId);
1372
1373 ItemImpl* maybeItem = keychain->_lookupItem(pk);
1374 if(maybeItem) {
1375 if(maybeItem->checkIntegrity()) {
1376 secnotice("integrity", "duplicate is real, throwing error");
1377 MacOSError::throwMe(errSecDuplicateItem);
1378 } else {
1379 secnotice("integrity", "existing duplicate item is invalid, removing...");
1380 Item item(maybeItem);
1381 keychain->deleteItem(item);
1382 }
1383 } else {
1384 KeyItem temp(keychain, pk, otherUniqueId);
1385
1386 if(temp.checkIntegrity()) {
1387 secnotice("integrity", "duplicate is real, throwing error");
1388 MacOSError::throwMe(errSecDuplicateItem);
1389 } else {
1390 secnotice("integrity", "duplicate is invalid, removing");
1391 // Keychain's idea of deleting items involves notifications and callbacks. We don't want that,
1392 // (since this isn't a real item and it should go away quietly), so use this roundabout method.
1393 otherUniqueId->deleteRecord();
1394 keychain->removeItem(temp.primaryKey(), &temp);
1395 }
1396 }
1397 }
1398 }
1399
1400 try {
1401 secnotice("integrity", "modifying unique id");
1402 uniqueId->modify(recordType, &newDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
1403 secnotice("integrity", "done modifying unique id");
1404 } catch(CssmError e) {
1405 // Just in case something went wrong, clean up after this add
1406 uniqueId->deleteRecord();
1407 throw;
1408 }
1409 }