Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions doc/dox_comments/header_files/pkcs7.h
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,50 @@ int wc_PKCS7_EncodeSignedData_ex(wc_PKCS7* pkcs7, const byte* hashBuf,
int wc_PKCS7_VerifySignedData(wc_PKCS7* pkcs7,
byte* pkiMsg, word32 pkiMsgSz);

/*!
\ingroup PKCS7

\brief This function decodes a PKCS7 SignedData message without requiring
its content. Certificates, the signer information and the content type are
parsed and stored in the PKCS7 structure like wc_PKCS7_VerifySignedData()
does. A message with attached content is verified while decoding. A
detached message whose content has not been set is only decoded:
pkcs7->detached is set and the signature is verified later by calling
wc_PKCS7_VerifySignedData() once the content is available.

\return 0 Returned on successfully decoding the message
\return BAD_FUNC_ARG Returned if pkcs7 is NULL
\return Any error returned by wc_PKCS7_VerifySignedData() on a malformed
message, or on a failed verification of attached content

\param pkcs7 pointer to the PKCS7 structure in which to store the parsed
certificates and signer information
\param pkiMsg pointer to the buffer containing the signed message to decode
\param pkiMsgSz size of the signed message

_Example_
\code
PKCS7 pkcs7;
int ret;
byte pkcs7Buff[] = {}; // the PKCS7 signature
byte content[] = {}; // the detached content

wc_PKCS7_InitWithCert(&pkcs7, NULL, 0);
ret = wc_PKCS7_DecodeSignedData(&pkcs7, pkcs7Buff, sizeof(pkcs7Buff));
if (ret == 0 && pkcs7.detached) {
pkcs7.content = content;
pkcs7.contentSz = sizeof(content);
ret = wc_PKCS7_VerifySignedData(&pkcs7, pkcs7Buff, sizeof(pkcs7Buff));
}
wc_PKCS7_Free(&pkcs7);
\endcode

\sa wc_PKCS7_VerifySignedData
\sa wc_PKCS7_InitWithCert
*/
int wc_PKCS7_DecodeSignedData(wc_PKCS7* pkcs7,
byte* pkiMsg, word32 pkiMsgSz);


/*!
\ingroup PKCS7
Expand Down
32 changes: 29 additions & 3 deletions src/ssl_p7p12.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,9 @@ void wolfSSL_PKCS7_SIGNED_free(PKCS7_SIGNED* p7)

/**
* Convert DER/ASN.1 encoded signedData structure to internal PKCS7
* structure. Note, does not support detached content.
* structure. An attached signature is verified while decoding. A detached
* signature is only decoded; verify it with wolfSSL_PKCS7_verify() and the
* content.
*
* p7 - pointer to set to address of newly created PKCS7 structure on return
* in - pointer to pointer of DER/ASN.1 data
Expand Down Expand Up @@ -182,20 +184,44 @@ PKCS7* wolfSSL_d2i_PKCS7_ex(PKCS7** p7, const unsigned char** in, int len,
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)
if (wc_PKCS7_DecodeSignedData(&pkcs7->pkcs7, pkcs7->data, pkcs7->len)
!= 0) {
WOLFSSL_MSG("wc_PKCS7_VerifySignedData failed");
WOLFSSL_MSG("wc_PKCS7_DecodeSignedData failed");
wolfSSL_PKCS7_free((PKCS7*)pkcs7);
if (p7 != NULL) {
*p7 = NULL;
}
return NULL;
}
pkcs7->type = SIGNED_DATA;
}

return (PKCS7*)pkcs7;
}

/* Returns 1 if the PKCS7 is a SignedData structure, otherwise 0. */
int wolfSSL_PKCS7_type_is_signed(PKCS7* pkcs7)
{
WOLFSSL_PKCS7* p7 = (WOLFSSL_PKCS7*)pkcs7;

if (p7 == NULL)
return 0;

return p7->type == SIGNED_DATA;
}

/* Returns 1 if the decoded SignedData carries no content (detached
* signature), otherwise 0. */
int wolfSSL_PKCS7_get_detached(PKCS7* pkcs7)
{
WOLFSSL_PKCS7* p7 = (WOLFSSL_PKCS7*)pkcs7;

if (p7 == NULL)
return 0;

return p7->pkcs7.detached != 0;
}


/**
* This API was added as a helper function for libest. It
Expand Down
141 changes: 136 additions & 5 deletions tests/api/test_ossl_p7p12.c
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ int test_wolfSSL_PKCS7_sign(void)
ExpectIntGT((outLen = i2d_PKCS7(p7, &out)), 0);
ExpectNotNull(out);

/* verify with wolfCrypt, d2i_PKCS7 does not support detached content */
/* verify with wolfCrypt */
ExpectNotNull(p7Ver = wc_PKCS7_New(HEAP_HINT, testDevId));
if (p7Ver != NULL) {
p7Ver->content = data;
Expand Down Expand Up @@ -371,10 +371,16 @@ int test_wolfSSL_PKCS7_sign(void)
p7Ver = NULL;
#endif /* !NO_PKCS7_STREAM */

/* verify expected failure (NULL return) from d2i_PKCS7, it does not
* yet support detached content */
/* d2i_PKCS7 decodes the detached bundle, PKCS7_verify is then given
* the content */
tmpPtr = out;
ExpectNull(p7Ver = d2i_PKCS7(NULL, (const byte**)&tmpPtr, outLen));
ExpectNotNull(p7Ver = d2i_PKCS7(NULL, (const byte**)&tmpPtr, outLen));
ExpectIntEQ(PKCS7_is_detached(p7Ver), 1);
BIO_free(inBio);
inBio = NULL;
ExpectNotNull(inBio = BIO_new_mem_buf(data, sizeof(data)));
ExpectIntEQ(PKCS7_verify(p7Ver, NULL, NULL, inBio, NULL,
PKCS7_BINARY | PKCS7_NOVERIFY), 1);
PKCS7_free(p7Ver);
p7Ver = NULL;

Expand All @@ -397,7 +403,7 @@ int test_wolfSSL_PKCS7_sign(void)
ExpectIntEQ(PKCS7_final(p7, inBio, flags), 1);
ExpectIntGT((outLen = i2d_PKCS7(p7, &out)), 0);

/* verify with wolfCrypt, d2i_PKCS7 does not support detached content */
/* verify with wolfCrypt */
ExpectNotNull(p7Ver = wc_PKCS7_New(HEAP_HINT, testDevId));
if (p7Ver != NULL) {
p7Ver->content = data;
Expand Down Expand Up @@ -675,6 +681,131 @@ int test_wolfSSL_PKCS7_verify_signer_forgery(void)
return EXPECT_RESULT();
}

/* d2i_PKCS7() decodes a detached SignedData so that PKCS7_verify() can be
* handed the content afterwards, as with OpenSSL. An attached SignedData is
* still verified while decoding. */
int test_wolfSSL_PKCS7_d2i_detached(void)
{
EXPECT_DECLS;
#if defined(OPENSSL_ALL) && defined(HAVE_PKCS7) && !defined(NO_BIO) && \
!defined(NO_FILESYSTEM) && !defined(NO_RSA)
const char* signerCertFile = "./certs/server-cert.pem";
const char* signerKeyFile = "./certs/server-key.pem";
const char* caFile = "./certs/ca-cert.pem";
byte content[] = "Detached content to verify.";

WOLFSSL_BIO* certBio = NULL;
WOLFSSL_BIO* keyBio = NULL;
WOLFSSL_BIO* caBio = NULL;
WOLFSSL_BIO* signBio = NULL;
WOLFSSL_BIO* inBio = NULL;
X509* signCert = NULL;
EVP_PKEY* signKey = NULL;
X509* caCert = NULL;
X509_STORE* store = NULL;
PKCS7* p7 = NULL;
PKCS7* p7Dec = NULL;
WOLFSSL_STACK* signers = NULL;
byte* der = NULL;
int derSz = 0;
const byte* p = NULL;
int i;

ExpectNotNull(certBio = BIO_new_file(signerCertFile, "r"));
ExpectNotNull(keyBio = BIO_new_file(signerKeyFile, "r"));
ExpectNotNull(caBio = BIO_new_file(caFile, "r"));
ExpectNotNull(signCert = PEM_read_bio_X509(certBio, NULL, 0, NULL));
ExpectNotNull(signKey = PEM_read_bio_PrivateKey(keyBio, NULL, 0, NULL));
ExpectNotNull(caCert = PEM_read_bio_X509(caBio, NULL, 0, NULL));
ExpectNotNull(store = X509_STORE_new());
ExpectIntEQ(X509_STORE_add_cert(store, caCert), 1);

/* detached signature over 'content' */
ExpectNotNull(signBio = BIO_new(BIO_s_mem()));
ExpectIntGT(BIO_write(signBio, content, sizeof(content)), 0);
ExpectNotNull(p7 = PKCS7_sign(signCert, signKey, NULL, signBio,
PKCS7_BINARY | PKCS7_DETACHED));
ExpectIntGT((derSz = i2d_PKCS7(p7, &der)), 0);
ExpectNotNull(der);

/* decodes without the content */
p = der;
ExpectNotNull(p7Dec = d2i_PKCS7(NULL, &p, derSz));
ExpectTrue(p == der + derSz);
ExpectIntEQ(PKCS7_type_is_signed(p7Dec), 1);
ExpectIntEQ(PKCS7_get_detached(p7Dec), 1);
ExpectIntEQ(PKCS7_is_detached(p7Dec), 1);

/* the embedded signer certificate is available right after decoding */
ExpectNotNull(signers = PKCS7_get0_signers(p7Dec, NULL, 0));
ExpectIntEQ(sk_X509_num(signers), 1);
sk_X509_pop_free(signers, NULL);
signers = NULL;

/* verify with the content supplied through the BIO */
ExpectNotNull(inBio = BIO_new_mem_buf(content, sizeof(content)));
ExpectIntEQ(PKCS7_verify(p7Dec, NULL, store, inBio, NULL, PKCS7_BINARY),
1);
BIO_free(inBio);
inBio = NULL;

/* wrong content still fails */
ExpectNotNull(inBio = BIO_new_mem_buf("bogus content", 13));
ExpectIntEQ(PKCS7_verify(p7Dec, NULL, store, inBio, NULL, PKCS7_BINARY),
WC_NO_ERR_TRACE(WOLFSSL_FAILURE));
BIO_free(inBio);
inBio = NULL;

PKCS7_free(p7Dec);
p7Dec = NULL;
PKCS7_free(p7);
p7 = NULL;
XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER);
der = NULL;
BIO_free(signBio);
signBio = NULL;

/* attached signature: verified while decoding, as before */
ExpectNotNull(signBio = BIO_new(BIO_s_mem()));
ExpectIntGT(BIO_write(signBio, content, sizeof(content)), 0);
ExpectNotNull(p7 = PKCS7_sign(signCert, signKey, NULL, signBio,
PKCS7_BINARY));
ExpectIntGT((derSz = i2d_PKCS7(p7, &der)), 0);
ExpectNotNull(der);
p = der;
ExpectNotNull(p7Dec = d2i_PKCS7(NULL, &p, derSz));
ExpectIntEQ(PKCS7_type_is_signed(p7Dec), 1);
ExpectIntEQ(PKCS7_is_detached(p7Dec), 0);
PKCS7_free(p7Dec);
p7Dec = NULL;

/* tampered attached content is rejected by d2i_PKCS7() */
if (der != NULL) {
for (i = 0; i + (int)sizeof(content) <= derSz; i++) {
if (XMEMCMP(der + i, content, sizeof(content)) == 0) {
der[i] ^= 0x01;
break;
}
}
ExpectTrue(i + (int)sizeof(content) <= derSz);
}
p = der;
ExpectNull(d2i_PKCS7(NULL, &p, derSz));

PKCS7_free(p7);
XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER);
X509_STORE_free(store);
X509_free(caCert);
X509_free(signCert);
EVP_PKEY_free(signKey);
BIO_free(signBio);
BIO_free(certBio);
BIO_free(keyBio);
BIO_free(caBio);
#endif
return EXPECT_RESULT();
}

