X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5dd5f9ec28f304ca377c42fd7f711d6cf12b90e1..5c19dc3ae3bd8e40a9c028b0deddd50ff337692c:/Security/libsecurity_keychain/lib/SecImportExportOpenSSH.cpp?ds=inline diff --git a/Security/libsecurity_keychain/lib/SecImportExportOpenSSH.cpp b/Security/libsecurity_keychain/lib/SecImportExportOpenSSH.cpp deleted file mode 100644 index d066c49c..00000000 --- a/Security/libsecurity_keychain/lib/SecImportExportOpenSSH.cpp +++ /dev/null @@ -1,633 +0,0 @@ -/* - * Copyright (c) 2006,2011-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@ - */ - - -/* - * opensshCoding.cpp - Encoding and decoding of OpenSSH format public keys. - * - */ - -#include "SecImportExportOpenSSH.h" -#include "SecImportExportUtils.h" -#include "SecImportExportCrypto.h" -#include -#include /* for CC_MD5_DIGEST_LENGTH */ -#include -#include - -#define SecSSHDbg(args...) secdebug("openssh", ## args) - -#define SSHv2_PUB_KEY_NAME "OpenSSHv2 Public Key" -#define SSHv1_PUB_KEY_NAME "OpenSSHv1 Public Key" -#define SSHv1_PRIV_KEY_NAME "OpenSSHv1 Private Key" - -#pragma mark --- Utility functions --- - -/* skip whitespace */ -static void skipWhite( - const unsigned char *&cp, - unsigned &bytesLeft) -{ - while(bytesLeft != 0) { - if(isspace((int)(*cp))) { - cp++; - bytesLeft--; - } - else { - return; - } - } -} - -/* find next whitespace or EOF - if EOF, rtn pointer points to one past EOF */ -static const unsigned char *findNextWhite( - const unsigned char *cp, - unsigned &bytesLeft) -{ - while(bytesLeft != 0) { - if(isspace((int)(*cp))) { - return cp; - } - cp++; - bytesLeft--; - } - return cp; -} - -/* obtain comment as the n'th whitespace-delimited field */ -static char *commentAsNthField( - const unsigned char *key, - unsigned keyLen, - unsigned n) - -{ - unsigned dex; - - skipWhite(key, keyLen); - if(keyLen == 0) { - return NULL; - } - for(dex=0; dex<(n-1); dex++) { - key = findNextWhite(key, keyLen); - if(keyLen == 0) { - return NULL; - } - skipWhite(key, keyLen); - if(keyLen == 0) { - return NULL; - } - } - - /* cp points to start of nth field */ - char *rtnStr = (char *)malloc(keyLen + 1); - memmove(rtnStr, key, keyLen); - if(rtnStr[keyLen - 1] == '\n') { - /* normal terminator - snip it off */ - rtnStr[keyLen - 1] = '\0'; - } - else { - rtnStr[keyLen] = '\0'; - } - return rtnStr; - -} - -static uint32_t readUint32( - const unsigned char *&cp, // IN/OUT - unsigned &len) // IN/OUT -{ - uint32_t r = 0; - - for(unsigned dex=0; dex len) { - cp += len; - len = 0; - return; - } - cp += numBytes; - len -= numBytes; -} - -static char *genPrintName( - const char *header, // e.g. SSHv2_PUB_KEY_NAME - const char *comment) // optional, from key -{ - size_t totalLen = strlen(header) + 1; - if(comment) { - /* append ": " */ - totalLen += strlen(comment); - totalLen += 2; - } - char *rtnStr = (char *)malloc(totalLen); - if(comment) { - snprintf(rtnStr, totalLen, "%s: %s", header, comment); - } - else { - strcpy(rtnStr, header); - } - return rtnStr; -} - -#pragma mark --- Infer PrintName attribute from raw keys --- - -/* obtain comment from OpenSSHv2 public key */ -static char *opensshV2PubComment( - const unsigned char *key, - unsigned keyLen) -{ - /* - * The format here is - * header - * - * keyblob - * - * optional comment - * \n - */ - char *comment = commentAsNthField(key, keyLen, 3); - char *rtnStr = genPrintName(SSHv2_PUB_KEY_NAME, comment); - if(comment) { - free(comment); - } - return rtnStr; -} - -/* obtain comment from OpenSSHv1 public key */ -static char *opensshV1PubComment( - const unsigned char *key, - unsigned keyLen) -{ - /* - * Format: - * numbits - * - * e (bignum in decimal) - * - * n (bignum in decimal) - * - * optional comment - * \n - */ - char *comment = commentAsNthField(key, keyLen, 4); - char *rtnStr = genPrintName(SSHv1_PUB_KEY_NAME, comment); - if(comment) { - free(comment); - } - return rtnStr; -} - -static const char *authfile_id_string = "SSH PRIVATE KEY FILE FORMAT 1.1\n"; - -/* obtain comment from OpenSSHv1 private key, wrapped or clear */ -static char *opensshV1PrivComment( - const unsigned char *key, - unsigned keyLen) -{ - /* - * Format: - * "SSH PRIVATE KEY FILE FORMAT 1.1\n" - * 1 byte cipherSpec - * 4 byte spares - * 4 bytes numBits - * bignum n - * bignum e - * 4 byte comment length - * comment - * private key components, possibly encrypted - * - * A bignum is encoded like so: - * 2 bytes numBits - * (numBits + 7)/8 bytes of data - */ - /* length: ID string, NULL, Cipher, 4-byte spare */ - size_t len = strlen(authfile_id_string); - if(keyLen < (len + 6)) { - return NULL; - } - if(memcmp(authfile_id_string, key, len)) { - return NULL; - } - key += (len + 6); - keyLen -= (len + 6); - - /* key points to numBits */ - if(keyLen < 4) { - return NULL; - } - key += 4; - keyLen -= 4; - - /* key points to n */ - skipBigNum(key, keyLen); - if(keyLen == 0) { - return NULL; - } - skipBigNum(key, keyLen); - if(keyLen == 0) { - return NULL; - } - - char *comment = NULL; - uint32 commentLen = readUint32(key, keyLen); - if((commentLen != 0) && (commentLen <= keyLen)) { - comment = (char *)malloc(commentLen + 1); - memmove(comment, key, commentLen); - comment[commentLen] = '\0'; - } - - char *rtnStr = genPrintName(SSHv1_PRIV_KEY_NAME, comment); - if(comment) { - free(comment); - } - return rtnStr; -} - -/* - * Infer PrintName attribute from raw key's 'comment' field. - * Returned string is mallocd and must be freed by caller. - */ -char *impExpOpensshInferPrintName( - CFDataRef external, - SecExternalItemType externType, - SecExternalFormat externFormat) -{ - const unsigned char *key = (const unsigned char *)CFDataGetBytePtr(external); - unsigned keyLen = (unsigned)CFDataGetLength(external); - switch(externType) { - case kSecItemTypePublicKey: - switch(externFormat) { - case kSecFormatSSH: - return opensshV1PubComment(key, keyLen); - case kSecFormatSSHv2: - return opensshV2PubComment(key, keyLen); - default: - /* impossible, right? */ - break; - } - break; - case kSecItemTypePrivateKey: - switch(externFormat) { - case kSecFormatSSH: - case kSecFormatWrappedSSH: - return opensshV1PrivComment(key, keyLen); - default: - break; - } - break; - default: - break; - } - return NULL; -} - -#pragma mark --- Infer DescriptiveData from PrintName --- - -/* - * Infer DescriptiveData (i.e., comment) from a SecKeyRef's PrintName - * attribute. - */ -void impExpOpensshInferDescData( - SecKeyRef keyRef, - CssmOwnedData &descData) -{ - OSStatus ortn; - SecKeychainAttributeInfo attrInfo; - SecKeychainAttrType attrType = kSecKeyPrintName; - attrInfo.count = 1; - attrInfo.tag = &attrType; - attrInfo.format = NULL; - SecKeychainAttributeList *attrList = NULL; - - ortn = SecKeychainItemCopyAttributesAndData( - (SecKeychainItemRef)keyRef, - &attrInfo, - NULL, // itemClass - &attrList, - NULL, // don't need the data - NULL); - if(ortn) { - SecSSHDbg("SecKeychainItemCopyAttributesAndData returned %ld", (unsigned long)ortn); - return; - } - /* subsequent errors to errOut: */ - SecKeychainAttribute *attr = attrList->attr; - - /* - * On a previous import, we would have set this to something like - * "OpenSSHv2 Public Key: comment". - * We want to strip off everything up to the actual comment. - */ - unsigned toStrip = 0; - - /* min length of attribute value for this code to be meaningful */ - unsigned len = strlen(SSHv2_PUB_KEY_NAME) + 1; - char *printNameStr = NULL; - if(len < attr->length) { - printNameStr = (char *)malloc(attr->length + 1); - memmove(printNameStr, attr->data, attr->length); - printNameStr[attr->length] = '\0'; - if(strstr(printNameStr, SSHv2_PUB_KEY_NAME) == printNameStr) { - toStrip = strlen(SSHv2_PUB_KEY_NAME); - } - else if(strstr(printNameStr, SSHv1_PUB_KEY_NAME) == printNameStr) { - toStrip = strlen(SSHv1_PUB_KEY_NAME); - } - else if(strstr(printNameStr, SSHv1_PRIV_KEY_NAME) == printNameStr) { - toStrip = strlen(SSHv1_PRIV_KEY_NAME); - } - if(toStrip) { - /* only strip if we have ": " after toStrip bytes */ - if((printNameStr[toStrip] == ':') && (printNameStr[toStrip+1] == ' ')) { - toStrip += 2; - } - } - } - if(printNameStr) { - free(printNameStr); - } - len = attr->length; - - unsigned char *attrVal; - - if(len < toStrip) { - SecSSHDbg("impExpOpensshInferDescData: string parse screwup"); - goto errOut; - } - if(len > toStrip) { - /* Normal case of stripping off leading header */ - len -= toStrip; - } - else { - /* - * If equal, then the attr value *is* "OpenSSHv2 Public Key: " with - * no comment. Not sure how that could happen, but let's be careful. - */ - toStrip = 0; - } - - attrVal = ((unsigned char *)attr->data) + toStrip; - descData.copy(attrVal, len); -errOut: - SecKeychainItemFreeAttributesAndData(attrList, NULL); - return; -} - -#pragma mark --- Derive SSHv1 wrap/unwrap key --- - -/* - * Common code to derive a wrap/unwrap key for OpenSSHv1. - * Caller must CSSM_FreeKey when done. - */ -static CSSM_RETURN openSSHv1DeriveKey( - CSSM_CSP_HANDLE cspHand, - const SecKeyImportExportParameters *keyParams, // required - impExpVerifyPhrase verifyPhrase, // for secure passphrase - CSSM_KEY_PTR symKey) // RETURNED -{ - CSSM_KEY *passKey = NULL; - CFDataRef cfPhrase = NULL; - CSSM_RETURN crtn; - OSStatus ortn; - CSSM_DATA dummyLabel; - uint32 keyAttr; - CSSM_CC_HANDLE ccHand = 0; - CSSM_ACCESS_CREDENTIALS creds; - CSSM_CRYPTO_DATA seed; - CSSM_DATA nullParam = {0, NULL}; - - memset(symKey, 0, sizeof(CSSM_KEY)); - - /* passphrase or passkey? */ - ortn = impExpPassphraseCommon(keyParams, cspHand, SPF_Data, verifyPhrase, - (CFTypeRef *)&cfPhrase, &passKey); - if(ortn) { - return ortn; - } - /* subsequent errors to errOut: */ - - memset(&seed, 0, sizeof(seed)); - if(cfPhrase != NULL) { - /* TBD - caller-supplied empty passphrase means "export in the clear" */ - size_t len = CFDataGetLength(cfPhrase); - seed.Param.Data = (uint8 *)malloc(len); - seed.Param.Length = len; - memmove(seed.Param.Data, CFDataGetBytePtr(cfPhrase), len); - CFRelease(cfPhrase); - } - - memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS)); - crtn = CSSM_CSP_CreateDeriveKeyContext(cspHand, - CSSM_ALGID_OPENSSH1, - CSSM_ALGID_OPENSSH1, - CC_MD5_DIGEST_LENGTH * 8, - &creds, - passKey, // BaseKey - 0, // iterationCount - NULL, // salt - &seed, - &ccHand); - if(crtn) { - SecSSHDbg("openSSHv1DeriveKey CSSM_CSP_CreateDeriveKeyContext failure"); - goto errOut; - } - - /* not extractable even for the short time this key lives */ - keyAttr = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_SENSITIVE; - dummyLabel.Data = (uint8 *)"temp unwrap key"; - dummyLabel.Length = strlen((char *)dummyLabel.Data); - - crtn = CSSM_DeriveKey(ccHand, - &nullParam, - CSSM_KEYUSE_ANY, - keyAttr, - &dummyLabel, - NULL, // cred and acl - symKey); - if(crtn) { - SecSSHDbg("openSSHv1DeriveKey CSSM_DeriveKey failure"); - } -errOut: - if(ccHand != 0) { - CSSM_DeleteContext(ccHand); - } - if(passKey != NULL) { - CSSM_FreeKey(cspHand, NULL, passKey, CSSM_FALSE); - free(passKey); - } - if(seed.Param.Data) { - memset(seed.Param.Data, 0, seed.Param.Length); - free(seed.Param.Data); - } - return crtn; -} - -#pragma mark -- OpenSSHv1 Wrap/Unwrap --- - -/* - * If cspHand is provided instead of importKeychain, the CSP - * handle MUST be for the CSPDL, not for the raw CSP. - */ -OSStatus impExpWrappedOpenSSHImport( - CFDataRef inData, - SecKeychainRef importKeychain, // optional - CSSM_CSP_HANDLE cspHand, // required - SecItemImportExportFlags flags, - const SecKeyImportExportParameters *keyParams, // optional - const char *printName, - CFMutableArrayRef outArray) // optional, append here -{ - OSStatus ortn; - impExpKeyUnwrapParams unwrapParams; - - assert(cspHand != 0); - if(keyParams == NULL) { - return errSecParam; - } - memset(&unwrapParams, 0, sizeof(unwrapParams)); - - /* derive unwrapping key */ - CSSM_KEY unwrappingKey; - - ortn = openSSHv1DeriveKey(cspHand, keyParams, VP_Import, &unwrappingKey); - if(ortn) { - return ortn; - } - - /* set up key to unwrap */ - CSSM_KEY wrappedKey; - CSSM_KEYHEADER &hdr = wrappedKey.KeyHeader; - memset(&wrappedKey, 0, sizeof(CSSM_KEY)); - hdr.HeaderVersion = CSSM_KEYHEADER_VERSION; - /* CspId : don't care */ - hdr.BlobType = CSSM_KEYBLOB_WRAPPED; - hdr.Format = CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1; - hdr.AlgorithmId = CSSM_ALGID_RSA; /* the oly algorithm supported in SSHv1 */ - hdr.KeyClass = CSSM_KEYCLASS_PRIVATE_KEY; - /* LogicalKeySizeInBits : calculated by CSP during unwrap */ - hdr.KeyAttr = CSSM_KEYATTR_EXTRACTABLE; - hdr.KeyUsage = CSSM_KEYUSE_ANY; - - wrappedKey.KeyData.Data = (uint8 *)CFDataGetBytePtr(inData); - wrappedKey.KeyData.Length = CFDataGetLength(inData); - - unwrapParams.unwrappingKey = &unwrappingKey; - unwrapParams.encrAlg = CSSM_ALGID_OPENSSH1; - - /* GO */ - ortn = impExpImportKeyCommon(&wrappedKey, importKeychain, cspHand, - flags, keyParams, &unwrapParams, printName, outArray); - - if(unwrappingKey.KeyData.Data != NULL) { - CSSM_FreeKey(cspHand, NULL, &unwrappingKey, CSSM_FALSE); - } - return ortn; -} - -OSStatus impExpWrappedOpenSSHExport( - SecKeyRef secKey, - SecItemImportExportFlags flags, - const SecKeyImportExportParameters *keyParams, // optional - const CssmData &descData, - CFMutableDataRef outData) // output appended here -{ - CSSM_CSP_HANDLE cspdlHand = 0; - OSStatus ortn; - bool releaseCspHand = false; - CSSM_RETURN crtn; - - if(keyParams == NULL) { - return errSecParam; - } - - /* we need a CSPDL handle - try to get it from the key */ - ortn = SecKeyGetCSPHandle(secKey, &cspdlHand); - if(ortn) { - cspdlHand = cuCspStartup(CSSM_FALSE); - if(cspdlHand == 0) { - return CSSMERR_CSSM_ADDIN_LOAD_FAILED; - } - releaseCspHand = true; - } - /* subsequent errors to errOut: */ - - /* derive wrapping key */ - CSSM_KEY wrappingKey; - crtn = openSSHv1DeriveKey(cspdlHand, keyParams, VP_Export, &wrappingKey); - if(crtn) { - goto errOut; - } - - /* GO */ - CSSM_KEY wrappedKey; - memset(&wrappedKey, 0, sizeof(CSSM_KEY)); - - crtn = impExpExportKeyCommon(cspdlHand, secKey, &wrappingKey, &wrappedKey, - CSSM_ALGID_OPENSSH1, CSSM_ALGMODE_NONE, CSSM_PADDING_NONE, - CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1, CSSM_ATTRIBUTE_NONE, CSSM_KEYBLOB_RAW_FORMAT_NONE, - &descData, - NULL); // IV - if(crtn) { - goto errOut; - } - - /* the wrappedKey's KeyData is out output */ - CFDataAppendBytes(outData, wrappedKey.KeyData.Data, wrappedKey.KeyData.Length); - CSSM_FreeKey(cspdlHand, NULL, &wrappedKey, CSSM_FALSE); - -errOut: - if(releaseCspHand) { - cuCspDetachUnload(cspdlHand, CSSM_FALSE); - } - return crtn; - -}