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