Files
wolfssl/scripts
Tobias Frauenschläger 16cc1f28bb Resume the TLS 1.3 certificate chain send after a WANT_WRITE
SendTls13Certificate keeps its chain walk cursor in the function locals len,
idx, offset and p, but the only state that survives the WANT_WRITE return of a
non-blocking send is ssl->fragOffset, and that is consulted for the leaf
certificate alone. A send that blocked part way through the chain therefore
re-primed the walk on the next call and copied the chain from its first byte
again. The byte count still matched the announced payload size, so the message
stayed well formed on the wire while the tail of the chain was replaced by a
repeat of its head, and the peer rejected it with BUFFER_ERROR.

Rebuild the cursor from ssl->fragOffset when a resume lands inside the chain.
NextCert reads each entry's three byte length prefix and skips it, so passing
over the entries already sent costs one hop per certificate and only happens on
a resume. Guard the extension index bump the same way the send loop does, so
builds without certificate status request keep the leaf extension size.

Track the size of the chain entry being written in its own variable rather than
folding the extension size into len once the entry completes. The send loop
detected completion with offset == len + OPAQUE16_LEN and kept that check
honest by adding extSz[extIdx] - OPAQUE16_LEN to len at the end of an entry, so
until then len held the raw certificate length and the check read as complete
whenever a fragment boundary landed exactly OPAQUE16_LEN bytes into a real
extension. The walk then jumped to the next certificate in the middle of the
current one. entrySz records len + extSz[extIdx] when the entry is picked up,
len keeps the raw certificate length AddCertExt expects, and both the resume
and the ordinary multi fragment path test the same condition.

The stapled chain in scripts/ocsp-stapling_tls13multi.test reproduces the entry
size case with the server records held to 1482 bytes: the boundary falls two
bytes into an 1837 byte OCSP extension and the handshake fails, while 1480,
1481, 1483 and 1484 all pass.

Reaching this needs a certificate message larger than one record, which is why
it stayed dormant with classic certificates. Add SLH-DSA scenarios with
simulated WANT_WRITE, for server and for mutual authentication, to
tests/test-tls13-slhdsa-entity-128s.conf.

The same resume path mishandles the stapled OCSP responses. WriteCSRToBuffer
fills extSz[] only for the entries whose buffer it allocates, so on a resumed
call every entry that still held a buffer, the one being written and all that
follow it, kept the OPAQUE16_LEN default of an empty extension. The message
length, the entry sizes and the extension bytes written for those entries were
all derived from that default. Recover the size from the extension length
already written into the buffer instead.

SetupOcspResp appends a fresh request per certificate on every call, so a
message that resumed often enough exhausted the extension array and the
handshake ended with MAX_CERT_EXTENSIONS_ERR. Look the responses up once, when
the message starts, and reuse them for the rest of it.

A resumed call also reallocates the extension buffers of the entries it has
already sent, and the walk passes over those entries without writing them
again, so free them there. Free the array in wolfSSL_ResourceFree as well:
nothing released it when a connection ended part way through a Certificate
message, which leaked one OCSP response per unsent entry.

Test case 8 of scripts/ocsp-stapling_tls13multi.test covers all three. A
maximum fragment length of 512 bytes splits the stapled message over about
twenty records and the server blocks on every one of them; without these fixes
the handshake fails with MAX_CERT_EXTENSIONS_ERR.
2026-08-07 08:14:25 +02:00
..
2024-09-05 15:10:50 -07:00
2026-05-14 13:10:13 +02:00
2026-03-04 15:06:55 -07:00
2026-07-23 21:51:26 -07:00
2026-07-08 09:33:47 +10:00