Files
wolfssl/wolfcrypt/src/pkcs12.c
2017-10-22 15:58:35 -07:00

1124 lines
34 KiB
C

/* pkcs12.c
*
* Copyright (C) 2006-2017 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>
#define ERROR_OUT(err, eLabel) { ret = (err); goto eLabel; }
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_DIGEST);
pkcs12->signData->digest = NULL;
}
if (pkcs12->signData->salt != NULL) {
XFREE(pkcs12->signData->salt, heap, DYNAMIC_TYPE_SALT);
pkcs12->signData->salt = NULL;
}
XFREE(pkcs12->signData, heap, DYNAMIC_TYPE_PKCS);
pkcs12->signData = NULL;
}
XFREE(pkcs12, NULL, DYNAMIC_TYPE_PKCS);
pkcs12 = NULL;
}
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 ret;
}
/* 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_DIGEST);
if (mac->digest == NULL || mac->digestSz + curIdx > totalSz) {
ERROR_OUT(MEMORY_E, exit_gsd);
}
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");
ERROR_OUT(ASN_PARSE_E, exit_gsd);
}
if ((ret = GetLength(mem, &curIdx, &size, totalSz)) <= 0) {
goto exit_gsd;
}
mac->saltSz = size;
mac->salt = (byte*)XMALLOC(mac->saltSz, pkcs12->heap, DYNAMIC_TYPE_SALT);
if (mac->salt == NULL || mac->saltSz + curIdx > totalSz) {
ERROR_OUT(MEMORY_E, exit_gsd);
}
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;
ret = 0; /* success */
exit_gsd:
/* failure cleanup */
if (ret != 0) {
if (mac) {
if (mac->digest)
XFREE(mac->digest, pkcs12->heap, DYNAMIC_TYPE_DIGEST);
XFREE(mac, pkcs12->heap, DYNAMIC_TYPE_PKCS);
}
}
return ret;
}
/* 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 = WC_SHA;
kLen = WC_SHA_DIGEST_SIZE;
break;
#endif
#ifndef NO_SHA256
case SHA256h: /* 414 */
typeH = WC_SHA256;
kLen = WC_SHA256_DIGEST_SIZE;
break;
#endif
#ifdef WOLFSSL_SHA384
case SHA384h: /* 415 */
typeH = WC_SHA384;
kLen = WC_SHA384_DIGEST_SIZE;
break;
#endif
#ifdef WOLFSSL_SHA512
case SHA512h: /* 416 */
typeH = WC_SHA512;
kLen = WC_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, pkcs12->heap, 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
* return 0 on success and negative on failure.
*/
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 ret;
}
/* helper function to free WC_DerCertList */
static void freeCertList(WC_DerCertList* list, void* heap)
{
WC_DerCertList* current = list;
WC_DerCertList* next;
if (list == NULL) {
return;
}
while (current != NULL) {
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 freeDecCertList(WC_DerCertList** list, byte** pkey, word32* pkeySz,
byte** cert, word32* certSz, void* heap)
{
WC_DerCertList* current = *list;
WC_DerCertList* previous = NULL;
DecodedCert DeCert;
while (current != NULL) {
InitDecodedCert(&DeCert, current->buffer, current->bufferSz, 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) {
*list = current->next;
}
else {
previous->next = current->next;
}
FreeDecodedCert(&DeCert);
XFREE(current, heap, DYNAMIC_TYPE_PKCS);
break;
}
}
FreeDecodedCert(&DeCert);
previous = current;
current = current->next;
}
}
/* return 0 on success and negative 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)) {
ERROR_OUT(ASN_PARSE_E, exit_pk12par);
}
if ((ret = GetLength(data, &idx, &size, ci->dataSz)) < 0) {
goto exit_pk12par;
}
if ((ret = GetSequence(data, &idx, &size, ci->dataSz)) < 0) {
goto exit_pk12par;
}
if ((ret = GetShortInt(data, &idx, &number, ci->dataSz)) < 0) {
goto exit_pk12par;
}
if (number != 0) {
WOLFSSL_MSG("Expecting 0 for Integer with Encrypted PKCS12");
}
if ((ret = GetSequence(data, &idx, &size, ci->dataSz)) < 0) {
goto exit_pk12par;
}
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");
ERROR_OUT(ASN_PARSE_E, exit_pk12par);
}
/* decrypted content overwrites input buffer */
size = ci->dataSz - idx;
buf = (byte*)XMALLOC(size, pkcs12->heap, DYNAMIC_TYPE_PKCS);
if (buf == NULL) {
ERROR_OUT(MEMORY_E, exit_pk12par);
}
XMEMCPY(buf, data + idx, size);
if ((ret = DecryptContent(buf, size, psw, pswSz)) < 0) {
WOLFSSL_MSG("Decryption failed, algorithm not compiled in?");
goto exit_pk12par;
}
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)) {
ERROR_OUT(ASN_PARSE_E, exit_pk12par);
}
if ((ret = GetLength(data, &idx, &size, ci->dataSz)) <= 0) {
goto exit_pk12par;
}
if (data[idx++] != ASN_OCTET_STRING) {
ERROR_OUT(ASN_PARSE_E, exit_pk12par);
}
if ((ret = GetLength(data, &idx, &size, ci->dataSz)) < 0) {
goto exit_pk12par;
}
}
/* parse through bags in ContentInfo */
if ((ret = GetSequence(data, &idx, &totalSz, ci->dataSz)) < 0) {
goto exit_pk12par;
}
totalSz += idx;
while ((int)idx < totalSz) {
int bagSz;
if ((ret = GetSequence(data, &idx, &bagSz, ci->dataSz)) < 0) {
goto exit_pk12par;
}
bagSz += idx;
if ((ret = GetObjectId(data, &idx, &oid, oidIgnoreType,
ci->dataSz)) < 0) {
goto exit_pk12par;
}
switch (oid) {
case WC_PKCS12_KeyBag: /* 667 */
WOLFSSL_MSG("PKCS12 Key Bag found");
if (data[idx++] !=
(ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC)) {
ERROR_OUT(ASN_PARSE_E, exit_pk12par);
}
if ((ret = GetLength(data, &idx, &size, ci->dataSz)) <= 0) {
goto exit_pk12par;
}
if (*pkey == NULL) {
*pkey = (byte*)XMALLOC(size, pkcs12->heap,
DYNAMIC_TYPE_PUBLIC_KEY);
if (*pkey == NULL) {
ERROR_OUT(MEMORY_E, exit_pk12par);
}
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)) {
ERROR_OUT(ASN_PARSE_E, exit_pk12par);
}
if ((ret = GetLength(data, &idx, &size,
ci->dataSz)) < 0) {
goto exit_pk12par;
}
k = (byte*)XMALLOC(size, pkcs12->heap,
DYNAMIC_TYPE_PUBLIC_KEY);
if (k == NULL) {
ERROR_OUT(MEMORY_E, exit_pk12par);
}
XMEMCPY(k, data + idx, size);
/* overwrites input, be warned */
if ((ret = ToTraditionalEnc(k, size, psw, pswSz)) < 0) {
XFREE(k, pkcs12->heap, DYNAMIC_TYPE_PUBLIC_KEY);
goto exit_pk12par;
}
if (ret < size) {
/* shrink key buffer */
byte* tmp = (byte*)XMALLOC(ret, pkcs12->heap,
DYNAMIC_TYPE_PUBLIC_KEY);
if (tmp == NULL) {
XFREE(k, pkcs12->heap, DYNAMIC_TYPE_PUBLIC_KEY);
ERROR_OUT(MEMORY_E, exit_pk12par);
}
XMEMCPY(tmp, k, ret);
XFREE(k, pkcs12->heap, DYNAMIC_TYPE_PUBLIC_KEY);
k = tmp;
}
size = ret;
if (*pkey == NULL) {
*pkey = k;
*pkeySz = size;
}
else { /* only expecting one key */
XFREE(k, pkcs12->heap, DYNAMIC_TYPE_PUBLIC_KEY);
}
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)) {
ERROR_OUT(ASN_PARSE_E, exit_pk12par);
}
if ((ret = GetLength(data, &idx, &size, ci->dataSz)) < 0) {
goto exit_pk12par;
}
/* get cert bag type */
if ((ret = GetSequence(data, &idx, &size, ci->dataSz)) <0) {
goto exit_pk12par;
}
if ((ret = GetObjectId(data, &idx, &oid, oidIgnoreType,
ci->dataSz)) < 0) {
goto exit_pk12par;
}
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)) {
ERROR_OUT(ASN_PARSE_E, exit_pk12par);
}
if ((ret = GetLength(data, &idx, &size, ci->dataSz))
<= 0) {
goto exit_pk12par;
}
if (data[idx++] != ASN_OCTET_STRING) {
ERROR_OUT(ASN_PARSE_E, exit_pk12par);
}
if ((ret = GetLength(data, &idx, &size, ci->dataSz))
< 0) {
goto exit_pk12par;
}
break;
default:
WOLFSSL_MSG("Unknown PKCS12 cert bag type");
}
if (size + idx > (word32)bagSz) {
ERROR_OUT(ASN_PARSE_E, exit_pk12par);
}
/* list to hold all certs found */
node = (WC_DerCertList*)XMALLOC(sizeof(WC_DerCertList),
pkcs12->heap, DYNAMIC_TYPE_PKCS);
if (node == NULL) {
ERROR_OUT(MEMORY_E, exit_pk12par);
}
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);
ERROR_OUT(MEMORY_E, exit_pk12par);
}
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 */
if (*pkey != NULL) {
freeDecCertList(&certList, pkey, pkeySz, cert, certSz, pkcs12->heap);
}
/* if ca arg provided return certList, otherwise free it */
if (ca != NULL) {
*ca = certList;
}
else {
/* free list, not wanted */
freeCertList(certList, pkcs12->heap);
}
ret = 0; /* success */
exit_pk12par:
if (ret != 0) {
/* failure cleanup */
if (*pkey) {
XFREE(*pkey, pkcs12->heap, DYNAMIC_TYPE_PUBLIC_KEY);
*pkey = NULL;
}
if (buf) {
XFREE(buf, pkcs12->heap, DYNAMIC_TYPE_PKCS);
buf = NULL;
}
freeCertList(certList, pkcs12->heap);
}
return ret;
}
/* 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;
}
#undef ERROR_OUT
#endif /* !NO_ASN && !NO_PWDBASED */