diff --git a/tests/api.c b/tests/api.c index e050b98c7..483132180 100644 --- a/tests/api.c +++ b/tests/api.c @@ -9670,6 +9670,9 @@ static int test_wolfSSL_PKCS12(void) AssertNotNull(pkey); AssertNotNull(cert); + wolfSSL_EVP_PKEY_free(pkey); + wolfSSL_X509_free(cert); + /* check parse with extra certs kept */ ret = PKCS12_parse(pkcs12, "wolfSSL test", &pkey, &cert, &ca); AssertIntEQ(ret, WOLFSSL_SUCCESS); diff --git a/wolfcrypt/src/pkcs12.c b/wolfcrypt/src/pkcs12.c index e3a8c55b7..36f2ae0c6 100644 --- a/wolfcrypt/src/pkcs12.c +++ b/wolfcrypt/src/pkcs12.c @@ -110,6 +110,13 @@ struct WC_PKCS12 { AuthenticatedSafe* safe; MacData* signData; word32 oid; /* DATA / Enveloped DATA ... */ + byte indefinite; +#ifdef ASN_BER_TO_DER + byte* safeDer; /* der encoded version of message */ + byte* der; /* der encoded version of message */ + word32 safeDersz; + word32 derSz; +#endif }; @@ -149,11 +156,14 @@ static void freeSafe(AuthenticatedSafe* safe, void* heap) ContentInfo* ci = safe->CI; safe->CI = ci->next; XFREE(ci, heap, DYNAMIC_TYPE_PKCS); + ci = NULL; } if (safe->data != NULL) { XFREE(safe->data, heap, DYNAMIC_TYPE_PKCS); + safe->data = NULL; } XFREE(safe, heap, DYNAMIC_TYPE_PKCS); + safe = NULL; (void)heap; } @@ -188,7 +198,19 @@ void wc_PKCS12_free(WC_PKCS12* pkcs12) pkcs12->signData = NULL; } +#ifdef ASN_BER_TO_DER + if (pkcs12->der != NULL) { + XFREE(pkcs12->der, pkcs12->heap, DYNAMIC_TYPE_PKCS); + pkcs12->der = NULL; + } + if (pkcs12->safeDer != NULL) { + XFREE(pkcs12->safeDer, pkcs12->heap, DYNAMIC_TYPE_PKCS); + pkcs12->safeDer = NULL; + } +#endif + XFREE(pkcs12, NULL, DYNAMIC_TYPE_PKCS); + pkcs12 = NULL; } @@ -269,15 +291,38 @@ static int GetSafeContent(WC_PKCS12* pkcs12, const byte* input, XMEMCPY(safe->data, input + localIdx, size); *idx = localIdx; + localIdx = 0; + input = safe->data; + size = safe->dataSz; + +#ifdef ASN_BER_TO_DER + if (pkcs12->indefinite) { + if ((ret = wc_BerToDer(input, safe->dataSz, NULL, + &pkcs12->safeDersz)) != LENGTH_ONLY_E) { + WOLFSSL_MSG("Not BER sequence"); + return ASN_PARSE_E; + } + + pkcs12->safeDer = (byte*)XMALLOC(pkcs12->safeDersz, pkcs12->heap, + DYNAMIC_TYPE_PKCS); + if (pkcs12->safeDer == NULL) { + freeSafe(safe, pkcs12->heap); + return MEMORY_E; + } + + ret = wc_BerToDer(input, safe->dataSz, pkcs12->safeDer, &pkcs12->safeDersz); + + input = pkcs12->safeDer; + } +#endif /* ASN_BER_TO_DER */ + /* an instance of AuthenticatedSafe is created from * ContentInfo's strung together in a SEQUENCE. Here we iterate * through the ContentInfo's and add them to our * AuthenticatedSafe struct */ - localIdx = 0; - input = safe->data; { int CISz; - ret = GetSequence(input, &localIdx, &CISz, safe->dataSz); + ret = GetSequence(input, &localIdx, &CISz, size); if (ret < 0) { freeSafe(safe, pkcs12->heap); return ASN_PARSE_E; @@ -292,8 +337,7 @@ static int GetSafeContent(WC_PKCS12* pkcs12, const byte* input, printf("\t\tlooking for Content Info.... "); #endif - if ((ret = GetSequence(input, &localIdx, &curSz, safe->dataSz)) - < 0) { + if ((ret = GetSequence(input, &localIdx, &curSz, size)) < 0) { freeSafe(safe, pkcs12->heap); return ret; } @@ -306,7 +350,7 @@ static int GetSafeContent(WC_PKCS12* pkcs12, const byte* input, curIdx = localIdx; if ((ret = GetObjectId(input, &localIdx, &oid, oidIgnoreType, - safe->dataSz)) < 0) { + size)) < 0) { WOLFSSL_LEAVE("Get object id failed", ret); freeSafe(safe, pkcs12->heap); return ret; @@ -651,7 +695,7 @@ int wc_d2i_PKCS12(const byte* der, word32 derSz, WC_PKCS12* pkcs12) } totalSz = derSz; - if (GetSequence(der, &idx, &size, totalSz) <= 0) { + if (GetSequence(der, &idx, &size, totalSz) < 0) { WOLFSSL_MSG("Failed to get PKCS12 sequence"); return ASN_PARSE_E; } @@ -661,6 +705,46 @@ int wc_d2i_PKCS12(const byte* der, word32 derSz, WC_PKCS12* pkcs12) return ret; } + #ifdef ASN_BER_TO_DER + if (size == 0) { + if ((ret = wc_BerToDer(der, totalSz, NULL, + (word32*)&size)) != LENGTH_ONLY_E) { + WOLFSSL_MSG("Not BER sequence"); + return ASN_PARSE_E; + } + + pkcs12->der = (byte*)XMALLOC(size, pkcs12->heap, DYNAMIC_TYPE_PKCS); + if (pkcs12->der == NULL) + return MEMORY_E; + ret = wc_BerToDer(der, derSz, pkcs12->der, (word32*)&size); + if (ret < 0) { + return ret; + } + + der = pkcs12->der; + derSz = pkcs12->derSz = size; + totalSz = size; + idx = 0; + + if ((ret = GetSequence(der, &idx, &size, totalSz)) < 0) { + WOLFSSL_MSG("Failed to get PKCS12 sequence"); + return ASN_PARSE_E; + } + + /* get version */ + if ((ret = GetMyVersion(der, &idx, &version, totalSz)) < 0) { + return ret; + } + + pkcs12->indefinite = 1; + + } + else +#endif /* ASN_BER_TO_DER */ + { + pkcs12->indefinite = 0; + } + #ifdef WOLFSSL_DEBUG_PKCS12 printf("\nBEGIN: PKCS12 size = %d\n", totalSz); printf("version = %d\n", version); @@ -685,6 +769,15 @@ int wc_d2i_PKCS12(const byte* der, word32 derSz, WC_PKCS12* pkcs12) return ret; } +#ifdef ASN_BER_TO_DER + /* If indef, skip EOF */ + if (pkcs12->indefinite) { + while(der[idx] == ASN_EOC && idx < totalSz) { + idx+=1; + } + } +#endif + /* if more buffer left check for MAC data */ if (idx < totalSz) { if ((ret = GetSequence(der, &idx, &size, totalSz)) < 0) { @@ -1060,6 +1153,141 @@ static WARN_UNUSED_RESULT int freeDecCertList(WC_DerCertList** list, return 0; } +#ifdef ASN_BER_TO_DER +/* append data to encrypted content cache in PKCS12 structure + * return buffer on success, NULL on error */ +static byte* PKCS12_ConcatonateContent(WC_PKCS12* pkcs12,byte* mergedData, + word32* mergedSz, byte* in, word32 inSz) +{ + byte* oldContent; + word32 oldContentSz; + + if (mergedData == NULL || in == NULL) + return NULL; + + /* save pointer to old cache */ + oldContent = mergedData; + oldContentSz = *mergedSz; + + /* re-allocate new buffer to fit appended data */ + mergedData = (byte*)XMALLOC(oldContentSz + inSz, pkcs12->heap, + DYNAMIC_TYPE_PKCS); + + if (oldContent != NULL) { + XMEMCPY(mergedData, oldContent, oldContentSz); + } + XMEMCPY(mergedData + oldContentSz, in, inSz); + *mergedSz += inSz; + + XFREE(oldContent, pkcs12->heap, DYNAMIC_TYPE_PKCS); + + return mergedData; +} + +/* Check if constructed [0] is seen after wc_BerToDer() or not. + * returns 1 if seen, 0 if not, ASN_PARSE_E on error */ +static int PKCS12_CheckConstructedZero(byte* data, word32 dataSz, word32* idx) +{ + word32 oid; + int ret = 0; + int number, size; + byte tag; + + if (GetSequence(data, idx, &size, dataSz) < 0) { + ret = ASN_PARSE_E; + } + + if (ret == 0 && GetObjectId(data, idx, &oid, oidIgnoreType, dataSz)) { + ret = ASN_PARSE_E; + } + + if (ret == 0 && GetSequence(data, idx, &size, dataSz) < 0) { + ret = ASN_PARSE_E; + } + + if (ret == 0 && GetOctetString(data, idx, &size, dataSz) < 0) { + ret = ASN_PARSE_E; + } + + *idx += size; + if (ret == 0 && GetShortInt(data, idx, &number, dataSz) < 0) { + ret = ASN_PARSE_E; + } + + /* Check if wc_BerToDer() handled constructed [0] and octet + * strings properly, manually fix it if not. */ + if (ret == 0 && GetASNTag(data, idx, &tag, dataSz) < 0) { + ret = ASN_PARSE_E; + } + else if (ret == 0 && tag == 0xa0) { + ret = 1; + } + + return ret; +} + +/* Manually coalesce definite length octet strings into one unconstructed + * definite length octet string. + * returns 0 on success, negative on failure */ +static int PKCS12_CoalesceOctetStrings(WC_PKCS12* pkcs12, byte* data, + word32 dataSz, word32* idx, int* curIdx) +{ + byte* mergedData = NULL; /* buffer for concatonated strings */ + word32 mergedSz = 0; /* total size of merged strings */ + int encryptedContentSz = 0; + int originalEncSz = 0; + int ret = 0; + int saveIdx; + byte tag; + + saveIdx = *idx; + + if (GetLength(data, idx, &originalEncSz, dataSz) < 0) { + ret = ASN_PARSE_E; + } + + /* Loop through octet strings and concatonate them without + * the tags and length */ + while ((int)*idx < originalEncSz + *curIdx) { + if (GetASNTag(data, idx, &tag, dataSz) < 0) { + ret = ASN_PARSE_E; + } + if (ret == 0 && (tag != ASN_OCTET_STRING)) { + ret = ASN_PARSE_E; + } + if (ret == 0 && GetLength(data, idx, + &encryptedContentSz, dataSz) <= 0) { + ret = ASN_PARSE_E; + } + if (ret == 0) { + if (mergedData == NULL) { + mergedData = (byte*)XMALLOC(encryptedContentSz, + pkcs12->heap, DYNAMIC_TYPE_PKCS); + if (mergedData == NULL) { + ret = MEMORY_E; + } + } + mergedData = PKCS12_ConcatonateContent(pkcs12, mergedData, + &mergedSz, &data[*idx], encryptedContentSz); + } + if (ret != 0) { + break; + } + *idx += encryptedContentSz; + } + + *idx = saveIdx; + + *idx += SetLength(mergedSz, &data[*idx]); + + /* Copy over concatonated octet strings into data buffer */ + XMEMCPY(&data[*idx], mergedData, mergedSz); + + XFREE(mergedData, pkcs12->heap, DYNAMIC_TYPE_PKCS); + + return ret; +} +#endif /* return 0 on success and negative on failure. * By side effect returns private key, cert, and optionally ca. @@ -1083,8 +1311,11 @@ int wc_PKCS12_parse(WC_PKCS12* pkcs12, const char* psw, WC_DerCertList* tailList = NULL; byte* buf = NULL; word32 i, oid; - int ret, pswSz; word32 algId; + int ret, pswSz; +#ifdef ASN_BER_TO_DER + int curIdx; +#endif WOLFSSL_ENTER("wc_PKCS12_parse"); @@ -1123,11 +1354,12 @@ int wc_PKCS12_parse(WC_PKCS12* pkcs12, const char* psw, int size, totalSz; byte tag; + data = ci->data; + if (ci->type == WC_PKCS12_ENCRYPTED_DATA) { int number; WOLFSSL_MSG("Decrypting PKCS12 Content Info Container"); - data = ci->data; if (GetASNTag(data, &idx, &tag, ci->dataSz) < 0) { ERROR_OUT(ASN_PARSE_E, exit_pk12par); } @@ -1161,7 +1393,24 @@ int wc_PKCS12_parse(WC_PKCS12* pkcs12, const char* psw, ERROR_OUT(ASN_PARSE_E, exit_pk12par); } - /* decrypted content overwrites input buffer */ + +#ifdef ASN_BER_TO_DER + curIdx = idx; + /* If indefinite length format, ensure it is in the ASN format + * the DecryptContent() expects */ + if (pkcs12->indefinite && PKCS12_CheckConstructedZero(data, + ci->dataSz, &idx) == 1) { + data[idx-1] = ASN_LONG_LENGTH; + ret = PKCS12_CoalesceOctetStrings(pkcs12, data, ci->dataSz, + &idx, &curIdx); + if (ret < 0) { + goto exit_pk12par; + } + } + idx = curIdx; +#endif + + /* decrypted content overwrites input buffer */ size = ci->dataSz - idx; buf = (byte*)XMALLOC(size, pkcs12->heap, DYNAMIC_TYPE_PKCS); if (buf == NULL) { @@ -1189,7 +1438,6 @@ int wc_PKCS12_parse(WC_PKCS12* pkcs12, const char* psw, } else { /* type DATA */ WOLFSSL_MSG("Parsing PKCS12 DATA Content Info Container"); - data = ci->data; if (GetASNTag(data, &idx, &tag, ci->dataSz) < 0) { ERROR_OUT(ASN_PARSE_E, exit_pk12par); }