add PKCS7_sign, PKCS7_final, SMIME_write_PKCS7. add signer cert verify support to PKCS7_verify, support for PKCS7_TEXT, PKCS7_DETACHED, PKCS7_STREAM

This commit is contained in:
Chris Conlon
2022-02-04 17:26:28 -07:00
parent ba4f3379ba
commit ce514e6fc5
5 changed files with 636 additions and 43 deletions

650
src/ssl.c
View File

@ -20907,7 +20907,7 @@ size_t wolfSSL_get_client_random(const WOLFSSL* ssl, unsigned char* out,
#if (defined(KEEP_PEER_CERT) && defined(SESSION_CERTS)) || \ #if (defined(KEEP_PEER_CERT) && defined(SESSION_CERTS)) || \
(defined(OPENSSL_ALL) && defined(HAVE_PKCS7)) (defined(OPENSSL_ALL) && defined(SESSION_CERTS))
/* Decode the X509 DER encoded certificate into a WOLFSSL_X509 object. /* Decode the X509 DER encoded certificate into a WOLFSSL_X509 object.
* *
* x509 WOLFSSL_X509 object to decode into. * x509 WOLFSSL_X509 object to decode into.
@ -61916,9 +61916,10 @@ PKCS7* wolfSSL_PKCS7_new(void)
WOLFSSL_PKCS7* pkcs7; WOLFSSL_PKCS7* pkcs7;
int ret = 0; int ret = 0;
pkcs7 = (WOLFSSL_PKCS7*)XMALLOC(sizeof(*pkcs7), NULL, DYNAMIC_TYPE_PKCS7); pkcs7 = (WOLFSSL_PKCS7*)XMALLOC(sizeof(WOLFSSL_PKCS7), NULL,
DYNAMIC_TYPE_PKCS7);
if (pkcs7 != NULL) { if (pkcs7 != NULL) {
XMEMSET(pkcs7, 0, sizeof(*pkcs7)); XMEMSET(pkcs7, 0, sizeof(WOLFSSL_PKCS7));
ret = wc_PKCS7_Init(&pkcs7->pkcs7, NULL, INVALID_DEVID); ret = wc_PKCS7_Init(&pkcs7->pkcs7, NULL, INVALID_DEVID);
} }
@ -61973,6 +61974,16 @@ void wolfSSL_PKCS7_SIGNED_free(PKCS7_SIGNED* p7)
return; 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) PKCS7* wolfSSL_d2i_PKCS7(PKCS7** p7, const unsigned char** in, int len)
{ {
return wolfSSL_d2i_PKCS7_ex(p7, in, len, NULL, 0); return wolfSSL_d2i_PKCS7_ex(p7, in, len, NULL, 0);
@ -61993,7 +62004,6 @@ PKCS7* wolfSSL_d2i_PKCS7_ex(PKCS7** p7, const unsigned char** in, int len,
byte* content, word32 contentSz) byte* content, word32 contentSz)
{ {
WOLFSSL_PKCS7* pkcs7 = NULL; WOLFSSL_PKCS7* pkcs7 = NULL;
word32 idx = 0;
WOLFSSL_ENTER("wolfSSL_d2i_PKCS7_ex"); WOLFSSL_ENTER("wolfSSL_d2i_PKCS7_ex");
@ -62003,12 +62013,7 @@ PKCS7* wolfSSL_d2i_PKCS7_ex(PKCS7** p7, const unsigned char** in, int len,
if ((pkcs7 = (WOLFSSL_PKCS7*)wolfSSL_PKCS7_new()) == NULL) if ((pkcs7 = (WOLFSSL_PKCS7*)wolfSSL_PKCS7_new()) == NULL)
return NULL; return NULL;
if (GetSequence(*in, &idx, &pkcs7->len, len) < 0) { pkcs7->len = len;
wolfSSL_PKCS7_free((PKCS7*)pkcs7);
return NULL;
}
pkcs7->len += idx;
pkcs7->data = (byte*)XMALLOC(pkcs7->len, NULL, DYNAMIC_TYPE_PKCS7); pkcs7->data = (byte*)XMALLOC(pkcs7->len, NULL, DYNAMIC_TYPE_PKCS7);
if (pkcs7->data == NULL) { if (pkcs7->data == NULL) {
wolfSSL_PKCS7_free((PKCS7*)pkcs7); wolfSSL_PKCS7_free((PKCS7*)pkcs7);
@ -62088,34 +62093,33 @@ error:
WOLFSSL_STACK* wolfSSL_PKCS7_get0_signers(PKCS7* pkcs7, WOLFSSL_STACK* certs, WOLFSSL_STACK* wolfSSL_PKCS7_get0_signers(PKCS7* pkcs7, WOLFSSL_STACK* certs,
int flags) int flags)
{ {
WOLFSSL_X509* x509 = NULL;
WOLFSSL_STACK* signers = NULL; WOLFSSL_STACK* signers = NULL;
WOLFSSL_PKCS7* p7 = (WOLFSSL_PKCS7*)pkcs7; WOLFSSL_PKCS7* p7 = (WOLFSSL_PKCS7*)pkcs7;
if (p7 == NULL) if (p7 == NULL)
return NULL; return NULL;
/* Only PKCS#7 messages with a single cert that is the verifying certificate /* Only PKCS#7 messages with a single cert that is the verifying certificate
* is supported. * is supported.
*/ */
if ((flags | PKCS7_NOINTERN) == PKCS7_NOINTERN) if (flags & PKCS7_NOINTERN) {
return NULL; WOLFSSL_MSG("PKCS7_NOINTERN flag not supported");
signers = (WOLFSSL_STACK*)XMALLOC(sizeof(WOLFSSL_STACK), NULL,
DYNAMIC_TYPE_X509);
if (signers == NULL)
return NULL;
signers->num = 1;
signers->data.x509 = (WOLFSSL_X509*)XMALLOC(sizeof(WOLFSSL_X509), NULL,
DYNAMIC_TYPE_X509);
if (signers->data.x509 == NULL) {
XFREE(signers, NULL, DYNAMIC_TYPE_X509);
return NULL; return NULL;
} }
if (DecodeToX509(signers->data.x509, p7->pkcs7.singleCert, signers = wolfSSL_sk_X509_new();
p7->pkcs7.singleCertSz) != 0) { if (signers == NULL)
XFREE(signers->data.x509, NULL, DYNAMIC_TYPE_X509); return NULL;
XFREE(signers, NULL, DYNAMIC_TYPE_X509);
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) != WOLFSSL_SUCCESS) {
wolfSSL_sk_X509_pop_free(signers, NULL);
return NULL; return NULL;
} }
@ -62197,6 +62201,7 @@ int wolfSSL_i2d_PKCS7(PKCS7 *p7, unsigned char **out)
WOLFSSL_MSG("malloc error"); WOLFSSL_MSG("malloc error");
goto cleanup; goto cleanup;
} }
XMEMSET(output, 0, len);
localBuf = 1; localBuf = 1;
} }
else { else {
@ -62250,15 +62255,337 @@ cleanup:
return ret; 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 signcert
* 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 = 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 */
if (err == 0) {
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, (int)sizeof(line))) > 0) {
if (line[lineLen - 1] == '\r' || line[lineLen - 1] == '\n') {
canonLineLen = (word32)lineLen;
if ((canonLine = wc_MIME_canonicalize(
line, &canonLineLen)) == NULL) {
ret = 1;
break;
}
/* remove trailing null */
if (canonLine[canonLineLen] == '\0') {
canonLineLen--;
}
if (wolfSSL_BIO_write(out, canonLine, (int)canonLineLen) < 0) {
ret = 1;
break;
}
XFREE(canonLine, NULL, DYNAMIC_TYPE_TMP_BUFFER);
canonLine = NULL;
}
else {
/* no line ending in current line, write direct to out */
if (wolfSSL_BIO_write(out, line, lineLen) < 0) {
ret = 1;
break;
}
}
}
if (canonLine != NULL) {
XFREE(canonLine, NULL, DYNAMIC_TYPE_TMP_BUFFER);
}
#ifdef WOLFSSL_SMALL_STACK
XFREE(line, in->heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif
return ret;
}
#endif /* HAVE_SMIME */
/**
* Finalize PKCS7 structure, currently supports signedData only.
*
* Does not do generation of 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;
static const char contTypeText[] = "Content-Type: text/plain\r\n\r\n";
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;
}
}
/* append Content-Type header if PKCS7_TEXT */
if ((ret == 1) && (flags & PKCS7_TEXT)) {
if (wolfSSL_BIO_write(data, contTypeText,
(int)XSTRLEN(contTypeText)) < 0) {
WOLFSSL_MSG("Error appending 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_mem_data(in, &mem)) < 0) {
ret = 0;
}
else if (wolfSSL_BIO_write(data, mem, memSz) < 0) {
ret = 0;
}
mem = NULL;
memSz = 0;
}
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) {
if (p7->data != NULL) {
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 = p7->len;
}
if (data != NULL) {
wolfSSL_BIO_free(data);
}
return ret;
}
int wolfSSL_PKCS7_verify(PKCS7* pkcs7, WOLFSSL_STACK* certs, int wolfSSL_PKCS7_verify(PKCS7* pkcs7, WOLFSSL_STACK* certs,
WOLFSSL_X509_STORE* store, WOLFSSL_BIO* in, WOLFSSL_BIO* out, int flags) WOLFSSL_X509_STORE* store, WOLFSSL_BIO* in, WOLFSSL_BIO* out, int flags)
{ {
int ret = 0; int i, ret = 0;
unsigned char* mem = NULL; unsigned char* mem = NULL;
int memSz = 0; int memSz = 0;
WOLFSSL_PKCS7* p7 = (WOLFSSL_PKCS7*)pkcs7; WOLFSSL_PKCS7* p7 = (WOLFSSL_PKCS7*)pkcs7;
static const char contTypeText[] = "Content-Type: text/plain\r\n\r\n"; static const char contTypeText[] = "Content-Type: text/plain\r\n\r\n";
int contTypeLen; int contTypeLen;
WOLFSSL_X509* signer = NULL;
WOLFSSL_STACK* signers = NULL;
WOLFSSL_ENTER("wolfSSL_PKCS7_verify"); WOLFSSL_ENTER("wolfSSL_PKCS7_verify");
@ -62285,8 +62612,30 @@ int wolfSSL_PKCS7_verify(PKCS7* pkcs7, WOLFSSL_STACK* certs,
return WOLFSSL_FAILURE; return WOLFSSL_FAILURE;
if ((flags & PKCS7_NOVERIFY) != PKCS7_NOVERIFY) { if ((flags & PKCS7_NOVERIFY) != PKCS7_NOVERIFY) {
/* All signer certificates are verified. */ /* Verify signer certificates */
return WOLFSSL_FAILURE; 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_free(signers);
return WOLFSSL_FAILURE;
}
}
wolfSSL_sk_X509_pop_free(signers, NULL);
} }
if (flags & PKCS7_TEXT) { if (flags & PKCS7_TEXT) {
@ -62534,6 +62883,7 @@ WOLFSSL_API PKCS7* wolfSSL_SMIME_read_PKCS7(WOLFSSL_BIO* in,
char* canonSection = NULL; char* canonSection = NULL;
PKCS7* pkcs7 = NULL; PKCS7* pkcs7 = NULL;
word32 outLen = 0; word32 outLen = 0;
word32 canonLineLen = 0;
byte* out = NULL; byte* out = NULL;
byte* outHead = NULL; byte* outHead = NULL;
@ -62649,14 +62999,16 @@ WOLFSSL_API PKCS7* wolfSSL_SMIME_read_PKCS7(WOLFSSL_BIO* in,
lineLen = wolfSSL_BIO_gets(in, section, remainLen); lineLen = wolfSSL_BIO_gets(in, section, remainLen);
while (XSTRNCMP(&section[sectionLen], boundary, boundLen) && while (XSTRNCMP(&section[sectionLen], boundary, boundLen) &&
remainLen > 0) { remainLen > 0) {
canonLine = wc_MIME_canonicalize(&section[sectionLen]); canonLineLen = lineLen;
canonLine = wc_MIME_canonicalize(&section[sectionLen],
&canonLineLen);
if (canonLine == NULL) { if (canonLine == NULL) {
goto error; goto error;
} }
/* If line endings were added, the initial length may be /* If line endings were added, the initial length may be
* exceeded. */ * exceeded. */
if ((canonPos + XSTRLEN(canonLine) + 1) >= canonSize) { if ((canonPos + canonLineLen) >= canonSize) {
canonSize = canonPos + XSTRLEN(canonLine) + 1; canonSize = canonPos + canonLineLen;
canonSection = (char*)XREALLOC(canonSection, canonSize, canonSection = (char*)XREALLOC(canonSection, canonSize,
NULL, DYNAMIC_TYPE_PKCS7); NULL, DYNAMIC_TYPE_PKCS7);
if (canonSection == NULL) { if (canonSection == NULL) {
@ -62664,8 +63016,8 @@ WOLFSSL_API PKCS7* wolfSSL_SMIME_read_PKCS7(WOLFSSL_BIO* in,
} }
} }
XMEMCPY(&canonSection[canonPos], canonLine, XMEMCPY(&canonSection[canonPos], canonLine,
(int)XSTRLEN(canonLine)); (int)canonLineLen - 1);
canonPos += XSTRLEN(canonLine); canonPos += canonLineLen - 1;
XFREE(canonLine, NULL, DYNAMIC_TYPE_PKCS7); XFREE(canonLine, NULL, DYNAMIC_TYPE_PKCS7);
canonLine = NULL; canonLine = NULL;
@ -62700,8 +63052,8 @@ WOLFSSL_API PKCS7* wolfSSL_SMIME_read_PKCS7(WOLFSSL_BIO* in,
*bcont = wolfSSL_BIO_new(wolfSSL_BIO_s_mem()); *bcont = wolfSSL_BIO_new(wolfSSL_BIO_s_mem());
ret = wolfSSL_BIO_write(*bcont, canonSection, ret = wolfSSL_BIO_write(*bcont, canonSection,
(int)XSTRLEN(canonSection)); canonPos + 1);
if (ret != (int)XSTRLEN(canonSection)) { if (ret != (canonPos+1)) {
goto error; goto error;
} }
if ((bcontMemSz = wolfSSL_BIO_get_mem_data(*bcont, &bcontMem)) if ((bcontMemSz = wolfSSL_BIO_get_mem_data(*bcont, &bcontMem))
@ -62711,7 +63063,6 @@ WOLFSSL_API PKCS7* wolfSSL_SMIME_read_PKCS7(WOLFSSL_BIO* in,
XFREE(canonSection, NULL, DYNAMIC_TYPE_PKCS7); XFREE(canonSection, NULL, DYNAMIC_TYPE_PKCS7);
canonSection = NULL; canonSection = NULL;
wc_MIME_free_hdrs(allHdrs); wc_MIME_free_hdrs(allHdrs);
allHdrs = NULL; allHdrs = NULL;
section[0] = '\0'; section[0] = '\0';
@ -62843,6 +63194,229 @@ error:
return NULL; 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;
WC_RNG rng;
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)) == WOLFSSL_FAILURE) {
WOLFSSL_MSG("Error in wolfSSL_i2d_PKCS7");
ret = 0;
}
}
/* Base64 encode signedData bundle */
if (ret > 0) {
if (Base64_Encode(p7out, len, NULL, &sigBase64Len) != 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, 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 (wc_InitRng(&rng) != 0) {
WOLFSSL_MSG("Error in wc_InitRng");
ret = 0;
}
if ((ret > 0) && (wc_RNG_GenerateBlock(&rng, (byte*)boundary,
sizeof(boundary)) != 0)) {
WOLFSSL_MSG("Error in wc_RNG_GenerateBlock");
ret = 0;
}
wc_FreeRng(&rng);
if (ret > 0) {
for (i = 0; i < (int)sizeof(boundary) - 1; i++) {
boundary[i] =
alphanum[boundary[i] % (sizeof(alphanum) - 1)];
}
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);
}
}
}
if (p7out != NULL) {
XFREE(p7out, NULL, DYNAMIC_TYPE_TMP_BUFFER);
}
if (sigBase64 != NULL) {
XFREE(sigBase64, NULL, DYNAMIC_TYPE_TMP_BUFFER);
}
if (ret > 0) {
return 1;
}
return 0;
}
#endif /* HAVE_SMIME */ #endif /* HAVE_SMIME */
#endif /* !NO_BIO */ #endif /* !NO_BIO */
#endif /* OPENSSL_ALL */ #endif /* OPENSSL_ALL */

View File

@ -32560,19 +32560,22 @@ MimeParam* wc_MIME_find_param_attr(const char* attribute,
* wc_MIME_canonicalize - Canonicalize a line by converting all line endings * wc_MIME_canonicalize - Canonicalize a line by converting all line endings
* to CRLF. * to CRLF.
* *
* line - input line to canonicalize
* len - length of line in chars on input, length of output array on return
*
* RETURNS: * RETURNS:
* returns a pointer to a canonicalized line on success, NULL on error. * returns a pointer to a canonicalized line on success, NULL on error.
*/ */
char* wc_MIME_canonicalize(const char* line) char* wc_MIME_canonicalize(const char* line, word32* len)
{ {
size_t end = 0; size_t end = 0;
char* canonLine = NULL; char* canonLine = NULL;
if (line == NULL || XSTRLEN(line) == 0) { if (line == NULL || len == NULL || *len == 0) {
return NULL; return NULL;
} }
end = XSTRLEN(line); end = *len - 1 ;
while (end >= 1 && ((line[end-1] == '\r') || (line[end-1] == '\n'))) { while (end >= 1 && ((line[end-1] == '\r') || (line[end-1] == '\n'))) {
end--; end--;
} }
@ -32583,10 +32586,11 @@ char* wc_MIME_canonicalize(const char* line)
return NULL; return NULL;
} }
XSTRNCPY(canonLine, line, end); XMEMCPY(canonLine, line, end);
canonLine[end] = '\r'; canonLine[end] = '\r';
canonLine[end+1] = '\n'; canonLine[end+1] = '\n';
canonLine[end+2] = '\0'; canonLine[end+2] = '\0';
*len = (word32)(end + 3);
return canonLine; return canonLine;
} }

View File

@ -35,14 +35,20 @@
#if defined(OPENSSL_ALL) && defined(HAVE_PKCS7) #if defined(OPENSSL_ALL) && defined(HAVE_PKCS7)
#define PKCS7_TEXT 0x1 #define PKCS7_TEXT 0x1
#define PKCS7_NOCERTS 0x2
#define PKCS7_DETACHED 0x40
#define PKCS7_BINARY 0x80
#define PKCS7_NOINTERN 0x0010 #define PKCS7_NOINTERN 0x0010
#define PKCS7_NOVERIFY 0x0020 #define PKCS7_NOVERIFY 0x0020
#define PKCS7_STREAM 0x1000
#define PKCS7_PARTIAL 0x4000
typedef struct WOLFSSL_PKCS7 typedef struct WOLFSSL_PKCS7
{ {
PKCS7 pkcs7; PKCS7 pkcs7;
unsigned char* data; unsigned char* data;
int len; int len;
int type; /* from PKCS7_TYPES, for PKCS7_final() */
WOLFSSL_STACK* certs; WOLFSSL_STACK* certs;
} WOLFSSL_PKCS7; } WOLFSSL_PKCS7;
@ -58,8 +64,11 @@ WOLFSSL_LOCAL PKCS7* wolfSSL_d2i_PKCS7_ex(PKCS7** p7, const unsigned char** in,
WOLFSSL_API PKCS7* wolfSSL_d2i_PKCS7_bio(WOLFSSL_BIO* bio, PKCS7** p7); WOLFSSL_API PKCS7* wolfSSL_d2i_PKCS7_bio(WOLFSSL_BIO* bio, PKCS7** p7);
WOLFSSL_API int wolfSSL_i2d_PKCS7_bio(WOLFSSL_BIO *bio, PKCS7 *p7); WOLFSSL_API int wolfSSL_i2d_PKCS7_bio(WOLFSSL_BIO *bio, PKCS7 *p7);
WOLFSSL_API int wolfSSL_i2d_PKCS7(PKCS7 *p7, unsigned char **out); WOLFSSL_API int wolfSSL_i2d_PKCS7(PKCS7 *p7, unsigned char **out);
WOLFSSL_API PKCS7* wolfSSL_PKCS7_sign(WOLFSSL_X509* signer,
WOLFSSL_EVP_PKEY* pkey, WOLFSSL_STACK* certs, WOLFSSL_BIO* in, int flags);
WOLFSSL_API int wolfSSL_PKCS7_verify(PKCS7* p7, WOLFSSL_STACK* certs, WOLFSSL_API int wolfSSL_PKCS7_verify(PKCS7* p7, WOLFSSL_STACK* certs,
WOLFSSL_X509_STORE* store, WOLFSSL_BIO* in, WOLFSSL_BIO* out, int flags); WOLFSSL_X509_STORE* store, WOLFSSL_BIO* in, WOLFSSL_BIO* out, int flags);
WOLFSSL_API int wolfSSL_PKCS7_final(PKCS7* pkcs7, WOLFSSL_BIO* in, int flags);
WOLFSSL_API int wolfSSL_PKCS7_encode_certs(PKCS7* p7, WOLFSSL_STACK* certs, WOLFSSL_API int wolfSSL_PKCS7_encode_certs(PKCS7* p7, WOLFSSL_STACK* certs,
WOLFSSL_BIO* out); WOLFSSL_BIO* out);
WOLFSSL_API WOLFSSL_STACK* wolfSSL_PKCS7_to_stack(PKCS7* pkcs7); WOLFSSL_API WOLFSSL_STACK* wolfSSL_PKCS7_to_stack(PKCS7* pkcs7);
@ -68,6 +77,8 @@ WOLFSSL_API WOLFSSL_STACK* wolfSSL_PKCS7_get0_signers(PKCS7* p7,
WOLFSSL_API int wolfSSL_PEM_write_bio_PKCS7(WOLFSSL_BIO* bio, PKCS7* p7); WOLFSSL_API int wolfSSL_PEM_write_bio_PKCS7(WOLFSSL_BIO* bio, PKCS7* p7);
#if defined(HAVE_SMIME) #if defined(HAVE_SMIME)
WOLFSSL_API PKCS7* wolfSSL_SMIME_read_PKCS7(WOLFSSL_BIO* in, WOLFSSL_BIO** bcont); WOLFSSL_API PKCS7* wolfSSL_SMIME_read_PKCS7(WOLFSSL_BIO* in, WOLFSSL_BIO** bcont);
WOLFSSL_API int wolfSSL_SMIME_write_PKCS7(WOLFSSL_BIO* out, PKCS7* pkcs7,
WOLFSSL_BIO* in, int flags);
#endif /* HAVE_SMIME */ #endif /* HAVE_SMIME */
@ -79,11 +90,14 @@ WOLFSSL_API PKCS7* wolfSSL_SMIME_read_PKCS7(WOLFSSL_BIO* in, WOLFSSL_BIO** bcont
#define d2i_PKCS7_bio wolfSSL_d2i_PKCS7_bio #define d2i_PKCS7_bio wolfSSL_d2i_PKCS7_bio
#define i2d_PKCS7_bio wolfSSL_i2d_PKCS7_bio #define i2d_PKCS7_bio wolfSSL_i2d_PKCS7_bio
#define i2d_PKCS7 wolfSSL_i2d_PKCS7 #define i2d_PKCS7 wolfSSL_i2d_PKCS7
#define PKCS7_sign wolfSSL_PKCS7_sign
#define PKCS7_verify wolfSSL_PKCS7_verify #define PKCS7_verify wolfSSL_PKCS7_verify
#define PKCS7_final wolfSSL_PKCS7_final
#define PKCS7_get0_signers wolfSSL_PKCS7_get0_signers #define PKCS7_get0_signers wolfSSL_PKCS7_get0_signers
#define PEM_write_bio_PKCS7 wolfSSL_PEM_write_bio_PKCS7 #define PEM_write_bio_PKCS7 wolfSSL_PEM_write_bio_PKCS7
#if defined(HAVE_SMIME) #if defined(HAVE_SMIME)
#define SMIME_read_PKCS7 wolfSSL_SMIME_read_PKCS7 #define SMIME_read_PKCS7 wolfSSL_SMIME_read_PKCS7
#define SMIME_write_PKCS7 wolfSSL_SMIME_write_PKCS7
#endif /* HAVE_SMIME */ #endif /* HAVE_SMIME */
#endif /* OPENSSL_ALL && HAVE_PKCS7 */ #endif /* OPENSSL_ALL && HAVE_PKCS7 */

View File

@ -2033,7 +2033,7 @@ WOLFSSL_LOCAL int wc_MIME_parse_headers(char* in, int inLen, MimeHdr** hdrs);
WOLFSSL_LOCAL int wc_MIME_header_strip(char* in, char** out, size_t start, size_t end); WOLFSSL_LOCAL int wc_MIME_header_strip(char* in, char** out, size_t start, size_t end);
WOLFSSL_LOCAL MimeHdr* wc_MIME_find_header_name(const char* name, MimeHdr* hdr); WOLFSSL_LOCAL MimeHdr* wc_MIME_find_header_name(const char* name, MimeHdr* hdr);
WOLFSSL_LOCAL MimeParam* wc_MIME_find_param_attr(const char* attribute, MimeParam* param); WOLFSSL_LOCAL MimeParam* wc_MIME_find_param_attr(const char* attribute, MimeParam* param);
WOLFSSL_LOCAL char* wc_MIME_canonicalize(const char* line); WOLFSSL_LOCAL char* wc_MIME_canonicalize(const char* line, word32* len);
WOLFSSL_LOCAL int wc_MIME_free_hdrs(MimeHdr* head); WOLFSSL_LOCAL int wc_MIME_free_hdrs(MimeHdr* head);
#endif /* HAVE_SMIME */ #endif /* HAVE_SMIME */

View File

@ -331,6 +331,7 @@ struct PKCS7 {
/* used by DecodeEnvelopedData with multiple encrypted contents */ /* used by DecodeEnvelopedData with multiple encrypted contents */
byte* cachedEncryptedContent; byte* cachedEncryptedContent;
word32 cachedEncryptedContentSz; word32 cachedEncryptedContentSz;
word16 contentCRLF:1; /* have content line endings been converted to CRLF */
/* !! NEW DATA MEMBERS MUST BE ADDED AT END !! */ /* !! NEW DATA MEMBERS MUST BE ADDED AT END !! */
}; };