wolfssl/wolfcrypt/src/pkcs12.c

2852 lines
85 KiB
C

/* pkcs12.c
*
* Copyright (C) 2006-2025 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
*/
/* PKCS#12 allows storage of key and certificates into containers */
#include <wolfssl/wolfcrypt/libwolfssl_sources.h>
#if defined(HAVE_PKCS12) && \
!defined(NO_ASN) && !defined(NO_PWDBASED) && !defined(NO_HMAC) && \
!defined(NO_CERTS)
#include <wolfssl/wolfcrypt/asn.h>
#include <wolfssl/wolfcrypt/asn_public.h>
#include <wolfssl/wolfcrypt/hmac.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>
#include <wolfssl/wolfcrypt/hash.h>
#define ERROR_OUT(err, eLabel) { ret = (err); goto eLabel; }
enum {
WC_PKCS12_DATA_OBJ_SZ = 11,
WC_PKCS12_MAC_SALT_SZ = 8
};
static const byte WC_PKCS12_ENCRYPTED_OID[] =
{0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x06};
static const byte WC_PKCS12_DATA_OID[] =
{0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01};
static const byte WC_PKCS12_CertBag_Type1_OID[] =
{0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x16, 0x01};
static const byte WC_PKCS12_CertBag_OID[] =
{0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0c, 0x0a, 0x01, 0x03};
static const byte WC_PKCS12_KeyBag_OID[] =
{0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0c, 0x0a, 0x01, 0x01};
static const byte WC_PKCS12_ShroudedKeyBag_OID[] =
{0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x0c, 0x0a, 0x01, 0x02};
typedef struct ContentInfo {
byte* data;
struct ContentInfo* next;
word32 encC; /* encryptedContent */
word32 dataSz;
int type; /* DATA / encrypted / enveloped */
} 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 iterations when creating HMAC key */
} MacData;
struct WC_PKCS12 {
void* heap;
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
};
/* for friendlyName, localKeyId .... */
typedef struct WC_PKCS12_ATTRIBUTE {
byte* data;
word32 oid;
word32 dataSz;
} WC_PKCS12_ATTRIBUTE;
WC_PKCS12* wc_PKCS12_new(void)
{
return wc_PKCS12_new_ex(NULL);
}
WC_PKCS12* wc_PKCS12_new_ex(void* heap)
{
WC_PKCS12* pkcs12 = (WC_PKCS12*)XMALLOC(sizeof(WC_PKCS12),
heap, DYNAMIC_TYPE_PKCS);
if (pkcs12 == NULL) {
WOLFSSL_MSG("Memory issue when creating WC_PKCS12 struct");
return NULL;
}
XMEMSET(pkcs12, 0, sizeof(WC_PKCS12));
pkcs12->heap = heap;
return pkcs12;
}
static void freeSafe(AuthenticatedSafe* safe, void* heap)
{
int i;
if (safe == NULL) {
return;
}
/* free content info structs */
for (i = (int)safe->numCI; i > 0; i--) {
ContentInfo* ci = safe->CI;
safe->CI = ci->next;
XFREE(ci, heap, DYNAMIC_TYPE_PKCS);
}
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) {
XFREE(pkcs12->signData->digest, heap, DYNAMIC_TYPE_DIGEST);
XFREE(pkcs12->signData->salt, heap, DYNAMIC_TYPE_SALT);
XFREE(pkcs12->signData, heap, DYNAMIC_TYPE_PKCS);
}
#ifdef ASN_BER_TO_DER
XFREE(pkcs12->der, pkcs12->heap, DYNAMIC_TYPE_PKCS);
XFREE(pkcs12->safeDer, pkcs12->heap, DYNAMIC_TYPE_PKCS);
#endif
XFREE(pkcs12, heap, DYNAMIC_TYPE_PKCS);
}
/* return 0 on success */
static int GetSafeContent(WC_PKCS12* pkcs12, const byte* input,
word32* idx, word32 maxIdx)
{
AuthenticatedSafe* safe;
word32 oid;
word32 localIdx = *idx;
int ret;
int size = 0;
byte tag;
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 (GetASNTag(input, &localIdx, &tag, maxIdx) < 0) {
freeSafe(safe, pkcs12->heap);
return ASN_PARSE_E;
}
if (tag != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC)) {
WOLFSSL_MSG("Unexpected tag in PKCS12 DER");
freeSafe(safe, pkcs12->heap);
return ASN_PARSE_E;
}
if (GetLength(input, &localIdx, &size, maxIdx) <= 0) {
freeSafe(safe, pkcs12->heap);
return ASN_PARSE_E;
}
switch (oid) {
case WC_PKCS12_ENCRYPTED_DATA:
WOLFSSL_MSG("Found PKCS12 OBJECT: ENCRYPTED DATA");
break;
case WC_PKCS12_DATA:
WOLFSSL_MSG("Found PKCS12 OBJECT: DATA");
/* get octets holding contents */
if (GetASNTag(input, &localIdx, &tag, maxIdx) < 0) {
freeSafe(safe, pkcs12->heap);
return ASN_PARSE_E;
}
if (tag != ASN_OCTET_STRING) {
WOLFSSL_MSG("Wrong tag with content PKCS12 type DATA");
freeSafe(safe, pkcs12->heap);
return ASN_PARSE_E;
}
if (GetLength(input, &localIdx, &size, maxIdx) <= 0) {
freeSafe(safe, pkcs12->heap);
return ASN_PARSE_E;
}
break;
}
safe->dataSz = (word32)size;
safe->data = (byte*)XMALLOC((size_t)size, pkcs12->heap, DYNAMIC_TYPE_PKCS);
if (safe->data == NULL) {
freeSafe(safe, pkcs12->heap);
return MEMORY_E;
}
XMEMCPY(safe->data, input + localIdx, (size_t)size);
*idx = localIdx;
localIdx = 0;
input = safe->data;
size = (int)safe->dataSz;
#ifdef ASN_BER_TO_DER
if (pkcs12->indefinite) {
if (wc_BerToDer(input, safe->dataSz, NULL,
&pkcs12->safeDersz) != WC_NO_ERR_TRACE(LENGTH_ONLY_E)) {
WOLFSSL_MSG("Not BER sequence");
freeSafe(safe, pkcs12->heap);
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);
if (ret < 0) {
freeSafe(safe, pkcs12->heap);
return ret;
}
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 */
{
int CISz;
ret = GetSequence(input, &localIdx, &CISz, (word32)size);
if (ret < 0) {
freeSafe(safe, pkcs12->heap);
return ASN_PARSE_E;
}
CISz += (int)localIdx;
while (localIdx < (word32)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, (word32)size)) < 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,
(word32)size)) < 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 = (int)oid;
ci->dataSz = (word32)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;
}
/* parse optional mac data
* return 0 on success */
static int GetSignData(WC_PKCS12* pkcs12, const byte* mem, word32* idx,
word32 totalSz)
{
MacData* mac;
word32 curIdx = *idx;
word32 oid = 0;
int size, ret;
byte tag;
/* Digest Info : Sequence
* DigestAlgorithmIdentifier
* Digest
*/
if (GetSequence(mem, &curIdx, &size, totalSz) <= 0) {
WOLFSSL_MSG("Failed to get PKCS12 sequence");
return ASN_PARSE_E;
}
#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 (GetASNTag(mem, &curIdx, &tag, totalSz) < 0) {
XFREE(mac, pkcs12->heap, DYNAMIC_TYPE_PKCS);
return ASN_PARSE_E;
}
if (tag != ASN_OCTET_STRING) {
WOLFSSL_MSG("Failed to get digest");
XFREE(mac, pkcs12->heap, DYNAMIC_TYPE_PKCS);
return ASN_PARSE_E;
}
if (GetLength(mem, &curIdx, &size, totalSz) <= 0) {
XFREE(mac, pkcs12->heap, DYNAMIC_TYPE_PKCS);
return ASN_PARSE_E;
}
mac->digestSz = (word32)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 (GetASNTag(mem, &curIdx, &tag, totalSz) < 0) {
ERROR_OUT(ASN_PARSE_E, exit_gsd);
}
if (tag != 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 = (word32)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 iterations, default to 1 */
mac->itt = WC_PKCS12_MAC_DEFAULT;
if (curIdx < totalSz) {
int number = 0;
if (GetShortInt(mem, &curIdx, &number, totalSz) >= 0) {
/* found a iteration value */
mac->itt = number;
}
}
#ifdef WOLFSSL_DEBUG_PKCS12
printf("\t\tITERATIONS : %d\n", mac->itt);
#endif
*idx = curIdx;
pkcs12->signData = mac;
ret = 0; /* success */
exit_gsd:
/* failure cleanup */
if (ret != 0) {
if (mac) {
XFREE(mac->digest, pkcs12->heap, DYNAMIC_TYPE_DIGEST);
XFREE(mac, pkcs12->heap, DYNAMIC_TYPE_PKCS);
}
}
return ret;
}
/* expects PKCS12 signData to be set up with OID
*
* returns the size of mac created on success. A negative value will be returned
* in the case that an error happened.
*/
static int wc_PKCS12_create_mac(WC_PKCS12* pkcs12, byte* data, word32 dataSz,
const byte* psw, word32 pswSz, byte* out, word32 outSz)
{
Hmac hmac;
MacData* mac;
int ret, kLen;
enum wc_HashType hashT;
int idx = 0;
int id = 3; /* value from RFC 7292 indicating key is used for MAC */
word32 i;
byte unicodePasswd[MAX_UNICODE_SZ];
byte key[PKCS_MAX_KEY_SIZE];
if (pkcs12 == NULL || pkcs12->signData == NULL || data == NULL ||
out == NULL) {
return BAD_FUNC_ARG;
}
mac = pkcs12->signData;
/* 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 */
hashT = wc_OidGetHash((int)mac->oid);
if (hashT == WC_HASH_TYPE_NONE) {
ForceZero(unicodePasswd, MAX_UNICODE_SZ);
WOLFSSL_MSG("Unsupported hash used");
return BAD_FUNC_ARG;
}
kLen = wc_HashGetDigestSize(hashT);
/* check out buffer is large enough */
if (kLen < 0 || outSz < (word32)kLen) {
ForceZero(unicodePasswd, MAX_UNICODE_SZ);
return BAD_FUNC_ARG;
}
/* idx contains size of unicodePasswd */
ret = wc_PKCS12_PBKDF_ex(key, unicodePasswd, idx, mac->salt, (int)mac->saltSz,
mac->itt, kLen, (int)hashT, id, pkcs12->heap);
ForceZero(unicodePasswd, MAX_UNICODE_SZ);
if (ret < 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, (int)hashT, key, (word32)kLen);
if (ret == 0)
ret = wc_HmacUpdate(&hmac, data, dataSz);
if (ret == 0)
ret = wc_HmacFinal(&hmac, out);
wc_HmacFree(&hmac);
if (ret != 0)
return ret;
return kLen; /* same as digest size */
}
/* 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)
{
MacData* mac;
int ret;
byte digest[WC_MAX_DIGEST_SIZE];
if (pkcs12 == NULL || pkcs12->signData == NULL || data == NULL) {
return BAD_FUNC_ARG;
}
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 > WC_MAX_DIGEST_SIZE) {
WOLFSSL_MSG("PKCS12 max digest size too small");
return BAD_FUNC_ARG;
}
if ((ret = wc_PKCS12_create_mac(pkcs12, data, dataSz, psw, pswSz,
digest, WC_MAX_DIGEST_SIZE)) < 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);
}
int wc_PKCS12_verify_ex(WC_PKCS12* pkcs12, const byte* psw, word32 pswSz)
{
if (pkcs12 == NULL || pkcs12->safe == NULL) {
return BAD_FUNC_ARG;
}
return wc_PKCS12_verify(pkcs12, pkcs12->safe->data, pkcs12->safe->dataSz,
psw, pswSz);
}
/* Convert DER format stored in der buffer to WC_PKCS12 struct
* Puts the raw contents of Content Info into structure without completely
* 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");
if (der == NULL || pkcs12 == NULL) {
return BAD_FUNC_ARG;
}
totalSz = derSz;
if (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;
}
#ifdef ASN_BER_TO_DER
if (size == 0) {
if (wc_BerToDer(der, totalSz, NULL,
(word32*)&size) != WC_NO_ERR_TRACE(LENGTH_ONLY_E)) {
WOLFSSL_MSG("Not BER sequence");
return ASN_PARSE_E;
}
pkcs12->der = (byte*)XMALLOC((size_t)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;
pkcs12->derSz = (word32)size;
totalSz = (word32)size;
idx = 0;
if (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);
#endif
if (version != WC_PKCS12_VERSION_DEFAULT) {
WOLFSSL_MSG("PKCS12 unsupported version!");
WOLFSSL_ERROR_VERBOSE(ASN_VERSION_E);
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, (word32)size + idx)) < 0) {
WOLFSSL_MSG("GetSafeContent error");
return ret;
}
#ifdef ASN_BER_TO_DER
/* If indef, skip EOF */
if (pkcs12->indefinite) {
while((idx < totalSz) && (der[idx] == ASN_EOC)) {
idx+=1;
}
}
#endif
/* 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;
}
#ifndef NO_FILESYSTEM
/* Parse the DER-encoded PKCS #12 object in the provided file. Populate the
* WC_PKCS12 object pointed to by the passed in pointer, allocating the object
* if necessary.
*
* file : path to PKCS #12 file.
* pkcs12: pointer to a pointer to a WC_PKCS12 object to populate. If *pkcs12 is
* NULL, this function will allocate a new WC_PKCS12.
* return 0 on success and negative on failure.
*/
int wc_d2i_PKCS12_fp(const char* file, WC_PKCS12** pkcs12)
{
int ret = 0;
byte* buf = NULL;
size_t bufSz = 0;
WC_PKCS12* tmpPkcs12 = NULL;
int callerAlloc = 1;
WOLFSSL_ENTER("wc_d2i_PKCS12_fp");
if (pkcs12 == NULL) {
WOLFSSL_MSG("pkcs12 parameter NULL.");
ret = BAD_FUNC_ARG;
}
if (ret == 0)
ret = wc_FileLoad(file, &buf, &bufSz, NULL);
if (ret == 0) {
if (*pkcs12 == NULL) {
tmpPkcs12 = wc_PKCS12_new();
if (tmpPkcs12 == NULL) {
WOLFSSL_MSG("Failed to allocate PKCS12 object.");
ret = MEMORY_E;
}
else {
*pkcs12 = tmpPkcs12;
callerAlloc = 0;
}
}
}
if (ret == 0) {
ret = wc_d2i_PKCS12(buf, (word32)bufSz, *pkcs12);
if (ret != 0) {
WOLFSSL_MSG("wc_d2i_PKCS12 failed.");
}
}
if (ret != 0 && callerAlloc == 0 && *pkcs12 != NULL) {
wc_PKCS12_free(*pkcs12);
*pkcs12 = NULL;
}
XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
WOLFSSL_LEAVE("wc_d2i_PKCS12_fp", ret);
return ret;
}
#endif /* NO_FILESYSTEM */
/* Convert WC_PKCS12 struct to allocated DER buffer.
* pkcs12 : non-null pkcs12 pointer
* der : pointer-pointer to der buffer. If NULL space will be
* allocated for der, which must be freed by application.
* derSz : size of buffer passed in when der is not NULL. NULL arg disables
* sanity checks on buffer read/writes. Max size gets set to derSz when
* the "der" buffer passed in is NULL and LENGTH_ONLY_E is returned.
* return size of DER on success and negative on failure.
*/
int wc_i2d_PKCS12(WC_PKCS12* pkcs12, byte** der, int* derSz)
{
int ret = 0;
word32 seqSz = 0, verSz = 0, totalSz = 0, idx = 0, sdBufSz = 0;
byte *buf = NULL;
byte ver[MAX_VERSION_SZ];
byte seq[MAX_SEQ_SZ];
byte *sdBuf = NULL;
if ((pkcs12 == NULL) || (pkcs12->safe == NULL) ||
(der == NULL && derSz == NULL)) {
return BAD_FUNC_ARG;
}
/* Create the MAC portion */
if (pkcs12->signData != NULL) {
MacData *mac = (MacData*)pkcs12->signData;
word32 innerSz = 0;
word32 outerSz = 0;
/* get exact size */
{
byte ASNLENGTH[MAX_LENGTH_SZ];
byte ASNSHORT[MAX_SHORT_SZ];
byte ASNALGO[MAX_ALGO_SZ];
word32 tmpIdx = 0;
/* algo id */
innerSz += SetAlgoID((int)mac->oid, ASNALGO, oidHashType, 0);
/* Octet string holding digest */
innerSz += ASN_TAG_SZ;
innerSz += SetLength(mac->digestSz, ASNLENGTH);
innerSz += mac->digestSz;
/* salt */
outerSz += ASN_TAG_SZ;
outerSz += SetLength(mac->saltSz, ASNLENGTH);
outerSz += mac->saltSz;
/* MAC iterations */
ret = SetShortInt(ASNSHORT, &tmpIdx, (word32)mac->itt, MAX_SHORT_SZ);
if (ret >= 0) {
outerSz += (word32)ret;
ret = 0;
}
else {
return ret;
}
/* sequence of inner data */
outerSz += SetSequence(innerSz, seq);
outerSz += innerSz;
}
sdBufSz = outerSz + SetSequence(outerSz, seq);
sdBuf = (byte*)XMALLOC(sdBufSz, pkcs12->heap, DYNAMIC_TYPE_PKCS);
if (sdBuf == NULL) {
ret = MEMORY_E;
}
if (ret == 0) {
idx += SetSequence(outerSz, sdBuf);
idx += SetSequence(innerSz, &sdBuf[idx]);
/* Set Algorithm Identifier */
{
word32 algoIdSz;
algoIdSz = SetAlgoID((int)mac->oid, &sdBuf[idx], oidHashType, 0);
if (algoIdSz == 0) {
ret = ALGO_ID_E;
}
else {
idx += algoIdSz;
}
}
}
if (ret == 0) {
/* Octet string holding digest */
idx += SetOctetString(mac->digestSz, &sdBuf[idx]);
XMEMCPY(&sdBuf[idx], mac->digest, mac->digestSz);
idx += mac->digestSz;
/* Set salt */
idx += SetOctetString(mac->saltSz, &sdBuf[idx]);
XMEMCPY(&sdBuf[idx], mac->salt, mac->saltSz);
idx += mac->saltSz;
/* MAC iterations */
{
int tmpSz;
word32 tmpIdx = 0;
byte ar[MAX_SHORT_SZ];
tmpSz = SetShortInt(ar, &tmpIdx, (word32)mac->itt, MAX_SHORT_SZ);
if (tmpSz < 0) {
ret = tmpSz;
}
else {
XMEMCPY(&sdBuf[idx], ar, (size_t)tmpSz);
}
}
totalSz += sdBufSz;
}
}
/* Calculate size of der */
if (ret == 0) {
totalSz += pkcs12->safe->dataSz;
totalSz += 4; /* Octet string */
totalSz += 4; /* Element */
totalSz += 2U + (word32)sizeof(WC_PKCS12_DATA_OID);
totalSz += 4; /* Seq */
ret = SetMyVersion(WC_PKCS12_VERSION_DEFAULT, ver, FALSE);
if (ret > 0) {
verSz = (word32)ret;
ret = 0; /* value larger than 0 is success */
totalSz += verSz;
seqSz = SetSequence(totalSz, seq);
totalSz += seqSz;
/* check if getting length only */
if (der == NULL && derSz != NULL) {
*derSz = (int)totalSz;
XFREE(sdBuf, pkcs12->heap, DYNAMIC_TYPE_PKCS);
return WC_NO_ERR_TRACE(LENGTH_ONLY_E);
}
if (*der == NULL) {
/* Allocate if requested */
buf = (byte*)XMALLOC(totalSz, NULL, DYNAMIC_TYPE_PKCS);
}
else {
buf = *der;
/* sanity check on buffer size if passed in */
if (derSz != NULL) {
if (*derSz < (int)totalSz) {
WOLFSSL_MSG("Buffer passed in is too small");
ret = BUFFER_E;
}
}
}
}
}
if (buf == NULL) {
ret = MEMORY_E;
}
if (ret == 0) {
idx = 0;
/* Copy parts to buf */
XMEMCPY(&buf[idx], seq, seqSz);
idx += seqSz;
XMEMCPY(&buf[idx], ver, verSz);
idx += verSz;
seqSz = SetSequence(totalSz - sdBufSz - idx - 4, seq);
XMEMCPY(&buf[idx], seq, seqSz);
idx += seqSz;
/* OID */
idx += (word32)SetObjectId(sizeof(WC_PKCS12_DATA_OID), &buf[idx]);
XMEMCPY(&buf[idx], WC_PKCS12_DATA_OID, sizeof(WC_PKCS12_DATA_OID));
idx += (word32)sizeof(WC_PKCS12_DATA_OID);
/* Element */
buf[idx++] = ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC;
idx += SetLength(totalSz - sdBufSz - idx - 3, &buf[idx]);
/* Octet string */
idx += SetOctetString(totalSz - sdBufSz - idx - 4, &buf[idx]);
XMEMCPY(&buf[idx], pkcs12->safe->data, pkcs12->safe->dataSz);
idx += pkcs12->safe->dataSz;
if (pkcs12->signData != NULL) {
XMEMCPY(&buf[idx], sdBuf, sdBufSz);
}
if (*der == NULL) {
/* Point to start of data allocated for DER */
*der = buf;
}
else {
/* Increment pointer to byte past DER */
*der = &buf[totalSz];
}
/* Return size of der */
ret = (int)totalSz;
}
XFREE(sdBuf, pkcs12->heap, DYNAMIC_TYPE_PKCS);
/* Allocation of buf was the last time ret could be a failure,
* so no need to free here */
return ret;
}
/* helper function to free WC_DerCertList */
void wc_FreeCertList(WC_DerCertList* list, void* heap)
{
WC_DerCertList* current = list;
WC_DerCertList* next;
if (list == NULL) {
return;
}
while (current != NULL) {
next = current->next;
XFREE(current->buffer, heap, DYNAMIC_TYPE_PKCS);
XFREE(current, heap, DYNAMIC_TYPE_PKCS);
current = next;
}
(void)heap;
}
static WARN_UNUSED_RESULT int freeDecCertList(WC_DerCertList** list,
byte** pkey, word32* pkeySz, byte** cert, word32* certSz, void* heap)
{
WC_DerCertList* current = *list;
WC_DerCertList* previous = NULL;
#ifdef WOLFSSL_SMALL_STACK
DecodedCert *DeCert = (DecodedCert *)XMALLOC(
sizeof(*DeCert), heap, DYNAMIC_TYPE_PKCS);
if (DeCert == NULL)
return MEMORY_E;
#else
DecodedCert DeCert[1];
#endif
while (current != NULL) {
InitDecodedCert(DeCert, current->buffer, current->bufferSz, heap);
if (ParseCertRelative(DeCert, CERT_TYPE, NO_VERIFY, NULL, NULL) == 0) {
if (wc_CheckPrivateKeyCert(*pkey, *pkeySz, DeCert, 0, heap) == 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;
}
#ifdef WOLFSSL_SMALL_STACK
XFREE(DeCert, heap, DYNAMIC_TYPE_PKCS);
#endif
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_ConcatenateContent(WC_PKCS12* pkcs12,byte* mergedData,
word32* mergedSz, byte* in, word32 inSz)
{
byte* oldContent;
word32 oldContentSz;
(void)pkcs12;
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 (mergedData != NULL) {
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 = 0;
byte tag = 0;
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 += (word32)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 == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC)) {
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 concatenated strings */
word32 mergedSz = 0; /* total size of merged strings */
int encryptedContentSz = 0;
int originalEncSz = 0;
int ret = 0;
word32 saveIdx;
byte tag;
saveIdx = *idx;
if (GetLength(data, idx, &originalEncSz, dataSz) < 0) {
ret = ASN_PARSE_E;
}
/* Loop through octet strings and concatenate 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((size_t)encryptedContentSz,
pkcs12->heap, DYNAMIC_TYPE_PKCS);
if (mergedData == NULL) {
ret = MEMORY_E;
}
}
mergedData = PKCS12_ConcatenateContent(pkcs12, mergedData,
&mergedSz, &data[*idx], (word32)encryptedContentSz);
if (mergedData == NULL) {
ret = MEMORY_E;
}
}
if (ret != 0) {
break;
}
*idx += (word32)encryptedContentSz;
}
if (ret == 0) {
*idx = saveIdx;
*idx += SetLength(mergedSz, &data[*idx]);
if (mergedSz > 0) {
/* Copy over concatenated 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.
* 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)
{
return wc_PKCS12_parse_ex(pkcs12, psw, pkey, pkeySz, cert, certSz, ca, 0);
}
/* 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
* keepKeyHeader : 0 removes PKCS8 header, other than 0 keeps PKCS8 header
*/
int wc_PKCS12_parse_ex(WC_PKCS12* pkcs12, const char* psw,
byte** pkey, word32* pkeySz, byte** cert, word32* certSz,
WC_DerCertList** ca, int keepKeyHeader)
{
ContentInfo* ci = NULL;
WC_DerCertList* certList = NULL;
WC_DerCertList* tailList = NULL;
byte* buf = NULL;
word32 i, oid;
word32 algId;
int ret, pswSz;
#ifdef ASN_BER_TO_DER
int curIdx;
#endif
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, (word32)pswSz)) != 0) {
WOLFSSL_MSG("PKCS12 Bad MAC on verify");
WOLFSSL_LEAVE("wc_PKCS12_parse verify ", ret);
(void)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;
byte tag;
data = ci->data;
if (ci->type == WC_PKCS12_ENCRYPTED_DATA) {
int number;
WOLFSSL_MSG("Decrypting PKCS12 Content Info Container");
if (GetASNTag(data, &idx, &tag, ci->dataSz) < 0) {
ERROR_OUT(ASN_PARSE_E, exit_pk12par);
}
if (tag != (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);
}
#ifdef ASN_BER_TO_DER
curIdx = (int)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 = (word32)curIdx;
#endif
/* decrypted content overwrites input buffer */
size = (int)(ci->dataSz - idx);
buf = (byte*)XMALLOC((size_t)size, pkcs12->heap, DYNAMIC_TYPE_PKCS);
if (buf == NULL) {
ERROR_OUT(MEMORY_E, exit_pk12par);
}
XMEMCPY(buf, data + idx, (size_t)size);
if ((ret = DecryptContent(buf, (word32)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");
if (GetASNTag(data, &idx, &tag, ci->dataSz) < 0) {
ERROR_OUT(ASN_PARSE_E, exit_pk12par);
}
if (tag != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC)) {
ERROR_OUT(ASN_PARSE_E, exit_pk12par);
}
if (GetLength(data, &idx, &size, ci->dataSz) <= 0) {
ERROR_OUT(ASN_PARSE_E, exit_pk12par);
}
if (GetASNTag(data, &idx, &tag, ci->dataSz) < 0) {
ERROR_OUT(ASN_PARSE_E, exit_pk12par);
}
if (tag != 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 += (int)idx;
while ((int)idx < totalSz) {
int bagSz;
if ((ret = GetSequence(data, &idx, &bagSz, ci->dataSz)) < 0) {
goto exit_pk12par;
}
bagSz += (int)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 (GetASNTag(data, &idx, &tag, ci->dataSz) < 0) {
ERROR_OUT(ASN_PARSE_E, exit_pk12par);
}
if (tag != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC)) {
ERROR_OUT(ASN_PARSE_E, exit_pk12par);
}
if ((ret = GetLength(data, &idx, &size, ci->dataSz)) <= 0) {
if (ret == 0)
ret = ASN_PARSE_E;
goto exit_pk12par;
}
if (*pkey == NULL) {
*pkey = (byte*)XMALLOC((size_t)size, pkcs12->heap,
DYNAMIC_TYPE_PUBLIC_KEY);
if (*pkey == NULL) {
ERROR_OUT(MEMORY_E, exit_pk12par);
}
XMEMCPY(*pkey, data + idx, (size_t)size);
if (keepKeyHeader) {
*pkeySz = (word32)size;
}
else {
*pkeySz = (word32)ToTraditional_ex(*pkey,
(word32)size, &algId);
}
}
#ifdef WOLFSSL_DEBUG_PKCS12
{
byte* p;
for (printf("\tKey = "), p = (byte*)*pkey;
p < (byte*)*pkey + size;
printf("%02X", *p), p++);
printf("\n");
}
#endif
idx += (word32)size;
break;
case WC_PKCS12_ShroudedKeyBag: /* 668 */
{
byte* k;
WOLFSSL_MSG("PKCS12 Shrouded Key Bag found");
if (GetASNTag(data, &idx, &tag, ci->dataSz) < 0) {
ERROR_OUT(ASN_PARSE_E, exit_pk12par);
}
if (tag != (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_t)size, pkcs12->heap,
DYNAMIC_TYPE_PUBLIC_KEY);
if (k == NULL) {
ERROR_OUT(MEMORY_E, exit_pk12par);
}
XMEMCPY(k, data + idx, (size_t)size);
/* overwrites input, be warned */
if (keepKeyHeader) {
if ((ret = wc_DecryptPKCS8Key(k, (word32)size, psw,
pswSz)) < 0) {
XFREE(k, pkcs12->heap, DYNAMIC_TYPE_PUBLIC_KEY);
goto exit_pk12par;
}
}
else {
if ((ret = ToTraditionalEnc(k, (word32)size, psw,
pswSz, &algId)) < 0) {
XFREE(k, pkcs12->heap, DYNAMIC_TYPE_PUBLIC_KEY);
goto exit_pk12par;
}
}
if (ret < size) {
/* shrink key buffer */
byte* tmp = (byte*)XMALLOC((size_t)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, (size_t)ret);
XFREE(k, pkcs12->heap, DYNAMIC_TYPE_PUBLIC_KEY);
k = tmp;
}
size = ret;
if (*pkey == NULL) {
*pkey = k;
*pkeySz = (word32)size;
}
else { /* only expecting one key */
XFREE(k, pkcs12->heap, DYNAMIC_TYPE_PUBLIC_KEY);
}
idx += (word32)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 (GetASNTag(data, &idx, &tag, ci->dataSz) < 0) {
ERROR_OUT(ASN_PARSE_E, exit_pk12par);
}
if (tag != (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 (GetASNTag(data, &idx, &tag, ci->dataSz) < 0) {
ERROR_OUT(ASN_PARSE_E, exit_pk12par);
}
if (tag != (ASN_CONSTRUCTED |
ASN_CONTEXT_SPECIFIC)) {
ERROR_OUT(ASN_PARSE_E, exit_pk12par);
}
if ((ret = GetLength(data, &idx, &size, ci->dataSz))
<= 0) {
if (ret == 0)
ret = ASN_PARSE_E;
goto exit_pk12par;
}
if (GetASNTag(data, &idx, &tag, ci->dataSz) < 0) {
ERROR_OUT(ASN_PARSE_E, exit_pk12par);
}
if (tag != 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 + (int)idx > 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_t)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_t)size);
node->bufferSz = (word32)size;
/* put the new node into the list */
if (certList != NULL) {
WOLFSSL_MSG("Pushing new cert onto queue");
tailList->next = node;
tailList = node;
}
else {
certList = node;
tailList = node;
}
/* on to next */
idx += (word32)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 = (word32)bagSz; /* skip for now */
}
}
/* free temporary buffer */
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) {
ret = freeDecCertList(&certList, pkey, pkeySz, cert, certSz,
pkcs12->heap);
if (ret < 0)
goto exit_pk12par;
}
/* if ca arg provided return certList, otherwise free it */
if (ca != NULL) {
*ca = certList;
}
else {
/* free list, not wanted */
wc_FreeCertList(certList, pkcs12->heap);
}
(void)tailList; /* not used */
ret = 0; /* success */
exit_pk12par:
if (ret != 0) {
/* failure cleanup */
if (*pkey) {
XFREE(*pkey, pkcs12->heap, DYNAMIC_TYPE_PUBLIC_KEY);
*pkey = NULL;
}
XFREE(buf, pkcs12->heap, DYNAMIC_TYPE_PKCS);
buf = NULL;
wc_FreeCertList(certList, pkcs12->heap);
}
return ret;
}
/* Helper function to get parameters for key and cert encryptions */
static int wc_PKCS12_get_enc_params(int inAlgo, int* vPKCS, int* outAlgo,
int* blkOid, int* hmacOid)
{
int ret = 0;
if (inAlgo == PBE_SHA1_RC4_128) {
*vPKCS = 1; /* PKCS#12 */
*outAlgo = PBE_SHA1_RC4_128;
*blkOid = 0; /* Unused */
*hmacOid = 0; /* Use SHA1 as default */
}
else if (inAlgo == PBE_SHA1_DES) {
*vPKCS = PKCS5;
*outAlgo = PBES1_SHA1_DES;
*blkOid = 0; /* Unused */
*hmacOid = 0; /* Use SHA1 as default */
}
else if (inAlgo == PBE_SHA1_DES3) {
*vPKCS = 1; /* PKCS#12 */
*outAlgo = PBE_SHA1_DES3;
*blkOid = 0; /* Unused */
*hmacOid = 0; /* Use SHA1 as default */
}
else if (inAlgo == PBE_AES256_CBC) {
*vPKCS = PKCS5;
*outAlgo = PBES2;
*blkOid = AES256CBCb;
*hmacOid = HMAC_SHA256_OID;
}
else if (inAlgo == PBE_AES128_CBC) {
*vPKCS = PKCS5;
*outAlgo = PBES2;
*blkOid = AES128CBCb;
*hmacOid = HMAC_SHA256_OID;
}
else {
WOLFSSL_MSG("Unsupported algorithm for PKCS12 encryption");
ret = ALGO_ID_E;
}
return ret;
}
/* Helper function to shroud keys.
*
* pkcs12 structure to use with shrouding key
* rng random number generator used
* out buffer to hold results
* outSz size of out buffer
* key key that is going to be shrouded
* keySz size of key buffer
* vAlgo algorithm version
* pass password to use
* passSz size of pass buffer
* itt number of iterations
*
* returns the size of the shrouded key on success
*/
static int wc_PKCS12_shroud_key(WC_PKCS12* pkcs12, WC_RNG* rng,
byte* out, word32* outSz, byte* key, word32 keySz, int vAlgo,
const char* pass, int passSz, int itt)
{
void* heap;
word32 tmpIdx = 0;
word32 sz;
word32 totalSz = 0;
int ret;
byte* pkcs8Key = NULL;
byte salt[PKCS5V2_SALT_SZ]; /* PKCS5V2_SALT_SZ > PKCS5_SALT_SZ */
word32 saltSz = 0;
int vPKCS = -1;
int outAlgo = -1;
int blkOid = 0;
int hmacOid = 0;
if (outSz == NULL || pkcs12 == NULL || rng == NULL || key == NULL ||
pass == NULL) {
return BAD_FUNC_ARG;
}
heap = wc_PKCS12_GetHeap(pkcs12);
/* check if trying to get size */
if (out != NULL) {
tmpIdx += MAX_LENGTH_SZ + 1; /* save room for length and tag (+1) */
sz = *outSz - tmpIdx;
pkcs8Key = out + tmpIdx;
}
/* case of no encryption */
if (vAlgo < 0) {
const byte* curveOID = NULL;
word32 oidSz = 0;
int algoID;
WOLFSSL_MSG("creating PKCS12 Key Bag");
/* check key type and get OID if ECC */
if ((ret = wc_GetKeyOID(key, keySz, &curveOID, &oidSz, &algoID, heap))
< 0) {
return ret;
}
/* PKCS#8 wrapping around key */
ret = wc_CreatePKCS8Key(pkcs8Key, &sz, key, keySz, algoID, curveOID,
oidSz);
}
else {
WOLFSSL_MSG("creating PKCS12 Shrouded Key Bag");
if ((ret = wc_PKCS12_get_enc_params(vAlgo, &vPKCS, &outAlgo, &blkOid,
&hmacOid)) < 0) {
return ret;
}
saltSz = (outAlgo != PBES2) ? PKCS5_SALT_SZ : PKCS5V2_SALT_SZ;
if ((ret = wc_RNG_GenerateBlock(rng, salt, saltSz)) < 0) {
return ret;
}
ret = TraditionalEnc_ex(key, keySz, pkcs8Key, &sz, pass, passSz,
vPKCS, outAlgo, blkOid, salt, saltSz, itt, hmacOid, rng, heap);
}
if (ret == WC_NO_ERR_TRACE(LENGTH_ONLY_E)) {
*outSz = sz + MAX_LENGTH_SZ + 1;
return WC_NO_ERR_TRACE(LENGTH_ONLY_E);
}
if (ret < 0) {
return ret;
}
totalSz += (word32)ret;
/* out should not be null at this point but check before writing */
if (out == NULL) {
return BAD_FUNC_ARG;
}
/* rewind index and set tag and length */
tmpIdx -= MAX_LENGTH_SZ + 1;
sz = (word32)SetExplicit(0, (word32)ret, out + tmpIdx, 0);
tmpIdx += sz; totalSz += sz;
XMEMMOVE(out + tmpIdx, out + MAX_LENGTH_SZ + 1, (size_t)ret);
return (int)totalSz;
}
/* Helper function to create key bag.
*
* pkcs12 structure to use with key bag
* rng random number generator used
* out buffer to hold results
* outSz size of out buffer
* key key that is going into key bag
* keySz size of key buffer
* algo algorithm version
* iter number of iterations
* pass password to use
* passSz size of pass buffer
*
* returns the size of the key bag on success
*/
static int wc_PKCS12_create_key_bag(WC_PKCS12* pkcs12, WC_RNG* rng,
byte* out, word32* outSz, byte* key, word32 keySz, int algo, int iter,
char* pass, int passSz)
{
void* heap;
byte* tmp;
word32 length = 0;
word32 idx = 0;
word32 totalSz = 0;
word32 sz;
word32 i;
word32 tmpSz;
int ret;
/* get max size for shrouded key */
ret = wc_PKCS12_shroud_key(pkcs12, rng, NULL, &length, key, keySz,
algo, pass, passSz, iter);
if (ret != WC_NO_ERR_TRACE(LENGTH_ONLY_E) && ret < 0) {
return ret;
}
if (out == NULL) {
*outSz = MAX_SEQ_SZ + WC_PKCS12_DATA_OBJ_SZ + 1 + MAX_LENGTH_SZ +
length;
return WC_NO_ERR_TRACE(LENGTH_ONLY_E);
}
heap = wc_PKCS12_GetHeap(pkcs12);
/* leave room for sequence */
idx += MAX_SEQ_SZ;
if (algo < 0) { /* not encrypted */
out[idx++] = ASN_OBJECT_ID; totalSz++;
sz = SetLength(sizeof(WC_PKCS12_KeyBag_OID), out + idx);
idx += sz; totalSz += sz;
for (i = 0; i < sizeof(WC_PKCS12_KeyBag_OID); i++) {
out[idx++] = WC_PKCS12_KeyBag_OID[i]; totalSz++;
}
}
else { /* encrypted */
out[idx++] = ASN_OBJECT_ID; totalSz++;
sz = SetLength(sizeof(WC_PKCS12_ShroudedKeyBag_OID), out + idx);
idx += sz; totalSz += sz;
for (i = 0; i < sizeof(WC_PKCS12_ShroudedKeyBag_OID); i++) {
out[idx++] = WC_PKCS12_ShroudedKeyBag_OID[i]; totalSz++;
}
}
/* shroud key */
tmp = (byte*)XMALLOC(length, heap, DYNAMIC_TYPE_TMP_BUFFER);
if (tmp == NULL) {
return MEMORY_E;
}
ret = wc_PKCS12_shroud_key(pkcs12, rng, tmp, &length, key, keySz,
algo, pass, passSz, iter);
if (ret < 0) {
XFREE(tmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
return ret;
}
length = (word32)ret;
XMEMCPY(out + idx, tmp, (size_t)length);
XFREE(tmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
totalSz += length;
/* set beginning sequence */
tmpSz = SetSequence(totalSz, out);
XMEMMOVE(out + tmpSz, out + MAX_SEQ_SZ, totalSz);
(void)heap;
return (int)(totalSz + tmpSz);
}
/* Helper function to create cert bag.
*
* pkcs12 structure to use with cert bag
* out buffer to hold results
* outSz size of out buffer
* cert cert that is going into cert bag
* certSz size of cert buffer
*
* returns the size of the cert bag on success
*/
static int wc_PKCS12_create_cert_bag(WC_PKCS12* pkcs12,
byte* out, word32* outSz, byte* cert, word32 certSz)
{
word32 length = 0;
word32 idx = 0;
word32 totalSz = 0;
word32 sz;
int WC_CERTBAG_OBJECT_ID = 13;
int WC_CERTBAG1_OBJECT_ID = 12;
word32 i;
word32 tmpSz;
if (out == NULL) {
*outSz = (word32)(MAX_SEQ_SZ + WC_CERTBAG_OBJECT_ID + 1 + MAX_LENGTH_SZ +
MAX_SEQ_SZ + WC_CERTBAG1_OBJECT_ID + 1 + MAX_LENGTH_SZ + 1 +
MAX_LENGTH_SZ + (int)certSz);
return WC_NO_ERR_TRACE(LENGTH_ONLY_E);
}
/* check buffer size able to handle max size */
if (*outSz < (word32)(MAX_SEQ_SZ + WC_CERTBAG_OBJECT_ID + 1 + MAX_LENGTH_SZ +
MAX_SEQ_SZ + WC_CERTBAG1_OBJECT_ID + 1 + MAX_LENGTH_SZ + 1 +
MAX_LENGTH_SZ + (int)certSz)) {
return BUFFER_E;
}
/* save room for sequence */
idx += MAX_SEQ_SZ;
/* objectId WC_PKCS12_CertBag */
out[idx++] = ASN_OBJECT_ID; totalSz++;
sz = SetLength(sizeof(WC_PKCS12_CertBag_OID), out + idx);
idx += sz; totalSz += sz;
for (i = 0; i < sizeof(WC_PKCS12_CertBag_OID); i++) {
out[idx++] = WC_PKCS12_CertBag_OID[i]; totalSz++;
}
/**** Cert Bag type 1 ****/
out[idx++] = (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC); totalSz++;
/* save room for length and sequence */
idx += MAX_LENGTH_SZ;
idx += MAX_SEQ_SZ;
/* object id WC_PKCS12_CertBag_Type1 */
out[idx++] = ASN_OBJECT_ID; length++;
sz = SetLength(sizeof(WC_PKCS12_CertBag_Type1_OID), out + idx);
idx += sz; length += sz;
for (i = 0; i < sizeof(WC_PKCS12_CertBag_Type1_OID); i++) {
out[idx++] = WC_PKCS12_CertBag_Type1_OID[i]; length++;
}
out[idx++] = (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC); length++;
sz = 0;
idx += MAX_LENGTH_SZ; /* save room for length */
/* place the cert in the buffer */
out[idx++] = ASN_OCTET_STRING; sz++;
tmpSz = SetLength(certSz, out + idx);
idx += tmpSz; sz += tmpSz;
XMEMCPY(out + idx, cert, certSz);
idx += certSz; sz += certSz;
/* rewind idx and place length */
idx -= (sz + MAX_LENGTH_SZ);
tmpSz = SetLength(sz, out + idx);
XMEMMOVE(out + idx + tmpSz, out + idx + MAX_LENGTH_SZ, sz);
idx += tmpSz + sz; length += tmpSz + sz;
/* rewind idx and set sequence */
idx -= (length + MAX_SEQ_SZ);
tmpSz = SetSequence(length, out + idx);
XMEMMOVE(out + idx + tmpSz, out + idx + MAX_SEQ_SZ, length);
length += tmpSz;
/* place final length */
idx -= MAX_LENGTH_SZ;
tmpSz = SetLength(length, out + idx);
XMEMMOVE(out + idx + tmpSz, out + idx + MAX_LENGTH_SZ, length);
length += tmpSz;
/* place final sequence */
totalSz += length;
tmpSz = SetSequence(totalSz, out);
XMEMMOVE(out + tmpSz, out + MAX_SEQ_SZ, totalSz);
(void)pkcs12;
return (int)(totalSz + tmpSz);
}
/* Helper function to encrypt content.
*
* pkcs12 structure to use with key bag
* rng random number generator used
* out buffer to hold results
* outSz size of out buffer
* content content to encrypt
* contentSz size of content buffer
* vAlgo algorithm version
* pass password to use
* passSz size of pass buffer
* iter number of iterations
* type content type i.e WC_PKCS12_ENCRYPTED_DATA or WC_PKCS12_DATA
*
* returns the size of result on success
*/
static int wc_PKCS12_encrypt_content(WC_PKCS12* pkcs12, WC_RNG* rng,
byte* out, word32* outSz, byte* content, word32 contentSz, int vAlgo,
const char* pass, int passSz, int iter, int type)
{
void* heap;
int ret;
byte* tmp;
word32 idx = 0;
word32 totalSz = 0;
word32 length = 0;
word32 tmpSz;
word32 encSz;
int vPKCS = -1;
int outAlgo = -1;
int blkOid = 0;
int hmacOid = 0;
byte seq[MAX_SEQ_SZ];
WOLFSSL_MSG("encrypting PKCS12 content");
heap = wc_PKCS12_GetHeap(pkcs12);
/* ENCRYPTED DATA
* ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC
* length
* sequence
* short int
* sequence
* get object id */
if (type == WC_PKCS12_ENCRYPTED_DATA) {
word32 outerSz = 0;
if ((ret = wc_PKCS12_get_enc_params(vAlgo, &vPKCS, &outAlgo, &blkOid,
&hmacOid)) < 0) {
return ret;
}
encSz = contentSz;
if ((ret = EncryptContent(NULL, contentSz, NULL, &encSz,
pass, passSz, vPKCS, outAlgo, blkOid, NULL, 0, iter, hmacOid,
rng, heap)) < 0) {
if (ret != WC_NO_ERR_TRACE(LENGTH_ONLY_E)) {
return ret;
}
}
/* calculate size */
totalSz = (word32)SetObjectId(sizeof(WC_PKCS12_ENCRYPTED_OID), seq);
totalSz += (word32)sizeof(WC_PKCS12_ENCRYPTED_OID);
totalSz += ASN_TAG_SZ;
length = (word32)SetMyVersion(0, seq, 0);
tmpSz = (word32)SetObjectId(sizeof(WC_PKCS12_DATA_OID), seq);
tmpSz += (word32)sizeof(WC_PKCS12_DATA_OID);
tmpSz += encSz;
length += SetSequence(tmpSz, seq) + tmpSz;
outerSz = SetSequence(length, seq) + length;
totalSz += SetLength(outerSz, seq) + outerSz;
if (out == NULL) {
*outSz = totalSz + SetSequence(totalSz, seq);
return WC_NO_ERR_TRACE(LENGTH_ONLY_E);
}
if (*outSz < totalSz + SetSequence(totalSz, seq)) {
return BUFFER_E;
}
idx = 0;
idx += SetSequence(totalSz, out + idx);
idx += (word32)SetObjectId(sizeof(WC_PKCS12_ENCRYPTED_OID), out + idx);
if (idx + sizeof(WC_PKCS12_ENCRYPTED_OID) > *outSz){
return BUFFER_E;
}
XMEMCPY(out + idx, WC_PKCS12_ENCRYPTED_OID,
sizeof(WC_PKCS12_ENCRYPTED_OID));
idx += (word32)sizeof(WC_PKCS12_ENCRYPTED_OID);
if (idx + 1 > *outSz){
return BUFFER_E;
}
out[idx++] = (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC);
idx += SetLength(outerSz, out + idx);
idx += SetSequence(length, out + idx);
idx += (word32)SetMyVersion(0, out + idx, 0);
tmp = (byte*)XMALLOC(encSz, heap, DYNAMIC_TYPE_TMP_BUFFER);
if (tmp == NULL) {
return MEMORY_E;
}
if ((ret = EncryptContent(content, contentSz, tmp, &encSz,
pass, passSz, vPKCS, outAlgo, blkOid, NULL, 0, iter, hmacOid,
rng, heap)) < 0) {
XFREE(tmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
return ret;
}
encSz = (word32)ret;
#ifdef WOLFSSL_DEBUG_PKCS12
{
byte* p;
for (printf("(size %u) Encrypted Content = ", encSz),
p = (byte*)tmp;
p < (byte*)tmp + encSz;
printf("%02X", *p), p++);
printf("\n");
}
#endif
idx += SetSequence(WC_PKCS12_DATA_OBJ_SZ + encSz, out + idx);
idx += (word32)SetObjectId(sizeof(WC_PKCS12_DATA_OID), out + idx);
if (idx + sizeof(WC_PKCS12_DATA_OID) > *outSz){
WOLFSSL_MSG("Buffer not large enough for DATA OID");
XFREE(tmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
return BUFFER_E;
}
XMEMCPY(out + idx, WC_PKCS12_DATA_OID, sizeof(WC_PKCS12_DATA_OID));
idx += (word32)sizeof(WC_PKCS12_DATA_OID);
/* copy over encrypted data */
if (idx + encSz > *outSz){
XFREE(tmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
return BUFFER_E;
}
XMEMCPY(out + idx, tmp, encSz);
XFREE(tmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
idx += encSz;
return (int)idx;
}
/* DATA
* ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC
* length
* ASN_OCTET_STRING
* length
* sequence containing all bags */
if (type == WC_PKCS12_DATA) {
/* calculate size */
totalSz = (word32)SetObjectId(sizeof(WC_PKCS12_DATA_OID), seq);
totalSz += (word32)sizeof(WC_PKCS12_DATA_OID);
totalSz += ASN_TAG_SZ;
length = SetOctetString(contentSz, seq);
length += contentSz;
totalSz += SetLength(length, seq);
totalSz += length;
if (out == NULL) {
*outSz = totalSz + SetSequence(totalSz, seq);
return WC_NO_ERR_TRACE(LENGTH_ONLY_E);
}
if (*outSz < (totalSz + SetSequence(totalSz, seq))) {
return BUFFER_E;
}
/* place data in output buffer */
idx = 0;
idx += SetSequence(totalSz, out);
idx += (word32)SetObjectId(sizeof(WC_PKCS12_DATA_OID), out + idx);
if (idx + sizeof(WC_PKCS12_DATA_OID) > *outSz){
WOLFSSL_MSG("Buffer not large enough for DATA OID");
return BUFFER_E;
}
XMEMCPY(out + idx, WC_PKCS12_DATA_OID, sizeof(WC_PKCS12_DATA_OID));
idx += (word32)sizeof(WC_PKCS12_DATA_OID);
if (idx + 1 > *outSz){
return BUFFER_E;
}
out[idx++] = (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC);
idx += SetLength(length, out + idx);
idx += SetOctetString(contentSz, out + idx);
if (idx + contentSz > *outSz){
return BUFFER_E;
}
XMEMCPY(out + idx, content, contentSz);
idx += contentSz;
return (int)idx;
}
WOLFSSL_MSG("Unknown/Unsupported content type");
return BAD_FUNC_ARG;
}
/* helper function to create the PKCS12 key content
* keyCiSz is output buffer size
* returns a pointer to be free'd by caller on success and NULL on failure */
static byte* PKCS12_create_key_content(WC_PKCS12* pkcs12, int nidKey,
word32* keyCiSz, WC_RNG* rng, char* pass, word32 passSz,
byte* key, word32 keySz, int iter)
{
byte* keyBuf;
word32 keyBufSz = 0;
byte* keyCi = NULL;
word32 tmpSz;
int ret;
int algo;
void* heap;
heap = wc_PKCS12_GetHeap(pkcs12);
*keyCiSz = 0;
switch (nidKey) {
/* supported key encryptions */
case PBE_SHA1_RC4_128:
algo = 1;
break;
case PBE_SHA1_DES:
algo = 2;
break;
case PBE_SHA1_DES3:
algo = 3;
break;
case PBE_AES256_CBC:
algo = PBE_AES256_CBC;
break;
case PBE_AES128_CBC:
algo = PBE_AES128_CBC;
break;
case -1: /* no encryption */
algo = -1;
break;
default:
WOLFSSL_MSG("Unknown/Unsupported key encryption");
return NULL;
}
/* get max size for key bag */
ret = wc_PKCS12_create_key_bag(pkcs12, rng, NULL, &keyBufSz, key, keySz,
algo, iter, pass, (int)passSz);
if (ret != WC_NO_ERR_TRACE(LENGTH_ONLY_E) && ret < 0) {
WOLFSSL_MSG("Error getting key bag size");
return NULL;
}
/* account for sequence around bag */
keyBufSz += MAX_SEQ_SZ;
keyBuf = (byte*)XMALLOC(keyBufSz, heap, DYNAMIC_TYPE_TMP_BUFFER);
if (keyBuf == NULL) {
WOLFSSL_MSG("Memory error creating keyBuf buffer");
return NULL;
}
ret = wc_PKCS12_create_key_bag(pkcs12, rng, keyBuf + MAX_SEQ_SZ, &keyBufSz,
key, keySz, algo, iter, pass, (int)passSz);
if (ret < 0) {
XFREE(keyBuf, heap, DYNAMIC_TYPE_TMP_BUFFER);
WOLFSSL_MSG("Error creating key bag");
return NULL;
}
keyBufSz = (word32)ret;
tmpSz = SetSequence(keyBufSz, keyBuf);
XMEMMOVE(keyBuf + tmpSz, keyBuf + MAX_SEQ_SZ, keyBufSz);
keyBufSz += tmpSz;
#ifdef WOLFSSL_DEBUG_PKCS12
{
word32 i;
printf("(size %u) Key Bag = ", keyBufSz);
for (i = 0; i < keyBufSz; i++)
printf("%02X", keyBuf[i]);
printf("\n");
}
#endif
ret = wc_PKCS12_encrypt_content(pkcs12, rng, NULL, keyCiSz,
NULL, keyBufSz, algo, pass, (int)passSz, iter, WC_PKCS12_DATA);
if (ret != WC_NO_ERR_TRACE(LENGTH_ONLY_E)) {
XFREE(keyBuf, heap, DYNAMIC_TYPE_TMP_BUFFER);
WOLFSSL_MSG("Error getting key encrypt content size");
return NULL;
}
keyCi = (byte*)XMALLOC(*keyCiSz, heap, DYNAMIC_TYPE_TMP_BUFFER);
if (keyCi == NULL) {
XFREE(keyBuf, heap, DYNAMIC_TYPE_TMP_BUFFER);
return NULL;
}
ret = wc_PKCS12_encrypt_content(pkcs12, rng, keyCi, keyCiSz,
keyBuf, keyBufSz, algo, pass, (int)passSz, iter, WC_PKCS12_DATA);
XFREE(keyBuf, heap, DYNAMIC_TYPE_TMP_BUFFER);
if (ret < 0 ) {
XFREE(keyCi, heap, DYNAMIC_TYPE_TMP_BUFFER);
WOLFSSL_MSG("Error creating key encrypt content");
return NULL;
}
*keyCiSz = (word32)ret;
#ifdef WOLFSSL_DEBUG_PKCS12
{
word32 i;
printf("(size %u) Key Content Info = ", *keyCiSz);
for (i = 0; i < *keyCiSz; i++)
printf("%02X", keyCi[i]);
printf("\n");
}
#endif
(void)heap;
return keyCi;
}
/* helper function to create the PKCS12 certificate content
* certCiSz is output buffer size
* returns a pointer to be free'd by caller on success and NULL on failure */
static byte* PKCS12_create_cert_content(WC_PKCS12* pkcs12, int nidCert,
WC_DerCertList* ca, byte* cert, word32 certSz, word32* certCiSz,
WC_RNG* rng, char* pass, word32 passSz, int iter)
{
int algo;
int ret;
int type;
byte* certBuf = NULL;
word32 certBufSz;
word32 idx;
word32 sz;
word32 tmpSz;
byte* certCi;
void* heap;
heap = wc_PKCS12_GetHeap(pkcs12);
switch (nidCert) {
/* supported certificate encryptions */
case PBE_SHA1_RC4_128:
type = WC_PKCS12_ENCRYPTED_DATA;
algo = 1;
break;
case PBE_SHA1_DES:
type = WC_PKCS12_ENCRYPTED_DATA;
algo = 2;
break;
case PBE_SHA1_DES3:
type = WC_PKCS12_ENCRYPTED_DATA;
algo = 3;
break;
case PBE_AES256_CBC:
type = WC_PKCS12_ENCRYPTED_DATA;
algo = PBE_AES256_CBC;
break;
case PBE_AES128_CBC:
type = WC_PKCS12_ENCRYPTED_DATA;
algo = PBE_AES128_CBC;
break;
case -1: /* no encryption */
type = WC_PKCS12_DATA;
algo = -1;
break;
default:
WOLFSSL_MSG("Unknown/Unsupported certificate encryption");
return NULL;
}
/* get max size of buffer needed */
ret = wc_PKCS12_create_cert_bag(pkcs12, NULL, &certBufSz, cert, certSz);
if (ret != WC_NO_ERR_TRACE(LENGTH_ONLY_E)) {
return NULL;
}
if (ca != NULL) {
WC_DerCertList* current = ca;
word32 curBufSz = 0;
/* get max buffer size */
while (current != NULL) {
ret = wc_PKCS12_create_cert_bag(pkcs12, NULL, &curBufSz,
current->buffer, current->bufferSz);
if (ret != WC_NO_ERR_TRACE(LENGTH_ONLY_E)) {
return NULL;
}
certBufSz += curBufSz;
current = current->next;
}
}
/* account for Sequence that holds all certificate bags */
certBufSz += MAX_SEQ_SZ;
/* completed getting max size, now create buffer and start adding bags */
certBuf = (byte*)XMALLOC(certBufSz, heap, DYNAMIC_TYPE_TMP_BUFFER);
if (certBuf == NULL) {
WOLFSSL_MSG("Memory error creating certificate bags");
return NULL;
}
idx = 0;
idx += MAX_SEQ_SZ;
sz = certBufSz - idx;
if ((ret = wc_PKCS12_create_cert_bag(pkcs12, certBuf + idx, &sz,
cert, certSz)) < 0) {
XFREE(certBuf, heap, DYNAMIC_TYPE_TMP_BUFFER);
return NULL;
}
idx += (word32)ret;
if (ca != NULL) {
WC_DerCertList* current = ca;
while (current != NULL) {
sz = certBufSz - idx;
if ((ret = wc_PKCS12_create_cert_bag(pkcs12, certBuf + idx, &sz,
current->buffer, current->bufferSz)) < 0) {
XFREE(certBuf, heap, DYNAMIC_TYPE_TMP_BUFFER);
return NULL;
}
idx += (word32)ret;
current = current->next;
}
}
/* set sequence and create encrypted content with all certificate bags */
tmpSz = SetSequence(idx - MAX_SEQ_SZ, certBuf);
XMEMMOVE(certBuf + tmpSz, certBuf + MAX_SEQ_SZ, idx - MAX_SEQ_SZ);
certBufSz = tmpSz + (idx - MAX_SEQ_SZ);
/* get buffer size needed for content info */
ret = wc_PKCS12_encrypt_content(pkcs12, rng, NULL, certCiSz,
NULL, certBufSz, algo, pass, (int)passSz, iter, type);
if (ret != WC_NO_ERR_TRACE(LENGTH_ONLY_E)) {
XFREE(certBuf, heap, DYNAMIC_TYPE_TMP_BUFFER);
WOLFSSL_LEAVE("wc_PKCS12_create()", ret);
return NULL;
}
certCi = (byte*)XMALLOC(*certCiSz, heap, DYNAMIC_TYPE_TMP_BUFFER);
if (certCi == NULL) {
XFREE(certBuf, heap, DYNAMIC_TYPE_TMP_BUFFER);
return NULL;
}
ret = wc_PKCS12_encrypt_content(pkcs12, rng, certCi, certCiSz,
certBuf, certBufSz, algo, pass, (int)passSz, iter, type);
XFREE(certBuf, heap, DYNAMIC_TYPE_TMP_BUFFER);
if (ret < 0) {
WOLFSSL_LEAVE("wc_PKCS12_create()", ret);
XFREE(certCi, heap, DYNAMIC_TYPE_TMP_BUFFER);
return NULL;
}
*certCiSz = (word32)ret;
#ifdef WOLFSSL_DEBUG_PKCS12
{
word32 i;
printf("(size %u) Encrypted Certificate Content Info = ", *certCiSz);
for (i = 0; i < *certCiSz; i++)
printf("%02X", certCi[i]);
printf("\n");
}
#endif
(void)heap;
return certCi;
}
/* helper function to create the PKCS12 safe
* returns 0 on success */
static int PKCS12_create_safe(WC_PKCS12* pkcs12, byte* certCi, word32 certCiSz,
byte* keyCi, word32 keyCiSz, WC_RNG* rng, char* pass, word32 passSz,
int iter)
{
int length;
int ret;
byte seq[MAX_SEQ_SZ];
word32 safeDataSz;
word32 innerDataSz;
byte *innerData = NULL;
byte *safeData = NULL;
word32 idx;
innerDataSz = certCiSz + keyCiSz+SetSequence(certCiSz + keyCiSz, seq);
/* add Content Info structs to safe, key first then cert */
ret = wc_PKCS12_encrypt_content(pkcs12, rng, NULL, &safeDataSz,
NULL, innerDataSz, 0, NULL, 0, 0, WC_PKCS12_DATA);
if (ret != WC_NO_ERR_TRACE(LENGTH_ONLY_E)) {
return ret;
}
safeData = (byte*)XMALLOC(safeDataSz, pkcs12->heap, DYNAMIC_TYPE_TMP_BUFFER);
if (safeData == NULL) {
WOLFSSL_MSG("Error malloc'ing safe data buffer");
return MEMORY_E;
}
/* create sequence of inner data */
innerData = (byte*)XMALLOC(innerDataSz, pkcs12->heap, DYNAMIC_TYPE_PKCS);
if (innerData == NULL) {
WOLFSSL_MSG("Error malloc'ing inner data buffer");
XFREE(safeData, pkcs12->heap, DYNAMIC_TYPE_TMP_BUFFER);
return MEMORY_E;
}
idx = 0;
idx += SetSequence(certCiSz + keyCiSz, innerData);
XMEMCPY(innerData + idx, certCi, certCiSz);
XMEMCPY(innerData + idx + certCiSz, keyCi, keyCiSz);
ret = wc_PKCS12_encrypt_content(pkcs12, rng, safeData, &safeDataSz,
innerData, innerDataSz, 0, pass, (int)passSz, iter, WC_PKCS12_DATA);
XFREE(innerData, pkcs12->heap, DYNAMIC_TYPE_PKCS);
if (ret < 0 ) {
WOLFSSL_MSG("Error setting data type for safe contents");
XFREE(safeData, pkcs12->heap, DYNAMIC_TYPE_TMP_BUFFER);
return ret;
}
idx = 0;
ret = GetSequence(safeData, &idx, &length, safeDataSz);
if (ret < 0) {
WOLFSSL_MSG("Error getting first sequence of safe");
XFREE(safeData, pkcs12->heap, DYNAMIC_TYPE_TMP_BUFFER);
return ret;
}
ret = GetSafeContent(pkcs12, safeData, &idx, safeDataSz);
XFREE(safeData, pkcs12->heap, DYNAMIC_TYPE_TMP_BUFFER);
if (ret < 0) {
WOLFSSL_MSG("Unable to create safe contents");
return ret;
}
return 0;
}
/*
* pass : password to use with encryption
* passSz : size of the password buffer
* name : friendlyName to use
* key : DER format of key
* keySz : size of key buffer
* cert : DER format of certificate
* certSz : size of the certificate buffer
* ca : a list of extra certificates
* nidKey : type of encryption to use on the key (-1 means no encryption)
* nidCert : type of encryption to use on the certificate
* (-1 means no encryption)
* iter : number of iterations with encryption
* macIter : number of iterations when creating MAC
* keyType : flag for signature and/or encryption key
* heap : pointer to allocate from memory
*
* returns a pointer to a new WC_PKCS12 structure on success and NULL if failed
*/
WC_PKCS12* wc_PKCS12_create(char* pass, word32 passSz, char* name,
byte* key, word32 keySz, byte* cert, word32 certSz, WC_DerCertList* ca,
int nidKey, int nidCert, int iter, int macIter, int keyType, void* heap)
{
WC_PKCS12* pkcs12;
WC_RNG rng;
int ret;
byte* certCi = NULL;
byte* keyCi = NULL;
word32 certCiSz;
word32 keyCiSz;
WOLFSSL_ENTER("wc_PKCS12_create");
if (wc_InitRng_ex(&rng, heap, INVALID_DEVID) != 0) {
return NULL;
}
if ((pkcs12 = wc_PKCS12_new_ex(heap)) == NULL) {
wc_FreeRng(&rng);
WOLFSSL_LEAVE("wc_PKCS12_create", MEMORY_E);
return NULL;
}
if (iter <= 0) {
iter = WC_PKCS12_ITT_DEFAULT;
}
/**** add private key bag ****/
keyCi = PKCS12_create_key_content(pkcs12, nidKey, &keyCiSz, &rng,
pass, passSz, key, keySz, iter);
if (keyCi == NULL) {
wc_PKCS12_free(pkcs12);
wc_FreeRng(&rng);
return NULL;
}
/**** add main certificate bag and extras ****/
certCi = PKCS12_create_cert_content(pkcs12, nidCert, ca, cert, certSz,
&certCiSz, &rng, pass, passSz, iter);
if (certCi == NULL) {
XFREE(keyCi, heap, DYNAMIC_TYPE_TMP_BUFFER);
wc_PKCS12_free(pkcs12);
wc_FreeRng(&rng);
return NULL;
}
/**** create safe and Content Info ****/
ret = PKCS12_create_safe(pkcs12, certCi, certCiSz, keyCi, keyCiSz, &rng,
pass, passSz, iter);
XFREE(keyCi, heap, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(certCi, heap, DYNAMIC_TYPE_TMP_BUFFER);
if (ret != 0) {
WOLFSSL_MSG("Unable to create PKCS12 safe");
wc_PKCS12_free(pkcs12);
wc_FreeRng(&rng);
return NULL;
}
/* create MAC */
if (macIter > 0) {
MacData* mac;
byte digest[WC_MAX_DIGEST_SIZE]; /* for MAC */
mac = (MacData*)XMALLOC(sizeof(MacData), heap, DYNAMIC_TYPE_PKCS);
if (mac == NULL) {
wc_PKCS12_free(pkcs12);
wc_FreeRng(&rng);
WOLFSSL_MSG("Error malloc'ing mac data buffer");
return NULL;
}
XMEMSET(mac, 0, sizeof(MacData));
pkcs12->signData = mac; /* now wc_PKCS12_free will free all mac too */
#ifndef NO_SHA256
mac->oid = SHA256h;
#elif !defined(NO_SHA)
mac->oid = SHA;
#elif defined(WOLFSSL_SHA384)
mac->oid = SHA384;
#elif defined(WOLFSSL_SHA512)
mac->oid = SHA512;
#else
WOLFSSL_MSG("No supported hash algorithm compiled in!");
wc_PKCS12_free(pkcs12);
wc_FreeRng(&rng);
return NULL;
#endif
/* store number of iterations */
mac->itt = macIter;
/* set mac salt */
mac->saltSz = WC_PKCS12_MAC_SALT_SZ;
mac->salt = (byte*)XMALLOC(WC_PKCS12_MAC_SALT_SZ, heap,
DYNAMIC_TYPE_PKCS);
if (mac->salt == NULL) {
wc_PKCS12_free(pkcs12);
wc_FreeRng(&rng);
WOLFSSL_MSG("Error malloc'ing salt data buffer");
return NULL;
}
if (wc_RNG_GenerateBlock(&rng, mac->salt, mac->saltSz) != 0) {
WOLFSSL_MSG("Error generating random salt");
wc_PKCS12_free(pkcs12);
wc_FreeRng(&rng);
return NULL;
}
ret = wc_PKCS12_create_mac(pkcs12, pkcs12->safe->data,
pkcs12->safe->dataSz, (const byte*)pass, passSz, digest,
WC_MAX_DIGEST_SIZE);
if (ret < 0) {
wc_PKCS12_free(pkcs12);
wc_FreeRng(&rng);
WOLFSSL_MSG("Error creating mac");
WOLFSSL_LEAVE("wc_PKCS12_create", ret);
return NULL;
}
mac->digestSz = (word32)ret;
mac->digest = (byte*)XMALLOC((size_t)ret, heap, DYNAMIC_TYPE_PKCS);
if (mac->digest == NULL) {
WOLFSSL_MSG("Error malloc'ing mac digest buffer");
wc_PKCS12_free(pkcs12);
wc_FreeRng(&rng);
return NULL;
}
XMEMCPY(mac->digest, digest, mac->digestSz);
}
else {
pkcs12->signData = NULL;
}
wc_FreeRng(&rng);
(void)name;
(void)keyType;
return pkcs12;
}
/* 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 /* HAVE_PKCS12 && !NO_ASN && !NO_PWDBASED && !NO_HMAC */