Commit aeeacc6
committed
preTBS: replace re-encode reconstruction with byte-exact extension excision
The previous wc_GeneratePreTBS reconstructed the preTBS by copying
DecodedCert metadata into a transient Cert and feeding it through
MakeAnyCert. Audit showed this approach is fundamentally fragile for
X9.146 interop:
- Re-encoding can never guarantee byte-identical output to the
issuer's original TBSCertificate. Extension order, criticality
flags (the encoder hardcodes KU=critical, has no knob for EKU /
CertPolicies / CRLDP / CertPolicies criticality), DN canonical
encoding, integer min-encoding and many other DER details all
have to align with whatever produced the original cert.
- DecodedCert only models a fixed set of extensions; AIA, Name
Constraints, Policy Constraints/Mappings, CT SCTs, OCSP Must
Staple, Subject Information Access and any vendor extension are
silently dropped.
- Even within the modelled set, basicConstCrit, AKID
authorityCertIssuer/SerialNumber, and the ACME identifier
extension were never copied; the encoder ignored most criticality
flags anyway.
- issRaw / sbjRaw are bounded by sizeof(CertName) (~352B) so long
DNs hit BUFFER_E.
X9.146 specifies the preTBS as exactly the TBSCertificate with the
altSignatureValue extension removed - byte for byte. The new
implementation does exactly that:
- Verifier-side (cert has altSigValue):
- Locate the [3] EXPLICIT wrapper using DecodedCert.extensions /
extensionsIdx / extensionsSz.
- Walk Extension items, find the one whose extnValue OCTET
STRING is dCert->altSigValDer (pointer overlap into source).
- Recompute the inner Extensions SEQUENCE / [3] EXPLICIT /
outer TBS SEQUENCE lengths.
- Emit: new TBS header + TBS prefix verbatim + new [3] header
+ new inner SEQ header + extensions before altSigVal verbatim
+ extensions after altSigVal verbatim.
- Issuer-side fast path (cert has no altSigValue yet, used while
computing the alt signature input): copy the parsed TBS verbatim.
Embedded-friendly: zero heap allocations, ~60 bytes of stack, single
forward parse of the extensions list, three XMEMCPY calls into the
caller's buffer. Function no longer depends on WOLFSSL_CERT_GEN /
WOLFSSL_CERT_EXT / WOLFSSL_ALT_NAMES / SetDatesFromDcert /
SetAltNamesFromDcert / MakeAnyCert / MakeCertReq.
Removed scaffolding (only existed to feed the old reconstruction
path; pure dead-weight under the new approach):
- DecodedCert.rawPublicKey / rawPubKeySize
- Cert.pubKeyDer / pubKeyLen
- GetCertKey raw-SPKI capture
- MakeAnyCert / MakeCertReq "pre-encoded SPKI" fast paths
- MakeAnyCert / MakeCertReq keyType-only validation relaxation
- SetAltNamesFromDcert altEmailNames merge + criticality propagation
(was working around DecodedCert -> Cert metadata loss; not needed
when we don't go through Cert)
- AddDNSEntryToList ordering change (was workaround for SAN order
in re-encoded preTBS)
- WOLFSSL_ALT_NAMES force-enable in settings.h DUAL_ALG_CERTS block
(was only required by the SetAltNamesFromDcert call)
CSR preTBS support deliberately left out: CSR extensions sit inside
the attributes field, not under a [3] EXPLICIT wrapper, so excision
needs a different walker. wc_GeneratePreTBS now returns
NOT_COMPILED_IN for CSRs; the existing dual-alg-cert verify flow does
not exercise CSRs.
Verified with --enable-dual-alg-certs --enable-experimental
(default) and additionally --enable-dilithium --enable-mldsa
+ -DWOLFSSL_CUSTOM_OID -DHAVE_OID_ENCODING -DHAVE_OID_DECODING:
- Both configs build clean under -Werror.
- tests/unit.test reports 0 failures.
- test_dual_alg_support, test_dual_alg_crit_ext_support and
test_dual_alg_ecdsa_mldsa all pass under the dilithium config -
the latter exercises the issuer-side fast path (TBS verbatim)
and the test_dual_alg_support TLS handshake exercises the
verifier-side excision path against an actual peer cert.1 parent f77fdad commit aeeacc6
4 files changed
Lines changed: 216 additions & 284 deletions
0 commit comments