]> git.saurik.com Git - apple/security.git/blobdiff - Security/libsecurity_keychain/lib/SecImportExportPem.cpp
Security-57336.1.9.tar.gz
[apple/security.git] / Security / libsecurity_keychain / lib / SecImportExportPem.cpp
diff --git a/Security/libsecurity_keychain/lib/SecImportExportPem.cpp b/Security/libsecurity_keychain/lib/SecImportExportPem.cpp
deleted file mode 100644 (file)
index df67556..0000000
+++ /dev/null
@@ -1,504 +0,0 @@
-/*
- * Copyright (c) 2004,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@
- *
- * SecImportExportPem.cpp - private PEM routines for SecImportExport
- */
-
-#include "SecImportExportPem.h"
-#include "SecExternalRep.h"
-#include "SecImportExportUtils.h"
-#include <security_cdsa_utils/cuEnc64.h>
-#include <security_cdsa_utils/cuPem.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-
-/* 
- * Text parsing routines. 
- *
- * Search incoming text for specified string. Does not assume inText is
- * NULL terminated. Returns pointer to start of found string in inText.
- */
-static const char *findStr(
-       const char *inText,
-       unsigned inTextLen,
-       const char *str)                                // NULL terminated - search for this
-{
-       /* probably not the hottest string search algorithm... */
-       const char *cp;
-       unsigned srchStrLen = (unsigned)strlen(str);
-       char c = str[0];
-       
-       /* last char * we can search in inText for start of str */
-       const char *endCp = inText + inTextLen - srchStrLen;
-       
-       for(cp=inText; cp<=endCp; cp++) {
-               if(*cp == c) {
-                       if(!memcmp(cp, str, srchStrLen)) {
-                               return cp;
-                       }
-               }
-       }
-       return NULL;
-}
-
-/*
- * Obtain one line from current text. Returns a mallocd, NULL-terminated string
- * which caller must free(). Also returns the number of chars consumed including
- * the returned chars PLUS EOL terminators (\n and/or \r).
- *
- * ALWAYS returns a mallocd string if there is ANY data remaining per the 
- * incoming inTextLen. Returns NULL if inTextLen is zero.
- */
-static const char *getLine(
-       const char *inText,
-       unsigned inTextLen,                     // RETURNED
-       unsigned *consumed)                     // RETURNED
-       
-{
-       *consumed = 0;
-       const char *cp = inText;
-       const char *newline = NULL;             // if we found a newline, this points to the first one
-       
-       while(inTextLen) {
-               char c = *cp;
-               if((c == '\r') || (c == '\n')) {
-                       if(newline == NULL) {
-                               /* first newline */
-                               newline = cp;
-                       }
-               }
-               else if(newline != NULL) {
-                       /* non newline after newline, done */
-                       break;
-               }
-               (*consumed)++;
-               inTextLen--;
-               cp++;
-       }
-       unsigned linelen;
-       if(newline) {
-               linelen = (unsigned)(newline - inText);
-       }
-       else {
-               linelen = *consumed;
-       }
-       char *rtn = (char *)malloc(linelen + 1);
-       memmove(rtn, inText, linelen);
-       rtn[linelen] = 0;
-       return rtn;
-}
-
-/*
- * Table to facilitate conversion of known PEM header strings to 
- * the things we know about.
- */
-typedef struct {
-       const char                      *pemStr;                        // e.g. PEM_STRING_X509, "CERTIFICATE"
-       SecExternalItemType itemType;
-       SecExternalFormat   format;
-       CSSM_ALGORITHMS         keyAlg;
-} PemHeader;
-
-#define NOALG   CSSM_ALGID_NONE
-
-static const PemHeader PemHeaders[] = 
-{
-       /* from openssl/pem.h standard header */
-       { PEM_STRING_X509_OLD, kSecItemTypeCertificate, kSecFormatX509Cert, NOALG},
-       { PEM_STRING_X509,  kSecItemTypeCertificate, kSecFormatX509Cert, NOALG },
-       { PEM_STRING_EVP_PKEY, kSecItemTypePrivateKey, kSecFormatOpenSSL, NOALG},
-       { PEM_STRING_PUBLIC, kSecItemTypePublicKey, kSecFormatOpenSSL, NOALG },
-       { PEM_STRING_RSA, kSecItemTypePrivateKey, kSecFormatOpenSSL, CSSM_ALGID_RSA },
-       { PEM_STRING_RSA_PUBLIC, kSecItemTypePublicKey, kSecFormatOpenSSL, CSSM_ALGID_RSA },
-       { PEM_STRING_DSA, kSecItemTypePrivateKey, kSecFormatOpenSSL, CSSM_ALGID_DSA },
-       { PEM_STRING_DSA_PUBLIC, kSecItemTypePublicKey, kSecFormatOpenSSL, CSSM_ALGID_DSA },
-       { PEM_STRING_PKCS7, kSecItemTypeAggregate, kSecFormatPKCS7, NOALG },
-       { PEM_STRING_PKCS8, kSecItemTypePrivateKey, kSecFormatWrappedPKCS8, NOALG },
-       { PEM_STRING_PKCS8INF, kSecItemTypePrivateKey, kSecFormatUnknown, NOALG },
-       /* we define these  */
-       { PEM_STRING_DH_PUBLIC, kSecItemTypePublicKey, kSecFormatOpenSSL, CSSM_ALGID_DH },
-       { PEM_STRING_DH_PRIVATE, kSecItemTypePrivateKey, kSecFormatOpenSSL, CSSM_ALGID_DH },
-       { PEM_STRING_PKCS12, kSecItemTypeAggregate, kSecFormatPKCS12, NOALG },
-       { PEM_STRING_SESSION, kSecItemTypeSessionKey, kSecFormatRawKey, NOALG },
-       { PEM_STRING_ECDSA_PUBLIC, kSecItemTypePublicKey, kSecFormatOpenSSL, CSSM_ALGID_ECDSA },
-       { PEM_STRING_ECDSA_PRIVATE, kSecItemTypePrivateKey, kSecFormatOpenSSL, CSSM_ALGID_ECDSA }
-};
-#define NUM_PEM_HEADERS (sizeof(PemHeaders) / sizeof(PemHeader))
-
-/*
- * PEM decode incoming data which we've previously determined to contain
- * exactly one reasonably well formed PEM blob (it has no more than one
- * START and END line - though it may have none - and is all ASCII).
- *
- * Returned SecImportRep may or may not have a known type and format and 
- * (if it is a key) algorithm. 
- */
-static OSStatus impExpImportSinglePEM(
-       const char                      *currCp,
-       unsigned                        lenToGo,
-       CFMutableArrayRef       importReps)             // output appended here
-{
-       unsigned consumed;
-       const char *currLine = NULL;            // mallocd by getLine()
-       const char *lastCp = currCp;
-       CFMutableArrayRef pemParamLines = NULL;
-       OSStatus ortn = errSecSuccess;
-       CFDataRef cdata = NULL;
-       Security::KeychainCore::SecImportRep *rep = NULL;
-       const char *start64;
-       unsigned base64Len;     
-       const char *end64;
-       unsigned char *decData;
-       unsigned decDataLen;
-       
-       /* we try to glean these from the header, but it's not fatal if we can not */
-       SecExternalFormat format = kSecFormatUnknown;
-       SecExternalItemType itemType = kSecItemTypeUnknown;
-       CSSM_ALGORITHMS keyAlg = CSSM_ALGID_NONE;
-       
-       /* search to START line, parse it to get type/format/alg */
-       const char *startLine = findStr(currCp, lenToGo, "-----BEGIN");
-       if(startLine != NULL) {
-               /* possibly skip over leading garbage */
-               consumed = (unsigned)(startLine - currCp);
-               lenToGo -= consumed;
-               currCp = startLine;
-               
-               /* get C string of START line */
-               currLine = getLine(startLine, lenToGo, &consumed);
-               if(currLine == NULL) {
-                       /* somehow got here with no data */
-                       assert(lenToGo == 0);
-                       SecImpInferDbg("impExpImportSinglePEM empty data");
-                       ortn = errSecUnsupportedFormat;
-                       goto errOut;
-               }
-               assert(consumed <= lenToGo);
-               currCp  += consumed;
-               lenToGo -= consumed;
-
-               /*
-                * Search currLine for known PEM header strings.
-                * It is not an error if we don't recognize this
-                * header.
-                */
-               for(unsigned dex=0; dex<NUM_PEM_HEADERS; dex++) {
-                       const PemHeader *ph = &PemHeaders[dex];
-                       if(!strstr(currLine, ph->pemStr)) {
-                               continue;
-                       }
-                       /* found one! */
-                       format   = ph->format;
-                       itemType = ph->itemType;
-                       keyAlg   = ph->keyAlg;
-                       break;
-               }
-               
-               free((void *)currLine);
-       }
-
-       /* 
-        * Skip empty lines. Save all lines containing ':' (used by openssl 
-        * to specify key wrapping parameters). These will be saved in 
-        * outgoing SecImportReps' pemParamLines.
-        */
-       for( ; ; ) {
-               currLine = getLine(currCp, lenToGo, &consumed);
-               if(currLine == NULL || currCp == lastCp) {
-                       /* out of data (unable to advance to next line) */
-                       SecImpInferDbg("impExpImportSinglePEM out of data");
-                       if (currLine) free((void *)currLine);
-                       ortn = errSecUnsupportedFormat;
-                       goto errOut;
-               }
-               lastCp = currCp;
-               
-               bool skipThis = false;
-               unsigned lineLen = (unsigned)strlen(currLine);
-               if(lineLen == 0) {
-                       /* empty line */
-                       skipThis = true;
-               }
-               if(strchr(currLine, ':')) {
-                       /* 
-                        * Save this PEM header info. Used for traditional openssl
-                        * wrapped keys to indicate IV.
-                        */
-                       SecImpInferDbg("import PEM: param line %s", currLine);
-                       CFStringRef cfStr = CFStringCreateWithCString(NULL, currLine,
-                               kCFStringEncodingASCII);
-                       if(pemParamLines == NULL) {
-                               /* first param line */
-                               pemParamLines = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
-                               
-                               /* 
-                                * If it says "ENCRYPTED" and this is a private key,
-                                * flag the fact that it's wrapped in openssl format
-                                */
-                               if(strstr(currLine, "ENCRYPTED")) {
-                                       if((format == kSecFormatOpenSSL) &&
-                                          (itemType == kSecItemTypePrivateKey)) {
-                                               format = kSecFormatWrappedOpenSSL;
-                                       }
-                               }
-                       }
-                       CFArrayAppendValue(pemParamLines, cfStr);
-                       CFRelease(cfStr);               // array owns it 
-                       skipThis = true;
-               }
-               free((void *)currLine);
-               if(!skipThis) {
-                       /* looks like good stuff; process */
-                       break;
-               }
-               /* skip this line */
-               assert(consumed <= lenToGo);
-               currCp  += consumed;
-               lenToGo -= consumed;
-       }
-       if(lenToGo <= 2) {
-               SecImpInferDbg("impExpImportSinglePEM no valid base64 data");
-               ortn = errSecUnsupportedFormat;
-               goto errOut;
-       }
-
-       /* 
-        * currCP points to start of base64 data - mark it and search for end line.
-        * We skip everything after the end line.
-        */
-       start64 = currCp;
-       base64Len = lenToGo;                    // if no END
-       end64 = findStr(currCp, lenToGo, "-----END");
-       if(end64 != NULL) {
-               if(end64 == start64) {
-                       /* Empty, nothing between START and END */
-                       SecImpInferDbg("impExpImportSinglePEM no base64 between terminators");
-                       ortn = errSecUnsupportedFormat;
-                       goto errOut;
-               }
-               base64Len = (unsigned)(end64 - start64);
-       }
-       /* else no END, no reason to complain about that as long as base64 decode works OK */
-       
-       /* Base 64 decode */
-       decData = cuDec64((const unsigned char *)start64, base64Len, &decDataLen);
-       if(decData == NULL) {
-               SecImpInferDbg("impExpImportSinglePEM bad base64 data");
-               ortn = errSecUnsupportedFormat;
-               goto errOut;
-       }
-       
-       cdata = CFDataCreate(NULL, decData, decDataLen);
-       free((void *)decData);
-       rep = new Security::KeychainCore::SecImportRep(cdata, itemType, format, keyAlg,
-               pemParamLines);
-       CFArrayAppendValue(importReps, rep);
-       CFRelease(cdata);               // SecImportRep holds ref
-       return errSecSuccess;
-       
-errOut:
-       if(pemParamLines != NULL) {
-               CFRelease(pemParamLines);
-       }
-       return ortn;
-}
-
-/*
- * PEM decode incoming data, appending SecImportRep's to specified array.
- * Returned SecImportReps may or may not have a known type and format and 
- * (if they are keys) algorithm. 
- */
-OSStatus impExpParsePemToImportRefs(
-       CFDataRef                       importedData,
-       CFMutableArrayRef       importReps,             // output appended here
-       bool                            *isPem)                 // true means we think it was PEM regardless of 
-                                                                               // final return code    
-{
-       /*
-        * First task: is this PEM or at least base64 encoded?
-        */
-       const char *currCp = (const char *)CFDataGetBytePtr(importedData);
-       const char *cp = currCp;
-       unsigned lenToGo = (unsigned)CFDataGetLength(importedData);
-       OSStatus ortn;
-       
-       *isPem = false;
-       unsigned dex;
-       bool allBlanks = true;
-       
-       for(dex=0; dex<lenToGo; dex++, cp++) {
-               if (!isspace(*cp)) {
-                       // it's not a space.  Is it a non-ascii character?
-                       if (!isascii(*cp)) {
-                               return errSecSuccess;
-                       }
-                       
-                       // is it a control character?
-                       if (iscntrl(*cp))
-                       {
-                               return errSecSuccess;
-                       }
-                       
-                       // no, mark that an acceptable character was encountered and keep going
-                       allBlanks = false;
-               }
-       }
-
-       if (allBlanks)
-       {
-               return errSecSuccess;
-       }
-       
-       /* search for START line */
-       const char *startLine = findStr(currCp, lenToGo, "-----BEGIN");
-       if(startLine == NULL) {
-               /* Assume one item, raw base64 */
-               SecImpInferDbg("impExpParsePemToImportRefs no PEM headers, assuming raw base64");
-               ortn = impExpImportSinglePEM(currCp, lenToGo, importReps);
-               if(ortn == errSecSuccess) {
-                       *isPem = true;
-               }
-               return ortn;
-       }
-
-       /* break up input into chunks between START and END lines */
-       ortn = errSecSuccess;
-       bool gotSomePem = false;
-       do {
-               /* get to beginning of START line */
-               startLine = findStr(currCp, lenToGo, "-----BEGIN");
-               if(startLine == NULL) {
-                       break;
-               }
-               unsigned consumed = (unsigned)(startLine - currCp);
-               assert(consumed <= lenToGo);
-               lenToGo -= consumed;
-               currCp  += consumed;
-               
-               /* get to beginning of END line */
-               const char *endLine = findStr(currCp+10, lenToGo, "-----END");
-               unsigned toDecode = lenToGo;
-               if(endLine) {
-                       consumed = (unsigned)(endLine - startLine);
-                       assert(consumed <= lenToGo);
-                       currCp  += consumed;
-                       lenToGo -= consumed;
-                       
-                       /* find end of END line */
-                       const char *tmpLine = getLine(endLine, lenToGo, &consumed);
-                       assert((tmpLine != NULL) && (tmpLine[0] != 0));
-                       /* don't decode the terminators */
-                       toDecode = (unsigned)(endLine - startLine + strlen(tmpLine));
-                       free((void *)tmpLine);
-                       
-                       /* skip past END line and newlines */
-                       assert(consumed <= lenToGo);
-                       currCp  += consumed;
-                       lenToGo -= consumed;
-               }
-               else {
-                       /* no END line, we'll allow that - decode to end of file */
-                       lenToGo = 0;
-               }
-               
-               ortn = impExpImportSinglePEM(startLine, toDecode, importReps);
-               if(ortn) {
-                       break;
-               }
-               gotSomePem = true;
-       } while(lenToGo != 0);
-       if(ortn == errSecSuccess) {
-               if(gotSomePem) {
-                       *isPem = true;
-               }
-               else {
-                       SecImpInferDbg("impExpParsePemToImportRefs empty at EOF, no PEM found");
-                       ortn = kSecFormatUnknown;
-               }
-       }
-       return ortn;
-}
-       
-
-/*
- * PEM encode a single SecExportRep's data, appending to a CFData.
- */
-OSStatus impExpPemEncodeExportRep(
-       CFDataRef                       derData,
-       const char                      *pemHeader,
-       CFArrayRef                      pemParamLines,  // optional 
-       CFMutableDataRef        outData)
-{
-       unsigned char *enc;
-       unsigned encLen;
-       
-       char headerLine[200];
-       if(strlen(pemHeader) > 150) {
-               return errSecParam;
-       }
-
-       /* First base64 encode */
-       enc = cuEnc64WithLines(CFDataGetBytePtr(derData), (unsigned)CFDataGetLength(derData),
-               64, &encLen);
-       if(enc == NULL) {
-               /* malloc error is actually the only known failure */
-               SecImpExpDbg("impExpPemEncodeExportRep: cuEnc64WithLines failure");
-               return errSecAllocate;
-       }
-       
-       /* strip off trailing NULL */
-       if((encLen != 0) && (enc[encLen - 1] == '\0')) {
-               encLen--;
-       }
-       sprintf(headerLine, "-----BEGIN %s-----\n", pemHeader);
-       CFDataAppendBytes(outData, (const UInt8 *)headerLine, strlen(headerLine));
-       
-       /* optional PEM parameters lines (currently used for openssl wrap format only) */
-       if(pemParamLines != NULL) {
-               CFIndex numLines = CFArrayGetCount(pemParamLines);
-               for(CFIndex dex=0; dex<numLines; dex++) {
-                       CFStringRef cfStr = 
-                               (CFStringRef)CFArrayGetValueAtIndex(pemParamLines, dex);
-                       char cStr[512];
-                       UInt8 nl = '\n';
-                       if(!CFStringGetCString(cfStr, cStr, sizeof(cStr), 
-                                       kCFStringEncodingASCII)) {
-                               /* 
-                                * Should never happen; this module created this CFString
-                                * from a C string with ASCII encoding. Keep going, though
-                                * this is probably fatal to the exported representation.
-                                */
-                               SecImpExpDbg("impExpPemEncodeExportRep: pemParamLine screwup");
-                               continue; 
-                       }
-                       CFDataAppendBytes(outData, (const UInt8 *)cStr, strlen(cStr));
-                       CFDataAppendBytes(outData, &nl, 1);
-               }
-       }
-       CFDataAppendBytes(outData, enc, encLen);
-       sprintf(headerLine, "-----END %s-----\n", pemHeader);
-       CFDataAppendBytes(outData, (const UInt8 *)headerLine, strlen(headerLine));
-       free((void *)enc);
-       return errSecSuccess;
-}
-