Skip to content

Compliance to new RFCs#10941

Open
Frauschi wants to merge 6 commits into
wolfSSL:masterfrom
Frauschi:rfc_compliance
Open

Compliance to new RFCs#10941
Frauschi wants to merge 6 commits into
wolfSSL:masterfrom
Frauschi:rfc_compliance

Conversation

@Frauschi

Copy link
Copy Markdown
Contributor

RFC compliance updates for TLS 1.3, the sniffer key-log reader, and the external-PSK certificate extension. Six focused, independent changes, grouped by the RFC they address. No public API is changed and every change stays behind its existing compile-time guards.

RFC 9846 (RFC 8446 successor, TLS 1.3 clarifications & errata)

  • general_error(117) alert — define the new generic alert from §6.2 in the AlertDescription enum and AlertTypeToString. In TLS 1.3 it is processed as a fatal alert by the existing alert handling, matching the spec.
  • Cap sender key updates at 2^48-1 — §4.7.3 requires a sending implementation to never let its key-update count exceed 2^48-1, while a receiver MUST NOT enforce this on the peer. Stream TLS 1.3 previously tracked no count (only the DTLS 1.3 epoch was gated). A keyUpdateCount field is added at the end of the Keys TLS 1.3 block; SendTls13KeyUpdate refuses once the ceiling is reached (BAD_STATE_E) and increments after deriving each new sending key. The receive path is untouched.
  • Accept empty CertificateRequest extensions — §4.4.2 sets the lower bound on CertificateRequest.extensions to 0, so a zero-length block is valid. The client no longer rejects it early with INVALID_PARAMETER; the empty block is instead caught by the existing mandatory signature_algorithms check, which returns the correct missing_extension alert.

RFC 9850 (SSLKEYLOGFILE format)

  • Line-by-line sniffer key-log parsing — the reader used fscanf with three whitespace-delimited tokens, which is not line-aware, so a comment line was parsed as fields spanning the line boundary and desynchronized the rest of the file. It now reads one line at a time with fgets, skips empty and # comment lines (§1), and silently skips malformed lines that do not yield the three expected fields (§2) so secrets are still recoverable from a corrupted file. Field buffers are cleared each iteration, the fixed-length client-random field is length-validated, and the line buffer is zeroed on every return path.
  • Honor the SSLKEYLOGFILE environment variable — RFC 9850 notes the informal SSLKEYLOGFILE env var used by curl, NSS, Wireshark and various other applications to share one key-log path. Both tls13ShowSecrets and tlsShowSecrets can now prefer XGETENV("SSLKEYLOGFILE") (when set and non-empty), falling back to the compile-time WOLFSSL_SSLKEYLOGFILE_OUTPUT. This is opt-in behind a new WOLFSSL_SSLKEYLOGFILE_USE_ENV macro (default off), so a build that exports the variable for other applications is unaffected and no XGETENV reference is emitted unless requested. It remains under the existing SHOW_SECRETS / WOLFSSL_SSLKEYLOGFILE guards, so production builds cannot log secrets regardless of the environment, per the RFC security considerations.

RFC 9973 (certificates with an external PSK)

  • Documentation-only reference update — RFC 9973 is now published and obsoletes RFC 8773; the cert_with_extern_psk implementation was written against the 8773bis draft that became RFC 9973 and is already compliant. Only textual references (comments, configure help, extension-codepoint comment, a test comment, Doxygen) are updated. The WOLFSSL_CERT_WITH_EXTERN_PSK macro, the --enable-cert-with-extern-psk option, the public API, and extension codepoint 33 (0x0021) are all unchanged.

Testing

  • New unit test test_tls13_KeyUpdate_sender_limit (group tls13) exercises both sides of the 2^48-1 boundary: seeded at 2^48-2, wolfSSL_update_keys succeeds and advances the count to exactly 2^48-1; at the ceiling the next call is refused with BAD_STATE_E and the count is unchanged. This pins the increment side-effect and the >= vs > boundary.
  • Built with ./configure --enable-tls13 --enable-debug && make and ran ./tests/unit.test --group tls13 — the new test runs (not skipped) and passes; group result 0 failed / 50 skipped / 30 passed, exit 0.
  • The sniffer key-log change adds a comment line, a blank line, and a malformed (INCOMPLETE_KEYLOG_LINE) line to the TLS 1.3 key-log test data so the sniffer keylog test exercises the new skip paths.
  • Remaining changes are guarded and behavior-preserving for existing configs; full make check recommended in CI across the standard build matrix.

Frauschi added 5 commits July 17, 2026 08:37
RFC 9846 section 6.2 defines a new generic alert, general_error(117),
sent to indicate an error condition when no more specific alert is
available or the sender wishes to conceal the specific error code.

Define the alert in the AlertDescription enumeration and add its name to
AlertTypeToString so it is recognized and logged. In TLS 1.3 it is
handled as a fatal alert by the existing alert processing, which matches
the specification.
RFC 9846 Section 4.7.3 upgrades the key update bound so that sending
implementations MUST NOT allow the number of key updates to exceed
2^48-1, while receiving implementations MUST NOT enforce this on the
peer. The DTLS 1.3 path already gated its sending epoch, but stream
TLS 1.3 tracked no key update count.

