diff --git a/src/ssl.c b/src/ssl.c index d0fad99990..7edcfdd138 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -14838,7 +14838,7 @@ static void ExternalFreeX509(WOLFSSL_X509* x509) { WOLFSSL_ENTER("wolfSSL_X509_get_der"); - if (x509 == NULL || outSz == NULL) + if (x509 == NULL || x509->derCert == NULL || outSz == NULL) return NULL; *outSz = (int)x509->derCert->length; @@ -16403,6 +16403,97 @@ WC_PKCS12* wolfSSL_d2i_PKCS12_bio(WOLFSSL_BIO* bio, WC_PKCS12** pkcs12) } +/* helper function to get DER buffer from WOLFSSL_EVP_PKEY */ +static int wolfSSL_i2d_PrivateKey(WOLFSSL_EVP_PKEY* key, unsigned char** der) +{ + *der = (unsigned char*)key->pkey.ptr; + + return key->pkey_sz; +} + + + +WC_PKCS12* wolfSSL_PKCS12_create(char* pass, char* name, + WOLFSSL_EVP_PKEY* pkey, WOLFSSL_X509* cert, + WOLF_STACK_OF(WOLFSSL_X509)* ca, + int keyNID, int certNID, int itt, int macItt, int keyType) +{ + WC_PKCS12* pkcs12; + WC_DerCertList* list = NULL; + word32 passSz = (word32)XSTRLEN(pass); + byte* keyDer; + word32 keyDerSz; + byte* certDer; + int certDerSz; + + int ret; + + WOLFSSL_ENTER("wolfSSL_PKCS12_create()"); + + if (pass == NULL || pkey == NULL || cert == NULL) { + WOLFSSL_LEAVE("wolfSSL_PKCS12_create()", BAD_FUNC_ARG); + return NULL; + } + + if ((ret = wolfSSL_i2d_PrivateKey(pkey, &keyDer)) < 0) { + WOLFSSL_LEAVE("wolfSSL_PKCS12_create", ret); + return NULL; + } + keyDerSz = ret; + + certDer = (byte*)wolfSSL_X509_get_der(cert, &certDerSz); + if (certDer == NULL) { + return NULL; + } + + if (ca != NULL) { + WC_DerCertList* cur; + unsigned long numCerts = ca->num; + byte* curDer; + int curDerSz = 0; + WOLFSSL_STACK* sk = ca; + + while (numCerts > 0 && sk != NULL) { + cur = (WC_DerCertList*)XMALLOC(sizeof(WC_DerCertList), NULL, + DYNAMIC_TYPE_PKCS); + if (cur == NULL) { + wc_FreeCertList(list, NULL); + return NULL; + } + + curDer = (byte*)wolfSSL_X509_get_der(sk->data.x509, &curDerSz); + if (certDer == NULL || curDerSz < 0) { + wc_FreeCertList(list, NULL); + return NULL; + } + + cur->buffer = (byte*)XMALLOC(curDerSz, NULL, DYNAMIC_TYPE_PKCS); + if (cur->buffer == NULL) { + wc_FreeCertList(list, NULL); + return NULL; + } + XMEMCPY(cur->buffer, curDer, curDerSz); + cur->bufferSz = curDerSz; + cur->next = list; + list = cur; + + sk = sk->next; + numCerts--; + } + } + + pkcs12 = wc_PKCS12_create(pass, passSz, name, keyDer, keyDerSz, + certDer, certDerSz, list, keyNID, certNID, itt, macItt, + keyType, NULL); + + if (ca != NULL) { + wc_FreeCertList(list, NULL); + } + + return pkcs12; +} + + /* return 1 on success, 0 on failure */ int wolfSSL_PKCS12_parse(WC_PKCS12* pkcs12, const char* psw, WOLFSSL_EVP_PKEY** pkey, WOLFSSL_X509** cert, WOLF_STACK_OF(WOLFSSL_X509)** ca) diff --git a/tests/api.c b/tests/api.c index 733e942b22..9c28e6a9ff 100644 --- a/tests/api.c +++ b/tests/api.c @@ -2523,11 +2523,13 @@ static void test_wolfSSL_PKCS12(void) !defined(NO_ASN) && !defined(NO_PWDBASED) && !defined(NO_RSA) byte buffer[5300]; char file[] = "./certs/test-servercert.p12"; + char pass[] = "a password"; FILE *f; int bytes, ret; WOLFSSL_BIO *bio; WOLFSSL_EVP_PKEY *pkey; WC_PKCS12 *pkcs12; + WC_PKCS12 *pkcs12_2; WOLFSSL_X509 *cert; WOLFSSL_X509 *tmp; WOLF_STACK_OF(WOLFSSL_X509) *ca; @@ -2571,6 +2573,7 @@ static void test_wolfSSL_PKCS12(void) AssertNotNull(cert); AssertNotNull(ca); + /* should be 2 other certs on stack */ tmp = sk_X509_pop(ca); AssertNotNull(tmp); @@ -2580,10 +2583,64 @@ static void test_wolfSSL_PKCS12(void) X509_free(tmp); AssertNull(sk_X509_pop(ca)); + EVP_PKEY_free(pkey); + X509_free(cert); + sk_X509_free(ca); + + /* check PKCS12_create */ + AssertNull(PKCS12_create(pass, NULL, NULL, NULL, NULL, -1, -1, -1, -1,0)); + AssertIntEQ(PKCS12_parse(pkcs12, "wolfSSL test", &pkey, &cert, &ca), + SSL_SUCCESS); + AssertNotNull((pkcs12_2 = PKCS12_create(pass, NULL, pkey, cert, ca, + -1, -1, 100, -1, 0))); + EVP_PKEY_free(pkey); + X509_free(cert); + sk_X509_free(ca); + + AssertIntEQ(PKCS12_parse(pkcs12_2, "a password", &pkey, &cert, &ca), + SSL_SUCCESS); + PKCS12_free(pkcs12_2); + AssertNotNull((pkcs12_2 = PKCS12_create(pass, NULL, pkey, cert, ca, + NID_pbe_WithSHA1And3_Key_TripleDES_CBC, + NID_pbe_WithSHA1And3_Key_TripleDES_CBC, + 2000, 1, 0))); + EVP_PKEY_free(pkey); + X509_free(cert); + sk_X509_free(ca); + + AssertIntEQ(PKCS12_parse(pkcs12_2, "a password", &pkey, &cert, &ca), + SSL_SUCCESS); + + /* should be 2 other certs on stack */ + tmp = sk_X509_pop(ca); + AssertNotNull(tmp); + X509_free(tmp); + tmp = sk_X509_pop(ca); + AssertNotNull(tmp); + X509_free(tmp); + AssertNull(sk_X509_pop(ca)); + + +#ifndef NO_RC4 + PKCS12_free(pkcs12_2); + AssertNotNull((pkcs12_2 = PKCS12_create(pass, NULL, pkey, cert, NULL, + NID_pbe_WithSHA1And128BitRC4, + NID_pbe_WithSHA1And128BitRC4, + 2000, 1, 0))); + EVP_PKEY_free(pkey); + X509_free(cert); + sk_X509_free(ca); + + AssertIntEQ(PKCS12_parse(pkcs12_2, "a password", &pkey, &cert, &ca), + SSL_SUCCESS); + +#endif /* NO_RC4 */ + EVP_PKEY_free(pkey); X509_free(cert); BIO_free(bio); PKCS12_free(pkcs12); + PKCS12_free(pkcs12_2); sk_X509_free(ca); printf(resultFmt, passed); diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 9ebbb01128..70c3ea3b96 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -842,6 +842,13 @@ static const byte extExtKeyUsageOcspSignOid[] = {43, 6, 1, 5, 5, 7, 3, 9}; /* kdfType */ static const byte pbkdf2Oid[] = {42, 134, 72, 134, 247, 13, 1, 5, 12}; +/* PKCS5 */ +static const byte pbeSha1Des[] = {42, 134, 72, 134, 247, 13, 1, 5, 10}; + +/* PKCS12 */ +static const byte pbeSha1RC4128[] = {42, 134, 72, 134, 247, 13, 1, 12, 1, 1}; +static const byte pbeSha1Des3[] = {42, 134, 72, 134, 247, 13, 1, 12, 1, 3}; + static const byte* OidFromId(word32 id, word32 type, word32* oidSz) { const byte* oid = NULL; @@ -1161,6 +1168,25 @@ static const byte* OidFromId(word32 id, word32 type, word32* oidSz) } break; + case oidPBEType: + switch (id) { + case PBE_SHA1_RC4_128: + oid = pbeSha1RC4128; + *oidSz = sizeof(pbeSha1RC4128); + break; + + case PBE_SHA1_DES: + oid = pbeSha1Des; + *oidSz = sizeof(pbeSha1Des); + break; + + case PBE_SHA1_DES3: + oid = pbeSha1Des3; + *oidSz = sizeof(pbeSha1Des3); + break; + } + break; + case oidKeyWrapType: switch (id) { case AES128_WRAP: @@ -1870,11 +1896,15 @@ static int CheckAlgo(int first, int second, int* id, int* version) if (first == 1) { switch (second) { - case 1: + case PBE_SHA1_RC4_128: *id = PBE_SHA1_RC4_128; *version = PKCS12v1; return 0; - case 3: + case PBE_SHA1_DES: + *id = PBE_SHA1_DES; + *version = PKCS12v1; + return 0; + case PBE_SHA1_DES3: *id = PBE_SHA1_DES3; *version = PKCS12v1; return 0; @@ -1926,10 +1956,10 @@ static int CheckAlgoV2(int oid, int* id) } -/* Decrypt input in place from parameters based on id */ -static int DecryptKey(const char* password, int passwordSz, byte* salt, +/* Decrypt/Encrypt input in place from parameters based on id */ +static int CryptKey(const char* password, int passwordSz, byte* salt, int saltSz, int iterations, int id, byte* input, - int length, int version, byte* cbcIv) + int length, int version, byte* cbcIv, int enc) { int typeH; int derivedLen; @@ -1942,6 +1972,9 @@ static int DecryptKey(const char* password, int passwordSz, byte* salt, (void)input; (void)length; + (void)enc; + + WOLFSSL_ENTER("CryptKey()"); switch (id) { case PBE_MD5_DES: @@ -1970,6 +2003,7 @@ static int DecryptKey(const char* password, int passwordSz, byte* salt, break; default: + WOLFSSL_MSG("Unknown/Unsupported encrypt/decrypt id"); return ALGO_ID_E; } @@ -2016,6 +2050,7 @@ static int DecryptKey(const char* password, int passwordSz, byte* salt, #ifdef WOLFSSL_SMALL_STACK XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif + WOLFSSL_MSG("Unknown/Unsupported PKCS version"); return ALGO_ID_E; } @@ -2031,13 +2066,18 @@ static int DecryptKey(const char* password, int passwordSz, byte* salt, case PBE_MD5_DES: case PBE_SHA1_DES: { - Des dec; + Des des; byte* desIv = key + 8; if (version == PKCS5v2 || version == PKCS12v1) desIv = cbcIv; - ret = wc_Des_SetKey(&dec, key, desIv, DES_DECRYPTION); + if (enc) { + ret = wc_Des_SetKey(&des, key, desIv, DES_ENCRYPTION); + } + else { + ret = wc_Des_SetKey(&des, key, desIv, DES_DECRYPTION); + } if (ret != 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER); @@ -2045,33 +2085,48 @@ static int DecryptKey(const char* password, int passwordSz, byte* salt, return ret; } - wc_Des_CbcDecrypt(&dec, input, input, length); + if (enc) { + wc_Des_CbcEncrypt(&des, input, input, length); + } + else { + wc_Des_CbcDecrypt(&des, input, input, length); + } break; } case PBE_SHA1_DES3: { - Des3 dec; + Des3 des; byte* desIv = key + 24; if (version == PKCS5v2 || version == PKCS12v1) desIv = cbcIv; - ret = wc_Des3Init(&dec, NULL, INVALID_DEVID); + ret = wc_Des3Init(&des, NULL, INVALID_DEVID); if (ret != 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return ret; } - ret = wc_Des3_SetKey(&dec, key, desIv, DES_DECRYPTION); + if (enc) { + ret = wc_Des3_SetKey(&des, key, desIv, DES_ENCRYPTION); + } + else { + ret = wc_Des3_SetKey(&des, key, desIv, DES_DECRYPTION); + } if (ret != 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return ret; } - ret = wc_Des3_CbcDecrypt(&dec, input, input, length); + if (enc) { + ret = wc_Des3_CbcEncrypt(&des, input, input, length); + } + else { + ret = wc_Des3_CbcDecrypt(&des, input, input, length); + } if (ret != 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER); @@ -2113,7 +2168,11 @@ static int DecryptKey(const char* password, int passwordSz, byte* salt, #endif default: - ret = ALGO_ID_E; +#ifdef WOLFSSL_SMALL_STACK + XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif + WOLFSSL_MSG("Unknown/Unsupported encrypt/decryption algorithm"); + return ALGO_ID_E; } #ifdef WOLFSSL_SMALL_STACK @@ -2128,64 +2187,45 @@ int wc_GetKeyOID(byte* key, word32 keySz, const byte** curveOID, word32* oidSz, int* algoID, void* heap) { word32 tmpIdx = 0; -#ifdef HAVE_ECC + + #ifdef HAVE_ECC ecc_key ecc; -#endif -#ifndef NO_RSA - RsaKey rsa; -#endif + #endif + #ifdef HAVE_ED25519 ed25519_key ed25519; #endif - if (algoID == NULL) { - return BAD_FUNC_ARG; - } - *algoID = 0; + #ifndef NO_RSA + RsaKey rsa; -#ifndef NO_RSA - if (wc_InitRsaKey(&rsa, heap) == 0) { - if (wc_RsaPrivateKeyDecode(key, &tmpIdx, &rsa, keySz) == 0) { - *algoID = RSAk; - } - else { - WOLFSSL_MSG("Not RSA DER key"); - } - wc_FreeRsaKey(&rsa); + wc_InitRsaKey(&rsa, heap); + if (wc_RsaPrivateKeyDecode(key, &tmpIdx, &rsa, keySz) == 0) { + *algoID = RSAk; } else { - WOLFSSL_MSG("GetKeyOID wc_InitRsaKey failed"); + WOLFSSL_MSG("Not RSA DER key"); } -#endif /* NO_RSA */ -#ifdef HAVE_ECC - if (*algoID != RSAk) { + wc_FreeRsaKey(&rsa); + #endif /* NO_RSA */ + #ifdef HAVE_ECC + if (algoID == 0) { tmpIdx = 0; - if (wc_ecc_init_ex(&ecc, heap, INVALID_DEVID) == 0) { - if (wc_EccPrivateKeyDecode(key, &tmpIdx, &ecc, keySz) == 0) { - *algoID = ECDSAk; + wc_ecc_init_ex(&ecc, heap, INVALID_DEVID); + if (wc_EccPrivateKeyDecode(key, &tmpIdx, &ecc, keySz) == 0) { + *algoID = ECDSAk; - /* sanity check on arguments */ - if (curveOID == NULL || oidSz == NULL) { - WOLFSSL_MSG("Error getting ECC curve OID"); - wc_ecc_free(&ecc); - return BAD_FUNC_ARG; - } - - /* now find oid */ - if (wc_ecc_get_oid(ecc.dp->oidSum, curveOID, oidSz) < 0) { - WOLFSSL_MSG("Error getting ECC curve OID"); - wc_ecc_free(&ecc); - return BAD_FUNC_ARG; - } + /* now find oid */ + if (wc_ecc_get_oid(ecc.dp->oidSum, curveOID, oidSz) < 0) { + WOLFSSL_MSG("Error getting ECC curve OID"); + wc_ecc_free(&ecc); + return BAD_FUNC_ARG; } - else { - WOLFSSL_MSG("Not ECC DER key either"); - } - wc_ecc_free(&ecc); } else { - WOLFSSL_MSG("GetKeyOID wc_ecc_init_ex failed"); + WOLFSSL_MSG("Not ECC DER key either"); } + wc_ecc_free(&ecc); } #endif /* HAVE_ECC */ #ifdef HAVE_ED25519 @@ -2213,13 +2253,218 @@ int wc_GetKeyOID(byte* key, word32 keySz, const byte** curveOID, word32* oidSz, return BAD_FUNC_ARG; } - (void)curveOID; - (void)oidSz; - return 1; } +/* + * Used when creating PKCS12 shrouded key bags + * vPKCS is the version of PKCS to use + * vAlgo is the algorithm version to use + * + * if salt is NULL a random number is generated + */ +int UnTraditionalEnc(byte* key, word32 keySz, byte* out, word32* outSz, + const char* password,int passwordSz, int vPKCS, int vAlgo, + byte* salt, word32 saltSz, int itt, WC_RNG* rng, void* heap) +{ + int algoID = 0; + byte* tmp; + word32 tmpSz; + word32 sz; + word32 seqSz; + word32 inOutIdx = 0; + word32 totalSz = 0; + int version, id; + int ret; + + const byte* curveOID = NULL; + word32 oidSz = 0; + +#ifdef WOLFSSL_SMALL_STACK + byte* saltTmp = NULL; + byte* cbcIv = NULL; +#else + byte saltTmp[MAX_IV_SIZE]; + byte cbcIv[MAX_IV_SIZE]; +#endif + + WOLFSSL_ENTER("UnTraditionalEnc()"); + + if (saltSz > MAX_SALT_SIZE) + return ASN_PARSE_E; + + + inOutIdx += MAX_SEQ_SZ; /* leave room for size of finished shroud */ + if (CheckAlgo(vPKCS, vAlgo, &id, &version) < 0) { + WOLFSSL_MSG("Bad/Unsupported algorithm ID"); + return ASN_INPUT_E; /* Algo ID error */ + } + + if (out != NULL) { + if (*outSz < inOutIdx + MAX_ALGO_SZ + MAX_SALT_SIZE + MAX_SEQ_SZ + 1 + + MAX_LENGTH_SZ + MAX_SHORT_SZ + 1) + return BUFFER_E; + + sz = SetAlgoID(id, out + inOutIdx, oidPBEType, 0); + totalSz += sz; inOutIdx += sz; + + if (version == PKCS5v2) { + WOLFSSL_MSG("PKCS5v2 Not supported yet\n"); + } + + if (salt == NULL || saltSz <= 0) { + saltSz = 8; + salt = saltTmp; + + #ifdef WOLFSSL_SMALL_STACK + saltTmp = (byte*)XMALLOC(saltSz, heap, DYNAMIC_TYPE_TMP_BUFFER); + if (saltTmp == NULL) + return MEMORY_E; + #endif + + if ((ret = wc_RNG_GenerateBlock(rng, saltTmp, saltSz)) != 0) { + WOLFSSL_MSG("Error generating random salt"); + #ifdef WOLFSSL_SMALL_STACK + if (saltTmp != NULL) + XFREE(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif + return ret; + } + } + + + /* leave room for a sequence (contains salt and itterations int) */ + inOutIdx += MAX_SEQ_SZ; sz = 0; + + /* place salt in buffer */ + out[inOutIdx++] = ASN_OCTET_STRING; sz++; + tmpSz = SetLength(saltSz, out + inOutIdx); + inOutIdx += tmpSz; sz += tmpSz; + XMEMCPY(&out[inOutIdx], salt, saltSz); + inOutIdx += saltSz; sz += saltSz; + + /* place itteration count in buffer */ + out[inOutIdx++] = ASN_INTEGER; sz++; + out[inOutIdx++] = sizeof(word32); sz++; + out[inOutIdx++] = (itt >> 24) & 0xFF; + out[inOutIdx++] = (itt >> 16) & 0xFF; + out[inOutIdx++] = (itt >> 8 ) & 0xFF; + out[inOutIdx++] = itt & 0xFF; + sz += 4; + + /* wind back index and set sequence then clean up buffer */ + inOutIdx -= (sz + MAX_SEQ_SZ); + tmpSz = SetSequence(sz, out + inOutIdx); + XMEMMOVE(out + inOutIdx + tmpSz, out + inOutIdx + MAX_SEQ_SZ, sz); + inOutIdx += tmpSz + sz; totalSz += tmpSz + sz; + + /* octet string containing encrypted key */ + out[inOutIdx++] = ASN_OCTET_STRING; totalSz++; + } + + /* 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 */ + if (wc_CreatePKCS8Key(NULL, &tmpSz, key, keySz, algoID, curveOID, oidSz) + != LENGTH_ONLY_E) { + #ifdef WOLFSSL_SMALL_STACK + if (saltTmp != NULL) + XFREE(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif + return MEMORY_E; + } + + /* check if should return max size */ + if (out == NULL) { + /* account for salt size */ + if (salt == NULL || saltSz <= 0) { + tmpSz += MAX_SALT_SIZE; + } + else { + tmpSz += saltSz; + } + + /* plus 3 for tags */ + *outSz = tmpSz + MAX_ALGO_SZ + MAX_LENGTH_SZ +MAX_LENGTH_SZ + MAX_SEQ_SZ + + MAX_LENGTH_SZ + MAX_SEQ_SZ + 3; + return LENGTH_ONLY_E; + } + + tmp = XMALLOC(tmpSz, heap, DYNAMIC_TYPE_TMP_BUFFER); + if (tmp == NULL) { + #ifdef WOLFSSL_SMALL_STACK + if (saltTmp != NULL) + XFREE(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif + return MEMORY_E; + } + + if ((ret = wc_CreatePKCS8Key(tmp, &tmpSz, key, keySz, algoID, curveOID, + oidSz)) < 0) { + XFREE(tmp, heap, DYNAMIC_TYPE_TMP_BUFFER); + WOLFSSL_MSG("Error wrapping key with PKCS#8"); + #ifdef WOLFSSL_SMALL_STACK + if (saltTmp != NULL) + XFREE(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif + return ret; + } + +#ifdef WOLFSSL_SMALL_STACK + cbcIv = (byte*)XMALLOC(MAX_IV_SIZE, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (cbcIv == NULL) { + if (saltTmp != NULL) + XFREE(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(salt, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return MEMORY_E; + } +#endif + + /* encrypt PKCS#8 wrapped key */ + if ((ret = CryptKey(password, passwordSz, salt, saltSz, itt, id, + tmp, tmpSz, version, cbcIv, 1)) < 0) { + XFREE(tmp, heap, DYNAMIC_TYPE_TMP_BUFFER); + WOLFSSL_MSG("Error encrypting key"); + #ifdef WOLFSSL_SMALL_STACK + if (saltTmp != NULL) + XFREE(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER); + if (cbcIv != NULL) + XFREE(cbcIv, heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif + return ret; /* encryption failure */ + } + totalSz += tmpSz; + + #ifdef WOLFSSL_SMALL_STACK + if (saltTmp != NULL) + XFREE(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER); + if (cbcIv != NULL) + XFREE(cbcIv, heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif + + if (*outSz < inOutIdx + tmpSz + MAX_LENGTH_SZ) { + XFREE(tmp, heap, DYNAMIC_TYPE_TMP_BUFFER); + return BUFFER_E; + } + + /* set length of key and copy over encrypted key */ + seqSz = SetLength(tmpSz, out + inOutIdx); + inOutIdx += seqSz; totalSz += seqSz; + XMEMCPY(out + inOutIdx, tmp, tmpSz); + XFREE(tmp, heap, DYNAMIC_TYPE_TMP_BUFFER); + + /* set total size at begining */ + sz = SetSequence(totalSz, out); + XMEMMOVE(out + sz, out + MAX_SEQ_SZ, totalSz); + + return totalSz + sz; +} + + /* Remove Encrypted PKCS8 header, move beginning of traditional to beginning of input */ int ToTraditionalEnc(byte* input, word32 sz,const char* password,int passwordSz) @@ -2340,8 +2585,8 @@ int ToTraditionalEnc(byte* input, word32 sz,const char* password,int passwordSz) if (ret < 0) goto exit_tte; - ret = DecryptKey(password, passwordSz, salt, saltSz, iterations, id, - input + inOutIdx, length, version, cbcIv); + ret = CryptKey(password, passwordSz, salt, saltSz, iterations, id, + input + inOutIdx, length, version, cbcIv, 0); exit_tte: #ifdef WOLFSSL_SMALL_STACK @@ -2357,6 +2602,156 @@ exit_tte: return ret; } + +/* encrypt PKCS 12 content */ +int EncryptContent(byte* input, word32 inputSz, byte* out, word32* outSz, + const char* password, int passwordSz, int vPKCS, int vAlgo, + byte* salt, word32 saltSz, int itt, WC_RNG* rng, void* heap) +{ + word32 sz; + word32 inOutIdx = 0; + word32 tmpIdx = 0; + word32 totalSz = 0; + word32 seqSz; + int ret; + int version, id; +#ifdef WOLFSSL_SMALL_STACK + byte* saltTmp = NULL; + byte* cbcIv = NULL; +#else + byte saltTmp[MAX_SALT_SIZE]; + byte cbcIv[MAX_IV_SIZE]; +#endif + + (void)heap; + + WOLFSSL_ENTER("EncryptContent()"); + + if (CheckAlgo(vPKCS, vAlgo, &id, &version) < 0) + return ASN_INPUT_E; /* Algo ID error */ + + if (version == PKCS5v2) { + WOLFSSL_MSG("PKCS#5 version 2 not supported yet"); + return BAD_FUNC_ARG; + } + + if (saltSz > MAX_SALT_SIZE) + return ASN_PARSE_E; + + if (out == NULL) { + sz = inputSz; + switch (id) { + case PBE_MD5_DES: + case PBE_SHA1_DES: + case PBE_SHA1_DES3: + /* set to block size of 8 for DES operations. This rounds up + * to the nearset multiple of 8 */ + sz &= 0xfffffff8; + sz += 8; + break; + + case PBE_SHA1_RC4_128: + break; + + case -1: + break; + + default: + return ALGO_ID_E; + } + + if (saltSz <= 0) { + sz += MAX_SALT_SIZE; + } + else { + sz += saltSz; + } + + /* add 2 for tags */ + *outSz = sz + MAX_ALGO_SZ + MAX_SEQ_SZ + MAX_LENGTH_SZ + + MAX_LENGTH_SZ + MAX_LENGTH_SZ + MAX_SHORT_SZ + 2; + + return LENGTH_ONLY_E; + } + + if (inOutIdx + MAX_ALGO_SZ + MAX_SEQ_SZ + 1 > *outSz) + return BUFFER_E; + + sz = SetAlgoID(id, out + inOutIdx, oidPBEType, 0); + inOutIdx += sz; totalSz += sz; + tmpIdx = inOutIdx; + tmpIdx += MAX_SEQ_SZ; /* save room for salt and itter sequence */ + out[tmpIdx++] = ASN_OCTET_STRING; + + /* create random salt if one not provided */ + if (salt == NULL || saltSz <= 0) { + saltSz = 8; + salt = saltTmp; + + #ifdef WOLFSSL_SMALL_STACK + saltTmp = (byte*)XMALLOC(saltSz, heap, DYNAMIC_TYPE_TMP_BUFFER); + if (saltTmp == NULL) + return MEMORY_E; + #endif + + if ((ret = wc_RNG_GenerateBlock(rng, saltTmp, saltSz)) != 0) { + WOLFSSL_MSG("Error generating random salt"); + XFREE(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER); + return ret; + } + } + + if (tmpIdx + MAX_LENGTH_SZ + saltSz + MAX_SHORT_SZ > *outSz) + return BUFFER_E; + + sz = SetLength(saltSz, out + tmpIdx); + tmpIdx += sz; + + XMEMCPY(out + tmpIdx, salt, saltSz); + tmpIdx += saltSz; + + /* place itteration setting in buffer */ + out[tmpIdx++] = ASN_INTEGER; + out[tmpIdx++] = sizeof(word32); + out[tmpIdx++] = (itt >> 24) & 0xFF; + out[tmpIdx++] = (itt >> 16) & 0xFF; + out[tmpIdx++] = (itt >> 8) & 0xFF; + out[tmpIdx++] = itt & 0xFF; + + /* rewind and place sequence */ + sz = tmpIdx - inOutIdx - MAX_SEQ_SZ; + seqSz = SetSequence(sz, out + inOutIdx); + XMEMMOVE(out + inOutIdx + seqSz, out + inOutIdx + MAX_SEQ_SZ, sz); + inOutIdx += seqSz; totalSz += seqSz; + inOutIdx += sz; totalSz += sz; + +#ifdef WOLFSSL_SMALL_STACK + cbcIv = (byte*)XMALLOC(MAX_IV_SIZE, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (cbcIv == NULL) { + XFREE(salt, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return MEMORY_E; + } +#endif + + if ((ret = CryptKey(password, passwordSz, salt, saltSz, itt, id, + input, inputSz, version, cbcIv, 1)) < 0) { + + return ret; /* encrypt failure */ + } + + if (inOutIdx + 1 + MAX_LENGTH_SZ + inputSz > *outSz) + return BUFFER_E; + + out[inOutIdx++] = ASN_LONG_LENGTH; totalSz++; + sz = SetLength(inputSz, out + inOutIdx); + inOutIdx += sz; totalSz += sz; + XMEMCPY(out + inOutIdx, input, inputSz); + totalSz += inputSz; + + return totalSz; +} + + /* decrypt PKCS */ int DecryptContent(byte* input, word32 sz,const char* password,int passwordSz) { @@ -2477,8 +2872,8 @@ int DecryptContent(byte* input, word32 sz,const char* password,int passwordSz) ERROR_OUT(ASN_PARSE_E, exit_dc); } - ret = DecryptKey(password, passwordSz, salt, saltSz, iterations, id, - input + inOutIdx, length, version, cbcIv); + ret = CryptKey(password, passwordSz, salt, saltSz, iterations, id, + input + inOutIdx, length, version, cbcIv, 0); exit_dc: diff --git a/wolfcrypt/src/pkcs12.c b/wolfcrypt/src/pkcs12.c index 2ca7219224..953f8f9a9b 100644 --- a/wolfcrypt/src/pkcs12.c +++ b/wolfcrypt/src/pkcs12.c @@ -55,8 +55,24 @@ enum { WC_PKCS12_SafeContentsBag = 672, WC_PKCS12_DATA = 651, WC_PKCS12_ENCRYPTED_DATA = 656, + + WC_PKCS12_DATA_OBJ_SZ = 11, }; +/* 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; @@ -454,10 +470,9 @@ exit_gsd: } -/* 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) +/* expects PKCS12 signData to be set up with OID */ +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; @@ -465,22 +480,20 @@ static int wc_PKCS12_verify(WC_PKCS12* pkcs12, byte* data, word32 dataSz, int idx = 0; int id = 3; /* value from RFC 7292 indicating key is used for MAC */ word32 i; - byte digest[MAX_DIGEST_SIZE]; byte unicodePasswd[MAX_UNICODE_SZ]; byte key[MAX_KEY_SIZE]; + if (pkcs12 == NULL || pkcs12->signData == NULL || data == NULL || + out == 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 > MAX_DIGEST_SIZE) { - WOLFSSL_MSG("PKCS12 max digest size too small"); - return BAD_FUNC_ARG; - } - /* unicode set up from asn.c */ if ((pswSz * 2 + 2) > (int)sizeof(unicodePasswd)) { WOLFSSL_MSG("PKCS12 max unicode size too small"); @@ -526,6 +539,11 @@ static int wc_PKCS12_verify(WC_PKCS12* pkcs12, byte* data, word32 dataSz, return BAD_FUNC_ARG; } + /* check out buffer is large enough */ + if (kLen < 0 || outSz < (word32)kLen) { + return BAD_FUNC_ARG; + } + /* idx contains size of unicodePasswd */ if ((ret = wc_PKCS12_PBKDF_ex(key, unicodePasswd, idx, mac->salt, mac->saltSz, mac->itt, kLen, typeH, id, pkcs12->heap)) < 0) { @@ -540,12 +558,46 @@ static int wc_PKCS12_verify(WC_PKCS12* pkcs12, byte* data, word32 dataSz, if (ret == 0) ret = wc_HmacUpdate(&hmac, data, dataSz); if (ret == 0) - ret = wc_HmacFinal(&hmac, digest); + 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[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 > 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, MAX_DIGEST_SIZE)) < 0) { + return ret; + } + #ifdef WOLFSSL_DEBUG_PKCS12 { byte* p; @@ -1071,7 +1123,7 @@ int wc_PKCS12_parse(WC_PKCS12* pkcs12, const char* psw, } else { /* free list, not wanted */ - freeCertList(certList, pkcs12->heap); + wc_FreeCertList(certList, pkcs12->heap); } ret = 0; /* success */ @@ -1096,6 +1148,832 @@ exit_pk12par: } + +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; + int vPKCS = 1; /* PKCS#12 is always set to 1 */ + word32 sz; + word32 totalSz = 0; + int ret; + + + 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; + } + + /* 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(out + tmpIdx, &sz, key, keySz, algoID, + curveOID, oidSz); + } + else { + WOLFSSL_MSG("creating PKCS12 Shrouded Key Bag"); + + ret = UnTraditionalEnc(key, keySz, out + tmpIdx, &sz, pass, passSz, + vPKCS, vAlgo, NULL, 0, itt, rng, heap); + } + if (ret == LENGTH_ONLY_E) { + *outSz = sz + MAX_LENGTH_SZ + 1; + return LENGTH_ONLY_E; + } + if (ret < 0) { + return ret; + } + + totalSz += 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 = SetExplicit(0, ret, out + tmpIdx); + tmpIdx += sz; totalSz += sz; + XMEMMOVE(out + tmpIdx, out + MAX_LENGTH_SZ + 1, ret); + + return totalSz; +} + + +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 != 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 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 = ret; + XMEMCPY(out + idx, tmp, length); + XFREE(tmp, heap, DYNAMIC_TYPE_TMP_BUFFER); + totalSz += length; + + /* set begining sequence */ + tmpSz = SetSequence(totalSz, out); + XMEMMOVE(out + tmpSz, out + MAX_SEQ_SZ, totalSz); + + return totalSz + tmpSz; +} + + +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 = 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 + certSz; + return LENGTH_ONLY_E; + } + + /* check buffer size able to handle max size */ + if (*outSz < (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 + 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 totalSz + tmpSz; +} + + +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 vPKCS = 1; /* PKCS#12 is always set to 1 */ + int ret; + byte* tmp; + word32 idx = 0; + word32 totalSz = 0; + word32 length = 0; + word32 tmpSz; + word32 encSz; + word32 i; + + 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) { + if (out == NULL) { + *outSz = 1 + MAX_LENGTH_SZ + MAX_SEQ_SZ + MAX_VERSION_SZ + + MAX_SEQ_SZ + WC_PKCS12_DATA_OBJ_SZ; + ret = EncryptContent(NULL, contentSz + MAX_SEQ_SZ, NULL, &encSz, + pass, passSz, vPKCS, vAlgo, NULL, 0, iter, rng, heap); + if (ret != LENGTH_ONLY_E) { + return ret; + } + + *outSz += encSz; + return LENGTH_ONLY_E; + } + + if (*outSz < (1 + MAX_LENGTH_SZ + MAX_SEQ_SZ + MAX_VERSION_SZ)) { + return BUFFER_E; + } + out[idx++] = (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC); totalSz++; + + /* save room for length and sequence */ + idx += MAX_LENGTH_SZ; + idx += MAX_SEQ_SZ; + + tmpSz = SetMyVersion(0, out + idx, 0); + idx += tmpSz; length += tmpSz; + + encSz = contentSz; + if ((ret = EncryptContent(NULL, contentSz, NULL, &encSz, + pass, passSz, vPKCS, vAlgo, NULL, 0, iter, rng, heap)) < 0) { + if (ret != LENGTH_ONLY_E) { + return ret; + } + } + + if (*outSz < (idx + MAX_SEQ_SZ + WC_PKCS12_DATA_OBJ_SZ + encSz)) { + return BUFFER_E; + } + 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, vAlgo, NULL, 0, iter, rng, heap)) < 0) { + return ret; + } + encSz = 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 + + tmpSz = SetSequence(WC_PKCS12_DATA_OBJ_SZ + encSz, out + idx); + idx += tmpSz; length += tmpSz; + + out[idx++] = ASN_OBJECT_ID; length++; + tmpSz = SetLength(sizeof(WC_PKCS12_DATA_OID), out + idx); + idx += tmpSz; length += tmpSz; + for (i = 0; i < sizeof(WC_PKCS12_DATA_OID); i++) { + out[idx++] = WC_PKCS12_DATA_OID[i]; length++; + } + + /* copy over encrypted data */ + XMEMCPY(out + idx, tmp, encSz); + XFREE(tmp, heap, DYNAMIC_TYPE_TMP_BUFFER); + idx += encSz; length += encSz; + + /* rewind and place sequence */ + idx -= (length + MAX_SEQ_SZ); + tmpSz = SetSequence(length, out + idx); + XMEMMOVE(out + idx + tmpSz, out + idx + MAX_SEQ_SZ, length); + length += tmpSz; + + /* now place length */ + idx -= MAX_LENGTH_SZ; + tmpSz = SetLength(length, out + idx); + XMEMMOVE(out + idx + tmpSz, out + idx + MAX_LENGTH_SZ, length); + totalSz += length + tmpSz; + + return totalSz; + } + + /* DATA + * ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC + * length + * ASN_OCTET_STRING + * length + * sequence containing all bags */ + if (type == WC_PKCS12_DATA) { + if (out == NULL) { + *outSz = 1 + MAX_LENGTH_SZ + 1 + MAX_LENGTH_SZ + contentSz; + return LENGTH_ONLY_E; + } + + if (*outSz < (1 + MAX_LENGTH_SZ + 1 + MAX_LENGTH_SZ + contentSz)) { + return BUFFER_E; + } + + out[idx++] = (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC); + totalSz++; + + /* save room for length */ + idx += MAX_LENGTH_SZ; + + out[idx++] = ASN_OCTET_STRING; length++; + tmpSz = SetLength(contentSz, out + idx); + idx += tmpSz; length += tmpSz; + + /* sequence containing all bags */ + XMEMCPY(out + idx, content, contentSz); + idx += contentSz; length += contentSz; + + idx -= (MAX_LENGTH_SZ + length); + tmpSz = SetLength(length, out + idx); + XMEMMOVE(out + idx + tmpSz, out + idx + MAX_LENGTH_SZ, length); + totalSz += length + tmpSz; + + return totalSz; + } + + WOLFSSL_MSG("Unknown/Unsupported content type"); + return BAD_FUNC_ARG; +} + + +/* + * p : password to use with encryption + * name : friendlyName to use + * key : DER format of key with keySz being the size of this buffer + * cert : DER format of certificate with certSz being the size of this buffer + * ca : a list of extra certificates + * nidKey : type of encryption to use on the key (-1 means no encryption) + * nidCert : type of ecnryption to use on the certificate + * (-1 means no encryption) + * iter : number of itterations with encryption + * macIter : number of itterations when creating MAC + * keyType : flag for signature and/or encryption key + */ +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; + AuthenticatedSafe* safe; + ContentInfo* ci; + WC_RNG rng; + int algo; + int ret; + int type; + word32 idx; + word32 sz; + word32 tmpSz; + + byte* certCi = NULL; + word32 certCiSz; + byte* keyCi; + word32 keyCiSz; + + byte* certBuf = NULL; + word32 certBufSz; + byte* keyBuf; + word32 keyBufSz; + + WOLFSSL_ENTER("wc_PKCS12_create()"); + + pkcs12 = wc_PKCS12_new(); + wc_PKCS12_SetHeap(pkcs12, heap); + wc_InitRng_ex(&rng, heap, INVALID_DEVID); + + + if (iter <= 0) { + iter = WC_PKCS12_ITT_DEFAULT; + } + + /**** add private key bag ****/ + switch (nidKey) { + case PBE_SHA1_RC4_128: + algo = 1; + break; + + case PBE_SHA1_DES: + algo = 2; + break; + + case PBE_SHA1_DES3: + algo = 3; + break; + + /* no encryption */ + case -1: + algo = -1; + break; + + default: + WOLFSSL_MSG("Unknown/Unsupported key encryption"); + wc_PKCS12_free(pkcs12); + wc_FreeRng(&rng); + return NULL; + } + + /* get max size for key bag */ + ret = wc_PKCS12_create_key_bag(pkcs12, &rng, NULL, &keyBufSz, key, keySz, + algo, iter, pass, passSz); + if (ret != LENGTH_ONLY_E && ret < 0) { + wc_PKCS12_free(pkcs12); + wc_FreeRng(&rng); + WOLFSSL_LEAVE("wc_PKCS12_create", ret); + return NULL; + } + + /* account for sequence around bag */ + keyBufSz += MAX_SEQ_SZ; + + keyBuf = (byte*)XMALLOC(keyBufSz, heap, DYNAMIC_TYPE_TMP_BUFFER); + if (keyBuf == NULL) { + wc_PKCS12_free(pkcs12); + wc_FreeRng(&rng); + WOLFSSL_MSG("Memory error"); + return NULL; + } + + ret = wc_PKCS12_create_key_bag(pkcs12, &rng, keyBuf + MAX_SEQ_SZ, &keyBufSz, + key, keySz, algo, iter, pass, passSz); + if (ret < 0) { + wc_PKCS12_free(pkcs12); + wc_FreeRng(&rng); + XFREE(keyBuf, heap, DYNAMIC_TYPE_TMP_BUFFER); + WOLFSSL_LEAVE("wc_PKCS12_create", ret); + return NULL; + } + keyBufSz = ret; + + tmpSz = SetSequence(keyBufSz, keyBuf); + XMEMMOVE(keyBuf + tmpSz, keyBuf + MAX_SEQ_SZ, keyBufSz); + keyBufSz += tmpSz; + + ret = wc_PKCS12_encrypt_content(pkcs12, &rng, NULL, &keyCiSz, + NULL, keyBufSz, algo, pass, passSz, iter, WC_PKCS12_DATA); + if (ret != LENGTH_ONLY_E) { + wc_PKCS12_free(pkcs12); + wc_FreeRng(&rng); + XFREE(keyBuf, heap, DYNAMIC_TYPE_TMP_BUFFER); + WOLFSSL_LEAVE("wc_PKCS12_create", ret); + return NULL; + } + keyCi = (byte*)XMALLOC(keyCiSz, heap, DYNAMIC_TYPE_TMP_BUFFER); + if (keyCi == NULL) { + wc_PKCS12_free(pkcs12); + wc_FreeRng(&rng); + XFREE(keyBuf, heap, DYNAMIC_TYPE_TMP_BUFFER); + return NULL; + } + + ret = wc_PKCS12_encrypt_content(pkcs12, &rng, keyCi, &keyCiSz, + keyBuf, keyBufSz, algo, pass, passSz, iter, WC_PKCS12_DATA); + if (ret < 0 ) { + wc_PKCS12_free(pkcs12); + wc_FreeRng(&rng); + XFREE(keyBuf, heap, DYNAMIC_TYPE_TMP_BUFFER); + WOLFSSL_LEAVE("wc_PKCS12_create", ret); + return NULL; + } + keyCiSz = ret; + XFREE(keyBuf, heap, DYNAMIC_TYPE_TMP_BUFFER); + + #ifdef WOLFSSL_DEBUG_PKCS12 + { + byte* p; + for (printf("(size %u) Key Content Info = ", keyCiSz), p = (byte*)keyCi; + p < (byte*)keyCi + keyCiSz; + printf("%02X", *p), p++); + printf("\n"); + } + #endif + + + /**** add main certificate bag and extras ****/ + switch (nidCert) { + 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 -1: + type = WC_PKCS12_DATA; + algo = -1; + break; + + default: + WOLFSSL_MSG("Unknown/Unsupported certificate encryption"); + wc_PKCS12_free(pkcs12); + wc_FreeRng(&rng); + return NULL; + } + + /* get max size of buffer needed */ + ret = wc_PKCS12_create_cert_bag(pkcs12, NULL, &certBufSz, cert, certSz); + if (ret != LENGTH_ONLY_E) { + wc_PKCS12_free(pkcs12); + wc_FreeRng(&rng); + 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 != LENGTH_ONLY_E) { + wc_PKCS12_free(pkcs12); + wc_FreeRng(&rng); + 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) { + wc_PKCS12_free(pkcs12); + wc_FreeRng(&rng); + 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); + wc_PKCS12_free(pkcs12); + wc_FreeRng(&rng); + return NULL; + } + idx += 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); + wc_PKCS12_free(pkcs12); + wc_FreeRng(&rng); + return NULL; + } + idx += 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, passSz, iter, type); + if (ret != LENGTH_ONLY_E) { + XFREE(certBuf, heap, DYNAMIC_TYPE_TMP_BUFFER); + wc_PKCS12_free(pkcs12); + wc_FreeRng(&rng); + 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); + wc_PKCS12_free(pkcs12); + wc_FreeRng(&rng); + return NULL; + } + + ret = wc_PKCS12_encrypt_content(pkcs12, &rng, certCi, &certCiSz, + certBuf, certBufSz, algo, pass, passSz, iter, type); + if (ret < 0) { + XFREE(certBuf, heap, DYNAMIC_TYPE_TMP_BUFFER); + wc_PKCS12_free(pkcs12); + wc_FreeRng(&rng); + WOLFSSL_LEAVE("wc_PKCS12_create()", ret); + return NULL; + } + certCiSz = ret; + XFREE(certBuf, heap, DYNAMIC_TYPE_TMP_BUFFER); + + #ifdef WOLFSSL_DEBUG_PKCS12 + { + byte* p; + for (printf("(size %u) Encrypted Certificate Content Info = ",certCiSz), + p = (byte*)certCi; + p < (byte*)certCi + certCiSz; + printf("%02X", *p), p++); + printf("\n"); + } + #endif + + /**** create safe and and Content Info ****/ + safe = (AuthenticatedSafe*)XMALLOC(sizeof(AuthenticatedSafe), heap, + DYNAMIC_TYPE_PKCS); + if (safe == NULL) { + wc_PKCS12_free(pkcs12); + wc_FreeRng(&rng); + return NULL; + } + pkcs12->safe = safe; /* set so all of safe is free'd with wc_PKCS12_free */ + XMEMSET(safe, 0, sizeof(AuthenticatedSafe)); + + safe->dataSz = certCiSz + keyCiSz; + safe->data = (byte*)XMALLOC(safe->dataSz, heap, DYNAMIC_TYPE_PKCS); + if (safe->data == NULL) { + wc_PKCS12_free(pkcs12); + wc_FreeRng(&rng); + return NULL; + } + XMEMCPY(safe->data, certCi, certCiSz); + XMEMCPY(safe->data + certCiSz, keyCi, keyCiSz); + XFREE(certCi, heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(keyCi, heap, DYNAMIC_TYPE_TMP_BUFFER); + + safe->numCI = 2; + + /* add Content Info structs to safe, key first then cert */ + ci = (ContentInfo*)XMALLOC(sizeof(ContentInfo), heap, DYNAMIC_TYPE_PKCS); + if (ci == NULL) { + wc_PKCS12_free(pkcs12); + wc_FreeRng(&rng); + return NULL; + } + XMEMSET(ci, 0, sizeof(ContentInfo)); + safe->CI = ci; + ci->data = safe->data + certCiSz; + ci->dataSz = keyCiSz; + ci->type = WC_PKCS12_DATA; + + ci = (ContentInfo*)XMALLOC(sizeof(ContentInfo), heap, DYNAMIC_TYPE_PKCS); + if (ci == NULL) { + wc_PKCS12_free(pkcs12); + wc_FreeRng(&rng); + return NULL; + } + XMEMSET(ci, 0, sizeof(ContentInfo)); + ci->next = safe->CI; + safe->CI = ci; + ci->data = safe->data; + ci->dataSz = certCiSz; + if (nidCert < 0) { + ci->type = WC_PKCS12_DATA; + } + else { + ci->type = WC_PKCS12_ENCRYPTED_DATA; + } + + /* create MAC */ + if (macIter > 0) { + MacData* mac; + byte digest[MAX_DIGEST_SIZE]; /* for MAC */ + + mac = (MacData*)XMALLOC(sizeof(MacData), heap, DYNAMIC_TYPE_PKCS); + if (mac == NULL) { + wc_PKCS12_free(pkcs12); + wc_FreeRng(&rng); + return NULL; + } + 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; + #elif + WOLFSSL_MSG("No supported hash algorithm compiled in!"); + wc_PKCS12_free(pkcs12); + wc_FreeRng(&rng); + return NULL; + #endif + + /* if macIter is 0 then default to 1 */ + if (macIter == 0) { + mac->itt = WC_PKCS12_MAC_DEFAULT; + } + else { + mac->itt = macIter; + } + + /* set mac salt */ + mac->saltSz = 8; + mac->salt = (byte*)XMALLOC(mac->saltSz, heap, DYNAMIC_TYPE_PKCS); + if (mac->salt == NULL) { + wc_PKCS12_free(pkcs12); + wc_FreeRng(&rng); + return NULL; + } + + if ((ret = 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, safe->data, safe->dataSz, + (const byte*)pass, passSz, digest, MAX_DIGEST_SIZE); + if (ret < 0) { + wc_PKCS12_free(pkcs12); + wc_FreeRng(&rng); + return NULL; + } + + mac->digestSz = ret; + mac->digest = (byte*)XMALLOC(ret, heap, DYNAMIC_TYPE_PKCS); + if (mac->digest == NULL) { + wc_PKCS12_free(pkcs12); + wc_FreeRng(&rng); + return NULL; + } + XMEMCPY(mac->digest, digest, mac->digestSz); + } + + wc_FreeRng(&rng); + (void)name; + (void)keyType; + + return pkcs12; +} + + /* if using a specific memory heap */ int wc_PKCS12_SetHeap(WC_PKCS12* pkcs12, void* heap) { @@ -1104,7 +1982,7 @@ int wc_PKCS12_SetHeap(WC_PKCS12* pkcs12, void* heap) } pkcs12->heap = heap; - return 0; + return 1; } diff --git a/wolfssl/openssl/pkcs12.h b/wolfssl/openssl/pkcs12.h index 1b95474548..92bb08ece5 100644 --- a/wolfssl/openssl/pkcs12.h +++ b/wolfssl/openssl/pkcs12.h @@ -21,8 +21,19 @@ /* pkcs12.h for openssl */ + +#include #include +#ifndef WOLFSSL_PKCS12_COMPAT_H_ +#define WOLFSSL_PKCS12_COMPAT_H_ + +#define NID_pbe_WithSHA1AndDES_CBC 2 +#define NID_pbe_WithSHA1And3_Key_TripleDES_CBC 3 +#define NID_pbe_WithSHA1And128BitRC4 1 + +#define PKCS12_DEFAULT_ITER WC_PKCS12_ITT_DEFAULT + /* wolfCrypt level does not make use of ssl.h */ #define PKCS12 WC_PKCS12 #define PKCS12_new wc_PKCS12_new @@ -31,5 +42,8 @@ /* wolfSSL level using structs from ssl.h and calls down to wolfCrypt */ #define d2i_PKCS12_bio wolfSSL_d2i_PKCS12_bio #define PKCS12_parse wolfSSL_PKCS12_parse +#define PKCS12_create wolfSSL_PKCS12_create #define PKCS12_PBE_add wolfSSL_PKCS12_PBE_add +#endif /* WOLFSSL_PKCS12_COMPAT_H_ */ + diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index ce7140aae0..4a4fbe55aa 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -1415,7 +1415,12 @@ typedef struct WC_PKCS12 WC_PKCS12; WOLFSSL_API WC_PKCS12* wolfSSL_d2i_PKCS12_bio(WOLFSSL_BIO* bio, WC_PKCS12** pkcs12); WOLFSSL_API int wolfSSL_PKCS12_parse(WC_PKCS12* pkcs12, const char* psw, - WOLFSSL_EVP_PKEY** pkey, WOLFSSL_X509** cert, WOLF_STACK_OF(WOLFSSL_X509)** ca); + WOLFSSL_EVP_PKEY** pkey, WOLFSSL_X509** cert, + WOLF_STACK_OF(WOLFSSL_X509)** ca); +WOLFSSL_API WC_PKCS12* wolfSSL_PKCS12_create(char* pass, char* name, + WOLFSSL_EVP_PKEY* pkey, WOLFSSL_X509* cert, + WOLF_STACK_OF(WOLFSSL_X509)* ca, + int keyNID, int certNID, int itt, int macItt, int keytype); WOLFSSL_API void wolfSSL_PKCS12_PBE_add(void); diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index 7a88482bed..310f611d98 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -108,10 +108,13 @@ enum DN_Tags { enum PBES { PBE_MD5_DES = 0, - PBE_SHA1_DES = 1, - PBE_SHA1_DES3 = 2, - PBE_SHA1_RC4_128 = 3, + PBE_SHA1_RC4_128 = 1, + PBE_SHA1_DES = 2, + PBE_SHA1_DES3 = 3, PBE_AES256_CBC = 4, + + PBE_SHA1_RC4_128_SUM = 657, + PBE_SHA1_DES3_SUM = 659, PBES2 = 13 /* algo ID */ }; @@ -146,6 +149,7 @@ enum Misc_ASN { MAX_ENCODED_SIG_SZ = 512, MAX_SIG_SZ = 256, MAX_ALGO_SZ = 20, + MAX_SHORT_SZ = 6, /* asn int + byte len + 4 byte length */ MAX_SEQ_SZ = 5, /* enum(seq | con) + length(4) */ MAX_SET_SZ = 5, /* enum(set | con) + length(4) */ MAX_OCTET_STR_SZ = 5, /* enum(set | con) + length(4) */ @@ -214,7 +218,8 @@ enum Oid_Types { oidKdfType = 11, oidKeyWrapType = 12, oidCmsKeyAgreeType = 13, - oidHmacType = 14, + oidPBEType = 14, + oidHmacType = 15, oidIgnoreType }; @@ -763,7 +768,13 @@ WOLFSSL_ASN_API int ToTraditional(byte* buffer, word32 length); WOLFSSL_LOCAL int ToTraditionalInline(const byte* input, word32* inOutIdx, word32 length); WOLFSSL_LOCAL int ToTraditionalEnc(byte* buffer, word32 length,const char*,int); +WOLFSSL_ASN_API int UnTraditionalEnc(byte* key, word32 keySz, byte* out, + word32* outSz, const char* password, int passwordSz, int vPKCS, + int vAlgo, byte* salt, word32 saltSz, int itt, WC_RNG* rng, void* heap); WOLFSSL_LOCAL int DecryptContent(byte* input, word32 sz,const char* psw,int pswSz); +WOLFSSL_LOCAL int EncryptContent(byte* input, word32 sz, byte* out, word32* outSz, + const char* password,int passwordSz, int vPKCS, int vAlgo, + byte* salt, word32 saltSz, int itt, WC_RNG* rng, void* heap); WOLFSSL_LOCAL int wc_GetKeyOID(byte* key, word32 keySz, const byte** curveOID, word32* oidSz, int* algoID, void* heap); diff --git a/wolfssl/wolfcrypt/pkcs12.h b/wolfssl/wolfcrypt/pkcs12.h index ca0ff3d74b..a8442f443b 100644 --- a/wolfssl/wolfcrypt/pkcs12.h +++ b/wolfssl/wolfcrypt/pkcs12.h @@ -39,7 +39,11 @@ typedef struct WC_DerCertList { /* dereferenced in ssl.c */ struct WC_DerCertList* next; } WC_DerCertList; - +/* default values for creating PKCS12 */ +enum { + WC_PKCS12_ITT_DEFAULT = 2048, + WC_PKCS12_MAC_DEFAULT = 1, +}; WOLFSSL_API WC_PKCS12* wc_PKCS12_new(void); WOLFSSL_API void wc_PKCS12_free(WC_PKCS12* pkcs12); @@ -47,10 +51,16 @@ WOLFSSL_API int wc_d2i_PKCS12(const byte* der, word32 derSz, WC_PKCS12* pkcs12); WOLFSSL_API int wc_PKCS12_parse(WC_PKCS12* pkcs12, const char* psw, byte** pkey, word32* pkeySz, byte** cert, word32* certSz, WC_DerCertList** ca); +WOLFSSL_API 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); -WOLFSSL_LOCAL int wc_PKCS12_SetHeap(WC_PKCS12* pkcs12, void* heap); -WOLFSSL_LOCAL void* wc_PKCS12_GetHeap(WC_PKCS12* pkcs12); +WOLFSSL_API int wc_PKCS12_SetHeap(WC_PKCS12* pkcs12, void* heap); +WOLFSSL_API void* wc_PKCS12_GetHeap(WC_PKCS12* pkcs12); + +WOLFSSL_LOCAL void wc_FreeCertList(WC_DerCertList* list, void* heap); #ifdef __cplusplus } /* extern "C" */