]> git.saurik.com Git - apple/security.git/blob - Keychain/KeyItem.cpp
Security-179.tar.gz
[apple/security.git] / Keychain / KeyItem.cpp
1 /*
2 * Copyright (c) 2002 Apple Computer, Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
8 * using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
16 */
17
18 //
19 // KeyItem.cpp
20 //
21 #include <Security/KeyItem.h>
22 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
23 #include <Security/cssmtype.h>
24 #include <Security/Access.h>
25 #include <Security/Keychains.h>
26 #include <Security/KeyItem.h>
27 #include <Security/wrapkey.h>
28 #include <Security/genkey.h>
29 #include <Security/globals.h>
30 #include "clNssUtils.h"
31 #include "KCEventNotifier.h"
32
33 // @@@ This needs to be shared.
34 static CSSM_DB_NAME_ATTR(kSecKeyPrintName, 1, "PrintName", 0, NULL, BLOB);
35 static CSSM_DB_NAME_ATTR(kSecKeyLabel, 6, "Label", 0, NULL, BLOB);
36 static CSSM_DB_NAME_ATTR(kSecApplicationTag, 7, "ApplicationTag", 0, NULL, BLOB);
37
38 using namespace KeychainCore;
39
40 KeyItem::KeyItem(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId) :
41 ItemImpl(keychain, primaryKey, uniqueId),
42 mKey()
43 {
44 }
45
46 KeyItem::KeyItem(const Keychain &keychain, const PrimaryKey &primaryKey) :
47 ItemImpl(keychain, primaryKey),
48 mKey()
49 {
50 }
51
52 KeyItem::KeyItem(KeyItem &keyItem) :
53 ItemImpl(keyItem),
54 mKey()
55 {
56 // @@@ this doesn't work for keys that are not in a keychain.
57 }
58
59 KeyItem::KeyItem(const CssmClient::Key &key) :
60 ItemImpl(key->keyClass() + CSSM_DL_DB_RECORD_PUBLIC_KEY, (OSType)0, (UInt32)0, (const void*)NULL),
61 mKey(key)
62 {
63 if (key->keyClass() > CSSM_KEYCLASS_SESSION_KEY)
64 MacOSError::throwMe(paramErr);
65 }
66
67 KeyItem::~KeyItem() throw()
68 {
69 }
70
71 void
72 KeyItem::update()
73 {
74 ItemImpl::update();
75 }
76
77 Item
78 KeyItem::copyTo(const Keychain &keychain, Access *newAccess)
79 {
80 if (!keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP)
81 MacOSError::throwMe(errSecInvalidKeychain);
82
83 /* Get the destination keychains db. */
84 SSDb ssDb(safe_cast<SSDbImpl *>(&(*keychain->database())));
85
86 /* Make sure mKey is valid. */
87 key();
88
89 // Generate a random label to use initially
90 CssmClient::CSP appleCsp(gGuidAppleCSP);
91 CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW);
92 uint8 labelBytes[20];
93 CssmData label(labelBytes, sizeof(labelBytes));
94 random.generate(label, label.Length);
95
96 /* Set up the ACL for the new key. */
97 SecPointer<Access> access;
98 if (newAccess)
99 access = newAccess;
100 else
101 access = new Access(*mKey);
102
103 /* Generate a random 3DES wrapping Key. */
104 CssmClient::GenerateKey genKey(csp(), CSSM_ALGID_3DES_3KEY, 192);
105 CssmClient::Key wrappingKey(genKey(KeySpec(CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP,
106 CSSM_KEYATTR_EXTRACTABLE /* | CSSM_KEYATTR_RETURN_DATA */)));
107
108 /* Extract the key by wrapping it with the wrapping key. */
109 CssmClient::WrapKey wrap(csp(), CSSM_ALGID_3DES_3KEY_EDE);
110 wrap.key(wrappingKey);
111 wrap.cred(getCredentials(CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED, kSecCredentialTypeDefault));
112 wrap.mode(CSSM_ALGMODE_ECBPad);
113 wrap.padding(CSSM_PADDING_PKCS7);
114 CssmClient::Key wrappedKey(wrap(mKey));
115
116 /* Unwrap the new key into the new Keychain. */
117 CssmClient::UnwrapKey unwrap(keychain->csp(), CSSM_ALGID_3DES_3KEY_EDE);
118 unwrap.key(wrappingKey);
119 unwrap.mode(CSSM_ALGMODE_ECBPad);
120 unwrap.padding(CSSM_PADDING_PKCS7);
121
122 /* Setup the dldbHandle in the context. */
123 unwrap.add(CSSM_ATTRIBUTE_DL_DB_HANDLE, ssDb->handle());
124
125 /* Set up an initial aclEntry so we can change it after the unwrap. */
126 Access::Maker maker;
127 ResourceControlContext rcc;
128 maker.initialOwner(rcc, NULL);
129 unwrap.aclEntry(rcc.input());
130
131 /* Unwrap the key. */
132 uint32 usage = mKey->usage();
133 /* Work around csp brokeness where it sets all usage bits in the Keyheader when CSSM_KEYUSE_ANY is set. */
134 if (usage & CSSM_KEYUSE_ANY)
135 usage = CSSM_KEYUSE_ANY;
136
137 CssmClient::Key unwrappedKey(unwrap(wrappedKey, KeySpec(usage,
138 (mKey->attributes() | CSSM_KEYATTR_PERMANENT) & ~(CSSM_KEYATTR_ALWAYS_SENSITIVE | CSSM_KEYATTR_NEVER_EXTRACTABLE),
139 label)));
140
141 /* Look up unwrapped key in the DLDB. */
142 DbUniqueRecord uniqueId;
143 SSDbCursor dbCursor(ssDb, 1);
144 dbCursor->recordType(recordType());
145 dbCursor->add(CSSM_DB_EQUAL, kSecKeyLabel, label);
146 CssmClient::Key copiedKey;
147 if (!dbCursor->nextKey(NULL, copiedKey, uniqueId))
148 MacOSError::throwMe(errSecItemNotFound);
149
150 /* Copy the Label, PrintName and ApplicationTag attributes from the old key to the new one. */
151 dbUniqueRecord();
152 DbAttributes oldDbAttributes(mUniqueId->database(), 3);
153 oldDbAttributes.add(kSecKeyLabel);
154 oldDbAttributes.add(kSecKeyPrintName);
155 oldDbAttributes.add(kSecApplicationTag);
156 mUniqueId->get(&oldDbAttributes, NULL);
157 uniqueId->modify(recordType(), &oldDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
158
159 /* Set the acl and owner on the unwrapped key. */
160 access->setAccess(*unwrappedKey, maker);
161
162 /* Return a keychain items which represents the new key. */
163 Item item(keychain->item(recordType(), uniqueId));
164
165 KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, item);
166
167 return item;
168 }
169
170 void
171 KeyItem::didModify()
172 {
173 }
174
175 PrimaryKey
176 KeyItem::add(Keychain &keychain)
177 {
178 MacOSError::throwMe(unimpErr);
179 }
180
181 CssmClient::SSDbUniqueRecord
182 KeyItem::ssDbUniqueRecord()
183 {
184 DbUniqueRecordImpl *impl = &*dbUniqueRecord();
185 return CssmClient::SSDbUniqueRecord(safe_cast<Security::CssmClient::SSDbUniqueRecordImpl *>(impl));
186 }
187
188 CssmClient::Key &
189 KeyItem::key()
190 {
191 if (!mKey)
192 {
193 CssmClient::SSDbUniqueRecord uniqueId(ssDbUniqueRecord());
194 CssmDataContainer dataBlob(uniqueId->allocator());
195 uniqueId->get(NULL, &dataBlob);
196 mKey = CssmClient::Key(uniqueId->database()->csp(), *reinterpret_cast<CssmKey *>(dataBlob.Data));
197 }
198
199 return mKey;
200 }
201
202 CssmClient::CSP
203 KeyItem::csp()
204 {
205 return key()->csp();
206 }
207
208
209 const CSSM_X509_ALGORITHM_IDENTIFIER&
210 KeyItem::algorithmIdentifier()
211 {
212 #if 0
213 CssmKey *mKey;
214 CSSM_KEY_TYPE algorithm
215 CSSM_KEY_PTR cssmKey = (CSSM_KEY_PTR)thisData->Data;
216 cssmKey->KeyHeader
217 static void printKeyHeader(
218 const CSSM_KEYHEADER &hdr)
219 {
220 printf(" Algorithm : ");
221 switch(hdr.AlgorithmId) {
222 CSSM_X509_ALGORITHM_IDENTIFIER algID;
223
224 CSSM_OID *CL_algToOid(
225 CSSM_ALGORITHMS algId)
226 typedef struct cssm_x509_algorithm_identifier {
227 CSSM_OID algorithm;
228 CSSM_DATA parameters;
229 } CSSM_X509_ALGORITHM_IDENTIFIER, *CSSM_X509_ALGORITHM_IDENTIFIER_PTR;
230 #endif
231
232 abort();
233 }
234
235 unsigned int
236 KeyItem::strengthInBits(const CSSM_X509_ALGORITHM_IDENTIFIER *algid)
237 {
238 // @@@ Make a context with key based on algid and use that to get the effective keysize and not just the logical one.
239 CSSM_KEY_SIZE keySize = {};
240 CSSM_RETURN rv = CSSM_QueryKeySizeInBits (csp()->handle(),
241 NULL,
242 key(),
243 &keySize);
244 if (rv)
245 return 0;
246
247 return keySize.LogicalKeySizeInBits;
248 }
249
250 const AccessCredentials *
251 KeyItem::getCredentials(
252 CSSM_ACL_AUTHORIZATION_TAG operation,
253 SecCredentialType credentialType)
254 {
255 // @@@ Fix this to actually examine the ACL for this key and consider operation and do the right thing.
256 //AutoAclEntryInfoList aclInfos;
257 //key()->getAcl(aclInfos);
258
259 AclFactory factory;
260 switch (credentialType)
261 {
262 case kSecCredentialTypeDefault:
263 return globals().credentials();
264 case kSecCredentialTypeWithUI:
265 return factory.promptCred();
266 case kSecCredentialTypeNoUI:
267 return factory.nullCred();
268 default:
269 MacOSError::throwMe(paramErr);
270 }
271 }
272
273 void
274 KeyItem::createPair(
275 Keychain keychain,
276 CSSM_ALGORITHMS algorithm,
277 uint32 keySizeInBits,
278 CSSM_CC_HANDLE contextHandle,
279 CSSM_KEYUSE publicKeyUsage,
280 uint32 publicKeyAttr,
281 CSSM_KEYUSE privateKeyUsage,
282 uint32 privateKeyAttr,
283 SecPointer<Access> initialAccess,
284 SecPointer<KeyItem> &outPublicKey,
285 SecPointer<KeyItem> &outPrivateKey)
286 {
287 bool freeKeys = false;
288 bool deleteContext = false;
289
290 if (!keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP)
291 MacOSError::throwMe(errSecInvalidKeychain);
292
293 SSDb ssDb(safe_cast<SSDbImpl *>(&(*keychain->database())));
294 CssmClient::CSP csp(keychain->csp());
295 CssmClient::CSP appleCsp(gGuidAppleCSP);
296
297 // Generate a random label to use initially
298 CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW);
299 uint8 labelBytes[20];
300 CssmData label(labelBytes, sizeof(labelBytes));
301 random.generate(label, label.Length);
302
303 // Create a Access::Maker for the initial owner of the private key.
304 ResourceControlContext rcc;
305 memset(&rcc, 0, sizeof(rcc));
306 Access::Maker maker;
307 // @@@ 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.
308 maker.initialOwner(rcc);
309 // Create the cred we need to manipulate the keys until we actually set a new access control for them.
310 const AccessCredentials *cred = maker.cred();
311
312 CSSM_KEY publicCssmKey, privateCssmKey;
313 memset(&publicCssmKey, 0, sizeof(publicCssmKey));
314 memset(&privateCssmKey, 0, sizeof(privateCssmKey));
315
316 CSSM_CC_HANDLE ccHandle = 0;
317
318 try
319 {
320 CSSM_RETURN status;
321 if (contextHandle)
322 ccHandle = contextHandle;
323 else
324 {
325 status = CSSM_CSP_CreateKeyGenContext(csp->handle(), algorithm, keySizeInBits, NULL, NULL, NULL, NULL, NULL, &ccHandle);
326 if (status)
327 CssmError::throwMe(status);
328 deleteContext = true;
329 }
330
331 CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle();
332 CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle;
333 CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } };
334 status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes);
335 if (status)
336 CssmError::throwMe(status);
337
338 // Generate the keypair
339 status = CSSM_GenerateKeyPair(ccHandle, publicKeyUsage, publicKeyAttr, &label, &publicCssmKey, privateKeyUsage, privateKeyAttr, &label, &rcc, &privateCssmKey);
340 if (status)
341 CssmError::throwMe(status);
342 freeKeys = true;
343
344 // Find the keys we just generated in the DL to get SecKeyRef's to them
345 // so we can change the label to be the hash of the public key, and
346 // fix up other attributes.
347
348 // Look up public key in the DLDB.
349 DbAttributes pubDbAttributes;
350 DbUniqueRecord pubUniqueId;
351 SSDbCursor dbPubCursor(ssDb, 1);
352 dbPubCursor->recordType(CSSM_DL_DB_RECORD_PUBLIC_KEY);
353 dbPubCursor->add(CSSM_DB_EQUAL, kSecKeyLabel, label);
354 CssmClient::Key publicKey;
355 if (!dbPubCursor->nextKey(&pubDbAttributes, publicKey, pubUniqueId))
356 MacOSError::throwMe(errSecItemNotFound);
357
358 // Look up private key in the DLDB.
359 DbAttributes privDbAttributes;
360 DbUniqueRecord privUniqueId;
361 SSDbCursor dbPrivCursor(ssDb, 1);
362 dbPrivCursor->recordType(CSSM_DL_DB_RECORD_PRIVATE_KEY);
363 dbPrivCursor->add(CSSM_DB_EQUAL, kSecKeyLabel, label);
364 CssmClient::Key privateKey;
365 if (!dbPrivCursor->nextKey(&privDbAttributes, privateKey, privUniqueId))
366 MacOSError::throwMe(errSecItemNotFound);
367
368 // Convert reference public key to a raw key so we can use it
369 // in the appleCsp.
370 CssmClient::WrapKey wrap(csp, CSSM_ALGID_NONE);
371 wrap.cred(cred);
372 CssmClient::Key rawPubKey = wrap(publicKey);
373
374 // Calculate the hash of the public key using the appleCSP.
375 CssmClient::PassThrough passThrough(appleCsp);
376 void *outData;
377 CssmData *cssmData;
378
379 /* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the
380 * associated key blob.
381 * Key is specified in CSSM_CSP_CreatePassThroughContext.
382 * Hash is allocated bythe CSP, in the App's memory, and returned
383 * in *outData. */
384 passThrough.key(rawPubKey);
385 passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData);
386 cssmData = reinterpret_cast<CssmData *>(outData);
387 CssmData &pubKeyHash = *cssmData;
388
389 std::string description(initialAccess->promptDescription());
390 // Set the label of the public key to the public key hash.
391 // Set the PrintName of the public key to the description in the acl.
392 pubDbAttributes.add(kSecKeyLabel, pubKeyHash);
393 pubDbAttributes.add(kSecKeyPrintName, description);
394 pubUniqueId->modify(CSSM_DL_DB_RECORD_PUBLIC_KEY, &pubDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
395
396 // Set the label of the private key to the public key hash.
397 // Set the PrintName of the private key to the description in the acl.
398 privDbAttributes.add(kSecKeyLabel, pubKeyHash);
399 privDbAttributes.add(kSecKeyPrintName, description);
400 privUniqueId->modify(CSSM_DL_DB_RECORD_PRIVATE_KEY, &privDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
401
402 // @@@ Not exception safe!
403 csp.allocator().free(cssmData->Data);
404 csp.allocator().free(cssmData);
405
406 // Finally fix the acl and owner of the private key to the specified access control settings.
407 initialAccess->setAccess(*privateKey, maker);
408
409 // Make the public key acl completely open
410 Access pubKeyAccess;
411 pubKeyAccess.setAccess(*publicKey, maker);
412
413 // Create keychain items which will represent the keys.
414 outPublicKey = safe_cast<KeyItem*>(&(*keychain->item(CSSM_DL_DB_RECORD_PUBLIC_KEY, pubUniqueId)));
415 outPrivateKey = safe_cast<KeyItem*>(&(*keychain->item(CSSM_DL_DB_RECORD_PRIVATE_KEY, privUniqueId)));
416 }
417 catch (...)
418 {
419 if (freeKeys)
420 {
421 // Delete the keys if something goes wrong so we don't end up with inaccesable keys in the database.
422 CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, TRUE);
423 CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, TRUE);
424 }
425
426 if (deleteContext)
427 CSSM_DeleteContext(ccHandle);
428
429 throw;
430 }
431
432 if (freeKeys)
433 {
434 CSSM_FreeKey(csp->handle(), NULL, &publicCssmKey, FALSE);
435 CSSM_FreeKey(csp->handle(), NULL, &privateCssmKey, FALSE);
436 }
437
438 if (deleteContext)
439 CSSM_DeleteContext(ccHandle);
440 }
441
442 void
443 KeyItem::importPair(
444 Keychain keychain,
445 const CSSM_KEY &publicWrappedKey,
446 const CSSM_KEY &privateWrappedKey,
447 SecPointer<Access> initialAccess,
448 SecPointer<KeyItem> &outPublicKey,
449 SecPointer<KeyItem> &outPrivateKey)
450 {
451 bool freePublicKey = false;
452 bool freePrivateKey = false;
453 bool deleteContext = false;
454
455 if (!keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP)
456 MacOSError::throwMe(errSecInvalidKeychain);
457
458 SSDb ssDb(safe_cast<SSDbImpl *>(&(*keychain->database())));
459 CssmClient::CSP csp(keychain->csp());
460 CssmClient::CSP appleCsp(gGuidAppleCSP);
461
462 // Create a Access::Maker for the initial owner of the private key.
463 ResourceControlContext rcc;
464 memset(&rcc, 0, sizeof(rcc));
465 Access::Maker maker;
466 // @@@ Potentially provide a credential argument which allows us to unwrap 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.
467 maker.initialOwner(rcc);
468 // Create the cred we need to manipulate the keys until we actually set a new access control for them.
469 const AccessCredentials *cred = maker.cred();
470
471 CSSM_KEY publicCssmKey, privateCssmKey;
472 memset(&publicCssmKey, 0, sizeof(publicCssmKey));
473 memset(&privateCssmKey, 0, sizeof(privateCssmKey));
474
475 CSSM_CC_HANDLE ccHandle = 0;
476
477 try
478 {
479 CSSM_RETURN status;
480
481 // Calculate the hash of the public key using the appleCSP.
482 CssmClient::PassThrough passThrough(appleCsp);
483 void *outData;
484 CssmData *cssmData;
485
486 /* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the
487 * associated key blob.
488 * Key is specified in CSSM_CSP_CreatePassThroughContext.
489 * Hash is allocated bythe CSP, in the App's memory, and returned
490 * in *outData. */
491 passThrough.key(&publicWrappedKey);
492 passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData);
493 cssmData = reinterpret_cast<CssmData *>(outData);
494 CssmData &pubKeyHash = *cssmData;
495
496 status = CSSM_CSP_CreateSymmetricContext(csp->handle(), publicWrappedKey.KeyHeader.WrapAlgorithmId, CSSM_ALGMODE_NONE, NULL, NULL, NULL, CSSM_PADDING_NONE, NULL, &ccHandle);
497 if (status)
498 CssmError::throwMe(status);
499 deleteContext = true;
500
501 CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle();
502 CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle;
503 CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } };
504 status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes);
505 if (status)
506 CssmError::throwMe(status);
507
508 // Unwrap the the keys
509 CSSM_DATA descriptiveData = {0, NULL};
510
511 status = CSSM_UnwrapKey(
512 ccHandle,
513 NULL,
514 &publicWrappedKey,
515 publicWrappedKey.KeyHeader.KeyUsage,
516 publicWrappedKey.KeyHeader.KeyAttr | CSSM_KEYATTR_PERMANENT,
517 &pubKeyHash,
518 &rcc,
519 &publicCssmKey,
520 &descriptiveData);
521
522 if (status)
523 CssmError::throwMe(status);
524 freePublicKey = true;
525
526 if (descriptiveData.Data != NULL)
527 free (descriptiveData.Data);
528
529 status = CSSM_UnwrapKey(
530 ccHandle,
531 NULL,
532 &privateWrappedKey,
533 privateWrappedKey.KeyHeader.KeyUsage,
534 privateWrappedKey.KeyHeader.KeyAttr | CSSM_KEYATTR_PERMANENT,
535 &pubKeyHash,
536 &rcc,
537 &privateCssmKey,
538 &descriptiveData);
539
540 if (status)
541 CssmError::throwMe(status);
542
543 if (descriptiveData.Data != NULL)
544 free (descriptiveData.Data);
545
546 freePrivateKey = true;
547
548 // Find the keys we just generated in the DL to get SecKeyRef's to them
549 // so we can change the label to be the hash of the public key, and
550 // fix up other attributes.
551
552 // Look up public key in the DLDB.
553 DbAttributes pubDbAttributes;
554 DbUniqueRecord pubUniqueId;
555 SSDbCursor dbPubCursor(ssDb, 1);
556 dbPubCursor->recordType(CSSM_DL_DB_RECORD_PUBLIC_KEY);
557 dbPubCursor->add(CSSM_DB_EQUAL, kSecKeyLabel, pubKeyHash);
558 CssmClient::Key publicKey;
559 if (!dbPubCursor->nextKey(&pubDbAttributes, publicKey, pubUniqueId))
560 MacOSError::throwMe(errSecItemNotFound);
561
562 // Look up private key in the DLDB.
563 DbAttributes privDbAttributes;
564 DbUniqueRecord privUniqueId;
565 SSDbCursor dbPrivCursor(ssDb, 1);
566 dbPrivCursor->recordType(CSSM_DL_DB_RECORD_PRIVATE_KEY);
567 dbPrivCursor->add(CSSM_DB_EQUAL, kSecKeyLabel, pubKeyHash);
568 CssmClient::Key privateKey;
569 if (!dbPrivCursor->nextKey(&privDbAttributes, privateKey, privUniqueId))
570 MacOSError::throwMe(errSecItemNotFound);
571
572 // @@@ Not exception safe!
573 csp.allocator().free(cssmData->Data);
574 csp.allocator().free(cssmData);
575
576 std::string description(initialAccess->promptDescription());
577 // Set the label of the public key to the public key hash.
578 // Set the PrintName of the public key to the description in the acl.
579 pubDbAttributes.add(kSecKeyPrintName, description);
580 pubUniqueId->modify(CSSM_DL_DB_RECORD_PUBLIC_KEY, &pubDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
581
582 // Set the label of the private key to the public key hash.
583 // Set the PrintName of the private key to the description in the acl.
584 privDbAttributes.add(kSecKeyPrintName, description);
585 privUniqueId->modify(CSSM_DL_DB_RECORD_PRIVATE_KEY, &privDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
586
587 // Finally fix the acl and owner of the private key to the specified access control settings.
588 initialAccess->setAccess(*privateKey, maker);
589
590 // Make the public key acl completely open
591 Access pubKeyAccess;
592 pubKeyAccess.setAccess(*publicKey, maker);
593
594 // Create keychain items which will represent the keys.
595 outPublicKey = safe_cast<KeyItem*>(&(*keychain->item(CSSM_DL_DB_RECORD_PUBLIC_KEY, pubUniqueId)));
596 outPrivateKey = safe_cast<KeyItem*>(&(*keychain->item(CSSM_DL_DB_RECORD_PRIVATE_KEY, privUniqueId)));
597 }
598 catch (...)
599 {
600 if (freePublicKey)
601 CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, TRUE);
602 if (freePrivateKey)
603 CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, TRUE);
604
605 if (deleteContext)
606 CSSM_DeleteContext(ccHandle);
607
608 throw;
609 }
610
611 if (freePublicKey)
612 CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, FALSE);
613 if (freePrivateKey)
614 CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, FALSE);
615
616 if (deleteContext)
617 CSSM_DeleteContext(ccHandle);
618 }
619
620 KeyItem *
621 KeyItem::generate(Keychain keychain,
622 CSSM_ALGORITHMS algorithm,
623 uint32 keySizeInBits,
624 CSSM_CC_HANDLE contextHandle,
625 CSSM_KEYUSE keyUsage,
626 uint32 keyAttr,
627 SecPointer<Access> initialAccess)
628 {
629 CssmClient::CSP appleCsp(gGuidAppleCSP);
630 CssmClient::CSP csp(NULL);
631 SSDb ssDb(NULL);
632 uint8 labelBytes[20];
633 CssmData label(labelBytes, sizeof(labelBytes));
634 bool freeKey = false;
635 bool deleteContext = false;
636 const CSSM_DATA *plabel = NULL;
637 KeyItem *outKey;
638
639 if (keychain)
640 {
641 if (!keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP)
642 MacOSError::throwMe(errSecInvalidKeychain);
643
644 ssDb = SSDb(safe_cast<SSDbImpl *>(&(*keychain->database())));
645 csp = keychain->csp();
646
647 // Generate a random label to use initially
648 CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW);
649 random.generate(label, label.Length);
650 plabel = &label;
651 }
652 else
653 {
654 // Not a persistant key so create it in the regular csp
655 csp = appleCsp;
656 }
657
658 // Create a Access::Maker for the initial owner of the private key.
659 ResourceControlContext *prcc = NULL, rcc;
660 const AccessCredentials *cred = NULL;
661 Access::Maker maker;
662 if (keychain && initialAccess)
663 {
664 memset(&rcc, 0, sizeof(rcc));
665 // @@@ 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.
666 maker.initialOwner(rcc);
667 // Create the cred we need to manipulate the keys until we actually set a new access control for them.
668 cred = maker.cred();
669 prcc = &rcc;
670 }
671
672 CSSM_KEY cssmKey;
673
674 CSSM_CC_HANDLE ccHandle = 0;
675
676 try
677 {
678 CSSM_RETURN status;
679 if (contextHandle)
680 ccHandle = contextHandle;
681 else
682 {
683 status = CSSM_CSP_CreateKeyGenContext(csp->handle(), algorithm, keySizeInBits, NULL, NULL, NULL, NULL, NULL, &ccHandle);
684 if (status)
685 CssmError::throwMe(status);
686 deleteContext = true;
687 }
688
689 if (ssDb)
690 {
691 CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle();
692 CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle;
693 CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } };
694 status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes);
695 if (status)
696 CssmError::throwMe(status);
697
698 keyAttr |= CSSM_KEYATTR_PERMANENT;
699 }
700
701 // Generate the key
702 status = CSSM_GenerateKey(ccHandle, keyUsage, keyAttr, plabel, prcc, &cssmKey);
703 if (status)
704 CssmError::throwMe(status);
705
706 if (ssDb)
707 {
708 freeKey = true;
709 // Find the keys we just generated in the DL to get SecKeyRef's to them
710 // so we can change the label to be the hash of the public key, and
711 // fix up other attributes.
712
713 // Look up key in the DLDB.
714 DbAttributes dbAttributes;
715 DbUniqueRecord uniqueId;
716 SSDbCursor dbCursor(ssDb, 1);
717 dbCursor->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY);
718 dbCursor->add(CSSM_DB_EQUAL, kSecKeyLabel, label);
719 CssmClient::Key key;
720 if (!dbCursor->nextKey(&dbAttributes, key, uniqueId))
721 MacOSError::throwMe(errSecItemNotFound);
722
723 // Finally fix the acl and owner of the key to the specified access control settings.
724 if (initialAccess)
725 initialAccess->setAccess(*key, maker);
726
727 // Create keychain items which will represent the keys.
728 outKey = safe_cast<KeyItem*>(&(*keychain->item(CSSM_DL_DB_RECORD_SYMMETRIC_KEY, uniqueId)));
729 }
730 else
731 {
732 CssmClient::Key tempKey(csp, cssmKey);
733 outKey = new KeyItem(tempKey);
734 }
735 }
736 catch (...)
737 {
738 if (freeKey)
739 {
740 // Delete the keys if something goes wrong so we don't end up with inaccesable keys in the database.
741 CSSM_FreeKey(csp->handle(), cred, &cssmKey, TRUE);
742 }
743
744 if (deleteContext)
745 CSSM_DeleteContext(ccHandle);
746
747 throw;
748 }
749
750 if (freeKey)
751 {
752 CSSM_FreeKey(csp->handle(), NULL, &cssmKey, FALSE);
753 }
754
755 if (deleteContext)
756 CSSM_DeleteContext(ccHandle);
757
758 return outKey;
759 }