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