]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/KeyItem.cpp
Security-57337.40.85.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / KeyItem.cpp
1 /*
2 * Copyright (c) 2002-2004,2011-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 //
25 // KeyItem.cpp
26 //
27 #include <security_keychain/KeyItem.h>
28 #include <Security/cssmtype.h>
29 #include <security_keychain/Access.h>
30 #include <security_keychain/Keychains.h>
31 #include <security_keychain/KeyItem.h>
32 #include <security_cdsa_client/wrapkey.h>
33 #include <security_cdsa_client/genkey.h>
34 #include <security_cdsa_client/signclient.h>
35 #include <security_cdsa_client/cryptoclient.h>
36 #include <security_utilities/CSPDLTransaction.h>
37
38 #include <security_keychain/Globals.h>
39 #include "KCEventNotifier.h"
40 #include <CommonCrypto/CommonDigest.h>
41 #include <SecBase.h>
42 #include <SecBasePriv.h>
43
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
51
52 using namespace KeychainCore;
53 using namespace CssmClient;
54
55 KeyItem::KeyItem(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId) :
56 ItemImpl(keychain, primaryKey, uniqueId),
57 mKey(),
58 algid(NULL),
59 mPubKeyHash(Allocator::standard())
60 {
61 }
62
63 KeyItem::KeyItem(const Keychain &keychain, const PrimaryKey &primaryKey) :
64 ItemImpl(keychain, primaryKey),
65 mKey(),
66 algid(NULL),
67 mPubKeyHash(Allocator::standard())
68 {
69 }
70
71 KeyItem* KeyItem::make(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId)
72 {
73 KeyItem* k = new KeyItem(keychain, primaryKey, uniqueId);
74 keychain->addItem(primaryKey, k);
75 return k;
76 }
77
78
79
80 KeyItem* KeyItem::make(const Keychain &keychain, const PrimaryKey &primaryKey)
81 {
82 KeyItem* k = new KeyItem(keychain, primaryKey);
83 keychain->addItem(primaryKey, k);
84 return k;
85 }
86
87
88
89 KeyItem::KeyItem(KeyItem &keyItem) :
90 ItemImpl(keyItem),
91 mKey(),
92 algid(NULL),
93 mPubKeyHash(Allocator::standard())
94 {
95 // @@@ this doesn't work for keys that are not in a keychain.
96 }
97
98 KeyItem::KeyItem(const CssmClient::Key &key) :
99 ItemImpl(key->keyClass() + CSSM_DL_DB_RECORD_PUBLIC_KEY, (OSType)0, (UInt32)0, (const void*)NULL),
100 mKey(key),
101 algid(NULL),
102 mPubKeyHash(Allocator::standard())
103 {
104 if (key->keyClass() > CSSM_KEYCLASS_SESSION_KEY)
105 MacOSError::throwMe(errSecParam);
106 }
107
108 KeyItem::~KeyItem()
109 {
110 }
111
112 void
113 KeyItem::update()
114 {
115 //Create a new CSPDLTransaction
116 Db db(mKeychain->database());
117 CSPDLTransaction transaction(db);
118
119 ItemImpl::update();
120
121 /* Update integrity on key */
122 setIntegrity();
123
124 transaction.success();
125 }
126
127 Item
128 KeyItem::copyTo(const Keychain &keychain, Access *newAccess)
129 {
130 if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
131 MacOSError::throwMe(errSecInvalidKeychain);
132
133 /* Get the destination keychain's db. */
134 SSDbImpl* dbImpl = dynamic_cast<SSDbImpl*>(&(*keychain->database()));
135 if (dbImpl == NULL)
136 {
137 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
138 }
139
140 SSDb ssDb(dbImpl);
141
142 /* Make sure mKey is valid. */
143 const CSSM_KEY *cssmKey = key();
144 if (cssmKey && (0==(cssmKey->KeyHeader.KeyAttr & CSSM_KEYATTR_EXTRACTABLE)))
145 {
146 MacOSError::throwMe(errSecDataNotAvailable);
147 }
148
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);
155
156 /* Set up the ACL for the new key. */
157 SecPointer<Access> access;
158 if (newAccess)
159 access = newAccess;
160 else
161 access = new Access(*mKey);
162
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 */)));
167
168 /* make a random IV */
169 uint8 ivBytes[8];
170 CssmData iv(ivBytes, sizeof(ivBytes));
171 random.generate(iv, (uint32)iv.length());
172
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);
179 wrap.initVector(iv);
180 CssmClient::Key wrappedKey(wrap(mKey));
181
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);
188
189 /* Setup the dldbHandle in the context. */
190 unwrap.add(CSSM_ATTRIBUTE_DL_DB_HANDLE, ssDb->handle());
191
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());
197
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;
203
204 CssmClient::Key unwrappedKey(unwrap(wrappedKey, KeySpec(usage,
205 (mKey->attributes() | CSSM_KEYATTR_PERMANENT) & ~(CSSM_KEYATTR_ALWAYS_SENSITIVE | CSSM_KEYATTR_NEVER_EXTRACTABLE),
206 label)));
207
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);
216
217 /* Copy the Label, PrintName and ApplicationTag attributes from the old key to the new one. */
218 dbUniqueRecord();
219 DbAttributes oldDbAttributes(mUniqueId->database(), 3);
220 oldDbAttributes.add(kInfoKeyLabel);
221 oldDbAttributes.add(kInfoKeyPrintName);
222 oldDbAttributes.add(kInfoKeyApplicationTag);
223 mUniqueId->get(&oldDbAttributes, NULL);
224 try
225 {
226 uniqueId->modify(recordType(), &oldDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
227 }
228 catch (CssmError e)
229 {
230 // clean up after trying to insert a duplicate key
231 uniqueId->deleteRecord ();
232 throw;
233 }
234
235 /* Set the acl and owner on the unwrapped key. */
236 addIntegrity(*access);
237 access->setAccess(*unwrappedKey, maker);
238
239 /* Return a keychain item which represents the new key. */
240 Item item(keychain->item(recordType(), uniqueId));
241
242 KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, item);
243
244 return item;
245 }
246
247 Item
248 KeyItem::importTo(const Keychain &keychain, Access *newAccess, SecKeychainAttributeList *attrList)
249 {
250 if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
251 MacOSError::throwMe(errSecInvalidKeychain);
252
253 /* Get the destination keychain's db. */
254 SSDbImpl* dbImpl = dynamic_cast<SSDbImpl*>(&(*keychain->database()));
255 if (dbImpl == NULL)
256 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
257
258 SSDb ssDb(dbImpl);
259
260 /* Make sure mKey is valid. */
261 /* We can't call key() here, since we won't have a unique record id yet */
262 if (!mKey)
263 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
264
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);
271
272 /* Set up the ACL for the new key. */
273 SecPointer<Access> access;
274 if (newAccess)
275 access = newAccess;
276 else
277 access = new Access(*mKey);
278
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 */)));
283
284 /* make a random IV */
285 uint8 ivBytes[8];
286 CssmData iv(ivBytes, sizeof(ivBytes));
287 random.generate(iv, (uint32)iv.length());
288
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);
295 wrap.initVector(iv);
296 CssmClient::Key wrappedKey(wrap(mKey));
297
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);
304
305 /* Setup the dldbHandle in the context. */
306 unwrap.add(CSSM_ATTRIBUTE_DL_DB_HANDLE, ssDb->handle());
307
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());
313
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;
319
320 CssmClient::Key unwrappedKey(unwrap(wrappedKey, KeySpec(usage,
321 (mKey->attributes() | CSSM_KEYATTR_PERMANENT) & ~(CSSM_KEYATTR_ALWAYS_SENSITIVE | CSSM_KEYATTR_NEVER_EXTRACTABLE),
322 label)));
323
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);
332
333 // Set the initial label, application label, and application tag (if provided)
334 if (attrList) {
335 DbAttributes newDbAttributes;
336
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);
342 }
343 if (attr.tag == kSecKeyLabel) {
344 newDbAttributes.add(kInfoKeyLabel, attrData);
345 }
346 if (attr.tag == kSecKeyApplicationTag) {
347 newDbAttributes.add(kInfoKeyApplicationTag, attrData);
348 }
349 }
350
351 modifyUniqueId(keychain, ssDb, uniqueId, newDbAttributes, recordType());
352 }
353
354 /* Set the acl and owner on the unwrapped key. */
355 addIntegrity(*access);
356 access->setAccess(*unwrappedKey, maker);
357
358 /* Return a keychain item which represents the new key. */
359 Item item(keychain->item(recordType(), uniqueId));
360
361 KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, item);
362
363 return item;
364 }
365
366 void
367 KeyItem::didModify()
368 {
369 }
370
371 PrimaryKey
372 KeyItem::add(Keychain &keychain)
373 {
374 MacOSError::throwMe(errSecUnimplemented);
375 }
376
377 CssmClient::SSDbUniqueRecord
378 KeyItem::ssDbUniqueRecord()
379 {
380 DbUniqueRecordImpl *impl = &*dbUniqueRecord();
381 Security::CssmClient::SSDbUniqueRecordImpl *simpl = dynamic_cast<Security::CssmClient::SSDbUniqueRecordImpl *>(impl);
382 if (simpl == NULL)
383 {
384 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
385 }
386
387 return CssmClient::SSDbUniqueRecord(simpl);
388 }
389
390 CssmClient::Key &
391 KeyItem::key()
392 {
393 StLock<Mutex>_(mMutex);
394 if (!mKey)
395 {
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));
400 }
401
402 return mKey;
403 }
404
405 CssmClient::CSP
406 KeyItem::csp()
407 {
408 return key()->csp();
409 }
410
411
412 const CSSM_X509_ALGORITHM_IDENTIFIER&
413 KeyItem::algorithmIdentifier()
414 {
415 #if 0
416 CssmKey *mKey;
417 CSSM_KEY_TYPE algorithm
418 CSSM_KEY_PTR cssmKey = (CSSM_KEY_PTR)thisData->Data;
419 cssmKey->KeyHeader
420 static void printKeyHeader(
421 const CSSM_KEYHEADER &hdr)
422 {
423 printf(" Algorithm : ");
424 switch(hdr.AlgorithmId) {
425 CSSM_X509_ALGORITHM_IDENTIFIER algID;
426
427 CSSM_OID *CL_algToOid(
428 CSSM_ALGORITHMS algId)
429 typedef struct cssm_x509_algorithm_identifier {
430 CSSM_OID algorithm;
431 CSSM_DATA parameters;
432 } CSSM_X509_ALGORITHM_IDENTIFIER, *CSSM_X509_ALGORITHM_IDENTIFIER_PTR;
433 #endif
434
435 abort();
436 }
437
438 /*
439 * itemID, used to locate Extended Attributes, is the public key hash for keys.
440 */
441 const CssmData &KeyItem::itemID()
442 {
443 if(mPubKeyHash.length() == 0) {
444 /*
445 * Fetch the attribute from disk.
446 */
447 UInt32 tag = kSecKeyLabel;
448 UInt32 format = 0;
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);
454 }
455 mPubKeyHash.copy(attrList->attr->data, attrList->attr->length);
456 freeAttributesAndData(attrList, NULL);
457 }
458 return mPubKeyHash;
459 }
460
461
462 unsigned int
463 KeyItem::strengthInBits(const CSSM_X509_ALGORITHM_IDENTIFIER *algid)
464 {
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(),
468 CSSM_INVALID_HANDLE,
469 key(),
470 &keySize);
471 if (rv)
472 return 0;
473
474 return keySize.LogicalKeySizeInBits;
475 }
476
477 const AccessCredentials *
478 KeyItem::getCredentials(
479 CSSM_ACL_AUTHORIZATION_TAG operation,
480 SecCredentialType credentialType)
481 {
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);
485
486 bool smartcard = keychain() != NULL ? (keychain()->database()->dl()->guid() == gGuidAppleSdCSPDL) : false;
487
488 AclFactory factory;
489 switch (credentialType)
490 {
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();
497 default:
498 MacOSError::throwMe(errSecParam);
499 }
500 }
501
502 bool
503 KeyItem::operator == (KeyItem &other)
504 {
505 if (mKey && *mKey)
506 {
507 // Pointer compare
508 return this == &other;
509 }
510
511 // If keychains are different, then keys are different
512 Keychain otherKeychain = other.keychain();
513 return (mKeychain && otherKeychain && (*mKeychain == *otherKeychain));
514 }
515
516 void
517 KeyItem::createPair(
518 Keychain keychain,
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)
529 {
530 bool freeKeys = false;
531 bool deleteContext = false;
532
533 if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
534 MacOSError::throwMe(errSecInvalidKeychain);
535
536 SSDbImpl* impl = dynamic_cast<SSDbImpl*>(&(*keychain->database()));
537 if (impl == NULL)
538 {
539 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
540 }
541
542 SSDb ssDb(impl);
543 CssmClient::CSP csp(keychain->csp());
544 CssmClient::CSP appleCsp(gGuidAppleCSP);
545
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);
551
552 // Create a Access::Maker for the initial owner of the private key.
553 ResourceControlContext rcc;
554 memset(&rcc, 0, sizeof(rcc));
555 Access::Maker maker;
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();
560
561 CSSM_KEY publicCssmKey, privateCssmKey;
562 memset(&publicCssmKey, 0, sizeof(publicCssmKey));
563 memset(&privateCssmKey, 0, sizeof(privateCssmKey));
564
565 CSSM_CC_HANDLE ccHandle = 0;
566
567 SecPointer<KeyItem> publicKeyItem, privateKeyItem;
568 try
569 {
570 CSSM_RETURN status;
571 if (contextHandle)
572 ccHandle = contextHandle;
573 else
574 {
575 status = CSSM_CSP_CreateKeyGenContext(csp->handle(), algorithm, keySizeInBits, NULL, NULL, NULL, NULL, NULL, &ccHandle);
576 if (status)
577 CssmError::throwMe(status);
578 deleteContext = true;
579 }
580
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);
585 if (status)
586 CssmError::throwMe(status);
587
588 // Generate the keypair
589 status = CSSM_GenerateKeyPair(ccHandle, publicKeyUsage, publicKeyAttr, &label, &publicCssmKey, privateKeyUsage, privateKeyAttr, &label, &rcc, &privateCssmKey);
590 if (status)
591 CssmError::throwMe(status);
592 freeKeys = true;
593
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.
597
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);
607
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);
617
618 // Convert reference public key to a raw key so we can use it
619 // in the appleCsp.
620 CssmClient::WrapKey wrap(csp, CSSM_ALGID_NONE);
621 wrap.cred(cred);
622 CssmClient::Key rawPubKey = wrap(publicKey);
623
624 // Calculate the hash of the public key using the appleCSP.
625 CssmClient::PassThrough passThrough(appleCsp);
626 void *outData;
627 CssmData *cssmData;
628
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
633 * in *outData. */
634 passThrough.key(rawPubKey);
635 passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData);
636 cssmData = reinterpret_cast<CssmData *>(outData);
637 CssmData &pubKeyHash = *cssmData;
638
639 auto_ptr<string>privDescription;
640 auto_ptr<string>pubDescription;
641 try {
642 privDescription.reset(new string(initialAccess->promptDescription()));
643 pubDescription.reset(new string(initialAccess->promptDescription()));
644 }
645 catch(...) {
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"));
649 }
650
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);
656
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);
662
663 // @@@ Not exception safe!
664 csp.allocator().free(cssmData->Data);
665 csp.allocator().free(cssmData);
666
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());
670
671 if (!publicKeyItem || !privateKeyItem)
672 {
673 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
674 }
675
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);
679
680 if(publicKeyAttr & CSSM_KEYATTR_PUBLIC_KEY_ENCRYPT) {
681 /*
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).
686 */
687 SecPointer<Access> pubKeyAccess(new Access());
688 publicKeyItem->addIntegrity(*pubKeyAccess);
689 pubKeyAccess->setAccess(*publicKey, maker);
690 }
691
692 outPublicKey = publicKeyItem;
693 outPrivateKey = privateKeyItem;
694 }
695 catch (...)
696 {
697 if (freeKeys)
698 {
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);
702 }
703
704 if (deleteContext)
705 CSSM_DeleteContext(ccHandle);
706
707 throw;
708 }
709
710 if (freeKeys)
711 {
712 CSSM_FreeKey(csp->handle(), NULL, &publicCssmKey, FALSE);
713 CSSM_FreeKey(csp->handle(), NULL, &privateCssmKey, FALSE);
714 }
715
716 if (deleteContext)
717 CSSM_DeleteContext(ccHandle);
718
719 if (keychain && publicKeyItem && privateKeyItem)
720 {
721 keychain->postEvent(kSecAddEvent, publicKeyItem);
722 keychain->postEvent(kSecAddEvent, privateKeyItem);
723 }
724 }
725
726 void
727 KeyItem::importPair(
728 Keychain keychain,
729 const CSSM_KEY &publicWrappedKey,
730 const CSSM_KEY &privateWrappedKey,
731 SecPointer<Access> initialAccess,
732 SecPointer<KeyItem> &outPublicKey,
733 SecPointer<KeyItem> &outPrivateKey)
734 {
735 bool freePublicKey = false;
736 bool freePrivateKey = false;
737 bool deleteContext = false;
738
739 if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
740 MacOSError::throwMe(errSecInvalidKeychain);
741
742 SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*keychain->database()));
743 if (impl == NULL)
744 {
745 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
746 }
747
748 SSDb ssDb(impl);
749 CssmClient::CSP csp(keychain->csp());
750 CssmClient::CSP appleCsp(gGuidAppleCSP);
751
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();
762
763 CSSM_KEY publicCssmKey, privateCssmKey;
764 memset(&publicCssmKey, 0, sizeof(publicCssmKey));
765 memset(&privateCssmKey, 0, sizeof(privateCssmKey));
766
767 CSSM_CC_HANDLE ccHandle = 0;
768
769 SecPointer<KeyItem> publicKeyItem, privateKeyItem;
770 try
771 {
772 CSSM_RETURN status;
773
774 // Calculate the hash of the public key using the appleCSP.
775 CssmClient::PassThrough passThrough(appleCsp);
776 void *outData;
777 CssmData *cssmData;
778
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
783 * in *outData. */
784 passThrough.key(&publicWrappedKey);
785 passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData);
786 cssmData = reinterpret_cast<CssmData *>(outData);
787 CssmData &pubKeyHash = *cssmData;
788
789 status = CSSM_CSP_CreateSymmetricContext(csp->handle(), publicWrappedKey.KeyHeader.WrapAlgorithmId, CSSM_ALGMODE_NONE, NULL, NULL, NULL, CSSM_PADDING_NONE, NULL, &ccHandle);
790 if (status)
791 CssmError::throwMe(status);
792 deleteContext = true;
793
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);
798 if (status)
799 CssmError::throwMe(status);
800
801 // Unwrap the the keys
802 CSSM_DATA descriptiveData = {0, NULL};
803
804 status = CSSM_UnwrapKey(
805 ccHandle,
806 NULL,
807 &publicWrappedKey,
808 publicWrappedKey.KeyHeader.KeyUsage,
809 publicWrappedKey.KeyHeader.KeyAttr | CSSM_KEYATTR_PERMANENT,
810 &pubKeyHash,
811 &rcc,
812 &publicCssmKey,
813 &descriptiveData);
814
815 if (status)
816 CssmError::throwMe(status);
817 freePublicKey = true;
818
819 if (descriptiveData.Data != NULL)
820 free (descriptiveData.Data);
821
822 status = CSSM_UnwrapKey(
823 ccHandle,
824 NULL,
825 &privateWrappedKey,
826 privateWrappedKey.KeyHeader.KeyUsage,
827 privateWrappedKey.KeyHeader.KeyAttr | CSSM_KEYATTR_PERMANENT,
828 &pubKeyHash,
829 &rcc,
830 &privateCssmKey,
831 &descriptiveData);
832
833 if (status)
834 CssmError::throwMe(status);
835
836 if (descriptiveData.Data != NULL)
837 free (descriptiveData.Data);
838
839 freePrivateKey = true;
840
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.
844
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);
854
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);
864
865 // @@@ Not exception safe!
866 csp.allocator().free(cssmData->Data);
867 csp.allocator().free(cssmData);
868
869 auto_ptr<string>privDescription;
870 auto_ptr<string>pubDescription;
871 try {
872 privDescription.reset(new string(initialAccess->promptDescription()));
873 pubDescription.reset(new string(initialAccess->promptDescription()));
874 }
875 catch(...) {
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"));
879 }
880
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);
885
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);
890
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());
894
895 if (!publicKeyItem || !privateKeyItem)
896 {
897 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
898 }
899
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);
903
904 // Make the public key acl completely open
905 SecPointer<Access> pubKeyAccess(new Access());
906 publicKeyItem->addIntegrity(*pubKeyAccess);
907 pubKeyAccess->setAccess(*publicKey, maker);
908
909 outPublicKey = publicKeyItem;
910 outPrivateKey = privateKeyItem;
911 }
912 catch (...)
913 {
914 if (freePublicKey)
915 CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, TRUE);
916 if (freePrivateKey)
917 CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, TRUE);
918
919 if (deleteContext)
920 CSSM_DeleteContext(ccHandle);
921
922 throw;
923 }
924
925 if (freePublicKey)
926 CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, FALSE);
927 if (freePrivateKey)
928 CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, FALSE);
929
930 if (deleteContext)
931 CSSM_DeleteContext(ccHandle);
932
933 if (keychain && publicKeyItem && privateKeyItem)
934 {
935 KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, Item(publicKeyItem));
936 KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, Item(privateKeyItem));
937 }
938 }
939
940 SecPointer<KeyItem>
941 KeyItem::generateWithAttributes(const SecKeychainAttributeList *attrList,
942 Keychain keychain,
943 CSSM_ALGORITHMS algorithm,
944 uint32 keySizeInBits,
945 CSSM_CC_HANDLE contextHandle,
946 CSSM_KEYUSE keyUsage,
947 uint32 keyAttr,
948 SecPointer<Access> initialAccess)
949 {
950 CssmClient::CSP appleCsp(gGuidAppleCSP);
951 CssmClient::CSP csp(NULL);
952 SSDb ssDb(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;
958
959 if (keychain)
960 {
961 if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
962 MacOSError::throwMe(errSecInvalidKeychain);
963
964 SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*keychain->database()));
965 if (impl == NULL)
966 {
967 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
968 }
969
970 ssDb = SSDb(impl);
971 csp = keychain->csp();
972
973 // Generate a random label to use initially
974 CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW);
975 random.generate(label, (uint32)label.Length);
976 plabel = &label;
977 }
978 else
979 {
980 // Not a persistent key so create it in the regular csp
981 csp = appleCsp;
982 }
983
984 // Create a Access::Maker for the initial owner of the private key.
985 ResourceControlContext *prcc = NULL, rcc;
986 const AccessCredentials *cred = NULL;
987 Access::Maker maker;
988
989
990
991 if (keychain)
992 {
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.
999 cred = maker.cred();
1000 prcc = &rcc;
1001
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));
1005 }
1006 }
1007
1008 CSSM_KEY cssmKey;
1009
1010 CSSM_CC_HANDLE ccHandle = 0;
1011
1012 SecPointer<KeyItem> keyItem;
1013 try
1014 {
1015 CSSM_RETURN status;
1016 if (contextHandle)
1017 ccHandle = contextHandle;
1018 else
1019 {
1020 status = CSSM_CSP_CreateKeyGenContext(csp->handle(), algorithm, keySizeInBits, NULL, NULL, NULL, NULL, NULL, &ccHandle);
1021 if (status)
1022 CssmError::throwMe(status);
1023 deleteContext = true;
1024 }
1025
1026 if (ssDb)
1027 {
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);
1032 if (status)
1033 CssmError::throwMe(status);
1034
1035 keyAttr |= CSSM_KEYATTR_PERMANENT;
1036 }
1037
1038 // Generate the key
1039 status = CSSM_GenerateKey(ccHandle, keyUsage, keyAttr, plabel, prcc, &cssmKey);
1040 if (status)
1041 CssmError::throwMe(status);
1042
1043 if (ssDb)
1044 {
1045 freeKey = true;
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.
1048
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);
1058
1059 // Set the initial label, application label, and application tag (if provided)
1060 if (attrList) {
1061 DbAttributes newDbAttributes;
1062
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);
1068 }
1069 if (attr.tag == kSecKeyLabel) {
1070 newDbAttributes.add(kInfoKeyLabel, attrData);
1071 }
1072 if (attr.tag == kSecKeyApplicationTag) {
1073 newDbAttributes.add(kInfoKeyApplicationTag, attrData);
1074 }
1075 }
1076
1077 modifyUniqueId(keychain, ssDb, uniqueId, newDbAttributes, CSSM_DL_DB_RECORD_SYMMETRIC_KEY);
1078 }
1079
1080 // Create keychain item which will represent the key.
1081 keyItem = dynamic_cast<KeyItem *>(keychain->item(CSSM_DL_DB_RECORD_SYMMETRIC_KEY, uniqueId).get());
1082
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);
1086 }
1087 else
1088 {
1089 CssmClient::Key tempKey(csp, cssmKey);
1090 keyItem = new KeyItem(tempKey);
1091 }
1092 }
1093 catch (...)
1094 {
1095 if (freeKey)
1096 {
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);
1099 }
1100
1101 if (deleteContext)
1102 CSSM_DeleteContext(ccHandle);
1103
1104 throw;
1105 }
1106
1107 if (freeKey)
1108 {
1109 CSSM_FreeKey(csp->handle(), NULL, &cssmKey, FALSE);
1110 }
1111
1112 if (deleteContext)
1113 CSSM_DeleteContext(ccHandle);
1114
1115 if (keychain && keyItem)
1116 keychain->postEvent(kSecAddEvent, keyItem);
1117
1118 KeyItem* item = dynamic_cast<KeyItem*>(&*keyItem);
1119 if (item == NULL)
1120 {
1121 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
1122 }
1123
1124 return item;
1125 }
1126
1127 SecPointer<KeyItem>
1128 KeyItem::generate(Keychain keychain,
1129 CSSM_ALGORITHMS algorithm,
1130 uint32 keySizeInBits,
1131 CSSM_CC_HANDLE contextHandle,
1132 CSSM_KEYUSE keyUsage,
1133 uint32 keyAttr,
1134 SecPointer<Access> initialAccess)
1135 {
1136 return KeyItem::generateWithAttributes(NULL, keychain,
1137 algorithm, keySizeInBits, contextHandle,
1138 keyUsage, keyAttr, initialAccess);
1139 }
1140
1141
1142 void KeyItem::RawSign(SecPadding padding, CSSM_DATA dataToSign, const AccessCredentials *credentials, CSSM_DATA& signature)
1143 {
1144 CSSM_ALGORITHMS baseAlg = key()->header().algorithm();
1145
1146 if ((baseAlg != CSSM_ALGID_RSA) && (baseAlg != CSSM_ALGID_ECDSA))
1147 {
1148 MacOSError::throwMe(errSecParam);
1149 }
1150
1151 CSSM_ALGORITHMS paddingAlg = CSSM_PADDING_PKCS1;
1152
1153 switch (padding)
1154 {
1155 case kSecPaddingPKCS1:
1156 {
1157 paddingAlg = CSSM_PADDING_PKCS1;
1158 break;
1159 }
1160
1161 case kSecPaddingPKCS1MD2:
1162 {
1163 baseAlg = CSSM_ALGID_MD2WithRSA;
1164 break;
1165 }
1166
1167 case kSecPaddingPKCS1MD5:
1168 {
1169 baseAlg = CSSM_ALGID_MD5WithRSA;
1170 break;
1171 }
1172
1173 case kSecPaddingPKCS1SHA1:
1174 {
1175 baseAlg = CSSM_ALGID_SHA1WithRSA;
1176 break;
1177 }
1178
1179 case kSecPaddingSigRaw:
1180 {
1181 paddingAlg = CSSM_PADDING_SIGRAW;
1182 break;
1183 }
1184
1185 default:
1186 {
1187 paddingAlg = CSSM_PADDING_NONE;
1188 break;
1189 }
1190 }
1191
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);
1198
1199 CssmData data(dataToSign.Data, dataToSign.Length);
1200 signContext.sign(data);
1201
1202 CssmData sig(signature.Data, signature.Length);
1203 signContext(sig); // yes, this is an accessor. Believe it, or not.
1204 signature.Length = sig.length();
1205 }
1206
1207
1208
1209 void KeyItem::RawVerify(SecPadding padding, CSSM_DATA dataToVerify, const AccessCredentials *credentials, CSSM_DATA sig)
1210 {
1211 CSSM_ALGORITHMS baseAlg = key()->header().algorithm();
1212 if ((baseAlg != CSSM_ALGID_RSA) && (baseAlg != CSSM_ALGID_ECDSA))
1213 {
1214 MacOSError::throwMe(errSecParam);
1215 }
1216
1217 CSSM_ALGORITHMS paddingAlg = CSSM_PADDING_PKCS1;
1218
1219 switch (padding)
1220 {
1221 case kSecPaddingPKCS1:
1222 {
1223 paddingAlg = CSSM_PADDING_PKCS1;
1224 break;
1225 }
1226
1227 case kSecPaddingPKCS1MD2:
1228 {
1229 baseAlg = CSSM_ALGID_MD2WithRSA;
1230 break;
1231 }
1232
1233 case kSecPaddingPKCS1MD5:
1234 {
1235 baseAlg = CSSM_ALGID_MD5WithRSA;
1236 break;
1237 }
1238
1239 case kSecPaddingPKCS1SHA1:
1240 {
1241 baseAlg = CSSM_ALGID_SHA1WithRSA;
1242 break;
1243 }
1244
1245 case kSecPaddingSigRaw:
1246 {
1247 paddingAlg = CSSM_PADDING_SIGRAW;
1248 break;
1249 }
1250
1251 default:
1252 {
1253 paddingAlg = CSSM_PADDING_NONE;
1254 break;
1255 }
1256 }
1257
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);
1264
1265 CssmData data(dataToVerify.Data, dataToVerify.Length);
1266 CssmData signature(sig.Data, sig.Length);
1267 verifyContext.verify(data, signature);
1268 }
1269
1270
1271
1272 void KeyItem::Encrypt(SecPadding padding, CSSM_DATA dataToEncrypt, const AccessCredentials *credentials, CSSM_DATA& encryptedData)
1273 {
1274 CSSM_ALGORITHMS baseAlg = key()->header().algorithm();
1275 if (baseAlg != CSSM_ALGID_RSA)
1276 {
1277 MacOSError::throwMe(errSecParam);
1278 }
1279
1280 CSSM_ALGORITHMS paddingAlg = CSSM_PADDING_PKCS1;
1281
1282 switch (padding)
1283 {
1284 case kSecPaddingPKCS1:
1285 {
1286 paddingAlg = CSSM_PADDING_PKCS1;
1287 break;
1288 }
1289
1290 default:
1291 {
1292 paddingAlg = CSSM_PADDING_NONE;
1293 break;
1294 }
1295 }
1296
1297 CssmClient::Encrypt encryptContext(csp(), baseAlg);
1298 encryptContext.key(key());
1299 encryptContext.padding(paddingAlg);
1300 encryptContext.cred(credentials);
1301
1302 CssmData inData(dataToEncrypt.Data, dataToEncrypt.Length);
1303 CssmData outData(encryptedData.Data, encryptedData.Length);
1304 CssmData remData((void*) NULL, 0);
1305
1306 encryptedData.Length = encryptContext.encrypt(inData, outData, remData);
1307 }
1308
1309
1310
1311 void KeyItem::Decrypt(SecPadding padding, CSSM_DATA dataToDecrypt, const AccessCredentials *credentials, CSSM_DATA& decryptedData)
1312 {
1313 CSSM_ALGORITHMS baseAlg = key()->header().algorithm();
1314 if (baseAlg != CSSM_ALGID_RSA)
1315 {
1316 MacOSError::throwMe(errSecParam);
1317 }
1318
1319 CSSM_ALGORITHMS paddingAlg = CSSM_PADDING_PKCS1;
1320
1321 switch (padding)
1322 {
1323 case kSecPaddingPKCS1:
1324 {
1325 paddingAlg = CSSM_PADDING_PKCS1;
1326 break;
1327 }
1328
1329
1330 default:
1331 {
1332 paddingAlg = CSSM_PADDING_NONE;
1333 break;
1334 }
1335 }
1336
1337 CssmClient::Decrypt decryptContext(csp(), baseAlg);
1338 decryptContext.key(key());
1339 decryptContext.padding(paddingAlg);
1340 decryptContext.cred(credentials);
1341
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)
1347 {
1348 free(remData.Data);
1349 }
1350 }
1351
1352 CFHashCode KeyItem::hash()
1353 {
1354 CFHashCode result = 0;
1355 const CSSM_KEY *cssmKey = key();
1356 if (NULL != cssmKey)
1357 {
1358 unsigned char digest[CC_SHA256_DIGEST_LENGTH];
1359
1360 CFIndex size_of_data = sizeof(CSSM_KEYHEADER) + cssmKey->KeyData.Length;
1361
1362 CFMutableDataRef temp_cfdata = CFDataCreateMutable(kCFAllocatorDefault, size_of_data);
1363 if (NULL == temp_cfdata)
1364 {
1365 return result;
1366 }
1367
1368 CFDataAppendBytes(temp_cfdata, (const UInt8 *)cssmKey, sizeof(CSSM_KEYHEADER));
1369 CFDataAppendBytes(temp_cfdata, cssmKey->KeyData.Data, cssmKey->KeyData.Length);
1370
1371 if (size_of_data < 80)
1372 {
1373 // If it is less than 80 bytes then CFData can be used
1374 result = CFHash(temp_cfdata);
1375 CFRelease(temp_cfdata);
1376 }
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
1380 else
1381 {
1382 memset(digest, 0, CC_SHA256_DIGEST_LENGTH);
1383
1384 CC_SHA256((const void *)CFDataGetBytePtr(temp_cfdata), (CC_LONG)CFDataGetLength(temp_cfdata), digest);
1385
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);
1391 }
1392 }
1393 return result;
1394 }
1395
1396 void KeyItem::setIntegrity(bool force) {
1397 ItemImpl::setIntegrity(*key(), force);
1398 }
1399
1400 bool KeyItem::checkIntegrity() {
1401 if(!isPersistent()) {
1402 return true;
1403 }
1404
1405 return ItemImpl::checkIntegrity(*key());
1406 }
1407
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.
1410 //
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.
1413 //
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);
1419
1420 bool checkForDuplicates = false;
1421 // Set up the ssdb cursor
1422 CssmDbAttributeData* label = newDbAttributes.findAttribute(kInfoKeyLabel);
1423 if(label) {
1424 otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label->at(0));
1425 checkForDuplicates = true;
1426 }
1427 CssmDbAttributeData* apptag = newDbAttributes.findAttribute(kInfoKeyApplicationTag);
1428 if(apptag) {
1429 otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyApplicationTag, apptag->at(0));
1430 checkForDuplicates = true;
1431 }
1432
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;
1438 }
1439
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;
1447
1448 while(otherDbCursor->nextKey(&otherDbAttributes, otherKey, otherUniqueId)) {
1449 secdebugfunc("integrity", "found a duplicate, checking integrity");
1450
1451 PrimaryKey pk = keychain->makePrimaryKey(recordType, otherUniqueId);
1452
1453 ItemImpl* maybeItem = keychain->_lookupItem(pk);
1454 if(maybeItem) {
1455 if(maybeItem->checkIntegrity()) {
1456 secdebugfunc("integrity", "duplicate is real, throwing error");
1457 MacOSError::throwMe(errSecDuplicateItem);
1458 } else {
1459 secdebugfunc("integrity", "existing duplicate item is invalid, removing...");
1460 Item item(maybeItem);
1461 keychain->deleteItem(item);
1462 }
1463 } else {
1464 KeyItem temp(keychain, pk, otherUniqueId);
1465
1466 if(temp.checkIntegrity()) {
1467 secdebugfunc("integrity", "duplicate is real, throwing error");
1468 MacOSError::throwMe(errSecDuplicateItem);
1469 } else {
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);
1475 }
1476 }
1477 }
1478 }
1479
1480 try {
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();
1487 throw;
1488 }
1489 }