mirror of https://github.com/wolfSSL/wolfssl.git
2852 lines
85 KiB
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 */
|