]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 A |
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> | |
b1ab9ed8 A |
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" | |
427c49bc A |
39 | #include <CommonCrypto/CommonDigest.h> |
40 | #include <SecBase.h> | |
b1ab9ed8 A |
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) | |
427c49bc | 100 | MacOSError::throwMe(errSecParam); |
b1ab9ed8 A |
101 | } |
102 | ||
c2a06e24 | 103 | KeyItem::~KeyItem() |
b1ab9ed8 A |
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. */ | |
427c49bc A |
129 | const CSSM_KEY *cssmKey = key(); |
130 | if (cssmKey && (0==(cssmKey->KeyHeader.KeyAttr & CSSM_KEYATTR_EXTRACTABLE))) | |
131 | { | |
132 | MacOSError::throwMe(errSecDataNotAvailable); | |
133 | } | |
b1ab9ed8 A |
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)); | |
427c49bc | 140 | random.generate(label, (uint32)label.Length); |
b1ab9ed8 A |
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)); | |
427c49bc | 157 | random.generate(iv, (uint32)iv.length()); |
b1ab9ed8 A |
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)); | |
427c49bc | 255 | random.generate(label, (uint32)label.Length); |
b1ab9ed8 A |
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)); | |
427c49bc | 272 | random.generate(iv, (uint32)iv.length()); |
b1ab9ed8 A |
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 | { | |
427c49bc | 380 | MacOSError::throwMe(errSecUnimplemented); |
b1ab9ed8 A |
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: | |
427c49bc | 503 | MacOSError::throwMe(errSecParam); |
b1ab9ed8 A |
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)); | |
427c49bc | 555 | random.generate(label, (uint32)label.Length); |
b1ab9ed8 A |
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); | |
427c49bc | 991 | random.generate(label, (uint32)label.Length); |
b1ab9ed8 A |
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(); | |
427c49bc A |
1166 | |
1167 | if ((baseAlg != CSSM_ALGID_RSA) && (baseAlg != CSSM_ALGID_ECDSA)) | |
b1ab9ed8 | 1168 | { |
427c49bc | 1169 | MacOSError::throwMe(errSecParam); |
b1ab9ed8 A |
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(); | |
427c49bc | 1225 | if ((baseAlg != CSSM_ALGID_RSA) && (baseAlg != CSSM_ALGID_ECDSA)) |
b1ab9ed8 | 1226 | { |
427c49bc | 1227 | MacOSError::throwMe(errSecParam); |
b1ab9ed8 A |
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 | { | |
427c49bc | 1282 | MacOSError::throwMe(errSecParam); |
b1ab9ed8 A |
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 | { | |
427c49bc | 1321 | MacOSError::throwMe(errSecParam); |
b1ab9ed8 A |
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 | ||
427c49bc A |
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 |