2 * Copyright (c) 2002 Apple Computer, Inc. All Rights Reserved.
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
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.
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>
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
);
34 using namespace KeychainCore
;
36 KeyItem::KeyItem(const Keychain
&keychain
, const PrimaryKey
&primaryKey
, const CssmClient::DbUniqueRecord
&uniqueId
) :
37 ItemImpl(keychain
, primaryKey
, uniqueId
),
42 KeyItem::KeyItem(const Keychain
&keychain
, const PrimaryKey
&primaryKey
) :
43 ItemImpl(keychain
, primaryKey
),
48 KeyItem::KeyItem(KeyItem
&keyItem
) :
58 CssmClient::SSDbUniqueRecord
uniqueId(ssDbUniqueRecord());
59 uniqueId
->database()->csp()->freeKey(*mKey
);
60 uniqueId
->allocator().free(mKey
);
67 MacOSError::throwMe(unimpErr
);
71 KeyItem::copyTo(const Keychain
&keychain
)
73 MacOSError::throwMe(unimpErr
);
82 KeyItem::add(Keychain
&keychain
)
84 MacOSError::throwMe(unimpErr
);
87 CssmClient::SSDbUniqueRecord
88 KeyItem::ssDbUniqueRecord()
90 DbUniqueRecordImpl
*impl
= &*dbUniqueRecord();
91 return CssmClient::SSDbUniqueRecord(safe_cast
<Security::CssmClient::SSDbUniqueRecordImpl
*>(impl
));
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
;
110 const AccessCredentials
*
111 KeyItem::getCredentials(
112 CSSM_ACL_AUTHORIZATION_TAG operation
,
113 SecCredentialType credentialType
)
115 // @@@ Fix this to actually examine the ACL for this key and consider operation and do the right thing.
117 switch (credentialType
)
119 case kSecCredentialTypeDefault
:
120 return globals().credentials();
121 case kSecCredentialTypeWithUI
:
122 return factory
.promptCred();
123 case kSecCredentialTypeNoUI
:
124 return factory
.nullCred();
126 MacOSError::throwMe(paramErr
);
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
)
144 bool freeKeys
= false;
145 bool deleteContext
= false;
147 if (!keychain
->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP
)
148 MacOSError::throwMe(errSecInvalidKeychain
);
150 SSDb
ssDb(safe_cast
<SSDbImpl
*>(&(*keychain
->database())));
151 CssmClient::CSP
csp(keychain
->csp());
152 CssmClient::CSP
appleCsp(gGuidAppleCSP
);
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
);
160 // Create a Access::Maker for the initial owner of the private key.
161 ResourceControlContext rcc
;
162 memset(&rcc
, 0, sizeof(rcc
));
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();
169 CSSM_KEY publicCssmKey
, privateCssmKey
;
170 memset(&publicCssmKey
, 0, sizeof(publicCssmKey
));
171 memset(&privateCssmKey
, 0, sizeof(privateCssmKey
));
173 CSSM_CC_HANDLE ccHandle
= 0;
179 ccHandle
= contextHandle
;
182 status
= CSSM_CSP_CreateKeyGenContext(csp
->handle(), algorithm
, keySizeInBits
, NULL
, NULL
, NULL
, NULL
, NULL
, &ccHandle
);
184 CssmError::throwMe(status
);
185 deleteContext
= true;
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
);
193 CssmError::throwMe(status
);
195 // Generate the keypair
196 status
= CSSM_GenerateKeyPair(ccHandle
, publicKeyUsage
, publicKeyAttr
, &label
, &publicCssmKey
, privateKeyUsage
, privateKeyAttr
, &label
, &rcc
, &privateCssmKey
);
198 CssmError::throwMe(status
);
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.
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
);
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
);
225 // Convert reference public key to a raw key so we can use it
227 CssmClient::WrapKey
wrap(csp
, CSSM_ALGID_NONE
);
229 CssmClient::Key rawPubKey
= wrap(publicKey
);
231 // Calculate the hash of the public key using the appleCSP.
232 CssmClient::PassThrough
passThrough(appleCsp
);
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
241 passThrough
.key(rawPubKey
);
242 passThrough(CSSM_APPLECSP_KEYDIGEST
, NULL
, &outData
);
243 cssmData
= reinterpret_cast<CssmData
*>(outData
);
244 CssmData
&pubKeyHash
= *cssmData
;
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
);
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
);
259 // @@@ Not exception safe!
260 csp
.allocator().free(cssmData
->Data
);
261 csp
.allocator().free(cssmData
);
263 // Finally fix the acl and owner of the private key to the specified access control settings.
264 initialAccess
->setAccess(*privateKey
, maker
);
266 // Make the public key acl completely open
268 pubKeyAccess
.setAccess(*publicKey
, maker
);
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
)));
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
);
284 CSSM_DeleteContext(ccHandle
);
291 CSSM_FreeKey(csp
->handle(), NULL
, &publicCssmKey
, FALSE
);
292 CSSM_FreeKey(csp
->handle(), NULL
, &privateCssmKey
, FALSE
);
296 CSSM_DeleteContext(ccHandle
);
302 const CSSM_KEY
&publicWrappedKey
,
303 const CSSM_KEY
&privateWrappedKey
,
304 RefPointer
<Access
> initialAccess
,
305 RefPointer
<KeyItem
> &outPublicKey
,
306 RefPointer
<KeyItem
> &outPrivateKey
)
308 bool freePublicKey
= false;
309 bool freePrivateKey
= false;
310 bool deleteContext
= false;
312 if (!keychain
->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP
)
313 MacOSError::throwMe(errSecInvalidKeychain
);
315 SSDb
ssDb(safe_cast
<SSDbImpl
*>(&(*keychain
->database())));
316 CssmClient::CSP
csp(keychain
->csp());
317 CssmClient::CSP
appleCsp(gGuidAppleCSP
);
319 // Create a Access::Maker for the initial owner of the private key.
320 ResourceControlContext rcc
;
321 memset(&rcc
, 0, sizeof(rcc
));
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();
328 CSSM_KEY publicCssmKey
, privateCssmKey
;
329 memset(&publicCssmKey
, 0, sizeof(publicCssmKey
));
330 memset(&privateCssmKey
, 0, sizeof(privateCssmKey
));
332 CSSM_CC_HANDLE ccHandle
= 0;
338 // Calculate the hash of the public key using the appleCSP.
339 CssmClient::PassThrough
passThrough(appleCsp
);
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
348 passThrough
.key(&publicWrappedKey
);
349 passThrough(CSSM_APPLECSP_KEYDIGEST
, NULL
, &outData
);
350 cssmData
= reinterpret_cast<CssmData
*>(outData
);
351 CssmData
&pubKeyHash
= *cssmData
;
353 status
= CSSM_CSP_CreateSymmetricContext(csp
->handle(), publicWrappedKey
.KeyHeader
.WrapAlgorithmId
, CSSM_ALGMODE_NONE
, NULL
, NULL
, NULL
, CSSM_PADDING_NONE
, NULL
, &ccHandle
);
355 CssmError::throwMe(status
);
356 deleteContext
= true;
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
);
363 CssmError::throwMe(status
);
365 // Unwrap the the keys
366 CSSM_DATA descriptiveData
= {0, NULL
};
368 status
= CSSM_UnwrapKey(
372 publicWrappedKey
.KeyHeader
.KeyUsage
,
373 publicWrappedKey
.KeyHeader
.KeyAttr
| CSSM_KEYATTR_PERMANENT
,
380 CssmError::throwMe(status
);
381 freePublicKey
= true;
383 if (descriptiveData
.Data
!= NULL
)
384 free (descriptiveData
.Data
);
386 status
= CSSM_UnwrapKey(
390 privateWrappedKey
.KeyHeader
.KeyUsage
,
391 privateWrappedKey
.KeyHeader
.KeyAttr
| CSSM_KEYATTR_PERMANENT
,
398 CssmError::throwMe(status
);
400 if (descriptiveData
.Data
!= NULL
)
401 free (descriptiveData
.Data
);
403 freePrivateKey
= true;
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.
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
);
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
);
429 // @@@ Not exception safe!
430 csp
.allocator().free(cssmData
->Data
);
431 csp
.allocator().free(cssmData
);
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
);
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
);
444 // Finally fix the acl and owner of the private key to the specified access control settings.
445 initialAccess
->setAccess(*privateKey
, maker
);
447 // Make the public key acl completely open
449 pubKeyAccess
.setAccess(*publicKey
, maker
);
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
)));
458 CSSM_FreeKey(csp
->handle(), cred
, &publicCssmKey
, TRUE
);
460 CSSM_FreeKey(csp
->handle(), cred
, &privateCssmKey
, TRUE
);
463 CSSM_DeleteContext(ccHandle
);
469 CSSM_FreeKey(csp
->handle(), cred
, &publicCssmKey
, FALSE
);
471 CSSM_FreeKey(csp
->handle(), cred
, &privateCssmKey
, FALSE
);
474 CSSM_DeleteContext(ccHandle
);