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>
42 #include <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 // 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).
85 mWeakSecKeyRef
->key
= NULL
;
86 mWeakSecKeyRef
= NULL
;
88 // We did not really want to retain, it was just weak->strong promotion test.
89 CFRelease(mWeakSecKeyRef
);
93 if (mWeakSecKeyRef
== NULL
) {
94 // Create enveloping ref on-demand. Transfer reference count from SecCFObject
95 // to newly created SecKeyRef wrapper.
98 return mWeakSecKeyRef
;
101 void KeyItem::initializeWithSecKeyRef(SecKeyRef ref
)
104 mWeakSecKeyRef
= ref
;
108 KeyItem::KeyItem(const Keychain
&keychain
, const PrimaryKey
&primaryKey
, const CssmClient::DbUniqueRecord
&uniqueId
) :
109 ItemImpl(keychain
, primaryKey
, uniqueId
),
112 mPubKeyHash(Allocator::standard())
116 KeyItem::KeyItem(const Keychain
&keychain
, const PrimaryKey
&primaryKey
) :
117 ItemImpl(keychain
, primaryKey
),
120 mPubKeyHash(Allocator::standard())
124 KeyItem
* KeyItem::make(const Keychain
&keychain
, const PrimaryKey
&primaryKey
, const CssmClient::DbUniqueRecord
&uniqueId
)
126 KeyItem
* k
= new KeyItem(keychain
, primaryKey
, uniqueId
);
127 keychain
->addItem(primaryKey
, k
);
133 KeyItem
* KeyItem::make(const Keychain
&keychain
, const PrimaryKey
&primaryKey
)
135 KeyItem
* k
= new KeyItem(keychain
, primaryKey
);
136 keychain
->addItem(primaryKey
, k
);
142 KeyItem::KeyItem(KeyItem
&keyItem
) :
146 mPubKeyHash(Allocator::standard())
148 // @@@ this doesn't work for keys that are not in a keychain.
151 KeyItem::KeyItem(const CssmClient::Key
&key
) :
152 ItemImpl((SecItemClass
) (key
->keyClass() + CSSM_DL_DB_RECORD_PUBLIC_KEY
), (OSType
)0, (UInt32
)0, (const void*)NULL
),
155 mPubKeyHash(Allocator::standard())
157 if (key
->keyClass() > CSSM_KEYCLASS_SESSION_KEY
)
158 MacOSError::throwMe(errSecParam
);
168 //Create a new CSPDLTransaction
169 Db
db(mKeychain
->database());
170 CSPDLTransaction
transaction(db
);
174 /* Update integrity on key */
177 transaction
.commit();
181 KeyItem::copyTo(const Keychain
&keychain
, Access
*newAccess
)
183 if (!(keychain
->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP
))
184 MacOSError::throwMe(errSecInvalidKeychain
);
186 /* Get the destination keychain's db. */
187 SSDbImpl
* dbImpl
= dynamic_cast<SSDbImpl
*>(&(*keychain
->database()));
190 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
195 /* Make sure mKey is valid. */
196 const CSSM_KEY
*cssmKey
= key();
197 if (cssmKey
&& (0==(cssmKey
->KeyHeader
.KeyAttr
& CSSM_KEYATTR_EXTRACTABLE
)))
199 MacOSError::throwMe(errSecDataNotAvailable
);
202 // Generate a random label to use initially
203 CssmClient::CSP
appleCsp(gGuidAppleCSP
);
204 CssmClient::Random
random(appleCsp
, CSSM_ALGID_APPLE_YARROW
);
205 uint8 labelBytes
[20];
206 CssmData
label(labelBytes
, sizeof(labelBytes
));
207 random
.generate(label
, (uint32
)label
.Length
);
209 /* Set up the ACL for the new key. */
210 SecPointer
<Access
> access
;
214 access
= new Access(*mKey
);
216 /* Generate a random 3DES wrapping Key. */
217 CssmClient::GenerateKey
genKey(csp(), CSSM_ALGID_3DES_3KEY
, 192);
218 CssmClient::Key
wrappingKey(genKey(KeySpec(CSSM_KEYUSE_WRAP
| CSSM_KEYUSE_UNWRAP
,
219 CSSM_KEYATTR_EXTRACTABLE
/* | CSSM_KEYATTR_RETURN_DATA */)));
221 /* make a random IV */
223 CssmData
iv(ivBytes
, sizeof(ivBytes
));
224 random
.generate(iv
, (uint32
)iv
.length());
226 /* Extract the key by wrapping it with the wrapping key. */
227 CssmClient::WrapKey
wrap(csp(), CSSM_ALGID_3DES_3KEY_EDE
);
228 wrap
.key(wrappingKey
);
229 wrap
.cred(getCredentials(CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED
, kSecCredentialTypeDefault
));
230 wrap
.mode(CSSM_ALGMODE_ECBPad
);
231 wrap
.padding(CSSM_PADDING_PKCS7
);
233 CssmClient::Key
wrappedKey(wrap(mKey
));
235 /* Unwrap the new key into the new Keychain. */
236 CssmClient::UnwrapKey
unwrap(keychain
->csp(), CSSM_ALGID_3DES_3KEY_EDE
);
237 unwrap
.key(wrappingKey
);
238 unwrap
.mode(CSSM_ALGMODE_ECBPad
);
239 unwrap
.padding(CSSM_PADDING_PKCS7
);
240 unwrap
.initVector(iv
);
242 /* Setup the dldbHandle in the context. */
243 unwrap
.add(CSSM_ATTRIBUTE_DL_DB_HANDLE
, ssDb
->handle());
245 /* Set up an initial aclEntry so we can change it after the unwrap. */
246 Access::Maker
maker(Allocator::standard(), Access::Maker::kAnyMakerType
);
247 ResourceControlContext rcc
;
248 maker
.initialOwner(rcc
, NULL
);
249 unwrap
.owner(rcc
.input());
251 /* Unwrap the key. */
252 uint32 usage
= mKey
->usage();
253 /* Work around csp brokeness where it sets all usage bits in the Keyheader when CSSM_KEYUSE_ANY is set. */
254 if (usage
& CSSM_KEYUSE_ANY
)
255 usage
= CSSM_KEYUSE_ANY
;
257 CssmClient::Key
unwrappedKey(unwrap(wrappedKey
, KeySpec(usage
,
258 (mKey
->attributes() | CSSM_KEYATTR_PERMANENT
) & ~(CSSM_KEYATTR_ALWAYS_SENSITIVE
| CSSM_KEYATTR_NEVER_EXTRACTABLE
),
261 /* Look up unwrapped key in the DLDB. */
262 DbUniqueRecord uniqueId
;
263 SSDbCursor
dbCursor(ssDb
, 1);
264 dbCursor
->recordType(recordType());
265 dbCursor
->add(CSSM_DB_EQUAL
, kInfoKeyLabel
, label
);
266 CssmClient::Key copiedKey
;
267 if (!dbCursor
->nextKey(NULL
, copiedKey
, uniqueId
))
268 MacOSError::throwMe(errSecItemNotFound
);
270 /* Copy the Label, PrintName and ApplicationTag attributes from the old key to the new one. */
272 DbAttributes
oldDbAttributes(mUniqueId
->database(), 3);
273 oldDbAttributes
.add(kInfoKeyLabel
);
274 oldDbAttributes
.add(kInfoKeyPrintName
);
275 oldDbAttributes
.add(kInfoKeyApplicationTag
);
276 mUniqueId
->get(&oldDbAttributes
, NULL
);
279 uniqueId
->modify(recordType(), &oldDbAttributes
, NULL
, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE
);
283 // clean up after trying to insert a duplicate key
284 uniqueId
->deleteRecord ();
288 /* Set the acl and owner on the unwrapped key. See note in ItemImpl::copyTo about removing rights. */
289 access
->removeAclsForRight(CSSM_ACL_AUTHORIZATION_PARTITION_ID
);
290 access
->removeAclsForRight(CSSM_ACL_AUTHORIZATION_INTEGRITY
);
291 access
->setAccess(*unwrappedKey
, maker
);
293 /* Return a keychain item which represents the new key. */
294 Item
item(keychain
->item(recordType(), uniqueId
));
296 KCEventNotifier::PostKeychainEvent(kSecAddEvent
, keychain
, item
);
302 KeyItem::importTo(const Keychain
&keychain
, Access
*newAccess
, SecKeychainAttributeList
*attrList
)
304 if (!(keychain
->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP
))
305 MacOSError::throwMe(errSecInvalidKeychain
);
307 /* Get the destination keychain's db. */
308 SSDbImpl
* dbImpl
= dynamic_cast<SSDbImpl
*>(&(*keychain
->database()));
310 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
314 /* Make sure mKey is valid. */
315 /* We can't call key() here, since we won't have a unique record id yet */
317 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
319 // Generate a random label to use initially
320 CssmClient::CSP
appleCsp(gGuidAppleCSP
);
321 CssmClient::Random
random(appleCsp
, CSSM_ALGID_APPLE_YARROW
);
322 uint8 labelBytes
[20];
323 CssmData
label(labelBytes
, sizeof(labelBytes
));
324 random
.generate(label
, (uint32
)label
.Length
);
326 /* Set up the ACL for the new key. */
327 SecPointer
<Access
> access
;
331 access
= new Access(*mKey
);
333 /* Generate a random 3DES wrapping Key. */
334 CssmClient::GenerateKey
genKey(csp(), CSSM_ALGID_3DES_3KEY
, 192);
335 CssmClient::Key
wrappingKey(genKey(KeySpec(CSSM_KEYUSE_WRAP
| CSSM_KEYUSE_UNWRAP
,
336 CSSM_KEYATTR_EXTRACTABLE
/* | CSSM_KEYATTR_RETURN_DATA */)));
338 /* make a random IV */
340 CssmData
iv(ivBytes
, sizeof(ivBytes
));
341 random
.generate(iv
, (uint32
)iv
.length());
343 /* Extract the key by wrapping it with the wrapping key. */
344 CssmClient::WrapKey
wrap(csp(), CSSM_ALGID_3DES_3KEY_EDE
);
345 wrap
.key(wrappingKey
);
346 wrap
.cred(getCredentials(CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED
, kSecCredentialTypeDefault
));
347 wrap
.mode(CSSM_ALGMODE_ECBPad
);
348 wrap
.padding(CSSM_PADDING_PKCS7
);
350 CssmClient::Key
wrappedKey(wrap(mKey
));
352 /* Unwrap the new key into the new Keychain. */
353 CssmClient::UnwrapKey
unwrap(keychain
->csp(), CSSM_ALGID_3DES_3KEY_EDE
);
354 unwrap
.key(wrappingKey
);
355 unwrap
.mode(CSSM_ALGMODE_ECBPad
);
356 unwrap
.padding(CSSM_PADDING_PKCS7
);
357 unwrap
.initVector(iv
);
359 /* Setup the dldbHandle in the context. */
360 unwrap
.add(CSSM_ATTRIBUTE_DL_DB_HANDLE
, ssDb
->handle());
362 /* Set up an initial aclEntry so we can change it after the unwrap. */
363 Access::Maker
maker(Allocator::standard(), Access::Maker::kAnyMakerType
);
364 ResourceControlContext rcc
;
365 maker
.initialOwner(rcc
, NULL
);
366 unwrap
.owner(rcc
.input());
368 /* Unwrap the key. */
369 uint32 usage
= mKey
->usage();
370 /* Work around csp brokeness where it sets all usage bits in the Keyheader when CSSM_KEYUSE_ANY is set. */
371 if (usage
& CSSM_KEYUSE_ANY
)
372 usage
= CSSM_KEYUSE_ANY
;
374 CssmClient::Key
unwrappedKey(unwrap(wrappedKey
, KeySpec(usage
,
375 (mKey
->attributes() | CSSM_KEYATTR_PERMANENT
) & ~(CSSM_KEYATTR_ALWAYS_SENSITIVE
| CSSM_KEYATTR_NEVER_EXTRACTABLE
),
378 /* Look up unwrapped key in the DLDB. */
379 DbUniqueRecord uniqueId
;
380 SSDbCursor
dbCursor(ssDb
, 1);
381 dbCursor
->recordType(recordType());
382 dbCursor
->add(CSSM_DB_EQUAL
, kInfoKeyLabel
, label
);
383 CssmClient::Key copiedKey
;
384 if (!dbCursor
->nextKey(NULL
, copiedKey
, uniqueId
))
385 MacOSError::throwMe(errSecItemNotFound
);
387 // Set the initial label, application label, and application tag (if provided)
389 DbAttributes newDbAttributes
;
391 for (UInt32 index
=0; index
< attrList
->count
; index
++) {
392 SecKeychainAttribute attr
= attrList
->attr
[index
];
393 CssmData
attrData(attr
.data
, attr
.length
);
394 if (attr
.tag
== kSecKeyPrintName
) {
395 newDbAttributes
.add(kInfoKeyPrintName
, attrData
);
397 if (attr
.tag
== kSecKeyLabel
) {
398 newDbAttributes
.add(kInfoKeyLabel
, attrData
);
400 if (attr
.tag
== kSecKeyApplicationTag
) {
401 newDbAttributes
.add(kInfoKeyApplicationTag
, attrData
);
405 modifyUniqueId(keychain
, ssDb
, uniqueId
, newDbAttributes
, recordType());
408 /* Set the acl and owner on the unwrapped key. */
409 addIntegrity(*access
);
410 access
->setAccess(*unwrappedKey
, maker
);
412 /* Return a keychain item which represents the new key. */
413 Item
item(keychain
->item(recordType(), uniqueId
));
415 KCEventNotifier::PostKeychainEvent(kSecAddEvent
, keychain
, item
);
426 KeyItem::add(Keychain
&keychain
)
428 MacOSError::throwMe(errSecUnimplemented
);
431 CssmClient::SSDbUniqueRecord
432 KeyItem::ssDbUniqueRecord()
434 DbUniqueRecordImpl
*impl
= &*dbUniqueRecord();
435 Security::CssmClient::SSDbUniqueRecordImpl
*simpl
= dynamic_cast<Security::CssmClient::SSDbUniqueRecordImpl
*>(impl
);
438 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
441 return CssmClient::SSDbUniqueRecord(simpl
);
445 KeyItem::unverifiedKeyHeader() {
446 return unverifiedKey()->header();
450 KeyItem::unverifiedKey()
452 StLock
<Mutex
>_(mMutex
);
455 CssmClient::SSDbUniqueRecord
uniqueId(ssDbUniqueRecord());
456 CssmDataContainer
dataBlob(uniqueId
->allocator());
457 uniqueId
->get(NULL
, &dataBlob
);
458 return CssmClient::Key(uniqueId
->database()->csp(), *reinterpret_cast<CssmKey
*>(dataBlob
.Data
));
467 StLock
<Mutex
>_(mMutex
);
470 mKey
= unverifiedKey();
473 if(!ItemImpl::checkIntegrity(*mKey
)) {
474 secnotice("integrity", "key has no integrity, denying access");
476 CssmError::throwMe(errSecInvalidItemRef
);
478 } catch(CssmError cssme
) {
480 secnotice("integrity", "error while checking integrity, denying access: %s", cssme
.what());
495 const CSSM_X509_ALGORITHM_IDENTIFIER
&
496 KeyItem::algorithmIdentifier()
500 CSSM_KEY_TYPE algorithm
501 CSSM_KEY_PTR cssmKey
= (CSSM_KEY_PTR
)thisData
->Data
;
503 static void printKeyHeader(
504 const CSSM_KEYHEADER
&hdr
)
506 printf(" Algorithm : ");
507 switch(hdr
.AlgorithmId
) {
508 CSSM_X509_ALGORITHM_IDENTIFIER algID
;
510 CSSM_OID
*CL_algToOid(
511 CSSM_ALGORITHMS algId
)
512 typedef struct cssm_x509_algorithm_identifier
{
514 CSSM_DATA parameters
;
515 } CSSM_X509_ALGORITHM_IDENTIFIER
, *CSSM_X509_ALGORITHM_IDENTIFIER_PTR
;
522 * itemID, used to locate Extended Attributes, is the public key hash for keys.
524 const CssmData
&KeyItem::itemID()
526 if(mPubKeyHash
.length() == 0) {
528 * Fetch the attribute from disk.
530 UInt32 tag
= kSecKeyLabel
;
532 SecKeychainAttributeInfo attrInfo
= {1, &tag
, &format
};
533 SecKeychainAttributeList
*attrList
= NULL
;
534 getAttributesAndData(&attrInfo
, NULL
, &attrList
, NULL
, NULL
);
535 if((attrList
== NULL
) || (attrList
->count
!= 1)) {
536 MacOSError::throwMe(errSecNoSuchAttr
);
538 mPubKeyHash
.copy(attrList
->attr
->data
, attrList
->attr
->length
);
539 freeAttributesAndData(attrList
, NULL
);
546 KeyItem::strengthInBits(const CSSM_X509_ALGORITHM_IDENTIFIER
*algid
)
548 // @@@ Make a context with key based on algid and use that to get the effective keysize and not just the logical one.
549 CSSM_KEY_SIZE keySize
= {};
550 CSSM_RETURN rv
= CSSM_QueryKeySizeInBits (csp()->handle(),
557 return keySize
.LogicalKeySizeInBits
;
560 const AccessCredentials
*
561 KeyItem::getCredentials(
562 CSSM_ACL_AUTHORIZATION_TAG operation
,
563 SecCredentialType credentialType
)
565 // @@@ Fix this to actually examine the ACL for this key and consider operation and do the right thing.
566 //AutoAclEntryInfoList aclInfos;
567 //key()->getAcl(aclInfos);
569 bool smartcard
= keychain() != NULL
? (keychain()->database()->dl()->guid() == gGuidAppleSdCSPDL
) : false;
572 switch (credentialType
)
574 case kSecCredentialTypeDefault
:
575 return smartcard
?globals().smartcardItemCredentials():globals().itemCredentials();
576 case kSecCredentialTypeWithUI
:
577 return smartcard
?globals().smartcardItemCredentials():factory
.promptCred();
578 case kSecCredentialTypeNoUI
:
579 return factory
.nullCred();
581 MacOSError::throwMe(errSecParam
);
586 KeyItem::publicKey() {
591 KeyItem::operator == (KeyItem
&other
)
596 return this == &other
;
599 // If keychains are different, then keys are different
600 Keychain otherKeychain
= other
.keychain();
601 return (mKeychain
&& otherKeychain
&& (*mKeychain
== *otherKeychain
));
607 CSSM_ALGORITHMS algorithm
,
608 uint32 keySizeInBits
,
609 CSSM_CC_HANDLE contextHandle
,
610 CSSM_KEYUSE publicKeyUsage
,
611 uint32 publicKeyAttr
,
612 CSSM_KEYUSE privateKeyUsage
,
613 uint32 privateKeyAttr
,
614 SecPointer
<Access
> initialAccess
,
615 SecPointer
<KeyItem
> &outPublicKey
,
616 SecPointer
<KeyItem
> &outPrivateKey
)
620 const AccessCredentials
*cred
= NULL
;
621 CssmClient::CSP
appleCsp(gGuidAppleCSP
);
622 CssmClient::CSP csp
= appleCsp
;
623 ResourceControlContext rcc
;
624 memset(&rcc
, 0, sizeof(rcc
));
626 uint8 labelBytes
[20];
629 if (!(keychain
->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP
))
630 MacOSError::throwMe(errSecInvalidKeychain
);
632 SSDbImpl
* impl
= dynamic_cast<SSDbImpl
*>(&(*keychain
->database()));
634 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
637 csp
= CssmClient::CSP(keychain
->csp());
639 // Generate a random label to use initially
640 CssmClient::Random
random(appleCsp
, CSSM_ALGID_APPLE_YARROW
);
641 label
= CssmData(labelBytes
, sizeof(labelBytes
));
642 random
.generate(label
, (uint32
)label
.length());
644 // Create a Access::Maker for the initial owner of the private key.
645 // @@@ 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.
646 maker
.initialOwner(rcc
);
647 // Create the cred we need to manipulate the keys until we actually set a new access control for them.
651 CssmKey publicCssmKey
, privateCssmKey
;
652 CSSM_CC_HANDLE ccHandle
= 0;
654 bool freePublicKey
= false;
655 bool freePrivateKey
= false;
656 bool deleteContext
= false;
657 bool permanentPubKey
= false;
658 bool permanentPrivKey
= false;
660 SecPointer
<KeyItem
> publicKeyItem
, privateKeyItem
;
664 ccHandle
= contextHandle
;
666 status
= CSSM_CSP_CreateKeyGenContext(csp
->handle(), algorithm
, keySizeInBits
, NULL
, NULL
, NULL
, NULL
, NULL
, &ccHandle
);
668 CssmError::throwMe(status
);
669 deleteContext
= true;
673 CSSM_DL_DB_HANDLE dldbHandle
= ssDb
->handle();
674 CSSM_DL_DB_HANDLE_PTR dldbHandlePtr
= &dldbHandle
;
675 CSSM_CONTEXT_ATTRIBUTE contextAttributes
= { CSSM_ATTRIBUTE_DL_DB_HANDLE
, sizeof(dldbHandle
), { (char *)dldbHandlePtr
} };
676 status
= CSSM_UpdateContextAttributes(ccHandle
, 1, &contextAttributes
);
678 CssmError::throwMe(status
);
681 // Generate the keypair
682 status
= CSSM_GenerateKeyPair(ccHandle
, publicKeyUsage
, publicKeyAttr
, &label
, &publicCssmKey
, privateKeyUsage
, privateKeyAttr
, &label
, &rcc
, &privateCssmKey
);
684 CssmError::throwMe(status
);
685 if ((publicKeyAttr
& CSSM_KEYATTR_PERMANENT
) != 0) {
686 permanentPubKey
= true;
687 freePublicKey
= true;
689 if ((privateKeyAttr
& CSSM_KEYATTR_PERMANENT
) != 0) {
690 permanentPrivKey
= true;
691 freePrivateKey
= true;
694 // 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
695 // fix up other attributes.
697 // Look up public key in the DLDB.
698 CssmClient::Key publicKey
;
699 DbAttributes pubDbAttributes
;
700 DbUniqueRecord pubUniqueId
;
701 if (permanentPubKey
) {
702 SSDbCursor
dbPubCursor(ssDb
, 1);
703 dbPubCursor
->recordType(CSSM_DL_DB_RECORD_PUBLIC_KEY
);
704 dbPubCursor
->add(CSSM_DB_EQUAL
, kInfoKeyLabel
, label
);
705 if (!dbPubCursor
->nextKey(&pubDbAttributes
, publicKey
, pubUniqueId
))
706 MacOSError::throwMe(errSecItemNotFound
);
708 publicKey
= CssmClient::Key(appleCsp
, publicCssmKey
);
709 outPublicKey
= new KeyItem(publicKey
);
710 freePublicKey
= false;
713 // Look up private key in the DLDB.
714 CssmClient::Key privateKey
;
715 DbAttributes privDbAttributes
;
716 DbUniqueRecord privUniqueId
;
717 if (permanentPrivKey
) {
718 SSDbCursor
dbPrivCursor(ssDb
, 1);
719 dbPrivCursor
->recordType(CSSM_DL_DB_RECORD_PRIVATE_KEY
);
720 dbPrivCursor
->add(CSSM_DB_EQUAL
, kInfoKeyLabel
, label
);
721 if (!dbPrivCursor
->nextKey(&privDbAttributes
, privateKey
, privUniqueId
))
722 MacOSError::throwMe(errSecItemNotFound
);
724 privateKey
= CssmClient::Key(appleCsp
, privateCssmKey
);
725 outPrivateKey
= new KeyItem(privateKey
);
726 freePrivateKey
= false;
730 // Convert reference public key to a raw key so we can use it in the appleCsp.
731 CssmClient::WrapKey
wrap(csp
, CSSM_ALGID_NONE
);
733 CssmClient::Key rawPubKey
= wrap(publicKey
);
735 // Calculate the hash of the public key using the appleCSP.
736 CssmClient::PassThrough
passThrough(appleCsp
);
738 /* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the
739 * associated key blob.
740 * Key is specified in CSSM_CSP_CreatePassThroughContext.
741 * Hash is allocated by the CSP, in the App's memory, and returned
743 passThrough
.key(rawPubKey
);
744 CssmData
*pubKeyHashData
;
745 passThrough(CSSM_APPLECSP_KEYDIGEST
, (const void *)NULL
, &pubKeyHashData
);
746 CssmAutoData
pubKeyHash(passThrough
.allocator());
747 pubKeyHash
.set(*pubKeyHashData
);
748 passThrough
.allocator().free(pubKeyHashData
);
750 auto_ptr
<string
>privDescription
;
751 auto_ptr
<string
>pubDescription
;
753 privDescription
.reset(new string(initialAccess
->promptDescription()));
754 pubDescription
.reset(new string(initialAccess
->promptDescription()));
757 /* this path taken if no promptDescription available, e.g., for complex ACLs */
758 privDescription
.reset(new string("Private key"));
759 pubDescription
.reset(new string("Public key"));
762 if (permanentPubKey
) {
763 // Set the label of the public key to the public key hash.
764 // Set the PrintName of the public key to the description in the acl.
765 pubDbAttributes
.add(kInfoKeyLabel
, pubKeyHash
.get());
766 pubDbAttributes
.add(kInfoKeyPrintName
, *pubDescription
);
767 modifyUniqueId(keychain
, ssDb
, pubUniqueId
, pubDbAttributes
, CSSM_DL_DB_RECORD_PUBLIC_KEY
);
769 // Create keychain item which will represent the public key.
770 publicKeyItem
= dynamic_cast<KeyItem
*>(keychain
->item(CSSM_DL_DB_RECORD_PUBLIC_KEY
, pubUniqueId
).get());
771 if (!publicKeyItem
) {
772 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
775 if (publicKeyAttr
& CSSM_KEYATTR_PUBLIC_KEY_ENCRYPT
) {
777 * Make the public key acl completely open.
778 * If the key was not encrypted, it already has a wide-open
779 * ACL (though that is a feature of securityd; it's not
780 * CDSA-specified behavior).
782 SecPointer
<Access
> pubKeyAccess(new Access());
783 publicKeyItem
->addIntegrity(*pubKeyAccess
);
784 pubKeyAccess
->setAccess(*publicKey
, maker
);
786 outPublicKey
= publicKeyItem
;
789 if (permanentPrivKey
) {
790 // Set the label of the private key to the public key hash.
791 // Set the PrintName of the private key to the description in the acl.
792 privDbAttributes
.add(kInfoKeyLabel
, pubKeyHash
.get());
793 privDbAttributes
.add(kInfoKeyPrintName
, *privDescription
);
794 modifyUniqueId(keychain
, ssDb
, privUniqueId
, privDbAttributes
, CSSM_DL_DB_RECORD_PRIVATE_KEY
);
796 // Create keychain item which will represent the private key.
797 privateKeyItem
= dynamic_cast<KeyItem
*>(keychain
->item(CSSM_DL_DB_RECORD_PRIVATE_KEY
, privUniqueId
).get());
798 if (!privateKeyItem
) {
799 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
802 // Finally fix the acl and owner of the private key to the specified access control settings.
803 privateKeyItem
->addIntegrity(*initialAccess
);
804 initialAccess
->setAccess(*privateKey
, maker
);
805 outPrivateKey
= privateKeyItem
;
808 outPrivateKey
->mPublicKey
= publicKey
;
812 // Delete the keys if something goes wrong so we don't end up with inaccessible keys in the database.
814 CSSM_FreeKey(csp
->handle(), cred
, &publicCssmKey
, permanentPubKey
);
816 if (freePrivateKey
) {
817 CSSM_FreeKey(csp
->handle(), cred
, &privateCssmKey
, permanentPrivKey
);
821 CSSM_DeleteContext(ccHandle
);
827 CSSM_FreeKey(csp
->handle(), NULL
, &publicCssmKey
, FALSE
);
829 if (freePrivateKey
) {
830 CSSM_FreeKey(csp
->handle(), NULL
, &privateCssmKey
, FALSE
);
834 CSSM_DeleteContext(ccHandle
);
837 if (permanentPubKey
) {
838 keychain
->postEvent(kSecAddEvent
, publicKeyItem
);
840 if (permanentPrivKey
) {
841 keychain
->postEvent(kSecAddEvent
, privateKeyItem
);
849 const CSSM_KEY
&publicWrappedKey
,
850 const CSSM_KEY
&privateWrappedKey
,
851 SecPointer
<Access
> initialAccess
,
852 SecPointer
<KeyItem
> &outPublicKey
,
853 SecPointer
<KeyItem
> &outPrivateKey
)
855 bool freePublicKey
= false;
856 bool freePrivateKey
= false;
857 bool deleteContext
= false;
859 if (!(keychain
->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP
))
860 MacOSError::throwMe(errSecInvalidKeychain
);
862 SSDbImpl
* impl
= dynamic_cast<SSDbImpl
*>(&(*keychain
->database()));
865 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
869 CssmClient::CSP
csp(keychain
->csp());
870 CssmClient::CSP
appleCsp(gGuidAppleCSP
);
872 // Create a Access::Maker for the initial owner of the private key.
873 ResourceControlContext rcc
;
874 memset(&rcc
, 0, sizeof(rcc
));
875 Access::Maker
maker(Allocator::standard(), Access::Maker::kAnyMakerType
);
876 // @@@ Potentially provide a credential argument which allows us to unwrap keys in the csp.
877 // Currently the CSP lets anyone do this, but we might restrict this in the future, e.g.
878 // a smartcard could require out of band pin entry before a key can be generated.
879 maker
.initialOwner(rcc
);
880 // Create the cred we need to manipulate the keys until we actually set a new access control for them.
881 const AccessCredentials
*cred
= maker
.cred();
883 CSSM_KEY publicCssmKey
, privateCssmKey
;
884 memset(&publicCssmKey
, 0, sizeof(publicCssmKey
));
885 memset(&privateCssmKey
, 0, sizeof(privateCssmKey
));
887 CSSM_CC_HANDLE ccHandle
= 0;
889 SecPointer
<KeyItem
> publicKeyItem
, privateKeyItem
;
894 // Calculate the hash of the public key using the appleCSP.
895 CssmClient::PassThrough
passThrough(appleCsp
);
899 /* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the
900 * associated key blob.
901 * Key is specified in CSSM_CSP_CreatePassThroughContext.
902 * Hash is allocated bythe CSP, in the App's memory, and returned
904 passThrough
.key(&publicWrappedKey
);
905 passThrough(CSSM_APPLECSP_KEYDIGEST
, NULL
, &outData
);
906 cssmData
= reinterpret_cast<CssmData
*>(outData
);
907 CssmData
&pubKeyHash
= *cssmData
;
909 status
= CSSM_CSP_CreateSymmetricContext(csp
->handle(), publicWrappedKey
.KeyHeader
.WrapAlgorithmId
, CSSM_ALGMODE_NONE
, NULL
, NULL
, NULL
, CSSM_PADDING_NONE
, NULL
, &ccHandle
);
911 CssmError::throwMe(status
);
912 deleteContext
= true;
914 CSSM_DL_DB_HANDLE dldbHandle
= ssDb
->handle();
915 CSSM_DL_DB_HANDLE_PTR dldbHandlePtr
= &dldbHandle
;
916 CSSM_CONTEXT_ATTRIBUTE contextAttributes
= { CSSM_ATTRIBUTE_DL_DB_HANDLE
, sizeof(dldbHandle
), { (char *)dldbHandlePtr
} };
917 status
= CSSM_UpdateContextAttributes(ccHandle
, 1, &contextAttributes
);
919 CssmError::throwMe(status
);
921 // Unwrap the the keys
922 CSSM_DATA descriptiveData
= {0, NULL
};
924 status
= CSSM_UnwrapKey(
928 publicWrappedKey
.KeyHeader
.KeyUsage
,
929 publicWrappedKey
.KeyHeader
.KeyAttr
| CSSM_KEYATTR_PERMANENT
,
936 CssmError::throwMe(status
);
937 freePublicKey
= true;
939 if (descriptiveData
.Data
!= NULL
)
940 free (descriptiveData
.Data
);
942 status
= CSSM_UnwrapKey(
946 privateWrappedKey
.KeyHeader
.KeyUsage
,
947 privateWrappedKey
.KeyHeader
.KeyAttr
| CSSM_KEYATTR_PERMANENT
,
954 CssmError::throwMe(status
);
956 if (descriptiveData
.Data
!= NULL
)
957 free (descriptiveData
.Data
);
959 freePrivateKey
= true;
961 // Find the keys we just generated in the DL to get SecKeyRefs to them
962 // so we can change the label to be the hash of the public key, and
963 // fix up other attributes.
965 // Look up public key in the DLDB.
966 DbAttributes pubDbAttributes
;
967 DbUniqueRecord pubUniqueId
;
968 SSDbCursor
dbPubCursor(ssDb
, 1);
969 dbPubCursor
->recordType(CSSM_DL_DB_RECORD_PUBLIC_KEY
);
970 dbPubCursor
->add(CSSM_DB_EQUAL
, kInfoKeyLabel
, pubKeyHash
);
971 CssmClient::Key publicKey
;
972 if (!dbPubCursor
->nextKey(&pubDbAttributes
, publicKey
, pubUniqueId
))
973 MacOSError::throwMe(errSecItemNotFound
);
975 // Look up private key in the DLDB.
976 DbAttributes privDbAttributes
;
977 DbUniqueRecord privUniqueId
;
978 SSDbCursor
dbPrivCursor(ssDb
, 1);
979 dbPrivCursor
->recordType(CSSM_DL_DB_RECORD_PRIVATE_KEY
);
980 dbPrivCursor
->add(CSSM_DB_EQUAL
, kInfoKeyLabel
, pubKeyHash
);
981 CssmClient::Key privateKey
;
982 if (!dbPrivCursor
->nextKey(&privDbAttributes
, privateKey
, privUniqueId
))
983 MacOSError::throwMe(errSecItemNotFound
);
985 // @@@ Not exception safe!
986 csp
.allocator().free(cssmData
->Data
);
987 csp
.allocator().free(cssmData
);
989 auto_ptr
<string
>privDescription
;
990 auto_ptr
<string
>pubDescription
;
992 privDescription
.reset(new string(initialAccess
->promptDescription()));
993 pubDescription
.reset(new string(initialAccess
->promptDescription()));
996 /* this path taken if no promptDescription available, e.g., for complex ACLs */
997 privDescription
.reset(new string("Private key"));
998 pubDescription
.reset(new string("Public key"));
1001 // Set the label of the public key to the public key hash.
1002 // Set the PrintName of the public key to the description in the acl.
1003 pubDbAttributes
.add(kInfoKeyPrintName
, *pubDescription
);
1004 modifyUniqueId(keychain
, ssDb
, pubUniqueId
, pubDbAttributes
, CSSM_DL_DB_RECORD_PUBLIC_KEY
);
1006 // Set the label of the private key to the public key hash.
1007 // Set the PrintName of the private key to the description in the acl.
1008 privDbAttributes
.add(kInfoKeyPrintName
, *privDescription
);
1009 modifyUniqueId(keychain
, ssDb
, privUniqueId
, privDbAttributes
, CSSM_DL_DB_RECORD_PRIVATE_KEY
);
1011 // Create keychain items which will represent the keys.
1012 publicKeyItem
= dynamic_cast<KeyItem
*>(keychain
->item(CSSM_DL_DB_RECORD_PUBLIC_KEY
, pubUniqueId
).get());
1013 privateKeyItem
= dynamic_cast<KeyItem
*>(keychain
->item(CSSM_DL_DB_RECORD_PRIVATE_KEY
, privUniqueId
).get());
1015 if (!publicKeyItem
|| !privateKeyItem
)
1017 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
1020 // Finally fix the acl and owner of the private key to the specified access control settings.
1021 privateKeyItem
->addIntegrity(*initialAccess
);
1022 initialAccess
->setAccess(*privateKey
, maker
);
1024 // Make the public key acl completely open
1025 SecPointer
<Access
> pubKeyAccess(new Access());
1026 publicKeyItem
->addIntegrity(*pubKeyAccess
);
1027 pubKeyAccess
->setAccess(*publicKey
, maker
);
1029 outPublicKey
= publicKeyItem
;
1030 outPrivateKey
= privateKeyItem
;
1035 CSSM_FreeKey(csp
->handle(), cred
, &publicCssmKey
, TRUE
);
1037 CSSM_FreeKey(csp
->handle(), cred
, &privateCssmKey
, TRUE
);
1040 CSSM_DeleteContext(ccHandle
);
1046 CSSM_FreeKey(csp
->handle(), cred
, &publicCssmKey
, FALSE
);
1048 CSSM_FreeKey(csp
->handle(), cred
, &privateCssmKey
, FALSE
);
1051 CSSM_DeleteContext(ccHandle
);
1053 if (keychain
&& publicKeyItem
&& privateKeyItem
)
1055 KCEventNotifier::PostKeychainEvent(kSecAddEvent
, keychain
, Item(publicKeyItem
));
1056 KCEventNotifier::PostKeychainEvent(kSecAddEvent
, keychain
, Item(privateKeyItem
));
1061 KeyItem::generateWithAttributes(const SecKeychainAttributeList
*attrList
,
1063 CSSM_ALGORITHMS algorithm
,
1064 uint32 keySizeInBits
,
1065 CSSM_CC_HANDLE contextHandle
,
1066 CSSM_KEYUSE keyUsage
,
1068 SecPointer
<Access
> initialAccess
)
1070 CssmClient::CSP
appleCsp(gGuidAppleCSP
);
1071 CssmClient::CSP
csp(NULL
);
1073 uint8 labelBytes
[20];
1074 CssmData
label(labelBytes
, sizeof(labelBytes
));
1075 bool freeKey
= false;
1076 bool deleteContext
= false;
1077 const CSSM_DATA
*plabel
= NULL
;
1081 if (!(keychain
->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP
))
1082 MacOSError::throwMe(errSecInvalidKeychain
);
1084 SSDbImpl
* impl
= dynamic_cast<SSDbImpl
*>(&(*keychain
->database()));
1087 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
1091 csp
= keychain
->csp();
1093 // Generate a random label to use initially
1094 CssmClient::Random
random(appleCsp
, CSSM_ALGID_APPLE_YARROW
);
1095 random
.generate(label
, (uint32
)label
.Length
);
1100 // Not a persistent key so create it in the regular csp
1104 // Create a Access::Maker for the initial owner of the private key.
1105 ResourceControlContext
*prcc
= NULL
, rcc
;
1106 const AccessCredentials
*cred
= NULL
;
1107 Access::Maker maker
;
1113 memset(&rcc
, 0, sizeof(rcc
));
1114 // @@@ Potentially provide a credential argument which allows us to generate keys in the csp.
1115 // Currently the CSP lets anyone do this, but we might restrict this in the future, e.g. a smartcard
1116 // could require out-of-band pin entry before a key can be generated.
1117 maker
.initialOwner(rcc
);
1118 // Create the cred we need to manipulate the keys until we actually set a new access control for them.
1119 cred
= maker
.cred();
1122 if (!initialAccess
) {
1123 // We don't have an access, but we need to set integrity. Make an Access.
1124 initialAccess
= new Access(string(label
));
1130 CSSM_CC_HANDLE ccHandle
= 0;
1132 SecPointer
<KeyItem
> keyItem
;
1137 ccHandle
= contextHandle
;
1140 status
= CSSM_CSP_CreateKeyGenContext(csp
->handle(), algorithm
, keySizeInBits
, NULL
, NULL
, NULL
, NULL
, NULL
, &ccHandle
);
1142 CssmError::throwMe(status
);
1143 deleteContext
= true;
1148 CSSM_DL_DB_HANDLE dldbHandle
= ssDb
->handle();
1149 CSSM_DL_DB_HANDLE_PTR dldbHandlePtr
= &dldbHandle
;
1150 CSSM_CONTEXT_ATTRIBUTE contextAttributes
= { CSSM_ATTRIBUTE_DL_DB_HANDLE
, sizeof(dldbHandle
), { (char *)dldbHandlePtr
} };
1151 status
= CSSM_UpdateContextAttributes(ccHandle
, 1, &contextAttributes
);
1153 CssmError::throwMe(status
);
1155 keyAttr
|= CSSM_KEYATTR_PERMANENT
;
1159 status
= CSSM_GenerateKey(ccHandle
, keyUsage
, keyAttr
, plabel
, prcc
, &cssmKey
);
1161 CssmError::throwMe(status
);
1166 // Find the key we just generated in the DL and get a SecKeyRef
1167 // so we can specify the label attribute(s) and initial ACL.
1169 // Look up key in the DLDB.
1170 DbAttributes dbAttributes
;
1171 DbUniqueRecord uniqueId
;
1172 SSDbCursor
dbCursor(ssDb
, 1);
1173 dbCursor
->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY
);
1174 dbCursor
->add(CSSM_DB_EQUAL
, kInfoKeyLabel
, label
);
1175 CssmClient::Key key
;
1176 if (!dbCursor
->nextKey(&dbAttributes
, key
, uniqueId
))
1177 MacOSError::throwMe(errSecItemNotFound
);
1179 // Set the initial label, application label, and application tag (if provided)
1181 DbAttributes newDbAttributes
;
1183 for (UInt32 index
=0; index
< attrList
->count
; index
++) {
1184 SecKeychainAttribute attr
= attrList
->attr
[index
];
1185 CssmData
attrData(attr
.data
, attr
.length
);
1186 if (attr
.tag
== kSecKeyPrintName
) {
1187 newDbAttributes
.add(kInfoKeyPrintName
, attrData
);
1189 if (attr
.tag
== kSecKeyLabel
) {
1190 newDbAttributes
.add(kInfoKeyLabel
, attrData
);
1192 if (attr
.tag
== kSecKeyApplicationTag
) {
1193 newDbAttributes
.add(kInfoKeyApplicationTag
, attrData
);
1197 modifyUniqueId(keychain
, ssDb
, uniqueId
, newDbAttributes
, CSSM_DL_DB_RECORD_SYMMETRIC_KEY
);
1200 // Create keychain item which will represent the key.
1201 keyItem
= dynamic_cast<KeyItem
*>(keychain
->item(CSSM_DL_DB_RECORD_SYMMETRIC_KEY
, uniqueId
).get());
1203 // Finally, fix the acl and owner of the key to the specified access control settings.
1204 keyItem
->addIntegrity(*initialAccess
);
1205 initialAccess
->setAccess(*key
, maker
);
1209 CssmClient::Key
tempKey(csp
, cssmKey
);
1210 keyItem
= new KeyItem(tempKey
);
1217 // Delete the key if something goes wrong so we don't end up with inaccessible keys in the database.
1218 CSSM_FreeKey(csp
->handle(), cred
, &cssmKey
, TRUE
);
1222 CSSM_DeleteContext(ccHandle
);
1229 CSSM_FreeKey(csp
->handle(), NULL
, &cssmKey
, FALSE
);
1233 CSSM_DeleteContext(ccHandle
);
1235 if (keychain
&& keyItem
)
1236 keychain
->postEvent(kSecAddEvent
, keyItem
);
1238 KeyItem
* item
= dynamic_cast<KeyItem
*>(&*keyItem
);
1241 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
1248 KeyItem::generate(Keychain keychain
,
1249 CSSM_ALGORITHMS algorithm
,
1250 uint32 keySizeInBits
,
1251 CSSM_CC_HANDLE contextHandle
,
1252 CSSM_KEYUSE keyUsage
,
1254 SecPointer
<Access
> initialAccess
)
1256 return KeyItem::generateWithAttributes(NULL
, keychain
,
1257 algorithm
, keySizeInBits
, contextHandle
,
1258 keyUsage
, keyAttr
, initialAccess
);
1262 CFHashCode
KeyItem::hash()
1264 CFHashCode result
= 0;
1265 const CSSM_KEY
*cssmKey
= key();
1266 if (NULL
!= cssmKey
)
1268 unsigned char digest
[CC_SHA256_DIGEST_LENGTH
];
1270 CFIndex size_of_data
= sizeof(CSSM_KEYHEADER
) + cssmKey
->KeyData
.Length
;
1272 CFMutableDataRef temp_cfdata
= CFDataCreateMutable(kCFAllocatorDefault
, size_of_data
);
1273 if (NULL
== temp_cfdata
)
1278 CFDataAppendBytes(temp_cfdata
, (const UInt8
*)cssmKey
, sizeof(CSSM_KEYHEADER
));
1279 CFDataAppendBytes(temp_cfdata
, cssmKey
->KeyData
.Data
, cssmKey
->KeyData
.Length
);
1281 if (size_of_data
< 80)
1283 // If it is less than 80 bytes then CFData can be used
1284 result
= CFHash(temp_cfdata
);
1285 CFRelease(temp_cfdata
);
1287 // CFData truncates its hash value to 80 bytes. ????
1288 // In order to do the 'right thing' a SHA 256 hash will be used to
1289 // include all of the data
1292 memset(digest
, 0, CC_SHA256_DIGEST_LENGTH
);
1294 CC_SHA256((const void *)CFDataGetBytePtr(temp_cfdata
), (CC_LONG
)CFDataGetLength(temp_cfdata
), digest
);
1296 CFDataRef data_to_hash
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
,
1297 (const UInt8
*)digest
, CC_SHA256_DIGEST_LENGTH
, kCFAllocatorNull
);
1298 result
= CFHash(data_to_hash
);
1299 CFRelease(data_to_hash
);
1300 CFRelease(temp_cfdata
);
1306 void KeyItem::setIntegrity(bool force
) {
1307 ItemImpl::setIntegrity(*unverifiedKey(), force
);
1310 bool KeyItem::checkIntegrity() {
1311 if(!isPersistent()) {
1316 // key() checks integrity of itself, and throws if there's a problem.
1319 } catch (CssmError cssme
) {
1324 void KeyItem::removeIntegrity(const AccessCredentials
*cred
) {
1325 ItemImpl::removeIntegrity(*key(), cred
);
1328 // KeyItems are a little bit special: the only modifications you can do to them
1329 // is to change their Print Name, Label, or Application Tag.
1331 // When we do this modification, we need to look ahead to see if there's an item
1332 // that's already there. If there are, we're going to throw a errSecDuplicateItem.
1334 // Unless that item doesn't pass the integrity check, in which case we delete it
1335 // and continue with the add.
1336 void KeyItem::modifyUniqueId(Keychain keychain
, SSDb ssDb
, DbUniqueRecord
& uniqueId
, DbAttributes
& newDbAttributes
, CSSM_DB_RECORDTYPE recordType
) {
1337 SSDbCursor
otherDbCursor(ssDb
, 1);
1338 otherDbCursor
->recordType(recordType
);
1340 bool checkForDuplicates
= false;
1341 // Set up the ssdb cursor
1342 CssmDbAttributeData
* label
= newDbAttributes
.findAttribute(kInfoKeyLabel
);
1344 otherDbCursor
->add(CSSM_DB_EQUAL
, kInfoKeyLabel
, label
->at(0));
1345 checkForDuplicates
= true;
1347 CssmDbAttributeData
* apptag
= newDbAttributes
.findAttribute(kInfoKeyApplicationTag
);
1349 otherDbCursor
->add(CSSM_DB_EQUAL
, kInfoKeyApplicationTag
, apptag
->at(0));
1350 checkForDuplicates
= true;
1353 // KeyItems only have integrity if the keychain supports it; otherwise,
1354 // don't pre-check for duplicates
1355 if((!keychain
) || !keychain
->hasIntegrityProtection()) {
1356 secnotice("integrity", "key skipping duplicate integrity check due to keychain version");
1357 checkForDuplicates
= false;
1360 if (checkForDuplicates
) {
1361 secnotice("integrity", "looking for duplicates");
1362 // If there are duplicates that are invalid, delete it and
1363 // continue. Otherwise, if there are duplicates, throw errSecDuplicateItem.
1364 DbAttributes otherDbAttributes
;
1365 DbUniqueRecord otherUniqueId
;
1366 CssmClient::Key otherKey
;
1368 while(otherDbCursor
->nextKey(&otherDbAttributes
, otherKey
, otherUniqueId
)) {
1369 secnotice("integrity", "found a duplicate, checking integrity");
1371 PrimaryKey pk
= keychain
->makePrimaryKey(recordType
, otherUniqueId
);
1373 ItemImpl
* maybeItem
= keychain
->_lookupItem(pk
);
1375 if(maybeItem
->checkIntegrity()) {
1376 secnotice("integrity", "duplicate is real, throwing error");
1377 MacOSError::throwMe(errSecDuplicateItem
);
1379 secnotice("integrity", "existing duplicate item is invalid, removing...");
1380 Item
item(maybeItem
);
1381 keychain
->deleteItem(item
);
1384 KeyItem
temp(keychain
, pk
, otherUniqueId
);
1386 if(temp
.checkIntegrity()) {
1387 secnotice("integrity", "duplicate is real, throwing error");
1388 MacOSError::throwMe(errSecDuplicateItem
);
1390 secnotice("integrity", "duplicate is invalid, removing");
1391 // Keychain's idea of deleting items involves notifications and callbacks. We don't want that,
1392 // (since this isn't a real item and it should go away quietly), so use this roundabout method.
1393 otherUniqueId
->deleteRecord();
1394 keychain
->removeItem(temp
.primaryKey(), &temp
);
1401 secnotice("integrity", "modifying unique id");
1402 uniqueId
->modify(recordType
, &newDbAttributes
, NULL
, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE
);
1403 secnotice("integrity", "done modifying unique id");
1404 } catch(CssmError e
) {
1405 // Just in case something went wrong, clean up after this add
1406 uniqueId
->deleteRecord();