/ OSX / libsecurity_keychain / lib / KeyItem.cpp
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  }