mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-08-23 14:43:23 +02:00
SignCert() checked the output buffer with
requestSz + MAX_SEQ_SZ * 2 + sigSz > buffSz
before handing the buffer to AddSignature(). That accounts for the outer
SEQUENCE header but not for the signatureAlgorithm AlgorithmIdentifier
(OID plus optional NULL parameters) or the signatureValue BIT STRING
header that AddSignature() also writes, an under-count of about 13 bytes.
AddSignature() takes no buffer size of its own, so any certificate whose
final encoding lands in that narrow band just under buffSz passed the
check and was written past the end of the buffer.
The same estimate was used in wc_SignCert_cb().
Both call sites now ask AddSignature() for the exact encoding size by
passing a NULL buffer first, then compare that against buffSz. This is
the two-pass idiom already used when signing CRLs in SignCrl(), in
wolfssl_x509_make_der() and in wolfSSL_X509_CRL_sign(). Both the template
and the original ASN.1 encoders support the NULL buffer sizing call.
The comparison is made unsigned, matching the pre-flight in SignCrl().
Casting buffSz to int made a buffer larger than INT_MAX compare negative
and rejected every signature for it.
Both functions also now bound requestSz against buffSz up front.
MakeSignature() and MakeSignatureCb() hash requestSz bytes out of buf
before any size check runs, so a caller passing the two mismatched got an
out of bounds read of up to requestSz - buffSz bytes before the function
returned. Only a negative requestSz was rejected before. Reaching this
needs the application to pass values that disagree, so it is API misuse
rather than attacker controlled input, but the read side now carries the
same guarantee as the write side.
Reachable from the OpenSSL compatibility layer through
wolfSSL_X509_sign() and wolfSSL_X509_REQ_sign(), where the caller
controls the certificate contents that steer the encoded size into the
band.
Adds test_wc_SignCert_buffer_bounds(), which signs into buffers sized
across the band below the exact encoding size and requires BUFFER_E and
an untouched guard region for each, while still accepting the exact size.
test_wc_SignCert_cb() gains the same check for the callback entry point,
using its RSA half where the PKCS#1 v1.5 signature is fixed length, in
both directions so that an over-conservative estimate is caught too.
The bounds test covers ECDSA as well as RSA. IsSigAlgoNoParams() drops
the NULL parameters from the AlgorithmIdentifier, so the width an
estimate under-counts by differs between the two: 24 bytes of wrapper
against the 12 byte estimate for RSA, but only 19 for ECDSA, putting the
capacities that used to be accepted and overrun within 8 bytes of the
exact size.
An ECDSA encoding size cannot be measured once and reused, because the
DER INTEGERs holding r and s change length with the leading zero bytes of
each new signature. The sweep measures a fresh reference size every
iteration and, rather than requiring BUFFER_E for a capacity that the
next signature might genuinely fit, asserts what has to hold either way:
the call returns BUFFER_E or a size within the capacity, and the guard
region past the capacity is untouched. That covers the whole band instead
of trading it away for a margin wide enough to absorb the jitter.
The prerequisites are split into one condition macro per algorithm rather
than one shared list. Gating the whole test on the RSA prerequisites
would have compiled the ECDSA sweep out of a build without RSA, which is
exactly where it is the only coverage that exists.
Both tests set an explicit serial number. wc_InitCert() leaves serialSz at
zero, so wc_MakeCert() generates a random serial, and GenerateInteger()
does not shrink the length after dropping leading zero bytes, which lets
the promoted byte carry the MSB and makes the encoder pad the INTEGER with
an extra 0x00. Measured over 200000 generated bodies, 813 of them, 0.406
percent, came out one byte longer, which would have made the swept
capacities disagree with the reference size for roughly one run in 128.
75 lines
3.4 KiB
C
75 lines
3.4 KiB
C
/* test_asn.h
|
|
*
|
|
* Copyright (C) 2006-2026 wolfSSL Inc.
|
|
*
|
|
* This file is part of wolfSSL.
|
|
*
|
|
* wolfSSL is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* wolfSSL is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
|
*/
|
|
|
|
#ifndef WOLFCRYPT_TEST_ASN_H
|
|
#define WOLFCRYPT_TEST_ASN_H
|
|
|
|
#include <tests/api/api_decl.h>
|
|
|
|
int test_SetAsymKeyDer(void);
|
|
int test_DecodeAsymKey_lenient_versions(void);
|
|
int test_DecodeAsymKey_negative(void);
|
|
int test_GetSetShortInt(void);
|
|
int test_wc_IndexSequenceOf(void);
|
|
int test_wolfssl_local_MatchBaseName(void);
|
|
int test_wolfssl_local_MatchDnsConstraintWildcard(void);
|
|
int test_wolfssl_local_MatchUriNameConstraint(void);
|
|
int test_wc_DecodeRsaPssParams(void);
|
|
int test_SerialNumber0_RootCA(void);
|
|
int test_DecodeAltNames_length_underflow(void);
|
|
int test_DecodeCertExtensions_dup_certpol(void);
|
|
int test_ParseCert_SM3wSM2_short_pubkey(void);
|
|
int test_ParseCert_dnBufferBoundary(void);
|
|
int test_wc_DecodeObjectId(void);
|
|
int test_ToTraditional_ex_handcrafted(void);
|
|
int test_ToTraditional_ex_roundtrip(void);
|
|
int test_ToTraditional_ex_negative(void);
|
|
int test_ToTraditional_ex_mldsa_bad_params(void);
|
|
int test_wc_SignCert_buffer_bounds(void);
|
|
int test_wc_AsnDecisionCoverage(void);
|
|
int test_wc_AsnFeatureCoverage(void);
|
|
|
|
#define TEST_ASN_DECLS \
|
|
TEST_DECL_GROUP("asn", test_SetAsymKeyDer), \
|
|
TEST_DECL_GROUP("asn", test_DecodeAsymKey_lenient_versions), \
|
|
TEST_DECL_GROUP("asn", test_DecodeAsymKey_negative), \
|
|
TEST_DECL_GROUP("asn", test_GetSetShortInt), \
|
|
TEST_DECL_GROUP("asn", test_wc_IndexSequenceOf), \
|
|
TEST_DECL_GROUP("asn", test_wolfssl_local_MatchBaseName), \
|
|
TEST_DECL_GROUP("asn", test_wolfssl_local_MatchDnsConstraintWildcard), \
|
|
TEST_DECL_GROUP("asn", test_wolfssl_local_MatchUriNameConstraint), \
|
|
TEST_DECL_GROUP("asn", test_wc_DecodeRsaPssParams), \
|
|
TEST_DECL_GROUP("asn", test_SerialNumber0_RootCA), \
|
|
TEST_DECL_GROUP("asn", test_DecodeAltNames_length_underflow), \
|
|
TEST_DECL_GROUP("asn", test_DecodeCertExtensions_dup_certpol), \
|
|
TEST_DECL_GROUP("asn", test_ParseCert_SM3wSM2_short_pubkey), \
|
|
TEST_DECL_GROUP("asn", test_ParseCert_dnBufferBoundary), \
|
|
TEST_DECL_GROUP("asn", test_wc_DecodeObjectId), \
|
|
TEST_DECL_GROUP("asn", test_ToTraditional_ex_handcrafted), \
|
|
TEST_DECL_GROUP("asn", test_ToTraditional_ex_roundtrip), \
|
|
TEST_DECL_GROUP("asn", test_ToTraditional_ex_negative), \
|
|
TEST_DECL_GROUP("asn", test_ToTraditional_ex_mldsa_bad_params), \
|
|
TEST_DECL_GROUP("asn", test_wc_SignCert_buffer_bounds), \
|
|
TEST_DECL_GROUP("asn", test_wc_AsnDecisionCoverage), \
|
|
TEST_DECL_GROUP("asn", test_wc_AsnFeatureCoverage)
|
|
|
|
#endif /* WOLFCRYPT_TEST_ASN_H */
|