]> git.saurik.com Git - apple/security.git/blob - Keychain/KeyItem.cpp
3147b06e37aa14b568c568a506d1c017b6fdac3c
[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/globals.h>
29
30 // @@@ This needs to be shared.
31 static CSSM_DB_NAME_ATTR(kSecKeyLabel, 6, "Label", 0, NULL, BLOB);
32 static CSSM_DB_NAME_ATTR(kSecKeyPrintName, 1, "PrintName", 0, NULL, BLOB);
33
34 using namespace KeychainCore;
35
36 KeyItem::KeyItem(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId) :
37 ItemImpl(keychain, primaryKey, uniqueId),
38 mKey(NULL)
39 {
40 }
41
42 KeyItem::KeyItem(const Keychain &keychain, const PrimaryKey &primaryKey) :
43 ItemImpl(keychain, primaryKey),
44 mKey(NULL)
45 {
46 }
47
48 KeyItem::KeyItem(KeyItem &keyItem) :
49 ItemImpl(keyItem),
50 mKey(NULL)
51 {
52 }
53
54 KeyItem::~KeyItem()
55 {
56 if (mKey)
57 {
58 CssmClient::SSDbUniqueRecord uniqueId(ssDbUniqueRecord());
59 uniqueId->database()->csp()->freeKey(*mKey);
60 uniqueId->allocator().free(mKey);
61 }
62 }
63
64 void
65 KeyItem::update()
66 {
67 MacOSError::throwMe(unimpErr);
68 }
69
70 Item
71 KeyItem::copyTo(const Keychain &keychain)
72 {
73 MacOSError::throwMe(unimpErr);
74 }
75
76 void
77 KeyItem::didModify()
78 {
79 }
80
81 PrimaryKey
82 KeyItem::add(Keychain &keychain)
83 {
84 MacOSError::throwMe(unimpErr);
85 }
86
87 CssmClient::SSDbUniqueRecord
88 KeyItem::ssDbUniqueRecord()
89 {
90 DbUniqueRecordImpl *impl = &*dbUniqueRecord();
91 return CssmClient::SSDbUniqueRecord(safe_cast<Security::CssmClient::SSDbUniqueRecordImpl *>(impl));
92 }
93
94 const CssmKey &
95 KeyItem::cssmKey()
96 {
97 if (!mKey)
98 {
99 CssmClient::SSDbUniqueRecord uniqueId(ssDbUniqueRecord());
100 CssmDataContainer dataBlob(uniqueId->allocator());
101 uniqueId->get(NULL, &dataBlob);
102 mKey = reinterpret_cast<CssmKey *>(dataBlob.Data);
103 dataBlob.Data = NULL;
104 dataBlob.Length = 0;
105 }
106
107 return *mKey;
108 }
109
110 const AccessCredentials *
111 KeyItem::getCredentials(
112 CSSM_ACL_AUTHORIZATION_TAG operation,
113 SecCredentialType credentialType)
114 {
115 // @@@ Fix this to actually examine the ACL for this key and consider operation and do the right thing.
116 AclFactory factory;
117 switch (credentialType)
118 {
119 case kSecCredentialTypeDefault:
120 return globals().credentials();
121 case kSecCredentialTypeWithUI:
122 return factory.promptCred();
123 case kSecCredentialTypeNoUI:
124 return factory.nullCred();
125 default:
126 MacOSError::throwMe(paramErr);
127 }
128 }
129
130 void
131 KeyItem::createPair(
132 Keychain keychain,
133 CSSM_ALGORITHMS algorithm,
134 uint32 keySizeInBits,
135 CSSM_CC_HANDLE contextHandle,
136 CSSM_KEYUSE publicKeyUsage,
137 uint32 publicKeyAttr,
138 CSSM_KEYUSE privateKeyUsage,
139 uint32 privateKeyAttr,
140 RefPointer<Access> initialAccess,
141 RefPointer<KeyItem> &outPublicKey,
142 RefPointer<KeyItem> &outPrivateKey)
143 {
144 bool freeKeys = false;
145 bool deleteContext = false;
146
147 if (!keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP)
148 MacOSError::throwMe(errSecInvalidKeychain);
149
150 SSDb ssDb(safe_cast<SSDbImpl *>(&(*keychain->database())));
151 CssmClient::CSP csp(keychain->csp());
152 CssmClient::CSP appleCsp(gGuidAppleCSP);
153
154 // Generate a random label to use initially
155 CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW);
156 uint8 labelBytes[20];
157 CssmData label(labelBytes, sizeof(labelBytes));
158 random.generate(label, label.Length);
159
160 // Create a Access::Maker for the initial owner of the private key.
161 ResourceControlContext rcc;
162 memset(&rcc, 0, sizeof(rcc));
163 Access::Maker maker;
164 // @@@ 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.
165 maker.initialOwner(rcc);
166 // Create the cred we need to manipulate the keys until we actually set a new access control for them.
167 const AccessCredentials *cred = maker.cred();
168
169 CSSM_KEY publicCssmKey, privateCssmKey;
170 memset(&publicCssmKey, 0, sizeof(publicCssmKey));
171 memset(&privateCssmKey, 0, sizeof(privateCssmKey));
172
173 CSSM_CC_HANDLE ccHandle = 0;
174
175 try
176 {
177 CSSM_RETURN status;
178 if (contextHandle)
179 ccHandle = contextHandle;
180 else
181 {
182 status = CSSM_CSP_CreateKeyGenContext(csp->handle(), algorithm, keySizeInBits, NULL, NULL, NULL, NULL, NULL, &ccHandle);
183 if (status)
184 CssmError::throwMe(status);
185 deleteContext = true;
186 }
187
188 CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle();
189 CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle;
190 CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } };
191 status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes);
192 if (status)
193 CssmError::throwMe(status);
194
195 // Generate the keypair
196 status = CSSM_GenerateKeyPair(ccHandle, publicKeyUsage, publicKeyAttr, &label, &publicCssmKey, privateKeyUsage, privateKeyAttr, &label, &rcc, &privateCssmKey);
197 if (status)
198 CssmError::throwMe(status);
199 freeKeys = true;
200
201 // Find the keys we just generated in the DL to get SecKeyRef's to them
202 // so we can change the label to be the hash of the public key, and
203 // fix up other attributes.
204
205 // Look up public key in the DLDB.
206 DbAttributes pubDbAttributes;
207 DbUniqueRecord pubUniqueId;
208 SSDbCursor dbPubCursor(ssDb, 1);
209 dbPubCursor->recordType(CSSM_DL_DB_RECORD_PUBLIC_KEY);
210 dbPubCursor->add(CSSM_DB_EQUAL, kSecKeyLabel, label);
211 CssmClient::Key publicKey;
212 if (!dbPubCursor->nextKey(&pubDbAttributes, publicKey, pubUniqueId))
213 MacOSError::throwMe(errSecItemNotFound);
214
215 // Look up private key in the DLDB.
216 DbAttributes privDbAttributes;
217 DbUniqueRecord privUniqueId;
218 SSDbCursor dbPrivCursor(ssDb, 1);
219 dbPrivCursor->recordType(CSSM_DL_DB_RECORD_PRIVATE_KEY);
220 dbPrivCursor->add(CSSM_DB_EQUAL, kSecKeyLabel, label);
221 CssmClient::Key privateKey;
222 if (!dbPrivCursor->nextKey(&privDbAttributes, privateKey, privUniqueId))
223 MacOSError::throwMe(errSecItemNotFound);
224
225 // Convert reference public key to a raw key so we can use it
226 // in the appleCsp.
227 CssmClient::WrapKey wrap(csp, CSSM_ALGID_NONE);
228 wrap.cred(cred);
229 CssmClient::Key rawPubKey = wrap(publicKey);
230
231 // Calculate the hash of the public key using the appleCSP.
232 CssmClient::PassThrough passThrough(appleCsp);
233 void *outData;
234 CssmData *cssmData;
235
236 /* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the
237 * associated key blob.
238 * Key is specified in CSSM_CSP_CreatePassThroughContext.
239 * Hash is allocated bythe CSP, in the App's memory, and returned
240 * in *outData. */
241 passThrough.key(rawPubKey);
242 passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData);
243 cssmData = reinterpret_cast<CssmData *>(outData);
244 CssmData &pubKeyHash = *cssmData;
245
246 std::string description(initialAccess->promptDescription());
247 // Set the label of the public key to the public key hash.
248 // Set the PrintName of the public key to the description in the acl.
249 pubDbAttributes.add(kSecKeyLabel, pubKeyHash);
250 pubDbAttributes.add(kSecKeyPrintName, description);
251 pubUniqueId->modify(CSSM_DL_DB_RECORD_PUBLIC_KEY, &pubDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
252
253 // Set the label of the private key to the public key hash.
254 // Set the PrintName of the private key to the description in the acl.
255 privDbAttributes.add(kSecKeyLabel, pubKeyHash);
256 privDbAttributes.add(kSecKeyPrintName, description);
257 privUniqueId->modify(CSSM_DL_DB_RECORD_PRIVATE_KEY, &privDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
258
259 // @@@ Not exception safe!
260 csp.allocator().free(cssmData->Data);
261 csp.allocator().free(cssmData);
262
263 // Finally fix the acl and owner of the private key to the specified access control settings.
264 initialAccess->setAccess(*privateKey, maker);
265
266 // Make the public key acl completely open
267 Access pubKeyAccess;
268 pubKeyAccess.setAccess(*publicKey, maker);
269
270 // Create keychain items which will represent the keys.
271 outPublicKey = safe_cast<KeyItem*>(&(*keychain->item(CSSM_DL_DB_RECORD_PUBLIC_KEY, pubUniqueId)));
272 outPrivateKey = safe_cast<KeyItem*>(&(*keychain->item(CSSM_DL_DB_RECORD_PRIVATE_KEY, privUniqueId)));
273 }
274 catch (...)
275 {
276 if (freeKeys)
277 {
278 // Delete the keys if something goes wrong so we don't end up with inaccesable keys in the database.
279 CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, TRUE);
280 CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, TRUE);
281 }
282
283 if (deleteContext)
284 CSSM_DeleteContext(ccHandle);
285
286 throw;
287 }
288
289 if (freeKeys)
290 {
291 CSSM_FreeKey(csp->handle(), NULL, &publicCssmKey, FALSE);
292 CSSM_FreeKey(csp->handle(), NULL, &privateCssmKey, FALSE);
293 }
294
295 if (deleteContext)
296 CSSM_DeleteContext(ccHandle);
297 }
298
299 void
300 KeyItem::importPair(
301 Keychain keychain,
302 const CSSM_KEY &publicWrappedKey,
303 const CSSM_KEY &privateWrappedKey,
304 RefPointer<Access> initialAccess,
305 RefPointer<KeyItem> &outPublicKey,
306 RefPointer<KeyItem> &outPrivateKey)
307 {
308 bool freePublicKey = false;
309 bool freePrivateKey = false;
310 bool deleteContext = false;
311
312 if (!keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP)
313 MacOSError::throwMe(errSecInvalidKeychain);
314
315 SSDb ssDb(safe_cast<SSDbImpl *>(&(*keychain->database())));
316 CssmClient::CSP csp(keychain->csp());
317 CssmClient::CSP appleCsp(gGuidAppleCSP);
318
319 // Create a Access::Maker for the initial owner of the private key.
320 ResourceControlContext rcc;
321 memset(&rcc, 0, sizeof(rcc));
322 Access::Maker maker;
323 // @@@ 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.
324 maker.initialOwner(rcc);
325 // Create the cred we need to manipulate the keys until we actually set a new access control for them.
326 const AccessCredentials *cred = maker.cred();
327
328 CSSM_KEY publicCssmKey, privateCssmKey;
329 memset(&publicCssmKey, 0, sizeof(publicCssmKey));
330 memset(&privateCssmKey, 0, sizeof(privateCssmKey));
331
332 CSSM_CC_HANDLE ccHandle = 0;
333
334 try
335 {
336 CSSM_RETURN status;
337
338 // Calculate the hash of the public key using the appleCSP.
339 CssmClient::PassThrough passThrough(appleCsp);
340 void *outData;
341 CssmData *cssmData;
342
343 /* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the
344 * associated key blob.
345 * Key is specified in CSSM_CSP_CreatePassThroughContext.
346 * Hash is allocated bythe CSP, in the App's memory, and returned
347 * in *outData. */
348 passThrough.key(&publicWrappedKey);
349 passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData);
350 cssmData = reinterpret_cast<CssmData *>(outData);
351 CssmData &pubKeyHash = *cssmData;
352
353 status = CSSM_CSP_CreateSymmetricContext(csp->handle(), publicWrappedKey.KeyHeader.WrapAlgorithmId, CSSM_ALGMODE_NONE, NULL, NULL, NULL, CSSM_PADDING_NONE, NULL, &ccHandle);
354 if (status)
355 CssmError::throwMe(status);
356 deleteContext = true;
357
358 CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle();
359 CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle;
360 CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } };
361 status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes);
362 if (status)
363 CssmError::throwMe(status);
364
365 // Unwrap the the keys
366 CSSM_DATA descriptiveData = {0, NULL};
367
368 status = CSSM_UnwrapKey(
369 ccHandle,
370 NULL,
371 &publicWrappedKey,
372 publicWrappedKey.KeyHeader.KeyUsage,
373 publicWrappedKey.KeyHeader.KeyAttr | CSSM_KEYATTR_PERMANENT,
374 &pubKeyHash,
375 &rcc,
376 &publicCssmKey,
377 &descriptiveData);
378
379 if (status)
380 CssmError::throwMe(status);
381 freePublicKey = true;
382
383 if (descriptiveData.Data != NULL)
384 free (descriptiveData.Data);
385
386 status = CSSM_UnwrapKey(
387 ccHandle,
388 NULL,
389 &privateWrappedKey,
390 privateWrappedKey.KeyHeader.KeyUsage,
391 privateWrappedKey.KeyHeader.KeyAttr | CSSM_KEYATTR_PERMANENT,
392 &pubKeyHash,
393 &rcc,
394 &privateCssmKey,
395 &descriptiveData);
396
397 if (status)
398 CssmError::throwMe(status);
399
400 if (descriptiveData.Data != NULL)
401 free (descriptiveData.Data);
402
403 freePrivateKey = true;
404
405 // Find the keys we just generated in the DL to get SecKeyRef's to them
406 // so we can change the label to be the hash of the public key, and
407 // fix up other attributes.
408
409 // Look up public key in the DLDB.
410 DbAttributes pubDbAttributes;
411 DbUniqueRecord pubUniqueId;
412 SSDbCursor dbPubCursor(ssDb, 1);
413 dbPubCursor->recordType(CSSM_DL_DB_RECORD_PUBLIC_KEY);
414 dbPubCursor->add(CSSM_DB_EQUAL, kSecKeyLabel, pubKeyHash);
415 CssmClient::Key publicKey;
416 if (!dbPubCursor->nextKey(&pubDbAttributes, publicKey, pubUniqueId))
417 MacOSError::throwMe(errSecItemNotFound);
418
419 // Look up private key in the DLDB.
420 DbAttributes privDbAttributes;
421 DbUniqueRecord privUniqueId;
422 SSDbCursor dbPrivCursor(ssDb, 1);
423 dbPrivCursor->recordType(CSSM_DL_DB_RECORD_PRIVATE_KEY);
424 dbPrivCursor->add(CSSM_DB_EQUAL, kSecKeyLabel, pubKeyHash);
425 CssmClient::Key privateKey;
426 if (!dbPrivCursor->nextKey(&privDbAttributes, privateKey, privUniqueId))
427 MacOSError::throwMe(errSecItemNotFound);
428
429 // @@@ Not exception safe!
430 csp.allocator().free(cssmData->Data);
431 csp.allocator().free(cssmData);
432
433 std::string description(initialAccess->promptDescription());
434 // Set the label of the public key to the public key hash.
435 // Set the PrintName of the public key to the description in the acl.
436 pubDbAttributes.add(kSecKeyPrintName, description);
437 pubUniqueId->modify(CSSM_DL_DB_RECORD_PUBLIC_KEY, &pubDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
438
439 // Set the label of the private key to the public key hash.
440 // Set the PrintName of the private key to the description in the acl.
441 privDbAttributes.add(kSecKeyPrintName, description);
442 privUniqueId->modify(CSSM_DL_DB_RECORD_PRIVATE_KEY, &privDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
443
444 // Finally fix the acl and owner of the private key to the specified access control settings.
445 initialAccess->setAccess(*privateKey, maker);
446
447 // Make the public key acl completely open
448 Access pubKeyAccess;
449 pubKeyAccess.setAccess(*publicKey, maker);
450
451 // Create keychain items which will represent the keys.
452 outPublicKey = safe_cast<KeyItem*>(&(*keychain->item(CSSM_DL_DB_RECORD_PUBLIC_KEY, pubUniqueId)));
453 outPrivateKey = safe_cast<KeyItem*>(&(*keychain->item(CSSM_DL_DB_RECORD_PRIVATE_KEY, privUniqueId)));
454 }
455 catch (...)
456 {
457 if (freePublicKey)
458 CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, TRUE);
459 if (freePrivateKey)
460 CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, TRUE);
461
462 if (deleteContext)
463 CSSM_DeleteContext(ccHandle);
464
465 throw;
466 }
467
468 if (freePublicKey)
469 CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, FALSE);
470 if (freePrivateKey)
471 CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, FALSE);
472
473 if (deleteContext)
474 CSSM_DeleteContext(ccHandle);
475 }