/* A degenerate (certs-only) PKCS#7 - one with an empty signerInfos SET and
* therefore no signature at all - must NOT be reported as verified, even when
* the embedded certificate chains to a trusted CA, and even when
Expand Down
2 changes: 2 additions & 0 deletions tests/api/test_ossl_p7p12.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ int test_wolfSSL_PKCS7_sign(void);
int test_wolfSSL_PKCS7_verify_signer_forgery(void);
int test_wolfSSL_PKCS7_verify_sid_binding(void);
int test_wolfSSL_PKCS7_verify_degenerate(void);
int test_wolfSSL_PKCS7_d2i_detached(void);
int test_wolfSSL_PKCS7_SIGNED_new(void);
int test_wolfSSL_PEM_write_bio_PKCS7(void);
int test_wolfSSL_PEM_write_bio_encryptedKey(void);
Expand All @@ -44,6 +45,7 @@ int test_wolfSSL_PKCS12(void);
TEST_DECL_GROUP("ossl_p7", test_wolfSSL_PKCS7_verify_signer_forgery), \
TEST_DECL_GROUP("ossl_p7", test_wolfSSL_PKCS7_verify_sid_binding), \
TEST_DECL_GROUP("ossl_p7", test_wolfSSL_PKCS7_verify_degenerate), \
TEST_DECL_GROUP("ossl_p7", test_wolfSSL_PKCS7_d2i_detached), \
TEST_DECL_GROUP("ossl_p7", test_wolfSSL_PKCS7_SIGNED_new), \
TEST_DECL_GROUP("ossl_p7", test_wolfSSL_PEM_write_bio_PKCS7), \
TEST_DECL_GROUP("ossl_p7", test_wolfSSL_PEM_write_bio_encryptedKey), \
Expand Down
87 changes: 87 additions & 0 deletions tests/api/test_pkcs7.c
Original file line number Diff line number Diff line change
Expand Up @@ -6915,6 +6915,93 @@ int test_wc_PKCS7_VerifySignedData_TruncCertSetTag(void)
* parser can see, and must still succeed after fixing that check to reject
* truncated bundles.
*/
/* wc_PKCS7_DecodeSignedData() decodes a detached bundle without its content
* and leaves the signature for a later wc_PKCS7_VerifySignedData(). An
* attached bundle is verified while decoding. */
int test_wc_PKCS7_DecodeSignedData(void)
{
EXPECT_DECLS;
#if defined(HAVE_PKCS7) && !defined(NO_FILESYSTEM) && !defined(NO_RSA)
PKCS7* pkcs7 = NULL;
byte output[6000];
word32 outputSz = sizeof(output);
byte data[] = "Test data to encode.";
byte badData[] = "This is different content than was signed";
word32 i;

/* detached bundle */
ExpectIntGT((outputSz = (word32)CreatePKCS7SignedData(output,
(int)outputSz, data, (word32)sizeof(data), 0, 1, 0, RSA_TYPE)), 0);

ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId));
ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0);
ExpectIntEQ(wc_PKCS7_DecodeSignedData(pkcs7, output, outputSz), 0);
if (pkcs7 != NULL) {
ExpectIntEQ(pkcs7->detached, 1);
ExpectNotNull(pkcs7->singleCert);
ExpectIntGT(pkcs7->singleCertSz, 0);
/* nothing was verified */
ExpectNull(pkcs7->verifyCert);
}

