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>
44 // @@@ This needs to be shared.
45 #pragma clang diagnostic push
46 #pragma clang diagnostic ignored "-Wunused-const-variable"
47 static CSSM_DB_NAME_ATTR(kInfoKeyPrintName
, kSecKeyPrintName
, (char*) "PrintName", 0, NULL
, BLOB
);
48 static CSSM_DB_NAME_ATTR(kInfoKeyLabel
, kSecKeyLabel
, (char*) "Label", 0, NULL
, BLOB
);
49 static CSSM_DB_NAME_ATTR(kInfoKeyApplicationTag
, kSecKeyApplicationTag
, (char*) "ApplicationTag", 0, NULL
, BLOB
);
50 #pragma clang diagnostic pop
52 using namespace KeychainCore
;
53 using namespace CssmClient
;
55 KeyItem::KeyItem(const Keychain
&keychain
, const PrimaryKey
&primaryKey
, const CssmClient::DbUniqueRecord
&uniqueId
) :
56 ItemImpl(keychain
, primaryKey
, uniqueId
),
59 mPubKeyHash(Allocator::standard())
63 KeyItem::KeyItem(const Keychain
&keychain
, const PrimaryKey
&primaryKey
) :
64 ItemImpl(keychain
, primaryKey
),
67 mPubKeyHash(Allocator::standard())
71 KeyItem
* KeyItem::make(const Keychain
&keychain
, const PrimaryKey
&primaryKey
, const CssmClient::DbUniqueRecord
&uniqueId
)
73 KeyItem
* k
= new KeyItem(keychain
, primaryKey
, uniqueId
);
74 keychain
->addItem(primaryKey
, k
);
80 KeyItem
* KeyItem::make(const Keychain
&keychain
, const PrimaryKey
&primaryKey
)
82 KeyItem
* k
= new KeyItem(keychain
, primaryKey
);
83 keychain
->addItem(primaryKey
, k
);
89 KeyItem::KeyItem(KeyItem
&keyItem
) :
93 mPubKeyHash(Allocator::standard())
95 // @@@ this doesn't work for keys that are not in a keychain.
98 KeyItem::KeyItem(const CssmClient::Key
&key
) :
99 ItemImpl(key
->keyClass() + CSSM_DL_DB_RECORD_PUBLIC_KEY
, (OSType
)0, (UInt32
)0, (const void*)NULL
),
102 mPubKeyHash(Allocator::standard())
104 if (key
->keyClass() > CSSM_KEYCLASS_SESSION_KEY
)
105 MacOSError::throwMe(errSecParam
);
115 //Create a new CSPDLTransaction
116 Db
db(mKeychain
->database());
117 CSPDLTransaction
transaction(db
);
121 /* Update integrity on key */
124 transaction
.success();
128 KeyItem::copyTo(const Keychain
&keychain
, Access
*newAccess
)
130 if (!(keychain
->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP
))
131 MacOSError::throwMe(errSecInvalidKeychain
);
133 /* Get the destination keychain's db. */
134 SSDbImpl
* dbImpl
= dynamic_cast<SSDbImpl
*>(&(*keychain
->database()));
137 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
142 /* Make sure mKey is valid. */
143 const CSSM_KEY
*cssmKey
= key();
144 if (cssmKey
&& (0==(cssmKey
->KeyHeader
.KeyAttr
& CSSM_KEYATTR_EXTRACTABLE
)))
146 MacOSError::throwMe(errSecDataNotAvailable
);
149 // Generate a random label to use initially
150 CssmClient::CSP
appleCsp(gGuidAppleCSP
);
151 CssmClient::Random
random(appleCsp
, CSSM_ALGID_APPLE_YARROW
);
152 uint8 labelBytes
[20];
153 CssmData
label(labelBytes
, sizeof(labelBytes
));
154 random
.generate(label
, (uint32
)label
.Length
);
156 /* Set up the ACL for the new key. */
157 SecPointer
<Access
> access
;
161 access
= new Access(*mKey
);
163 /* Generate a random 3DES wrapping Key. */
164 CssmClient::GenerateKey
genKey(csp(), CSSM_ALGID_3DES_3KEY
, 192);
165 CssmClient::Key
wrappingKey(genKey(KeySpec(CSSM_KEYUSE_WRAP
| CSSM_KEYUSE_UNWRAP
,
166 CSSM_KEYATTR_EXTRACTABLE
/* | CSSM_KEYATTR_RETURN_DATA */)));
168 /* make a random IV */
170 CssmData
iv(ivBytes
, sizeof(ivBytes
));
171 random
.generate(iv
, (uint32
)iv
.length());
173 /* Extract the key by wrapping it with the wrapping key. */
174 CssmClient::WrapKey
wrap(csp(), CSSM_ALGID_3DES_3KEY_EDE
);
175 wrap
.key(wrappingKey
);
176 wrap
.cred(getCredentials(CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED
, kSecCredentialTypeDefault
));
177 wrap
.mode(CSSM_ALGMODE_ECBPad
);
178 wrap
.padding(CSSM_PADDING_PKCS7
);
180 CssmClient::Key
wrappedKey(wrap(mKey
));
182 /* Unwrap the new key into the new Keychain. */
183 CssmClient::UnwrapKey
unwrap(keychain
->csp(), CSSM_ALGID_3DES_3KEY_EDE
);
184 unwrap
.key(wrappingKey
);
185 unwrap
.mode(CSSM_ALGMODE_ECBPad
);
186 unwrap
.padding(CSSM_PADDING_PKCS7
);
187 unwrap
.initVector(iv
);
189 /* Setup the dldbHandle in the context. */
190 unwrap
.add(CSSM_ATTRIBUTE_DL_DB_HANDLE
, ssDb
->handle());
192 /* Set up an initial aclEntry so we can change it after the unwrap. */
193 Access::Maker
maker(Allocator::standard(), Access::Maker::kAnyMakerType
);
194 ResourceControlContext rcc
;
195 maker
.initialOwner(rcc
, NULL
);
196 unwrap
.owner(rcc
.input());
198 /* Unwrap the key. */
199 uint32 usage
= mKey
->usage();
200 /* Work around csp brokeness where it sets all usage bits in the Keyheader when CSSM_KEYUSE_ANY is set. */
201 if (usage
& CSSM_KEYUSE_ANY
)
202 usage
= CSSM_KEYUSE_ANY
;
204 CssmClient::Key
unwrappedKey(unwrap(wrappedKey
, KeySpec(usage
,
205 (mKey
->attributes() | CSSM_KEYATTR_PERMANENT
) & ~(CSSM_KEYATTR_ALWAYS_SENSITIVE
| CSSM_KEYATTR_NEVER_EXTRACTABLE
),
208 /* Look up unwrapped key in the DLDB. */
209 DbUniqueRecord uniqueId
;
210 SSDbCursor
dbCursor(ssDb
, 1);
211 dbCursor
->recordType(recordType());
212 dbCursor
->add(CSSM_DB_EQUAL
, kInfoKeyLabel
, label
);
213 CssmClient::Key copiedKey
;
214 if (!dbCursor
->nextKey(NULL
, copiedKey
, uniqueId
))
215 MacOSError::throwMe(errSecItemNotFound
);
217 /* Copy the Label, PrintName and ApplicationTag attributes from the old key to the new one. */
219 DbAttributes
oldDbAttributes(mUniqueId
->database(), 3);
220 oldDbAttributes
.add(kInfoKeyLabel
);
221 oldDbAttributes
.add(kInfoKeyPrintName
);
222 oldDbAttributes
.add(kInfoKeyApplicationTag
);
223 mUniqueId
->get(&oldDbAttributes
, NULL
);
226 uniqueId
->modify(recordType(), &oldDbAttributes
, NULL
, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE
);
230 // clean up after trying to insert a duplicate key
231 uniqueId
->deleteRecord ();
235 /* Set the acl and owner on the unwrapped key. */
236 addIntegrity(*access
);
237 access
->setAccess(*unwrappedKey
, maker
);
239 /* Return a keychain item which represents the new key. */
240 Item
item(keychain
->item(recordType(), uniqueId
));
242 KCEventNotifier::PostKeychainEvent(kSecAddEvent
, keychain
, item
);
248 KeyItem::importTo(const Keychain
&keychain
, Access
*newAccess
, SecKeychainAttributeList
*attrList
)
250 if (!(keychain
->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP
))
251 MacOSError::throwMe(errSecInvalidKeychain
);
253 /* Get the destination keychain's db. */
254 SSDbImpl
* dbImpl
= dynamic_cast<SSDbImpl
*>(&(*keychain
->database()));
256 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
260 /* Make sure mKey is valid. */
261 /* We can't call key() here, since we won't have a unique record id yet */
263 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
265 // Generate a random label to use initially
266 CssmClient::CSP
appleCsp(gGuidAppleCSP
);
267 CssmClient::Random
random(appleCsp
, CSSM_ALGID_APPLE_YARROW
);
268 uint8 labelBytes
[20];
269 CssmData
label(labelBytes
, sizeof(labelBytes
));
270 random
.generate(label
, (uint32
)label
.Length
);
272 /* Set up the ACL for the new key. */
273 SecPointer
<Access
> access
;
277 access
= new Access(*mKey
);
279 /* Generate a random 3DES wrapping Key. */
280 CssmClient::GenerateKey
genKey(csp(), CSSM_ALGID_3DES_3KEY
, 192);
281 CssmClient::Key
wrappingKey(genKey(KeySpec(CSSM_KEYUSE_WRAP
| CSSM_KEYUSE_UNWRAP
,
282 CSSM_KEYATTR_EXTRACTABLE
/* | CSSM_KEYATTR_RETURN_DATA */)));
284 /* make a random IV */
286 CssmData
iv(ivBytes
, sizeof(ivBytes
));
287 random
.generate(iv
, (uint32
)iv
.length());
289 /* Extract the key by wrapping it with the wrapping key. */
290 CssmClient::WrapKey
wrap(csp(), CSSM_ALGID_3DES_3KEY_EDE
);
291 wrap
.key(wrappingKey
);
292 wrap
.cred(getCredentials(CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED
, kSecCredentialTypeDefault
));
293 wrap
.mode(CSSM_ALGMODE_ECBPad
);
294 wrap
.padding(CSSM_PADDING_PKCS7
);
296 CssmClient::Key
wrappedKey(wrap(mKey
));
298 /* Unwrap the new key into the new Keychain. */
299 CssmClient::UnwrapKey
unwrap(keychain
->csp(), CSSM_ALGID_3DES_3KEY_EDE
);
300 unwrap
.key(wrappingKey
);
301 unwrap
.mode(CSSM_ALGMODE_ECBPad
);
302 unwrap
.padding(CSSM_PADDING_PKCS7
);
303 unwrap
.initVector(iv
);
305 /* Setup the dldbHandle in the context. */
306 unwrap
.add(CSSM_ATTRIBUTE_DL_DB_HANDLE
, ssDb
->handle());
308 /* Set up an initial aclEntry so we can change it after the unwrap. */
309 Access::Maker
maker(Allocator::standard(), Access::Maker::kAnyMakerType
);
310 ResourceControlContext rcc
;
311 maker
.initialOwner(rcc
, NULL
);
312 unwrap
.owner(rcc
.input());
314 /* Unwrap the key. */
315 uint32 usage
= mKey
->usage();
316 /* Work around csp brokeness where it sets all usage bits in the Keyheader when CSSM_KEYUSE_ANY is set. */
317 if (usage
& CSSM_KEYUSE_ANY
)
318 usage
= CSSM_KEYUSE_ANY
;
320 CssmClient::Key
unwrappedKey(unwrap(wrappedKey
, KeySpec(usage
,
321 (mKey
->attributes() | CSSM_KEYATTR_PERMANENT
) & ~(CSSM_KEYATTR_ALWAYS_SENSITIVE
| CSSM_KEYATTR_NEVER_EXTRACTABLE
),
324 /* Look up unwrapped key in the DLDB. */
325 DbUniqueRecord uniqueId
;
326 SSDbCursor
dbCursor(ssDb
, 1);
327 dbCursor
->recordType(recordType());
328 dbCursor
->add(CSSM_DB_EQUAL
, kInfoKeyLabel
, label
);
329 CssmClient::Key copiedKey
;
330 if (!dbCursor
->nextKey(NULL
, copiedKey
, uniqueId
))
331 MacOSError::throwMe(errSecItemNotFound
);
333 // Set the initial label, application label, and application tag (if provided)
335 DbAttributes newDbAttributes
;
337 for (UInt32 index
=0; index
< attrList
->count
; index
++) {
338 SecKeychainAttribute attr
= attrList
->attr
[index
];
339 CssmData
attrData(attr
.data
, attr
.length
);
340 if (attr
.tag
== kSecKeyPrintName
) {
341 newDbAttributes
.add(kInfoKeyPrintName
, attrData
);
343 if (attr
.tag
== kSecKeyLabel
) {
344 newDbAttributes
.add(kInfoKeyLabel
, attrData
);
346 if (attr
.tag
== kSecKeyApplicationTag
) {
347 newDbAttributes
.add(kInfoKeyApplicationTag
, attrData
);
351 modifyUniqueId(keychain
, ssDb
, uniqueId
, newDbAttributes
, recordType());
354 /* Set the acl and owner on the unwrapped key. */
355 addIntegrity(*access
);
356 access
->setAccess(*unwrappedKey
, maker
);
358 /* Return a keychain item which represents the new key. */
359 Item
item(keychain
->item(recordType(), uniqueId
));
361 KCEventNotifier::PostKeychainEvent(kSecAddEvent
, keychain
, item
);
372 KeyItem::add(Keychain
&keychain
)
374 MacOSError::throwMe(errSecUnimplemented
);
377 CssmClient::SSDbUniqueRecord
378 KeyItem::ssDbUniqueRecord()
380 DbUniqueRecordImpl
*impl
= &*dbUniqueRecord();
381 Security::CssmClient::SSDbUniqueRecordImpl
*simpl
= dynamic_cast<Security::CssmClient::SSDbUniqueRecordImpl
*>(impl
);
384 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
387 return CssmClient::SSDbUniqueRecord(simpl
);
393 StLock
<Mutex
>_(mMutex
);
396 CssmClient::SSDbUniqueRecord
uniqueId(ssDbUniqueRecord());
397 CssmDataContainer
dataBlob(uniqueId
->allocator());
398 uniqueId
->get(NULL
, &dataBlob
);
399 mKey
= CssmClient::Key(uniqueId
->database()->csp(), *reinterpret_cast<CssmKey
*>(dataBlob
.Data
));
412 const CSSM_X509_ALGORITHM_IDENTIFIER
&
413 KeyItem::algorithmIdentifier()
417 CSSM_KEY_TYPE algorithm
418 CSSM_KEY_PTR cssmKey
= (CSSM_KEY_PTR
)thisData
->Data
;
420 static void printKeyHeader(
421 const CSSM_KEYHEADER
&hdr
)
423 printf(" Algorithm : ");
424 switch(hdr
.AlgorithmId
) {
425 CSSM_X509_ALGORITHM_IDENTIFIER algID
;
427 CSSM_OID
*CL_algToOid(
428 CSSM_ALGORITHMS algId
)
429 typedef struct cssm_x509_algorithm_identifier
{
431 CSSM_DATA parameters
;
432 } CSSM_X509_ALGORITHM_IDENTIFIER
, *CSSM_X509_ALGORITHM_IDENTIFIER_PTR
;
439 * itemID, used to locate Extended Attributes, is the public key hash for keys.
441 const CssmData
&KeyItem::itemID()
443 if(mPubKeyHash
.length() == 0) {
445 * Fetch the attribute from disk.
447 UInt32 tag
= kSecKeyLabel
;
449 SecKeychainAttributeInfo attrInfo
= {1, &tag
, &format
};
450 SecKeychainAttributeList
*attrList
= NULL
;
451 getAttributesAndData(&attrInfo
, NULL
, &attrList
, NULL
, NULL
);
452 if((attrList
== NULL
) || (attrList
->count
!= 1)) {
453 MacOSError::throwMe(errSecNoSuchAttr
);
455 mPubKeyHash
.copy(attrList
->attr
->data
, attrList
->attr
->length
);
456 freeAttributesAndData(attrList
, NULL
);
463 KeyItem::strengthInBits(const CSSM_X509_ALGORITHM_IDENTIFIER
*algid
)
465 // @@@ Make a context with key based on algid and use that to get the effective keysize and not just the logical one.
466 CSSM_KEY_SIZE keySize
= {};
467 CSSM_RETURN rv
= CSSM_QueryKeySizeInBits (csp()->handle(),
474 return keySize
.LogicalKeySizeInBits
;
477 const AccessCredentials
*
478 KeyItem::getCredentials(
479 CSSM_ACL_AUTHORIZATION_TAG operation
,
480 SecCredentialType credentialType
)
482 // @@@ Fix this to actually examine the ACL for this key and consider operation and do the right thing.
483 //AutoAclEntryInfoList aclInfos;
484 //key()->getAcl(aclInfos);
486 bool smartcard
= keychain() != NULL
? (keychain()->database()->dl()->guid() == gGuidAppleSdCSPDL
) : false;
489 switch (credentialType
)
491 case kSecCredentialTypeDefault
:
492 return smartcard
?globals().smartcardItemCredentials():globals().itemCredentials();
493 case kSecCredentialTypeWithUI
:
494 return smartcard
?globals().smartcardItemCredentials():factory
.promptCred();
495 case kSecCredentialTypeNoUI
:
496 return factory
.nullCred();
498 MacOSError::throwMe(errSecParam
);
503 KeyItem::operator == (KeyItem
&other
)
508 return this == &other
;
511 // If keychains are different, then keys are different
512 Keychain otherKeychain
= other
.keychain();
513 return (mKeychain
&& otherKeychain
&& (*mKeychain
== *otherKeychain
));
519 CSSM_ALGORITHMS algorithm
,
520 uint32 keySizeInBits
,
521 CSSM_CC_HANDLE contextHandle
,
522 CSSM_KEYUSE publicKeyUsage
,
523 uint32 publicKeyAttr
,
524 CSSM_KEYUSE privateKeyUsage
,
525 uint32 privateKeyAttr
,
526 SecPointer
<Access
> initialAccess
,
527 SecPointer
<KeyItem
> &outPublicKey
,
528 SecPointer
<KeyItem
> &outPrivateKey
)
530 bool freeKeys
= false;
531 bool deleteContext
= false;
533 if (!(keychain
->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP
))
534 MacOSError::throwMe(errSecInvalidKeychain
);
536 SSDbImpl
* impl
= dynamic_cast<SSDbImpl
*>(&(*keychain
->database()));
539 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
543 CssmClient::CSP
csp(keychain
->csp());
544 CssmClient::CSP
appleCsp(gGuidAppleCSP
);
546 // Generate a random label to use initially
547 CssmClient::Random
random(appleCsp
, CSSM_ALGID_APPLE_YARROW
);
548 uint8 labelBytes
[20];
549 CssmData
label(labelBytes
, sizeof(labelBytes
));
550 random
.generate(label
, (uint32
)label
.Length
);
552 // Create a Access::Maker for the initial owner of the private key.
553 ResourceControlContext rcc
;
554 memset(&rcc
, 0, sizeof(rcc
));
556 // @@@ 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.
557 maker
.initialOwner(rcc
);
558 // Create the cred we need to manipulate the keys until we actually set a new access control for them.
559 const AccessCredentials
*cred
= maker
.cred();
561 CSSM_KEY publicCssmKey
, privateCssmKey
;
562 memset(&publicCssmKey
, 0, sizeof(publicCssmKey
));
563 memset(&privateCssmKey
, 0, sizeof(privateCssmKey
));
565 CSSM_CC_HANDLE ccHandle
= 0;
567 SecPointer
<KeyItem
> publicKeyItem
, privateKeyItem
;
572 ccHandle
= contextHandle
;
575 status
= CSSM_CSP_CreateKeyGenContext(csp
->handle(), algorithm
, keySizeInBits
, NULL
, NULL
, NULL
, NULL
, NULL
, &ccHandle
);
577 CssmError::throwMe(status
);
578 deleteContext
= true;
581 CSSM_DL_DB_HANDLE dldbHandle
= ssDb
->handle();
582 CSSM_DL_DB_HANDLE_PTR dldbHandlePtr
= &dldbHandle
;
583 CSSM_CONTEXT_ATTRIBUTE contextAttributes
= { CSSM_ATTRIBUTE_DL_DB_HANDLE
, sizeof(dldbHandle
), { (char *)dldbHandlePtr
} };
584 status
= CSSM_UpdateContextAttributes(ccHandle
, 1, &contextAttributes
);
586 CssmError::throwMe(status
);
588 // Generate the keypair
589 status
= CSSM_GenerateKeyPair(ccHandle
, publicKeyUsage
, publicKeyAttr
, &label
, &publicCssmKey
, privateKeyUsage
, privateKeyAttr
, &label
, &rcc
, &privateCssmKey
);
591 CssmError::throwMe(status
);
594 // Find the keys we just generated in the DL to get SecKeyRef's to them
595 // so we can change the label to be the hash of the public key, and
596 // fix up other attributes.
598 // Look up public key in the DLDB.
599 DbAttributes pubDbAttributes
;
600 DbUniqueRecord pubUniqueId
;
601 SSDbCursor
dbPubCursor(ssDb
, 1);
602 dbPubCursor
->recordType(CSSM_DL_DB_RECORD_PUBLIC_KEY
);
603 dbPubCursor
->add(CSSM_DB_EQUAL
, kInfoKeyLabel
, label
);
604 CssmClient::Key publicKey
;
605 if (!dbPubCursor
->nextKey(&pubDbAttributes
, publicKey
, pubUniqueId
))
606 MacOSError::throwMe(errSecItemNotFound
);
608 // Look up private key in the DLDB.
609 DbAttributes privDbAttributes
;
610 DbUniqueRecord privUniqueId
;
611 SSDbCursor
dbPrivCursor(ssDb
, 1);
612 dbPrivCursor
->recordType(CSSM_DL_DB_RECORD_PRIVATE_KEY
);
613 dbPrivCursor
->add(CSSM_DB_EQUAL
, kInfoKeyLabel
, label
);
614 CssmClient::Key privateKey
;
615 if (!dbPrivCursor
->nextKey(&privDbAttributes
, privateKey
, privUniqueId
))
616 MacOSError::throwMe(errSecItemNotFound
);
618 // Convert reference public key to a raw key so we can use it
620 CssmClient::WrapKey
wrap(csp
, CSSM_ALGID_NONE
);
622 CssmClient::Key rawPubKey
= wrap(publicKey
);
624 // Calculate the hash of the public key using the appleCSP.
625 CssmClient::PassThrough
passThrough(appleCsp
);
629 /* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the
630 * associated key blob.
631 * Key is specified in CSSM_CSP_CreatePassThroughContext.
632 * Hash is allocated bythe CSP, in the App's memory, and returned
634 passThrough
.key(rawPubKey
);
635 passThrough(CSSM_APPLECSP_KEYDIGEST
, NULL
, &outData
);
636 cssmData
= reinterpret_cast<CssmData
*>(outData
);
637 CssmData
&pubKeyHash
= *cssmData
;
639 auto_ptr
<string
>privDescription
;
640 auto_ptr
<string
>pubDescription
;
642 privDescription
.reset(new string(initialAccess
->promptDescription()));
643 pubDescription
.reset(new string(initialAccess
->promptDescription()));
646 /* this path taken if no promptDescription available, e.g., for complex ACLs */
647 privDescription
.reset(new string("Private key"));
648 pubDescription
.reset(new string("Public key"));
651 // Set the label of the public key to the public key hash.
652 // Set the PrintName of the public key to the description in the acl.
653 pubDbAttributes
.add(kInfoKeyLabel
, pubKeyHash
);
654 pubDbAttributes
.add(kInfoKeyPrintName
, *pubDescription
);
655 modifyUniqueId(keychain
, ssDb
, pubUniqueId
, pubDbAttributes
, CSSM_DL_DB_RECORD_PUBLIC_KEY
);
657 // Set the label of the private key to the public key hash.
658 // Set the PrintName of the private key to the description in the acl.
659 privDbAttributes
.add(kInfoKeyLabel
, pubKeyHash
);
660 privDbAttributes
.add(kInfoKeyPrintName
, *privDescription
);
661 modifyUniqueId(keychain
, ssDb
, privUniqueId
, privDbAttributes
, CSSM_DL_DB_RECORD_PRIVATE_KEY
);
663 // @@@ Not exception safe!
664 csp
.allocator().free(cssmData
->Data
);
665 csp
.allocator().free(cssmData
);
667 // Create keychain items which will represent the keys.
668 publicKeyItem
= dynamic_cast<KeyItem
*>(keychain
->item(CSSM_DL_DB_RECORD_PUBLIC_KEY
, pubUniqueId
).get());
669 privateKeyItem
= dynamic_cast<KeyItem
*>(keychain
->item(CSSM_DL_DB_RECORD_PRIVATE_KEY
, privUniqueId
).get());
671 if (!publicKeyItem
|| !privateKeyItem
)
673 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
676 // Finally fix the acl and owner of the private key to the specified access control settings.
677 privateKeyItem
->addIntegrity(*initialAccess
);
678 initialAccess
->setAccess(*privateKey
, maker
);
680 if(publicKeyAttr
& CSSM_KEYATTR_PUBLIC_KEY_ENCRYPT
) {
682 * Make the public key acl completely open.
683 * If the key was not encrypted, it already has a wide-open
684 * ACL (though that is a feature of securityd; it's not
685 * CDSA-specified behavior).
687 SecPointer
<Access
> pubKeyAccess(new Access());
688 publicKeyItem
->addIntegrity(*pubKeyAccess
);
689 pubKeyAccess
->setAccess(*publicKey
, maker
);
692 outPublicKey
= publicKeyItem
;
693 outPrivateKey
= privateKeyItem
;
699 // Delete the keys if something goes wrong so we don't end up with inaccessible keys in the database.
700 CSSM_FreeKey(csp
->handle(), cred
, &publicCssmKey
, TRUE
);
701 CSSM_FreeKey(csp
->handle(), cred
, &privateCssmKey
, TRUE
);
705 CSSM_DeleteContext(ccHandle
);
712 CSSM_FreeKey(csp
->handle(), NULL
, &publicCssmKey
, FALSE
);
713 CSSM_FreeKey(csp
->handle(), NULL
, &privateCssmKey
, FALSE
);
717 CSSM_DeleteContext(ccHandle
);
719 if (keychain
&& publicKeyItem
&& privateKeyItem
)
721 keychain
->postEvent(kSecAddEvent
, publicKeyItem
);
722 keychain
->postEvent(kSecAddEvent
, privateKeyItem
);
729 const CSSM_KEY
&publicWrappedKey
,
730 const CSSM_KEY
&privateWrappedKey
,
731 SecPointer
<Access
> initialAccess
,
732 SecPointer
<KeyItem
> &outPublicKey
,
733 SecPointer
<KeyItem
> &outPrivateKey
)
735 bool freePublicKey
= false;
736 bool freePrivateKey
= false;
737 bool deleteContext
= false;
739 if (!(keychain
->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP
))
740 MacOSError::throwMe(errSecInvalidKeychain
);
742 SSDbImpl
* impl
= dynamic_cast<SSDbImpl
*>(&(*keychain
->database()));
745 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
749 CssmClient::CSP
csp(keychain
->csp());
750 CssmClient::CSP
appleCsp(gGuidAppleCSP
);
752 // Create a Access::Maker for the initial owner of the private key.
753 ResourceControlContext rcc
;
754 memset(&rcc
, 0, sizeof(rcc
));
755 Access::Maker
maker(Allocator::standard(), Access::Maker::kAnyMakerType
);
756 // @@@ Potentially provide a credential argument which allows us to unwrap keys in the csp.
757 // Currently the CSP lets anyone do this, but we might restrict this in the future, e.g.
758 // a smartcard could require out of band pin entry before a key can be generated.
759 maker
.initialOwner(rcc
);
760 // Create the cred we need to manipulate the keys until we actually set a new access control for them.
761 const AccessCredentials
*cred
= maker
.cred();
763 CSSM_KEY publicCssmKey
, privateCssmKey
;
764 memset(&publicCssmKey
, 0, sizeof(publicCssmKey
));
765 memset(&privateCssmKey
, 0, sizeof(privateCssmKey
));
767 CSSM_CC_HANDLE ccHandle
= 0;
769 SecPointer
<KeyItem
> publicKeyItem
, privateKeyItem
;
774 // Calculate the hash of the public key using the appleCSP.
775 CssmClient::PassThrough
passThrough(appleCsp
);
779 /* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the
780 * associated key blob.
781 * Key is specified in CSSM_CSP_CreatePassThroughContext.
782 * Hash is allocated bythe CSP, in the App's memory, and returned
784 passThrough
.key(&publicWrappedKey
);
785 passThrough(CSSM_APPLECSP_KEYDIGEST
, NULL
, &outData
);
786 cssmData
= reinterpret_cast<CssmData
*>(outData
);
787 CssmData
&pubKeyHash
= *cssmData
;
789 status
= CSSM_CSP_CreateSymmetricContext(csp
->handle(), publicWrappedKey
.KeyHeader
.WrapAlgorithmId
, CSSM_ALGMODE_NONE
, NULL
, NULL
, NULL
, CSSM_PADDING_NONE
, NULL
, &ccHandle
);
791 CssmError::throwMe(status
);
792 deleteContext
= true;
794 CSSM_DL_DB_HANDLE dldbHandle
= ssDb
->handle();
795 CSSM_DL_DB_HANDLE_PTR dldbHandlePtr
= &dldbHandle
;
796 CSSM_CONTEXT_ATTRIBUTE contextAttributes
= { CSSM_ATTRIBUTE_DL_DB_HANDLE
, sizeof(dldbHandle
), { (char *)dldbHandlePtr
} };
797 status
= CSSM_UpdateContextAttributes(ccHandle
, 1, &contextAttributes
);
799 CssmError::throwMe(status
);
801 // Unwrap the the keys
802 CSSM_DATA descriptiveData
= {0, NULL
};
804 status
= CSSM_UnwrapKey(
808 publicWrappedKey
.KeyHeader
.KeyUsage
,
809 publicWrappedKey
.KeyHeader
.KeyAttr
| CSSM_KEYATTR_PERMANENT
,
816 CssmError::throwMe(status
);
817 freePublicKey
= true;
819 if (descriptiveData
.Data
!= NULL
)
820 free (descriptiveData
.Data
);
822 status
= CSSM_UnwrapKey(
826 privateWrappedKey
.KeyHeader
.KeyUsage
,
827 privateWrappedKey
.KeyHeader
.KeyAttr
| CSSM_KEYATTR_PERMANENT
,
834 CssmError::throwMe(status
);
836 if (descriptiveData
.Data
!= NULL
)
837 free (descriptiveData
.Data
);
839 freePrivateKey
= true;
841 // Find the keys we just generated in the DL to get SecKeyRefs to them
842 // so we can change the label to be the hash of the public key, and
843 // fix up other attributes.
845 // Look up public key in the DLDB.
846 DbAttributes pubDbAttributes
;
847 DbUniqueRecord pubUniqueId
;
848 SSDbCursor
dbPubCursor(ssDb
, 1);
849 dbPubCursor
->recordType(CSSM_DL_DB_RECORD_PUBLIC_KEY
);
850 dbPubCursor
->add(CSSM_DB_EQUAL
, kInfoKeyLabel
, pubKeyHash
);
851 CssmClient::Key publicKey
;
852 if (!dbPubCursor
->nextKey(&pubDbAttributes
, publicKey
, pubUniqueId
))
853 MacOSError::throwMe(errSecItemNotFound
);
855 // Look up private key in the DLDB.
856 DbAttributes privDbAttributes
;
857 DbUniqueRecord privUniqueId
;
858 SSDbCursor
dbPrivCursor(ssDb
, 1);
859 dbPrivCursor
->recordType(CSSM_DL_DB_RECORD_PRIVATE_KEY
);
860 dbPrivCursor
->add(CSSM_DB_EQUAL
, kInfoKeyLabel
, pubKeyHash
);
861 CssmClient::Key privateKey
;
862 if (!dbPrivCursor
->nextKey(&privDbAttributes
, privateKey
, privUniqueId
))
863 MacOSError::throwMe(errSecItemNotFound
);
865 // @@@ Not exception safe!
866 csp
.allocator().free(cssmData
->Data
);
867 csp
.allocator().free(cssmData
);
869 auto_ptr
<string
>privDescription
;
870 auto_ptr
<string
>pubDescription
;
872 privDescription
.reset(new string(initialAccess
->promptDescription()));
873 pubDescription
.reset(new string(initialAccess
->promptDescription()));
876 /* this path taken if no promptDescription available, e.g., for complex ACLs */
877 privDescription
.reset(new string("Private key"));
878 pubDescription
.reset(new string("Public key"));
881 // Set the label of the public key to the public key hash.
882 // Set the PrintName of the public key to the description in the acl.
883 pubDbAttributes
.add(kInfoKeyPrintName
, *pubDescription
);
884 modifyUniqueId(keychain
, ssDb
, pubUniqueId
, pubDbAttributes
, CSSM_DL_DB_RECORD_PUBLIC_KEY
);
886 // Set the label of the private key to the public key hash.
887 // Set the PrintName of the private key to the description in the acl.
888 privDbAttributes
.add(kInfoKeyPrintName
, *privDescription
);
889 modifyUniqueId(keychain
, ssDb
, privUniqueId
, privDbAttributes
, CSSM_DL_DB_RECORD_PRIVATE_KEY
);
891 // Create keychain items which will represent the keys.
892 publicKeyItem
= dynamic_cast<KeyItem
*>(keychain
->item(CSSM_DL_DB_RECORD_PUBLIC_KEY
, pubUniqueId
).get());
893 privateKeyItem
= dynamic_cast<KeyItem
*>(keychain
->item(CSSM_DL_DB_RECORD_PRIVATE_KEY
, privUniqueId
).get());
895 if (!publicKeyItem
|| !privateKeyItem
)
897 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
900 // Finally fix the acl and owner of the private key to the specified access control settings.
901 privateKeyItem
->addIntegrity(*initialAccess
);
902 initialAccess
->setAccess(*privateKey
, maker
);
904 // Make the public key acl completely open
905 SecPointer
<Access
> pubKeyAccess(new Access());
906 publicKeyItem
->addIntegrity(*pubKeyAccess
);
907 pubKeyAccess
->setAccess(*publicKey
, maker
);
909 outPublicKey
= publicKeyItem
;
910 outPrivateKey
= privateKeyItem
;
915 CSSM_FreeKey(csp
->handle(), cred
, &publicCssmKey
, TRUE
);
917 CSSM_FreeKey(csp
->handle(), cred
, &privateCssmKey
, TRUE
);
920 CSSM_DeleteContext(ccHandle
);
926 CSSM_FreeKey(csp
->handle(), cred
, &publicCssmKey
, FALSE
);
928 CSSM_FreeKey(csp
->handle(), cred
, &privateCssmKey
, FALSE
);
931 CSSM_DeleteContext(ccHandle
);
933 if (keychain
&& publicKeyItem
&& privateKeyItem
)
935 KCEventNotifier::PostKeychainEvent(kSecAddEvent
, keychain
, Item(publicKeyItem
));
936 KCEventNotifier::PostKeychainEvent(kSecAddEvent
, keychain
, Item(privateKeyItem
));
941 KeyItem::generateWithAttributes(const SecKeychainAttributeList
*attrList
,
943 CSSM_ALGORITHMS algorithm
,
944 uint32 keySizeInBits
,
945 CSSM_CC_HANDLE contextHandle
,
946 CSSM_KEYUSE keyUsage
,
948 SecPointer
<Access
> initialAccess
)
950 CssmClient::CSP
appleCsp(gGuidAppleCSP
);
951 CssmClient::CSP
csp(NULL
);
953 uint8 labelBytes
[20];
954 CssmData
label(labelBytes
, sizeof(labelBytes
));
955 bool freeKey
= false;
956 bool deleteContext
= false;
957 const CSSM_DATA
*plabel
= NULL
;
961 if (!(keychain
->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP
))
962 MacOSError::throwMe(errSecInvalidKeychain
);
964 SSDbImpl
* impl
= dynamic_cast<SSDbImpl
*>(&(*keychain
->database()));
967 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
971 csp
= keychain
->csp();
973 // Generate a random label to use initially
974 CssmClient::Random
random(appleCsp
, CSSM_ALGID_APPLE_YARROW
);
975 random
.generate(label
, (uint32
)label
.Length
);
980 // Not a persistent key so create it in the regular csp
984 // Create a Access::Maker for the initial owner of the private key.
985 ResourceControlContext
*prcc
= NULL
, rcc
;
986 const AccessCredentials
*cred
= NULL
;
993 memset(&rcc
, 0, sizeof(rcc
));
994 // @@@ Potentially provide a credential argument which allows us to generate keys in the csp.
995 // Currently the CSP lets anyone do this, but we might restrict this in the future, e.g. a smartcard
996 // could require out-of-band pin entry before a key can be generated.
997 maker
.initialOwner(rcc
);
998 // Create the cred we need to manipulate the keys until we actually set a new access control for them.
1002 if (!initialAccess
) {
1003 // We don't have an access, but we need to set integrity. Make an Access.
1004 initialAccess
= new Access(string(label
));
1010 CSSM_CC_HANDLE ccHandle
= 0;
1012 SecPointer
<KeyItem
> keyItem
;
1017 ccHandle
= contextHandle
;
1020 status
= CSSM_CSP_CreateKeyGenContext(csp
->handle(), algorithm
, keySizeInBits
, NULL
, NULL
, NULL
, NULL
, NULL
, &ccHandle
);
1022 CssmError::throwMe(status
);
1023 deleteContext
= true;
1028 CSSM_DL_DB_HANDLE dldbHandle
= ssDb
->handle();
1029 CSSM_DL_DB_HANDLE_PTR dldbHandlePtr
= &dldbHandle
;
1030 CSSM_CONTEXT_ATTRIBUTE contextAttributes
= { CSSM_ATTRIBUTE_DL_DB_HANDLE
, sizeof(dldbHandle
), { (char *)dldbHandlePtr
} };
1031 status
= CSSM_UpdateContextAttributes(ccHandle
, 1, &contextAttributes
);
1033 CssmError::throwMe(status
);
1035 keyAttr
|= CSSM_KEYATTR_PERMANENT
;
1039 status
= CSSM_GenerateKey(ccHandle
, keyUsage
, keyAttr
, plabel
, prcc
, &cssmKey
);
1041 CssmError::throwMe(status
);
1046 // Find the key we just generated in the DL and get a SecKeyRef
1047 // so we can specify the label attribute(s) and initial ACL.
1049 // Look up key in the DLDB.
1050 DbAttributes dbAttributes
;
1051 DbUniqueRecord uniqueId
;
1052 SSDbCursor
dbCursor(ssDb
, 1);
1053 dbCursor
->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY
);
1054 dbCursor
->add(CSSM_DB_EQUAL
, kInfoKeyLabel
, label
);
1055 CssmClient::Key key
;
1056 if (!dbCursor
->nextKey(&dbAttributes
, key
, uniqueId
))
1057 MacOSError::throwMe(errSecItemNotFound
);
1059 // Set the initial label, application label, and application tag (if provided)
1061 DbAttributes newDbAttributes
;
1063 for (UInt32 index
=0; index
< attrList
->count
; index
++) {
1064 SecKeychainAttribute attr
= attrList
->attr
[index
];
1065 CssmData
attrData(attr
.data
, attr
.length
);
1066 if (attr
.tag
== kSecKeyPrintName
) {
1067 newDbAttributes
.add(kInfoKeyPrintName
, attrData
);
1069 if (attr
.tag
== kSecKeyLabel
) {
1070 newDbAttributes
.add(kInfoKeyLabel
, attrData
);
1072 if (attr
.tag
== kSecKeyApplicationTag
) {
1073 newDbAttributes
.add(kInfoKeyApplicationTag
, attrData
);
1077 modifyUniqueId(keychain
, ssDb
, uniqueId
, newDbAttributes
, CSSM_DL_DB_RECORD_SYMMETRIC_KEY
);
1080 // Create keychain item which will represent the key.
1081 keyItem
= dynamic_cast<KeyItem
*>(keychain
->item(CSSM_DL_DB_RECORD_SYMMETRIC_KEY
, uniqueId
).get());
1083 // Finally, fix the acl and owner of the key to the specified access control settings.
1084 keyItem
->addIntegrity(*initialAccess
);
1085 initialAccess
->setAccess(*key
, maker
);
1089 CssmClient::Key
tempKey(csp
, cssmKey
);
1090 keyItem
= new KeyItem(tempKey
);
1097 // Delete the key if something goes wrong so we don't end up with inaccessible keys in the database.
1098 CSSM_FreeKey(csp
->handle(), cred
, &cssmKey
, TRUE
);
1102 CSSM_DeleteContext(ccHandle
);
1109 CSSM_FreeKey(csp
->handle(), NULL
, &cssmKey
, FALSE
);
1113 CSSM_DeleteContext(ccHandle
);
1115 if (keychain
&& keyItem
)
1116 keychain
->postEvent(kSecAddEvent
, keyItem
);
1118 KeyItem
* item
= dynamic_cast<KeyItem
*>(&*keyItem
);
1121 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
1128 KeyItem::generate(Keychain keychain
,
1129 CSSM_ALGORITHMS algorithm
,
1130 uint32 keySizeInBits
,
1131 CSSM_CC_HANDLE contextHandle
,
1132 CSSM_KEYUSE keyUsage
,
1134 SecPointer
<Access
> initialAccess
)
1136 return KeyItem::generateWithAttributes(NULL
, keychain
,
1137 algorithm
, keySizeInBits
, contextHandle
,
1138 keyUsage
, keyAttr
, initialAccess
);
1142 void KeyItem::RawSign(SecPadding padding
, CSSM_DATA dataToSign
, const AccessCredentials
*credentials
, CSSM_DATA
& signature
)
1144 CSSM_ALGORITHMS baseAlg
= key()->header().algorithm();
1146 if ((baseAlg
!= CSSM_ALGID_RSA
) && (baseAlg
!= CSSM_ALGID_ECDSA
))
1148 MacOSError::throwMe(errSecParam
);
1151 CSSM_ALGORITHMS paddingAlg
= CSSM_PADDING_PKCS1
;
1155 case kSecPaddingPKCS1
:
1157 paddingAlg
= CSSM_PADDING_PKCS1
;
1161 case kSecPaddingPKCS1MD2
:
1163 baseAlg
= CSSM_ALGID_MD2WithRSA
;
1167 case kSecPaddingPKCS1MD5
:
1169 baseAlg
= CSSM_ALGID_MD5WithRSA
;
1173 case kSecPaddingPKCS1SHA1
:
1175 baseAlg
= CSSM_ALGID_SHA1WithRSA
;
1179 case kSecPaddingSigRaw
:
1181 paddingAlg
= CSSM_PADDING_SIGRAW
;
1187 paddingAlg
= CSSM_PADDING_NONE
;
1192 Sign
signContext(csp(), baseAlg
);
1193 signContext
.key(key());
1194 signContext
.cred(credentials
);
1195 // Fields required for CSSM_CSP_CreateSignatureContext set above. Using add instead of set ensures
1196 // that the context is constructed before the set is attempted, which would fail silently otherwise.
1197 signContext
.add(CSSM_ATTRIBUTE_PADDING
, paddingAlg
);
1199 CssmData
data(dataToSign
.Data
, dataToSign
.Length
);
1200 signContext
.sign(data
);
1202 CssmData
sig(signature
.Data
, signature
.Length
);
1203 signContext(sig
); // yes, this is an accessor. Believe it, or not.
1204 signature
.Length
= sig
.length();
1209 void KeyItem::RawVerify(SecPadding padding
, CSSM_DATA dataToVerify
, const AccessCredentials
*credentials
, CSSM_DATA sig
)
1211 CSSM_ALGORITHMS baseAlg
= key()->header().algorithm();
1212 if ((baseAlg
!= CSSM_ALGID_RSA
) && (baseAlg
!= CSSM_ALGID_ECDSA
))
1214 MacOSError::throwMe(errSecParam
);
1217 CSSM_ALGORITHMS paddingAlg
= CSSM_PADDING_PKCS1
;
1221 case kSecPaddingPKCS1
:
1223 paddingAlg
= CSSM_PADDING_PKCS1
;
1227 case kSecPaddingPKCS1MD2
:
1229 baseAlg
= CSSM_ALGID_MD2WithRSA
;
1233 case kSecPaddingPKCS1MD5
:
1235 baseAlg
= CSSM_ALGID_MD5WithRSA
;
1239 case kSecPaddingPKCS1SHA1
:
1241 baseAlg
= CSSM_ALGID_SHA1WithRSA
;
1245 case kSecPaddingSigRaw
:
1247 paddingAlg
= CSSM_PADDING_SIGRAW
;
1253 paddingAlg
= CSSM_PADDING_NONE
;
1258 Verify
verifyContext(csp(), baseAlg
);
1259 verifyContext
.key(key());
1260 verifyContext
.cred(credentials
);
1261 // Fields required for CSSM_CSP_CreateSignatureContext set above. Using add instead of set ensures
1262 // that the context is constructed before the set is attempted, which would fail silently otherwise.
1263 verifyContext
.add(CSSM_ATTRIBUTE_PADDING
, paddingAlg
);
1265 CssmData
data(dataToVerify
.Data
, dataToVerify
.Length
);
1266 CssmData
signature(sig
.Data
, sig
.Length
);
1267 verifyContext
.verify(data
, signature
);
1272 void KeyItem::Encrypt(SecPadding padding
, CSSM_DATA dataToEncrypt
, const AccessCredentials
*credentials
, CSSM_DATA
& encryptedData
)
1274 CSSM_ALGORITHMS baseAlg
= key()->header().algorithm();
1275 if (baseAlg
!= CSSM_ALGID_RSA
)
1277 MacOSError::throwMe(errSecParam
);
1280 CSSM_ALGORITHMS paddingAlg
= CSSM_PADDING_PKCS1
;
1284 case kSecPaddingPKCS1
:
1286 paddingAlg
= CSSM_PADDING_PKCS1
;
1292 paddingAlg
= CSSM_PADDING_NONE
;
1297 CssmClient::Encrypt
encryptContext(csp(), baseAlg
);
1298 encryptContext
.key(key());
1299 encryptContext
.padding(paddingAlg
);
1300 encryptContext
.cred(credentials
);
1302 CssmData
inData(dataToEncrypt
.Data
, dataToEncrypt
.Length
);
1303 CssmData
outData(encryptedData
.Data
, encryptedData
.Length
);
1304 CssmData
remData((void*) NULL
, 0);
1306 encryptedData
.Length
= encryptContext
.encrypt(inData
, outData
, remData
);
1311 void KeyItem::Decrypt(SecPadding padding
, CSSM_DATA dataToDecrypt
, const AccessCredentials
*credentials
, CSSM_DATA
& decryptedData
)
1313 CSSM_ALGORITHMS baseAlg
= key()->header().algorithm();
1314 if (baseAlg
!= CSSM_ALGID_RSA
)
1316 MacOSError::throwMe(errSecParam
);
1319 CSSM_ALGORITHMS paddingAlg
= CSSM_PADDING_PKCS1
;
1323 case kSecPaddingPKCS1
:
1325 paddingAlg
= CSSM_PADDING_PKCS1
;
1332 paddingAlg
= CSSM_PADDING_NONE
;
1337 CssmClient::Decrypt
decryptContext(csp(), baseAlg
);
1338 decryptContext
.key(key());
1339 decryptContext
.padding(paddingAlg
);
1340 decryptContext
.cred(credentials
);
1342 CssmData
inData(dataToDecrypt
.Data
, dataToDecrypt
.Length
);
1343 CssmData
outData(decryptedData
.Data
, decryptedData
.Length
);
1344 CssmData
remData((void*) NULL
, 0);
1345 decryptedData
.Length
= decryptContext
.decrypt(inData
, outData
, remData
);
1346 if (remData
.Data
!= NULL
)
1352 CFHashCode
KeyItem::hash()
1354 CFHashCode result
= 0;
1355 const CSSM_KEY
*cssmKey
= key();
1356 if (NULL
!= cssmKey
)
1358 unsigned char digest
[CC_SHA256_DIGEST_LENGTH
];
1360 CFIndex size_of_data
= sizeof(CSSM_KEYHEADER
) + cssmKey
->KeyData
.Length
;
1362 CFMutableDataRef temp_cfdata
= CFDataCreateMutable(kCFAllocatorDefault
, size_of_data
);
1363 if (NULL
== temp_cfdata
)
1368 CFDataAppendBytes(temp_cfdata
, (const UInt8
*)cssmKey
, sizeof(CSSM_KEYHEADER
));
1369 CFDataAppendBytes(temp_cfdata
, cssmKey
->KeyData
.Data
, cssmKey
->KeyData
.Length
);
1371 if (size_of_data
< 80)
1373 // If it is less than 80 bytes then CFData can be used
1374 result
= CFHash(temp_cfdata
);
1375 CFRelease(temp_cfdata
);
1377 // CFData truncates its hash value to 80 bytes. ????
1378 // In order to do the 'right thing' a SHA 256 hash will be used to
1379 // include all of the data
1382 memset(digest
, 0, CC_SHA256_DIGEST_LENGTH
);
1384 CC_SHA256((const void *)CFDataGetBytePtr(temp_cfdata
), (CC_LONG
)CFDataGetLength(temp_cfdata
), digest
);
1386 CFDataRef data_to_hash
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
,
1387 (const UInt8
*)digest
, CC_SHA256_DIGEST_LENGTH
, kCFAllocatorNull
);
1388 result
= CFHash(data_to_hash
);
1389 CFRelease(data_to_hash
);
1390 CFRelease(temp_cfdata
);
1396 void KeyItem::setIntegrity(bool force
) {
1397 ItemImpl::setIntegrity(*key(), force
);
1400 bool KeyItem::checkIntegrity() {
1401 if(!isPersistent()) {
1405 return ItemImpl::checkIntegrity(*key());
1408 // KeyItems are a little bit special: the only modifications you can do to them
1409 // is to change their Print Name, Label, or Application Tag.
1411 // When we do this modification, we need to look ahead to see if there's an item
1412 // that's already there. If there are, we're going to throw a errSecDuplicateItem.
1414 // Unless that item doesn't pass the integrity check, in which case we delete it
1415 // and continue with the add.
1416 void KeyItem::modifyUniqueId(Keychain keychain
, SSDb ssDb
, DbUniqueRecord
& uniqueId
, DbAttributes
& newDbAttributes
, CSSM_DB_RECORDTYPE recordType
) {
1417 SSDbCursor
otherDbCursor(ssDb
, 1);
1418 otherDbCursor
->recordType(recordType
);
1420 bool checkForDuplicates
= false;
1421 // Set up the ssdb cursor
1422 CssmDbAttributeData
* label
= newDbAttributes
.findAttribute(kInfoKeyLabel
);
1424 otherDbCursor
->add(CSSM_DB_EQUAL
, kInfoKeyLabel
, label
->at(0));
1425 checkForDuplicates
= true;
1427 CssmDbAttributeData
* apptag
= newDbAttributes
.findAttribute(kInfoKeyApplicationTag
);
1429 otherDbCursor
->add(CSSM_DB_EQUAL
, kInfoKeyApplicationTag
, apptag
->at(0));
1430 checkForDuplicates
= true;
1433 // KeyItems only have integrity if the keychain supports it; otherwise,
1434 // don't pre-check for duplicates
1435 if((!keychain
) || !keychain
->hasIntegrityProtection()) {
1436 secdebugfunc("integrity", "key skipping duplicate integrity check due to keychain version");
1437 checkForDuplicates
= false;
1440 if (checkForDuplicates
) {
1441 secdebugfunc("integrity", "looking for duplicates");
1442 // If there are duplicates that are invalid, delete it and
1443 // continue. Otherwise, if there are duplicates, throw errSecDuplicateItem.
1444 DbAttributes otherDbAttributes
;
1445 DbUniqueRecord otherUniqueId
;
1446 CssmClient::Key otherKey
;
1448 while(otherDbCursor
->nextKey(&otherDbAttributes
, otherKey
, otherUniqueId
)) {
1449 secdebugfunc("integrity", "found a duplicate, checking integrity");
1451 PrimaryKey pk
= keychain
->makePrimaryKey(recordType
, otherUniqueId
);
1453 ItemImpl
* maybeItem
= keychain
->_lookupItem(pk
);
1455 if(maybeItem
->checkIntegrity()) {
1456 secdebugfunc("integrity", "duplicate is real, throwing error");
1457 MacOSError::throwMe(errSecDuplicateItem
);
1459 secdebugfunc("integrity", "existing duplicate item is invalid, removing...");
1460 Item
item(maybeItem
);
1461 keychain
->deleteItem(item
);
1464 KeyItem
temp(keychain
, pk
, otherUniqueId
);
1466 if(temp
.checkIntegrity()) {
1467 secdebugfunc("integrity", "duplicate is real, throwing error");
1468 MacOSError::throwMe(errSecDuplicateItem
);
1470 secdebugfunc("integrity", "duplicate is invalid, removing");
1471 // Keychain's idea of deleting items involves notifications and callbacks. We don't want that,
1472 // (since this isn't a real item and it should go away quietly), so use this roundabout method.
1473 otherUniqueId
->deleteRecord();
1474 keychain
->removeItem(temp
.primaryKey(), &temp
);
1481 secdebugfunc("integrity", "modifying unique id");
1482 uniqueId
->modify(recordType
, &newDbAttributes
, NULL
, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE
);
1483 secdebugfunc("integrity", "done modifying unique id");
1484 } catch(CssmError e
) {
1485 // Just in case something went wrong, clean up after this add
1486 uniqueId
->deleteRecord();