wolfSSL 3.11.1 for TLS1.3 beta
Fork of wolfSSL by
Diff: wolfcrypt/src/pkcs12.c
- Revision:
- 11:cee25a834751
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wolfcrypt/src/pkcs12.c Tue May 30 01:44:10 2017 +0000 @@ -0,0 +1,1165 @@ +/* pkcs12.c + * + * Copyright (C) 2006-2016 wolfSSL Inc. + * + * This file is part of wolfSSL. + * + * wolfSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + + +#ifdef HAVE_CONFIG_H + #include <config.h> +#endif + +#include <wolfssl/wolfcrypt/settings.h> + +#if !defined(NO_ASN) && !defined(NO_PWDBASED) + +#include <wolfssl/wolfcrypt/asn.h> +#include <wolfssl/wolfcrypt/asn_public.h> +#include <wolfssl/wolfcrypt/error-crypt.h> +#include <wolfssl/wolfcrypt/hmac.h> +#include <wolfssl/wolfcrypt/logging.h> +#ifdef NO_INLINE + #include <wolfssl/wolfcrypt/misc.h> +#else + #define WOLFSSL_MISC_INCLUDED + #include <wolfcrypt/src/misc.c> +#endif +#include <wolfssl/wolfcrypt/pkcs12.h> +#include <wolfssl/wolfcrypt/pwdbased.h> + + +enum { + WC_PKCS12_KeyBag = 667, + WC_PKCS12_ShroudedKeyBag = 668, + WC_PKCS12_CertBag = 669, + WC_PKCS12_CertBag_Type1 = 675, + WC_PKCS12_CrlBag = 670, + WC_PKCS12_SecretBag = 671, + WC_PKCS12_SafeContentsBag = 672, + WC_PKCS12_DATA = 651, + WC_PKCS12_ENCRYPTED_DATA = 656, +}; + +typedef struct ContentInfo { + byte* data; + struct ContentInfo* next; + word32 encC; /* encryptedContent */ + word32 dataSz; + int type; /* DATA / encrypted / envelpoed */ +} ContentInfo; + + +typedef struct AuthenticatedSafe { + ContentInfo* CI; + byte* data; /* T contents.... */ + word32 oid; /* encrypted or not */ + word32 numCI; /* number of Content Info structs */ + word32 dataSz; +} AuthenticatedSafe; + + +typedef struct MacData { + byte* digest; + byte* salt; + word32 oid; + word32 digestSz; + word32 saltSz; + int itt; /* number of itterations when creating HMAC key */ +} MacData; + + +struct WC_PKCS12 { + void* heap; + AuthenticatedSafe* safe; + MacData* signData; + word32 oid; /* DATA / Enveloped DATA ... */ +}; + + +/* for friendlyName, localKeyId .... */ +typedef struct WC_PKCS12_ATTRIBUTE { + byte* data; + word32 oid; + word32 dataSz; +} WC_PKCS12_ATTRIBUTE; + + +WC_PKCS12* wc_PKCS12_new(void) +{ + WC_PKCS12* pkcs12 = (WC_PKCS12*)XMALLOC(sizeof(WC_PKCS12), + NULL, DYNAMIC_TYPE_PKCS); + if (pkcs12 == NULL) { + WOLFSSL_MSG("Memory issue when creating WC_PKCS12 struct"); + return NULL; + } + + XMEMSET(pkcs12, 0, sizeof(WC_PKCS12)); + + return pkcs12; +} + + +static void freeSafe(AuthenticatedSafe* safe, void* heap) +{ + int i; + + if (safe == NULL) { + return; + } + + /* free content info structs */ + for (i = safe->numCI; i > 0; i--) { + ContentInfo* ci = safe->CI; + safe->CI = ci->next; + XFREE(ci, heap, DYNAMIC_TYPE_PKCS); + } + if (safe->data != NULL) { + XFREE(safe->data, heap, DYNAMIC_TYPE_PKCS); + } + XFREE(safe, heap, DYNAMIC_TYPE_PKCS); + + (void)heap; +} + + +void wc_PKCS12_free(WC_PKCS12* pkcs12) +{ + void* heap; + + /* if null pointer is passed in do nothing */ + if (pkcs12 == NULL) { + WOLFSSL_MSG("Trying to free null WC_PKCS12 object"); + return; + } + + heap = pkcs12->heap; + if (pkcs12->safe != NULL) { + freeSafe(pkcs12->safe, heap); + } + + /* free mac data */ + if (pkcs12->signData != NULL) { + if (pkcs12->signData->digest != NULL) { + XFREE(pkcs12->signData->digest, heap, DYNAMIC_TYPE_PKCS); + } + if (pkcs12->signData->salt != NULL) { + XFREE(pkcs12->signData->salt, heap, DYNAMIC_TYPE_PKCS); + } + XFREE(pkcs12->signData, heap, DYNAMIC_TYPE_PKCS); + } + + XFREE(pkcs12, NULL, DYNAMIC_TYPE_PKCS); + pkcs12 = NULL; + + (void)heap; +} + + +static int GetSafeContent(WC_PKCS12* pkcs12, const byte* input, + word32* idx, int maxIdx) +{ + AuthenticatedSafe* safe; + word32 oid; + word32 localIdx = *idx; + int ret; + int size = 0; + + safe = (AuthenticatedSafe*)XMALLOC(sizeof(AuthenticatedSafe), pkcs12->heap, + DYNAMIC_TYPE_PKCS); + if (safe == NULL) { + return MEMORY_E; + } + XMEMSET(safe, 0, sizeof(AuthenticatedSafe)); + + ret = GetObjectId(input, &localIdx, &oid, oidIgnoreType, maxIdx); + if (ret < 0) { + WOLFSSL_LEAVE("Get object id failed", ret); + freeSafe(safe, pkcs12->heap); + return ASN_PARSE_E; + } + + safe->oid = oid; + /* check tag, length */ + if (input[localIdx++] != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC)) { + WOLFSSL_MSG("Unexpected tag in PKCS12 DER"); + freeSafe(safe, pkcs12->heap); + return ASN_PARSE_E; + } + if ((ret = GetLength(input, &localIdx, &size, maxIdx)) <= 0) { + freeSafe(safe, pkcs12->heap); + return ret; + } + + switch (oid) { + case WC_PKCS12_ENCRYPTED_DATA: + WOLFSSL_MSG("Found PKCS12 OBJECT: ENCRYPTED DATA\n"); + break; + + case WC_PKCS12_DATA: + WOLFSSL_MSG("Found PKCS12 OBJECT: DATA"); + /* get octets holding contents */ + if (input[localIdx++] != ASN_OCTET_STRING) { + WOLFSSL_MSG("Wrong tag with content PKCS12 type DATA"); + freeSafe(safe, pkcs12->heap); + return ASN_PARSE_E; + } + if ((ret = GetLength(input, &localIdx, &size, maxIdx)) <= 0) { + freeSafe(safe, pkcs12->heap); + return ret; + } + + break; + } + + safe->dataSz = size; + safe->data = (byte*)XMALLOC(size, pkcs12->heap, DYNAMIC_TYPE_PKCS); + if (safe->data == NULL) { + freeSafe(safe, pkcs12->heap); + return MEMORY_E; + } + XMEMCPY(safe->data, input + localIdx, size); + *idx = localIdx; + + /* an instance of AuthenticatedSafe is created from + * ContentInfo's strung together in a SEQUENCE. Here we itterate + * 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); + if (ret < 0) { + freeSafe(safe, pkcs12->heap); + return ASN_PARSE_E; + } + CISz += localIdx; + while ((int)localIdx < CISz) { + int curSz = 0; + word32 curIdx; + ContentInfo* ci = NULL; + + #ifdef WOLFSSL_DEBUG_PKCS12 + printf("\t\tlooking for Content Info.... "); + #endif + + if ((ret = GetSequence(input, &localIdx, &curSz, safe->dataSz)) + < 0) { + freeSafe(safe, pkcs12->heap); + return ret; + } + + if (curSz > CISz) { + /* subset should not be larger than universe */ + freeSafe(safe, pkcs12->heap); + return ASN_PARSE_E; + } + + curIdx = localIdx; + if ((ret = GetObjectId(input, &localIdx, &oid, oidIgnoreType, + safe->dataSz)) < 0) { + WOLFSSL_LEAVE("Get object id failed", ret); + freeSafe(safe, pkcs12->heap); + return ret; + } + + /* create new content info struct ... possible OID sanity check? */ + ci = (ContentInfo*)XMALLOC(sizeof(ContentInfo), pkcs12->heap, + DYNAMIC_TYPE_PKCS); + if (ci == NULL) { + freeSafe(safe, pkcs12->heap); + return MEMORY_E; + } + + ci->type = oid; + ci->dataSz = curSz - (localIdx-curIdx); + ci->data = (byte*)input + localIdx; + localIdx += ci->dataSz; + + #ifdef WOLFSSL_DEBUG_PKCS12 + switch (oid) { + case WC_PKCS12_ENCRYPTED_DATA: + printf("CONTENT INFO: ENCRYPTED DATA, size = %d\n", ci->dataSz); + break; + + case WC_PKCS12_DATA: + printf("CONTENT INFO: DATA, size = %d\n", ci->dataSz); + break; + default: + printf("CONTENT INFO: UNKNOWN, size = %d\n", ci->dataSz); + } + #endif + + /* insert to head of list */ + ci->next = safe->CI; + safe->CI = ci; + safe->numCI += 1; + } + } + + pkcs12->safe = safe; + *idx += localIdx; + + return 1; +} + + +/* optional mac data */ +static int GetSignData(WC_PKCS12* pkcs12, const byte* mem, word32* idx, + word32 totalSz) +{ + MacData* mac; + word32 curIdx = *idx; + word32 oid = 0; + int size, ret; + + + /* Digest Info : Sequence + * DigestAlgorithmIdentifier + * Digest + */ + if ((ret = GetSequence(mem, &curIdx, &size, totalSz)) <= 0) { + WOLFSSL_MSG("Failed to get PKCS12 sequence"); + return ret; + } + +#ifdef WOLFSSL_DEBUG_PKCS12 + printf("\t\tSEQUENCE: DigestInfo size = %d\n", size); +#endif + + mac = (MacData*)XMALLOC(sizeof(MacData), pkcs12->heap, DYNAMIC_TYPE_PKCS); + if (mac == NULL) { + return MEMORY_E; + } + XMEMSET(mac, 0, sizeof(MacData)); + + /* DigestAlgorithmIdentifier */ + if ((ret = GetAlgoId(mem, &curIdx, &oid, oidIgnoreType, totalSz)) < 0) { + WOLFSSL_MSG("Failed to get PKCS12 sequence"); + XFREE(mac, pkcs12->heap, DYNAMIC_TYPE_PKCS); + return ret; + } + mac->oid = oid; + + #ifdef WOLFSSL_DEBUG_PKCS12 + printf("\t\tALGO ID = %d\n", oid); + #endif + + /* Digest: should be octet type holding digest */ + if (mem[curIdx++] != ASN_OCTET_STRING) { + WOLFSSL_MSG("Failed to get digest"); + XFREE(mac, pkcs12->heap, DYNAMIC_TYPE_PKCS); + return ASN_PARSE_E; + } + + if ((ret = GetLength(mem, &curIdx, &size, totalSz)) <= 0) { + XFREE(mac, pkcs12->heap, DYNAMIC_TYPE_PKCS); + return ret; + } + mac->digestSz = size; + mac->digest = (byte*)XMALLOC(mac->digestSz, pkcs12->heap, + DYNAMIC_TYPE_PKCS); + if (mac->digest == NULL || mac->digestSz + curIdx > totalSz) { + XFREE(mac, pkcs12->heap, DYNAMIC_TYPE_PKCS); + return MEMORY_E; + } + XMEMCPY(mac->digest, mem + curIdx, mac->digestSz); + #ifdef WOLFSSL_DEBUG_PKCS12 + { + byte* p; + for (printf("\t\tDigest = "), p = (byte*)mem+curIdx; + p < (byte*)mem + curIdx + mac->digestSz; + printf("%02X", *p), p++); + printf(" : size = %d\n", mac->digestSz); + } + #endif + curIdx += mac->digestSz; + + /* get salt, should be octet string */ + if (mem[curIdx++] != ASN_OCTET_STRING) { + WOLFSSL_MSG("Failed to get salt"); + XFREE(mac->digest, pkcs12->heap, DYNAMIC_TYPE_PKCS); + XFREE(mac, pkcs12->heap, DYNAMIC_TYPE_PKCS); + return ASN_PARSE_E; + } + + if ((ret = GetLength(mem, &curIdx, &size, totalSz)) <= 0) { + XFREE(mac->digest, pkcs12->heap, DYNAMIC_TYPE_PKCS); + XFREE(mac, pkcs12->heap, DYNAMIC_TYPE_PKCS); + return ret; + } + mac->saltSz = size; + mac->salt = (byte*)XMALLOC(mac->saltSz, pkcs12->heap, + DYNAMIC_TYPE_PKCS); + if (mac->salt == NULL || mac->saltSz + curIdx > totalSz) { + XFREE(mac->digest, pkcs12->heap, DYNAMIC_TYPE_PKCS); + XFREE(mac, pkcs12->heap, DYNAMIC_TYPE_PKCS); + return MEMORY_E; + } + XMEMCPY(mac->salt, mem + curIdx, mac->saltSz); + #ifdef WOLFSSL_DEBUG_PKCS12 + { + byte* p; + for (printf("\t\tSalt = "), p = (byte*)mem + curIdx; + p < (byte*)mem + curIdx + mac->saltSz; + printf("%02X", *p), p++); + printf(" : size = %d\n", mac->saltSz); + } + #endif + curIdx += mac->saltSz; + + /* check for MAC itterations, default to 1 */ + mac->itt = 1; + if (curIdx < totalSz) { + int number = 0; + if ((ret = GetShortInt(mem, &curIdx, &number, totalSz)) >= 0) { + /* found a itteration value */ + mac->itt = number; + } + } + +#ifdef WOLFSSL_DEBUG_PKCS12 + printf("\t\tITTERATIONS : %d\n", mac->itt); +#endif + + *idx = curIdx; + pkcs12->signData = mac; + + return 0; +} + + +/* check mac on pkcs12, pkcs12->mac has been sanity checked before entering * + * returns the result of comparison, success is 0 */ +static int wc_PKCS12_verify(WC_PKCS12* pkcs12, byte* data, word32 dataSz, + const byte* psw, word32 pswSz) +{ + Hmac hmac; + MacData* mac; + int ret, typeH, kLen; + int idx = 0; + int id = 3; /* value from RFC 7292 indicating key is used for MAC */ + word32 i; + byte digest[MAX_DIGEST_SIZE]; + byte unicodePasswd[MAX_UNICODE_SZ]; + byte key[MAX_KEY_SIZE]; + + mac = pkcs12->signData; + +#ifdef WOLFSSL_DEBUG_PKCS12 + printf("Verifying MAC with OID = %d\n", mac->oid); +#endif + + /* check if this builds digest size is too small */ + if (mac->digestSz > MAX_DIGEST_SIZE) { + WOLFSSL_MSG("PKCS12 max digest size too small"); + return BAD_FUNC_ARG; + } + + /* unicode set up from asn.c */ + if ( (pswSz * 2 + 2) > (int)sizeof(unicodePasswd)) { + WOLFSSL_MSG("PKCS12 max unicode size too small"); + return UNICODE_SIZE_E; + } + + for (i = 0; i < pswSz; i++) { + unicodePasswd[idx++] = 0x00; + unicodePasswd[idx++] = (byte)psw[i]; + } + /* add trailing NULL */ + unicodePasswd[idx++] = 0x00; + unicodePasswd[idx++] = 0x00; + + /* get hash type used and resulting size of HMAC key */ + switch (mac->oid) { + #ifndef NO_SHA + case SHAh: /* 88 */ + typeH = SHA; + kLen = SHA_DIGEST_SIZE; + break; + #endif + + #ifndef NO_SHA256 + case SHA256h: /* 414 */ + typeH = SHA256; + kLen = SHA256_DIGEST_SIZE; + break; + #endif + + #ifdef WOLFSSL_SHA384 + case SHA384h: /* 415 */ + typeH = SHA384; + kLen = SHA384_DIGEST_SIZE; + break; + #endif + + #ifdef WOLFSSL_SHA512 + case SHA512h: /* 416 */ + typeH = SHA512; + kLen = SHA512_DIGEST_SIZE; + break; + #endif + + default: /* May be SHA224 or was just not built in */ + WOLFSSL_MSG("Unsupported hash used"); + return BAD_FUNC_ARG; + } + + /* idx contains size of unicodePasswd */ + if ((ret = wc_PKCS12_PBKDF_ex(key, unicodePasswd, idx, mac->salt, + mac->saltSz, mac->itt, kLen, typeH, id, pkcs12->heap)) < 0) { + return ret; + } + + /* now that key has been created use it to get HMAC hash on data */ + if ((ret = wc_HmacInit(&hmac, NULL, INVALID_DEVID)) != 0) { + return ret; + } + ret = wc_HmacSetKey(&hmac, typeH, key, kLen); + if (ret == 0) + ret = wc_HmacUpdate(&hmac, data, dataSz); + if (ret == 0) + ret = wc_HmacFinal(&hmac, digest); + wc_HmacFree(&hmac); + + if (ret != 0) + return ret; + +#ifdef WOLFSSL_DEBUG_PKCS12 + { + byte* p; + for (printf("\t\tHash = "), p = (byte*)digest; + p < (byte*)digest + mac->digestSz; + printf("%02X", *p), p++); + printf(" : size = %d\n", mac->digestSz); + } +#endif + + return XMEMCMP(digest, mac->digest, mac->digestSz); +} + + +/* Convert DER format stored in der buffer to WC_PKCS12 struct + * Puts the raw contents of Content Info into structure without completly + * parsing or decoding. + * der : pointer to der buffer holding PKCS12 + * derSz : size of der buffer + * pkcs12 : non-null pkcs12 pointer + */ +int wc_d2i_PKCS12(const byte* der, word32 derSz, WC_PKCS12* pkcs12) +{ + word32 idx = 0; + word32 totalSz = 0; + int ret; + int size = 0; + int version = 0; + + WOLFSSL_ENTER("wolfSSL_d2i_PKCS12_bio"); + + if (der == NULL || pkcs12 == NULL) { + return BAD_FUNC_ARG; + } + + totalSz = derSz; + if ((ret = GetSequence(der, &idx, &size, totalSz)) <= 0) { + WOLFSSL_MSG("Failed to get PKCS12 sequence"); + return ret; + } + + /* get version */ + if ((ret = GetMyVersion(der, &idx, &version, totalSz)) < 0) { + return ret; + } + + #ifdef WOLFSSL_DEBUG_PKCS12 + printf("\nBEGIN: PKCS12 size = %d\n", totalSz); + printf("version = %d\n", version); + #endif + + if (version != 3) { + WOLFSSL_MSG("PKCS12 unsupported version!"); + return ASN_VERSION_E; + } + + if ((ret = GetSequence(der, &idx, &size, totalSz)) < 0) { + return ret; + } + + #ifdef WOLFSSL_DEBUG_PKCS12 + printf("\tSEQUENCE: AuthenticatedSafe size = %d\n", size); + #endif + + if ((ret = GetSafeContent(pkcs12, der, &idx, size + idx)) < 0) { + WOLFSSL_MSG("GetSafeContent error"); + return ret; + } + + /* if more buffer left check for MAC data */ + if (idx < totalSz) { + if ((ret = GetSequence(der, &idx, &size, totalSz)) < 0) { + WOLFSSL_MSG("Ignoring unknown data at end of PKCS12 DER buffer"); + } + else { + #ifdef WOLFSSL_DEBUG_PKCS12 + printf("\tSEQUENCE: Signature size = %d\n", size); + #endif + + if ((ret = GetSignData(pkcs12, der, &idx, totalSz)) < 0) { + return ASN_PARSE_E; + } + } + } + + #ifdef WOLFSSL_DEBUG_PKCS12 + printf("END: PKCS12\n"); + #endif + + return 1; +} + + +/* helper function to free WC_DerCertList */ +static void freeCertList(WC_DerCertList* list, void* heap) { + WC_DerCertList* current; + + if (list == NULL) { + return; + } + + current = list; + while(current != NULL) { + WC_DerCertList* next = current->next; + if (current->buffer != NULL) { + XFREE(current->buffer, heap, DYNAMIC_TYPE_PKCS); + } + XFREE(current, heap, DYNAMIC_TYPE_PKCS); + current = next; + } + + (void)heap; +} + + +static void freeBuffers(byte* a, byte* b, void* heap) +{ + if (a != NULL) { + XFREE(a, heap, DYNAMIC_TYPE_PKCS); + } + if (b != NULL) { + XFREE(b, heap, DYNAMIC_TYPE_PKCS); + } + (void)heap; +} + + +/* return 1 on success and 0 on failure. + * By side effect returns private key, cert, and optionally ca. + * Parses and decodes the parts of PKCS12 + * + * NOTE: can parse with USER RSA enabled but may return cert that is not the + * pair for the key when using RSA key pairs. + * + * pkcs12 : non-null WC_PKCS12 struct + * psw : password to use for PKCS12 decode + * pkey : Private key returned + * cert : x509 cert returned + * ca : optional ca returned + */ +int wc_PKCS12_parse(WC_PKCS12* pkcs12, const char* psw, + byte** pkey, word32* pkeySz, byte** cert, word32* certSz, + WC_DerCertList** ca) +{ + ContentInfo* ci = NULL; + WC_DerCertList* certList = NULL; + byte* buf = NULL; + word32 i, oid; + int ret, pswSz; + + WOLFSSL_ENTER("wc_PKCS12_parse"); + + if (pkcs12 == NULL || psw == NULL || cert == NULL || certSz == NULL || + pkey == NULL || pkeySz == NULL) { + return BAD_FUNC_ARG; + } + pswSz = (int)XSTRLEN(psw); + *cert = NULL; + *pkey = NULL; + if (ca != NULL) { + *ca = NULL; + } + + /* if there is sign data then verify the MAC */ + if (pkcs12->signData != NULL ) { + if ((ret = wc_PKCS12_verify(pkcs12, pkcs12->safe->data, + pkcs12->safe->dataSz, (byte*)psw, pswSz)) != 0) { + WOLFSSL_MSG("PKCS12 Bad MAC on verify"); + WOLFSSL_LEAVE("wc_PKCS12_parse verify ", ret); + return MAC_CMP_FAILED_E; + } + } + + if (pkcs12->safe == NULL) { + WOLFSSL_MSG("No PKCS12 safes to parse"); + return BAD_FUNC_ARG; + } + + /* Decode content infos */ + ci = pkcs12->safe->CI; + for (i = 0; i < pkcs12->safe->numCI; i++) { + byte* data; + word32 idx = 0; + int size, totalSz; + + if (ci->type == WC_PKCS12_ENCRYPTED_DATA) { + int number; + + WOLFSSL_MSG("Decrypting PKCS12 Content Info Container"); + data = ci->data; + if (data[idx++] != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC)) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return ASN_PARSE_E; + } + if ((ret = GetLength(data, &idx, &size, ci->dataSz)) < 0) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return ret; + } + + if ((ret = GetSequence(data, &idx, &size, ci->dataSz)) < 0) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return ret; + } + + if ((ret = GetShortInt(data, &idx, &number, ci->dataSz)) < 0) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return ret; + } + + if (number != 0) { + WOLFSSL_MSG("Expecting 0 for Integer with Encrypted PKCS12"); + } + + if ((ret = GetSequence(data, &idx, &size, ci->dataSz)) < 0) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return ret; + } + + ret = GetObjectId(data, &idx, &oid, oidIgnoreType, ci->dataSz); + if (ret < 0 || oid != WC_PKCS12_DATA) { + WOLFSSL_MSG("Not PKCS12 DATA object or get object parse error"); + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return ASN_PARSE_E; + } + + /* decrypted content overwrites input buffer */ + size = ci->dataSz - idx; + buf = (byte*)XMALLOC(size, pkcs12->heap, DYNAMIC_TYPE_PKCS); + if (buf == NULL) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return MEMORY_E; + } + XMEMCPY(buf, data + idx, size); + + if ((ret = DecryptContent(buf, size, psw, pswSz)) < 0) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + WOLFSSL_MSG("Decryption failed, algorithm not compiled in?"); + return ret; + } + + data = buf; + idx = 0; + #ifdef WOLFSSL_DEBUG_PKCS12 + { + byte* p; + for (printf("\tData = "), p = (byte*)buf; + p < (byte*)buf + size; + printf("%02X", *p), p++); + printf("\n"); + } + #endif + } + else { /* type DATA */ + WOLFSSL_MSG("Parsing PKCS12 DATA Content Info Container"); + data = ci->data; + if (data[idx++] != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC)) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return ASN_PARSE_E; + } + if ((ret = GetLength(data, &idx, &size, ci->dataSz)) <= 0) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return ret; + } + if (data[idx++] != ASN_OCTET_STRING) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return ASN_PARSE_E; + } + if ((ret = GetLength(data, &idx, &size, ci->dataSz)) < 0) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return ret; + } + + } + + /* parse through bags in ContentInfo */ + if ((ret = GetSequence(data, &idx, &totalSz, ci->dataSz)) < 0) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return ret; + } + totalSz += idx; + + while ((int)idx < totalSz) { + int bagSz; + if ((ret = GetSequence(data, &idx, &bagSz, ci->dataSz)) < 0) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return ret; + } + bagSz += idx; + + if ((ret = GetObjectId(data, &idx, &oid, oidIgnoreType, + ci->dataSz)) < 0) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return ret; + } + + switch (oid) { + case WC_PKCS12_KeyBag: /* 667 */ + WOLFSSL_MSG("PKCS12 Key Bag found"); + if (data[idx++] != + (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC)) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return ASN_PARSE_E; + } + if ((ret = GetLength(data, &idx, &size, ci->dataSz)) <= 0) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return ASN_PARSE_E; + } + if (*pkey == NULL) { + *pkey = (byte*)XMALLOC(size, pkcs12->heap, + DYNAMIC_TYPE_PKCS); + if (*pkey == NULL) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return MEMORY_E; + } + XMEMCPY(*pkey, data + idx, size); + *pkeySz = ToTraditional(*pkey, size); + } + #ifdef WOLFSSL_DEBUG_PKCS12 + { + byte* p; + for (printf("\tKey = "), p = (byte*)*pkey; + p < (byte*)*pkey + size; + printf("%02X", *p), p++); + printf("\n"); + } + #endif + idx += size; + break; + + case WC_PKCS12_ShroudedKeyBag: /* 668 */ + { + byte* k; + WOLFSSL_MSG("PKCS12 Shrouded Key Bag found"); + if (data[idx++] != + (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC)) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return ASN_PARSE_E; + } + if ((ret = GetLength(data, &idx, &size, + ci->dataSz)) < 0) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return ASN_PARSE_E; + } + + k = (byte*)XMALLOC(size, pkcs12->heap, + DYNAMIC_TYPE_PKCS); + if (k == NULL) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return MEMORY_E; + } + XMEMCPY(k, data + idx, size); + + /* overwrites input, be warned */ + if ((ret = ToTraditionalEnc(k, size, psw, pswSz)) < 0) { + freeBuffers(k, NULL, pkcs12->heap); + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return ret; + } + + if (ret < size) { + /* shrink key buffer */ + k = (byte*)XREALLOC(k, ret, pkcs12->heap, + DYNAMIC_TYPE_PKCS); + if (k == NULL) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return MEMORY_E; + } + } + size = ret; + + if (*pkey == NULL) { + *pkey = k; + *pkeySz = size; + } + else { /* only expecting one key */ + freeBuffers(k, NULL, pkcs12->heap); + } + idx += size; + + #ifdef WOLFSSL_DEBUG_PKCS12 + { + byte* p; + for (printf("\tKey = "), p = (byte*)k; + p < (byte*)k + ret; + printf("%02X", *p), p++); + printf("\n"); + } + #endif + } + break; + + case WC_PKCS12_CertBag: /* 669 */ + { + WC_DerCertList* node; + WOLFSSL_MSG("PKCS12 Cert Bag found"); + if (data[idx++] != + (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC)) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return ASN_PARSE_E; + } + if ((ret = GetLength(data, &idx, &size, ci->dataSz)) < 0) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return ret; + } + + /* get cert bag type */ + if ((ret = GetSequence(data, &idx, &size, ci->dataSz)) <0) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return ret; + } + + if ((ret = GetObjectId(data, &idx, &oid, oidIgnoreType, + ci->dataSz)) < 0) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return ret; + } + + switch (oid) { + case WC_PKCS12_CertBag_Type1: /* 675 */ + /* type 1 */ + WOLFSSL_MSG("PKCS12 cert bag type 1"); + if (data[idx++] != + (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC)) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return ASN_PARSE_E; + } + if ((ret = GetLength(data, &idx, &size, ci->dataSz)) + <= 0) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return ret; + } + if (data[idx++] != ASN_OCTET_STRING) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return ASN_PARSE_E; + } + if ((ret = GetLength(data, &idx, &size, ci->dataSz)) + < 0) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return ret; + } + break; + default: + WOLFSSL_MSG("Unknown PKCS12 cert bag type"); + } + + if (size + idx > (word32)bagSz) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return ASN_PARSE_E; + } + + /* list to hold all certs found */ + node = (WC_DerCertList*)XMALLOC(sizeof(WC_DerCertList), + pkcs12->heap, DYNAMIC_TYPE_PKCS); + if (node == NULL) { + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return MEMORY_E; + } + XMEMSET(node, 0, sizeof(WC_DerCertList)); + + node->buffer = (byte*)XMALLOC(size, pkcs12->heap, + DYNAMIC_TYPE_PKCS); + if (node->buffer == NULL) { + XFREE(node, pkcs12->heap, DYNAMIC_TYPE_PKCS); + freeBuffers(*pkey, buf, pkcs12->heap); + freeCertList(certList, pkcs12->heap); + return MEMORY_E; + } + XMEMCPY(node->buffer, data + idx, size); + node->bufferSz = size; + + /* put the new node into the list */ + if (certList != NULL) { + WOLFSSL_MSG("Pushing new cert onto stack"); + node->next = certList; + certList = node; + } + else { + certList = node; + } + + /* on to next */ + idx += size; + } + break; + + case WC_PKCS12_CrlBag: /* 670 */ + WOLFSSL_MSG("PKCS12 CRL BAG not yet supported"); + break; + + case WC_PKCS12_SecretBag: /* 671 */ + WOLFSSL_MSG("PKCS12 Secret BAG not yet supported"); + break; + + case WC_PKCS12_SafeContentsBag: /* 672 */ + WOLFSSL_MSG("PKCS12 Safe Contents BAG not yet supported"); + break; + + default: + WOLFSSL_MSG("Unknown PKCS12 BAG type found"); + } + + /* Attribute, unknown bag or unsupported */ + if ((int)idx < bagSz) { + idx = bagSz; /* skip for now */ + } + } + + /* free temporary buffer */ + if (buf != NULL) { + XFREE(buf, pkcs12->heap, DYNAMIC_TYPE_PKCS); + buf = NULL; + } + ci = ci->next; + WOLFSSL_MSG("Done Parsing PKCS12 Content Info Container"); + } + + /* check if key pair, remove from list */ + { + WC_DerCertList* current = certList; + WC_DerCertList* previous = NULL; + + if (*pkey != NULL) { + + while (current != NULL) { + DecodedCert DeCert; + InitDecodedCert(&DeCert, current->buffer, current->bufferSz, + pkcs12->heap); + if (ParseCertRelative(&DeCert, CERT_TYPE, NO_VERIFY, NULL) == 0) { + if (wc_CheckPrivateKey(*pkey, *pkeySz, &DeCert) == 1) { + WOLFSSL_MSG("Key Pair found"); + *cert = current->buffer; + *certSz = current->bufferSz; + + if (previous == NULL) { + certList = current->next; + } + else { + previous->next = current->next; + } + FreeDecodedCert(&DeCert); + XFREE(current, pkcs12->heap, DYNAMIC_TYPE_PKCS); + break; + } + } + + FreeDecodedCert(&DeCert); + previous = current; + current = current->next; + } + + } + } + + if (ca != NULL) { + *ca = certList; + } + else { + /* free list, not wanted */ + freeCertList(certList, pkcs12->heap); + } + + return 1; +} + + +/* if using a specific memory heap */ +int wc_PKCS12_SetHeap(WC_PKCS12* pkcs12, void* heap) +{ + if (pkcs12 == NULL) { + return BAD_FUNC_ARG; + } + pkcs12->heap = heap; + + return 0; +} + + +/* getter for heap */ +void* wc_PKCS12_GetHeap(WC_PKCS12* pkcs12) +{ + if (pkcs12 == NULL) { + return NULL; + } + + return pkcs12->heap; +} + +#endif /* !defined(NO_ASN) && !defined(NO_PWDBASED) */ + +