X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_keychain/lib/SecExternalRep.cpp?ds=sidebyside diff --git a/Security/libsecurity_keychain/lib/SecExternalRep.cpp b/Security/libsecurity_keychain/lib/SecExternalRep.cpp new file mode 100644 index 00000000..14784c12 --- /dev/null +++ b/Security/libsecurity_keychain/lib/SecExternalRep.cpp @@ -0,0 +1,541 @@ +/* + * 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 +#include +#include +#include +#include + +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; +} +