Skip to content

Commit 37d36e7

Browse files
Code review feedback
1 parent 6c6f3a6 commit 37d36e7

3 files changed

Lines changed: 140 additions & 5 deletions

File tree

tests/api.c

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12268,6 +12268,108 @@ static int test_wc_CheckCertSigPubKey(void)
1226812268
return EXPECT_RESULT();
1226912269
}
1227012270

12271+
/* Trailing data after a certificate's outer SEQUENCE must be rejected at parse
12272+
* time on a default (strict) build and tolerated when WOLFSSL_NO_ASN_STRICT is
12273+
* defined. In the latter case the stored/canonical DER (wolfSSL_X509_get_der /
12274+
* wolfSSL_i2d_X509) must still be bounded to the certificate itself and never
12275+
* re-emit the trailing bytes. */
12276+
static int test_cert_trailing_data(void)
12277+
{
12278+
EXPECT_DECLS;
12279+
#if !defined(NO_CERTS) && !defined(NO_RSA) && !defined(NO_FILESYSTEM) && \
12280+
defined(WOLFSSL_PEM_TO_DER)
12281+
const char* ca_cert = "./certs/ca-cert.pem";
12282+
byte* pem = NULL;
12283+
size_t pemSz = 0;
12284+
byte* der = NULL;
12285+
word32 derBufSz = 0;
12286+
int certSz = 0;
12287+
byte* certPlusJunk = NULL;
12288+
word32 certPlusJunkSz = 0;
12289+
const word32 junkSz = 24;
12290+
DecodedCert decoded;
12291+
12292+
/* Load a known-good certificate and convert it to DER. */
12293+
ExpectIntEQ(load_file(ca_cert, &pem, &pemSz), 0);
12294+
derBufSz = (word32)pemSz; /* DER is always smaller than its PEM. */
12295+
ExpectNotNull(der = (byte*)XMALLOC(derBufSz, NULL, DYNAMIC_TYPE_TMP_BUFFER));
12296+
ExpectIntGT(certSz = wc_CertPemToDer(pem, (int)pemSz, der, (int)derBufSz,
12297+
CERT_TYPE), 0);
12298+
12299+
/* Build a buffer holding the certificate followed by N junk bytes. */
12300+
if (certSz > 0) {
12301+
certPlusJunkSz = (word32)certSz + junkSz;
12302+
ExpectNotNull(certPlusJunk = (byte*)XMALLOC(certPlusJunkSz, NULL,
12303+
DYNAMIC_TYPE_TMP_BUFFER));
12304+
}
12305+
if (certPlusJunk != NULL) {
12306+
XMEMCPY(certPlusJunk, der, (size_t)certSz);
12307+
XMEMSET(certPlusJunk + certSz, 0xA5, junkSz);
12308+
}
12309+
12310+
/* Sanity: the clean certificate parses successfully. */
12311+
if (certSz > 0) {
12312+
XMEMSET(&decoded, 0, sizeof(decoded));
12313+
wc_InitDecodedCert(&decoded, der, (word32)certSz, NULL);
12314+
ExpectIntEQ(wc_ParseCert(&decoded, CERT_TYPE, NO_VERIFY, NULL), 0);
12315+
wc_FreeDecodedCert(&decoded);
12316+
}
12317+
12318+
/* Certificate with appended junk. */
12319+
if (certPlusJunk != NULL) {
12320+
XMEMSET(&decoded, 0, sizeof(decoded));
12321+
wc_InitDecodedCert(&decoded, certPlusJunk, certPlusJunkSz, NULL);
12322+
#ifdef WOLFSSL_NO_ASN_STRICT
12323+
/* Opt-out build: trailing data is tolerated at parse time, but srcIdx
12324+
* must stop at the end of the certificate (not the junk). */
12325+
ExpectIntEQ(wc_ParseCert(&decoded, CERT_TYPE, NO_VERIFY, NULL), 0);
12326+
ExpectIntEQ((int)decoded.srcIdx, certSz);
12327+
#else
12328+
/* Default (strict) build: trailing data is rejected. */
12329+
ExpectIntEQ(wc_ParseCert(&decoded, CERT_TYPE, NO_VERIFY, NULL),
12330+
WC_NO_ERR_TRACE(ASN_PARSE_E));
12331+
#endif
12332+
wc_FreeDecodedCert(&decoded);
12333+
}
12334+
12335+
/* derCert bounding: on a non-strict build a certificate with trailing data
12336+
* loads into a WOLFSSL_X509, but wolfSSL_X509_get_der / wolfSSL_i2d_X509
12337+
* must re-emit only the certificate bytes (srcIdx), proving the trailing
12338+
* junk is never stored or re-serialized. */
12339+
#if defined(OPENSSL_EXTRA) && defined(WOLFSSL_NO_ASN_STRICT)
12340+
if (certPlusJunk != NULL) {
12341+
WOLFSSL_X509* x509 = NULL;
12342+
const byte* getDer = NULL;
12343+
int getDerSz = 0;
12344+
byte* i2dDer = NULL;
12345+
int i2dSz = 0;
12346+
12347+
x509 = wolfSSL_X509_load_certificate_buffer(certPlusJunk,
12348+
(int)certPlusJunkSz, WOLFSSL_FILETYPE_ASN1);
12349+
ExpectNotNull(x509);
12350+
12351+
getDer = wolfSSL_X509_get_der(x509, &getDerSz);
12352+
ExpectNotNull(getDer);
12353+
ExpectIntEQ(getDerSz, certSz);
12354+
12355+
ExpectIntEQ((i2dSz = wolfSSL_i2d_X509(x509, &i2dDer)), certSz);
12356+
if (i2dDer != NULL)
12357+
XFREE(i2dDer, HEAP_HINT, DYNAMIC_TYPE_OPENSSL);
12358+
12359+
wolfSSL_X509_free(x509);
12360+
}
12361+
#endif /* OPENSSL_EXTRA && WOLFSSL_NO_ASN_STRICT */
12362+
12363+
if (certPlusJunk != NULL)
12364+
XFREE(certPlusJunk, NULL, DYNAMIC_TYPE_TMP_BUFFER);
12365+
if (der != NULL)
12366+
XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER);
12367+
if (pem != NULL)
12368+
XFREE(pem, NULL, DYNAMIC_TYPE_TMP_BUFFER);
12369+
#endif
12370+
return EXPECT_RESULT();
12371+
}
12372+
1227112373
static int test_wolfSSL_X509_ext_d2i(void)
1227212374
{
1227312375
EXPECT_DECLS;
@@ -37009,6 +37111,7 @@ TEST_CASE testCases[] = {
3700937111
TEST_DECL(test_wc_GetPubKeyDerFromCert),
3701037112
TEST_DECL(test_wc_GetSubjectPubKeyInfoDerFromCert),
3701137113
TEST_DECL(test_wc_CheckCertSigPubKey),
37114+
TEST_DECL(test_cert_trailing_data),
3701237115

3701337116
/* wolfCrypt ASN tests */
3701437117
TEST_DECL(test_ToTraditional),

wolfcrypt/src/asn.c

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22558,7 +22558,7 @@ static int DecodeCertInternal(DecodedCert* cert, int verify, int* criticalExt,
2255822558
* whole template but their callers may pass larger buffers. The TRUSTED
2255922559
* CERTIFICATE format legitimately carries auxiliary trust data after the
2256022560
* certificate, so allow it when cert->allowTrailing is set. */
22561-
if ((ret == 0) && (!done) && (!stopAtPubKey) && (!stopAfterPubKey) &&
22561+
if ((ret == 0) && (!done) &&
2256222562
(!cert->allowTrailing) && (cert->srcIdx != cert->maxIdx)
2256322563
#ifdef WOLFSSL_CERT_REQ
2256422564
&& (!cert->isCSR)
@@ -23770,6 +23770,11 @@ int ParseCertRelative(DecodedCert* cert, int type, int verify, void* cm,
2377023770
int ret = 0;
2377123771
#ifndef WOLFSSL_ASN_TEMPLATE
2377223772
word32 confirmOID = 0;
23773+
#ifndef WOLFSSL_NO_ASN_STRICT
23774+
/* Full input length before GetCertHeader() rebounds cert->maxIdx to the
23775+
* certificate's outer SEQUENCE; used to detect trailing data. */
23776+
word32 origMaxIdx = 0;
23777+
#endif
2377323778
#ifdef WOLFSSL_CERT_REQ
2377423779
int len = 0;
2377523780
#endif
@@ -23808,6 +23813,12 @@ int ParseCertRelative(DecodedCert* cert, int type, int verify, void* cm,
2380823813

2380923814
if (cert->sigCtx.state == SIG_STATE_BEGIN) {
2381023815
#ifndef WOLFSSL_ASN_TEMPLATE
23816+
#ifndef WOLFSSL_NO_ASN_STRICT
23817+
/* Capture the full input length now: DecodeToKey()->GetCertHeader()
23818+
* rebounds cert->maxIdx to the certificate's outer SEQUENCE, after
23819+
* which trailing bytes would otherwise be invisible. */
23820+
origMaxIdx = cert->maxIdx;
23821+
#endif
2381123822
cert->badDate = 0;
2381223823
cert->criticalExt = 0;
2381323824
if ((ret = DecodeToKey(cert, verify)) < 0) {
@@ -24050,6 +24061,27 @@ int ParseCertRelative(DecodedCert* cert, int type, int verify, void* cm,
2405024061
WOLFSSL_ERROR_VERBOSE(ASN_SIG_OID_E);
2405124062
return ASN_SIG_OID_E;
2405224063
}
24064+
24065+
#ifndef WOLFSSL_NO_ASN_STRICT
24066+
/* Reject trailing data after the certificate's outer SEQUENCE.
24067+
*
24068+
* This is the non-template counterpart to the trailing-data check in
24069+
* DecodeCertInternal(). GetCertHeader() rebounded cert->maxIdx to the
24070+
* end of the certificate's outer SEQUENCE, so comparing it against the
24071+
* original input length (origMaxIdx) detects any appended bytes. The
24072+
* TRUSTED CERTIFICATE format legitimately carries auxiliary trust data
24073+
* after the certificate (cert->allowTrailing), and CSRs carry their own
24074+
* trailing structure, so both are exempt. */
24075+
if ((cert->maxIdx != origMaxIdx) && (!cert->allowTrailing)
24076+
#ifdef WOLFSSL_CERT_REQ
24077+
&& (!cert->isCSR)
24078+
#endif
24079+
) {
24080+
WOLFSSL_MSG("Trailing data after certificate");
24081+
WOLFSSL_ERROR_VERBOSE(ASN_PARSE_E);
24082+
return ASN_PARSE_E;
24083+
}
24084+
#endif /* !WOLFSSL_NO_ASN_STRICT */
2405324085
#else
2405424086
#ifdef WOLFSSL_CERT_REQ
2405524087
if (cert->isCSR) {

wolfssl/wolfcrypt/asn.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2142,10 +2142,6 @@ struct DecodedCert {
21422142

21432143
/* Option Bits */
21442144
WC_BITFIELD subjectCNStored:1; /* have we saved a copy we own */
2145-
WC_BITFIELD allowTrailing:1; /* permit data after the cert's outer
2146-
* SEQUENCE. Used internally for the
2147-
* TRUSTED CERTIFICATE auxiliary trust
2148-
* info. */
21492145
WC_BITFIELD extSubjKeyIdSet:1; /* Set when the SKID was read from cert */
21502146
WC_BITFIELD extAuthKeyIdSet:1; /* Set when the AKID was read from cert */
21512147
#ifndef IGNORE_NAME_CONSTRAINTS
@@ -2214,6 +2210,10 @@ struct DecodedCert {
22142210
#ifdef HAVE_RPK
22152211
WC_BITFIELD isRPK:1; /* indicate the cert is Raw-Public-Key cert in RFC7250 */
22162212
#endif
2213+
WC_BITFIELD allowTrailing:1; /* permit data after the cert's outer
2214+
* SEQUENCE. Used internally for the
2215+
* TRUSTED CERTIFICATE auxiliary trust
2216+
* info. */
22172217
#ifdef WC_ASN_UNKNOWN_EXT_CB
22182218
wc_UnknownExtCallback unknownExtCallback;
22192219
wc_UnknownExtCallbackEx unknownExtCallbackEx;

0 commit comments

Comments
 (0)