]> git.saurik.com Git - apple/security.git/blob - Security/libsecurity_keychain/lib/KeyItem.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / Security / 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 if (!mKey)
403 {
404 CssmClient::SSDbUniqueRecord uniqueId(ssDbUniqueRecord());
405 CssmDataContainer dataBlob(uniqueId->allocator());
406 uniqueId->get(NULL, &dataBlob);
407 mKey = CssmClient::Key(uniqueId->database()->csp(), *reinterpret_cast<CssmKey *>(dataBlob.Data));
408 }
409
410 return mKey;
411 }
412
413 CssmClient::CSP
414 KeyItem::csp()
415 {
416 return key()->csp();
417 }
418
419
420 const CSSM_X509_ALGORITHM_IDENTIFIER&
421 KeyItem::algorithmIdentifier()
422 {
423 #if 0
424 CssmKey *mKey;
425 CSSM_KEY_TYPE algorithm
426 CSSM_KEY_PTR cssmKey = (CSSM_KEY_PTR)thisData->Data;
427 cssmKey->KeyHeader
428 static void printKeyHeader(
429 const CSSM_KEYHEADER &hdr)
430 {
431 printf(" Algorithm : ");
432 switch(hdr.AlgorithmId) {
433 CSSM_X509_ALGORITHM_IDENTIFIER algID;
434
435 CSSM_OID *CL_algToOid(
436 CSSM_ALGORITHMS algId)
437 typedef struct cssm_x509_algorithm_identifier {
438 CSSM_OID algorithm;
439 CSSM_DATA parameters;
440 } CSSM_X509_ALGORITHM_IDENTIFIER, *CSSM_X509_ALGORITHM_IDENTIFIER_PTR;
441 #endif
442
443 abort();
444 }
445
446 /*
447 * itemID, used to locate Extended Attributes, is the public key hash for keys.
448 */
449 const CssmData &KeyItem::itemID()
450 {
451 if(mPubKeyHash.length() == 0) {
452 /*
453 * Fetch the attribute from disk.
454 */
455 UInt32 tag = kSecKeyLabel;
456 UInt32 format = 0;
457 SecKeychainAttributeInfo attrInfo = {1, &tag, &format};
458 SecKeychainAttributeList *attrList = NULL;
459 getAttributesAndData(&attrInfo, NULL, &attrList, NULL, NULL);
460 if((attrList == NULL) || (attrList->count != 1)) {
461 MacOSError::throwMe(errSecNoSuchAttr);
462 }
463 mPubKeyHash.copy(attrList->attr->data, attrList->attr->length);
464 freeAttributesAndData(attrList, NULL);
465 }
466 return mPubKeyHash;
467 }
468
469
470 unsigned int
471 KeyItem::strengthInBits(const CSSM_X509_ALGORITHM_IDENTIFIER *algid)
472 {
473 // @@@ Make a context with key based on algid and use that to get the effective keysize and not just the logical one.
474 CSSM_KEY_SIZE keySize = {};
475 CSSM_RETURN rv = CSSM_QueryKeySizeInBits (csp()->handle(),
476 CSSM_INVALID_HANDLE,
477 key(),
478 &keySize);
479 if (rv)
480 return 0;
481
482 return keySize.LogicalKeySizeInBits;
483 }
484
485 const AccessCredentials *
486 KeyItem::getCredentials(
487 CSSM_ACL_AUTHORIZATION_TAG operation,
488 SecCredentialType credentialType)
489 {
490 // @@@ Fix this to actually examine the ACL for this key and consider operation and do the right thing.
491 //AutoAclEntryInfoList aclInfos;
492 //key()->getAcl(aclInfos);
493
494 bool smartcard = keychain() != NULL ? (keychain()->database()->dl()->guid() == gGuidAppleSdCSPDL) : false;
495
496 AclFactory factory;
497 switch (credentialType)
498 {
499 case kSecCredentialTypeDefault:
500 return smartcard?globals().smartcardItemCredentials():globals().itemCredentials();
501 case kSecCredentialTypeWithUI:
502 return smartcard?globals().smartcardItemCredentials():factory.promptCred();
503 case kSecCredentialTypeNoUI:
504 return factory.nullCred();
505 default:
506 MacOSError::throwMe(errSecParam);
507 }
508 }
509
510 bool
511 KeyItem::operator == (KeyItem &other)
512 {
513 if (mKey && *mKey)
514 {
515 // Pointer compare
516 return this == &other;
517 }
518
519 // If keychains are different, then keys are different
520 Keychain otherKeychain = other.keychain();
521 return (mKeychain && otherKeychain && (*mKeychain == *otherKeychain));
522 }
523
524 void
525 KeyItem::createPair(
526 Keychain keychain,
527 CSSM_ALGORITHMS algorithm,
528 uint32 keySizeInBits,
529 CSSM_CC_HANDLE contextHandle,
530 CSSM_KEYUSE publicKeyUsage,
531 uint32 publicKeyAttr,
532 CSSM_KEYUSE privateKeyUsage,
533 uint32 privateKeyAttr,
534 SecPointer<Access> initialAccess,
535 SecPointer<KeyItem> &outPublicKey,
536 SecPointer<KeyItem> &outPrivateKey)
537 {
538 bool freeKeys = false;
539 bool deleteContext = false;
540
541 if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
542 MacOSError::throwMe(errSecInvalidKeychain);
543
544 SSDbImpl* impl = dynamic_cast<SSDbImpl*>(&(*keychain->database()));
545 if (impl == NULL)
546 {
547 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
548 }
549
550 SSDb ssDb(impl);
551 CssmClient::CSP csp(keychain->csp());
552 CssmClient::CSP appleCsp(gGuidAppleCSP);
553
554 // Generate a random label to use initially
555 CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW);
556 uint8 labelBytes[20];
557 CssmData label(labelBytes, sizeof(labelBytes));
558 random.generate(label, (uint32)label.Length);
559
560 // Create a Access::Maker for the initial owner of the private key.
561 ResourceControlContext rcc;
562 memset(&rcc, 0, sizeof(rcc));
563 Access::Maker maker;
564 // @@@ 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.
565 maker.initialOwner(rcc);
566 // Create the cred we need to manipulate the keys until we actually set a new access control for them.
567 const AccessCredentials *cred = maker.cred();
568
569 CSSM_KEY publicCssmKey, privateCssmKey;
570 memset(&publicCssmKey, 0, sizeof(publicCssmKey));
571 memset(&privateCssmKey, 0, sizeof(privateCssmKey));
572
573 CSSM_CC_HANDLE ccHandle = 0;
574
575 Item publicKeyItem, privateKeyItem;
576 try
577 {
578 CSSM_RETURN status;
579 if (contextHandle)
580 ccHandle = contextHandle;
581 else
582 {
583 status = CSSM_CSP_CreateKeyGenContext(csp->handle(), algorithm, keySizeInBits, NULL, NULL, NULL, NULL, NULL, &ccHandle);
584 if (status)
585 CssmError::throwMe(status);
586 deleteContext = true;
587 }
588
589 CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle();
590 CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle;
591 CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } };
592 status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes);
593 if (status)
594 CssmError::throwMe(status);
595
596 // Generate the keypair
597 status = CSSM_GenerateKeyPair(ccHandle, publicKeyUsage, publicKeyAttr, &label, &publicCssmKey, privateKeyUsage, privateKeyAttr, &label, &rcc, &privateCssmKey);
598 if (status)
599 CssmError::throwMe(status);
600 freeKeys = true;
601
602 // Find the keys we just generated in the DL to get SecKeyRef's to them
603 // so we can change the label to be the hash of the public key, and
604 // fix up other attributes.
605
606 // Look up public key in the DLDB.
607 DbAttributes pubDbAttributes;
608 DbUniqueRecord pubUniqueId;
609 SSDbCursor dbPubCursor(ssDb, 1);
610 dbPubCursor->recordType(CSSM_DL_DB_RECORD_PUBLIC_KEY);
611 dbPubCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label);
612 CssmClient::Key publicKey;
613 if (!dbPubCursor->nextKey(&pubDbAttributes, publicKey, pubUniqueId))
614 MacOSError::throwMe(errSecItemNotFound);
615
616 // Look up private key in the DLDB.
617 DbAttributes privDbAttributes;
618 DbUniqueRecord privUniqueId;
619 SSDbCursor dbPrivCursor(ssDb, 1);
620 dbPrivCursor->recordType(CSSM_DL_DB_RECORD_PRIVATE_KEY);
621 dbPrivCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label);
622 CssmClient::Key privateKey;
623 if (!dbPrivCursor->nextKey(&privDbAttributes, privateKey, privUniqueId))
624 MacOSError::throwMe(errSecItemNotFound);
625
626 // Convert reference public key to a raw key so we can use it
627 // in the appleCsp.
628 CssmClient::WrapKey wrap(csp, CSSM_ALGID_NONE);
629 wrap.cred(cred);
630 CssmClient::Key rawPubKey = wrap(publicKey);
631
632 // Calculate the hash of the public key using the appleCSP.
633 CssmClient::PassThrough passThrough(appleCsp);
634 void *outData;
635 CssmData *cssmData;
636
637 /* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the
638 * associated key blob.
639 * Key is specified in CSSM_CSP_CreatePassThroughContext.
640 * Hash is allocated bythe CSP, in the App's memory, and returned
641 * in *outData. */
642 passThrough.key(rawPubKey);
643 passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData);
644 cssmData = reinterpret_cast<CssmData *>(outData);
645 CssmData &pubKeyHash = *cssmData;
646
647 auto_ptr<string>privDescription;
648 auto_ptr<string>pubDescription;
649 try {
650 privDescription.reset(new string(initialAccess->promptDescription()));
651 pubDescription.reset(new string(initialAccess->promptDescription()));
652 }
653 catch(...) {
654 /* this path taken if no promptDescription available, e.g., for complex ACLs */
655 privDescription.reset(new string("Private key"));
656 pubDescription.reset(new string("Public key"));
657 }
658
659 // Set the label of the public key to the public key hash.
660 // Set the PrintName of the public key to the description in the acl.
661 pubDbAttributes.add(kInfoKeyLabel, pubKeyHash);
662 pubDbAttributes.add(kInfoKeyPrintName, *pubDescription);
663 pubUniqueId->modify(CSSM_DL_DB_RECORD_PUBLIC_KEY, &pubDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
664
665 // Set the label of the private key to the public key hash.
666 // Set the PrintName of the private key to the description in the acl.
667 privDbAttributes.add(kInfoKeyLabel, pubKeyHash);
668 privDbAttributes.add(kInfoKeyPrintName, *privDescription);
669 privUniqueId->modify(CSSM_DL_DB_RECORD_PRIVATE_KEY, &privDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
670
671 // @@@ Not exception safe!
672 csp.allocator().free(cssmData->Data);
673 csp.allocator().free(cssmData);
674
675 // Finally fix the acl and owner of the private key to the specified access control settings.
676 initialAccess->setAccess(*privateKey, maker);
677
678 if(publicKeyAttr & CSSM_KEYATTR_PUBLIC_KEY_ENCRYPT) {
679 /*
680 * Make the public key acl completely open.
681 * If the key was not encrypted, it already has a wide-open
682 * ACL (though that is a feature of securityd; it's not
683 * CDSA-specified behavior).
684 */
685 SecPointer<Access> pubKeyAccess(new Access());
686 pubKeyAccess->setAccess(*publicKey, maker);
687 }
688
689 // Create keychain items which will represent the keys.
690 publicKeyItem = keychain->item(CSSM_DL_DB_RECORD_PUBLIC_KEY, pubUniqueId);
691 privateKeyItem = keychain->item(CSSM_DL_DB_RECORD_PRIVATE_KEY, privUniqueId);
692
693 KeyItem* impl = dynamic_cast<KeyItem*>(&(*publicKeyItem));
694 if (impl == NULL)
695 {
696 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
697 }
698
699 outPublicKey = impl;
700
701 impl = dynamic_cast<KeyItem*>(&(*privateKeyItem));
702 if (impl == NULL)
703 {
704 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
705 }
706
707 outPrivateKey = impl;
708 }
709 catch (...)
710 {
711 if (freeKeys)
712 {
713 // Delete the keys if something goes wrong so we don't end up with inaccessible keys in the database.
714 CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, TRUE);
715 CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, TRUE);
716 }
717
718 if (deleteContext)
719 CSSM_DeleteContext(ccHandle);
720
721 throw;
722 }
723
724 if (freeKeys)
725 {
726 CSSM_FreeKey(csp->handle(), NULL, &publicCssmKey, FALSE);
727 CSSM_FreeKey(csp->handle(), NULL, &privateCssmKey, FALSE);
728 }
729
730 if (deleteContext)
731 CSSM_DeleteContext(ccHandle);
732
733 if (keychain && publicKeyItem && privateKeyItem)
734 {
735 keychain->postEvent(kSecAddEvent, publicKeyItem);
736 keychain->postEvent(kSecAddEvent, privateKeyItem);
737 }
738 }
739
740 void
741 KeyItem::importPair(
742 Keychain keychain,
743 const CSSM_KEY &publicWrappedKey,
744 const CSSM_KEY &privateWrappedKey,
745 SecPointer<Access> initialAccess,
746 SecPointer<KeyItem> &outPublicKey,
747 SecPointer<KeyItem> &outPrivateKey)
748 {
749 bool freePublicKey = false;
750 bool freePrivateKey = false;
751 bool deleteContext = false;
752
753 if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
754 MacOSError::throwMe(errSecInvalidKeychain);
755
756 SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*keychain->database()));
757 if (impl == NULL)
758 {
759 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
760 }
761
762 SSDb ssDb(impl);
763 CssmClient::CSP csp(keychain->csp());
764 CssmClient::CSP appleCsp(gGuidAppleCSP);
765
766 // Create a Access::Maker for the initial owner of the private key.
767 ResourceControlContext rcc;
768 memset(&rcc, 0, sizeof(rcc));
769 Access::Maker maker(Allocator::standard(), Access::Maker::kAnyMakerType);
770 // @@@ Potentially provide a credential argument which allows us to unwrap keys in the csp.
771 // Currently the CSP lets anyone do this, but we might restrict this in the future, e.g.
772 // a smartcard could require out of band pin entry before a key can be generated.
773 maker.initialOwner(rcc);
774 // Create the cred we need to manipulate the keys until we actually set a new access control for them.
775 const AccessCredentials *cred = maker.cred();
776
777 CSSM_KEY publicCssmKey, privateCssmKey;
778 memset(&publicCssmKey, 0, sizeof(publicCssmKey));
779 memset(&privateCssmKey, 0, sizeof(privateCssmKey));
780
781 CSSM_CC_HANDLE ccHandle = 0;
782
783 Item publicKeyItem, privateKeyItem;
784 try
785 {
786 CSSM_RETURN status;
787
788 // Calculate the hash of the public key using the appleCSP.
789 CssmClient::PassThrough passThrough(appleCsp);
790 void *outData;
791 CssmData *cssmData;
792
793 /* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the
794 * associated key blob.
795 * Key is specified in CSSM_CSP_CreatePassThroughContext.
796 * Hash is allocated bythe CSP, in the App's memory, and returned
797 * in *outData. */
798 passThrough.key(&publicWrappedKey);
799 passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData);
800 cssmData = reinterpret_cast<CssmData *>(outData);
801 CssmData &pubKeyHash = *cssmData;
802
803 status = CSSM_CSP_CreateSymmetricContext(csp->handle(), publicWrappedKey.KeyHeader.WrapAlgorithmId, CSSM_ALGMODE_NONE, NULL, NULL, NULL, CSSM_PADDING_NONE, NULL, &ccHandle);
804 if (status)
805 CssmError::throwMe(status);
806 deleteContext = true;
807
808 CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle();
809 CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle;
810 CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } };
811 status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes);
812 if (status)
813 CssmError::throwMe(status);
814
815 // Unwrap the the keys
816 CSSM_DATA descriptiveData = {0, NULL};
817
818 status = CSSM_UnwrapKey(
819 ccHandle,
820 NULL,
821 &publicWrappedKey,
822 publicWrappedKey.KeyHeader.KeyUsage,
823 publicWrappedKey.KeyHeader.KeyAttr | CSSM_KEYATTR_PERMANENT,
824 &pubKeyHash,
825 &rcc,
826 &publicCssmKey,
827 &descriptiveData);
828
829 if (status)
830 CssmError::throwMe(status);
831 freePublicKey = true;
832
833 if (descriptiveData.Data != NULL)
834 free (descriptiveData.Data);
835
836 status = CSSM_UnwrapKey(
837 ccHandle,
838 NULL,
839 &privateWrappedKey,
840 privateWrappedKey.KeyHeader.KeyUsage,
841 privateWrappedKey.KeyHeader.KeyAttr | CSSM_KEYATTR_PERMANENT,
842 &pubKeyHash,
843 &rcc,
844 &privateCssmKey,
845 &descriptiveData);
846
847 if (status)
848 CssmError::throwMe(status);
849
850 if (descriptiveData.Data != NULL)
851 free (descriptiveData.Data);
852
853 freePrivateKey = true;
854
855 // Find the keys we just generated in the DL to get SecKeyRefs to them
856 // so we can change the label to be the hash of the public key, and
857 // fix up other attributes.
858
859 // Look up public key in the DLDB.
860 DbAttributes pubDbAttributes;
861 DbUniqueRecord pubUniqueId;
862 SSDbCursor dbPubCursor(ssDb, 1);
863 dbPubCursor->recordType(CSSM_DL_DB_RECORD_PUBLIC_KEY);
864 dbPubCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, pubKeyHash);
865 CssmClient::Key publicKey;
866 if (!dbPubCursor->nextKey(&pubDbAttributes, publicKey, pubUniqueId))
867 MacOSError::throwMe(errSecItemNotFound);
868
869 // Look up private key in the DLDB.
870 DbAttributes privDbAttributes;
871 DbUniqueRecord privUniqueId;
872 SSDbCursor dbPrivCursor(ssDb, 1);
873 dbPrivCursor->recordType(CSSM_DL_DB_RECORD_PRIVATE_KEY);
874 dbPrivCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, pubKeyHash);
875 CssmClient::Key privateKey;
876 if (!dbPrivCursor->nextKey(&privDbAttributes, privateKey, privUniqueId))
877 MacOSError::throwMe(errSecItemNotFound);
878
879 // @@@ Not exception safe!
880 csp.allocator().free(cssmData->Data);
881 csp.allocator().free(cssmData);
882
883 auto_ptr<string>privDescription;
884 auto_ptr<string>pubDescription;
885 try {
886 privDescription.reset(new string(initialAccess->promptDescription()));
887 pubDescription.reset(new string(initialAccess->promptDescription()));
888 }
889 catch(...) {
890 /* this path taken if no promptDescription available, e.g., for complex ACLs */
891 privDescription.reset(new string("Private key"));
892 pubDescription.reset(new string("Public key"));
893 }
894
895 // Set the label of the public key to the public key hash.
896 // Set the PrintName of the public key to the description in the acl.
897 pubDbAttributes.add(kInfoKeyPrintName, *pubDescription);
898 pubUniqueId->modify(CSSM_DL_DB_RECORD_PUBLIC_KEY, &pubDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
899
900 // Set the label of the private key to the public key hash.
901 // Set the PrintName of the private key to the description in the acl.
902 privDbAttributes.add(kInfoKeyPrintName, *privDescription);
903 privUniqueId->modify(CSSM_DL_DB_RECORD_PRIVATE_KEY, &privDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
904
905 // Finally fix the acl and owner of the private key to the specified access control settings.
906 initialAccess->setAccess(*privateKey, maker);
907
908 // Make the public key acl completely open
909 SecPointer<Access> pubKeyAccess(new Access());
910 pubKeyAccess->setAccess(*publicKey, maker);
911
912 // Create keychain items which will represent the keys.
913 publicKeyItem = keychain->item(CSSM_DL_DB_RECORD_PUBLIC_KEY, pubUniqueId);
914 privateKeyItem = keychain->item(CSSM_DL_DB_RECORD_PRIVATE_KEY, privUniqueId);
915
916 KeyItem* impl = dynamic_cast<KeyItem*>(&(*publicKeyItem));
917 if (impl == NULL)
918 {
919 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
920 }
921
922 outPublicKey = impl;
923
924 impl = dynamic_cast<KeyItem*>(&(*privateKeyItem));
925 if (impl == NULL)
926 {
927 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
928 }
929 outPrivateKey = impl;
930 }
931 catch (...)
932 {
933 if (freePublicKey)
934 CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, TRUE);
935 if (freePrivateKey)
936 CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, TRUE);
937
938 if (deleteContext)
939 CSSM_DeleteContext(ccHandle);
940
941 throw;
942 }
943
944 if (freePublicKey)
945 CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, FALSE);
946 if (freePrivateKey)
947 CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, FALSE);
948
949 if (deleteContext)
950 CSSM_DeleteContext(ccHandle);
951
952 if (keychain && publicKeyItem && privateKeyItem)
953 {
954 KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, publicKeyItem);
955 KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, privateKeyItem);
956 }
957 }
958
959 SecPointer<KeyItem>
960 KeyItem::generateWithAttributes(const SecKeychainAttributeList *attrList,
961 Keychain keychain,
962 CSSM_ALGORITHMS algorithm,
963 uint32 keySizeInBits,
964 CSSM_CC_HANDLE contextHandle,
965 CSSM_KEYUSE keyUsage,
966 uint32 keyAttr,
967 SecPointer<Access> initialAccess)
968 {
969 CssmClient::CSP appleCsp(gGuidAppleCSP);
970 CssmClient::CSP csp(NULL);
971 SSDb ssDb(NULL);
972 uint8 labelBytes[20];
973 CssmData label(labelBytes, sizeof(labelBytes));
974 bool freeKey = false;
975 bool deleteContext = false;
976 const CSSM_DATA *plabel = NULL;
977
978 if (keychain)
979 {
980 if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
981 MacOSError::throwMe(errSecInvalidKeychain);
982
983 SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*keychain->database()));
984 if (impl == NULL)
985 {
986 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
987 }
988
989 ssDb = SSDb(impl);
990 csp = keychain->csp();
991
992 // Generate a random label to use initially
993 CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW);
994 random.generate(label, (uint32)label.Length);
995 plabel = &label;
996 }
997 else
998 {
999 // Not a persistent key so create it in the regular csp
1000 csp = appleCsp;
1001 }
1002
1003 // Create a Access::Maker for the initial owner of the private key.
1004 ResourceControlContext *prcc = NULL, rcc;
1005 const AccessCredentials *cred = NULL;
1006 Access::Maker maker;
1007 if (keychain && initialAccess)
1008 {
1009 memset(&rcc, 0, sizeof(rcc));
1010 // @@@ Potentially provide a credential argument which allows us to generate keys in the csp.
1011 // Currently the CSP lets anyone do this, but we might restrict this in the future, e.g. a smartcard
1012 // could require out-of-band pin entry before a key can be generated.
1013 maker.initialOwner(rcc);
1014 // Create the cred we need to manipulate the keys until we actually set a new access control for them.
1015 cred = maker.cred();
1016 prcc = &rcc;
1017 }
1018
1019 CSSM_KEY cssmKey;
1020
1021 CSSM_CC_HANDLE ccHandle = 0;
1022
1023 Item keyItem;
1024 try
1025 {
1026 CSSM_RETURN status;
1027 if (contextHandle)
1028 ccHandle = contextHandle;
1029 else
1030 {
1031 status = CSSM_CSP_CreateKeyGenContext(csp->handle(), algorithm, keySizeInBits, NULL, NULL, NULL, NULL, NULL, &ccHandle);
1032 if (status)
1033 CssmError::throwMe(status);
1034 deleteContext = true;
1035 }
1036
1037 if (ssDb)
1038 {
1039 CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle();
1040 CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle;
1041 CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } };
1042 status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes);
1043 if (status)
1044 CssmError::throwMe(status);
1045
1046 keyAttr |= CSSM_KEYATTR_PERMANENT;
1047 }
1048
1049 // Generate the key
1050 status = CSSM_GenerateKey(ccHandle, keyUsage, keyAttr, plabel, prcc, &cssmKey);
1051 if (status)
1052 CssmError::throwMe(status);
1053
1054 if (ssDb)
1055 {
1056 freeKey = true;
1057 // Find the key we just generated in the DL and get a SecKeyRef
1058 // so we can specify the label attribute(s) and initial ACL.
1059
1060 // Look up key in the DLDB.
1061 DbAttributes dbAttributes;
1062 DbUniqueRecord uniqueId;
1063 SSDbCursor dbCursor(ssDb, 1);
1064 dbCursor->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY);
1065 dbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label);
1066 CssmClient::Key key;
1067 if (!dbCursor->nextKey(&dbAttributes, key, uniqueId))
1068 MacOSError::throwMe(errSecItemNotFound);
1069
1070 // Set the initial label, application label, and application tag (if provided)
1071 if (attrList) {
1072 DbAttributes newDbAttributes;
1073 SSDbCursor otherDbCursor(ssDb, 1);
1074 otherDbCursor->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY);
1075 bool checkForDuplicates = false;
1076
1077 for (UInt32 index=0; index < attrList->count; index++) {
1078 SecKeychainAttribute attr = attrList->attr[index];
1079 CssmData attrData(attr.data, attr.length);
1080 if (attr.tag == kSecKeyPrintName) {
1081 newDbAttributes.add(kInfoKeyPrintName, attrData);
1082 }
1083 if (attr.tag == kSecKeyLabel) {
1084 newDbAttributes.add(kInfoKeyLabel, attrData);
1085 otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, attrData);
1086 checkForDuplicates = true;
1087 }
1088 if (attr.tag == kSecKeyApplicationTag) {
1089 newDbAttributes.add(kInfoKeyApplicationTag, attrData);
1090 otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyApplicationTag, attrData);
1091 checkForDuplicates = true;
1092 }
1093 }
1094
1095 DbAttributes otherDbAttributes;
1096 DbUniqueRecord otherUniqueId;
1097 CssmClient::Key otherKey;
1098 if (checkForDuplicates && otherDbCursor->nextKey(&otherDbAttributes, otherKey, otherUniqueId))
1099 MacOSError::throwMe(errSecDuplicateItem);
1100
1101 uniqueId->modify(CSSM_DL_DB_RECORD_SYMMETRIC_KEY, &newDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
1102 }
1103
1104 // Finally, fix the acl and owner of the key to the specified access control settings.
1105 if (initialAccess)
1106 initialAccess->setAccess(*key, maker);
1107
1108 // Create keychain item which will represent the key.
1109 keyItem = keychain->item(CSSM_DL_DB_RECORD_SYMMETRIC_KEY, uniqueId);
1110 }
1111 else
1112 {
1113 CssmClient::Key tempKey(csp, cssmKey);
1114 keyItem = new KeyItem(tempKey);
1115 }
1116 }
1117 catch (...)
1118 {
1119 if (freeKey)
1120 {
1121 // Delete the key if something goes wrong so we don't end up with inaccessible keys in the database.
1122 CSSM_FreeKey(csp->handle(), cred, &cssmKey, TRUE);
1123 }
1124
1125 if (deleteContext)
1126 CSSM_DeleteContext(ccHandle);
1127
1128 throw;
1129 }
1130
1131 if (freeKey)
1132 {
1133 CSSM_FreeKey(csp->handle(), NULL, &cssmKey, FALSE);
1134 }
1135
1136 if (deleteContext)
1137 CSSM_DeleteContext(ccHandle);
1138
1139 if (keychain && keyItem)
1140 keychain->postEvent(kSecAddEvent, keyItem);
1141
1142 KeyItem* item = dynamic_cast<KeyItem*>(&*keyItem);
1143 if (item == NULL)
1144 {
1145 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
1146 }
1147
1148 return item;
1149 }
1150
1151 SecPointer<KeyItem>
1152 KeyItem::generate(Keychain keychain,
1153 CSSM_ALGORITHMS algorithm,
1154 uint32 keySizeInBits,
1155 CSSM_CC_HANDLE contextHandle,
1156 CSSM_KEYUSE keyUsage,
1157 uint32 keyAttr,
1158 SecPointer<Access> initialAccess)
1159 {
1160 return KeyItem::generateWithAttributes(NULL, keychain,
1161 algorithm, keySizeInBits, contextHandle,
1162 keyUsage, keyAttr, initialAccess);
1163 }
1164
1165
1166 void KeyItem::RawSign(SecPadding padding, CSSM_DATA dataToSign, const AccessCredentials *credentials, CSSM_DATA& signature)
1167 {
1168 CSSM_ALGORITHMS baseAlg = key()->header().algorithm();
1169
1170 if ((baseAlg != CSSM_ALGID_RSA) && (baseAlg != CSSM_ALGID_ECDSA))
1171 {
1172 MacOSError::throwMe(errSecParam);
1173 }
1174
1175 CSSM_ALGORITHMS paddingAlg = CSSM_PADDING_PKCS1;
1176
1177 switch (padding)
1178 {
1179 case kSecPaddingPKCS1:
1180 {
1181 paddingAlg = CSSM_PADDING_PKCS1;
1182 break;
1183 }
1184
1185 case kSecPaddingPKCS1MD2:
1186 {
1187 baseAlg = CSSM_ALGID_MD2WithRSA;
1188 break;
1189 }
1190
1191 case kSecPaddingPKCS1MD5:
1192 {
1193 baseAlg = CSSM_ALGID_MD5WithRSA;
1194 break;
1195 }
1196
1197 case kSecPaddingPKCS1SHA1:
1198 {
1199 baseAlg = CSSM_ALGID_SHA1WithRSA;
1200 break;
1201 }
1202
1203 default:
1204 {
1205 paddingAlg = CSSM_PADDING_NONE;
1206 break;
1207 }
1208 }
1209
1210 Sign signContext(csp(), baseAlg);
1211 signContext.key(key());
1212 signContext.set(CSSM_ATTRIBUTE_PADDING, paddingAlg);
1213 signContext.cred(credentials);
1214
1215 CssmData data(dataToSign.Data, dataToSign.Length);
1216 signContext.sign(data);
1217
1218 CssmData sig(signature.Data, signature.Length);
1219 signContext(sig); // yes, this is an accessor. Believe it, or not.
1220 signature.Length = sig.length();
1221 }
1222
1223
1224
1225 void KeyItem::RawVerify(SecPadding padding, CSSM_DATA dataToVerify, const AccessCredentials *credentials, CSSM_DATA sig)
1226 {
1227 CSSM_ALGORITHMS baseAlg = key()->header().algorithm();
1228 if ((baseAlg != CSSM_ALGID_RSA) && (baseAlg != CSSM_ALGID_ECDSA))
1229 {
1230 MacOSError::throwMe(errSecParam);
1231 }
1232
1233 CSSM_ALGORITHMS paddingAlg = CSSM_PADDING_PKCS1;
1234
1235 switch (padding)
1236 {
1237 case kSecPaddingPKCS1:
1238 {
1239 paddingAlg = CSSM_PADDING_PKCS1;
1240 break;
1241 }
1242
1243 case kSecPaddingPKCS1MD2:
1244 {
1245 baseAlg = CSSM_ALGID_MD2WithRSA;
1246 break;
1247 }
1248
1249 case kSecPaddingPKCS1MD5:
1250 {
1251 baseAlg = CSSM_ALGID_MD5WithRSA;
1252 break;
1253 }
1254
1255 case kSecPaddingPKCS1SHA1:
1256 {
1257 baseAlg = CSSM_ALGID_SHA1WithRSA;
1258 break;
1259 }
1260
1261 default:
1262 {
1263 paddingAlg = CSSM_PADDING_NONE;
1264 break;
1265 }
1266 }
1267
1268 Verify verifyContext(csp(), baseAlg);
1269 verifyContext.key(key());
1270 verifyContext.set(CSSM_ATTRIBUTE_PADDING, paddingAlg);
1271 verifyContext.cred(credentials);
1272
1273 CssmData data(dataToVerify.Data, dataToVerify.Length);
1274 CssmData signature(sig.Data, sig.Length);
1275 verifyContext.verify(data, signature);
1276 }
1277
1278
1279
1280 void KeyItem::Encrypt(SecPadding padding, CSSM_DATA dataToEncrypt, const AccessCredentials *credentials, CSSM_DATA& encryptedData)
1281 {
1282 CSSM_ALGORITHMS baseAlg = key()->header().algorithm();
1283 if (baseAlg != CSSM_ALGID_RSA)
1284 {
1285 MacOSError::throwMe(errSecParam);
1286 }
1287
1288 CSSM_ALGORITHMS paddingAlg = CSSM_PADDING_PKCS1;
1289
1290 switch (padding)
1291 {
1292 case kSecPaddingPKCS1:
1293 {
1294 paddingAlg = CSSM_PADDING_PKCS1;
1295 break;
1296 }
1297
1298 default:
1299 {
1300 paddingAlg = CSSM_PADDING_NONE;
1301 break;
1302 }
1303 }
1304
1305 CssmClient::Encrypt encryptContext(csp(), baseAlg);
1306 encryptContext.key(key());
1307 encryptContext.padding(paddingAlg);
1308 encryptContext.cred(credentials);
1309
1310 CssmData inData(dataToEncrypt.Data, dataToEncrypt.Length);
1311 CssmData outData(encryptedData.Data, encryptedData.Length);
1312 CssmData remData((void*) NULL, 0);
1313
1314 encryptedData.Length = encryptContext.encrypt(inData, outData, remData);
1315 }
1316
1317
1318
1319 void KeyItem::Decrypt(SecPadding padding, CSSM_DATA dataToDecrypt, const AccessCredentials *credentials, CSSM_DATA& decryptedData)
1320 {
1321 CSSM_ALGORITHMS baseAlg = key()->header().algorithm();
1322 if (baseAlg != CSSM_ALGID_RSA)
1323 {
1324 MacOSError::throwMe(errSecParam);
1325 }
1326
1327 CSSM_ALGORITHMS paddingAlg = CSSM_PADDING_PKCS1;
1328
1329 switch (padding)
1330 {
1331 case kSecPaddingPKCS1:
1332 {
1333 paddingAlg = CSSM_PADDING_PKCS1;
1334 break;
1335 }
1336
1337
1338 default:
1339 {
1340 paddingAlg = CSSM_PADDING_NONE;
1341 break;
1342 }
1343 }
1344
1345 CssmClient::Decrypt decryptContext(csp(), baseAlg);
1346 decryptContext.key(key());
1347 decryptContext.padding(paddingAlg);
1348 decryptContext.cred(credentials);
1349
1350 CssmData inData(dataToDecrypt.Data, dataToDecrypt.Length);
1351 CssmData outData(decryptedData.Data, decryptedData.Length);
1352 CssmData remData((void*) NULL, 0);
1353 decryptedData.Length = decryptContext.decrypt(inData, outData, remData);
1354 if (remData.Data != NULL)
1355 {
1356 free(remData.Data);
1357 }
1358 }
1359
1360 CFHashCode KeyItem::hash()
1361 {
1362 CFHashCode result = 0;
1363 const CSSM_KEY *cssmKey = key();
1364 if (NULL != cssmKey)
1365 {
1366 unsigned char digest[CC_SHA256_DIGEST_LENGTH];
1367
1368 CFIndex size_of_data = sizeof(CSSM_KEYHEADER) + cssmKey->KeyData.Length;
1369
1370 CFMutableDataRef temp_cfdata = CFDataCreateMutable(kCFAllocatorDefault, size_of_data);
1371 if (NULL == temp_cfdata)
1372 {
1373 return result;
1374 }
1375
1376 CFDataAppendBytes(temp_cfdata, (const UInt8 *)cssmKey, sizeof(CSSM_KEYHEADER));
1377 CFDataAppendBytes(temp_cfdata, cssmKey->KeyData.Data, cssmKey->KeyData.Length);
1378
1379 if (size_of_data < 80)
1380 {
1381 // If it is less than 80 bytes then CFData can be used
1382 result = CFHash(temp_cfdata);
1383 CFRelease(temp_cfdata);
1384 }
1385 // CFData truncates its hash value to 80 bytes. ????
1386 // In order to do the 'right thing' a SHA 256 hash will be used to
1387 // include all of the data
1388 else
1389 {
1390 memset(digest, 0, CC_SHA256_DIGEST_LENGTH);
1391
1392 CC_SHA256((const void *)CFDataGetBytePtr(temp_cfdata), (CC_LONG)CFDataGetLength(temp_cfdata), digest);
1393
1394 CFDataRef data_to_hash = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
1395 (const UInt8 *)digest, CC_SHA256_DIGEST_LENGTH, kCFAllocatorNull);
1396 result = CFHash(data_to_hash);
1397 CFRelease(data_to_hash);
1398 CFRelease(temp_cfdata);
1399 }
1400 }
1401 return result;
1402 }
1403