--- /dev/null
+/*
+ * Copyright (c) 2004,2011,2013-2014 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ *
+ * SecExternalRep.cpp - private class representing an external representation of
+ * a SecKeychainItemRef, used by SecImportExport.h
+ */
+
+#include "SecExternalRep.h"
+#include "SecImportExportPem.h"
+#include "SecImportExportAgg.h"
+#include "SecImportExportUtils.h"
+#include "SecImportExportPkcs8.h"
+#include "SecImportExportCrypto.h"
+#include "SecImportExportOpenSSH.h"
+#include <security_utilities/errors.h>
+#include <Security/SecBase.h>
+#include <Security/SecKeyPriv.h>
+#include <Security/SecCertificate.h>
+#include <Security/cssmapi.h>
+
+using namespace Security;
+using namespace KeychainCore;
+
+
+#pragma mark --- SecExportRep Subclasses seen only by SecExportRep::vend() ---
+
+namespace SecExport {
+
+class Key : public SecExportRep
+{
+ friend class SecExportRep;
+protected:
+ Key(
+ CFTypeRef kcItemRef);
+ ~Key();
+ OSStatus exportRep(
+ SecExternalFormat format,
+ SecItemImportExportFlags flags,
+ const SecKeyImportExportParameters *keyParams, // optional
+ CFMutableDataRef outData, // data appended here
+ const char **pemHeader); // e.g., "RSA PUBLIC KEY"
+
+private:
+ CSSM_ALGORITHMS mKeyAlg;
+ const CSSM_KEY *mCssmKey;
+};
+
+class Cert : public SecExportRep
+{
+ friend class SecExportRep;
+protected:
+ Cert(
+ CFTypeRef kcItemRef);
+ ~Cert();
+ OSStatus exportRep(
+ SecExternalFormat format,
+ SecItemImportExportFlags flags,
+ const SecKeyImportExportParameters *keyParams, // optional
+ CFMutableDataRef outData, // data appended here
+ const char **pemHeader); // e.g., "CERTIFICATE"
+};
+
+} /* namespace SecExport */
+
+#pragma mark --- SecExportRep: Representation of an internal object on export ---
+
+SecExportRep::SecExportRep(
+ CFTypeRef kcItemRef) :
+ mKcItem((SecKeychainItemRef)kcItemRef),
+ mPemParamLines(NULL)
+{
+ CFRetain(mKcItem);
+}
+
+SecExportRep::~SecExportRep()
+{
+ if(mKcItem) {
+ CFRelease(mKcItem);
+ }
+ if(mPemParamLines) {
+ CFRelease(mPemParamLines);
+ }
+}
+
+SecExportRep::SecExportRep() {
+ MacOSError::throwMe(errSecInvalidItemRef);
+}
+
+/* must be implemented by subclass */
+OSStatus SecExportRep::exportRep(
+ SecExternalFormat format,
+ SecItemImportExportFlags flags,
+ const SecKeyImportExportParameters *keyParams, // optional
+ CFMutableDataRef outData, // data appended here
+ const char **pemHeader) // e.g., "X509 CERTIFICATE"
+{
+ MacOSError::throwMe(errSecInvalidItemRef);
+}
+
+/*
+ * Sole public means of obtaining a SecExportRep object. In fact only instances
+ * of subclasses are vended but caller does not know that.
+ *
+ * Gleans SecExternalItemType from incoming type, throws MacOSError if
+ * incoming type is bogus.
+ *
+ * Vended object holds a reference to kcItem for its lifetime.
+ */
+SecExportRep *SecExportRep::vend(
+ CFTypeRef kcItemRef)
+{
+ CFTypeID itemType = CFGetTypeID(kcItemRef);
+ if(itemType == SecCertificateGetTypeID()) {
+ return new SecExport::Cert(kcItemRef);
+ }
+ else if(itemType == SecKeyGetTypeID()) {
+ return new SecExport::Key(kcItemRef);
+ }
+ else {
+ MacOSError::throwMe(errSecInvalidItemRef);
+ }
+}
+
+#pragma mark --- Key External rep ---
+
+SecExport::Key::Key(
+ CFTypeRef kcItemRef) :
+ SecExportRep(kcItemRef)
+{
+
+ /* figure out if it's public, private, or session */
+ OSStatus ortn;
+ ortn = SecKeyGetCSSMKey((SecKeyRef)kcItemRef, &mCssmKey);
+ if(ortn) {
+ SecImpExpDbg("SecKeyGetCSSMKey failure in SecExportRep::Key()");
+ MacOSError::throwMe(ortn);
+ }
+ switch(mCssmKey->KeyHeader.KeyClass) {
+ case CSSM_KEYCLASS_PUBLIC_KEY:
+ mExternType = kSecItemTypePublicKey;
+ SecImpExpDbg("SecExportRep::Key(): SET_PubKey");
+ break;
+ case CSSM_KEYCLASS_PRIVATE_KEY:
+ mExternType = kSecItemTypePrivateKey;
+ SecImpExpDbg("SecExportRep::Key(): SET_PrivKey");
+ break;
+ case CSSM_KEYCLASS_SESSION_KEY:
+ mExternType = kSecItemTypeSessionKey;
+ SecImpExpDbg("SecExportRep::Key(): SET_SessionKey");
+ break;
+ default:
+ SecImpExpDbg("SecExportRep::Key(): invalid KeyClass (%lu)",
+ (unsigned long)mCssmKey->KeyHeader.KeyClass);
+ MacOSError::throwMe(errSecInvalidItemRef);
+ }
+ mKeyAlg = mCssmKey->KeyHeader.AlgorithmId;
+}
+
+SecExport::Key::~Key()
+{
+ /* nothing for now */
+}
+
+/*
+ * The heart of this class: cook up external representation, appending to
+ * existing CFMutableDataRef.
+ */
+OSStatus SecExport::Key::exportRep(
+ SecExternalFormat format,
+ SecItemImportExportFlags flags,
+ const SecKeyImportExportParameters *keyParams, // optional
+ CFMutableDataRef outData, // data appended here
+ const char **pemHeader)// e.g., "X509 CERTIFICATE"
+{
+ assert(outData != NULL);
+ assert(mKcItem != NULL);
+ assert(mCssmKey != NULL);
+
+ /*
+ * Currently only OpsnSSH formats allow for a DescriptiveData field
+ * in either wrapped or NULL wrap forms. (In OpenSSH parlance this is
+ * the 'comment' field). Infer the DescriptiveData to be embedded
+ * in the exported key from the item's PrintName attribute.
+ */
+ CssmAutoData descrData(Allocator::standard());
+ switch(format) {
+ case kSecFormatSSH:
+ case kSecFormatSSHv2:
+ case kSecFormatWrappedSSH:
+ impExpOpensshInferDescData((SecKeyRef)mKcItem, descrData);
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * Handle wrapped key formats.
+ */
+ switch(format) {
+ case kSecFormatWrappedPKCS8:
+ return impExpPkcs8Export((SecKeyRef)mKcItem, flags, keyParams,
+ outData, pemHeader);
+ case kSecFormatWrappedOpenSSL:
+ return impExpWrappedKeyOpenSslExport((SecKeyRef)mKcItem, flags, keyParams,
+ outData, pemHeader, &mPemParamLines);
+ case kSecFormatWrappedSSH:
+ return impExpWrappedOpenSSHExport((SecKeyRef)mKcItem, flags, keyParams,
+ descrData, outData);
+ case kSecFormatWrappedLSH:
+ return errSecUnsupportedFormat;
+ default:
+ break;
+ }
+
+ /*
+ * Remaining formats just do a NULL key wrap. Figure out the appropriate
+ * CDSA-specific format and wrap parameters.
+ */
+ OSStatus ortn = errSecSuccess;
+ CSSM_KEYBLOB_FORMAT blobForm;
+
+ switch(mExternType) {
+ case kSecItemTypePublicKey:
+ switch(mKeyAlg) {
+ case CSSM_ALGID_RSA:
+ *pemHeader = PEM_STRING_RSA_PUBLIC;
+ break;
+ case CSSM_ALGID_DH:
+ *pemHeader = PEM_STRING_DH_PUBLIC;
+ break;
+ case CSSM_ALGID_DSA:
+ *pemHeader = PEM_STRING_DSA_PUBLIC;
+ break;
+ case CSSM_ALGID_ECDSA:
+ *pemHeader = PEM_STRING_ECDSA_PUBLIC;
+ break;
+ default:
+ SecImpExpDbg("SecExportRep::exportRep unknown public key alg %lu",
+ (unsigned long)mKeyAlg);
+ return errSecUnsupportedFormat;
+ } /* end switch(mKeyAlg) */
+ break; /* from case externType kSecItemTypePublicKey */
+
+ case kSecItemTypePrivateKey:
+ switch(mKeyAlg) {
+ case CSSM_ALGID_RSA:
+ *pemHeader = PEM_STRING_RSA;
+ break;
+ case CSSM_ALGID_DH:
+ *pemHeader = PEM_STRING_DH_PRIVATE;
+ break;
+ case CSSM_ALGID_DSA:
+ *pemHeader = PEM_STRING_DSA;
+ break;
+ case CSSM_ALGID_ECDSA:
+ *pemHeader = PEM_STRING_ECDSA_PRIVATE;
+ break;
+ default:
+ SecImpExpDbg("SecExportRep::exportRep unknown private key alg "
+ "%lu", (unsigned long)mKeyAlg);
+ return errSecUnsupportedFormat;
+ } /* end switch(mKeyAlg) */
+ break; /* from case externType kSecItemTypePrivateKey */
+
+ case kSecItemTypeSessionKey:
+ *pemHeader = PEM_STRING_SESSION;
+ break;
+ default:
+ assert(0);
+ return errSecInvalidItemRef;
+ } /* switch(mExternType) */
+
+ /* Map our external params to CDSA blob format */
+ CSSM_KEYCLASS keyClass;
+ ortn = impExpKeyForm(format, mExternType, mKeyAlg, &blobForm, &keyClass);
+ if(ortn) {
+ return ortn;
+ }
+
+ /* Specify format of null-wrapped key */
+ CSSM_ATTRIBUTE_TYPE formatAttrType = CSSM_ATTRIBUTE_NONE;
+ switch(mExternType) {
+ case kSecItemTypePrivateKey:
+ formatAttrType = CSSM_ATTRIBUTE_PRIVATE_KEY_FORMAT;
+ break;
+ case kSecItemTypePublicKey:
+ formatAttrType = CSSM_ATTRIBUTE_PUBLIC_KEY_FORMAT;
+ break;
+ /* symmetric key doesn't have a format */
+ default:
+ break;
+ }
+
+ CSSM_CSP_HANDLE cspHand;
+ ortn = SecKeyGetCSPHandle((SecKeyRef)mKcItem, &cspHand);
+ if(ortn) {
+ SecImpExpDbg("SecExportRep::exportRep SecKeyGetCSPHandle error");
+ return ortn;
+ }
+
+ /* perform the NULL wrap --> wrapped Key */
+ CSSM_KEY wrappedKey;
+ memset(&wrappedKey, 0, sizeof(wrappedKey));
+ const CSSM_DATA &dd = descrData;
+ ortn = impExpExportKeyCommon(cspHand,
+ (SecKeyRef)mKcItem,
+ NULL, // wrappingKey not used for NULL
+ &wrappedKey, // destination
+ CSSM_ALGID_NONE,
+ CSSM_ALGMODE_NONE,
+ CSSM_PADDING_NONE,
+ CSSM_KEYBLOB_WRAPPED_FORMAT_NONE,
+ formatAttrType,
+ blobForm,
+ &dd, // descriptiveData
+ NULL); // IV
+
+ if(ortn == CSSM_OK) {
+ /* pass key data back to caller */
+ CFDataAppendBytes(outData, wrappedKey.KeyData.Data, wrappedKey.KeyData.Length);
+ }
+ CSSM_FreeKey(cspHand, NULL, &wrappedKey, CSSM_FALSE);
+ return ortn;
+}
+
+#pragma mark --- Certificate External rep ---
+
+SecExport::Cert::Cert(
+ CFTypeRef kcItemRef) :
+ SecExportRep(kcItemRef)
+{
+ mExternType = kSecItemTypeCertificate;
+}
+
+SecExport::Cert::~Cert()
+{
+ /* nothing for now */
+}
+
+/*
+ * The heart of this class: cook up external representation, appending to
+ * existing CFMutableDataRef.
+ */
+OSStatus SecExport::Cert::exportRep(
+ SecExternalFormat format,
+ SecItemImportExportFlags flags,
+ const SecKeyImportExportParameters *keyParams, // optional
+ CFMutableDataRef outData, // data appended here
+ const char **pemHeader)// e.g., "X509 CERTIFICATE"
+{
+ assert(outData != NULL);
+ assert(mKcItem != NULL);
+
+ switch(format) {
+ case kSecFormatUnknown: // default
+ case kSecFormatX509Cert: // currently, only supported format
+ break;
+ default:
+ SecImpExpDbg("SecExportRep::exportRep unsupported format for cert");
+ return errSecUnsupportedFormat;
+ }
+
+ CFDataRef cdata = SecCertificateCopyData((SecCertificateRef)mKcItem);
+ if(!cdata) {
+ SecImpExpDbg("SecExportRep::exportRep SecCertificateGetData error");
+ return errSecUnsupportedFormat;
+ }
+
+ CFDataAppendBytes(outData, CFDataGetBytePtr(cdata), CFDataGetLength(cdata));
+ CFRelease(cdata);
+ *pemHeader = PEM_STRING_X509;
+ return errSecSuccess;
+}
+
+#pragma mark --- SecImportRep: Representation of an external object on import ---
+
+/*
+ * for import, when we have the external representation.
+ * All arguments except for the CFDataRef are optional (i.e., "unknown"
+ * is legal).
+ */
+SecImportRep::SecImportRep(
+ CFDataRef external,
+ SecExternalItemType externType, // may be unknown
+ SecExternalFormat externFormat, // may be unknown
+ CSSM_ALGORITHMS keyAlg, // may be unknown, CSSM_ALGID_NONE
+ CFArrayRef pemParamLines /* = NULL */ ) :
+ mPrintName(NULL),
+ mExternal(external),
+ mExternType(externType),
+ mExternFormat(externFormat),
+ mKeyAlg(keyAlg),
+ mPemParamLines(pemParamLines)
+{
+ CFRetain(mExternal);
+}
+
+SecImportRep::~SecImportRep()
+{
+ if(mPrintName) {
+ free(mPrintName);
+ }
+ if(mExternal) {
+ CFRelease(mExternal);
+ }
+ if(mPemParamLines) {
+ CFRelease(mPemParamLines);
+ }
+}
+
+/*
+ * Convert to one or more SecItemRefs and/or import to keychain.
+ * The cspHand handle MUST be a CSPDL handle, not a raw CSP handle.
+ */
+OSStatus SecImportRep::importRep(
+ SecKeychainRef importKeychain, // optional
+ CSSM_CSP_HANDLE cspHand, // required
+ SecItemImportExportFlags flags,
+ const SecKeyImportExportParameters *keyParams, // optional
+ ImpPrivKeyImportState &keyImportState, // IN/OUT
+ CFMutableArrayRef outArray) // optional, append here
+{
+ /* caller must have sorted this out by now */
+ assert((mExternType != kSecItemTypeUnknown) &&
+ (mExternFormat != kSecFormatUnknown));
+
+ /* app could conceivably botch these with crafty PEM hacking */
+ if((mExternal == NULL) || (CFDataGetLength(mExternal) == 0)) {
+ return errSecParam;
+ }
+
+ /* handle the easy ones first */
+ switch(mExternFormat) {
+ case kSecFormatPKCS12:
+ return impExpPkcs12Import(mExternal, flags, keyParams,
+ keyImportState, importKeychain, cspHand, outArray);
+ case kSecFormatX509Cert:
+ case kSecFormatPKCS7:
+ {
+ OSStatus rx = impExpPkcs7Import(mExternal, flags, keyParams, importKeychain,
+ outArray);
+ if (rx == errSecUnknownFormat)
+ {
+ CSSM_DATA cdata;
+ cdata.Data = (uint8 *)CFDataGetBytePtr(mExternal);
+ cdata.Length = (CSSM_SIZE)CFDataGetLength(mExternal);
+ return impExpImportCertCommon(&cdata, importKeychain, outArray);
+ }
+ return rx;
+ }
+ case kSecFormatNetscapeCertSequence:
+ return impExpNetscapeCertImport(mExternal, flags, keyParams, importKeychain,
+ outArray);
+ default:
+ break;
+ }
+
+ if((mExternType == kSecItemTypeCertificate) ||
+ (mExternType == kSecItemTypeAggregate)) {
+ SecImpExpDbg("SecImportRep::importRep screwup");
+ return errSecUnimplemented;
+ }
+
+ /*
+ * All that's left: keys.
+ */
+ if((mExternType == kSecItemTypePrivateKey) && (keyImportState == PIS_NoMore)) {
+ /* multi key import against caller's wishes */
+ return errSecMultiplePrivKeys;
+ }
+
+ /* optionally infer PrintName attribute */
+ switch(mExternFormat) {
+ case kSecFormatSSH:
+ case kSecFormatWrappedSSH:
+ case kSecFormatSSHv2:
+ mPrintName = impExpOpensshInferPrintName(mExternal, mExternType, mExternFormat);
+ break;
+ default:
+ /* use defaults */
+ break;
+ }
+
+ OSStatus ortn = errSecSuccess;
+
+ switch(mExternFormat) {
+ case kSecFormatOpenSSL:
+ case kSecFormatSSH:
+ case kSecFormatSSHv2:
+ case kSecFormatBSAFE:
+ case kSecFormatRawKey:
+ if(mExternal != NULL || CFDataGetLength(mExternal) != 0){
+ ortn = impExpImportRawKey(mExternal, mExternFormat, mExternType,
+ mKeyAlg, importKeychain, cspHand, flags, keyParams, mPrintName, outArray);
+ }
+ else{
+ MacOSError::throwMe(errSecUnsupportedKeySize);
+ }
+ break;
+ case kSecFormatWrappedPKCS8:
+ ortn = impExpPkcs8Import(mExternal, importKeychain, cspHand, flags,
+ keyParams, outArray);
+ break;
+ case kSecFormatWrappedOpenSSL:
+ ortn = importWrappedKeyOpenssl(importKeychain, cspHand, flags, keyParams,
+ outArray);
+ break;
+ case kSecFormatWrappedSSH:
+ ortn = impExpWrappedOpenSSHImport(mExternal, importKeychain, cspHand,
+ flags, keyParams, mPrintName, outArray);
+ break;
+ case kSecFormatWrappedLSH:
+ default:
+ return errSecUnknownFormat;
+ }
+ if((ortn == errSecSuccess) && (keyImportState == PIS_AllowOne)) {
+ /* reached our limit */
+ keyImportState = PIS_NoMore;
+ }
+ return ortn;
+}
+