/* verifying without the content still fails */
ExpectIntLT(wc_PKCS7_VerifySignedData(pkcs7, output, outputSz), 0);

/* verify once the content is supplied */
if (pkcs7 != NULL) {
pkcs7->content = data;
pkcs7->contentSz = (word32)sizeof(data);
}
ExpectIntEQ(wc_PKCS7_VerifySignedData(pkcs7, output, outputSz), 0);
if (pkcs7 != NULL) {
ExpectNotNull(pkcs7->verifyCert);
}

/* wrong content is rejected */
if (pkcs7 != NULL) {
pkcs7->content = badData;
pkcs7->contentSz = (word32)sizeof(badData);
}
ExpectIntLT(wc_PKCS7_VerifySignedData(pkcs7, output, outputSz), 0);

wc_PKCS7_Free(pkcs7);
pkcs7 = NULL;

/* attached bundle is verified while decoding */
outputSz = sizeof(output);
ExpectIntGT((outputSz = (word32)CreatePKCS7SignedData(output,
(int)outputSz, data, (word32)sizeof(data), 0, 0, 0, RSA_TYPE)), 0);

ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId));
ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0);
ExpectIntEQ(wc_PKCS7_DecodeSignedData(pkcs7, output, outputSz), 0);
if (pkcs7 != NULL) {
ExpectIntEQ(pkcs7->detached, 0);
ExpectNotNull(pkcs7->verifyCert);
}
wc_PKCS7_Free(pkcs7);
pkcs7 = NULL;

/* tampered attached content is rejected */
for (i = 0; i + sizeof(data) <= outputSz; i++) {
if (XMEMCMP(output + i, data, sizeof(data)) == 0) {
output[i] ^= 0x01;
break;
}
}
ExpectTrue(i + sizeof(data) <= outputSz);
ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId));
ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0);
ExpectIntLT(wc_PKCS7_DecodeSignedData(pkcs7, output, outputSz), 0);
wc_PKCS7_Free(pkcs7);
pkcs7 = NULL;

ExpectIntEQ(wc_PKCS7_DecodeSignedData(NULL, output, outputSz),
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
#endif
return EXPECT_RESULT();
}

int test_wc_PKCS7_VerifySignedData_DegenerateMinimal(void)
{
EXPECT_DECLS;
Expand Down
Loading
Loading