int pemDecode( const unsigned char *inData, unsigned inDataLen, unsigned char **outData, unsigned *outDataLen) { char *cp; char *curr1, *curr2; char *startPem = NULL; char *endPem = NULL; unsigned char *out; unsigned outLen; int ourRtn = 0; char *freeCp = NULL; /* make the whole thing a NULL-terminated string */ if(inData[inDataLen - 1] != '\0') { cp = freeCp = (char *)malloc(inDataLen + 1); memmove(cp, inData, inDataLen); cp[inDataLen] = '\0'; inDataLen++; } else { /* already is */ cp = (char *)inData; } /* cp is start of NULL-terminated buffer, size inDataLen */ /* skip over everything until "-----" */ curr1 = strstr(cp, "-----"); if(curr1 == NULL) { printf("***pemDecode: no terminator found\n"); ourRtn = -1; goto abort; } /* find end of separator line, handling both flavors of terminator */ cp = curr1; curr1 = strchr(cp, '\n'); curr2 = strchr(cp, '\r'); if((curr1 == NULL) & (curr2 == NULL)) { printf("***pemDecode: Bad PEM format (1)\n"); ourRtn = -1; goto abort; } if(curr1 == NULL) { startPem = curr2; } else { startPem = curr1; } /* startPem points to end of separator line */ /* locate ending terminator and lop it off */ curr1 = strstr(startPem, "-----"); if(curr1 == NULL) { printf("***pemDecode: Bad PEM format (2)\n"); ourRtn = -1; goto abort; } endPem = curr1; /* endPem points to last PEM data plus one */ out = cuDec64((unsigned char *)startPem, (unsigned int)(endPem-startPem), &outLen); if(out == NULL) { printf("Bad PEM format (3)\n"); ourRtn = -1; goto abort; } *outData = out; *outDataLen = outLen; abort: if(freeCp) { free(freeCp); } return ourRtn; }
/* * 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; }