mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-01-27 08:42:19 +01:00
2087 lines
64 KiB
C
2087 lines
64 KiB
C
/* ssl_p7p12.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
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <wolfssl/wolfcrypt/settings.h>
|
|
|
|
#if defined(OPENSSL_EXTRA) && (defined(HAVE_FIPS) || defined(HAVE_SELFTEST))
|
|
#include <wolfssl/wolfcrypt/pkcs7.h>
|
|
#endif
|
|
#if defined(OPENSSL_ALL) && defined(HAVE_PKCS7)
|
|
#include <wolfssl/openssl/pkcs7.h>
|
|
#endif
|
|
|
|
#if !defined(WOLFSSL_SSL_P7P12_INCLUDED)
|
|
#ifndef WOLFSSL_IGNORE_FILE_WARN
|
|
#warning ssl_p7p12.c does not need to be compiled separately from ssl.c
|
|
#endif
|
|
#else
|
|
|
|
#if !defined(WOLFCRYPT_ONLY) && !defined(NO_CERTS)
|
|
|
|
/*******************************************************************************
|
|
* START OF PKCS7 APIs
|
|
******************************************************************************/
|
|
#ifdef HAVE_PKCS7
|
|
|
|
#ifdef OPENSSL_ALL
|
|
PKCS7* wolfSSL_PKCS7_new(void)
|
|
{
|
|
WOLFSSL_PKCS7* pkcs7;
|
|
int ret = 0;
|
|
|
|
pkcs7 = (WOLFSSL_PKCS7*)XMALLOC(sizeof(WOLFSSL_PKCS7), NULL,
|
|
DYNAMIC_TYPE_PKCS7);
|
|
if (pkcs7 != NULL) {
|
|
XMEMSET(pkcs7, 0, sizeof(WOLFSSL_PKCS7));
|
|
ret = wc_PKCS7_Init(&pkcs7->pkcs7, NULL, INVALID_DEVID);
|
|
}
|
|
|
|
if (ret != 0 && pkcs7 != NULL) {
|
|
XFREE(pkcs7, NULL, DYNAMIC_TYPE_PKCS7);
|
|
pkcs7 = NULL;
|
|
}
|
|
|
|
return (PKCS7*)pkcs7;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* wolfSSL_PKCS7_SIGNED_new - allocates PKCS7 and initialize it for a signed data
|
|
*
|
|
* RETURNS:
|
|
* returns pointer to the PKCS7 structure on success, otherwise returns NULL
|
|
*/
|
|
PKCS7_SIGNED* wolfSSL_PKCS7_SIGNED_new(void)
|
|
{
|
|
byte signedData[]= { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02};
|
|
PKCS7* pkcs7 = NULL;
|
|
|
|
if ((pkcs7 = wolfSSL_PKCS7_new()) == NULL)
|
|
return NULL;
|
|
pkcs7->contentOID = SIGNED_DATA;
|
|
if ((wc_PKCS7_SetContentType(pkcs7, signedData, sizeof(signedData))) < 0) {
|
|
if (pkcs7) {
|
|
wolfSSL_PKCS7_free(pkcs7);
|
|
return NULL;
|
|
}
|
|
}
|
|
return pkcs7;
|
|
}
|
|
|
|
void wolfSSL_PKCS7_free(PKCS7* pkcs7)
|
|
{
|
|
WOLFSSL_PKCS7* p7 = (WOLFSSL_PKCS7*)pkcs7;
|
|
|
|
if (p7 != NULL) {
|
|
XFREE(p7->data, NULL, DYNAMIC_TYPE_PKCS7);
|
|
wc_PKCS7_Free(&p7->pkcs7);
|
|
if (p7->certs)
|
|
wolfSSL_sk_pop_free(p7->certs, NULL);
|
|
XFREE(p7, NULL, DYNAMIC_TYPE_PKCS7);
|
|
}
|
|
}
|
|
|
|
void wolfSSL_PKCS7_SIGNED_free(PKCS7_SIGNED* p7)
|
|
{
|
|
wolfSSL_PKCS7_free(p7);
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Convert DER/ASN.1 encoded signedData structure to internal PKCS7
|
|
* structure. Note, does not support detached content.
|
|
*
|
|
* p7 - pointer to set to address of newly created PKCS7 structure on return
|
|
* in - pointer to pointer of DER/ASN.1 data
|
|
* len - length of input data, bytes
|
|
*
|
|
* Returns newly allocated and populated PKCS7 structure or NULL on error.
|
|
*/
|
|
PKCS7* wolfSSL_d2i_PKCS7(PKCS7** p7, const unsigned char** in, int len)
|
|
{
|
|
return wolfSSL_d2i_PKCS7_ex(p7, in, len, NULL, 0);
|
|
}
|
|
|
|
/* This internal function is only decoding and setting up the PKCS7 struct. It
|
|
* does not verify the PKCS7 signature.
|
|
*
|
|
* RETURNS:
|
|
* returns pointer to a PKCS7 structure on success, otherwise returns NULL
|
|
*/
|
|
static PKCS7* wolfSSL_d2i_PKCS7_only(PKCS7** p7, const unsigned char** in,
|
|
int len, byte* content, word32 contentSz)
|
|
{
|
|
WOLFSSL_PKCS7* pkcs7 = NULL;
|
|
|
|
WOLFSSL_ENTER("wolfSSL_d2i_PKCS7_ex");
|
|
|
|
if (in == NULL || *in == NULL || len < 0)
|
|
return NULL;
|
|
|
|
if ((pkcs7 = (WOLFSSL_PKCS7*)wolfSSL_PKCS7_new()) == NULL)
|
|
return NULL;
|
|
|
|
pkcs7->len = len;
|
|
pkcs7->data = (byte*)XMALLOC(pkcs7->len, NULL, DYNAMIC_TYPE_PKCS7);
|
|
if (pkcs7->data == NULL) {
|
|
wolfSSL_PKCS7_free((PKCS7*)pkcs7);
|
|
return NULL;
|
|
}
|
|
XMEMCPY(pkcs7->data, *in, pkcs7->len);
|
|
|
|
if (content != NULL) {
|
|
pkcs7->pkcs7.content = content;
|
|
pkcs7->pkcs7.contentSz = contentSz;
|
|
}
|
|
|
|
if (p7 != NULL)
|
|
*p7 = (PKCS7*)pkcs7;
|
|
*in += pkcs7->len;
|
|
return (PKCS7*)pkcs7;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* wolfSSL_d2i_PKCS7_ex - Converts the given unsigned char buffer of size len
|
|
* into a PKCS7 object. Optionally, accepts a byte buffer of content which
|
|
* is stored as the PKCS7 object's content, to support detached signatures.
|
|
* @param content The content which is signed, in case the signature is
|
|
* detached. Ignored if NULL.
|
|
* @param contentSz The size of the passed in content.
|
|
*
|
|
* RETURNS:
|
|
* returns pointer to a PKCS7 structure on success, otherwise returns NULL
|
|
*/
|
|
PKCS7* wolfSSL_d2i_PKCS7_ex(PKCS7** p7, const unsigned char** in, int len,
|
|
byte* content, word32 contentSz)
|
|
{
|
|
WOLFSSL_PKCS7* pkcs7 = NULL;
|
|
|
|
WOLFSSL_ENTER("wolfSSL_d2i_PKCS7_ex");
|
|
|
|
if (in == NULL || *in == NULL || len < 0)
|
|
return NULL;
|
|
|
|
pkcs7 = (WOLFSSL_PKCS7*)wolfSSL_d2i_PKCS7_only(p7, in, len, content,
|
|
contentSz);
|
|
if (pkcs7 != NULL) {
|
|
if (wc_PKCS7_VerifySignedData(&pkcs7->pkcs7, pkcs7->data, pkcs7->len)
|
|
!= 0) {
|
|
WOLFSSL_MSG("wc_PKCS7_VerifySignedData failed");
|
|
wolfSSL_PKCS7_free((PKCS7*)pkcs7);
|
|
if (p7 != NULL) {
|
|
*p7 = NULL;
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return (PKCS7*)pkcs7;
|
|
}
|
|
|
|
|
|
/**
|
|
* This API was added as a helper function for libest. It
|
|
* extracts a stack of certificates from the pkcs7 object.
|
|
* @param pkcs7 PKCS7 parameter object
|
|
* @return WOLFSSL_STACK_OF(WOLFSSL_X509)*
|
|
*/
|
|
WOLFSSL_STACK* wolfSSL_PKCS7_to_stack(PKCS7* pkcs7)
|
|
{
|
|
int i;
|
|
WOLFSSL_PKCS7* p7 = (WOLFSSL_PKCS7*)pkcs7;
|
|
WOLF_STACK_OF(WOLFSSL_X509)* ret = NULL;
|
|
|
|
WOLFSSL_ENTER("wolfSSL_PKCS7_to_stack");
|
|
|
|
if (!p7) {
|
|
WOLFSSL_MSG("Bad parameter");
|
|
return NULL;
|
|
}
|
|
|
|
if (p7->certs)
|
|
return p7->certs;
|
|
|
|
for (i = 0; i < MAX_PKCS7_CERTS && p7->pkcs7.cert[i]; i++) {
|
|
WOLFSSL_X509* x509 = wolfSSL_X509_d2i_ex(NULL, p7->pkcs7.cert[i],
|
|
p7->pkcs7.certSz[i], pkcs7->heap);
|
|
if (!ret)
|
|
ret = wolfSSL_sk_X509_new_null();
|
|
if (x509) {
|
|
if (wolfSSL_sk_X509_push(ret, x509) <= 0) {
|
|
wolfSSL_X509_free(x509);
|
|
WOLFSSL_MSG("wolfSSL_sk_X509_push error");
|
|
goto error;
|
|
}
|
|
}
|
|
else {
|
|
WOLFSSL_MSG("wolfSSL_X509_d2i error");
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
/* Save stack to free later */
|
|
if (p7->certs)
|
|
wolfSSL_sk_pop_free(p7->certs, NULL);
|
|
p7->certs = ret;
|
|
|
|
return ret;
|
|
error:
|
|
if (ret) {
|
|
wolfSSL_sk_pop_free(ret, NULL);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Return stack of signers contained in PKCS7 cert.
|
|
* Notes:
|
|
* - Currently only PKCS#7 messages with a single signer cert is supported.
|
|
* - Returned WOLFSSL_STACK must be freed by caller.
|
|
*
|
|
* pkcs7 - PKCS7 struct to retrieve signer certs from.
|
|
* certs - currently unused
|
|
* flags - flags to control function behavior.
|
|
*
|
|
* Return WOLFSSL_STACK of signers on success, NULL on error.
|
|
*/
|
|
WOLFSSL_STACK* wolfSSL_PKCS7_get0_signers(PKCS7* pkcs7, WOLFSSL_STACK* certs,
|
|
int flags)
|
|
{
|
|
WOLFSSL_X509* x509 = NULL;
|
|
WOLFSSL_STACK* signers = NULL;
|
|
WOLFSSL_PKCS7* p7 = (WOLFSSL_PKCS7*)pkcs7;
|
|
|
|
if (p7 == NULL)
|
|
return NULL;
|
|
|
|
/* Only PKCS#7 messages with a single cert that is the verifying certificate
|
|
* is supported.
|
|
*/
|
|
if (flags & PKCS7_NOINTERN) {
|
|
WOLFSSL_MSG("PKCS7_NOINTERN flag not supported");
|
|
return NULL;
|
|
}
|
|
|
|
signers = wolfSSL_sk_X509_new_null();
|
|
if (signers == NULL)
|
|
return NULL;
|
|
|
|
if (wolfSSL_d2i_X509(&x509, (const byte**)&p7->pkcs7.singleCert,
|
|
p7->pkcs7.singleCertSz) == NULL) {
|
|
wolfSSL_sk_X509_pop_free(signers, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
if (wolfSSL_sk_X509_push(signers, x509) <= 0) {
|
|
wolfSSL_sk_X509_pop_free(signers, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
(void)certs;
|
|
|
|
return signers;
|
|
}
|
|
|
|
#ifndef NO_BIO
|
|
|
|
PKCS7* wolfSSL_d2i_PKCS7_bio(WOLFSSL_BIO* bio, PKCS7** p7)
|
|
{
|
|
WOLFSSL_PKCS7* pkcs7;
|
|
int ret;
|
|
|
|
WOLFSSL_ENTER("wolfSSL_d2i_PKCS7_bio");
|
|
|
|
if (bio == NULL)
|
|
return NULL;
|
|
|
|
if ((pkcs7 = (WOLFSSL_PKCS7*)wolfSSL_PKCS7_new()) == NULL)
|
|
return NULL;
|
|
|
|
pkcs7->len = wolfSSL_BIO_get_len(bio);
|
|
pkcs7->data = (byte*)XMALLOC(pkcs7->len, NULL, DYNAMIC_TYPE_PKCS7);
|
|
if (pkcs7->data == NULL) {
|
|
wolfSSL_PKCS7_free((PKCS7*)pkcs7);
|
|
return NULL;
|
|
}
|
|
|
|
if ((ret = wolfSSL_BIO_read(bio, pkcs7->data, pkcs7->len)) <= 0) {
|
|
wolfSSL_PKCS7_free((PKCS7*)pkcs7);
|
|
return NULL;
|
|
}
|
|
/* pkcs7->len may change if using b64 for example */
|
|
pkcs7->len = ret;
|
|
|
|
if (wc_PKCS7_VerifySignedData(&pkcs7->pkcs7, pkcs7->data, pkcs7->len)
|
|
!= 0) {
|
|
WOLFSSL_MSG("wc_PKCS7_VerifySignedData failed");
|
|
wolfSSL_PKCS7_free((PKCS7*)pkcs7);
|
|
return NULL;
|
|
}
|
|
|
|
if (p7 != NULL)
|
|
*p7 = (PKCS7*)pkcs7;
|
|
return (PKCS7*)pkcs7;
|
|
}
|
|
|
|
int wolfSSL_i2d_PKCS7(PKCS7 *p7, unsigned char **out)
|
|
{
|
|
byte* output = NULL;
|
|
int localBuf = 0;
|
|
int len;
|
|
WC_RNG rng;
|
|
int ret = WC_NO_ERR_TRACE(WOLFSSL_FAILURE);
|
|
WOLFSSL_ENTER("wolfSSL_i2d_PKCS7");
|
|
|
|
if (!out || !p7) {
|
|
WOLFSSL_MSG("Bad parameter");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
|
|
if (!p7->rng) {
|
|
if (wc_InitRng(&rng) != 0) {
|
|
WOLFSSL_MSG("wc_InitRng error");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
p7->rng = &rng; /* cppcheck-suppress autoVariables
|
|
*/
|
|
}
|
|
|
|
if ((len = wc_PKCS7_EncodeSignedData(p7, NULL, 0)) < 0) {
|
|
WOLFSSL_MSG("wc_PKCS7_EncodeSignedData error");
|
|
goto cleanup;
|
|
}
|
|
|
|
if (*out == NULL) {
|
|
output = (byte*)XMALLOC(len, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (!output) {
|
|
WOLFSSL_MSG("malloc error");
|
|
goto cleanup;
|
|
}
|
|
localBuf = 1;
|
|
}
|
|
else {
|
|
output = *out;
|
|
}
|
|
|
|
if ((len = wc_PKCS7_EncodeSignedData(p7, output, (word32)len)) < 0) {
|
|
WOLFSSL_MSG("wc_PKCS7_EncodeSignedData error");
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = len;
|
|
cleanup:
|
|
if (p7->rng == &rng) {
|
|
wc_FreeRng(&rng);
|
|
p7->rng = NULL;
|
|
}
|
|
if (ret == WC_NO_ERR_TRACE(WOLFSSL_FAILURE) && localBuf)
|
|
XFREE(output, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (ret != WC_NO_ERR_TRACE(WOLFSSL_FAILURE))
|
|
*out = output;
|
|
return ret;
|
|
}
|
|
|
|
int wolfSSL_i2d_PKCS7_bio(WOLFSSL_BIO *bio, PKCS7 *p7)
|
|
{
|
|
byte* output = NULL;
|
|
int len;
|
|
int ret = WC_NO_ERR_TRACE(WOLFSSL_FAILURE);
|
|
WOLFSSL_ENTER("wolfSSL_i2d_PKCS7_bio");
|
|
|
|
if (!bio || !p7) {
|
|
WOLFSSL_MSG("Bad parameter");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
|
|
if ((len = wolfSSL_i2d_PKCS7(p7, &output)) ==
|
|
WC_NO_ERR_TRACE(WOLFSSL_FAILURE))
|
|
{
|
|
WOLFSSL_MSG("wolfSSL_i2d_PKCS7 error");
|
|
goto cleanup;
|
|
}
|
|
|
|
if (wolfSSL_BIO_write(bio, output, len) <= 0) {
|
|
WOLFSSL_MSG("wolfSSL_BIO_write error");
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = WOLFSSL_SUCCESS;
|
|
cleanup:
|
|
XFREE(output, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Creates and returns a PKCS7 signedData structure.
|
|
*
|
|
* Inner content type is set to DATA to match OpenSSL behavior.
|
|
*
|
|
* signer - certificate to sign bundle with
|
|
* pkey - private key matching signer
|
|
* certs - optional additional set of certificates to include
|
|
* in - input data to be signed
|
|
* flags - optional set of flags to control sign behavior
|
|
*
|
|
* PKCS7_BINARY - Do not translate input data to MIME canonical
|
|
* format (\r\n line endings), thus preventing corruption of
|
|
* binary content.
|
|
* PKCS7_TEXT - Prepend MIME headers for text/plain to content.
|
|
* PKCS7_DETACHED - Set signature detached, omit content from output bundle.
|
|
* PKCS7_STREAM - initialize PKCS7 struct for signing, do not read data.
|
|
*
|
|
* Flags not currently supported:
|
|
* PKCS7_NOCERTS - Do not include the signer cert in the output bundle.
|
|
* PKCS7_PARTIAL - Allow for PKCS7_sign() to be only partially set up,
|
|
* then signers etc to be added separately before
|
|
* calling PKCS7_final().
|
|
*
|
|
* Returns valid PKCS7 structure pointer, or NULL if an error occurred.
|
|
*/
|
|
PKCS7* wolfSSL_PKCS7_sign(WOLFSSL_X509* signer, WOLFSSL_EVP_PKEY* pkey,
|
|
WOLFSSL_STACK* certs, WOLFSSL_BIO* in, int flags)
|
|
{
|
|
int err = 0;
|
|
WOLFSSL_PKCS7* p7 = NULL;
|
|
WOLFSSL_STACK* cert = certs;
|
|
|
|
WOLFSSL_ENTER("wolfSSL_PKCS7_sign");
|
|
|
|
if (flags & PKCS7_NOCERTS) {
|
|
WOLFSSL_MSG("PKCS7_NOCERTS flag not yet supported");
|
|
err = 1;
|
|
}
|
|
|
|
if (flags & PKCS7_PARTIAL) {
|
|
WOLFSSL_MSG("PKCS7_PARTIAL flag not yet supported");
|
|
err = 1;
|
|
}
|
|
|
|
if ((err == 0) && (signer == NULL || signer->derCert == NULL ||
|
|
signer->derCert->length == 0)) {
|
|
WOLFSSL_MSG("Bad function arg, signer is NULL or incomplete");
|
|
err = 1;
|
|
}
|
|
|
|
if ((err == 0) && (pkey == NULL || pkey->pkey.ptr == NULL ||
|
|
pkey->pkey_sz <= 0)) {
|
|
WOLFSSL_MSG("Bad function arg, pkey is NULL or incomplete");
|
|
err = 1;
|
|
}
|
|
|
|
if ((err == 0) && (in == NULL) && !(flags & PKCS7_STREAM)) {
|
|
WOLFSSL_MSG("input data required unless PKCS7_STREAM used");
|
|
err = 1;
|
|
}
|
|
|
|
if ((err == 0) && ((p7 = (WOLFSSL_PKCS7*)wolfSSL_PKCS7_new()) == NULL)) {
|
|
WOLFSSL_MSG("Error allocating new WOLFSSL_PKCS7");
|
|
err = 1;
|
|
}
|
|
|
|
/* load signer certificate */
|
|
if (err == 0) {
|
|
if (wc_PKCS7_InitWithCert(&p7->pkcs7, signer->derCert->buffer,
|
|
signer->derCert->length) != 0) {
|
|
WOLFSSL_MSG("Failed to load signer certificate");
|
|
err = 1;
|
|
}
|
|
}
|
|
|
|
/* set signer private key, data types, defaults */
|
|
if (err == 0) {
|
|
p7->pkcs7.privateKey = (byte*)pkey->pkey.ptr;
|
|
p7->pkcs7.privateKeySz = (word32)pkey->pkey_sz;
|
|
p7->pkcs7.contentOID = DATA; /* inner content default is DATA */
|
|
p7->pkcs7.hashOID = SHA256h; /* default to SHA-256 hash type */
|
|
p7->type = SIGNED_DATA; /* PKCS7_final switches on type */
|
|
}
|
|
|
|
/* add additional chain certs if provided */
|
|
while (cert && (err == 0)) {
|
|
if (cert->data.x509 != NULL && cert->data.x509->derCert != NULL) {
|
|
if (wc_PKCS7_AddCertificate(&p7->pkcs7,
|
|
cert->data.x509->derCert->buffer,
|
|
cert->data.x509->derCert->length) != 0) {
|
|
WOLFSSL_MSG("Error in wc_PKCS7_AddCertificate");
|
|
err = 1;
|
|
}
|
|
}
|
|
cert = cert->next;
|
|
}
|
|
|
|
if ((err == 0) && (flags & PKCS7_DETACHED)) {
|
|
if (wc_PKCS7_SetDetached(&p7->pkcs7, 1) != 0) {
|
|
WOLFSSL_MSG("Failed to set signature detached");
|
|
err = 1;
|
|
}
|
|
}
|
|
|
|
if ((err == 0) && (flags & PKCS7_STREAM)) {
|
|
/* if streaming, return before finalizing */
|
|
return (PKCS7*)p7;
|
|
}
|
|
|
|
if ((err == 0) && (wolfSSL_PKCS7_final((PKCS7*)p7, in, flags) != 1)) {
|
|
WOLFSSL_MSG("Error calling wolfSSL_PKCS7_final");
|
|
err = 1;
|
|
}
|
|
|
|
if ((err != 0) && (p7 != NULL)) {
|
|
wolfSSL_PKCS7_free((PKCS7*)p7);
|
|
p7 = NULL;
|
|
}
|
|
|
|
return (PKCS7*)p7;
|
|
}
|
|
|
|
#ifdef HAVE_SMIME
|
|
|
|
#ifndef MAX_MIME_LINE_LEN
|
|
#define MAX_MIME_LINE_LEN 1024
|
|
#endif
|
|
|
|
/**
|
|
* Copy input BIO to output BIO, but convert all line endings to CRLF (\r\n),
|
|
* used by PKCS7_final().
|
|
*
|
|
* in - input WOLFSSL_BIO to be converted
|
|
* out - output WOLFSSL_BIO to hold copy of in, with line endings adjusted
|
|
*
|
|
* Return 0 on success, negative on error
|
|
*/
|
|
static int wolfSSL_BIO_to_MIME_crlf(WOLFSSL_BIO* in, WOLFSSL_BIO* out)
|
|
{
|
|
int ret = 0;
|
|
int lineLen = 0;
|
|
word32 canonLineLen = 0;
|
|
char* canonLine = NULL;
|
|
#ifdef WOLFSSL_SMALL_STACK
|
|
char* line = NULL;
|
|
#else
|
|
char line[MAX_MIME_LINE_LEN];
|
|
#endif
|
|
|
|
if (in == NULL || out == NULL) {
|
|
return BAD_FUNC_ARG;
|
|
}
|
|
|
|
#ifdef WOLFSSL_SMALL_STACK
|
|
line = (char*)XMALLOC(MAX_MIME_LINE_LEN, in->heap,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (line == NULL) {
|
|
return MEMORY_E;
|
|
}
|
|
#endif
|
|
XMEMSET(line, 0, MAX_MIME_LINE_LEN);
|
|
|
|
while ((lineLen = wolfSSL_BIO_gets(in, line, MAX_MIME_LINE_LEN)) > 0) {
|
|
|
|
if (line[lineLen - 1] == '\r' || line[lineLen - 1] == '\n') {
|
|
canonLineLen = (word32)lineLen;
|
|
if ((canonLine = wc_MIME_single_canonicalize(
|
|
line, &canonLineLen)) == NULL) {
|
|
ret = WOLFSSL_FATAL_ERROR;
|
|
break;
|
|
}
|
|
|
|
/* remove trailing null */
|
|
if (canonLineLen >= 1 && canonLine[canonLineLen-1] == '\0') {
|
|
canonLineLen--;
|
|
}
|
|
|
|
if (wolfSSL_BIO_write(out, canonLine, (int)canonLineLen) < 0) {
|
|
ret = WOLFSSL_FATAL_ERROR;
|
|
break;
|
|
}
|
|
XFREE(canonLine, NULL, DYNAMIC_TYPE_PKCS7);
|
|
canonLine = NULL;
|
|
}
|
|
else {
|
|
/* no line ending in current line, write direct to out */
|
|
if (wolfSSL_BIO_write(out, line, lineLen) < 0) {
|
|
ret = WOLFSSL_FATAL_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
XFREE(canonLine, NULL, DYNAMIC_TYPE_PKCS7);
|
|
#ifdef WOLFSSL_SMALL_STACK
|
|
XFREE(line, in->heap, DYNAMIC_TYPE_TMP_BUFFER);
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
#endif /* HAVE_SMIME */
|
|
|
|
/* Used by both PKCS7_final() and PKCS7_verify() */
|
|
static const char contTypeText[] = "Content-Type: text/plain\r\n\r\n";
|
|
|
|
/**
|
|
* Finalize PKCS7 structure, currently supports signedData only.
|
|
*
|
|
* Does not generate final bundle (ie: signedData), but finalizes
|
|
* the PKCS7 structure in preparation for a output function to be called next.
|
|
*
|
|
* pkcs7 - initialized PKCS7 structure, populated with signer, etc
|
|
* in - input data
|
|
* flags - flags to control PKCS7 behavior. Other flags except those noted
|
|
* below are ignored:
|
|
*
|
|
* PKCS7_BINARY - Do not translate input data to MIME canonical
|
|
* format (\r\n line endings), thus preventing corruption of
|
|
* binary content.
|
|
* PKCS7_TEXT - Prepend MIME headers for text/plain to content.
|
|
*
|
|
* Returns 1 on success, 0 on error
|
|
*/
|
|
int wolfSSL_PKCS7_final(PKCS7* pkcs7, WOLFSSL_BIO* in, int flags)
|
|
{
|
|
int ret = 1;
|
|
int memSz = 0;
|
|
unsigned char* mem = NULL;
|
|
WOLFSSL_PKCS7* p7 = (WOLFSSL_PKCS7*)pkcs7;
|
|
WOLFSSL_BIO* data = NULL;
|
|
|
|
WOLFSSL_ENTER("wolfSSL_PKCS7_final");
|
|
|
|
if (p7 == NULL || in == NULL) {
|
|
WOLFSSL_MSG("Bad input args to PKCS7_final");
|
|
ret = 0;
|
|
}
|
|
|
|
if (ret == 1) {
|
|
if ((data = wolfSSL_BIO_new(wolfSSL_BIO_s_mem())) == NULL) {
|
|
WOLFSSL_MSG("Error in wolfSSL_BIO_new");
|
|
ret = 0;
|
|
}
|
|
}
|
|
|
|
/* prepend Content-Type header if PKCS7_TEXT */
|
|
if ((ret == 1) && (flags & PKCS7_TEXT)) {
|
|
if (wolfSSL_BIO_write(data, contTypeText,
|
|
(int)XSTR_SIZEOF(contTypeText)) < 0) {
|
|
WOLFSSL_MSG("Error prepending Content-Type header");
|
|
ret = 0;
|
|
}
|
|
}
|
|
|
|
/* convert line endings to CRLF if !PKCS7_BINARY */
|
|
if (ret == 1) {
|
|
if (flags & PKCS7_BINARY) {
|
|
|
|
/* no CRLF conversion, direct copy content */
|
|
if ((memSz = wolfSSL_BIO_get_len(in)) <= 0) {
|
|
ret = 0;
|
|
}
|
|
if (ret == 1) {
|
|
mem = (unsigned char*)XMALLOC(memSz, in->heap,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (mem == NULL) {
|
|
WOLFSSL_MSG("Failed to allocate memory for input data");
|
|
ret = 0;
|
|
}
|
|
}
|
|
|
|
if (ret == 1) {
|
|
if (wolfSSL_BIO_read(in, mem, memSz) != memSz) {
|
|
WOLFSSL_MSG("Error reading from input BIO");
|
|
ret = 0;
|
|
}
|
|
else if (wolfSSL_BIO_write(data, mem, memSz) < 0) {
|
|
ret = 0;
|
|
}
|
|
}
|
|
|
|
XFREE(mem, in->heap, DYNAMIC_TYPE_TMP_BUFFER);
|
|
}
|
|
else {
|
|
#ifdef HAVE_SMIME
|
|
/* convert content line endings to CRLF */
|
|
if (wolfSSL_BIO_to_MIME_crlf(in, data) != 0) {
|
|
WOLFSSL_MSG("Error converting line endings to CRLF");
|
|
ret = 0;
|
|
}
|
|
else {
|
|
p7->pkcs7.contentCRLF = 1;
|
|
}
|
|
#else
|
|
WOLFSSL_MSG("Without PKCS7_BINARY requires wolfSSL to be built "
|
|
"with HAVE_SMIME");
|
|
ret = 0;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if ((ret == 1) && ((memSz = wolfSSL_BIO_get_mem_data(data, &mem)) < 0)) {
|
|
WOLFSSL_MSG("Error in wolfSSL_BIO_get_mem_data");
|
|
ret = 0;
|
|
}
|
|
|
|
if (ret == 1) {
|
|
XFREE(p7->data, NULL, DYNAMIC_TYPE_PKCS7);
|
|
p7->data = (byte*)XMALLOC(memSz, NULL, DYNAMIC_TYPE_PKCS7);
|
|
if (p7->data == NULL) {
|
|
ret = 0;
|
|
}
|
|
else {
|
|
XMEMCPY(p7->data, mem, memSz);
|
|
p7->len = memSz;
|
|
}
|
|
}
|
|
|
|
if (ret == 1) {
|
|
p7->pkcs7.content = p7->data;
|
|
p7->pkcs7.contentSz = (word32)p7->len;
|
|
}
|
|
|
|
if (data != NULL) {
|
|
wolfSSL_BIO_free(data);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int wolfSSL_PKCS7_verify(PKCS7* pkcs7, WOLFSSL_STACK* certs,
|
|
WOLFSSL_X509_STORE* store, WOLFSSL_BIO* in, WOLFSSL_BIO* out, int flags)
|
|
{
|
|
int i, ret = 0;
|
|
unsigned char* mem = NULL;
|
|
int memSz = 0;
|
|
WOLFSSL_PKCS7* p7 = (WOLFSSL_PKCS7*)pkcs7;
|
|
int contTypeLen;
|
|
WOLFSSL_X509* signer = NULL;
|
|
WOLFSSL_STACK* signers = NULL;
|
|
|
|
WOLFSSL_ENTER("wolfSSL_PKCS7_verify");
|
|
|
|
if (pkcs7 == NULL)
|
|
return WOLFSSL_FAILURE;
|
|
|
|
if (in != NULL) {
|
|
if ((memSz = wolfSSL_BIO_get_mem_data(in, &mem)) < 0)
|
|
return WOLFSSL_FAILURE;
|
|
|
|
p7->pkcs7.content = mem;
|
|
p7->pkcs7.contentSz = (word32)memSz;
|
|
}
|
|
|
|
/* certs is the list of certificates to find the cert with issuer/serial. */
|
|
(void)certs;
|
|
/* store is the certificate store to use to verify signer certificate
|
|
* associated with the signers.
|
|
*/
|
|
(void)store;
|
|
|
|
ret = wc_PKCS7_VerifySignedData(&p7->pkcs7, p7->data, p7->len);
|
|
if (ret != 0)
|
|
return WOLFSSL_FAILURE;
|
|
|
|
if ((flags & PKCS7_NOVERIFY) != PKCS7_NOVERIFY) {
|
|
/* Verify signer certificates */
|
|
if (store == NULL || store->cm == NULL) {
|
|
WOLFSSL_MSG("No store or store certs, but PKCS7_NOVERIFY not set");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
|
|
signers = wolfSSL_PKCS7_get0_signers(pkcs7, certs, flags);
|
|
if (signers == NULL) {
|
|
WOLFSSL_MSG("No signers found to verify");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
for (i = 0; i < wolfSSL_sk_X509_num(signers); i++) {
|
|
signer = wolfSSL_sk_X509_value(signers, i);
|
|
|
|
if (wolfSSL_CertManagerVerifyBuffer(store->cm,
|
|
signer->derCert->buffer,
|
|
signer->derCert->length,
|
|
WOLFSSL_FILETYPE_ASN1) != WOLFSSL_SUCCESS) {
|
|
WOLFSSL_MSG("Failed to verify signer certificate");
|
|
wolfSSL_sk_X509_pop_free(signers, NULL);
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
}
|
|
wolfSSL_sk_X509_pop_free(signers, NULL);
|
|
}
|
|
|
|
if (flags & PKCS7_TEXT) {
|
|
/* strip MIME header for text/plain, otherwise error */
|
|
contTypeLen = XSTR_SIZEOF(contTypeText);
|
|
if ((p7->pkcs7.contentSz < (word32)contTypeLen) ||
|
|
(XMEMCMP(p7->pkcs7.content, contTypeText, contTypeLen) != 0)) {
|
|
WOLFSSL_MSG("Error PKCS7 Content-Type not found with PKCS7_TEXT");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
p7->pkcs7.content += contTypeLen;
|
|
p7->pkcs7.contentSz -= contTypeLen;
|
|
}
|
|
|
|
if (out != NULL) {
|
|
wolfSSL_BIO_write(out, p7->pkcs7.content, p7->pkcs7.contentSz);
|
|
}
|
|
|
|
WOLFSSL_LEAVE("wolfSSL_PKCS7_verify", WOLFSSL_SUCCESS);
|
|
|
|
return WOLFSSL_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* This API was added as a helper function for libest. It
|
|
* encodes a stack of certificates to pkcs7 format.
|
|
* @param pkcs7 PKCS7 parameter object
|
|
* @param certs WOLFSSL_STACK_OF(WOLFSSL_X509)*
|
|
* @param out Output bio
|
|
* @return WOLFSSL_SUCCESS on success and WOLFSSL_FAILURE on failure
|
|
*/
|
|
int wolfSSL_PKCS7_encode_certs(PKCS7* pkcs7, WOLFSSL_STACK* certs,
|
|
WOLFSSL_BIO* out)
|
|
{
|
|
int ret;
|
|
WOLFSSL_PKCS7* p7;
|
|
WOLFSSL_ENTER("wolfSSL_PKCS7_encode_certs");
|
|
|
|
if (!pkcs7 || !certs || !out) {
|
|
WOLFSSL_MSG("Bad parameter");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
|
|
p7 = (WOLFSSL_PKCS7*)pkcs7;
|
|
|
|
/* take ownership of certs */
|
|
p7->certs = certs;
|
|
/* TODO: takes ownership even on failure below but not on above failure. */
|
|
|
|
if (pkcs7->certList) {
|
|
WOLFSSL_MSG("wolfSSL_PKCS7_encode_certs called multiple times on same "
|
|
"struct");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
|
|
if (certs) {
|
|
/* Save some of the values */
|
|
int hashOID = pkcs7->hashOID;
|
|
byte version = pkcs7->version;
|
|
|
|
if (!certs->data.x509 || !certs->data.x509->derCert) {
|
|
WOLFSSL_MSG("Missing cert");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
|
|
if (wc_PKCS7_InitWithCert(pkcs7, certs->data.x509->derCert->buffer,
|
|
certs->data.x509->derCert->length) != 0) {
|
|
WOLFSSL_MSG("wc_PKCS7_InitWithCert error");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
certs = certs->next;
|
|
|
|
pkcs7->hashOID = hashOID;
|
|
pkcs7->version = version;
|
|
}
|
|
|
|
/* Add the certs to the PKCS7 struct */
|
|
while (certs) {
|
|
if (!certs->data.x509 || !certs->data.x509->derCert) {
|
|
WOLFSSL_MSG("Missing cert");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
if (wc_PKCS7_AddCertificate(pkcs7, certs->data.x509->derCert->buffer,
|
|
certs->data.x509->derCert->length) != 0) {
|
|
WOLFSSL_MSG("wc_PKCS7_AddCertificate error");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
certs = certs->next;
|
|
}
|
|
|
|
if (wc_PKCS7_SetSignerIdentifierType(pkcs7, DEGENERATE_SID) != 0) {
|
|
WOLFSSL_MSG("wc_PKCS7_SetSignerIdentifierType error");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
|
|
ret = wolfSSL_i2d_PKCS7_bio(out, pkcs7);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* wolfSSL_PEM_write_bio_PKCS7 - writes the PKCS7 data to BIO
|
|
*
|
|
* RETURNS:
|
|
* returns WOLFSSL_SUCCESS on success, otherwise returns WOLFSSL_FAILURE
|
|
*/
|
|
int wolfSSL_PEM_write_bio_PKCS7(WOLFSSL_BIO* bio, PKCS7* p7)
|
|
{
|
|
#ifdef WOLFSSL_SMALL_STACK
|
|
byte* outputHead;
|
|
byte* outputFoot;
|
|
#else
|
|
byte outputHead[2048];
|
|
byte outputFoot[2048];
|
|
#endif
|
|
word32 outputHeadSz = 2048;
|
|
word32 outputFootSz = 2048;
|
|
word32 outputSz = 0;
|
|
byte* output = NULL;
|
|
byte* pem = NULL;
|
|
int pemSz = -1;
|
|
enum wc_HashType hashType;
|
|
byte hashBuf[WC_MAX_DIGEST_SIZE];
|
|
word32 hashSz = 0;
|
|
|
|
WOLFSSL_ENTER("wolfSSL_PEM_write_bio_PKCS7");
|
|
|
|
if (bio == NULL || p7 == NULL)
|
|
return WOLFSSL_FAILURE;
|
|
|
|
#ifdef WOLFSSL_SMALL_STACK
|
|
outputHead = (byte*)XMALLOC(outputHeadSz, bio->heap,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (outputHead == NULL)
|
|
return MEMORY_E;
|
|
|
|
outputFoot = (byte*)XMALLOC(outputFootSz, bio->heap,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (outputFoot == NULL)
|
|
goto error;
|
|
|
|
#endif
|
|
|
|
XMEMSET(hashBuf, 0, WC_MAX_DIGEST_SIZE);
|
|
XMEMSET(outputHead, 0, outputHeadSz);
|
|
XMEMSET(outputFoot, 0, outputFootSz);
|
|
|
|
hashType = wc_OidGetHash(p7->hashOID);
|
|
hashSz = (word32)wc_HashGetDigestSize(hashType);
|
|
if (hashSz > WC_MAX_DIGEST_SIZE)
|
|
goto error;
|
|
|
|
/* only SIGNED_DATA is supported */
|
|
switch (p7->contentOID) {
|
|
case SIGNED_DATA:
|
|
break;
|
|
default:
|
|
WOLFSSL_MSG("Unknown PKCS#7 Type");
|
|
goto error;
|
|
};
|
|
|
|
if ((wc_PKCS7_EncodeSignedData_ex(p7, hashBuf, hashSz,
|
|
outputHead, &outputHeadSz, outputFoot, &outputFootSz)) != 0)
|
|
goto error;
|
|
|
|
outputSz = outputHeadSz + p7->contentSz + outputFootSz;
|
|
output = (byte*)XMALLOC(outputSz, bio->heap, DYNAMIC_TYPE_TMP_BUFFER);
|
|
|
|
if (!output)
|
|
goto error;
|
|
|
|
XMEMSET(output, 0, outputSz);
|
|
outputSz = 0;
|
|
XMEMCPY(&output[outputSz], outputHead, outputHeadSz);
|
|
outputSz += outputHeadSz;
|
|
XMEMCPY(&output[outputSz], p7->content, p7->contentSz);
|
|
outputSz += p7->contentSz;
|
|
XMEMCPY(&output[outputSz], outputFoot, outputFootSz);
|
|
outputSz += outputFootSz;
|
|
|
|
/* get PEM size */
|
|
pemSz = wc_DerToPemEx(output, outputSz, NULL, 0, NULL, CERT_TYPE);
|
|
if (pemSz < 0)
|
|
goto error;
|
|
|
|
pemSz++; /* for '\0'*/
|
|
|
|
/* create PEM buffer and convert from DER to PEM*/
|
|
if ((pem = (byte*)XMALLOC(pemSz, bio->heap, DYNAMIC_TYPE_TMP_BUFFER))
|
|
== NULL)
|
|
goto error;
|
|
|
|
XMEMSET(pem, 0, pemSz);
|
|
|
|
if (wc_DerToPemEx(output, outputSz, pem, (word32)pemSz, NULL, CERT_TYPE) < 0) {
|
|
goto error;
|
|
}
|
|
if ((wolfSSL_BIO_write(bio, pem, pemSz) == pemSz)) {
|
|
XFREE(output, bio->heap, DYNAMIC_TYPE_TMP_BUFFER);
|
|
XFREE(pem, bio->heap, DYNAMIC_TYPE_TMP_BUFFER);
|
|
#ifdef WOLFSSL_SMALL_STACK
|
|
XFREE(outputHead, bio->heap, DYNAMIC_TYPE_TMP_BUFFER);
|
|
XFREE(outputFoot, bio->heap, DYNAMIC_TYPE_TMP_BUFFER);
|
|
#endif
|
|
return WOLFSSL_SUCCESS;
|
|
}
|
|
|
|
error:
|
|
#ifdef WOLFSSL_SMALL_STACK
|
|
XFREE(outputHead, bio->heap, DYNAMIC_TYPE_TMP_BUFFER);
|
|
XFREE(outputFoot, bio->heap, DYNAMIC_TYPE_TMP_BUFFER);
|
|
#endif
|
|
XFREE(output, bio->heap, DYNAMIC_TYPE_TMP_BUFFER);
|
|
XFREE(pem, bio->heap, DYNAMIC_TYPE_TMP_BUFFER);
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
|
|
#ifdef HAVE_SMIME
|
|
/*****************************************************************************
|
|
* wolfSSL_SMIME_read_PKCS7 - Reads the given S/MIME message and parses it into
|
|
* a PKCS7 object. In case of a multipart message, stores the signed data in
|
|
* bcont.
|
|
*
|
|
* RETURNS:
|
|
* returns pointer to a PKCS7 structure on success, otherwise returns NULL
|
|
*/
|
|
PKCS7* wolfSSL_SMIME_read_PKCS7(WOLFSSL_BIO* in,
|
|
WOLFSSL_BIO** bcont)
|
|
{
|
|
MimeHdr* allHdrs = NULL;
|
|
MimeHdr* curHdr = NULL;
|
|
MimeParam* curParam = NULL;
|
|
int inLen = 0;
|
|
byte* bcontMem = NULL;
|
|
int bcontMemSz = 0;
|
|
int sectionLen = 0;
|
|
int ret = -1;
|
|
char* section = NULL;
|
|
char* canonLine = NULL;
|
|
char* canonSection = NULL;
|
|
PKCS7* pkcs7 = NULL;
|
|
word32 outLen = 0;
|
|
word32 canonLineLen = 0;
|
|
byte* out = NULL;
|
|
byte* outHead = NULL;
|
|
|
|
int canonPos = 0;
|
|
int lineLen = 0;
|
|
int remainLen = 0;
|
|
byte isEnd = 0;
|
|
size_t canonSize = 0;
|
|
size_t boundLen = 0;
|
|
char* boundary = NULL;
|
|
|
|
static const char kContType[] = "Content-Type";
|
|
static const char kCTE[] = "Content-Transfer-Encoding";
|
|
static const char kMultSigned[] = "multipart/signed";
|
|
static const char kAppPkcsSign[] = "application/pkcs7-signature";
|
|
static const char kAppXPkcsSign[] = "application/x-pkcs7-signature";
|
|
static const char kAppPkcs7Mime[] = "application/pkcs7-mime";
|
|
static const char kAppXPkcs7Mime[] = "application/x-pkcs7-mime";
|
|
|
|
WOLFSSL_ENTER("wolfSSL_SMIME_read_PKCS7");
|
|
|
|
if (in == NULL || bcont == NULL) {
|
|
goto error;
|
|
}
|
|
inLen = wolfSSL_BIO_get_len(in);
|
|
if (inLen <= 0) {
|
|
goto error;
|
|
}
|
|
remainLen = wolfSSL_BIO_get_len(in);
|
|
if (remainLen <= 0) {
|
|
goto error;
|
|
}
|
|
|
|
section = (char*)XMALLOC(remainLen+1, NULL, DYNAMIC_TYPE_PKCS7);
|
|
if (section == NULL) {
|
|
goto error;
|
|
}
|
|
lineLen = wolfSSL_BIO_gets(in, section, remainLen);
|
|
if (lineLen <= 0) {
|
|
goto error;
|
|
}
|
|
while (isEnd == 0 && remainLen > 0) {
|
|
sectionLen += lineLen;
|
|
remainLen -= lineLen;
|
|
lineLen = wolfSSL_BIO_gets(in, §ion[sectionLen], remainLen);
|
|
if (lineLen <= 0) {
|
|
goto error;
|
|
}
|
|
/* Line with just newline signals end of headers. */
|
|
if ((lineLen==2 && !XSTRNCMP(§ion[sectionLen],
|
|
"\r\n", 2)) ||
|
|
(lineLen==1 && (section[sectionLen] == '\r' ||
|
|
section[sectionLen] == '\n'))) {
|
|
isEnd = 1;
|
|
}
|
|
}
|
|
section[sectionLen] = '\0';
|
|
ret = wc_MIME_parse_headers(section, sectionLen, &allHdrs);
|
|
if (ret < 0) {
|
|
WOLFSSL_MSG("Parsing MIME headers failed.");
|
|
goto error;
|
|
}
|
|
isEnd = 0;
|
|
section[0] = '\0';
|
|
sectionLen = 0;
|
|
|
|
curHdr = wc_MIME_find_header_name(kContType, allHdrs);
|
|
if (curHdr && !XSTRNCMP(curHdr->body, kMultSigned,
|
|
XSTR_SIZEOF(kMultSigned))) {
|
|
curParam = wc_MIME_find_param_attr("protocol", curHdr->params);
|
|
if (curParam && (!XSTRNCMP(curParam->value, kAppPkcsSign,
|
|
XSTR_SIZEOF(kAppPkcsSign)) ||
|
|
!XSTRNCMP(curParam->value, kAppXPkcsSign,
|
|
XSTR_SIZEOF(kAppXPkcsSign)))) {
|
|
curParam = wc_MIME_find_param_attr("boundary", curHdr->params);
|
|
if (curParam == NULL) {
|
|
goto error;
|
|
}
|
|
|
|
boundLen = XSTRLEN(curParam->value) + 2;
|
|
boundary = (char*)XMALLOC(boundLen+1, NULL, DYNAMIC_TYPE_PKCS7);
|
|
if (boundary == NULL) {
|
|
goto error;
|
|
}
|
|
XMEMSET(boundary, 0, (word32)(boundLen+1));
|
|
boundary[0] = boundary[1] = '-';
|
|
/* analyzers have issues with using strncpy and strcpy here */
|
|
XMEMCPY(&boundary[2], curParam->value, boundLen - 2);
|
|
|
|
/* Parse up to first boundary, ignore everything here. */
|
|
lineLen = wolfSSL_BIO_gets(in, section, remainLen);
|
|
if (lineLen <= 0) {
|
|
goto error;
|
|
}
|
|
while (XSTRNCMP(§ion[sectionLen], boundary, boundLen) &&
|
|
remainLen > 0) {
|
|
sectionLen += lineLen;
|
|
remainLen -= lineLen;
|
|
lineLen = wolfSSL_BIO_gets(in, §ion[sectionLen],
|
|
remainLen);
|
|
if (lineLen <= 0) {
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
section[0] = '\0';
|
|
sectionLen = 0;
|
|
canonSize = (size_t)remainLen + 1;
|
|
canonSection = (char*)XMALLOC(canonSize, NULL,
|
|
DYNAMIC_TYPE_PKCS7);
|
|
if (canonSection == NULL) {
|
|
goto error;
|
|
}
|
|
|
|
lineLen = wolfSSL_BIO_gets(in, section, remainLen);
|
|
if (lineLen < 0) {
|
|
goto error;
|
|
}
|
|
while (XSTRNCMP(§ion[sectionLen], boundary, boundLen) &&
|
|
remainLen > 0) {
|
|
canonLineLen = (word32)lineLen;
|
|
canonLine = wc_MIME_single_canonicalize(§ion[sectionLen],
|
|
&canonLineLen);
|
|
if (canonLine == NULL) {
|
|
goto error;
|
|
}
|
|
/* If line endings were added, the initial length may be
|
|
* exceeded. */
|
|
if ((canonPos + canonLineLen) >= canonSize) {
|
|
canonSize = canonPos + canonLineLen;
|
|
canonSection = (char*)XREALLOC(canonSection, canonSize,
|
|
NULL, DYNAMIC_TYPE_PKCS7);
|
|
if (canonSection == NULL) {
|
|
goto error;
|
|
}
|
|
}
|
|
XMEMCPY(&canonSection[canonPos], canonLine,
|
|
(int)canonLineLen - 1);
|
|
canonPos += canonLineLen - 1;
|
|
XFREE(canonLine, NULL, DYNAMIC_TYPE_PKCS7);
|
|
canonLine = NULL;
|
|
|
|
sectionLen += lineLen;
|
|
remainLen -= lineLen;
|
|
|
|
lineLen = wolfSSL_BIO_gets(in, §ion[sectionLen],
|
|
remainLen);
|
|
if (lineLen <= 0) {
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
if (canonPos > 0) {
|
|
canonPos--;
|
|
}
|
|
|
|
/* Strip the final trailing newline. Support \r, \n or \r\n. */
|
|
if (canonSection[canonPos] == '\n') {
|
|
if (canonPos > 0) {
|
|
canonPos--;
|
|
}
|
|
}
|
|
|
|
if (canonSection[canonPos] == '\r') {
|
|
if (canonPos > 0) {
|
|
canonPos--;
|
|
}
|
|
}
|
|
|
|
canonSection[canonPos+1] = '\0';
|
|
|
|
*bcont = wolfSSL_BIO_new(wolfSSL_BIO_s_mem());
|
|
ret = wolfSSL_BIO_write(*bcont, canonSection,
|
|
canonPos + 1);
|
|
if (ret != (canonPos+1)) {
|
|
goto error;
|
|
}
|
|
if ((bcontMemSz = wolfSSL_BIO_get_mem_data(*bcont, &bcontMem))
|
|
< 0) {
|
|
goto error;
|
|
}
|
|
XFREE(canonSection, NULL, DYNAMIC_TYPE_PKCS7);
|
|
canonSection = NULL;
|
|
|
|
wc_MIME_free_hdrs(allHdrs);
|
|
allHdrs = NULL;
|
|
section[0] = '\0';
|
|
sectionLen = 0;
|
|
lineLen = wolfSSL_BIO_gets(in, section, remainLen);
|
|
if (lineLen <= 0) {
|
|
goto error;
|
|
}
|
|
while (isEnd == 0 && remainLen > 0) {
|
|
sectionLen += lineLen;
|
|
remainLen -= lineLen;
|
|
lineLen = wolfSSL_BIO_gets(in, §ion[sectionLen],
|
|
remainLen);
|
|
if (lineLen <= 0) {
|
|
goto error;
|
|
}
|
|
/* Line with just newline signals end of headers. */
|
|
if ((lineLen==2 && !XSTRNCMP(§ion[sectionLen],
|
|
"\r\n", 2)) ||
|
|
(lineLen==1 && (section[sectionLen] == '\r' ||
|
|
section[sectionLen] == '\n'))) {
|
|
isEnd = 1;
|
|
}
|
|
}
|
|
section[sectionLen] = '\0';
|
|
ret = wc_MIME_parse_headers(section, sectionLen, &allHdrs);
|
|
if (ret < 0) {
|
|
WOLFSSL_MSG("Parsing MIME headers failed.");
|
|
goto error;
|
|
}
|
|
curHdr = wc_MIME_find_header_name(kContType, allHdrs);
|
|
if (curHdr == NULL || (XSTRNCMP(curHdr->body, kAppPkcsSign,
|
|
XSTR_SIZEOF(kAppPkcsSign)) &&
|
|
XSTRNCMP(curHdr->body, kAppXPkcsSign,
|
|
XSTR_SIZEOF(kAppXPkcsSign)))) {
|
|
WOLFSSL_MSG("S/MIME headers not found inside "
|
|
"multipart message.\n");
|
|
goto error;
|
|
}
|
|
|
|
section[0] = '\0';
|
|
sectionLen = 0;
|
|
lineLen = wolfSSL_BIO_gets(in, section, remainLen);
|
|
while (XSTRNCMP(§ion[sectionLen], boundary, boundLen) &&
|
|
remainLen > 0) {
|
|
sectionLen += lineLen;
|
|
remainLen -= lineLen;
|
|
lineLen = wolfSSL_BIO_gets(in, §ion[sectionLen],
|
|
remainLen);
|
|
if (lineLen <= 0) {
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
XFREE(boundary, NULL, DYNAMIC_TYPE_PKCS7);
|
|
boundary = NULL;
|
|
}
|
|
}
|
|
else if (curHdr && (!XSTRNCMP(curHdr->body, kAppPkcs7Mime,
|
|
XSTR_SIZEOF(kAppPkcs7Mime)) ||
|
|
!XSTRNCMP(curHdr->body, kAppXPkcs7Mime,
|
|
XSTR_SIZEOF(kAppXPkcs7Mime)))) {
|
|
sectionLen = wolfSSL_BIO_get_len(in);
|
|
if (sectionLen <= 0) {
|
|
goto error;
|
|
}
|
|
ret = wolfSSL_BIO_read(in, section, sectionLen);
|
|
if (ret < 0 || ret != sectionLen) {
|
|
WOLFSSL_MSG("Error reading input BIO.");
|
|
goto error;
|
|
}
|
|
}
|
|
else {
|
|
WOLFSSL_MSG("S/MIME headers not found.");
|
|
goto error;
|
|
}
|
|
|
|
curHdr = wc_MIME_find_header_name(kCTE, allHdrs);
|
|
if (curHdr == NULL) {
|
|
WOLFSSL_MSG("Content-Transfer-Encoding header not found, "
|
|
"assuming base64 encoding.");
|
|
}
|
|
else if (XSTRNCMP(curHdr->body, "base64", XSTRLEN("base64"))) {
|
|
WOLFSSL_MSG("S/MIME encodings other than base64 are not "
|
|
"currently supported.\n");
|
|
goto error;
|
|
}
|
|
|
|
if (section == NULL || sectionLen <= 0) {
|
|
goto error;
|
|
}
|
|
outLen = (word32)((sectionLen*3+3)/4)+1;
|
|
out = (byte*)XMALLOC(outLen*sizeof(byte), NULL, DYNAMIC_TYPE_PKCS7);
|
|
outHead = out;
|
|
if (outHead == NULL) {
|
|
goto error;
|
|
}
|
|
/* Strip trailing newlines. */
|
|
while ((sectionLen > 0) &&
|
|
(section[sectionLen-1] == '\r' || section[sectionLen-1] == '\n')) {
|
|
sectionLen--;
|
|
}
|
|
section[sectionLen] = '\0';
|
|
ret = Base64_Decode((const byte*)section, (word32)sectionLen, out, &outLen);
|
|
if (ret < 0) {
|
|
WOLFSSL_MSG("Error base64 decoding S/MIME message.");
|
|
goto error;
|
|
}
|
|
pkcs7 = wolfSSL_d2i_PKCS7_only(NULL, (const unsigned char**)&out, (int)outLen,
|
|
bcontMem, (word32)bcontMemSz);
|
|
|
|
wc_MIME_free_hdrs(allHdrs);
|
|
XFREE(outHead, NULL, DYNAMIC_TYPE_PKCS7);
|
|
XFREE(section, NULL, DYNAMIC_TYPE_PKCS7);
|
|
|
|
return pkcs7;
|
|
|
|
error:
|
|
wc_MIME_free_hdrs(allHdrs);
|
|
XFREE(boundary, NULL, DYNAMIC_TYPE_PKCS7);
|
|
XFREE(outHead, NULL, DYNAMIC_TYPE_PKCS7);
|
|
XFREE(section, NULL, DYNAMIC_TYPE_PKCS7);
|
|
XFREE(canonSection, NULL, DYNAMIC_TYPE_PKCS7);
|
|
XFREE(canonLine, NULL, DYNAMIC_TYPE_PKCS7);
|
|
if (bcont) {
|
|
wolfSSL_BIO_free(*bcont);
|
|
*bcont = NULL; /* reset 'bcount' pointer to NULL on failure */
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Convert hash algo OID (from Hash_Sum in asn.h) to SMIME string equivalent.
|
|
* Returns hash algorithm string or "unknown" if not found */
|
|
static const char* wolfSSL_SMIME_HashOIDToString(int hashOID)
|
|
{
|
|
switch (hashOID) {
|
|
case MD5h:
|
|
return "md5";
|
|
case SHAh:
|
|
return "sha1";
|
|
case SHA224h:
|
|
return "sha-224";
|
|
case SHA256h:
|
|
return "sha-256";
|
|
case SHA384h:
|
|
return "sha-384";
|
|
case SHA512h:
|
|
return "sha-512";
|
|
case SHA3_224h:
|
|
return "sha3-224";
|
|
case SHA3_384h:
|
|
return "sha3-384";
|
|
case SHA3_512h:
|
|
return "sha3-512";
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return "unknown";
|
|
}
|
|
|
|
/* Convert PKCS#7 type (from PKCS7_TYPES in pkcs7.h) to SMIME string.
|
|
* RFC2633 only defines signed-data, enveloped-data, certs-only.
|
|
* Returns string on success, NULL on unknown type. */
|
|
static const char* wolfSSL_SMIME_PKCS7TypeToString(int type)
|
|
{
|
|
switch (type) {
|
|
case SIGNED_DATA:
|
|
return "signed-data";
|
|
case ENVELOPED_DATA:
|
|
return "enveloped-data";
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Convert PKCS7 structure to SMIME format, adding necessary headers.
|
|
*
|
|
* Handles generation of PKCS7 bundle (ie: signedData). PKCS7 structure
|
|
* should be set up beforehand with PKCS7_sign/final/etc. Output is always
|
|
* Base64 encoded.
|
|
*
|
|
* out - output BIO for SMIME formatted data to be placed
|
|
* pkcs7 - input PKCS7 structure, initialized and set up
|
|
* in - input content to be encoded into PKCS7
|
|
* flags - flags to control behavior of PKCS7 generation
|
|
*
|
|
* Returns 1 on success, 0 or negative on failure
|
|
*/
|
|
int wolfSSL_SMIME_write_PKCS7(WOLFSSL_BIO* out, PKCS7* pkcs7, WOLFSSL_BIO* in,
|
|
int flags)
|
|
{
|
|
int i;
|
|
int ret = 1;
|
|
WOLFSSL_PKCS7* p7 = (WOLFSSL_PKCS7*)pkcs7;
|
|
byte* p7out = NULL;
|
|
int len = 0;
|
|
|
|
char boundary[33]; /* 32 chars + \0 */
|
|
byte* sigBase64 = NULL;
|
|
word32 sigBase64Len = 0;
|
|
const char* p7TypeString = NULL;
|
|
|
|
static const char alphanum[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
if (out == NULL || p7 == NULL) {
|
|
WOLFSSL_MSG("Bad function arguments");
|
|
return 0;
|
|
}
|
|
|
|
if (in != NULL && (p7->pkcs7.content == NULL || p7->pkcs7.contentSz == 0 ||
|
|
p7->pkcs7.contentCRLF == 0)) {
|
|
/* store and adjust content line endings for CRLF if needed */
|
|
if (wolfSSL_PKCS7_final((PKCS7*)p7, in, flags) != 1) {
|
|
ret = 0;
|
|
}
|
|
}
|
|
|
|
if (ret > 0) {
|
|
/* Generate signedData bundle, DER in output (dynamic) */
|
|
if ((len = wolfSSL_i2d_PKCS7((PKCS7*)p7, &p7out)) ==
|
|
WC_NO_ERR_TRACE(WOLFSSL_FAILURE))
|
|
{
|
|
WOLFSSL_MSG("Error in wolfSSL_i2d_PKCS7");
|
|
ret = 0;
|
|
}
|
|
}
|
|
|
|
/* Base64 encode signedData bundle */
|
|
if (ret > 0) {
|
|
if (Base64_Encode(p7out, (word32)len, NULL, &sigBase64Len) !=
|
|
WC_NO_ERR_TRACE(LENGTH_ONLY_E)) {
|
|
ret = 0;
|
|
}
|
|
else {
|
|
sigBase64 = (byte*)XMALLOC(sigBase64Len, NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (sigBase64 == NULL) {
|
|
ret = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ret > 0) {
|
|
XMEMSET(sigBase64, 0, sigBase64Len);
|
|
if (Base64_Encode(p7out, (word32)len, sigBase64, &sigBase64Len) < 0) {
|
|
WOLFSSL_MSG("Error in Base64_Encode of signature");
|
|
ret = 0;
|
|
}
|
|
}
|
|
|
|
/* build up SMIME message */
|
|
if (ret > 0) {
|
|
if (flags & PKCS7_DETACHED) {
|
|
|
|
/* generate random boundary */
|
|
if (initGlobalRNG == 0 && wolfSSL_RAND_Init() != WOLFSSL_SUCCESS) {
|
|
WOLFSSL_MSG("No RNG to use");
|
|
ret = 0;
|
|
}
|
|
|
|
/* no need to generate random byte for null terminator (size-1) */
|
|
if ((ret > 0) && (wc_RNG_GenerateBlock(&globalRNG, (byte*)boundary,
|
|
sizeof(boundary) - 1 ) != 0)) {
|
|
WOLFSSL_MSG("Error in wc_RNG_GenerateBlock");
|
|
ret = 0;
|
|
}
|
|
|
|
if (ret > 0) {
|
|
for (i = 0; i < (int)sizeof(boundary) - 1; i++) {
|
|
boundary[i] =
|
|
alphanum[boundary[i] % XSTR_SIZEOF(alphanum)];
|
|
}
|
|
boundary[sizeof(boundary)-1] = 0;
|
|
}
|
|
|
|
if (ret > 0) {
|
|
/* S/MIME header beginning */
|
|
ret = wolfSSL_BIO_printf(out,
|
|
"MIME-Version: 1.0\n"
|
|
"Content-Type: multipart/signed; "
|
|
"protocol=\"application/x-pkcs7-signature\"; "
|
|
"micalg=\"%s\"; "
|
|
"boundary=\"----%s\"\n\n"
|
|
"This is an S/MIME signed message\n\n"
|
|
"------%s\n",
|
|
wolfSSL_SMIME_HashOIDToString(p7->pkcs7.hashOID),
|
|
boundary, boundary);
|
|
}
|
|
|
|
if (ret > 0) {
|
|
/* S/MIME content */
|
|
ret = wolfSSL_BIO_write(out,
|
|
p7->pkcs7.content, p7->pkcs7.contentSz);
|
|
}
|
|
|
|
if (ret > 0) {
|
|
/* S/SMIME header end boundary */
|
|
ret = wolfSSL_BIO_printf(out,
|
|
"\n------%s\n", boundary);
|
|
}
|
|
|
|
if (ret > 0) {
|
|
/* Signature and header */
|
|
ret = wolfSSL_BIO_printf(out,
|
|
"Content-Type: application/x-pkcs7-signature; "
|
|
"name=\"smime.p7s\"\n"
|
|
"Content-Transfer-Encoding: base64\n"
|
|
"Content-Disposition: attachment; "
|
|
"filename=\"smime.p7s\"\n\n"
|
|
"%.*s\n" /* Base64 encoded signature */
|
|
"------%s--\n\n",
|
|
sigBase64Len, sigBase64,
|
|
boundary);
|
|
}
|
|
}
|
|
else {
|
|
p7TypeString = wolfSSL_SMIME_PKCS7TypeToString(p7->type);
|
|
if (p7TypeString == NULL) {
|
|
WOLFSSL_MSG("Unsupported PKCS7 SMIME type");
|
|
ret = 0;
|
|
}
|
|
|
|
if (ret > 0) {
|
|
/* not detached */
|
|
ret = wolfSSL_BIO_printf(out,
|
|
"MIME-Version: 1.0\n"
|
|
"Content-Disposition: attachment; "
|
|
"filename=\"smime.p7m\"\n"
|
|
"Content-Type: application/x-pkcs7-mime; "
|
|
"smime-type=%s; name=\"smime.p7m\"\n"
|
|
"Content-Transfer-Encoding: base64\n\n"
|
|
"%.*s\n" /* signature */,
|
|
p7TypeString, sigBase64Len, sigBase64);
|
|
}
|
|
}
|
|
}
|
|
|
|
XFREE(p7out, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
XFREE(sigBase64, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
|
|
if (ret > 0) {
|
|
return WOLFSSL_SUCCESS;
|
|
}
|
|
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
|
|
#endif /* HAVE_SMIME */
|
|
#endif /* !NO_BIO */
|
|
#endif /* OPENSSL_ALL */
|
|
|
|
#endif /* HAVE_PKCS7 */
|
|
/*******************************************************************************
|
|
* END OF PKCS7 APIs
|
|
******************************************************************************/
|
|
|
|
/*******************************************************************************
|
|
* START OF PKCS12 APIs
|
|
******************************************************************************/
|
|
#ifdef OPENSSL_EXTRA
|
|
|
|
/* no-op function. Was initially used for adding encryption algorithms available
|
|
* for PKCS12 */
|
|
void wolfSSL_PKCS12_PBE_add(void)
|
|
{
|
|
WOLFSSL_ENTER("wolfSSL_PKCS12_PBE_add");
|
|
}
|
|
|
|
#if !defined(NO_FILESYSTEM) && !defined(NO_STDIO_FILESYSTEM)
|
|
WOLFSSL_X509_PKCS12 *wolfSSL_d2i_PKCS12_fp(XFILE fp,
|
|
WOLFSSL_X509_PKCS12 **pkcs12)
|
|
{
|
|
WOLFSSL_ENTER("wolfSSL_d2i_PKCS12_fp");
|
|
return (WOLFSSL_X509_PKCS12 *)wolfSSL_d2i_X509_fp_ex(fp, (void **)pkcs12,
|
|
PKCS12_TYPE);
|
|
}
|
|
#endif /* !NO_FILESYSTEM */
|
|
|
|
#endif /* OPENSSL_EXTRA */
|
|
|
|
#if defined(HAVE_PKCS12)
|
|
|
|
#ifdef OPENSSL_EXTRA
|
|
|
|
#if !defined(NO_ASN) && !defined(NO_PWDBASED)
|
|
|
|
#ifndef NO_BIO
|
|
WC_PKCS12* wolfSSL_d2i_PKCS12_bio(WOLFSSL_BIO* bio, WC_PKCS12** pkcs12)
|
|
{
|
|
WC_PKCS12* localPkcs12 = NULL;
|
|
unsigned char* mem = NULL;
|
|
long memSz;
|
|
int ret = -1;
|
|
|
|
WOLFSSL_ENTER("wolfSSL_d2i_PKCS12_bio");
|
|
|
|
if (bio == NULL) {
|
|
WOLFSSL_MSG("Bad Function Argument bio is NULL");
|
|
return NULL;
|
|
}
|
|
|
|
memSz = wolfSSL_BIO_get_len(bio);
|
|
if (memSz <= 0) {
|
|
return NULL;
|
|
}
|
|
mem = (unsigned char*)XMALLOC(memSz, bio->heap, DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (mem == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (mem != NULL) {
|
|
localPkcs12 = wc_PKCS12_new_ex(bio->heap);
|
|
if (localPkcs12 == NULL) {
|
|
WOLFSSL_MSG("Memory error");
|
|
}
|
|
}
|
|
|
|
if (mem != NULL && localPkcs12 != NULL) {
|
|
if (wolfSSL_BIO_read(bio, mem, (int)memSz) == memSz) {
|
|
ret = wc_d2i_PKCS12(mem, (word32)memSz, localPkcs12);
|
|
if (ret < 0) {
|
|
WOLFSSL_MSG("Failed to get PKCS12 sequence");
|
|
}
|
|
}
|
|
else {
|
|
WOLFSSL_MSG("Failed to get data from bio struct");
|
|
}
|
|
}
|
|
|
|
/* cleanup */
|
|
XFREE(mem, bio->heap, DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (ret < 0 && localPkcs12 != NULL) {
|
|
wc_PKCS12_free(localPkcs12);
|
|
localPkcs12 = NULL;
|
|
}
|
|
if (pkcs12 != NULL)
|
|
*pkcs12 = localPkcs12;
|
|
|
|
return localPkcs12;
|
|
}
|
|
|
|
/* Converts the PKCS12 to DER format and outputs it into bio.
|
|
*
|
|
* bio is the structure to hold output DER
|
|
* pkcs12 structure to create DER from
|
|
*
|
|
* return 1 for success or 0 if an error occurs
|
|
*/
|
|
int wolfSSL_i2d_PKCS12_bio(WOLFSSL_BIO *bio, WC_PKCS12 *pkcs12)
|
|
{
|
|
int ret = WC_NO_ERR_TRACE(WOLFSSL_FAILURE);
|
|
|
|
WOLFSSL_ENTER("wolfSSL_i2d_PKCS12_bio");
|
|
|
|
if ((bio != NULL) && (pkcs12 != NULL)) {
|
|
word32 certSz = 0;
|
|
byte *certDer = NULL;
|
|
|
|
certSz = (word32)wc_i2d_PKCS12(pkcs12, &certDer, NULL);
|
|
if ((certSz > 0) && (certDer != NULL)) {
|
|
if (wolfSSL_BIO_write(bio, certDer, (int)certSz) == (int)certSz) {
|
|
ret = WOLFSSL_SUCCESS;
|
|
}
|
|
}
|
|
|
|
XFREE(certDer, NULL, DYNAMIC_TYPE_PKCS);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* !NO_BIO */
|
|
|
|
/* Creates a new WC_PKCS12 structure
|
|
*
|
|
* pass password to use
|
|
* name friendlyName to use
|
|
* pkey private key to go into PKCS12 bundle
|
|
* cert certificate to go into PKCS12 bundle
|
|
* ca extra certificates that can be added to bundle. Can be NULL
|
|
* keyNID type of encryption to use on the key (-1 means no encryption)
|
|
* certNID type of encryption to use on the certificate
|
|
* itt number of iterations with encryption
|
|
* macItt number of iterations with mac creation
|
|
* keyType flag for signature and/or encryption key
|
|
*
|
|
* returns a pointer to a new WC_PKCS12 structure on success and NULL on fail
|
|
*/
|
|
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;
|
|
byte* keyDer = NULL;
|
|
word32 keyDerSz;
|
|
byte* certDer;
|
|
int certDerSz;
|
|
|
|
WOLFSSL_ENTER("wolfSSL_PKCS12_create");
|
|
|
|
if (pass == NULL || pkey == NULL || cert == NULL) {
|
|
WOLFSSL_LEAVE("wolfSSL_PKCS12_create", BAD_FUNC_ARG);
|
|
return NULL;
|
|
}
|
|
passSz = (word32)XSTRLEN(pass);
|
|
|
|
keyDer = (byte*)pkey->pkey.ptr;
|
|
keyDerSz = (word32)pkey->pkey_sz;
|
|
|
|
certDer = (byte*)wolfSSL_X509_get_der(cert, &certDerSz);
|
|
if (certDer == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (ca != NULL) {
|
|
unsigned long numCerts = ca->num;
|
|
WOLFSSL_STACK* sk = ca;
|
|
|
|
while (numCerts > 0 && sk != NULL) {
|
|
byte* curDer;
|
|
WC_DerCertList* cur;
|
|
int curDerSz = 0;
|
|
|
|
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 (curDer == NULL || curDerSz < 0) {
|
|
XFREE(cur, NULL, DYNAMIC_TYPE_PKCS);
|
|
wc_FreeCertList(list, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
cur->buffer = (byte*)XMALLOC(curDerSz, NULL, DYNAMIC_TYPE_PKCS);
|
|
if (cur->buffer == NULL) {
|
|
XFREE(cur, NULL, DYNAMIC_TYPE_PKCS);
|
|
wc_FreeCertList(list, NULL);
|
|
return NULL;
|
|
}
|
|
XMEMCPY(cur->buffer, curDer, curDerSz);
|
|
cur->bufferSz = (word32)curDerSz;
|
|
cur->next = list;
|
|
list = cur;
|
|
|
|
sk = sk->next;
|
|
numCerts--;
|
|
}
|
|
}
|
|
|
|
pkcs12 = wc_PKCS12_create(pass, passSz, name, keyDer, keyDerSz,
|
|
certDer, (word32)certDerSz, list, keyNID, certNID, itt, macItt,
|
|
keyType, NULL);
|
|
|
|
if (ca != NULL) {
|
|
wc_FreeCertList(list, NULL);
|
|
}
|
|
|
|
return pkcs12;
|
|
}
|
|
|
|
|
|
/* return WOLFSSL_SUCCESS on success, WOLFSSL_FAILURE 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)
|
|
{
|
|
void* heap = NULL;
|
|
int ret;
|
|
byte* certData = NULL;
|
|
word32 certDataSz;
|
|
byte* pk = NULL;
|
|
word32 pkSz;
|
|
WC_DerCertList* certList = NULL;
|
|
#ifdef WOLFSSL_SMALL_STACK
|
|
DecodedCert *DeCert;
|
|
#else
|
|
DecodedCert DeCert[1];
|
|
#endif
|
|
|
|
WOLFSSL_ENTER("wolfSSL_PKCS12_parse");
|
|
|
|
/* make sure we init return args */
|
|
if (pkey) *pkey = NULL;
|
|
if (cert) *cert = NULL;
|
|
if (ca) *ca = NULL;
|
|
|
|
if (pkcs12 == NULL || psw == NULL || pkey == NULL || cert == NULL) {
|
|
WOLFSSL_MSG("Bad argument value");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
|
|
heap = wc_PKCS12_GetHeap(pkcs12);
|
|
|
|
if (ca == NULL) {
|
|
ret = wc_PKCS12_parse(pkcs12, psw, &pk, &pkSz, &certData, &certDataSz,
|
|
NULL);
|
|
}
|
|
else {
|
|
ret = wc_PKCS12_parse(pkcs12, psw, &pk, &pkSz, &certData, &certDataSz,
|
|
&certList);
|
|
}
|
|
if (ret < 0) {
|
|
WOLFSSL_LEAVE("wolfSSL_PKCS12_parse", ret);
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
|
|
#ifdef WOLFSSL_SMALL_STACK
|
|
DeCert = (DecodedCert *)XMALLOC(sizeof(*DeCert), heap,
|
|
DYNAMIC_TYPE_DCERT);
|
|
if (DeCert == NULL) {
|
|
WOLFSSL_MSG("out of memory");
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
#endif
|
|
|
|
/* Decode cert and place in X509 stack struct */
|
|
if (certList != NULL) {
|
|
WC_DerCertList* current = certList;
|
|
|
|
*ca = (WOLF_STACK_OF(WOLFSSL_X509)*)XMALLOC(
|
|
sizeof(WOLF_STACK_OF(WOLFSSL_X509)), heap, DYNAMIC_TYPE_X509);
|
|
if (*ca == NULL) {
|
|
XFREE(pk, heap, DYNAMIC_TYPE_PUBLIC_KEY);
|
|
XFREE(certData, heap, DYNAMIC_TYPE_PKCS);
|
|
/* Free up WC_DerCertList and move on */
|
|
while (current != NULL) {
|
|
WC_DerCertList* next = current->next;
|
|
|
|
XFREE(current->buffer, heap, DYNAMIC_TYPE_PKCS);
|
|
XFREE(current, heap, DYNAMIC_TYPE_PKCS);
|
|
current = next;
|
|
}
|
|
ret = WOLFSSL_FAILURE;
|
|
goto out;
|
|
}
|
|
XMEMSET(*ca, 0, sizeof(WOLF_STACK_OF(WOLFSSL_X509)));
|
|
|
|
/* add list of DER certs as X509's to stack */
|
|
while (current != NULL) {
|
|
WC_DerCertList* toFree = current;
|
|
WOLFSSL_X509* x509;
|
|
|
|
x509 = (WOLFSSL_X509*)XMALLOC(sizeof(WOLFSSL_X509), heap,
|
|
DYNAMIC_TYPE_X509);
|
|
InitX509(x509, 1, heap);
|
|
InitDecodedCert(DeCert, current->buffer, current->bufferSz, heap);
|
|
if (ParseCertRelative(DeCert, CERT_TYPE, NO_VERIFY, NULL, NULL) != 0) {
|
|
WOLFSSL_MSG("Issue with parsing certificate");
|
|
FreeDecodedCert(DeCert);
|
|
wolfSSL_X509_free(x509);
|
|
}
|
|
else {
|
|
if (CopyDecodedToX509(x509, DeCert) != 0) {
|
|
WOLFSSL_MSG("Failed to copy decoded cert");
|
|
FreeDecodedCert(DeCert);
|
|
wolfSSL_X509_free(x509);
|
|
wolfSSL_sk_X509_pop_free(*ca, NULL); *ca = NULL;
|
|
XFREE(pk, heap, DYNAMIC_TYPE_PUBLIC_KEY);
|
|
XFREE(certData, heap, DYNAMIC_TYPE_PKCS);
|
|
/* Free up WC_DerCertList */
|
|
while (current != NULL) {
|
|
WC_DerCertList* next = current->next;
|
|
|
|
XFREE(current->buffer, heap, DYNAMIC_TYPE_PKCS);
|
|
XFREE(current, heap, DYNAMIC_TYPE_PKCS);
|
|
current = next;
|
|
}
|
|
ret = WOLFSSL_FAILURE;
|
|
goto out;
|
|
}
|
|
FreeDecodedCert(DeCert);
|
|
|
|
if (wolfSSL_sk_X509_push(*ca, x509) <= 0) {
|
|
WOLFSSL_MSG("Failed to push x509 onto stack");
|
|
wolfSSL_X509_free(x509);
|
|
wolfSSL_sk_X509_pop_free(*ca, NULL); *ca = NULL;
|
|
XFREE(pk, heap, DYNAMIC_TYPE_PUBLIC_KEY);
|
|
XFREE(certData, heap, DYNAMIC_TYPE_PKCS);
|
|
|
|
/* Free up WC_DerCertList */
|
|
while (current != NULL) {
|
|
WC_DerCertList* next = current->next;
|
|
|
|
XFREE(current->buffer, heap, DYNAMIC_TYPE_PKCS);
|
|
XFREE(current, heap, DYNAMIC_TYPE_PKCS);
|
|
current = next;
|
|
}
|
|
ret = WOLFSSL_FAILURE;
|
|
goto out;
|
|
}
|
|
}
|
|
current = current->next;
|
|
XFREE(toFree->buffer, heap, DYNAMIC_TYPE_PKCS);
|
|
XFREE(toFree, heap, DYNAMIC_TYPE_PKCS);
|
|
}
|
|
}
|
|
|
|
|
|
/* Decode cert and place in X509 struct */
|
|
if (certData != NULL) {
|
|
*cert = (WOLFSSL_X509*)XMALLOC(sizeof(WOLFSSL_X509), heap,
|
|
DYNAMIC_TYPE_X509);
|
|
if (*cert == NULL) {
|
|
XFREE(pk, heap, DYNAMIC_TYPE_PUBLIC_KEY);
|
|
if (ca != NULL) {
|
|
wolfSSL_sk_X509_pop_free(*ca, NULL); *ca = NULL;
|
|
}
|
|
XFREE(certData, heap, DYNAMIC_TYPE_PKCS);
|
|
ret = WOLFSSL_FAILURE;
|
|
goto out;
|
|
}
|
|
InitX509(*cert, 1, heap);
|
|
InitDecodedCert(DeCert, certData, certDataSz, heap);
|
|
if (ParseCertRelative(DeCert, CERT_TYPE, NO_VERIFY, NULL, NULL) != 0) {
|
|
WOLFSSL_MSG("Issue with parsing certificate");
|
|
}
|
|
if (CopyDecodedToX509(*cert, DeCert) != 0) {
|
|
WOLFSSL_MSG("Failed to copy decoded cert");
|
|
FreeDecodedCert(DeCert);
|
|
XFREE(pk, heap, DYNAMIC_TYPE_PUBLIC_KEY);
|
|
if (ca != NULL) {
|
|
wolfSSL_sk_X509_pop_free(*ca, NULL); *ca = NULL;
|
|
}
|
|
wolfSSL_X509_free(*cert); *cert = NULL;
|
|
XFREE(certData, heap, DYNAMIC_TYPE_PKCS);
|
|
ret = WOLFSSL_FAILURE;
|
|
goto out;
|
|
}
|
|
FreeDecodedCert(DeCert);
|
|
XFREE(certData, heap, DYNAMIC_TYPE_PKCS);
|
|
}
|
|
|
|
|
|
/* get key type */
|
|
ret = BAD_STATE_E;
|
|
if (pk != NULL) { /* decode key if present */
|
|
*pkey = wolfSSL_EVP_PKEY_new_ex(heap);
|
|
if (*pkey == NULL) {
|
|
wolfSSL_X509_free(*cert); *cert = NULL;
|
|
if (ca != NULL) {
|
|
wolfSSL_sk_X509_pop_free(*ca, NULL); *ca = NULL;
|
|
}
|
|
XFREE(pk, heap, DYNAMIC_TYPE_PUBLIC_KEY);
|
|
ret = WOLFSSL_FAILURE;
|
|
goto out;
|
|
}
|
|
|
|
#ifndef NO_RSA
|
|
{
|
|
const unsigned char* pt = pk;
|
|
if (wolfSSL_d2i_PrivateKey(WC_EVP_PKEY_RSA, pkey, &pt, pkSz) !=
|
|
NULL) {
|
|
ret = 0;
|
|
}
|
|
}
|
|
#endif /* NO_RSA */
|
|
|
|
#ifdef HAVE_ECC
|
|
if (ret != 0) { /* if is in fail state check if ECC key */
|
|
const unsigned char* pt = pk;
|
|
if (wolfSSL_d2i_PrivateKey(WC_EVP_PKEY_EC, pkey, &pt, pkSz) !=
|
|
NULL) {
|
|
ret = 0;
|
|
}
|
|
}
|
|
#endif /* HAVE_ECC */
|
|
XFREE(pk, heap, DYNAMIC_TYPE_PKCS);
|
|
if (ret != 0) { /* if is in fail state and no PKEY then fail */
|
|
wolfSSL_X509_free(*cert); *cert = NULL;
|
|
if (ca != NULL) {
|
|
wolfSSL_sk_X509_pop_free(*ca, NULL); *ca = NULL;
|
|
}
|
|
wolfSSL_EVP_PKEY_free(*pkey); *pkey = NULL;
|
|
WOLFSSL_MSG("Bad PKCS12 key format");
|
|
ret = WOLFSSL_FAILURE;
|
|
goto out;
|
|
}
|
|
|
|
if (pkey != NULL && *pkey != NULL) {
|
|
(*pkey)->save_type = 0;
|
|
}
|
|
}
|
|
|
|
(void)ret;
|
|
(void)ca;
|
|
|
|
ret = WOLFSSL_SUCCESS;
|
|
|
|
out:
|
|
|
|
#ifdef WOLFSSL_SMALL_STACK
|
|
XFREE(DeCert, heap, DYNAMIC_TYPE_DCERT);
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
int wolfSSL_PKCS12_verify_mac(WC_PKCS12 *pkcs12, const char *psw,
|
|
int pswLen)
|
|
{
|
|
WOLFSSL_ENTER("wolfSSL_PKCS12_verify_mac");
|
|
|
|
if (!pkcs12) {
|
|
return WOLFSSL_FAILURE;
|
|
}
|
|
|
|
return wc_PKCS12_verify_ex(pkcs12, (const byte*)psw, (word32)pswLen) == 0 ?
|
|
WOLFSSL_SUCCESS : WOLFSSL_FAILURE;
|
|
}
|
|
|
|
#endif /* !NO_ASN && !NO_PWDBASED */
|
|
|
|
#endif /* OPENSSL_EXTRA */
|
|
|
|
#endif /* HAVE_PKCS12 */
|
|
/*******************************************************************************
|
|
* END OF PKCS12 APIs
|
|
******************************************************************************/
|
|
|
|
#endif /* !WOLFCRYPT_ONLY && !NO_CERTS */
|
|
|
|
#endif /* !WOLFSSL_SSL_P7P12_INCLUDED */
|