2 * Copyright (c) 2002-2004,2011-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
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>
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>
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
53 using namespace KeychainCore
;
54 using namespace CssmClient
;
56 KeyItem
*KeyItem::required(SecKeyRef ptr
)
58 if (KeyItem
*p
= optional(ptr
)) {
61 MacOSError::throwMe(errSecInvalidItemRef
);
65 KeyItem
*KeyItem::optional(SecKeyRef ptr
)
68 if (KeyItem
*pp
= dynamic_cast<KeyItem
*>(fromSecKeyRef(ptr
))) {
71 MacOSError::throwMe(errSecInvalidItemRef
);
78 KeyItem::operator CFTypeRef() const throw()
80 StMaybeLock
<Mutex
> _(this->getMutexForObject());
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
;
89 // We did not really want to retain, it was just weak->strong promotion test.
90 CFRelease(mWeakSecKeyRef
);
94 if (mWeakSecKeyRef
== NULL
) {
95 // Create enveloping ref on-demand. Transfer reference count from SecCFObject
96 // to newly created SecKeyRef wrapper.
99 return mWeakSecKeyRef
;
102 void KeyItem::initializeWithSecKeyRef(SecKeyRef ref
)
105 mWeakSecKeyRef
= ref
;
109 KeyItem::KeyItem(const Keychain
&keychain
, const PrimaryKey
&primaryKey
, const CssmClient::DbUniqueRecord
&uniqueId
) :
110 ItemImpl(keychain
, primaryKey
, uniqueId
),
113 mPubKeyHash(Allocator::standard())
117 KeyItem::KeyItem(const Keychain
&keychain
, const PrimaryKey
&primaryKey
) :
118 ItemImpl(keychain
, primaryKey
),
121 mPubKeyHash(Allocator::standard())
125 KeyItem
* KeyItem::make(const Keychain
&keychain
, const PrimaryKey
&primaryKey
, const CssmClient::DbUniqueRecord
&uniqueId
)
127 KeyItem
* k
= new KeyItem(keychain
, primaryKey
, uniqueId
);
128 keychain
->addItem(primaryKey
, k
);
134 KeyItem
* KeyItem::make(const Keychain
&keychain
, const PrimaryKey
&primaryKey
)
136 KeyItem
* k
= new KeyItem(keychain
, primaryKey
);
137 keychain
->addItem(primaryKey
, k
);
143 KeyItem::KeyItem(KeyItem
&keyItem
) :
147 mPubKeyHash(Allocator::standard())
149 // @@@ this doesn't work for keys that are not in a keychain.
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
),
156 mPubKeyHash(Allocator::standard())
158 if (key
->keyClass() > CSSM_KEYCLASS_SESSION_KEY
)
159 MacOSError::throwMe(errSecParam
);
169 //Create a new CSPDLTransaction
170 Db
db(mKeychain
->database());
171 CSPDLTransaction
transaction(db
);
175 /* Update integrity on key */
178 transaction
.commit();
182 KeyItem::copyTo(const Keychain
&keychain
, Access
*newAccess
)
184 if (!(keychain
->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP
))
185 MacOSError::throwMe(errSecInvalidKeychain
);
187 /* Get the destination keychain's db. */
188 SSDbImpl
* dbImpl
= dynamic_cast<SSDbImpl
*>(&(*keychain
->database()));
191 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
196 /* Make sure mKey is valid. */
197 const CSSM_KEY
*cssmKey
= key();
198 if (cssmKey
&& (0==(cssmKey
->KeyHeader
.KeyAttr
& CSSM_KEYATTR_EXTRACTABLE
)))
200 MacOSError::throwMe(errSecDataNotAvailable
);
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
);
210 /* Set up the ACL for the new key. */
211 SecPointer
<Access
> access
;
215 access
= new Access(*mKey
);
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 */)));
222 /* make a random IV */
224 CssmData
iv(ivBytes
, sizeof(ivBytes
));
225 random
.generate(iv
, (uint32
)iv
.length());
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
);
234 CssmClient::Key
wrappedKey(wrap(mKey
));
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
);
243 /* Setup the dldbHandle in the context. */
244 unwrap
.add(CSSM_ATTRIBUTE_DL_DB_HANDLE
, ssDb
->handle());
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());
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
;
258 CssmClient::Key
unwrappedKey(unwrap(wrappedKey
, KeySpec(usage
,
259 (mKey
->attributes() | CSSM_KEYATTR_PERMANENT
) & ~(CSSM_KEYATTR_ALWAYS_SENSITIVE
| CSSM_KEYATTR_NEVER_EXTRACTABLE
),
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
);
271 /* Copy the Label, PrintName and ApplicationTag attributes from the old key to the new one. */
273 DbAttributes
oldDbAttributes(mUniqueId
->database(), 3);
274 oldDbAttributes
.add(kInfoKeyLabel
);
275 oldDbAttributes
.add(kInfoKeyPrintName
);
276 oldDbAttributes
.add(kInfoKeyApplicationTag
);
277 mUniqueId
->get(&oldDbAttributes
, NULL
);
280 uniqueId
->modify(recordType(), &oldDbAttributes
, NULL
, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE
);
284 // clean up after trying to insert a duplicate key
285 uniqueId
->deleteRecord ();
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
);
294 /* Return a keychain item which represents the new key. */
295 Item
item(keychain
->item(recordType(), uniqueId
));
297 KCEventNotifier::PostKeychainEvent(kSecAddEvent
, keychain
, item
);
303 KeyItem::importTo(const Keychain
&keychain
, Access
*newAccess
, SecKeychainAttributeList
*attrList
)
305 if (!(keychain
->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP
))
306 MacOSError::throwMe(errSecInvalidKeychain
);
308 /* Get the destination keychain's db. */
309 SSDbImpl
* dbImpl
= dynamic_cast<SSDbImpl
*>(&(*keychain
->database()));
311 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
315 /* Make sure mKey is valid. */
316 /* We can't call key() here, since we won't have a unique record id yet */
318 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
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
);
327 /* Set up the ACL for the new key. */
328 SecPointer
<Access
> access
;
332 access
= new Access(*mKey
);
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 */)));
339 /* make a random IV */
341 CssmData
iv(ivBytes
, sizeof(ivBytes
));
342 random
.generate(iv
, (uint32
)iv
.length());
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
);
351 CssmClient::Key
wrappedKey(wrap(mKey
));
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
);
363 // Wrapping key can be used directly, because source and target CSPs are the same.
364 unwrap
.key(wrappingKey
);
366 unwrap
.mode(CSSM_ALGMODE_ECBPad
);
367 unwrap
.padding(CSSM_PADDING_PKCS7
);
368 unwrap
.initVector(iv
);
370 /* Setup the dldbHandle in the context. */
371 unwrap
.add(CSSM_ATTRIBUTE_DL_DB_HANDLE
, ssDb
->handle());
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());
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
;
385 CssmClient::Key
unwrappedKey(unwrap(wrappedKey
, KeySpec(usage
,
386 (mKey
->attributes() | CSSM_KEYATTR_PERMANENT
) & ~(CSSM_KEYATTR_ALWAYS_SENSITIVE
| CSSM_KEYATTR_NEVER_EXTRACTABLE
),
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
);
398 // Set the initial label, application label, and application tag (if provided)
400 DbAttributes newDbAttributes
;
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
);
408 if (attr
.tag
== kSecKeyLabel
) {
409 newDbAttributes
.add(kInfoKeyLabel
, attrData
);
411 if (attr
.tag
== kSecKeyApplicationTag
) {
412 newDbAttributes
.add(kInfoKeyApplicationTag
, attrData
);
416 modifyUniqueId(keychain
, ssDb
, uniqueId
, newDbAttributes
, recordType());
419 /* Set the acl and owner on the unwrapped key. */
420 addIntegrity(*access
);
421 access
->setAccess(*unwrappedKey
, maker
);
423 /* Return a keychain item which represents the new key. */
424 Item
item(keychain
->item(recordType(), uniqueId
));
426 KCEventNotifier::PostKeychainEvent(kSecAddEvent
, keychain
, item
);
437 KeyItem::add(Keychain
&keychain
)
439 MacOSError::throwMe(errSecUnimplemented
);
442 CssmClient::SSDbUniqueRecord
443 KeyItem::ssDbUniqueRecord()
445 DbUniqueRecordImpl
*impl
= &*dbUniqueRecord();
446 Security::CssmClient::SSDbUniqueRecordImpl
*simpl
= dynamic_cast<Security::CssmClient::SSDbUniqueRecordImpl
*>(impl
);
449 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
452 return CssmClient::SSDbUniqueRecord(simpl
);
456 KeyItem::unverifiedKeyHeader() {
457 return unverifiedKey()->header();
461 KeyItem::unverifiedKey()
463 StLock
<Mutex
>_(mMutex
);
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
));
478 StLock
<Mutex
>_(mMutex
);
481 mKey
= unverifiedKey();
484 if(!ItemImpl::checkIntegrity(*mKey
)) {
485 secnotice("integrity", "key has no integrity, denying access");
487 CssmError::throwMe(errSecInvalidItemRef
);
489 } catch(CssmError cssme
) {
491 secnotice("integrity", "error while checking integrity, denying access: %s", cssme
.what());
506 const CSSM_X509_ALGORITHM_IDENTIFIER
&
507 KeyItem::algorithmIdentifier()
511 CSSM_KEY_TYPE algorithm
512 CSSM_KEY_PTR cssmKey
= (CSSM_KEY_PTR
)thisData
->Data
;
514 static void printKeyHeader(
515 const CSSM_KEYHEADER
&hdr
)
517 printf(" Algorithm : ");
518 switch(hdr
.AlgorithmId
) {
519 CSSM_X509_ALGORITHM_IDENTIFIER algID
;
521 CSSM_OID
*CL_algToOid(
522 CSSM_ALGORITHMS algId
)
523 typedef struct cssm_x509_algorithm_identifier
{
525 CSSM_DATA parameters
;
526 } CSSM_X509_ALGORITHM_IDENTIFIER
, *CSSM_X509_ALGORITHM_IDENTIFIER_PTR
;
533 * itemID, used to locate Extended Attributes, is the public key hash for keys.
535 const CssmData
&KeyItem::itemID()
537 if(mPubKeyHash
.length() == 0) {
539 * Fetch the attribute from disk.
541 UInt32 tag
= kSecKeyLabel
;
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
);
549 mPubKeyHash
.copy(attrList
->attr
->data
, attrList
->attr
->length
);
550 freeAttributesAndData(attrList
, NULL
);
557 KeyItem::strengthInBits(const CSSM_X509_ALGORITHM_IDENTIFIER
*algid
)
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(),
568 return keySize
.LogicalKeySizeInBits
;
571 const AccessCredentials
*
572 KeyItem::getCredentials(
573 CSSM_ACL_AUTHORIZATION_TAG operation
,
574 SecCredentialType credentialType
)
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);
580 bool smartcard
= keychain() != NULL
? (keychain()->database()->dl()->guid() == gGuidAppleSdCSPDL
) : false;
583 switch (credentialType
)
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();
592 MacOSError::throwMe(errSecParam
);
597 KeyItem::publicKey() {
602 KeyItem::operator == (KeyItem
&other
)
607 return this == &other
;
610 // If keychains are different, then keys are different
611 Keychain otherKeychain
= other
.keychain();
612 return (mKeychain
&& otherKeychain
&& (*mKeychain
== *otherKeychain
));
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
)
631 const AccessCredentials
*cred
= NULL
;
632 CssmClient::CSP
appleCsp(gGuidAppleCSP
);
633 CssmClient::CSP csp
= appleCsp
;
634 ResourceControlContext rcc
;
635 memset(&rcc
, 0, sizeof(rcc
));
637 uint8 labelBytes
[20];
640 if (!(keychain
->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP
))
641 MacOSError::throwMe(errSecInvalidKeychain
);
643 SSDbImpl
* impl
= dynamic_cast<SSDbImpl
*>(&(*keychain
->database()));
645 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
648 csp
= keychain
->csp();
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());
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.
662 CssmKey publicCssmKey
, privateCssmKey
;
663 CSSM_CC_HANDLE ccHandle
= 0;
665 bool freePublicKey
= false;
666 bool freePrivateKey
= false;
667 bool deleteContext
= false;
668 bool permanentPubKey
= false;
669 bool permanentPrivKey
= false;
671 SecPointer
<KeyItem
> publicKeyItem
, privateKeyItem
;
675 ccHandle
= contextHandle
;
677 status
= CSSM_CSP_CreateKeyGenContext(csp
->handle(), algorithm
, keySizeInBits
, NULL
, NULL
, NULL
, NULL
, NULL
, &ccHandle
);
679 CssmError::throwMe(status
);
680 deleteContext
= true;
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
);
689 CssmError::throwMe(status
);
692 // Generate the keypair
693 status
= CSSM_GenerateKeyPair(ccHandle
, publicKeyUsage
, publicKeyAttr
, &label
, &publicCssmKey
, privateKeyUsage
, privateKeyAttr
, &label
, &rcc
, &privateCssmKey
);
695 CssmError::throwMe(status
);
696 if ((publicKeyAttr
& CSSM_KEYATTR_PERMANENT
) != 0) {
697 permanentPubKey
= true;
698 freePublicKey
= true;
700 if ((privateKeyAttr
& CSSM_KEYATTR_PERMANENT
) != 0) {
701 permanentPrivKey
= true;
702 freePrivateKey
= true;
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.
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
);
719 publicKey
= CssmClient::Key(csp
, publicCssmKey
);
720 outPublicKey
= new KeyItem(publicKey
);
721 freePublicKey
= false;
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
);
735 privateKey
= CssmClient::Key(csp
, privateCssmKey
);
736 outPrivateKey
= new KeyItem(privateKey
);
737 freePrivateKey
= false;
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
);
744 CssmClient::Key rawPubKey
= wrap(publicKey
);
746 // Calculate the hash of the public key using the appleCSP.
747 CssmClient::PassThrough
passThrough(appleCsp
);
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
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
);
761 auto_ptr
<string
> privDescription
;
762 auto_ptr
<string
> pubDescription
;
764 privDescription
.reset(new string(initialAccess
->promptDescription()));
765 pubDescription
.reset(new string(initialAccess
->promptDescription()));
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"));
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
);
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
);
786 if (publicKeyAttr
& CSSM_KEYATTR_PUBLIC_KEY_ENCRYPT
) {
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).
793 SecPointer
<Access
> pubKeyAccess(new Access());
794 publicKeyItem
->addIntegrity(*pubKeyAccess
);
795 pubKeyAccess
->setAccess(*publicKey
, maker
);
797 outPublicKey
= publicKeyItem
;
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
);
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
);
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
;
819 outPrivateKey
->mPublicKey
= publicKey
;
823 // Delete the keys if something goes wrong so we don't end up with inaccessible keys in the database.
825 CSSM_FreeKey(csp
->handle(), cred
, &publicCssmKey
, permanentPubKey
);
827 if (freePrivateKey
) {
828 CSSM_FreeKey(csp
->handle(), cred
, &privateCssmKey
, permanentPrivKey
);
832 CSSM_DeleteContext(ccHandle
);
838 CSSM_FreeKey(csp
->handle(), NULL
, &publicCssmKey
, FALSE
);
840 if (freePrivateKey
) {
841 CSSM_FreeKey(csp
->handle(), NULL
, &privateCssmKey
, FALSE
);
845 CSSM_DeleteContext(ccHandle
);
848 if (permanentPubKey
) {
849 keychain
->postEvent(kSecAddEvent
, publicKeyItem
);
851 if (permanentPrivKey
) {
852 keychain
->postEvent(kSecAddEvent
, privateKeyItem
);
860 const CSSM_KEY
&publicWrappedKey
,
861 const CSSM_KEY
&privateWrappedKey
,
862 SecPointer
<Access
> initialAccess
,
863 SecPointer
<KeyItem
> &outPublicKey
,
864 SecPointer
<KeyItem
> &outPrivateKey
)
866 bool freePublicKey
= false;
867 bool freePrivateKey
= false;
868 bool deleteContext
= false;
870 if (!(keychain
->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP
))
871 MacOSError::throwMe(errSecInvalidKeychain
);
873 SSDbImpl
* impl
= dynamic_cast<SSDbImpl
*>(&(*keychain
->database()));
876 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
880 CssmClient::CSP
csp(keychain
->csp());
881 CssmClient::CSP
appleCsp(gGuidAppleCSP
);
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();
894 CSSM_KEY publicCssmKey
, privateCssmKey
;
895 memset(&publicCssmKey
, 0, sizeof(publicCssmKey
));
896 memset(&privateCssmKey
, 0, sizeof(privateCssmKey
));
898 CSSM_CC_HANDLE ccHandle
= 0;
900 SecPointer
<KeyItem
> publicKeyItem
, privateKeyItem
;
905 // Calculate the hash of the public key using the appleCSP.
906 CssmClient::PassThrough
passThrough(appleCsp
);
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
915 passThrough
.key(&publicWrappedKey
);
916 passThrough(CSSM_APPLECSP_KEYDIGEST
, NULL
, &outData
);
917 cssmData
= reinterpret_cast<CssmData
*>(outData
);
918 CssmData
&pubKeyHash
= *cssmData
;
920 status
= CSSM_CSP_CreateSymmetricContext(csp
->handle(), publicWrappedKey
.KeyHeader
.WrapAlgorithmId
, CSSM_ALGMODE_NONE
, NULL
, NULL
, NULL
, CSSM_PADDING_NONE
, NULL
, &ccHandle
);
922 CssmError::throwMe(status
);
923 deleteContext
= true;
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
);
930 CssmError::throwMe(status
);
932 // Unwrap the the keys
933 CSSM_DATA descriptiveData
= {0, NULL
};
935 status
= CSSM_UnwrapKey(
939 publicWrappedKey
.KeyHeader
.KeyUsage
,
940 publicWrappedKey
.KeyHeader
.KeyAttr
| CSSM_KEYATTR_PERMANENT
,
947 CssmError::throwMe(status
);
948 freePublicKey
= true;
950 if (descriptiveData
.Data
!= NULL
)
951 free (descriptiveData
.Data
);
953 status
= CSSM_UnwrapKey(
957 privateWrappedKey
.KeyHeader
.KeyUsage
,
958 privateWrappedKey
.KeyHeader
.KeyAttr
| CSSM_KEYATTR_PERMANENT
,
965 CssmError::throwMe(status
);
967 if (descriptiveData
.Data
!= NULL
)
968 free (descriptiveData
.Data
);
970 freePrivateKey
= true;
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.
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
);
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
);
996 // @@@ Not exception safe!
997 csp
.allocator().free(cssmData
->Data
);
998 csp
.allocator().free(cssmData
);
1000 auto_ptr
<string
>privDescription
;
1001 auto_ptr
<string
>pubDescription
;
1003 privDescription
.reset(new string(initialAccess
->promptDescription()));
1004 pubDescription
.reset(new string(initialAccess
->promptDescription()));
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"));
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
);
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
);
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());
1026 if (!publicKeyItem
|| !privateKeyItem
)
1028 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
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
);
1035 // Make the public key acl completely open
1036 SecPointer
<Access
> pubKeyAccess(new Access());
1037 publicKeyItem
->addIntegrity(*pubKeyAccess
);
1038 pubKeyAccess
->setAccess(*publicKey
, maker
);
1040 outPublicKey
= publicKeyItem
;
1041 outPrivateKey
= privateKeyItem
;
1046 CSSM_FreeKey(csp
->handle(), cred
, &publicCssmKey
, TRUE
);
1048 CSSM_FreeKey(csp
->handle(), cred
, &privateCssmKey
, TRUE
);
1051 CSSM_DeleteContext(ccHandle
);
1057 CSSM_FreeKey(csp
->handle(), cred
, &publicCssmKey
, FALSE
);
1059 CSSM_FreeKey(csp
->handle(), cred
, &privateCssmKey
, FALSE
);
1062 CSSM_DeleteContext(ccHandle
);
1064 if (keychain
&& publicKeyItem
&& privateKeyItem
)
1066 KCEventNotifier::PostKeychainEvent(kSecAddEvent
, keychain
, Item(publicKeyItem
));
1067 KCEventNotifier::PostKeychainEvent(kSecAddEvent
, keychain
, Item(privateKeyItem
));
1072 KeyItem::generateWithAttributes(const SecKeychainAttributeList
*attrList
,
1074 CSSM_ALGORITHMS algorithm
,
1075 uint32 keySizeInBits
,
1076 CSSM_CC_HANDLE contextHandle
,
1077 CSSM_KEYUSE keyUsage
,
1079 SecPointer
<Access
> initialAccess
)
1081 CssmClient::CSP
appleCsp(gGuidAppleCSP
);
1082 CssmClient::CSP
csp(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
;
1092 if (!(keychain
->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP
))
1093 MacOSError::throwMe(errSecInvalidKeychain
);
1095 SSDbImpl
* impl
= dynamic_cast<SSDbImpl
*>(&(*keychain
->database()));
1098 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
1102 csp
= keychain
->csp();
1104 // Generate a random label to use initially
1105 CssmClient::Random
random(appleCsp
, CSSM_ALGID_APPLE_YARROW
);
1106 random
.generate(label
, (uint32
)label
.Length
);
1111 // Not a persistent key so create it in the regular csp
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
;
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();
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());
1141 CSSM_CC_HANDLE ccHandle
= 0;
1143 SecPointer
<KeyItem
> keyItem
;
1148 ccHandle
= contextHandle
;
1151 status
= CSSM_CSP_CreateKeyGenContext(csp
->handle(), algorithm
, keySizeInBits
, NULL
, NULL
, NULL
, NULL
, NULL
, &ccHandle
);
1153 CssmError::throwMe(status
);
1154 deleteContext
= true;
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
);
1164 CssmError::throwMe(status
);
1166 keyAttr
|= CSSM_KEYATTR_PERMANENT
;
1170 status
= CSSM_GenerateKey(ccHandle
, keyUsage
, keyAttr
, plabel
, prcc
, &cssmKey
);
1172 CssmError::throwMe(status
);
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.
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
);
1190 // Set the initial label, application label, and application tag (if provided)
1192 DbAttributes newDbAttributes
;
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
);
1200 if (attr
.tag
== kSecKeyLabel
) {
1201 newDbAttributes
.add(kInfoKeyLabel
, attrData
);
1203 if (attr
.tag
== kSecKeyApplicationTag
) {
1204 newDbAttributes
.add(kInfoKeyApplicationTag
, attrData
);
1208 modifyUniqueId(keychain
, ssDb
, uniqueId
, newDbAttributes
, CSSM_DL_DB_RECORD_SYMMETRIC_KEY
);
1211 // Create keychain item which will represent the key.
1212 keyItem
= dynamic_cast<KeyItem
*>(keychain
->item(CSSM_DL_DB_RECORD_SYMMETRIC_KEY
, uniqueId
).get());
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
);
1220 CssmClient::Key
tempKey(csp
, cssmKey
);
1221 keyItem
= new KeyItem(tempKey
);
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
);
1233 CSSM_DeleteContext(ccHandle
);
1240 CSSM_FreeKey(csp
->handle(), NULL
, &cssmKey
, FALSE
);
1244 CSSM_DeleteContext(ccHandle
);
1246 if (keychain
&& keyItem
)
1247 keychain
->postEvent(kSecAddEvent
, keyItem
);
1249 KeyItem
* item
= dynamic_cast<KeyItem
*>(&*keyItem
);
1252 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
1259 KeyItem::generate(Keychain keychain
,
1260 CSSM_ALGORITHMS algorithm
,
1261 uint32 keySizeInBits
,
1262 CSSM_CC_HANDLE contextHandle
,
1263 CSSM_KEYUSE keyUsage
,
1265 SecPointer
<Access
> initialAccess
)
1267 return KeyItem::generateWithAttributes(NULL
, keychain
,
1268 algorithm
, keySizeInBits
, contextHandle
,
1269 keyUsage
, keyAttr
, initialAccess
);
1273 CFHashCode
KeyItem::hash()
1275 CFHashCode result
= 0;
1276 const CSSM_KEY
*cssmKey
= key();
1277 if (NULL
!= cssmKey
)
1279 unsigned char digest
[CC_SHA256_DIGEST_LENGTH
];
1281 CFIndex size_of_data
= sizeof(CSSM_KEYHEADER
) + cssmKey
->KeyData
.Length
;
1283 CFMutableDataRef temp_cfdata
= CFDataCreateMutable(kCFAllocatorDefault
, size_of_data
);
1284 if (NULL
== temp_cfdata
)
1289 CFDataAppendBytes(temp_cfdata
, (const UInt8
*)cssmKey
, sizeof(CSSM_KEYHEADER
));
1290 CFDataAppendBytes(temp_cfdata
, cssmKey
->KeyData
.Data
, cssmKey
->KeyData
.Length
);
1292 if (size_of_data
< 80)
1294 // If it is less than 80 bytes then CFData can be used
1295 result
= CFHash(temp_cfdata
);
1296 CFRelease(temp_cfdata
);
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
1303 memset(digest
, 0, CC_SHA256_DIGEST_LENGTH
);
1305 CC_SHA256((const void *)CFDataGetBytePtr(temp_cfdata
), (CC_LONG
)CFDataGetLength(temp_cfdata
), digest
);
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
);
1317 void KeyItem::setIntegrity(bool force
) {
1318 ItemImpl::setIntegrity(*unverifiedKey(), force
);
1321 bool KeyItem::checkIntegrity() {
1322 if(!isPersistent()) {
1327 // key() checks integrity of itself, and throws if there's a problem.
1330 } catch (CssmError cssme
) {
1335 void KeyItem::removeIntegrity(const AccessCredentials
*cred
) {
1336 ItemImpl::removeIntegrity(*key(), cred
);
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.
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.
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
);
1351 bool checkForDuplicates
= false;
1352 // Set up the ssdb cursor
1353 CssmDbAttributeData
* label
= newDbAttributes
.findAttribute(kInfoKeyLabel
);
1355 otherDbCursor
->add(CSSM_DB_EQUAL
, kInfoKeyLabel
, label
->at(0));
1356 checkForDuplicates
= true;
1358 CssmDbAttributeData
* apptag
= newDbAttributes
.findAttribute(kInfoKeyApplicationTag
);
1360 otherDbCursor
->add(CSSM_DB_EQUAL
, kInfoKeyApplicationTag
, apptag
->at(0));
1361 checkForDuplicates
= true;
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;
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
;
1379 while(otherDbCursor
->nextKey(&otherDbAttributes
, otherKey
, otherUniqueId
)) {
1380 secnotice("integrity", "found a duplicate, checking integrity");
1382 PrimaryKey pk
= keychain
->makePrimaryKey(recordType
, otherUniqueId
);
1384 ItemImpl
* maybeItem
= keychain
->_lookupItem(pk
);
1386 if(maybeItem
->checkIntegrity()) {
1387 secnotice("integrity", "duplicate is real, throwing error");
1388 MacOSError::throwMe(errSecDuplicateItem
);
1390 secnotice("integrity", "existing duplicate item is invalid, removing...");
1391 Item
item(maybeItem
);
1392 keychain
->deleteItem(item
);
1395 KeyItem
temp(keychain
, pk
, otherUniqueId
);
1397 if(temp
.checkIntegrity()) {
1398 secnotice("integrity", "duplicate is real, throwing error");
1399 MacOSError::throwMe(errSecDuplicateItem
);
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
);
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();