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