]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/KeyItem.cpp
Security-57337.20.44.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
37 #include <security_keychain/Globals.h>
38 #include "KCEventNotifier.h"
39 #include <CommonCrypto/CommonDigest.h>
40 #include <SecBase.h>
41
42 // @@@ This needs to be shared.
43 #pragma clang diagnostic push
44 #pragma clang diagnostic ignored "-Wunused-const-variable"
45 static CSSM_DB_NAME_ATTR(kInfoKeyPrintName, kSecKeyPrintName, (char*) "PrintName", 0, NULL, BLOB);
46 static CSSM_DB_NAME_ATTR(kInfoKeyLabel, kSecKeyLabel, (char*) "Label", 0, NULL, BLOB);
47 static CSSM_DB_NAME_ATTR(kInfoKeyApplicationTag, kSecKeyApplicationTag, (char*) "ApplicationTag", 0, NULL, BLOB);
48 #pragma clang diagnostic pop
49
50 using namespace KeychainCore;
51 using namespace CssmClient;
52
53 KeyItem::KeyItem(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId) :
54 ItemImpl(keychain, primaryKey, uniqueId),
55 mKey(),
56 algid(NULL),
57 mPubKeyHash(Allocator::standard())
58 {
59 }
60
61 KeyItem::KeyItem(const Keychain &keychain, const PrimaryKey &primaryKey) :
62 ItemImpl(keychain, primaryKey),
63 mKey(),
64 algid(NULL),
65 mPubKeyHash(Allocator::standard())
66 {
67 }
68
69 KeyItem* KeyItem::make(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId)
70 {
71 KeyItem* k = new KeyItem(keychain, primaryKey, uniqueId);
72 keychain->addItem(primaryKey, k);
73 return k;
74 }
75
76
77
78 KeyItem* KeyItem::make(const Keychain &keychain, const PrimaryKey &primaryKey)
79 {
80 KeyItem* k = new KeyItem(keychain, primaryKey);
81 keychain->addItem(primaryKey, k);
82 return k;
83 }
84
85
86
87 KeyItem::KeyItem(KeyItem &keyItem) :
88 ItemImpl(keyItem),
89 mKey(),
90 algid(NULL),
91 mPubKeyHash(Allocator::standard())
92 {
93 // @@@ this doesn't work for keys that are not in a keychain.
94 }
95
96 KeyItem::KeyItem(const CssmClient::Key &key) :
97 ItemImpl(key->keyClass() + CSSM_DL_DB_RECORD_PUBLIC_KEY, (OSType)0, (UInt32)0, (const void*)NULL),
98 mKey(key),
99 algid(NULL),
100 mPubKeyHash(Allocator::standard())
101 {
102 if (key->keyClass() > CSSM_KEYCLASS_SESSION_KEY)
103 MacOSError::throwMe(errSecParam);
104 }
105
106 KeyItem::~KeyItem()
107 {
108 }
109
110 void
111 KeyItem::update()
112 {
113 ItemImpl::update();
114 }
115
116 Item
117 KeyItem::copyTo(const Keychain &keychain, Access *newAccess)
118 {
119 if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
120 MacOSError::throwMe(errSecInvalidKeychain);
121
122 /* Get the destination keychain's db. */
123 SSDbImpl* dbImpl = dynamic_cast<SSDbImpl*>(&(*keychain->database()));
124 if (dbImpl == NULL)
125 {
126 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
127 }
128
129 SSDb ssDb(dbImpl);
130
131 /* Make sure mKey is valid. */
132 const CSSM_KEY *cssmKey = key();
133 if (cssmKey && (0==(cssmKey->KeyHeader.KeyAttr & CSSM_KEYATTR_EXTRACTABLE)))
134 {
135 MacOSError::throwMe(errSecDataNotAvailable);
136 }
137
138 // Generate a random label to use initially
139 CssmClient::CSP appleCsp(gGuidAppleCSP);
140 CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW);
141 uint8 labelBytes[20];
142 CssmData label(labelBytes, sizeof(labelBytes));
143 random.generate(label, (uint32)label.Length);
144
145 /* Set up the ACL for the new key. */
146 SecPointer<Access> access;
147 if (newAccess)
148 access = newAccess;
149 else
150 access = new Access(*mKey);
151
152 /* Generate a random 3DES wrapping Key. */
153 CssmClient::GenerateKey genKey(csp(), CSSM_ALGID_3DES_3KEY, 192);
154 CssmClient::Key wrappingKey(genKey(KeySpec(CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP,
155 CSSM_KEYATTR_EXTRACTABLE /* | CSSM_KEYATTR_RETURN_DATA */)));
156
157 /* make a random IV */
158 uint8 ivBytes[8];
159 CssmData iv(ivBytes, sizeof(ivBytes));
160 random.generate(iv, (uint32)iv.length());
161
162 /* Extract the key by wrapping it with the wrapping key. */
163 CssmClient::WrapKey wrap(csp(), CSSM_ALGID_3DES_3KEY_EDE);
164 wrap.key(wrappingKey);
165 wrap.cred(getCredentials(CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED, kSecCredentialTypeDefault));
166 wrap.mode(CSSM_ALGMODE_ECBPad);
167 wrap.padding(CSSM_PADDING_PKCS7);
168 wrap.initVector(iv);
169 CssmClient::Key wrappedKey(wrap(mKey));
170
171 /* Unwrap the new key into the new Keychain. */
172 CssmClient::UnwrapKey unwrap(keychain->csp(), CSSM_ALGID_3DES_3KEY_EDE);
173 unwrap.key(wrappingKey);
174 unwrap.mode(CSSM_ALGMODE_ECBPad);
175 unwrap.padding(CSSM_PADDING_PKCS7);
176 unwrap.initVector(iv);
177
178 /* Setup the dldbHandle in the context. */
179 unwrap.add(CSSM_ATTRIBUTE_DL_DB_HANDLE, ssDb->handle());
180
181 /* Set up an initial aclEntry so we can change it after the unwrap. */
182 Access::Maker maker(Allocator::standard(), Access::Maker::kAnyMakerType);
183 ResourceControlContext rcc;
184 maker.initialOwner(rcc, NULL);
185 unwrap.owner(rcc.input());
186
187 /* Unwrap the key. */
188 uint32 usage = mKey->usage();
189 /* Work around csp brokeness where it sets all usage bits in the Keyheader when CSSM_KEYUSE_ANY is set. */
190 if (usage & CSSM_KEYUSE_ANY)
191 usage = CSSM_KEYUSE_ANY;
192
193 CssmClient::Key unwrappedKey(unwrap(wrappedKey, KeySpec(usage,
194 (mKey->attributes() | CSSM_KEYATTR_PERMANENT) & ~(CSSM_KEYATTR_ALWAYS_SENSITIVE | CSSM_KEYATTR_NEVER_EXTRACTABLE),
195 label)));
196
197 /* Look up unwrapped key in the DLDB. */
198 DbUniqueRecord uniqueId;
199 SSDbCursor dbCursor(ssDb, 1);
200 dbCursor->recordType(recordType());
201 dbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label);
202 CssmClient::Key copiedKey;
203 if (!dbCursor->nextKey(NULL, copiedKey, uniqueId))
204 MacOSError::throwMe(errSecItemNotFound);
205
206 /* Copy the Label, PrintName and ApplicationTag attributes from the old key to the new one. */
207 dbUniqueRecord();
208 DbAttributes oldDbAttributes(mUniqueId->database(), 3);
209 oldDbAttributes.add(kInfoKeyLabel);
210 oldDbAttributes.add(kInfoKeyPrintName);
211 oldDbAttributes.add(kInfoKeyApplicationTag);
212 mUniqueId->get(&oldDbAttributes, NULL);
213 try
214 {
215 uniqueId->modify(recordType(), &oldDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
216 }
217 catch (CssmError e)
218 {
219 // clean up after trying to insert a duplicate key
220 uniqueId->deleteRecord ();
221 throw;
222 }
223
224 /* Set the acl and owner on the unwrapped key. */
225 access->setAccess(*unwrappedKey, maker);
226
227 /* Return a keychain item which represents the new key. */
228 Item item(keychain->item(recordType(), uniqueId));
229
230 KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, item);
231
232 return item;
233 }
234
235 Item
236 KeyItem::importTo(const Keychain &keychain, Access *newAccess, SecKeychainAttributeList *attrList)
237 {
238 if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
239 MacOSError::throwMe(errSecInvalidKeychain);
240
241 /* Get the destination keychain's db. */
242 SSDbImpl* dbImpl = dynamic_cast<SSDbImpl*>(&(*keychain->database()));
243 if (dbImpl == NULL)
244 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
245
246 SSDb ssDb(dbImpl);
247
248 /* Make sure mKey is valid. */
249 /* We can't call key() here, since we won't have a unique record id yet */
250 if (!mKey)
251 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
252
253 // Generate a random label to use initially
254 CssmClient::CSP appleCsp(gGuidAppleCSP);
255 CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW);
256 uint8 labelBytes[20];
257 CssmData label(labelBytes, sizeof(labelBytes));
258 random.generate(label, (uint32)label.Length);
259
260 /* Set up the ACL for the new key. */
261 SecPointer<Access> access;
262 if (newAccess)
263 access = newAccess;
264 else
265 access = new Access(*mKey);
266
267 /* Generate a random 3DES wrapping Key. */
268 CssmClient::GenerateKey genKey(csp(), CSSM_ALGID_3DES_3KEY, 192);
269 CssmClient::Key wrappingKey(genKey(KeySpec(CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP,
270 CSSM_KEYATTR_EXTRACTABLE /* | CSSM_KEYATTR_RETURN_DATA */)));
271
272 /* make a random IV */
273 uint8 ivBytes[8];
274 CssmData iv(ivBytes, sizeof(ivBytes));
275 random.generate(iv, (uint32)iv.length());
276
277 /* Extract the key by wrapping it with the wrapping key. */
278 CssmClient::WrapKey wrap(csp(), CSSM_ALGID_3DES_3KEY_EDE);
279 wrap.key(wrappingKey);
280 wrap.cred(getCredentials(CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED, kSecCredentialTypeDefault));
281 wrap.mode(CSSM_ALGMODE_ECBPad);
282 wrap.padding(CSSM_PADDING_PKCS7);
283 wrap.initVector(iv);
284 CssmClient::Key wrappedKey(wrap(mKey));
285
286 /* Unwrap the new key into the new Keychain. */
287 CssmClient::UnwrapKey unwrap(keychain->csp(), CSSM_ALGID_3DES_3KEY_EDE);
288 unwrap.key(wrappingKey);
289 unwrap.mode(CSSM_ALGMODE_ECBPad);
290 unwrap.padding(CSSM_PADDING_PKCS7);
291 unwrap.initVector(iv);
292
293 /* Setup the dldbHandle in the context. */
294 unwrap.add(CSSM_ATTRIBUTE_DL_DB_HANDLE, ssDb->handle());
295
296 /* Set up an initial aclEntry so we can change it after the unwrap. */
297 Access::Maker maker(Allocator::standard(), Access::Maker::kAnyMakerType);
298 ResourceControlContext rcc;
299 maker.initialOwner(rcc, NULL);
300 unwrap.owner(rcc.input());
301
302 /* Unwrap the key. */
303 uint32 usage = mKey->usage();
304 /* Work around csp brokeness where it sets all usage bits in the Keyheader when CSSM_KEYUSE_ANY is set. */
305 if (usage & CSSM_KEYUSE_ANY)
306 usage = CSSM_KEYUSE_ANY;
307
308 CssmClient::Key unwrappedKey(unwrap(wrappedKey, KeySpec(usage,
309 (mKey->attributes() | CSSM_KEYATTR_PERMANENT) & ~(CSSM_KEYATTR_ALWAYS_SENSITIVE | CSSM_KEYATTR_NEVER_EXTRACTABLE),
310 label)));
311
312 /* Look up unwrapped key in the DLDB. */
313 DbUniqueRecord uniqueId;
314 SSDbCursor dbCursor(ssDb, 1);
315 dbCursor->recordType(recordType());
316 dbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label);
317 CssmClient::Key copiedKey;
318 if (!dbCursor->nextKey(NULL, copiedKey, uniqueId))
319 MacOSError::throwMe(errSecItemNotFound);
320
321 // Set the initial label, application label, and application tag (if provided)
322 if (attrList) {
323 DbAttributes newDbAttributes;
324 SSDbCursor otherDbCursor(ssDb, 1);
325 otherDbCursor->recordType(recordType());
326 bool checkForDuplicates = false;
327
328 for (UInt32 index=0; index < attrList->count; index++) {
329 SecKeychainAttribute attr = attrList->attr[index];
330 CssmData attrData(attr.data, attr.length);
331 if (attr.tag == kSecKeyPrintName) {
332 newDbAttributes.add(kInfoKeyPrintName, attrData);
333 }
334 if (attr.tag == kSecKeyLabel) {
335 newDbAttributes.add(kInfoKeyLabel, attrData);
336 otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, attrData);
337 checkForDuplicates = true;
338 }
339 if (attr.tag == kSecKeyApplicationTag) {
340 newDbAttributes.add(kInfoKeyApplicationTag, attrData);
341 otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyApplicationTag, attrData);
342 checkForDuplicates = true;
343 }
344 }
345
346 DbAttributes otherDbAttributes;
347 DbUniqueRecord otherUniqueId;
348 CssmClient::Key otherKey;
349 try
350 {
351 if (checkForDuplicates && otherDbCursor->nextKey(&otherDbAttributes, otherKey, otherUniqueId))
352 MacOSError::throwMe(errSecDuplicateItem);
353
354 uniqueId->modify(recordType(), &newDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
355 }
356 catch (CssmError e)
357 {
358 // clean up after trying to insert a duplicate key
359 uniqueId->deleteRecord ();
360 throw;
361 }
362 }
363
364 /* Set the acl and owner on the unwrapped key. */
365 access->setAccess(*unwrappedKey, maker);
366
367 /* Return a keychain item which represents the new key. */
368 Item item(keychain->item(recordType(), uniqueId));
369
370 KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, item);
371
372 return item;
373 }
374
375 void
376 KeyItem::didModify()
377 {
378 }
379
380 PrimaryKey
381 KeyItem::add(Keychain &keychain)
382 {
383 MacOSError::throwMe(errSecUnimplemented);
384 }
385
386 CssmClient::SSDbUniqueRecord
387 KeyItem::ssDbUniqueRecord()
388 {
389 DbUniqueRecordImpl *impl = &*dbUniqueRecord();
390 Security::CssmClient::SSDbUniqueRecordImpl *simpl = dynamic_cast<Security::CssmClient::SSDbUniqueRecordImpl *>(impl);
391 if (simpl == NULL)
392 {
393 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
394 }
395
396 return CssmClient::SSDbUniqueRecord(simpl);
397 }
398
399 CssmClient::Key &
400 KeyItem::key()
401 {
402 StLock<Mutex>_(mMutex);
403 if (!mKey)
404 {
405 CssmClient::SSDbUniqueRecord uniqueId(ssDbUniqueRecord());
406 CssmDataContainer dataBlob(uniqueId->allocator());
407 uniqueId->get(NULL, &dataBlob);
408 mKey = CssmClient::Key(uniqueId->database()->csp(), *reinterpret_cast<CssmKey *>(dataBlob.Data));
409 }
410
411 return mKey;
412 }
413
414 CssmClient::CSP
415 KeyItem::csp()
416 {
417 return key()->csp();
418 }
419
420
421 const CSSM_X509_ALGORITHM_IDENTIFIER&
422 KeyItem::algorithmIdentifier()
423 {
424 #if 0
425 CssmKey *mKey;
426 CSSM_KEY_TYPE algorithm
427 CSSM_KEY_PTR cssmKey = (CSSM_KEY_PTR)thisData->Data;
428 cssmKey->KeyHeader
429 static void printKeyHeader(
430 const CSSM_KEYHEADER &hdr)
431 {
432 printf(" Algorithm : ");
433 switch(hdr.AlgorithmId) {
434 CSSM_X509_ALGORITHM_IDENTIFIER algID;
435
436 CSSM_OID *CL_algToOid(
437 CSSM_ALGORITHMS algId)
438 typedef struct cssm_x509_algorithm_identifier {
439 CSSM_OID algorithm;
440 CSSM_DATA parameters;
441 } CSSM_X509_ALGORITHM_IDENTIFIER, *CSSM_X509_ALGORITHM_IDENTIFIER_PTR;
442 #endif
443
444 abort();
445 }
446
447 /*
448 * itemID, used to locate Extended Attributes, is the public key hash for keys.
449 */
450 const CssmData &KeyItem::itemID()
451 {
452 if(mPubKeyHash.length() == 0) {
453 /*
454 * Fetch the attribute from disk.
455 */
456 UInt32 tag = kSecKeyLabel;
457 UInt32 format = 0;
458 SecKeychainAttributeInfo attrInfo = {1, &tag, &format};
459 SecKeychainAttributeList *attrList = NULL;
460 getAttributesAndData(&attrInfo, NULL, &attrList, NULL, NULL);
461 if((attrList == NULL) || (attrList->count != 1)) {
462 MacOSError::throwMe(errSecNoSuchAttr);
463 }
464 mPubKeyHash.copy(attrList->attr->data, attrList->attr->length);
465 freeAttributesAndData(attrList, NULL);
466 }
467 return mPubKeyHash;
468 }
469
470
471 unsigned int
472 KeyItem::strengthInBits(const CSSM_X509_ALGORITHM_IDENTIFIER *algid)
473 {
474 // @@@ Make a context with key based on algid and use that to get the effective keysize and not just the logical one.
475 CSSM_KEY_SIZE keySize = {};
476 CSSM_RETURN rv = CSSM_QueryKeySizeInBits (csp()->handle(),
477 CSSM_INVALID_HANDLE,
478 key(),
479 &keySize);
480 if (rv)
481 return 0;
482
483 return keySize.LogicalKeySizeInBits;
484 }
485
486 const AccessCredentials *
487 KeyItem::getCredentials(
488 CSSM_ACL_AUTHORIZATION_TAG operation,
489 SecCredentialType credentialType)
490 {
491 // @@@ Fix this to actually examine the ACL for this key and consider operation and do the right thing.
492 //AutoAclEntryInfoList aclInfos;
493 //key()->getAcl(aclInfos);
494
495 bool smartcard = keychain() != NULL ? (keychain()->database()->dl()->guid() == gGuidAppleSdCSPDL) : false;
496
497 AclFactory factory;
498 switch (credentialType)
499 {
500 case kSecCredentialTypeDefault:
501 return smartcard?globals().smartcardItemCredentials():globals().itemCredentials();
502 case kSecCredentialTypeWithUI:
503 return smartcard?globals().smartcardItemCredentials():factory.promptCred();
504 case kSecCredentialTypeNoUI:
505 return factory.nullCred();
506 default:
507 MacOSError::throwMe(errSecParam);
508 }
509 }
510
511 bool
512 KeyItem::operator == (KeyItem &other)
513 {
514 if (mKey && *mKey)
515 {
516 // Pointer compare
517 return this == &other;
518 }
519
520 // If keychains are different, then keys are different
521 Keychain otherKeychain = other.keychain();
522 return (mKeychain && otherKeychain && (*mKeychain == *otherKeychain));
523 }
524
525 void
526 KeyItem::createPair(
527 Keychain keychain,
528 CSSM_ALGORITHMS algorithm,
529 uint32 keySizeInBits,
530 CSSM_CC_HANDLE contextHandle,
531 CSSM_KEYUSE publicKeyUsage,
532 uint32 publicKeyAttr,
533 CSSM_KEYUSE privateKeyUsage,
534 uint32 privateKeyAttr,
535 SecPointer<Access> initialAccess,
536 SecPointer<KeyItem> &outPublicKey,
537 SecPointer<KeyItem> &outPrivateKey)
538 {
539 bool freeKeys = false;
540 bool deleteContext = false;
541
542 if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
543 MacOSError::throwMe(errSecInvalidKeychain);
544
545 SSDbImpl* impl = dynamic_cast<SSDbImpl*>(&(*keychain->database()));
546 if (impl == NULL)
547 {
548 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
549 }
550
551 SSDb ssDb(impl);
552 CssmClient::CSP csp(keychain->csp());
553 CssmClient::CSP appleCsp(gGuidAppleCSP);
554
555 // Generate a random label to use initially
556 CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW);
557 uint8 labelBytes[20];
558 CssmData label(labelBytes, sizeof(labelBytes));
559 random.generate(label, (uint32)label.Length);
560
561 // Create a Access::Maker for the initial owner of the private key.
562 ResourceControlContext rcc;
563 memset(&rcc, 0, sizeof(rcc));
564 Access::Maker maker;
565 // @@@ 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.
566 maker.initialOwner(rcc);
567 // Create the cred we need to manipulate the keys until we actually set a new access control for them.
568 const AccessCredentials *cred = maker.cred();
569
570 CSSM_KEY publicCssmKey, privateCssmKey;
571 memset(&publicCssmKey, 0, sizeof(publicCssmKey));
572 memset(&privateCssmKey, 0, sizeof(privateCssmKey));
573
574 CSSM_CC_HANDLE ccHandle = 0;
575
576 Item publicKeyItem, privateKeyItem;
577 try
578 {
579 CSSM_RETURN status;
580 if (contextHandle)
581 ccHandle = contextHandle;
582 else
583 {
584 status = CSSM_CSP_CreateKeyGenContext(csp->handle(), algorithm, keySizeInBits, NULL, NULL, NULL, NULL, NULL, &ccHandle);
585 if (status)
586 CssmError::throwMe(status);
587 deleteContext = true;
588 }
589
590 CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle();
591 CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle;
592 CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } };
593 status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes);
594 if (status)
595 CssmError::throwMe(status);
596
597 // Generate the keypair
598 status = CSSM_GenerateKeyPair(ccHandle, publicKeyUsage, publicKeyAttr, &label, &publicCssmKey, privateKeyUsage, privateKeyAttr, &label, &rcc, &privateCssmKey);
599 if (status)
600 CssmError::throwMe(status);
601 freeKeys = true;
602
603 // Find the keys we just generated in the DL to get SecKeyRef's to them
604 // so we can change the label to be the hash of the public key, and
605 // fix up other attributes.
606
607 // Look up public key in the DLDB.
608 DbAttributes pubDbAttributes;
609 DbUniqueRecord pubUniqueId;
610 SSDbCursor dbPubCursor(ssDb, 1);
611 dbPubCursor->recordType(CSSM_DL_DB_RECORD_PUBLIC_KEY);
612 dbPubCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label);
613 CssmClient::Key publicKey;
614 if (!dbPubCursor->nextKey(&pubDbAttributes, publicKey, pubUniqueId))
615 MacOSError::throwMe(errSecItemNotFound);
616
617 // Look up private key in the DLDB.
618 DbAttributes privDbAttributes;
619 DbUniqueRecord privUniqueId;
620 SSDbCursor dbPrivCursor(ssDb, 1);
621 dbPrivCursor->recordType(CSSM_DL_DB_RECORD_PRIVATE_KEY);
622 dbPrivCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label);
623 CssmClient::Key privateKey;
624 if (!dbPrivCursor->nextKey(&privDbAttributes, privateKey, privUniqueId))
625 MacOSError::throwMe(errSecItemNotFound);
626
627 // Convert reference public key to a raw key so we can use it
628 // in the appleCsp.
629 CssmClient::WrapKey wrap(csp, CSSM_ALGID_NONE);
630 wrap.cred(cred);
631 CssmClient::Key rawPubKey = wrap(publicKey);
632
633 // Calculate the hash of the public key using the appleCSP.
634 CssmClient::PassThrough passThrough(appleCsp);
635 void *outData;
636 CssmData *cssmData;
637
638 /* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the
639 * associated key blob.
640 * Key is specified in CSSM_CSP_CreatePassThroughContext.
641 * Hash is allocated bythe CSP, in the App's memory, and returned
642 * in *outData. */
643 passThrough.key(rawPubKey);
644 passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData);
645 cssmData = reinterpret_cast<CssmData *>(outData);
646 CssmData &pubKeyHash = *cssmData;
647
648 auto_ptr<string>privDescription;
649 auto_ptr<string>pubDescription;
650 try {
651 privDescription.reset(new string(initialAccess->promptDescription()));
652 pubDescription.reset(new string(initialAccess->promptDescription()));
653 }
654 catch(...) {
655 /* this path taken if no promptDescription available, e.g., for complex ACLs */
656 privDescription.reset(new string("Private key"));
657 pubDescription.reset(new string("Public key"));
658 }
659
660 // Set the label of the public key to the public key hash.
661 // Set the PrintName of the public key to the description in the acl.
662 pubDbAttributes.add(kInfoKeyLabel, pubKeyHash);
663 pubDbAttributes.add(kInfoKeyPrintName, *pubDescription);
664 pubUniqueId->modify(CSSM_DL_DB_RECORD_PUBLIC_KEY, &pubDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
665
666 // Set the label of the private key to the public key hash.
667 // Set the PrintName of the private key to the description in the acl.
668 privDbAttributes.add(kInfoKeyLabel, pubKeyHash);
669 privDbAttributes.add(kInfoKeyPrintName, *privDescription);
670 privUniqueId->modify(CSSM_DL_DB_RECORD_PRIVATE_KEY, &privDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
671
672 // @@@ Not exception safe!
673 csp.allocator().free(cssmData->Data);
674 csp.allocator().free(cssmData);
675
676 // Finally fix the acl and owner of the private key to the specified access control settings.
677 initialAccess->setAccess(*privateKey, maker);
678
679 if(publicKeyAttr & CSSM_KEYATTR_PUBLIC_KEY_ENCRYPT) {
680 /*
681 * Make the public key acl completely open.
682 * If the key was not encrypted, it already has a wide-open
683 * ACL (though that is a feature of securityd; it's not
684 * CDSA-specified behavior).
685 */
686 SecPointer<Access> pubKeyAccess(new Access());
687 pubKeyAccess->setAccess(*publicKey, maker);
688 }
689
690 // Create keychain items which will represent the keys.
691 publicKeyItem = keychain->item(CSSM_DL_DB_RECORD_PUBLIC_KEY, pubUniqueId);
692 privateKeyItem = keychain->item(CSSM_DL_DB_RECORD_PRIVATE_KEY, privUniqueId);
693
694 KeyItem* impl = dynamic_cast<KeyItem*>(&(*publicKeyItem));
695 if (impl == NULL)
696 {
697 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
698 }
699
700 outPublicKey = impl;
701
702 impl = dynamic_cast<KeyItem*>(&(*privateKeyItem));
703 if (impl == NULL)
704 {
705 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
706 }
707
708 outPrivateKey = impl;
709 }
710 catch (...)
711 {
712 if (freeKeys)
713 {
714 // Delete the keys if something goes wrong so we don't end up with inaccessible keys in the database.
715 CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, TRUE);
716 CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, TRUE);
717 }
718
719 if (deleteContext)
720 CSSM_DeleteContext(ccHandle);
721
722 throw;
723 }
724
725 if (freeKeys)
726 {
727 CSSM_FreeKey(csp->handle(), NULL, &publicCssmKey, FALSE);
728 CSSM_FreeKey(csp->handle(), NULL, &privateCssmKey, FALSE);
729 }
730
731 if (deleteContext)
732 CSSM_DeleteContext(ccHandle);
733
734 if (keychain && publicKeyItem && privateKeyItem)
735 {
736 keychain->postEvent(kSecAddEvent, publicKeyItem);
737 keychain->postEvent(kSecAddEvent, privateKeyItem);
738 }
739 }
740
741 void
742 KeyItem::importPair(
743 Keychain keychain,
744 const CSSM_KEY &publicWrappedKey,
745 const CSSM_KEY &privateWrappedKey,
746 SecPointer<Access> initialAccess,
747 SecPointer<KeyItem> &outPublicKey,
748 SecPointer<KeyItem> &outPrivateKey)
749 {
750 bool freePublicKey = false;
751 bool freePrivateKey = false;
752 bool deleteContext = false;
753
754 if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
755 MacOSError::throwMe(errSecInvalidKeychain);
756
757 SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*keychain->database()));
758 if (impl == NULL)
759 {
760 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
761 }
762
763 SSDb ssDb(impl);
764 CssmClient::CSP csp(keychain->csp());
765 CssmClient::CSP appleCsp(gGuidAppleCSP);
766
767 // Create a Access::Maker for the initial owner of the private key.
768 ResourceControlContext rcc;
769 memset(&rcc, 0, sizeof(rcc));
770 Access::Maker maker(Allocator::standard(), Access::Maker::kAnyMakerType);
771 // @@@ Potentially provide a credential argument which allows us to unwrap keys in the csp.
772 // Currently the CSP lets anyone do this, but we might restrict this in the future, e.g.
773 // a smartcard could require out of band pin entry before a key can be generated.
774 maker.initialOwner(rcc);
775 // Create the cred we need to manipulate the keys until we actually set a new access control for them.
776 const AccessCredentials *cred = maker.cred();
777
778 CSSM_KEY publicCssmKey, privateCssmKey;
779 memset(&publicCssmKey, 0, sizeof(publicCssmKey));
780 memset(&privateCssmKey, 0, sizeof(privateCssmKey));
781
782 CSSM_CC_HANDLE ccHandle = 0;
783
784 Item publicKeyItem, privateKeyItem;
785 try
786 {
787 CSSM_RETURN status;
788
789 // Calculate the hash of the public key using the appleCSP.
790 CssmClient::PassThrough passThrough(appleCsp);
791 void *outData;
792 CssmData *cssmData;
793
794 /* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the
795 * associated key blob.
796 * Key is specified in CSSM_CSP_CreatePassThroughContext.
797 * Hash is allocated bythe CSP, in the App's memory, and returned
798 * in *outData. */
799 passThrough.key(&publicWrappedKey);
800 passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData);
801 cssmData = reinterpret_cast<CssmData *>(outData);
802 CssmData &pubKeyHash = *cssmData;
803
804 status = CSSM_CSP_CreateSymmetricContext(csp->handle(), publicWrappedKey.KeyHeader.WrapAlgorithmId, CSSM_ALGMODE_NONE, NULL, NULL, NULL, CSSM_PADDING_NONE, NULL, &ccHandle);
805 if (status)
806 CssmError::throwMe(status);
807 deleteContext = true;
808
809 CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle();
810 CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle;
811 CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } };
812 status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes);
813 if (status)
814 CssmError::throwMe(status);
815
816 // Unwrap the the keys
817 CSSM_DATA descriptiveData = {0, NULL};
818
819 status = CSSM_UnwrapKey(
820 ccHandle,
821 NULL,
822 &publicWrappedKey,
823 publicWrappedKey.KeyHeader.KeyUsage,
824 publicWrappedKey.KeyHeader.KeyAttr | CSSM_KEYATTR_PERMANENT,
825 &pubKeyHash,
826 &rcc,
827 &publicCssmKey,
828 &descriptiveData);
829
830 if (status)
831 CssmError::throwMe(status);
832 freePublicKey = true;
833
834 if (descriptiveData.Data != NULL)
835 free (descriptiveData.Data);
836
837 status = CSSM_UnwrapKey(
838 ccHandle,
839 NULL,
840 &privateWrappedKey,
841 privateWrappedKey.KeyHeader.KeyUsage,
842 privateWrappedKey.KeyHeader.KeyAttr | CSSM_KEYATTR_PERMANENT,
843 &pubKeyHash,
844 &rcc,
845 &privateCssmKey,
846 &descriptiveData);
847
848 if (status)
849 CssmError::throwMe(status);
850
851 if (descriptiveData.Data != NULL)
852 free (descriptiveData.Data);
853
854 freePrivateKey = true;
855
856 // Find the keys we just generated in the DL to get SecKeyRefs to them
857 // so we can change the label to be the hash of the public key, and
858 // fix up other attributes.
859
860 // Look up public key in the DLDB.
861 DbAttributes pubDbAttributes;
862 DbUniqueRecord pubUniqueId;
863 SSDbCursor dbPubCursor(ssDb, 1);
864 dbPubCursor->recordType(CSSM_DL_DB_RECORD_PUBLIC_KEY);
865 dbPubCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, pubKeyHash);
866 CssmClient::Key publicKey;
867 if (!dbPubCursor->nextKey(&pubDbAttributes, publicKey, pubUniqueId))
868 MacOSError::throwMe(errSecItemNotFound);
869
870 // Look up private key in the DLDB.
871 DbAttributes privDbAttributes;
872 DbUniqueRecord privUniqueId;
873 SSDbCursor dbPrivCursor(ssDb, 1);
874 dbPrivCursor->recordType(CSSM_DL_DB_RECORD_PRIVATE_KEY);
875 dbPrivCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, pubKeyHash);
876 CssmClient::Key privateKey;
877 if (!dbPrivCursor->nextKey(&privDbAttributes, privateKey, privUniqueId))
878 MacOSError::throwMe(errSecItemNotFound);
879
880 // @@@ Not exception safe!
881 csp.allocator().free(cssmData->Data);
882 csp.allocator().free(cssmData);
883
884 auto_ptr<string>privDescription;
885 auto_ptr<string>pubDescription;
886 try {
887 privDescription.reset(new string(initialAccess->promptDescription()));
888 pubDescription.reset(new string(initialAccess->promptDescription()));
889 }
890 catch(...) {
891 /* this path taken if no promptDescription available, e.g., for complex ACLs */
892 privDescription.reset(new string("Private key"));
893 pubDescription.reset(new string("Public key"));
894 }
895
896 // Set the label of the public key to the public key hash.
897 // Set the PrintName of the public key to the description in the acl.
898 pubDbAttributes.add(kInfoKeyPrintName, *pubDescription);
899 pubUniqueId->modify(CSSM_DL_DB_RECORD_PUBLIC_KEY, &pubDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
900
901 // Set the label of the private key to the public key hash.
902 // Set the PrintName of the private key to the description in the acl.
903 privDbAttributes.add(kInfoKeyPrintName, *privDescription);
904 privUniqueId->modify(CSSM_DL_DB_RECORD_PRIVATE_KEY, &privDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
905
906 // Finally fix the acl and owner of the private key to the specified access control settings.
907 initialAccess->setAccess(*privateKey, maker);
908
909 // Make the public key acl completely open
910 SecPointer<Access> pubKeyAccess(new Access());
911 pubKeyAccess->setAccess(*publicKey, maker);
912
913 // Create keychain items which will represent the keys.
914 publicKeyItem = keychain->item(CSSM_DL_DB_RECORD_PUBLIC_KEY, pubUniqueId);
915 privateKeyItem = keychain->item(CSSM_DL_DB_RECORD_PRIVATE_KEY, privUniqueId);
916
917 KeyItem* impl = dynamic_cast<KeyItem*>(&(*publicKeyItem));
918 if (impl == NULL)
919 {
920 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
921 }
922
923 outPublicKey = impl;
924
925 impl = dynamic_cast<KeyItem*>(&(*privateKeyItem));
926 if (impl == NULL)
927 {
928 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
929 }
930 outPrivateKey = impl;
931 }
932 catch (...)
933 {
934 if (freePublicKey)
935 CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, TRUE);
936 if (freePrivateKey)
937 CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, TRUE);
938
939 if (deleteContext)
940 CSSM_DeleteContext(ccHandle);
941
942 throw;
943 }
944
945 if (freePublicKey)
946 CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, FALSE);
947 if (freePrivateKey)
948 CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, FALSE);
949
950 if (deleteContext)
951 CSSM_DeleteContext(ccHandle);
952
953 if (keychain && publicKeyItem && privateKeyItem)
954 {
955 KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, publicKeyItem);
956 KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, privateKeyItem);
957 }
958 }
959
960 SecPointer<KeyItem>
961 KeyItem::generateWithAttributes(const SecKeychainAttributeList *attrList,
962 Keychain keychain,
963 CSSM_ALGORITHMS algorithm,
964 uint32 keySizeInBits,
965 CSSM_CC_HANDLE contextHandle,
966 CSSM_KEYUSE keyUsage,
967 uint32 keyAttr,
968 SecPointer<Access> initialAccess)
969 {
970 CssmClient::CSP appleCsp(gGuidAppleCSP);
971 CssmClient::CSP csp(NULL);
972 SSDb ssDb(NULL);
973 uint8 labelBytes[20];
974 CssmData label(labelBytes, sizeof(labelBytes));
975 bool freeKey = false;
976 bool deleteContext = false;
977 const CSSM_DATA *plabel = NULL;
978
979 if (keychain)
980 {
981 if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
982 MacOSError::throwMe(errSecInvalidKeychain);
983
984 SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*keychain->database()));
985 if (impl == NULL)
986 {
987 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
988 }
989
990 ssDb = SSDb(impl);
991 csp = keychain->csp();
992
993 // Generate a random label to use initially
994 CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW);
995 random.generate(label, (uint32)label.Length);
996 plabel = &label;
997 }
998 else
999 {
1000 // Not a persistent key so create it in the regular csp
1001 csp = appleCsp;
1002 }
1003
1004 // Create a Access::Maker for the initial owner of the private key.
1005 ResourceControlContext *prcc = NULL, rcc;
1006 const AccessCredentials *cred = NULL;
1007 Access::Maker maker;
1008 if (keychain && initialAccess)
1009 {
1010 memset(&rcc, 0, sizeof(rcc));
1011 // @@@ Potentially provide a credential argument which allows us to generate keys in the csp.
1012 // Currently the CSP lets anyone do this, but we might restrict this in the future, e.g. a smartcard
1013 // could require out-of-band pin entry before a key can be generated.
1014 maker.initialOwner(rcc);
1015 // Create the cred we need to manipulate the keys until we actually set a new access control for them.
1016 cred = maker.cred();
1017 prcc = &rcc;
1018 }
1019
1020 CSSM_KEY cssmKey;
1021
1022 CSSM_CC_HANDLE ccHandle = 0;
1023
1024 Item keyItem;
1025 try
1026 {
1027 CSSM_RETURN status;
1028 if (contextHandle)
1029 ccHandle = contextHandle;
1030 else
1031 {
1032 status = CSSM_CSP_CreateKeyGenContext(csp->handle(), algorithm, keySizeInBits, NULL, NULL, NULL, NULL, NULL, &ccHandle);
1033 if (status)
1034 CssmError::throwMe(status);
1035 deleteContext = true;
1036 }
1037
1038 if (ssDb)
1039 {
1040 CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle();
1041 CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle;
1042 CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } };
1043 status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes);
1044 if (status)
1045 CssmError::throwMe(status);
1046
1047 keyAttr |= CSSM_KEYATTR_PERMANENT;
1048 }
1049
1050 // Generate the key
1051 status = CSSM_GenerateKey(ccHandle, keyUsage, keyAttr, plabel, prcc, &cssmKey);
1052 if (status)
1053 CssmError::throwMe(status);
1054
1055 if (ssDb)
1056 {
1057 freeKey = true;
1058 // Find the key we just generated in the DL and get a SecKeyRef
1059 // so we can specify the label attribute(s) and initial ACL.
1060
1061 // Look up key in the DLDB.
1062 DbAttributes dbAttributes;
1063 DbUniqueRecord uniqueId;
1064 SSDbCursor dbCursor(ssDb, 1);
1065 dbCursor->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY);
1066 dbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label);
1067 CssmClient::Key key;
1068 if (!dbCursor->nextKey(&dbAttributes, key, uniqueId))
1069 MacOSError::throwMe(errSecItemNotFound);
1070
1071 // Set the initial label, application label, and application tag (if provided)
1072 if (attrList) {
1073 DbAttributes newDbAttributes;
1074 SSDbCursor otherDbCursor(ssDb, 1);
1075 otherDbCursor->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY);
1076 bool checkForDuplicates = false;
1077
1078 for (UInt32 index=0; index < attrList->count; index++) {
1079 SecKeychainAttribute attr = attrList->attr[index];
1080 CssmData attrData(attr.data, attr.length);
1081 if (attr.tag == kSecKeyPrintName) {
1082 newDbAttributes.add(kInfoKeyPrintName, attrData);
1083 }
1084 if (attr.tag == kSecKeyLabel) {
1085 newDbAttributes.add(kInfoKeyLabel, attrData);
1086 otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, attrData);
1087 checkForDuplicates = true;
1088 }
1089 if (attr.tag == kSecKeyApplicationTag) {
1090 newDbAttributes.add(kInfoKeyApplicationTag, attrData);
1091 otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyApplicationTag, attrData);
1092 checkForDuplicates = true;
1093 }
1094 }
1095
1096 DbAttributes otherDbAttributes;
1097 DbUniqueRecord otherUniqueId;
1098 CssmClient::Key otherKey;
1099 if (checkForDuplicates && otherDbCursor->nextKey(&otherDbAttributes, otherKey, otherUniqueId))
1100 MacOSError::throwMe(errSecDuplicateItem);
1101
1102 uniqueId->modify(CSSM_DL_DB_RECORD_SYMMETRIC_KEY, &newDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
1103 }
1104
1105 // Finally, fix the acl and owner of the key to the specified access control settings.
1106 if (initialAccess)
1107 initialAccess->setAccess(*key, maker);
1108
1109 // Create keychain item which will represent the key.
1110 keyItem = keychain->item(CSSM_DL_DB_RECORD_SYMMETRIC_KEY, uniqueId);
1111 }
1112 else
1113 {
1114 CssmClient::Key tempKey(csp, cssmKey);
1115 keyItem = new KeyItem(tempKey);
1116 }
1117 }
1118 catch (...)
1119 {
1120 if (freeKey)
1121 {
1122 // Delete the key if something goes wrong so we don't end up with inaccessible keys in the database.
1123 CSSM_FreeKey(csp->handle(), cred, &cssmKey, TRUE);
1124 }
1125
1126 if (deleteContext)
1127 CSSM_DeleteContext(ccHandle);
1128
1129 throw;
1130 }
1131
1132 if (freeKey)
1133 {
1134 CSSM_FreeKey(csp->handle(), NULL, &cssmKey, FALSE);
1135 }
1136
1137 if (deleteContext)
1138 CSSM_DeleteContext(ccHandle);
1139
1140 if (keychain && keyItem)
1141 keychain->postEvent(kSecAddEvent, keyItem);
1142
1143 KeyItem* item = dynamic_cast<KeyItem*>(&*keyItem);
1144 if (item == NULL)
1145 {
1146 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
1147 }
1148
1149 return item;
1150 }
1151
1152 SecPointer<KeyItem>
1153 KeyItem::generate(Keychain keychain,
1154 CSSM_ALGORITHMS algorithm,
1155 uint32 keySizeInBits,
1156 CSSM_CC_HANDLE contextHandle,
1157 CSSM_KEYUSE keyUsage,
1158 uint32 keyAttr,
1159 SecPointer<Access> initialAccess)
1160 {
1161 return KeyItem::generateWithAttributes(NULL, keychain,
1162 algorithm, keySizeInBits, contextHandle,
1163 keyUsage, keyAttr, initialAccess);
1164 }
1165
1166
1167 void KeyItem::RawSign(SecPadding padding, CSSM_DATA dataToSign, const AccessCredentials *credentials, CSSM_DATA& signature)
1168 {
1169 CSSM_ALGORITHMS baseAlg = key()->header().algorithm();
1170
1171 if ((baseAlg != CSSM_ALGID_RSA) && (baseAlg != CSSM_ALGID_ECDSA))
1172 {
1173 MacOSError::throwMe(errSecParam);
1174 }
1175
1176 CSSM_ALGORITHMS paddingAlg = CSSM_PADDING_PKCS1;
1177
1178 switch (padding)
1179 {
1180 case kSecPaddingPKCS1:
1181 {
1182 paddingAlg = CSSM_PADDING_PKCS1;
1183 break;
1184 }
1185
1186 case kSecPaddingPKCS1MD2:
1187 {
1188 baseAlg = CSSM_ALGID_MD2WithRSA;
1189 break;
1190 }
1191
1192 case kSecPaddingPKCS1MD5:
1193 {
1194 baseAlg = CSSM_ALGID_MD5WithRSA;
1195 break;
1196 }
1197
1198 case kSecPaddingPKCS1SHA1:
1199 {
1200 baseAlg = CSSM_ALGID_SHA1WithRSA;
1201 break;
1202 }
1203
1204 case kSecPaddingSigRaw:
1205 {
1206 paddingAlg = CSSM_PADDING_SIGRAW;
1207 break;
1208 }
1209
1210 default:
1211 {
1212 paddingAlg = CSSM_PADDING_NONE;
1213 break;
1214 }
1215 }
1216
1217 Sign signContext(csp(), baseAlg);
1218 signContext.key(key());
1219 signContext.cred(credentials);
1220 // Fields required for CSSM_CSP_CreateSignatureContext set above. Using add instead of set ensures
1221 // that the context is constructed before the set is attempted, which would fail silently otherwise.
1222 signContext.add(CSSM_ATTRIBUTE_PADDING, paddingAlg);
1223
1224 CssmData data(dataToSign.Data, dataToSign.Length);
1225 signContext.sign(data);
1226
1227 CssmData sig(signature.Data, signature.Length);
1228 signContext(sig); // yes, this is an accessor. Believe it, or not.
1229 signature.Length = sig.length();
1230 }
1231
1232
1233
1234 void KeyItem::RawVerify(SecPadding padding, CSSM_DATA dataToVerify, const AccessCredentials *credentials, CSSM_DATA sig)
1235 {
1236 CSSM_ALGORITHMS baseAlg = key()->header().algorithm();
1237 if ((baseAlg != CSSM_ALGID_RSA) && (baseAlg != CSSM_ALGID_ECDSA))
1238 {
1239 MacOSError::throwMe(errSecParam);
1240 }
1241
1242 CSSM_ALGORITHMS paddingAlg = CSSM_PADDING_PKCS1;
1243
1244 switch (padding)
1245 {
1246 case kSecPaddingPKCS1:
1247 {
1248 paddingAlg = CSSM_PADDING_PKCS1;
1249 break;
1250 }
1251
1252 case kSecPaddingPKCS1MD2:
1253 {
1254 baseAlg = CSSM_ALGID_MD2WithRSA;
1255 break;
1256 }
1257
1258 case kSecPaddingPKCS1MD5:
1259 {
1260 baseAlg = CSSM_ALGID_MD5WithRSA;
1261 break;
1262 }
1263
1264 case kSecPaddingPKCS1SHA1:
1265 {
1266 baseAlg = CSSM_ALGID_SHA1WithRSA;
1267 break;
1268 }
1269
1270 case kSecPaddingSigRaw:
1271 {
1272 paddingAlg = CSSM_PADDING_SIGRAW;
1273 break;
1274 }
1275
1276 default:
1277 {
1278 paddingAlg = CSSM_PADDING_NONE;
1279 break;
1280 }
1281 }
1282
1283 Verify verifyContext(csp(), baseAlg);
1284 verifyContext.key(key());
1285 verifyContext.cred(credentials);
1286 // Fields required for CSSM_CSP_CreateSignatureContext set above. Using add instead of set ensures
1287 // that the context is constructed before the set is attempted, which would fail silently otherwise.
1288 verifyContext.add(CSSM_ATTRIBUTE_PADDING, paddingAlg);
1289
1290 CssmData data(dataToVerify.Data, dataToVerify.Length);
1291 CssmData signature(sig.Data, sig.Length);
1292 verifyContext.verify(data, signature);
1293 }
1294
1295
1296
1297 void KeyItem::Encrypt(SecPadding padding, CSSM_DATA dataToEncrypt, const AccessCredentials *credentials, CSSM_DATA& encryptedData)
1298 {
1299 CSSM_ALGORITHMS baseAlg = key()->header().algorithm();
1300 if (baseAlg != CSSM_ALGID_RSA)
1301 {
1302 MacOSError::throwMe(errSecParam);
1303 }
1304
1305 CSSM_ALGORITHMS paddingAlg = CSSM_PADDING_PKCS1;
1306
1307 switch (padding)
1308 {
1309 case kSecPaddingPKCS1:
1310 {
1311 paddingAlg = CSSM_PADDING_PKCS1;
1312 break;
1313 }
1314
1315 default:
1316 {
1317 paddingAlg = CSSM_PADDING_NONE;
1318 break;
1319 }
1320 }
1321
1322 CssmClient::Encrypt encryptContext(csp(), baseAlg);
1323 encryptContext.key(key());
1324 encryptContext.padding(paddingAlg);
1325 encryptContext.cred(credentials);
1326
1327 CssmData inData(dataToEncrypt.Data, dataToEncrypt.Length);
1328 CssmData outData(encryptedData.Data, encryptedData.Length);
1329 CssmData remData((void*) NULL, 0);
1330
1331 encryptedData.Length = encryptContext.encrypt(inData, outData, remData);
1332 }
1333
1334
1335
1336 void KeyItem::Decrypt(SecPadding padding, CSSM_DATA dataToDecrypt, const AccessCredentials *credentials, CSSM_DATA& decryptedData)
1337 {
1338 CSSM_ALGORITHMS baseAlg = key()->header().algorithm();
1339 if (baseAlg != CSSM_ALGID_RSA)
1340 {
1341 MacOSError::throwMe(errSecParam);
1342 }
1343
1344 CSSM_ALGORITHMS paddingAlg = CSSM_PADDING_PKCS1;
1345
1346 switch (padding)
1347 {
1348 case kSecPaddingPKCS1:
1349 {
1350 paddingAlg = CSSM_PADDING_PKCS1;
1351 break;
1352 }
1353
1354
1355 default:
1356 {
1357 paddingAlg = CSSM_PADDING_NONE;
1358 break;
1359 }
1360 }
1361
1362 CssmClient::Decrypt decryptContext(csp(), baseAlg);
1363 decryptContext.key(key());
1364 decryptContext.padding(paddingAlg);
1365 decryptContext.cred(credentials);
1366
1367 CssmData inData(dataToDecrypt.Data, dataToDecrypt.Length);
1368 CssmData outData(decryptedData.Data, decryptedData.Length);
1369 CssmData remData((void*) NULL, 0);
1370 decryptedData.Length = decryptContext.decrypt(inData, outData, remData);
1371 if (remData.Data != NULL)
1372 {
1373 free(remData.Data);
1374 }
1375 }
1376
1377 CFHashCode KeyItem::hash()
1378 {
1379 CFHashCode result = 0;
1380 const CSSM_KEY *cssmKey = key();
1381 if (NULL != cssmKey)
1382 {
1383 unsigned char digest[CC_SHA256_DIGEST_LENGTH];
1384
1385 CFIndex size_of_data = sizeof(CSSM_KEYHEADER) + cssmKey->KeyData.Length;
1386
1387 CFMutableDataRef temp_cfdata = CFDataCreateMutable(kCFAllocatorDefault, size_of_data);
1388 if (NULL == temp_cfdata)
1389 {
1390 return result;
1391 }
1392
1393 CFDataAppendBytes(temp_cfdata, (const UInt8 *)cssmKey, sizeof(CSSM_KEYHEADER));
1394 CFDataAppendBytes(temp_cfdata, cssmKey->KeyData.Data, cssmKey->KeyData.Length);
1395
1396 if (size_of_data < 80)
1397 {
1398 // If it is less than 80 bytes then CFData can be used
1399 result = CFHash(temp_cfdata);
1400 CFRelease(temp_cfdata);
1401 }
1402 // CFData truncates its hash value to 80 bytes. ????
1403 // In order to do the 'right thing' a SHA 256 hash will be used to
1404 // include all of the data
1405 else
1406 {
1407 memset(digest, 0, CC_SHA256_DIGEST_LENGTH);
1408
1409 CC_SHA256((const void *)CFDataGetBytePtr(temp_cfdata), (CC_LONG)CFDataGetLength(temp_cfdata), digest);
1410
1411 CFDataRef data_to_hash = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
1412 (const UInt8 *)digest, CC_SHA256_DIGEST_LENGTH, kCFAllocatorNull);
1413 result = CFHash(data_to_hash);
1414 CFRelease(data_to_hash);
1415 CFRelease(temp_cfdata);
1416 }
1417 }
1418 return result;
1419 }
1420