diff --git a/src/ssl.c b/src/ssl.c index a8d300523..632d81254 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -20920,7 +20920,7 @@ size_t wolfSSL_get_client_random(const WOLFSSL* ssl, unsigned char* out, #if (defined(KEEP_PEER_CERT) && defined(SESSION_CERTS)) || \ - (defined(OPENSSL_ALL) && defined(HAVE_PKCS7)) + (defined(OPENSSL_EXTRA) && defined(SESSION_CERTS)) /* Decode the X509 DER encoded certificate into a WOLFSSL_X509 object. * * x509 WOLFSSL_X509 object to decode into. @@ -20962,7 +20962,7 @@ size_t wolfSSL_get_client_random(const WOLFSSL* ssl, unsigned char* out, return ret; } -#endif /* (KEEP_PEER_CERT && SESSION_CERTS) || (OPENSSL_ALL && HAVE_PKCS7) */ +#endif /* (KEEP_PEER_CERT & SESSION_CERTS) || (OPENSSL_EXTRA & SESSION_CERTS) */ #ifdef KEEP_PEER_CERT @@ -60641,9 +60641,10 @@ PKCS7* wolfSSL_PKCS7_new(void) WOLFSSL_PKCS7* pkcs7; 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) { - XMEMSET(pkcs7, 0, sizeof(*pkcs7)); + XMEMSET(pkcs7, 0, sizeof(WOLFSSL_PKCS7)); ret = wc_PKCS7_Init(&pkcs7->pkcs7, NULL, INVALID_DEVID); } @@ -60698,6 +60699,16 @@ void wolfSSL_PKCS7_SIGNED_free(PKCS7_SIGNED* 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); @@ -60718,22 +60729,16 @@ PKCS7* wolfSSL_d2i_PKCS7_ex(PKCS7** p7, const unsigned char** in, int len, byte* content, word32 contentSz) { WOLFSSL_PKCS7* pkcs7 = NULL; - word32 idx = 0; WOLFSSL_ENTER("wolfSSL_d2i_PKCS7_ex"); - if (in == NULL || *in == NULL) + if (in == NULL || *in == NULL || len < 0) return NULL; if ((pkcs7 = (WOLFSSL_PKCS7*)wolfSSL_PKCS7_new()) == NULL) return NULL; - if (GetSequence(*in, &idx, &pkcs7->len, len) < 0) { - wolfSSL_PKCS7_free((PKCS7*)pkcs7); - return NULL; - } - pkcs7->len += idx; - + pkcs7->len = len; pkcs7->data = (byte*)XMALLOC(pkcs7->len, NULL, DYNAMIC_TYPE_PKCS7); if (pkcs7->data == NULL) { wolfSSL_PKCS7_free((PKCS7*)pkcs7); @@ -60811,37 +60816,48 @@ error: 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) == PKCS7_NOINTERN) - return NULL; - - 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); + if (flags & PKCS7_NOINTERN) { + WOLFSSL_MSG("PKCS7_NOINTERN flag not supported"); return NULL; } - if (DecodeToX509(signers->data.x509, p7->pkcs7.singleCert, - p7->pkcs7.singleCertSz) != 0) { - XFREE(signers->data.x509, NULL, DYNAMIC_TYPE_X509); - XFREE(signers, NULL, DYNAMIC_TYPE_X509); + signers = wolfSSL_sk_X509_new(); + 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) != WOLFSSL_SUCCESS) { + wolfSSL_sk_X509_pop_free(signers, NULL); return NULL; } @@ -60977,13 +60993,354 @@ cleanup: 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 = 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, (int)sizeof(line))) > 0) { + + if (line[lineLen - 1] == '\r' || line[lineLen - 1] == '\n') { + canonLineLen = (word32)lineLen; + if ((canonLine = wc_MIME_single_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_PKCS7); + 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_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; + } + } + + if (mem != NULL) { + 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) { + 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, WOLFSSL_X509_STORE* store, WOLFSSL_BIO* in, WOLFSSL_BIO* out, int flags) { - int ret = 0; + 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"); @@ -61010,12 +61367,49 @@ int wolfSSL_PKCS7_verify(PKCS7* pkcs7, WOLFSSL_STACK* certs, return WOLFSSL_FAILURE; if ((flags & PKCS7_NOVERIFY) != PKCS7_NOVERIFY) { - /* All signer certificates are verified. */ - return WOLFSSL_FAILURE; + /* 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 (out != NULL) - wolfSSL_BIO_write(out, p7->pkcs7.content, p7->pkcs7.contentSz); + 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; } @@ -61246,6 +61640,7 @@ WOLFSSL_API PKCS7* wolfSSL_SMIME_read_PKCS7(WOLFSSL_BIO* in, char* canonSection = NULL; PKCS7* pkcs7 = NULL; word32 outLen = 0; + word32 canonLineLen = 0; byte* out = NULL; byte* outHead = NULL; @@ -61265,6 +61660,7 @@ WOLFSSL_API PKCS7* wolfSSL_SMIME_read_PKCS7(WOLFSSL_BIO* in, 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; @@ -61361,14 +61757,16 @@ WOLFSSL_API PKCS7* wolfSSL_SMIME_read_PKCS7(WOLFSSL_BIO* in, lineLen = wolfSSL_BIO_gets(in, section, remainLen); while (XSTRNCMP(§ion[sectionLen], boundary, boundLen) && remainLen > 0) { - canonLine = wc_MIME_canonicalize(§ion[sectionLen]); + canonLineLen = 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 + XSTRLEN(canonLine) + 1) >= canonSize) { - canonSize = canonPos + XSTRLEN(canonLine) + 1; + if ((canonPos + canonLineLen) >= canonSize) { + canonSize = canonPos + canonLineLen; canonSection = (char*)XREALLOC(canonSection, canonSize, NULL, DYNAMIC_TYPE_PKCS7); if (canonSection == NULL) { @@ -61376,8 +61774,8 @@ WOLFSSL_API PKCS7* wolfSSL_SMIME_read_PKCS7(WOLFSSL_BIO* in, } } XMEMCPY(&canonSection[canonPos], canonLine, - (int)XSTRLEN(canonLine)); - canonPos += XSTRLEN(canonLine); + (int)canonLineLen - 1); + canonPos += canonLineLen - 1; XFREE(canonLine, NULL, DYNAMIC_TYPE_PKCS7); canonLine = NULL; @@ -61412,8 +61810,8 @@ WOLFSSL_API PKCS7* wolfSSL_SMIME_read_PKCS7(WOLFSSL_BIO* in, *bcont = wolfSSL_BIO_new(wolfSSL_BIO_s_mem()); ret = wolfSSL_BIO_write(*bcont, canonSection, - (int)XSTRLEN(canonSection)); - if (ret != (int)XSTRLEN(canonSection)) { + canonPos + 1); + if (ret != (canonPos+1)) { goto error; } if ((bcontMemSz = wolfSSL_BIO_get_mem_data(*bcont, &bcontMem)) @@ -61423,7 +61821,6 @@ WOLFSSL_API PKCS7* wolfSSL_SMIME_read_PKCS7(WOLFSSL_BIO* in, XFREE(canonSection, NULL, DYNAMIC_TYPE_PKCS7); canonSection = NULL; - wc_MIME_free_hdrs(allHdrs); allHdrs = NULL; section[0] = '\0'; @@ -61555,6 +61952,228 @@ error: 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)) == 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 (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); + } + } + } + + if (p7out != NULL) { + XFREE(p7out, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + if (sigBase64 != NULL) { + 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 */ diff --git a/tests/api.c b/tests/api.c index 125efee56..bdba2a20f 100644 --- a/tests/api.c +++ b/tests/api.c @@ -47104,6 +47104,181 @@ static void test_wolfssl_PKCS7(void) #endif } +static void test_wolfSSL_PKCS7_sign(void) +{ +#if defined(OPENSSL_ALL) && defined(HAVE_PKCS7) && !defined(NO_BIO) && \ + !defined(NO_FILESYSTEM) && !defined(NO_RSA) + + PKCS7* p7 = NULL; + PKCS7* p7Ver = NULL; + byte* out = NULL; + byte* tmpPtr = NULL; + int outLen = 0; + int flags = 0; + byte data[] = "Test data to encode."; + + const char* cert = "./certs/server-cert.pem"; + const char* key = "./certs/server-key.pem"; + const char* ca = "./certs/ca-cert.pem"; + + WOLFSSL_BIO* certBio = NULL; + WOLFSSL_BIO* keyBio = NULL; + WOLFSSL_BIO* caBio = NULL; + WOLFSSL_BIO* inBio = NULL; + X509* signCert = NULL; + EVP_PKEY* signKey = NULL; + X509* caCert = NULL; + X509_STORE* store = NULL; + + printf(testingFmt, "wolfSSL_PKCS7_sign()"); + + /* read signer cert/key into BIO */ + AssertNotNull(certBio = BIO_new_file(cert, "r")); + AssertNotNull(keyBio = BIO_new_file(key, "r")); + AssertNotNull(signCert = PEM_read_bio_X509(certBio, NULL, 0, NULL)); + AssertNotNull(signKey = PEM_read_bio_PrivateKey(keyBio, NULL, 0, NULL)); + + /* read CA cert into store (for verify) */ + AssertNotNull(caBio = BIO_new_file(ca, "r")); + AssertNotNull(caCert = PEM_read_bio_X509(caBio, NULL, 0, NULL)); + AssertNotNull(store = X509_STORE_new()); + AssertIntEQ(X509_STORE_add_cert(store, caCert), 1); + + /* data to be signed into BIO */ + AssertNotNull(inBio = BIO_new(BIO_s_mem())); + AssertIntGT(BIO_write(inBio, data, sizeof(data)), 0); + + /* PKCS7_sign, bad args: signer NULL */ + AssertNull(p7 = PKCS7_sign(NULL, signKey, NULL, inBio, 0)); + /* PKCS7_sign, bad args: signer key NULL */ + AssertNull(p7 = PKCS7_sign(signCert, NULL, NULL, inBio, 0)); + /* PKCS7_sign, bad args: in data NULL without PKCS7_STREAM */ + AssertNull(p7 = PKCS7_sign(signCert, signKey, NULL, NULL, 0)); + /* PKCS7_sign, bad args: PKCS7_NOCERTS flag not supported */ + AssertNull(p7 = PKCS7_sign(signCert, signKey, NULL, inBio, PKCS7_NOCERTS)); + /* PKCS7_sign, bad args: PKCS7_PARTIAL flag not supported */ + AssertNull(p7 = PKCS7_sign(signCert, signKey, NULL, inBio, PKCS7_PARTIAL)); + + /* TEST SUCCESS: Not detached, not streaming, not MIME */ + { + flags = PKCS7_BINARY; + AssertNotNull(p7 = PKCS7_sign(signCert, signKey, NULL, inBio, flags)); + AssertIntGT((outLen = i2d_PKCS7(p7, &out)), 0); + + /* verify with d2i_PKCS7 */ + tmpPtr = out; + AssertNotNull(p7Ver = d2i_PKCS7(NULL, (const byte**)&tmpPtr, outLen)); + AssertIntEQ(PKCS7_verify(p7Ver, NULL, store, NULL, NULL, flags), 1); + PKCS7_free(p7Ver); + + /* verify with wc_PKCS7_VerifySignedData */ + AssertNotNull(p7Ver = wc_PKCS7_New(HEAP_HINT, devId)); + AssertIntEQ(wc_PKCS7_Init(p7Ver, HEAP_HINT, INVALID_DEVID), 0); + AssertIntEQ(wc_PKCS7_VerifySignedData(p7Ver, out, outLen), 0); + wc_PKCS7_Free(p7Ver); + + AssertNotNull(out); + XFREE(out, NULL, DYNAMIC_TYPE_TMP_BUFFER); + out = NULL; + PKCS7_free(p7); + } + + /* TEST SUCCESS: Not detached, streaming, not MIME. Also bad arg + * tests for PKCS7_final() while we have a PKCS7 pointer to use */ + { + /* re-populate input BIO, may have been consumed */ + BIO_free(inBio); + AssertNotNull(inBio = BIO_new(BIO_s_mem())); + AssertIntGT(BIO_write(inBio, data, sizeof(data)), 0); + + flags = PKCS7_BINARY | PKCS7_STREAM; + AssertNotNull(p7 = PKCS7_sign(signCert, signKey, NULL, inBio, flags)); + AssertIntEQ(PKCS7_final(p7, inBio, flags), 1); + AssertIntGT((outLen = i2d_PKCS7(p7, &out)), 0); + + /* PKCS7_final, bad args: PKCS7 null */ + AssertIntEQ(PKCS7_final(NULL, inBio, 0), 0); + /* PKCS7_final, bad args: PKCS7 null */ + AssertIntEQ(PKCS7_final(p7, NULL, 0), 0); + + tmpPtr = out; + AssertNotNull(p7Ver = d2i_PKCS7(NULL, (const byte**)&tmpPtr, outLen)); + AssertIntEQ(PKCS7_verify(p7Ver, NULL, store, NULL, NULL, flags), 1); + PKCS7_free(p7Ver); + + AssertNotNull(out); + XFREE(out, NULL, DYNAMIC_TYPE_TMP_BUFFER); + out = NULL; + PKCS7_free(p7); + } + + /* TEST SUCCESS: Detached, not streaming, not MIME */ + { + /* re-populate input BIO, may have been consumed */ + BIO_free(inBio); + AssertNotNull(inBio = BIO_new(BIO_s_mem())); + AssertIntGT(BIO_write(inBio, data, sizeof(data)), 0); + + flags = PKCS7_BINARY | PKCS7_DETACHED; + AssertNotNull(p7 = PKCS7_sign(signCert, signKey, NULL, inBio, flags)); + AssertIntGT((outLen = i2d_PKCS7(p7, &out)), 0); + + /* verify with wolfCrypt, d2i_PKCS7 does not support detached content */ + AssertNotNull(p7Ver = wc_PKCS7_New(HEAP_HINT, devId)); + p7Ver->content = data; + p7Ver->contentSz = sizeof(data); + AssertIntEQ(wc_PKCS7_VerifySignedData(p7Ver, out, outLen), 0); + wc_PKCS7_Free(p7Ver); + + /* verify expected failure (NULL return) from d2i_PKCS7, it does not + * yet support detached content */ + tmpPtr = out; + AssertNull(p7Ver = d2i_PKCS7(NULL, (const byte**)&tmpPtr, outLen)); + PKCS7_free(p7Ver); + + AssertNotNull(out); + XFREE(out, NULL, DYNAMIC_TYPE_TMP_BUFFER); + out = NULL; + PKCS7_free(p7); + } + + /* TEST SUCCESS: Detached, streaming, not MIME */ + { + /* re-populate input BIO, may have been consumed */ + BIO_free(inBio); + AssertNotNull(inBio = BIO_new(BIO_s_mem())); + AssertIntGT(BIO_write(inBio, data, sizeof(data)), 0); + + flags = PKCS7_BINARY | PKCS7_DETACHED | PKCS7_STREAM; + AssertNotNull(p7 = PKCS7_sign(signCert, signKey, NULL, inBio, flags)); + AssertIntEQ(PKCS7_final(p7, inBio, flags), 1); + AssertIntGT((outLen = i2d_PKCS7(p7, &out)), 0); + + /* verify with wolfCrypt, d2i_PKCS7 does not support detached content */ + AssertNotNull(p7Ver = wc_PKCS7_New(HEAP_HINT, devId)); + p7Ver->content = data; + p7Ver->contentSz = sizeof(data); + AssertIntEQ(wc_PKCS7_VerifySignedData(p7Ver, out, outLen), 0); + wc_PKCS7_Free(p7Ver); + + AssertNotNull(out); + XFREE(out, NULL, DYNAMIC_TYPE_TMP_BUFFER); + PKCS7_free(p7); + } + + X509_STORE_free(store); + X509_free(caCert); + X509_free(signCert); + EVP_PKEY_free(signKey); + BIO_free(inBio); + BIO_free(keyBio); + BIO_free(certBio); + BIO_free(caBio); + + printf(resultFmt, passed); +#endif +} + static void test_wolfSSL_PKCS7_SIGNED_new(void) { #if defined(OPENSSL_ALL) && defined(HAVE_PKCS7) @@ -47243,43 +47418,73 @@ static void test_wolfSSL_SMIME_read_PKCS7(void) PKCS7* pkcs7 = NULL; BIO* bio = NULL; BIO* bcont = NULL; + BIO* out = NULL; + const byte* outBuf = NULL; + int outBufLen = 0; + static const char contTypeText[] = "Content-Type: text/plain\r\n\r\n"; XFILE smimeTestFile = XFOPEN("./certs/test/smime-test.p7s", "r"); printf(testingFmt, "wolfSSL_SMIME_read_PKCS7()"); + /* smime-test.p7s */ bio = wolfSSL_BIO_new(wolfSSL_BIO_s_file()); AssertNotNull(bio); AssertIntEQ(wolfSSL_BIO_set_fp(bio, smimeTestFile, BIO_CLOSE), SSL_SUCCESS); pkcs7 = wolfSSL_SMIME_read_PKCS7(bio, &bcont); AssertNotNull(pkcs7); - AssertIntEQ(wolfSSL_PKCS7_verify(pkcs7, NULL, NULL, bcont, NULL, PKCS7_NOVERIFY), SSL_SUCCESS); + AssertIntEQ(wolfSSL_PKCS7_verify(pkcs7, NULL, NULL, bcont, NULL, + PKCS7_NOVERIFY), SSL_SUCCESS); XFCLOSE(smimeTestFile); if (bcont) BIO_free(bcont); wolfSSL_PKCS7_free(pkcs7); + /* smime-test-multipart.p7s */ smimeTestFile = XFOPEN("./certs/test/smime-test-multipart.p7s", "r"); AssertIntEQ(wolfSSL_BIO_set_fp(bio, smimeTestFile, BIO_CLOSE), SSL_SUCCESS); pkcs7 = wolfSSL_SMIME_read_PKCS7(bio, &bcont); AssertNotNull(pkcs7); - AssertIntEQ(wolfSSL_PKCS7_verify(pkcs7, NULL, NULL, bcont, NULL, PKCS7_NOVERIFY), SSL_SUCCESS); + AssertIntEQ(wolfSSL_PKCS7_verify(pkcs7, NULL, NULL, bcont, NULL, + PKCS7_NOVERIFY), SSL_SUCCESS); XFCLOSE(smimeTestFile); if (bcont) BIO_free(bcont); wolfSSL_PKCS7_free(pkcs7); + /* smime-test-multipart-badsig.p7s */ smimeTestFile = XFOPEN("./certs/test/smime-test-multipart-badsig.p7s", "r"); AssertIntEQ(wolfSSL_BIO_set_fp(bio, smimeTestFile, BIO_CLOSE), SSL_SUCCESS); pkcs7 = wolfSSL_SMIME_read_PKCS7(bio, &bcont); AssertNull(pkcs7); - AssertIntEQ(wolfSSL_PKCS7_verify(pkcs7, NULL, NULL, bcont, NULL, PKCS7_NOVERIFY), SSL_FAILURE); + AssertIntEQ(wolfSSL_PKCS7_verify(pkcs7, NULL, NULL, bcont, NULL, + PKCS7_NOVERIFY), SSL_FAILURE); XFCLOSE(smimeTestFile); if (bcont) BIO_free(bcont); wolfSSL_PKCS7_free(pkcs7); + /* smime-test-canon.p7s */ smimeTestFile = XFOPEN("./certs/test/smime-test-canon.p7s", "r"); AssertIntEQ(wolfSSL_BIO_set_fp(bio, smimeTestFile, BIO_CLOSE), SSL_SUCCESS); pkcs7 = wolfSSL_SMIME_read_PKCS7(bio, &bcont); AssertNotNull(pkcs7); - AssertIntEQ(wolfSSL_PKCS7_verify(pkcs7, NULL, NULL, bcont, NULL, PKCS7_NOVERIFY), SSL_SUCCESS); + AssertIntEQ(wolfSSL_PKCS7_verify(pkcs7, NULL, NULL, bcont, NULL, + PKCS7_NOVERIFY), SSL_SUCCESS); + if (bcont) BIO_free(bcont); + wolfSSL_PKCS7_free(pkcs7); + + /* Test PKCS7_TEXT, PKCS7_verify() should remove Content-Type: text/plain */ + smimeTestFile = XFOPEN("./certs/test/smime-test-canon.p7s", "r"); + AssertIntEQ(wolfSSL_BIO_set_fp(bio, smimeTestFile, BIO_CLOSE), SSL_SUCCESS); + pkcs7 = wolfSSL_SMIME_read_PKCS7(bio, &bcont); + AssertNotNull(pkcs7); + out = wolfSSL_BIO_new(BIO_s_mem()); + AssertNotNull(out); + AssertIntEQ(wolfSSL_PKCS7_verify(pkcs7, NULL, NULL, bcont, out, + PKCS7_NOVERIFY | PKCS7_TEXT), SSL_SUCCESS); + AssertIntGT((outBufLen = BIO_get_mem_data(out, &outBuf)), 0); + /* Content-Type should not show up at beginning of output buffer */ + AssertIntGT(outBufLen, XSTRLEN(contTypeText)); + AssertIntGT(XMEMCMP(outBuf, contTypeText, XSTRLEN(contTypeText)), 0); + + BIO_free(out); BIO_free(bio); if (bcont) BIO_free(bcont); wolfSSL_PKCS7_free(pkcs7); @@ -47287,6 +47492,142 @@ static void test_wolfSSL_SMIME_read_PKCS7(void) printf(resultFmt, passed); #endif } + +static void test_wolfSSL_SMIME_write_PKCS7(void) +{ +#if defined(OPENSSL_ALL) && defined(HAVE_PKCS7) && !defined(NO_RSA) + + PKCS7* p7 = NULL; + PKCS7* p7Ver = NULL; + int flags = 0; + byte data[] = "Test data to encode."; + + const char* cert = "./certs/server-cert.pem"; + const char* key = "./certs/server-key.pem"; + const char* ca = "./certs/ca-cert.pem"; + + WOLFSSL_BIO* certBio = NULL; + WOLFSSL_BIO* keyBio = NULL; + WOLFSSL_BIO* caBio = NULL; + WOLFSSL_BIO* inBio = NULL; + WOLFSSL_BIO* outBio = NULL; + WOLFSSL_BIO* content = NULL; + X509* signCert = NULL; + EVP_PKEY* signKey = NULL; + X509* caCert = NULL; + X509_STORE* store = NULL; + + printf(testingFmt, "wolfSSL_SMIME_write_PKCS7()"); + + /* read signer cert/key into BIO */ + AssertNotNull(certBio = BIO_new_file(cert, "r")); + AssertNotNull(keyBio = BIO_new_file(key, "r")); + AssertNotNull(signCert = PEM_read_bio_X509(certBio, NULL, 0, NULL)); + AssertNotNull(signKey = PEM_read_bio_PrivateKey(keyBio, NULL, 0, NULL)); + + /* read CA cert into store (for verify) */ + AssertNotNull(caBio = BIO_new_file(ca, "r")); + AssertNotNull(caCert = PEM_read_bio_X509(caBio, NULL, 0, NULL)); + AssertNotNull(store = X509_STORE_new()); + AssertIntEQ(X509_STORE_add_cert(store, caCert), 1); + + + /* generate and verify SMIME: not detached */ + { + AssertNotNull(inBio = BIO_new(BIO_s_mem())); + AssertIntGT(BIO_write(inBio, data, sizeof(data)), 0); + + flags = PKCS7_STREAM; + AssertNotNull(p7 = PKCS7_sign(signCert, signKey, NULL, inBio, flags)); + AssertNotNull(outBio = BIO_new(BIO_s_mem())); + AssertIntEQ(SMIME_write_PKCS7(outBio, p7, inBio, flags), 1); + + /* bad arg: out NULL */ + AssertIntEQ(SMIME_write_PKCS7(NULL, p7, inBio, flags), 0); + /* bad arg: pkcs7 NULL */ + AssertIntEQ(SMIME_write_PKCS7(outBio, NULL, inBio, flags), 0); + + AssertNotNull(p7Ver = SMIME_read_PKCS7(outBio, &content)); + AssertIntEQ(PKCS7_verify(p7Ver, NULL, store, NULL, NULL, flags), 1); + + BIO_free(content); + BIO_free(inBio); + BIO_free(outBio); + PKCS7_free(p7Ver); + PKCS7_free(p7); + } + + /* generate and verify SMIME: not detached, add Content-Type */ + { + AssertNotNull(inBio = BIO_new(BIO_s_mem())); + AssertIntGT(BIO_write(inBio, data, sizeof(data)), 0); + + flags = PKCS7_STREAM | PKCS7_TEXT; + AssertNotNull(p7 = PKCS7_sign(signCert, signKey, NULL, inBio, flags)); + AssertNotNull(outBio = BIO_new(BIO_s_mem())); + AssertIntEQ(SMIME_write_PKCS7(outBio, p7, inBio, flags), 1); + + AssertNotNull(p7Ver = SMIME_read_PKCS7(outBio, &content)); + AssertIntEQ(PKCS7_verify(p7Ver, NULL, store, NULL, NULL, flags), 1); + + BIO_free(content); + BIO_free(inBio); + BIO_free(outBio); + PKCS7_free(p7Ver); + PKCS7_free(p7); + } + + /* generate and verify SMIME: detached */ + { + AssertNotNull(inBio = BIO_new(BIO_s_mem())); + AssertIntGT(BIO_write(inBio, data, sizeof(data)), 0); + + flags = PKCS7_DETACHED | PKCS7_STREAM; + AssertNotNull(p7 = PKCS7_sign(signCert, signKey, NULL, inBio, flags)); + AssertNotNull(outBio = BIO_new(BIO_s_mem())); + AssertIntEQ(SMIME_write_PKCS7(outBio, p7, inBio, flags), 1); + + AssertNotNull(p7Ver = SMIME_read_PKCS7(outBio, &content)); + AssertIntEQ(PKCS7_verify(p7Ver, NULL, store, content, NULL, flags), 1); + + BIO_free(content); + BIO_free(inBio); + BIO_free(outBio); + PKCS7_free(p7Ver); + PKCS7_free(p7); + } + + /* generate and verify SMIME: PKCS7_TEXT to add Content-Type header */ + { + AssertNotNull(inBio = BIO_new(BIO_s_mem())); + AssertIntGT(BIO_write(inBio, data, sizeof(data)), 0); + + flags = PKCS7_STREAM | PKCS7_DETACHED | PKCS7_TEXT; + AssertNotNull(p7 = PKCS7_sign(signCert, signKey, NULL, inBio, flags)); + AssertNotNull(outBio = BIO_new(BIO_s_mem())); + AssertIntEQ(SMIME_write_PKCS7(outBio, p7, inBio, flags), 1); + + AssertNotNull(p7Ver = SMIME_read_PKCS7(outBio, &content)); + AssertIntEQ(PKCS7_verify(p7Ver, NULL, store, content, NULL, flags), 1); + + BIO_free(content); + BIO_free(inBio); + BIO_free(outBio); + PKCS7_free(p7Ver); + PKCS7_free(p7); + } + + X509_STORE_free(store); + X509_free(caCert); + X509_free(signCert); + EVP_PKEY_free(signKey); + BIO_free(keyBio); + BIO_free(certBio); + BIO_free(caBio); + + printf(resultFmt, passed); +#endif +} #endif /* HAVE_SMIME*/ #endif /* !NO_BIO */ @@ -53195,11 +53536,13 @@ void ApiTest(void) test_X509_REQ(); /* OpenSSL PKCS7 API test */ test_wolfssl_PKCS7(); + test_wolfSSL_PKCS7_sign(); test_wolfSSL_PKCS7_SIGNED_new(); #ifndef NO_BIO test_wolfSSL_PEM_write_bio_PKCS7(); #ifdef HAVE_SMIME test_wolfSSL_SMIME_read_PKCS7(); + test_wolfSSL_SMIME_write_PKCS7(); #endif #endif diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index fa444f38e..6a38d0c48 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -32493,22 +32493,25 @@ MimeParam* wc_MIME_find_param_attr(const char* attribute, } /***************************************************************************** -* wc_MIME_canonicalize - Canonicalize a line by converting all line endings -* to CRLF. +* wc_MIME_single_canonicalize - Canonicalize a line by converting the trailing +* line ending to CRLF. +* +* line - input line to canonicalize +* len - length of line in chars on input, length of output array on return * * RETURNS: * returns a pointer to a canonicalized line on success, NULL on error. */ -char* wc_MIME_canonicalize(const char* line) +char* wc_MIME_single_canonicalize(const char* line, word32* len) { size_t end = 0; char* canonLine = NULL; - if (line == NULL || XSTRLEN(line) == 0) { + if (line == NULL || len == NULL || *len == 0) { return NULL; } - end = XSTRLEN(line); + end = *len; while (end >= 1 && ((line[end-1] == '\r') || (line[end-1] == '\n'))) { end--; } @@ -32519,10 +32522,11 @@ char* wc_MIME_canonicalize(const char* line) return NULL; } - XSTRNCPY(canonLine, line, end); + XMEMCPY(canonLine, line, end); canonLine[end] = '\r'; canonLine[end+1] = '\n'; canonLine[end+2] = '\0'; + *len = (word32)(end + 3); return canonLine; } diff --git a/wolfssl/openssl/pkcs7.h b/wolfssl/openssl/pkcs7.h index ec02849a3..dfede29e7 100644 --- a/wolfssl/openssl/pkcs7.h +++ b/wolfssl/openssl/pkcs7.h @@ -34,14 +34,21 @@ #if defined(OPENSSL_ALL) && defined(HAVE_PKCS7) +#define PKCS7_TEXT 0x1 +#define PKCS7_NOCERTS 0x2 +#define PKCS7_DETACHED 0x40 +#define PKCS7_BINARY 0x80 #define PKCS7_NOINTERN 0x0010 #define PKCS7_NOVERIFY 0x0020 +#define PKCS7_STREAM 0x1000 +#define PKCS7_PARTIAL 0x4000 typedef struct WOLFSSL_PKCS7 { PKCS7 pkcs7; unsigned char* data; int len; + int type; /* from PKCS7_TYPES, for PKCS7_final() */ WOLFSSL_STACK* certs; } WOLFSSL_PKCS7; @@ -57,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 int wolfSSL_i2d_PKCS7_bio(WOLFSSL_BIO *bio, PKCS7 *p7); 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_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_BIO* out); WOLFSSL_API WOLFSSL_STACK* wolfSSL_PKCS7_to_stack(PKCS7* pkcs7); @@ -67,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); #if defined(HAVE_SMIME) 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 */ @@ -78,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 i2d_PKCS7_bio wolfSSL_i2d_PKCS7_bio #define i2d_PKCS7 wolfSSL_i2d_PKCS7 +#define PKCS7_sign wolfSSL_PKCS7_sign #define PKCS7_verify wolfSSL_PKCS7_verify +#define PKCS7_final wolfSSL_PKCS7_final #define PKCS7_get0_signers wolfSSL_PKCS7_get0_signers #define PEM_write_bio_PKCS7 wolfSSL_PEM_write_bio_PKCS7 #if defined(HAVE_SMIME) #define SMIME_read_PKCS7 wolfSSL_SMIME_read_PKCS7 +#define SMIME_write_PKCS7 wolfSSL_SMIME_write_PKCS7 #endif /* HAVE_SMIME */ #endif /* OPENSSL_ALL && HAVE_PKCS7 */ diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index 6c423dc78..4fca5344f 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -2029,11 +2029,9 @@ WOLFSSL_LOCAL void FreeDer(DerBuffer** der); #ifdef HAVE_SMIME 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_create_header(char* name, char* body, MimeHdr** hdr); -WOLFSSL_LOCAL int wc_MIME_create_parameter(char* attribute, char* value, MimeParam** param); 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 char* wc_MIME_canonicalize(const char* line); +WOLFSSL_LOCAL char* wc_MIME_single_canonicalize(const char* line, word32* len); WOLFSSL_LOCAL int wc_MIME_free_hdrs(MimeHdr* head); #endif /* HAVE_SMIME */ diff --git a/wolfssl/wolfcrypt/pkcs7.h b/wolfssl/wolfcrypt/pkcs7.h index 8e332e54e..430ae2831 100644 --- a/wolfssl/wolfcrypt/pkcs7.h +++ b/wolfssl/wolfcrypt/pkcs7.h @@ -331,6 +331,7 @@ struct PKCS7 { /* used by DecodeEnvelopedData with multiple encrypted contents */ byte* cachedEncryptedContent; word32 cachedEncryptedContentSz; + word16 contentCRLF:1; /* have content line endings been converted to CRLF */ /* !! NEW DATA MEMBERS MUST BE ADDED AT END !! */ };