mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-08-18 01:01:42 +02:00
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.