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 }