]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/SecExternalRep.cpp
Security-57740.1.18.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / SecExternalRep.cpp
1 /*
2 * Copyright (c) 2004,2011,2013-2014 Apple 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 * SecExternalRep.cpp - private class representing an external representation of
24 * a SecKeychainItemRef, used by SecImportExport.h
25 */
26
27 #include "SecExternalRep.h"
28 #include "SecImportExportPem.h"
29 #include "SecImportExportAgg.h"
30 #include "SecImportExportUtils.h"
31 #include "SecImportExportPkcs8.h"
32 #include "SecImportExportCrypto.h"
33 #include "SecImportExportOpenSSH.h"
34 #include <security_utilities/errors.h>
35 #include <Security/SecBase.h>
36 #include <Security/SecKeyPriv.h>
37 #include <Security/SecCertificate.h>
38 #include <Security/cssmapi.h>
39
40 using namespace Security;
41 using namespace KeychainCore;
42
43
44 #pragma mark --- SecExportRep Subclasses seen only by SecExportRep::vend() ---
45
46 namespace SecExport {
47
48 class Key : public SecExportRep
49 {
50 friend class SecExportRep;
51 protected:
52 Key(
53 CFTypeRef kcItemRef);
54 ~Key();
55 OSStatus exportRep(
56 SecExternalFormat format,
57 SecItemImportExportFlags flags,
58 const SecKeyImportExportParameters *keyParams, // optional
59 CFMutableDataRef outData, // data appended here
60 const char **pemHeader); // e.g., "RSA PUBLIC KEY"
61
62 private:
63 CSSM_ALGORITHMS mKeyAlg;
64 const CSSM_KEY *mCssmKey;
65 };
66
67 class Cert : public SecExportRep
68 {
69 friend class SecExportRep;
70 protected:
71 Cert(
72 CFTypeRef kcItemRef);
73 ~Cert();
74 OSStatus exportRep(
75 SecExternalFormat format,
76 SecItemImportExportFlags flags,
77 const SecKeyImportExportParameters *keyParams, // optional
78 CFMutableDataRef outData, // data appended here
79 const char **pemHeader); // e.g., "CERTIFICATE"
80 };
81
82 } /* namespace SecExport */
83
84 #pragma mark --- SecExportRep: Representation of an internal object on export ---
85
86 SecExportRep::SecExportRep(
87 CFTypeRef kcItemRef) :
88 mKcItem((SecKeychainItemRef)kcItemRef),
89 mPemParamLines(NULL)
90 {
91 CFRetain(mKcItem);
92 }
93
94 SecExportRep::~SecExportRep()
95 {
96 if(mKcItem) {
97 CFRelease(mKcItem);
98 }
99 if(mPemParamLines) {
100 CFRelease(mPemParamLines);
101 }
102 }
103
104 SecExportRep::SecExportRep() {
105 MacOSError::throwMe(errSecInvalidItemRef);
106 }
107
108 /* must be implemented by subclass */
109 OSStatus SecExportRep::exportRep(
110 SecExternalFormat format,
111 SecItemImportExportFlags flags,
112 const SecKeyImportExportParameters *keyParams, // optional
113 CFMutableDataRef outData, // data appended here
114 const char **pemHeader) // e.g., "X509 CERTIFICATE"
115 {
116 MacOSError::throwMe(errSecInvalidItemRef);
117 }
118
119 /*
120 * Sole public means of obtaining a SecExportRep object. In fact only instances
121 * of subclasses are vended but caller does not know that.
122 *
123 * Gleans SecExternalItemType from incoming type, throws MacOSError if
124 * incoming type is bogus.
125 *
126 * Vended object holds a reference to kcItem for its lifetime.
127 */
128 SecExportRep *SecExportRep::vend(
129 CFTypeRef kcItemRef)
130 {
131 CFTypeID itemType = CFGetTypeID(kcItemRef);
132 if(itemType == SecCertificateGetTypeID()) {
133 return new SecExport::Cert(kcItemRef);
134 }
135 else if(itemType == SecKeyGetTypeID()) {
136 return new SecExport::Key(kcItemRef);
137 }
138 else {
139 MacOSError::throwMe(errSecInvalidItemRef);
140 }
141 }
142
143 #pragma mark --- Key External rep ---
144
145 SecExport::Key::Key(
146 CFTypeRef kcItemRef) :
147 SecExportRep(kcItemRef)
148 {
149
150 /* figure out if it's public, private, or session */
151 OSStatus ortn;
152 ortn = SecKeyGetCSSMKey((SecKeyRef)kcItemRef, &mCssmKey);
153 if(ortn) {
154 SecImpExpDbg("SecKeyGetCSSMKey failure in SecExportRep::Key()");
155 MacOSError::throwMe(ortn);
156 }
157 switch(mCssmKey->KeyHeader.KeyClass) {
158 case CSSM_KEYCLASS_PUBLIC_KEY:
159 mExternType = kSecItemTypePublicKey;
160 SecImpExpDbg("SecExportRep::Key(): SET_PubKey");
161 break;
162 case CSSM_KEYCLASS_PRIVATE_KEY:
163 mExternType = kSecItemTypePrivateKey;
164 SecImpExpDbg("SecExportRep::Key(): SET_PrivKey");
165 break;
166 case CSSM_KEYCLASS_SESSION_KEY:
167 mExternType = kSecItemTypeSessionKey;
168 SecImpExpDbg("SecExportRep::Key(): SET_SessionKey");
169 break;
170 default:
171 SecImpExpDbg("SecExportRep::Key(): invalid KeyClass (%lu)",
172 (unsigned long)mCssmKey->KeyHeader.KeyClass);
173 MacOSError::throwMe(errSecInvalidItemRef);
174 }
175 mKeyAlg = mCssmKey->KeyHeader.AlgorithmId;
176 }
177
178 SecExport::Key::~Key()
179 {
180 /* nothing for now */
181 }
182
183 /*
184 * The heart of this class: cook up external representation, appending to
185 * existing CFMutableDataRef.
186 */
187 OSStatus SecExport::Key::exportRep(
188 SecExternalFormat format,
189 SecItemImportExportFlags flags,
190 const SecKeyImportExportParameters *keyParams, // optional
191 CFMutableDataRef outData, // data appended here
192 const char **pemHeader)// e.g., "X509 CERTIFICATE"
193 {
194 assert(outData != NULL);
195 assert(mKcItem != NULL);
196 assert(mCssmKey != NULL);
197
198 /*
199 * Currently only OpsnSSH formats allow for a DescriptiveData field
200 * in either wrapped or NULL wrap forms. (In OpenSSH parlance this is
201 * the 'comment' field). Infer the DescriptiveData to be embedded
202 * in the exported key from the item's PrintName attribute.
203 */
204 CssmAutoData descrData(Allocator::standard());
205 switch(format) {
206 case kSecFormatSSH:
207 case kSecFormatSSHv2:
208 case kSecFormatWrappedSSH:
209 impExpOpensshInferDescData((SecKeyRef)mKcItem, descrData);
210 break;
211 default:
212 break;
213 }
214
215 /*
216 * Handle wrapped key formats.
217 */
218 switch(format) {
219 case kSecFormatWrappedPKCS8:
220 return impExpPkcs8Export((SecKeyRef)mKcItem, flags, keyParams,
221 outData, pemHeader);
222 case kSecFormatWrappedOpenSSL:
223 return impExpWrappedKeyOpenSslExport((SecKeyRef)mKcItem, flags, keyParams,
224 outData, pemHeader, &mPemParamLines);
225 case kSecFormatWrappedSSH:
226 return impExpWrappedOpenSSHExport((SecKeyRef)mKcItem, flags, keyParams,
227 descrData, outData);
228 case kSecFormatWrappedLSH:
229 return errSecUnsupportedFormat;
230 default:
231 break;
232 }
233
234 /*
235 * Remaining formats just do a NULL key wrap. Figure out the appropriate
236 * CDSA-specific format and wrap parameters.
237 */
238 OSStatus ortn = errSecSuccess;
239 CSSM_KEYBLOB_FORMAT blobForm;
240
241 switch(mExternType) {
242 case kSecItemTypePublicKey:
243 switch(mKeyAlg) {
244 case CSSM_ALGID_RSA:
245 *pemHeader = PEM_STRING_RSA_PUBLIC;
246 break;
247 case CSSM_ALGID_DH:
248 *pemHeader = PEM_STRING_DH_PUBLIC;
249 break;
250 case CSSM_ALGID_DSA:
251 *pemHeader = PEM_STRING_DSA_PUBLIC;
252 break;
253 case CSSM_ALGID_ECDSA:
254 *pemHeader = PEM_STRING_ECDSA_PUBLIC;
255 break;
256 default:
257 SecImpExpDbg("SecExportRep::exportRep unknown public key alg %lu",
258 (unsigned long)mKeyAlg);
259 return errSecUnsupportedFormat;
260 } /* end switch(mKeyAlg) */
261 break; /* from case externType kSecItemTypePublicKey */
262
263 case kSecItemTypePrivateKey:
264 switch(mKeyAlg) {
265 case CSSM_ALGID_RSA:
266 *pemHeader = PEM_STRING_RSA;
267 break;
268 case CSSM_ALGID_DH:
269 *pemHeader = PEM_STRING_DH_PRIVATE;
270 break;
271 case CSSM_ALGID_DSA:
272 *pemHeader = PEM_STRING_DSA;
273 break;
274 case CSSM_ALGID_ECDSA:
275 *pemHeader = PEM_STRING_ECDSA_PRIVATE;
276 break;
277 default:
278 SecImpExpDbg("SecExportRep::exportRep unknown private key alg "
279 "%lu", (unsigned long)mKeyAlg);
280 return errSecUnsupportedFormat;
281 } /* end switch(mKeyAlg) */
282 break; /* from case externType kSecItemTypePrivateKey */
283
284 case kSecItemTypeSessionKey:
285 *pemHeader = PEM_STRING_SESSION;
286 break;
287 default:
288 assert(0);
289 return errSecInvalidItemRef;
290 } /* switch(mExternType) */
291
292 /* Map our external params to CDSA blob format */
293 CSSM_KEYCLASS keyClass;
294 ortn = impExpKeyForm(format, mExternType, mKeyAlg, &blobForm, &keyClass);
295 if(ortn) {
296 return ortn;
297 }
298
299 /* Specify format of null-wrapped key */
300 CSSM_ATTRIBUTE_TYPE formatAttrType = CSSM_ATTRIBUTE_NONE;
301 switch(mExternType) {
302 case kSecItemTypePrivateKey:
303 formatAttrType = CSSM_ATTRIBUTE_PRIVATE_KEY_FORMAT;
304 break;
305 case kSecItemTypePublicKey:
306 formatAttrType = CSSM_ATTRIBUTE_PUBLIC_KEY_FORMAT;
307 break;
308 /* symmetric key doesn't have a format */
309 default:
310 break;
311 }
312
313 CSSM_CSP_HANDLE cspHand;
314 ortn = SecKeyGetCSPHandle((SecKeyRef)mKcItem, &cspHand);
315 if(ortn) {
316 SecImpExpDbg("SecExportRep::exportRep SecKeyGetCSPHandle error");
317 return ortn;
318 }
319
320 /* perform the NULL wrap --> wrapped Key */
321 CSSM_KEY wrappedKey;
322 memset(&wrappedKey, 0, sizeof(wrappedKey));
323 const CSSM_DATA &dd = descrData;
324 ortn = impExpExportKeyCommon(cspHand,
325 (SecKeyRef)mKcItem,
326 NULL, // wrappingKey not used for NULL
327 &wrappedKey, // destination
328 CSSM_ALGID_NONE,
329 CSSM_ALGMODE_NONE,
330 CSSM_PADDING_NONE,
331 CSSM_KEYBLOB_WRAPPED_FORMAT_NONE,
332 formatAttrType,
333 blobForm,
334 &dd, // descriptiveData
335 NULL); // IV
336
337 if(ortn == CSSM_OK) {
338 /* pass key data back to caller */
339 CFDataAppendBytes(outData, wrappedKey.KeyData.Data, wrappedKey.KeyData.Length);
340 }
341 CSSM_FreeKey(cspHand, NULL, &wrappedKey, CSSM_FALSE);
342 return ortn;
343 }
344
345 #pragma mark --- Certificate External rep ---
346
347 SecExport::Cert::Cert(
348 CFTypeRef kcItemRef) :
349 SecExportRep(kcItemRef)
350 {
351 mExternType = kSecItemTypeCertificate;
352 }
353
354 SecExport::Cert::~Cert()
355 {
356 /* nothing for now */
357 }
358
359 /*
360 * The heart of this class: cook up external representation, appending to
361 * existing CFMutableDataRef.
362 */
363 OSStatus SecExport::Cert::exportRep(
364 SecExternalFormat format,
365 SecItemImportExportFlags flags,
366 const SecKeyImportExportParameters *keyParams, // optional
367 CFMutableDataRef outData, // data appended here
368 const char **pemHeader)// e.g., "X509 CERTIFICATE"
369 {
370 assert(outData != NULL);
371 assert(mKcItem != NULL);
372
373 switch(format) {
374 case kSecFormatUnknown: // default
375 case kSecFormatX509Cert: // currently, only supported format
376 break;
377 default:
378 SecImpExpDbg("SecExportRep::exportRep unsupported format for cert");
379 return errSecUnsupportedFormat;
380 }
381
382 CFDataRef cdata = SecCertificateCopyData((SecCertificateRef)mKcItem);
383 if(!cdata) {
384 SecImpExpDbg("SecExportRep::exportRep SecCertificateGetData error");
385 return errSecUnsupportedFormat;
386 }
387
388 CFDataAppendBytes(outData, CFDataGetBytePtr(cdata), CFDataGetLength(cdata));
389 CFRelease(cdata);
390 *pemHeader = PEM_STRING_X509;
391 return errSecSuccess;
392 }
393
394 #pragma mark --- SecImportRep: Representation of an external object on import ---
395
396 /*
397 * for import, when we have the external representation.
398 * All arguments except for the CFDataRef are optional (i.e., "unknown"
399 * is legal).
400 */
401 SecImportRep::SecImportRep(
402 CFDataRef external,
403 SecExternalItemType externType, // may be unknown
404 SecExternalFormat externFormat, // may be unknown
405 CSSM_ALGORITHMS keyAlg, // may be unknown, CSSM_ALGID_NONE
406 CFArrayRef pemParamLines /* = NULL */ ) :
407 mPrintName(NULL),
408 mExternal(external),
409 mExternType(externType),
410 mExternFormat(externFormat),
411 mKeyAlg(keyAlg),
412 mPemParamLines(pemParamLines)
413 {
414 CFRetain(mExternal);
415 }
416
417 SecImportRep::~SecImportRep()
418 {
419 if(mPrintName) {
420 free(mPrintName);
421 }
422 if(mExternal) {
423 CFRelease(mExternal);
424 }
425 if(mPemParamLines) {
426 CFRelease(mPemParamLines);
427 }
428 }
429
430 /*
431 * Convert to one or more SecItemRefs and/or import to keychain.
432 * The cspHand handle MUST be a CSPDL handle, not a raw CSP handle.
433 */
434 OSStatus SecImportRep::importRep(
435 SecKeychainRef importKeychain, // optional
436 CSSM_CSP_HANDLE cspHand, // required
437 SecItemImportExportFlags flags,
438 const SecKeyImportExportParameters *keyParams, // optional
439 ImpPrivKeyImportState &keyImportState, // IN/OUT
440 CFMutableArrayRef outArray) // optional, append here
441 {
442 /* caller must have sorted this out by now */
443 assert((mExternType != kSecItemTypeUnknown) &&
444 (mExternFormat != kSecFormatUnknown));
445
446 /* app could conceivably botch these with crafty PEM hacking */
447 if((mExternal == NULL) || (CFDataGetLength(mExternal) == 0)) {
448 return errSecParam;
449 }
450
451 /* handle the easy ones first */
452 switch(mExternFormat) {
453 case kSecFormatPKCS12:
454 return impExpPkcs12Import(mExternal, flags, keyParams,
455 keyImportState, importKeychain, cspHand, outArray);
456 case kSecFormatX509Cert:
457 case kSecFormatPKCS7:
458 {
459 OSStatus rx = impExpPkcs7Import(mExternal, flags, keyParams, importKeychain,
460 outArray);
461 if (rx == errSecUnknownFormat)
462 {
463 CSSM_DATA cdata;
464 cdata.Data = (uint8 *)CFDataGetBytePtr(mExternal);
465 cdata.Length = (CSSM_SIZE)CFDataGetLength(mExternal);
466 return impExpImportCertCommon(&cdata, importKeychain, outArray);
467 }
468 return rx;
469 }
470 case kSecFormatNetscapeCertSequence:
471 return impExpNetscapeCertImport(mExternal, flags, keyParams, importKeychain,
472 outArray);
473 default:
474 break;
475 }
476
477 if((mExternType == kSecItemTypeCertificate) ||
478 (mExternType == kSecItemTypeAggregate)) {
479 SecImpExpDbg("SecImportRep::importRep screwup");
480 return errSecUnimplemented;
481 }
482
483 /*
484 * All that's left: keys.
485 */
486 if((mExternType == kSecItemTypePrivateKey) && (keyImportState == PIS_NoMore)) {
487 /* multi key import against caller's wishes */
488 return errSecMultiplePrivKeys;
489 }
490
491 /* optionally infer PrintName attribute */
492 switch(mExternFormat) {
493 case kSecFormatSSH:
494 case kSecFormatWrappedSSH:
495 case kSecFormatSSHv2:
496 mPrintName = impExpOpensshInferPrintName(mExternal, mExternType, mExternFormat);
497 break;
498 default:
499 /* use defaults */
500 break;
501 }
502
503 OSStatus ortn = errSecSuccess;
504
505 switch(mExternFormat) {
506 case kSecFormatOpenSSL:
507 case kSecFormatSSH:
508 case kSecFormatSSHv2:
509 case kSecFormatBSAFE:
510 case kSecFormatRawKey:
511 if(mExternal != NULL || CFDataGetLength(mExternal) != 0){
512 ortn = impExpImportRawKey(mExternal, mExternFormat, mExternType,
513 mKeyAlg, importKeychain, cspHand, flags, keyParams, mPrintName, outArray);
514 }
515 else{
516 MacOSError::throwMe(errSecUnsupportedKeySize);
517 }
518 break;
519 case kSecFormatWrappedPKCS8:
520 ortn = impExpPkcs8Import(mExternal, importKeychain, cspHand, flags,
521 keyParams, outArray);
522 break;
523 case kSecFormatWrappedOpenSSL:
524 ortn = importWrappedKeyOpenssl(importKeychain, cspHand, flags, keyParams,
525 outArray);
526 break;
527 case kSecFormatWrappedSSH:
528 ortn = impExpWrappedOpenSSHImport(mExternal, importKeychain, cspHand,
529 flags, keyParams, mPrintName, outArray);
530 break;
531 case kSecFormatWrappedLSH:
532 default:
533 return errSecUnknownFormat;
534 }
535 if((ortn == errSecSuccess) && (keyImportState == PIS_AllowOne)) {
536 /* reached our limit */
537 keyImportState = PIS_NoMore;
538 }
539 return ortn;
540 }
541