Add a keyUpdateCount field at the end of the Keys structure TLS 1.3
block, refuse to send a KeyUpdate in SendTls13KeyUpdate once the sender
reaches the ceiling, and increment the count after deriving each new
sending key. The receive path is left unchanged so it never enforces a
limit on the peer.

Add a test that exercises both sides of the boundary: seeded one below
the ceiling, wolfSSL_update_keys succeeds and advances the sender count
to exactly 2^48-1; at the ceiling the next KeyUpdate is refused with
BAD_STATE_E and the count is left unchanged.
RFC 9846 Section 4.4.2 corrects the lower bound on
CertificateRequest.extensions to 0, so a zero length extensions block is
syntactically valid. The client rejected it early with INVALID_PARAMETER
before parsing.

Remove the early rejection so the extensions are parsed and an empty
block is instead caught by the existing mandatory signature_algorithms
check, which returns the correct missing_extension alert. TLSX_Parse
handles a zero length input by parsing no extensions.
RFC 9973 "TLS 1.3 Extension for Using Certificates with an External
Pre-Shared Key" is now published and obsoletes RFC 8773. The
cert_with_extern_psk implementation was written against the 8773bis
draft that became RFC 9973, so it is already compliant.

Update the textual references in comments, the configure help comment,
the extension codepoint comment, a test comment, and the Doxygen docs.
This is a documentation only change. The WOLFSSL_CERT_WITH_EXTERN_PSK
macro, the --enable-cert-with-extern-psk option, the public API names,
and the extension codepoint 33 (0x0021) are all unchanged, and no logic
is affected. Historical ChangeLog and README entries are left as they
shipped.
The keylog reader used fscanf with three whitespace delimited tokens,
which is not line aware. RFC 9850 Section 1 requires readers to ignore
empty lines and lines whose first character is '#', and Section 2
recommends ignoring lines that do not conform to the format so secrets
can still be recovered from corrupted files. A comment line such as
"# note" was parsed as three fields spanning the line boundary, which
desynchronized the rest of the file.

Read one line at a time with fgets, skip empty and comment lines, and
parse each line with sscanf, skipping any line that does not yield the
three expected fields instead of aborting. Clear the field buffers each
iteration so a short field cannot pick up stale bytes from a previous
line, validate that the fixed length client random field is the exact
expected length, and leave the secret length variable because it depends
on the negotiated hash. Zero the new line buffer on every return path,
matching the existing ForceZero handling of the secret buffers.

Add a comment line, a blank line, and a malformed line to the TLS 1.3
keylog test data so the sniffer keylog test exercises the skip paths.
@Frauschi Frauschi self-assigned this Jul 17, 2026
@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown

MemBrowse Memory Report

gcc-arm-cortex-m3

  • FLASH: +14 B (+0.0%, 122,519 B / 262,144 B, total: 47% used)

gcc-arm-cortex-m4

  • FLASH: +14 B (+0.0%, 200,668 B / 262,144 B, total: 77% used)

gcc-arm-cortex-m4-dtls13

  • FLASH: .rodata +12 B (+0.0%, 180,964 B / 1,048,576 B, total: 17% used)

gcc-arm-cortex-m4-openssl-compat

  • FLASH: .rodata +8 B (+0.0%, 771,564 B / 1,048,576 B, total: 74% used)

gcc-arm-cortex-m4-pq

  • FLASH: .rodata +16 B (+0.0%, 279,816 B / 1,048,576 B, total: 27% used)

gcc-arm-cortex-m4-rsa-only

  • FLASH: .rodata +8 B, .text +64 B (+0.0%, 325,472 B / 1,048,576 B, total: 31% used)

gcc-arm-cortex-m4-tls12

  • FLASH: +14 B (+0.0%, 123,291 B / 262,144 B, total: 47% used)

gcc-arm-cortex-m4-tls13

  • FLASH: +14 B (+0.0%, 236,494 B / 262,144 B, total: 90% used)

gcc-arm-cortex-m7

  • FLASH: +14 B (+0.0%, 200,604 B / 262,144 B, total: 77% used)

gcc-arm-cortex-m7-pq

  • FLASH: .rodata +16 B (+0.0%, 280,392 B / 1,048,576 B, total: 27% used)

gcc-arm-cortex-m7-tls13

@Frauschi

Frauschi commented Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

Jenkins retest this please.

RFC 9850 notes that implementations informally use an environment
variable named SSLKEYLOGFILE to select the key log path, which lets tools
such as curl, NSS and Wireshark share one file. The key log writers only
used the compile-time WOLFSSL_SSLKEYLOGFILE_OUTPUT path.

Add an opt-in WOLFSSL_SSLKEYLOGFILE_USE_ENV macro (default off) that makes
both tls13ShowSecrets and tlsShowSecrets prefer XGETENV("SSLKEYLOGFILE")
when it is set and non-empty, falling back to the compile-time path
otherwise. Keeping it opt-in leaves the default behavior unchanged, so a
build that exports the variable for other applications is unaffected, and
no XGETENV reference is emitted unless the macro is defined (avoiding a
dependency on environment access, for example under NO_STDIO_FILESYSTEM).
XGETENV resolves to NULL on targets without environment support, so it
falls back automatically. This stays behind the existing SHOW_SECRETS and
WOLFSSL_SSLKEYLOGFILE compile-time guards, so production builds that do
not define them cannot log secrets regardless of the environment, as
required by the RFC security considerations.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants