a positive test proves Ze does what the requirement demands and a negative one proves it refuses what the requirement forbids
RFC 5216 - The EAP-TLS Authentication Protocol
Every requirement this repository extracted from RFC 5216, the tests bound to it, and what a reader has verified about them. This summary is enrolled and gated by ./le rfc check.
Overview
Positive
what Ze has
the requirement admits no counter-case, so one polarity plus a recorded reason is the whole proof available for it
one direction is tested, the other is neither tested nor excused, and nothing states which
scoped tests exist; the remaining obligation is unmet or unproven, with zero whole-requirement credit
a red was observed once under a recorded procedure, and the unit, the claim and the producer it rested on still hash to what was recorded. The break is not re-run. A test pair is not a proof until one has been observed
Neutral
measures that are neither good news nor bad
MUST-level requirements the gate HOLDS. A population, not a result: the shares beside it are what says how Ze stands
an obligation that does not bind Ze. A {not-applicable} annotation says it never bound; a {feature-declined} annotation says its condition is an optional feature Ze does not offer, and quotes the RFC sentence that makes it optional. Scope, not coverage: it stays in the denominator every share on this page is taken over
a {not-applicable} annotation says the obligation does not bind Ze, so no test is owed for it. It stays in the denominator every share here is taken over
a {lower-layer} annotation says a layer under Ze performs the behavior, on state Ze installs into that layer, and names the producer that installs it. The obligation binds Ze and is met; Ze proves none of it, because its own boundary carries no value the behavior reads
a {feature-declined} annotation says the obligation is conditional on a feature the RFC makes optional and Ze does not offer, and it quotes the sentence that makes it optional. The condition is false, so nothing is owed and nothing is missing. It stays in the denominator every share here is taken over
Negative
what Ze owes
no test carries the requirement id, whether or not a gap states why
1 weak, wrong or unimplemented, 0 no longer current. Each is named below under its own requirement id
The 8 shares marked as a part above are the whole of the 47 gated MUSTs: they add to 100%. Proven by a recorded break is a share of TAGGED UNITS, a different population, so it is not one of them.
How to read the colors
A color names what the measure MEANS, not how well Ze scores on it. Green is a good outcome at any value, red is a bad one, and neither a population nor a scope count is an outcome, so both take no color. The number under the label is what says how far Ze has got.
| Card | Tone here | Why that color |
|---|---|---|
| Gated MUSTs | neutral | no color: a population is a scale, and a larger one is neither good news nor bad. It is the accounting total |
| Out of scope | neutral | no color: an obligation that never bound Ze is neither an achievement nor a failure, and counting it either way would be a claim |
| Tested both ways | ok | green at every value: a test pair is the outcome this gate exists to produce, and the share under the label is what says how far Ze has got |
| One polarity plus reason | ok | green at every value: where no counter-case exists, one polarity IS the complete answer, and a recorded reason is what the gate demands beside it |
| One polarity, unexcused | ok | green at zero, RED above it: half a proof with no reason for the other half |
| Partial proof; remaining gap | ok | green at zero, RED above it: a tested clause cannot prove the whole requirement |
| No test at all | bad | green at zero, RED above it: a binding obligation nothing exercises is a claim with nothing behind it, whether or not a reason is stated |
| Not applicable | neutral | no color: an obligation that never bound Ze is neither an achievement nor a failure, and counting it either way would be a claim |
| Met below Ze | neutral | no color: an obligation met below Ze is neither a test Ze wrote nor work Ze owes, and the two green shares above are what says how much Ze proves itself |
| Optional feature declined | neutral | no color: an obligation whose condition Ze never meets is neither an achievement nor a failure. The absent FEATURE is disclosed on the RFC's own status row, as an implementation gap a later scope decision can revisit |
| Proven by a recorded break | ok | green at every value: an observed break is the outcome the discrimination gate exists to produce. The denominator is TAGGED UNITS, not obligations, so this share is not one of the parts above |
| Audit verdicts | bad | RED on the first weak, wrong or unimplemented verdict, amber while a verdict is no longer current or a gated MUST is unjudged, green when every one is judged sound and current |
At a glance
| Field | Value |
|---|---|
| Public status | Partial |
| Enrolment | Enrolled |
| Requirements | 54 |
| Gated MUST-level | 47 |
| Not applicable, so out of scope | 0 |
| Declared gaps | 9 |
| Declared gaps a test demonstrates | 0 |
| Gated with no test | 0 |
| Nightly-only evidence | 0 |
| Test tags | 101 |
| Tagged units | 101 |
| Recorded audit verdicts | 40 |
| Discrimination records | 87 |
| Summary | rfc/short/rfc5216.md |
| Requirement shard | rfc/requirements/rfc5216.md |
| RFC text | rfc/full/rfc5216.txt |
Enrolment
Enrolled: EAP-TLS (RFC 5216, EAP inside IKEv2): 52 rows, 47 of them MUST-level. 11 are MET in both polarities (fragmentation, L-bit, mutual-auth handshake, and all seven Section 2.1.3 termination MUSTs: EAP-Failure answers a peer TLS alert, the authenticator waits for the peer reply after its own alert, that reply is answered with EAP-Failure, the authenticator sends its change_cipher_spec and Finished closing flight before it concludes, the peer replies to the authenticator before it terminates, the peer answers the closing flight with a no-data EAP-Response, and the authenticator answers that with EAP-Success), and 5 carry a single-polarity positive proof (reserved flags, TLS>=1.0 both sides, no cipher-leak, MSK label); no compression (RFC5216-2.4-3) is proven in both polarities since 2026-09-29. RFC5216-2.4-5, the mandatory TLS_RSA_WITH_3DES_EDE_CBC_SHA ciphersuite, is a gap. The 2026-09-21 extraction walk read the document against this checklist and added 27 MUST-level rows it did not carry: the Section 2.1.1 base-case flight and the Section 2.1.2 resumption flight, the six Section 2.1.5 fragment-ACK and Identifier obligations, TLS v1.0 support (2.4-7), the four Section 3 packet-field obligations, the five Section 5.2 certificate naming obligations, and the two Section 5.3 authorization obligations. None of the 27 carries a tagged test, so each is an open gap. The same walk corrected RFC5216-5.3-1 from MUST to SHOULD, which is the level Section 5.3 states for RFC 3280 path validation, and moved the section reference of RFC5216-2.4-1 and RFC5216-2.4-2 to Section 2.1.1, where the two TLS version sentences are written. Handshake harness enabled by the eap deadlock+cert-validation fix (commit 0816c2b74)
What the public ledger says
Status: Partial
What the ledger says is covered
- Full EAP-TLS handshake as both authenticator (RequireAndVerifyClientCert + ClientCAs) and peer (RootCAs), L/M/S fragmentation with a 64KB reassembly bound and empty-message fragment ACK, and MSK export via the label "client EAP encryption" feeding the IKEv2 AUTH payload
- certs from the PKI store. Section 2.1.3 termination in both directions: a rejected peer receives the fatal TLS alert in an EAP-Request, the authenticator waits for its EAP-Response and only then sends EAP-Failure, and a peer TLS alert that rejects the authenticator is answered with EAP-Failure in the round that carries it. A peer that rejects the authenticator replies first and reports the cause on the round after. Section 2.1.3 termination on the success path: the authenticator sends its change_cipher_spec and Finished closing flight, the peer answers with a no-data EAP-Response, and the authenticator answers that with EAP-Success. One reachability limit is not a conformance gap and is stated here so no reader assumes otherwise: the Section 2.3 derivation is a crypto/tls ExportKeyingMaterial call, and Go refuses that export on a TLS 1.2 session that did not negotiate the RFC 7627 extended master secret, so a peer such as strongSwan 5.9.14 cannot be authenticated over TLS 1.2 at all. That peer lands on TLS 1.2 by default rather than by limitation, and
charon.tls.version_max = 1.3moves the same build onto the RFC 9190 path that scenario eap-tls13 proves. Ze implements the derivation correctly and reports the refusal with the peer, the negotiated version, RFC 7627 and the operator's answers (eapTLS12ExportRefused,internal/core/eap/eap_tls.go) - Go 1.27 removed the GODEBUG setting that once lifted the refusal and offers no replacement, and RFC 9190 covers the TLS 1.3 path, which needs none of it.
What the ledger says remains
RFC5216-2.4-5, the mandatory TLS_RSA_WITH_3DES_EDE_CBC_SHA ciphersuite, is not offered (TLS 1.2+ Go defaults exclude insecure 3DES). The 15 rows the 2026-09-21 extraction walk added were classified the same day under the owner ruling that every MUST is a requirement. Five TLS handshake obligations crypto/tls performs on the tls.Config Ze installs (the resumption ciphersuite, the server_certificate flight, the peer's full and resumed flights, and the server's resumed flight) are proven at that boundary in rfc5216_tls_config_test.go; the server_key_exchange (RFC5216-2.1.1-7) is decided by the crypto/tls default suites and carries a lower-layer annotation. RFC5216-2.1.1-4 is audited wrong: the server does not choose a sessionId for a new session, because crypto/tls, as newTLSMethod configures it, sends an empty ServerHello session_id on a TLS 1.2 full handshake, and its tags prove the session-ticket rule instead. Eight are gaps.
Seven are scheduled: TLS 1.0 support (RFC5216-2.4-7, which RFC 8996 forbids, plan/spec-eap-tls-tls10.md, the owner decides which document governs); the Peer-Id and Server-Id derivation and authorization (RFC5216-5.2-1, RFC5216-5.3-2, RFC5216-5.3-3, plan/immediate/spec-eap-tls-identity-authorization.md); and the certificate naming profile on issuers and verifiers (RFC5216-5.2-2, RFC5216-5.2-3, RFC5216-5.2-4, plan/spec-eap-tls-certificate-profile.md). RFC5216-5.2-5 is structural in crypto/x509 and carries a lower-layer annotation. The 12 rows the same walk added that are now proven in both polarities are the Section 2.1.1 Start and first Response, the six Section 2.1.5 fragment-ACK and Identifier obligations, and the four Section 3 packet-field obligations (rfc5216_flight_rules_test.go and rfc5216_fragment_ack_test.go, internal/core/eap). RFC5216-5.4-2 is no longer among them: the peer re-checks the revocation status of the authenticator certificate once the CHILD_SA gives it a network, over https, and closes the SA when the responder reports it revoked (startServerCertRecheck, internal/component/ike/engine/postauth_revocation.go, reached from runEstablished). It runs whichever TLS version the exchange negotiated, which is what this RFC asks for, and the chain it re-reads is the one the peer accepted (eap.PeerSession.ServerChains). RFC5216-5.4-1 is no longer among them either: checkChainRevocation (internal/core/eap/revocation.go) checks every certificate on each verified chain except the trust anchor against the crl leaf-list its CA publishes, on the authenticator through tls.Config.VerifyConnection and on the peer through serverChainCheck.verifyConnection, and it is proven in both polarities on TLS 1.2, which is the version this RFC governs. The eighth is RFC5216-5.3-5: Section 5.3 asks the server to retrieve the intermediate certificates a path needs (RFC 3280 Section 4.2.2.1, Authority Information Access), and Ze performs no such fetch. What Ze implements is the fallback the same section names for a server unable to retrieve them: the authenticator completes the client chain from the intermediate-certificate list the operator configures (PKI ca entries, never trust anchors) beside the intermediates the peer sends (clientChainVerifier, internal/core/eap/client_chain.go).
Coverage
| Bucket | Count | What it counts |
|---|---|---|
| Positive and negative tests | 33 | one part of the gated population |
| Annotated (including scoped evidence) | 14 | one part of the gated population |
| One polarity only | 0 | one part of the gated population |
| No test and no annotation | 0 | one part of the gated population |
| Partial proof; remaining gap (subset of annotated; zero whole-requirement credit) | 0 | an overlay: each of these is also counted by the part it falls in |
| Evidence that runs nightly only | 0 | an overlay: each of these is also counted by the part it falls in |
| Derived from other rows | 0 | outside the gated population: each asserts nothing and derives its state from the rows it names, which the parts above already count |
| Gated MUST-level requirements | 47 | every gated MUST falls in exactly one bucket above |
Positive and negative tests (33): RFC5216-3-1, RFC5216-2.4-3, RFC5216-2.4-4, RFC5216-2.1.1-1, RFC5216-2.1.1-2, RFC5216-2.1.5-1, RFC5216-5.4-1, RFC5216-5.4-2, RFC5216-2.1.3-1, RFC5216-2.1.3-3, RFC5216-2.1.3-4, RFC5216-2.1.3-5, RFC5216-2.1.3-6, RFC5216-2.1.3-7, RFC5216-2.1.3-8, RFC5216-2.1.1-3, RFC5216-2.1.1-4, RFC5216-2.1.1-5, RFC5216-2.1.1-6, RFC5216-2.1.1-8, RFC5216-2.1.1-9, RFC5216-2.1.1-10, RFC5216-2.1.2-1, RFC5216-2.1.5-2, RFC5216-2.1.5-3, RFC5216-2.1.5-4, RFC5216-2.1.5-5, RFC5216-2.1.5-6, RFC5216-2.1.5-7, RFC5216-3-3, RFC5216-3-4, RFC5216-3-5, RFC5216-3-6
Annotated (including scoped evidence) (14): RFC5216-3-2, RFC5216-2.4-1, RFC5216-2.4-2, RFC5216-2.4-5, RFC5216-2.3-1, RFC5216-2.1.1-7, RFC5216-2.4-7, RFC5216-5.2-1, RFC5216-5.2-2, RFC5216-5.2-3, RFC5216-5.2-4, RFC5216-5.2-5, RFC5216-5.3-2, RFC5216-5.3-3
Requirements
| Requirement | Level | Section | Tests |
|---|---|---|---|
RFC5216-3-1 | The L bit (length included) is set to indicate the presence of the four-octet TLS Message Length field, and MUST be set for the first fragment of a fragmented TLS message or set of messages. (§3, Flags) | ||
| MUST | 3 | ||
RFC5216-3-2 | Implementations of this specification MUST set the reserved bits to zero (§3, Flags) | ||
| MUST | 3 | negative
no testno negative test{single-polarity}: the flags byte is assembled only from L/M in nextFragment (and S alone in Start), so no code path can set reserved bits 3-7 and only the positive assertion is reachable (internal/core/eap/eap_tls.go:106, :190) |
|
RFC5216-2.4-1 | The version offered by the peer MUST correspond to TLS v1.0 or later. (§2.1.1) | ||
| MUST | 2.1.1 | negative
no testno negative test{single-polarity}: the peer's tls.Config forces MinVersion TLS 1.2, so its ClientHello always offers at least TLS 1.0 and no path can offer lower (internal/core/eap/peer.go:334) |
|
RFC5216-2.4-2 | The version offered by the server MUST correspond to TLS v1.0 or later. (§2.1.1) | ||
| MUST | 2.1.1 | negative
no testno negative test{single-polarity}: the server's tls.Config forces MinVersion TLS 1.2, so any ServerHello it emits is at least TLS 1.0 and it never negotiates lower (internal/core/eap/eap_tls.go:167) |
|
RFC5216-2.4-3 | However, during the EAP-TLS conversation the EAP peer and server MUST NOT request or negotiate compression. (§2.4) | ||
| MUST NOT | 2.4 | ||
RFC5216-2.4-4 | Since the ciphersuite negotiated within EAP-TLS applies only to the EAP conversation, TLS ciphersuite negotiation MUST NOT be used to negotiate the ciphersuites used to secure data. (§2.4) | ||
| MUST NOT | 2.4 | ||
RFC5216-2.4-5 | To ensure interoperability, EAP-TLS peers and servers MUST support the TLS [RFC4346] mandatory-to-implement ciphersuite: TLS_RSA_WITH_3DES_EDE_CBC_SHA (§2.4) | ||
| MUST | 2.4 | positive
no testno positive testnegative
no testno negative test{gap}: the server sets no CipherSuites and requires TLS 1.2+, so it inherits Go's secure defaults which classify 3DES as insecure and do not enable TLS_RSA_WITH_3DES_EDE_CBC_SHA, leaving the RFC's mandatory-to-implement ciphersuite unavailable (internal/core/eap/eap_tls.go:163, peer.go:330) |
|
RFC5216-2.1.1-1 | If the EAP server sent a certificate_request message in the preceding EAP-Request packet, then unless the peer is configured for privacy (see Section 2.1.4) the peer MUST send, in addition, certificate and certificate_verify messages. (§2.1.1) | ||
| MUST | 2.1.1 | ||
RFC5216-2.1.1-2 | The EAP-TLS conversation will then begin, with the peer sending an EAP-Response packet with EAP-Type=EAP-TLS. The data field of that packet will encapsulate one or more TLS records in TLS record layer format, containing a TLS client_hello handshake message. (§2.1.1) | ||
| MUST | 2.1.1 | ||
RFC5216-2.1.5-1 | As a result, an EAP-TLS implementation MUST provide its own support for fragmentation and reassembly. (§2.1.5) | ||
| MUST | 2.1.5 | ||
RFC5216-5.3-1 | Since the EAP-TLS server is typically connected to the Internet, it SHOULD support validating the peer certificate using RFC 3280 [RFC3280] compliant path validation (§5.3) | ||
| SHOULD | 5.3 | ||
RFC5216-5.3-4 | Therefore, the EAP-TLS server SHOULD provide its entire certificate chain minus the root to facilitate certificate validation by the peer. The EAP-TLS peer SHOULD support validating the server certificate using RFC 3280 [RFC3280] compliant path validation. (§5.3) | ||
| SHOULD | 5.3 | ||
RFC5216-5.3-5 | including the ability to retrieve intermediate certificates that may be necessary to validate the peer certificate. (§5.3) | ||
| SHOULD | 5.3 | positive
no testno positive testnegative
no testno negative test{gap}: the authenticator performs no Authority Information Access caIssuers retrieval (RFC 3280 Section 4.2.2.1), so it does not retrieve an intermediate the peer left out; it implements only the fallback the next paragraph names for a server unable to retrieve, pre-configuration through the intermediate-certificate list (clientChainVerifier, internal/core/eap/client_chain.go); the four intermediate-pool tests that claimed this row prove only that fallback, and their tags were taken off after the independent audit of 2026-10-18 found the claim wider than the assertions |
|
RFC5216-5.4-1 | EAP-TLS peer and server implementations MUST support the use of Certificate Revocation Lists (CRLs); for details, see Section 3.3 of [RFC3280]. (§5.4) | ||
| MUST | 5.4 | ||
RFC5216-5.4-2 | peer implementations MUST also support checking for certificate revocation after authentication completes and network connectivity is available (§5.4) | ||
| MUST | 5.4 | ||
RFC5216-2.1.3-1 | The EAP Server MUST reply with an EAP-Failure packet since server authentication failure is a terminal condition. (§2.1.3) | ||
| MUST | 2.1.3 | ||
RFC5216-2.1.3-3 | To ensure that the peer receives the TLS alert message, the EAP server MUST wait for the peer to reply with an EAP-Response packet. (§2.1.3) | ||
| MUST | 2.1.3 | ||
RFC5216-2.1.3-4 | it MAY contain an EAP-Response packet with EAP-Type=EAP-TLS and no data, in which case the EAP-Server MUST send an EAP-Failure packet and terminate the conversation. (§2.1.3) | ||
| MUST | 2.1.3 | ||
RFC5216-2.1.3-5 | If the peer authenticates successfully, the EAP server MUST respond with an EAP-Request packet with EAP-Type=EAP-TLS, which includes, in the case of a new TLS session, one or more TLS records containing TLS change_cipher_spec and finished handshake messages. (§2.1.3) | ||
| MUST | 2.1.3 | ||
RFC5216-2.1.3-6 | To ensure that the EAP Server receives the TLS alert message, the peer MUST wait for the EAP Server to reply before terminating the conversation. (§2.1.3) | ||
| MUST | 2.1.3 | ||
RFC5216-2.1.3-7 | If the EAP server authenticates successfully, the peer MUST send an EAP-Response packet of EAP-Type=EAP-TLS, and no data. (§2.1.3) | ||
| MUST | 2.1.3 | ||
RFC5216-2.1.3-8 | The EAP Server then MUST respond with an EAP-Success message. (§2.1.3) | ||
| MUST | 2.1.3 | ||
RFC5216-2.3-1 | Key_Material = TLS-PRF-128(master_secret, "client EAP encryption", client.random || server.random) MSK = Key_Material(0,63) EMSK = Key_Material(64,127) (§2.3) | ||
| MUST | 2.3 | negative
no testno negative test{single-polarity}: both server and peer export the 64-octet MSK via ExportKeyingMaterial with the exact RFC label 'client EAP encryption', the only key the IKEv2 lower layer consumes (internal/core/eap/eap_tls.go:268, peer.go:381) |
|
RFC5216-2.1.1-3 | Once having received the peer's Identity, the EAP server MUST respond with an EAP-TLS/Start packet, which is an EAP-Request packet with EAP-Type=EAP-TLS, the Start (S) bit set, and no data. (§2.1.1) | ||
| MUST | 2.1.1 | ||
RFC5216-2.1.1-4 | If the peer's sessionId is null or unrecognized by the server, the server MUST choose the sessionId to establish a new session. (§2.1.1) | ||
| MUST | 2.1.1 | ||
RFC5216-2.1.1-5 | If the session matches the peer's, then the ciphersuite MUST match the one negotiated during the handshake protocol execution that established the session. (§2.1.1) | ||
| MUST | 2.1.1 | ||
RFC5216-2.1.1-6 | If the EAP server is not resuming a previously established session, then it MUST include a TLS server_certificate handshake message, and a server_hello_done handshake message MUST be the last handshake message encapsulated in this EAP-Request packet. (§2.1.1) | ||
| MUST | 2.1.1 | ||
RFC5216-2.1.1-7 | The certificate message contains a public key certificate chain for either a key exchange public key (such as an RSA or Diffie-Hellman key exchange public key) or a signature public key (such as an RSA or Digital Signature Standard (DSS) signature public key). In the latter case, a TLS server_key_exchange handshake message MUST also be included to allow the key exchange to take place. (§2.1.1) | ||
| MUST | 2.1.1 | positive
no testno positive testnegative
no testno negative test{lower-layer}: Go crypto/tls; internal/core/eap/eap_tls.go::newTLSMethod sets no CipherSuites, so the default ECDHE suites of crypto/tls decide that a server_key_exchange is written and no value Ze writes chooses it |
|
RFC5216-2.1.1-8 | If the peer supports EAP-TLS and is configured to use it, it MUST respond to the EAP-Request with an EAP-Response packet of EAP- Type=EAP-TLS. (§2.1.1) | ||
| MUST | 2.1.1 | ||
RFC5216-2.1.1-9 | If the preceding server_hello message sent by the EAP server in the preceding EAP-Request packet did not indicate the resumption of a previous session, the data field of this packet MUST encapsulate one or more TLS records containing a TLS client_key_exchange, change_cipher_spec, and finished messages. (§2.1.1) | ||
| MUST | 2.1.1 | ||
RFC5216-2.1.1-10 | If the preceding server_hello message sent by the EAP server in the preceding EAP-Request packet indicated the resumption of a previous session, then the peer MUST send only the change_cipher_spec and finished handshake messages. (§2.1.1) | ||
| MUST | 2.1.1 | ||
RFC5216-2.1.2-1 | If the EAP server is resuming a previously established session, then it MUST include only a TLS change_cipher_spec message and a TLS finished handshake message after the server_hello message. (§2.1.2) | ||
| MUST | 2.1.2 | ||
RFC5216-2.1.5-2 | When an EAP-TLS peer receives an EAP-Request packet with the M bit set, it MUST respond with an EAP-Response with EAP-Type=EAP-TLS and no data. (§2.1.5) | ||
| MUST | 2.1.5 | ||
RFC5216-2.1.5-3 | The EAP server MUST wait until it receives the EAP-Response before sending another fragment. (§2.1.5) | ||
| MUST | 2.1.5 | ||
RFC5216-2.1.5-4 | In order to prevent errors in processing of fragments, the EAP server MUST increment the Identifier field for each fragment contained within an EAP-Request, and the peer MUST include this Identifier value in the fragment ACK contained within the EAP-Response. (§2.1.5) | ||
| MUST | 2.1.5 | ||
RFC5216-2.1.5-5 | Similarly, when the EAP server receives an EAP-Response with the M bit set, it MUST respond with an EAP-Request with EAP-Type=EAP-TLS and no data. (§2.1.5) | ||
| MUST | 2.1.5 | ||
RFC5216-2.1.5-6 | The EAP peer MUST wait until it receives the EAP-Request before sending another fragment. (§2.1.5) | ||
| MUST | 2.1.5 | ||
RFC5216-2.1.5-7 | In order to prevent errors in the processing of fragments, the EAP server MUST increment the Identifier value for each fragment ACK contained within an EAP-Request, and the peer MUST include this Identifier value in the subsequent fragment contained within an EAP- Response. (§2.1.5) | ||
| MUST | 2.1.5 | ||
RFC5216-2.4-7 | EAP-TLS implementations MUST support TLS v1.0. (§2.4) | ||
| MUST | 2.4 | positive
no testno positive testnegative
no testno negative test{gap}: both roles set MinVersion TLS 1.2 and RFC 8996 forbids TLS 1.0, so the owner decides which document governs; plan/spec-eap-tls-tls10.md |
|
RFC5216-3-3 | The Identifier field MUST be changed on each Request packet. (§3) | ||
| MUST | 3 | ||
RFC5216-3-4 | Octets outside the range of the Length field should be treated as Data Link Layer padding and MUST be ignored on reception. (§3) | ||
| MUST | 3 | ||
RFC5216-3-5 | Implementations of this specification MUST set the reserved bits to zero, and MUST ignore them on reception. (§3) | ||
| MUST | 3 | ||
RFC5216-3-6 | The Identifier field is one octet and MUST match the Identifier field from the corresponding request. (§3.2) | ||
| MUST | 3.2 | ||
RFC5216-5.2-1 | Where the subjectAltName field is present in the peer or server certificate, the Peer-Id or Server-Id MUST be set to the contents of the subjectAltName. (§5.2) | ||
| MUST | 5.2 | positive
no testno positive testnegative
no testno negative test{gap}: eapTLSPeerName reads the subject and never the subjectAltName, and no Server-Id is exported; plan/immediate/spec-eap-tls-identity-authorization.md |
|
RFC5216-5.2-2 | If subject naming information is present only in the subjectAltName extension of a peer or server certificate, then the subject field MUST be an empty sequence and the subjectAltName extension MUST be critical. (§5.2) | ||
| MUST | 5.2 | positive
no testno positive testnegative
no testno negative test{gap}: no verifier refuses an empty-subject leaf whose subjectAltName is not critical; plan/spec-eap-tls-certificate-profile.md |
|
RFC5216-5.2-3 | Conforming implementations generating new certificates with Network Access Identifiers (NAIs) MUST use the rfc822Name in the subject alternative name field to describe such identities. (§5.2) | ||
| MUST | 5.2 | positive
no testno positive testnegative
no testno negative test{gap}: no Ze issuer takes an NAI to write as an rfc822Name; plan/spec-eap-tls-certificate-profile.md |
|
RFC5216-5.2-4 | The use of the subject name field to contain an emailAddress Relative Distinguished Name (RDN) is deprecated, and MUST NOT be used. (§5.2) | ||
| MUST NOT | 5.2 | positive
no testno positive testnegative
no testno negative test{gap}: no verifier refuses a subject carrying an emailAddress RDN; plan/spec-eap-tls-certificate-profile.md |
|
RFC5216-5.2-5 | Where it is non-empty, the subject name field MUST contain an X.500 distinguished name (DN). (§5.2) | ||
| MUST | 5.2 | positive
no testno positive testnegative
no testno negative test{lower-layer}: Go crypto/x509; internal/component/pki/ca.go::LoadOrGenerateRoot issues every subject as a pkix.Name and x509.ParseCertificate accepts no other encoding, so no value Ze writes can make a non-empty subject anything but a distinguished name |
|
RFC5216-5.3-2 | Once a TLS session is established, EAP-TLS peer and server implementations MUST validate that the identities represented in the certificate are appropriate and authorized for use with EAP-TLS. (§5.3) | ||
| MUST | 5.3 | positive
no testno positive testnegative
no testno negative test{gap}: neither role authorizes the Peer-Id or Server-Id against configuration; plan/immediate/spec-eap-tls-identity-authorization.md |
|
RFC5216-5.3-3 | When performing this comparison, implementations MUST follow the validation rules specified in Section 3.1 of [RFC2818]. (§5.3) | ||
| MUST | 5.3 | positive
no testno positive testnegative
no testno negative test{gap}: the peer sets no expected server identity, so no RFC 2818 Section 3.1 comparison runs; plan/immediate/spec-eap-tls-identity-authorization.md |
|
RFC5216-5.4-3 | EAP-TLS peer and server implementations SHOULD also support the Online Certificate Status Protocol (OCSP) (§5.4) | ||
| SHOULD | 5.4 | positive
no testno positive testnegative
no testno negative test |
|
RFC5216-5.4-4 | EAP-TLS peers and servers SHOULD implement Certificate Status Request messages (§5.4) | ||
| SHOULD | 5.4 | positive
no testno positive testnegative
no testno negative test |
|
RFC5216-2.4-6 | EAP-TLS peers and servers SHOULD also support and be able to negotiate the following TLS ciphersuites: TLS_RSA_WITH_RC4_128_SHA [RFC4346] TLS_RSA_WITH_AES_128_CBC_SHA [RFC3268] (§2.4) | ||
| SHOULD | 2.4 | positive
no testno positive testnegative
no testno negative test |
|
RFC5216-2.1.3-2 | in which case the EAP server MAY allow the EAP-TLS conversation to be restarted, or it MAY contain an EAP-Response packet with EAP-Type=EAP-TLS and no data, in which case the EAP-Server MUST send an EAP-Failure packet and terminate the conversation. It is up to the EAP server whether to allow restarts, and if so, how many times the conversation can be restarted. An EAP Server implementing restart capability SHOULD impose a per-peer limit on the number of restarts, so as to protect against denial-of-service attacks. (§2.1.3) | ||
| MAY | 2.1.3 | positive
no testno positive testnegative
no testno negative test |
|
Gaps and untested MUSTs
| Requirement | State | Reason |
|---|---|---|
RFC5216-2.4-5 To ensure interoperability, EAP-TLS peers and servers MUST support the TLS [RFC4346] mandatory-to-implement ciphersuite: TLS_RSA_WITH_3DES_EDE_CBC_SHA (§2.4) |
{gap}, no test | the server sets no CipherSuites and requires TLS 1.2+, so it inherits Go's secure defaults which classify 3DES as insecure and do not enable TLS_RSA_WITH_3DES_EDE_CBC_SHA, leaving the RFC's mandatory-to-implement ciphersuite unavailable (internal/core/eap/eap_tls.go:163, peer.go:330) |
RFC5216-5.3-5 including the ability to retrieve intermediate certificates that may be necessary to validate the peer certificate. (§5.3) |
{gap} | the authenticator performs no Authority Information Access caIssuers retrieval (RFC 3280 Section 4.2.2.1), so it does not retrieve an intermediate the peer left out; it implements only the fallback the next paragraph names for a server unable to retrieve, pre-configuration through the intermediate-certificate list (clientChainVerifier, internal/core/eap/client_chain.go); the four intermediate-pool tests that claimed this row prove only that fallback, and their tags were taken off after the independent audit of 2026-10-18 found the claim wider than the assertions |
RFC5216-2.1.1-7 The certificate message contains a public key certificate chain for either a key exchange public key (such as an RSA or Diffie-Hellman key exchange public key) or a signature public key (such as an RSA or Digital Signature Standard (DSS) signature public key). In the latter case, a TLS server_key_exchange handshake message MUST also be included to allow the key exchange to take place. (§2.1.1) |
no test | no test carries this requirement id; annotated {lower-layer}: Go crypto/tls; internal/core/eap/eap_tls.go::newTLSMethod sets no CipherSuites, so the default ECDHE suites of crypto/tls decide that a server_key_exchange is written and no value Ze writes chooses it |
RFC5216-2.4-7 EAP-TLS implementations MUST support TLS v1.0. (§2.4) |
{gap}, no test | both roles set MinVersion TLS 1.2 and RFC 8996 forbids TLS 1.0, so the owner decides which document governs; plan/spec-eap-tls-tls10.md |
RFC5216-5.2-1 Where the subjectAltName field is present in the peer or server certificate, the Peer-Id or Server-Id MUST be set to the contents of the subjectAltName. (§5.2) |
{gap}, no test | eapTLSPeerName reads the subject and never the subjectAltName, and no Server-Id is exported; plan/immediate/spec-eap-tls-identity-authorization.md |
RFC5216-5.2-2 If subject naming information is present only in the subjectAltName extension of a peer or server certificate, then the subject field MUST be an empty sequence and the subjectAltName extension MUST be critical. (§5.2) |
{gap}, no test | no verifier refuses an empty-subject leaf whose subjectAltName is not critical; plan/spec-eap-tls-certificate-profile.md |
RFC5216-5.2-3 Conforming implementations generating new certificates with Network Access Identifiers (NAIs) MUST use the rfc822Name in the subject alternative name field to describe such identities. (§5.2) |
{gap}, no test | no Ze issuer takes an NAI to write as an rfc822Name; plan/spec-eap-tls-certificate-profile.md |
RFC5216-5.2-4 The use of the subject name field to contain an emailAddress Relative Distinguished Name (RDN) is deprecated, and MUST NOT be used. (§5.2) |
{gap}, no test | no verifier refuses a subject carrying an emailAddress RDN; plan/spec-eap-tls-certificate-profile.md |
RFC5216-5.2-5 Where it is non-empty, the subject name field MUST contain an X.500 distinguished name (DN). (§5.2) |
no test | no test carries this requirement id; annotated {lower-layer}: Go crypto/x509; internal/component/pki/ca.go::LoadOrGenerateRoot issues every subject as a pkix.Name and x509.ParseCertificate accepts no other encoding, so no value Ze writes can make a non-empty subject anything but a distinguished name |
RFC5216-5.3-2 Once a TLS session is established, EAP-TLS peer and server implementations MUST validate that the identities represented in the certificate are appropriate and authorized for use with EAP-TLS. (§5.3) |
{gap}, no test | neither role authorizes the Peer-Id or Server-Id against configuration; plan/immediate/spec-eap-tls-identity-authorization.md |
RFC5216-5.3-3 When performing this comparison, implementations MUST follow the validation rules specified in Section 3.1 of [RFC2818]. (§5.3) |
{gap}, no test | the peer sets no expected server identity, so no RFC 2818 Section 3.1 comparison runs; plan/immediate/spec-eap-tls-identity-authorization.md |
Proof state
A tagged unit reads unproven where no discrimination record exists for it: nothing in this tree has been observed to break it, so the claim its tag makes is unproven.
RFC5216-3-1
The L bit (length included) is set to indicate the presence of the four-octet TLS Message Length field, and MUST be set for the first fragment of a fragmented TLS message or set of messages. (§3, Flags)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Re-judged 2026-10-01 (owner ruling 5, ruling 2 reading). Positive TestTLSFragmenterFirstFragmentHasLength: Ze's first fragment of a multi-fragment message carries L, M and the exact 4-octet TLS Message Length (2000). Negative TestRFC5216FirstFragmentWithoutLengthIsRefused (new, D-8 fix in reassemble): the peer's handleTLSRequest refuses a first fragment with M set and L clear, isolated from a clean state, with an error naming the L bit, no fragment ACK and 0 octets buffered; the same payload with L+M is ACKed as control. Producer checked: the guard fires only when no message is in progress (inExpected 0, inBuf empty), so middle fragments of an L-opened train and unfragmented messages without L pass; shared by authenticator Process and peer. TestTLSFragmenterMiddleAndLastFragmentsHaveNoLength stays tagged negative as supplementary: its prose now states L-only-on-first is Ze's choice, not an RFC ban. All three units hold observed-red revert records.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestTLSFragmenterMiddleAndLastFragmentsHaveNoLength |
unit/verify | revert, verified |
| negative | TestRFC5216FirstFragmentWithoutLengthIsRefused |
unit/verify | revert, verified |
| positive | TestTLSFragmenterFirstFragmentHasLength |
unit/verify | revert, verified |
RFC5216-3-2
Implementations of this specification MUST set the reserved bits to zero (§3, Flags)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Single-polarity positive. Forbidden: an emitted flags octet with any of bits 3-7 set. TestTLSFragmentReservedFlagBitsAreZero fatals if the S constant or any flags octet nextFragment produces for first (L+M), middle (M), last and single-fragment (L) sends intersects 0x1F. The only other flags octet sent is the literal 0x00 ACK.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| positive | TestTLSFragmentReservedFlagBitsAreZero |
unit/verify | unproven |
RFC5216-2.4-1
The version offered by the peer MUST correspond to TLS v1.0 or later. (§2.1.1)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Unchanged after comment-only edits to TestEAPTLSMutualAuthHandshakeSucceeds. Single-polarity positive (row marker): TestRFC5216BothRolesInstallAVersionFloor reads the peer tls.Config tlsClientConfig builds, MinVersion TLS 1.2; the handshake unit asserts the negotiated version >= TLS 1.0. Recorded.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| positive | TestRFC5216BothRolesInstallAVersionFloor |
unit/verify | revert, verified |
| positive | TestEAPTLSMutualAuthHandshakeSucceeds |
unit/verify | revert, verified |
RFC5216-2.4-2
The version offered by the server MUST correspond to TLS v1.0 or later. (§2.1.1)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Unchanged after comment-only edits to TestEAPTLSMutualAuthHandshakeSucceeds. Single-polarity positive (row marker): the tls.Config newTLSMethod installs carries MinVersion TLS 1.2; the handshake unit asserts the authenticator's negotiated version >= TLS 1.0. Recorded.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| positive | TestRFC5216BothRolesInstallAVersionFloor |
unit/verify | revert, verified |
| positive | TestEAPTLSMutualAuthHandshakeSucceeds |
unit/verify | revert, verified |
RFC5216-2.4-3
However, during the EAP-TLS conversation the EAP peer and server MUST NOT request or negotiate compression. (§2.4)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Re-judged 2026-10-12 (independent auditor) after 37dbe54095, where the authenticator with resumption on installs redeemTLS13Only (resumption.go), which refuses every TLS 1.2 ticket. This unit is byte-identical; the file around it changed. Its helpers (tls12Exchange, fullTLS12Flights) gained a tune argument passed as nil to driveTunedEAPTLSFlight, which driveEAPTLSFlight itself calls with nil, and fresh stores offer no ticket, so the producer change never runs here. Verdict stands. The weak HandshakeComplete tag on TestEAPTLSMutualAuthHandshakeSucceeds is gone. What remains reads the wire in both polarities, both recorded: the peer's ClientHello offers compression_methods [0] and the ServerHello selects 0 (TestRFC5216NeitherSideNegotiatesCompression); an injected ClientHello offering [1, 0] is answered with null compression (TestRFC5216ServerRefusesTheCompressionAPeerOffers).
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC5216ServerRefusesTheCompressionAPeerOffers |
unit/verify | revert, verified |
| positive | TestRFC5216NeitherSideNegotiatesCompression |
unit/verify | revert, verified |
RFC5216-2.4-4
Since the ciphersuite negotiated within EAP-TLS applies only to the EAP conversation, TLS ciphersuite negotiation MUST NOT be used to negotiate the ciphersuites used to secure data. (§2.4)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Rejudged 2026-09-30 (ike-eap judge, R6). The {single-polarity} marker is removed and a genuine negative is held in internal/component/ike/engine: TestRFC5216DataCipherComesFromTheESPProposal runs a real EAP-TLS exchange (Ze as authenticator, TLS 1.3 by Go's default, whose suites are all AEAD), sets EAPSession/EAPMSK on the IKE SA as responder_eap.go does, and createFirstChildSA installs the ESP proposal's aes256/sha256 non-AEAD transform; TestRFC5216TLSSessionDoesNotChooseTheDataCipher has one TLS session authenticate two IKE SAs with different ESP proposals and requires the installed transforms to differ and follow each proposal (aes128/sha512 on the second), so data ciphers taken from or pinned by the TLS session go red. Both carry records on child.go createFirstChildSA. The negotiated TLS version/suite is not asserted in the body (the fixture relies on Go's TLS 1.3 default). HEAD tag on eap TestEAPTLSMutualAuthHandshakeSucceeds is supplementary, holds no record, and its prose ('not reused as the lower-layer key') claims more than its 64-octet-MSK body: rewording is author work; the verdict does not rest on it.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC5216TLSSessionDoesNotChooseTheDataCipher |
unit/verify | revert, verified |
| positive | TestRFC5216DataCipherComesFromTheESPProposal |
unit/verify | revert, verified |
| positive | TestEAPTLSMutualAuthHandshakeSucceeds |
unit/verify | revert, verified |
RFC5216-2.4-5
To ensure interoperability, EAP-TLS peers and servers MUST support the TLS [RFC4346] mandatory-to-implement ciphersuite: TLS_RSA_WITH_3DES_EDE_CBC_SHA (§2.4)
Audit verdict: not audited: no reader has judged these tests
No test carries RFC5216-2.4-5, so no unit is bound to it.
RFC5216-2.1.1-1
If the EAP server sent a certificate_request message in the preceding EAP-Request packet, then unless the peer is configured for privacy (see Section 2.1.4) the peer MUST send, in addition, certificate and certificate_verify messages. (§2.1.1)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Re-judged 2026-10-12 (independent auditor) after 37dbe54095, where the authenticator with resumption on installs redeemTLS13Only (resumption.go), which refuses every TLS 1.2 ticket. This unit is byte-identical; the file around it changed. Its helpers (tls12Exchange, fullTLS12Flights) gained a tune argument passed as nil to driveTunedEAPTLSFlight, which driveEAPTLSFlight itself calls with nil, and fresh stores offer no ticket, so the producer change never runs here. Verdict stands. Re-judged 2026-09-30 after the D-8 peer certificate fix (answerCertificateRequest). D-8 fixed: tlsClientConfig answers every certificate_request through GetClientCertificate (answerCertificateRequest, peer.go), so the peer no longer drops to an empty certificate_list when the request names another CA. Positive: TestRFC5216PeerSendsItsCertificateWhateverTheRequestNames reads the TLS 1.2 peer flight off the wire after a certificate_request naming a CA other than the peer certificate's issuer: a certificate message whose first entry is the configured DER, and a certificate_verify (red before the fix, record on answerCertificateRequest); TestRFC5216PeerFlightAnswersTheCertificateRequest keeps the exact compliant flight. Negative (R1 a, non-compliant form on the wire): TestRFC5216AuthenticatorRefusesAPeerThatSendsNoCertificate configures a TRUSTED client cert, empties the peer's answer after the TLS client starts, asserts the empty certificate_list and absent certificate_verify on the wire, and the authenticator sends no EAP-Success, does not succeed or complete its handshake, and refuses with an error that is not a certificate verification failure. Privacy (Section 2.1.4) is not implemented, so the unless-clause never applies. Every tagged unit carries an observed-red record.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC5216AuthenticatorRefusesAPeerThatSendsNoCertificate |
unit/verify | revert, verified |
| negative | TestEAPTLSAuthenticatorRequiresClientCert |
unit/verify | revert, verified |
| positive | TestEAPTLSMutualAuthHandshakeSucceeds |
unit/verify | revert, verified |
| positive | TestRFC5216PeerFlightAnswersTheCertificateRequest |
unit/verify | revert, verified |
| positive | TestRFC5216PeerSendsItsCertificateWhateverTheRequestNames |
unit/verify | revert, verified |
RFC5216-2.1.1-2
The EAP-TLS conversation will then begin, with the peer sending an EAP-Response packet with EAP-Type=EAP-TLS. The data field of that packet will encapsulate one or more TLS records in TLS record layer format, containing a TLS client_hello handshake message. (§2.1.1)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Forbidden: the answer to the Start is not an EAP-Response of Type 13, or its data is not a TLS record carrying client_hello. TestEAPTLSPeerFirstResponseCarriesClientHello fatals on Code != CodeResponse or Type != TypeTLS, and assertHandshakeRecord fails unless the TypeData holds a handshake record of type client_hello. Negative: TestEAPTLSPeerStalledClientFailsRatherThanAcknowledging fatals if a peer whose TLS engine produced nothing sends any Response instead of errTLSClientStalled, so an EAP-TLS Response with no client_hello is refused.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestEAPTLSPeerStalledClientFailsRatherThanAcknowledging |
unit/verify | unproven |
| positive | TestEAPTLSPeerFirstResponseCarriesClientHello |
unit/verify | unproven |
RFC5216-2.1.5-1
As a result, an EAP-TLS implementation MUST provide its own support for fragmentation and reassembly. (§2.1.5)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Re-judged 2026-10-01 (owner ruling 5). Positive TestTLSFragmenterRoundTrip: a 3000-octet message is split into >=3 fragments and reassembled byte-for-byte. Negatives (inconsistent reassembly is refused): TestRFC5216ReassemblyLongerThanDeclaredIsRefused declares 30, sends 20+20, and the overrunning fragment is refused with 'exceeds declared length', no answer, buffer keeps the 20-octet prefix; TestRFC5216ReassemblyShorterThanDeclaredIsRefused declares 3000 and ends at 1524 (M clear), refused naming '1524 of 3000', no answer, not drained to crypto/tls. Both drive the peer's handleTLSRequest; the authenticator shares reassemble/reassemblyComplete. TestTLSReassemblyRejectsOversized (cap) stays as a supplementary DoS bound. All four units hold observed-red revert records.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestTLSReassemblyRejectsOversized |
unit/verify | revert, verified |
| negative | TestRFC5216ReassemblyLongerThanDeclaredIsRefused |
unit/verify | revert, verified |
| negative | TestRFC5216ReassemblyShorterThanDeclaredIsRefused |
unit/verify | revert, verified |
| positive | TestTLSFragmenterRoundTrip |
unit/verify | revert, verified |
RFC5216-5.3-1
Since the EAP-TLS server is typically connected to the Internet, it SHOULD support validating the peer certificate using RFC 3280 [RFC3280] compliant path validation (§5.3)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Re-judged 2026-09-30 after the D-8 peer certificate fix (answerCertificateRequest). Server path-validation SHOULD. Positive: a valid peer chain is path-validated and accepted (TestEAPTLSMutualAuthHandshakeSucceeds). Negative no longer confounded: since D-8 the peer sends its untrusted certificate, and TestEAPTLSServerRejectsUntrustedClientChain asserts the authenticator's refusal is a tls.CertificateVerificationError wrapping x509.UnknownAuthorityError whose UnverifiedCertificates[0] is the configured untrusted leaf, besides no EAP-Success and an incomplete handshake; it is distinct from the certless refusal of 2.1.1-1, which asserts NOT a verification error. Records observed red on newTLSMethod for both polarities.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestEAPTLSServerRejectsUntrustedClientChain |
unit/verify | revert, verified |
| positive | TestEAPTLSMutualAuthHandshakeSucceeds |
unit/verify | revert, verified |
RFC5216-5.3-4
Therefore, the EAP-TLS server SHOULD provide its entire certificate chain minus the root to facilitate certificate validation by the peer. The EAP-TLS peer SHOULD support validating the server certificate using RFC 3280 [RFC3280] compliant path validation. (§5.3)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Re-judged 2026-10-12 (independent auditor) after 37dbe54095, where the authenticator with resumption on installs redeemTLS13Only (resumption.go), which refuses every TLS 1.2 ticket. This unit is byte-identical; the file around it changed. Its helpers (tls12Exchange, fullTLS12Flights) gained a tune argument passed as nil to driveTunedEAPTLSFlight, which driveEAPTLSFlight itself calls with nil, and fresh stores offer no ticket, so the producer change never runs here. Verdict stands. Re-judged 2026-09-30 after the D-8 peer certificate fix (answerCertificateRequest). Server clause now exercised with a real intermediate: TestRFC5216ServerSendsItsIntermediateButNotTheRoot configures leaf+intermediate under the peer's root and reads the certificate message off the wire: exactly [leaf, intermediate], the root absent (the root is also the authenticator's ClientCAs anchor, so a server appending its pool would fail), peer validates and reaches EAP-Success. Negative (R1 a): TestRFC5216PeerRefusesAServerChainMissingItsIntermediate puts [leaf] alone on the wire and the peer refuses with x509.UnknownAuthorityError, never concluding. Peer clause also by TestEAPTLSPeerRejectsUntrustedServerChain and TestEAPTLSPeerWithoutCARefusesToStart (record re-observed on the changed startTLSClient). Limit named: Ze sends the chain the operator configures; it does not strip a configured root nor complete a leaf-only configuration, so the minus-the-root property rests on the configured chain.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestEAPTLSPeerRejectsUntrustedServerChain |
unit/verify | revert, verified |
| negative | TestEAPTLSPeerWithoutCARefusesToStart |
unit/verify | revert, verified |
| negative | TestRFC5216PeerRefusesAServerChainMissingItsIntermediate |
unit/verify | revert, verified |
| positive | TestEAPTLSMutualAuthHandshakeSucceeds |
unit/verify | revert, verified |
| positive | TestRFC5216ServerSendsItsChainWithoutTheRoot |
unit/verify | revert, verified |
| positive | TestRFC5216ServerSendsItsIntermediateButNotTheRoot |
unit/verify | revert, verified |
RFC5216-5.3-5
including the ability to retrieve intermediate certificates that may be necessary to validate the peer certificate. (§5.3)
Audit verdict: unimplemented (no code path enforces the requirement), fresh. Re-judged 2026-10-18 (independent auditor, after f6c2374f02). RFC 5216 Section 5.3: "it SHOULD support validating the peer certificate using RFC 3280 [RFC3280] compliant path validation, including the ability to retrieve intermediate certificates that may be necessary to validate the peer certificate. For details, see Section 4.2.2.1 of [RFC3280]." The cited section is Authority Information Access, the fetch. The next paragraph names the alternatives for a server that does NOT meet this: "Where the EAP-TLS server is unable to retrieve intermediate certificates, either it will need to be pre-configured with the necessary intermediate certificates to complete path validation or it will rely on the EAP-TLS peer to provide this information". f6c2374f02 implements the first alternative: the intermediate-certificate list (responder_eap.go eapTLSServerConfig) feeds clientChainVerifier.verify (client_chain.go), which path-validates against ca-certificate roots only, with configured plus peer-sent certificates as Intermediates, clientAuth EKU and the current time, and newTLSMethod (eap_tls.go) runs it in VerifyConnection on TLS 1.2, 1.3 and resumed sessions before checkChainRevocation. The tagged tests prove that alternative well (mutants putting the pool in Roots, dropping EKU, time or revocation, or ignoring the verify error each go red). They do not prove retrieval, because the code performs none: no AIA caIssuers lookup exists in internal/core/eap or internal/component/ike, and verify's own comment says Ze retrieves nothing over the network. Pre-configuration is the RFC's named fallback for a server unable to retrieve, not this SHOULD, so the row stays an implementation gap (a SHOULD deviation, not a defect). The row needs its {gap} annotation restored (naming AIA retrieval absent and pre-configuration implemented), and Support remaining must stop saying RFC5216-5.3-5 is no longer among the gaps.
No test carries RFC5216-5.3-5, so no unit is bound to it.
RFC5216-5.4-1
EAP-TLS peer and server implementations MUST support the use of Certificate Revocation Lists (CRLs); for details, see Section 3.3 of [RFC3280]. (§5.4)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Re-judged 2026-10-12 (independent auditor) after 37dbe54095, where the authenticator with resumption on installs redeemTLS13Only (resumption.go), which refuses every TLS 1.2 ticket. This unit is byte-identical; the file around it changed. Its helpers (tls12Exchange, fullTLS12Flights) gained a tune argument passed as nil to driveTunedEAPTLSFlight, which driveEAPTLSFlight itself calls with nil, and fresh stores offer no ticket, so the producer change never runs here. Verdict stands. Server half: a CRL revoking the client certificate refuses the TLS 1.2 exchange, a CRL naming nobody completes. Peer half (new, TestRFC5216PeerRefusesARevokedServerCertificateOverTLS12): a peer CRL revoking the authenticator certificate over TLS 1.2 gives a peer error, no EAP-Success, no MSK; isolated by TestEAPTLS12CompletesWithAnUnrevokedChain, where the peer holds a CRL that revokes nothing and completes. Every tagged unit carries a record.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestEAPTLS12CompletesWithAnUnrevokedChain |
unit/verify | revert, verified |
| positive | TestRFC5216PeerRefusesARevokedServerCertificateOverTLS12 |
unit/verify | revert, verified |
| positive | TestEAPTLS12RefusesARevokedClientCertificate |
unit/verify | revert, verified |
RFC5216-5.4-2
peer implementations MUST also support checking for certificate revocation after authentication completes and network connectivity is available (§5.4)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Forbidden: a peer with no revocation check after authentication. TestPostAuthenticationCheckClosesTheSAWhenTheResponderReportsRevoked fatals if startServerCertRecheck (called from established.go after the exchange) starts no check or reports no verdict when the OCSP responder says revoked; TestPostAuthenticationCheckLeavesTheSAUpWhenTheResponderReportsGood fatals on any verdict when it says good. The eap units prove the accepted TLS 1.2 chain is kept for the check and none is published on a refused handshake. The first unit's name claims the SA closes; it asserts only the verdict.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestPostAuthenticationCheckLeavesTheSAUpWhenTheResponderReportsGood |
unit/verify | revert, verified |
| negative | TestEAPTLSPeerPublishesNoChainWhenTheHandshakeRefusedIt |
unit/verify | revert, verified |
| positive | TestPostAuthenticationCheckClosesTheSAWhenTheResponderReportsRevoked |
unit/verify | revert, verified |
| positive | TestEAPTLS12PeerKeepsTheChainItAcceptedForTheLaterCheck |
unit/verify | revert, verified |
RFC5216-2.1.3-1
The EAP Server MUST reply with an EAP-Failure packet since server authentication failure is a terminal condition. (§2.1.3)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Forbidden: the server answering the peer's alert with anything but EAP-Failure, or later than the next packet. TestRFC5216ServerRepliesEAPFailureToPeerAlert fatals if no Failure is sent, errors on any Success, and errors unless failureAt == alertAt+1. Negative: TestRFC5216ServerSendsNoEAPFailureWhenBothSidesAuthenticate errors if a mutually authenticated conversation draws any Failure.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC5216ServerSendsNoEAPFailureWhenBothSidesAuthenticate |
unit/verify | unproven |
| positive | TestRFC5216ServerRepliesEAPFailureToPeerAlert |
unit/verify | unproven |
RFC5216-2.1.3-3
To ensure that the peer receives the TLS alert message, the EAP server MUST wait for the peer to reply with an EAP-Response packet. (§2.1.3)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Forbidden: the server concluding (EAP-Failure) on the round it sends its alert, instead of waiting for the peer's Response. alertFlight fatals if the round that records the cause carries CodeFailure, TestEAPTLSAuthenticatorSendsTheAlertBeforeItReportsTheFailure errors unless that round is an EAP-Request with a TLS alert record, and only the later Response draws the Failure. Negative: TestEAPTLSAuthenticatorReportsTheFailureWithNoAlertToSend fatals if a failure with no alert to send reports no error, so the wait is bound to an alert.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestEAPTLSAuthenticatorReportsTheFailureWithNoAlertToSend |
unit/verify | unproven |
| positive | TestEAPTLSAuthenticatorSendsTheAlertBeforeItReportsTheFailure |
unit/verify | revert, verified |
RFC5216-2.1.3-4
it MAY contain an EAP-Response packet with EAP-Type=EAP-TLS and no data, in which case the EAP-Server MUST send an EAP-Failure packet and terminate the conversation. (§2.1.3)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Re-judged 2026-10-12 (independent auditor) after 37dbe54095, where the authenticator with resumption on installs redeemTLS13Only (resumption.go), which refuses every TLS 1.2 ticket. This unit is byte-identical; the file around it changed. Its helpers (tls12Exchange, fullTLS12Flights) gained a tune argument passed as nil to driveTunedEAPTLSFlight, which driveEAPTLSFlight itself calls with nil, and fresh stores offer no ticket, so the producer change never runs here. Verdict stands. Both clauses asserted: the no-data Response after the authenticator's alert draws EAP-Failure, and later Responses (same one, Identifiers at and after the Failure's) draw nothing with Succeeded false; the alert is on the wire before EAP-Failure. Control negative: a mutually authenticated exchange sends no EAP-Failure and reaches EAP-Success, so the Failure answers the rejection only. All units recorded.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC5216ServerSendsNoEAPFailureWhenBothSidesAuthenticate |
unit/verify | revert, verified |
| positive | TestEAPTLSAuthenticatorSendsTheAlertBeforeItReportsTheFailure |
unit/verify | revert, verified |
| positive | TestEAPTLSSessionPutsTheAlertOnTheWireBeforeEAPFailure |
unit/verify | revert, verified |
| positive | TestRFC5216AuthenticatorEndsTheConversationAfterItsFailure |
unit/verify | revert, verified |
RFC5216-2.1.3-5
If the peer authenticates successfully, the EAP server MUST respond with an EAP-Request packet with EAP-Type=EAP-TLS, which includes, in the case of a new TLS session, one or more TLS records containing TLS change_cipher_spec and finished handshake messages. (§2.1.3)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Forbidden: the packet before EAP-Success is not an EAP-TLS Request carrying change_cipher_spec then the Finished handshake record, or a rejected peer receives that flight. TestRFC5216SuccessfulTerminationSendsFlightAckThenSuccess fatals on a non-Request/non-TLS packet or a bare ACK there, errors unless record types are [change_cipher_spec, handshake], and requires equal non-zero MSKs. Negative: TestRFC5216NoClosingFlightOrSuccessWhenThePeerIsRejected errors on any change_cipher_spec record sent to a rejected peer.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC5216NoClosingFlightOrSuccessWhenThePeerIsRejected |
unit/verify | unproven |
| positive | TestRFC5216SuccessfulTerminationSendsFlightAckThenSuccess |
unit/verify | unproven |
RFC5216-2.1.3-6
To ensure that the EAP Server receives the TLS alert message, the peer MUST wait for the EAP Server to reply before terminating the conversation. (§2.1.3)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Forbidden: the peer terminating on the round it discovers the failure, before the server replies. TestRFC5216PeerRepliesBeforeItTerminates fatals if that round returns Err or no Response, errors if the Response is a bare ACK rather than the alert, and requires the error only from the round that processes the server's EAP-Failure. Negative: TestRFC5216PeerDoesNotWaitWhenItSentNothing errors if a peer that sent nothing parks its error or sends a Response.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC5216PeerDoesNotWaitWhenItSentNothing |
unit/verify | unproven |
| positive | TestRFC5216PeerRepliesBeforeItTerminates |
unit/verify | unproven |
RFC5216-2.1.3-7
If the EAP server authenticates successfully, the peer MUST send an EAP-Response packet of EAP-Type=EAP-TLS, and no data. (§2.1.3)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Forbidden: the peer answering the server's closing flight with anything but a no-data EAP-TLS Response. TestRFC5216SuccessfulTerminationSendsFlightAckThenSuccess errors unless bareEAPTLSResponse(peerSent[successAt-1]). Negative: TestRFC5216PeerSendsItsAlertRatherThanTheNoDataResponse errors if a peer refusing the server sends the no-data Response as its last packet.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC5216PeerSendsItsAlertRatherThanTheNoDataResponse |
unit/verify | unproven |
| positive | TestRFC5216SuccessfulTerminationSendsFlightAckThenSuccess |
unit/verify | unproven |
RFC5216-2.1.3-8
The EAP Server then MUST respond with an EAP-Success message. (§2.1.3)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Forbidden: the server answering the peer's no-data Response with anything but EAP-Success. TestRFC5216SuccessfulTerminationSendsFlightAckThenSuccess errors unless serverSent[successAt].Code == CodeSuccess directly after that Response. Negative: TestRFC5216NoClosingFlightOrSuccessWhenThePeerIsRejected errors on any Success to a rejected peer.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC5216NoClosingFlightOrSuccessWhenThePeerIsRejected |
unit/verify | unproven |
| positive | TestRFC5216SuccessfulTerminationSendsFlightAckThenSuccess |
unit/verify | unproven |
RFC5216-2.3-1
Key_Material = TLS-PRF-128(master_secret, "client EAP encryption", client.random || server.random) MSK = Key_Material(0,63) EMSK = Key_Material(64,127) (§2.3)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Unchanged after comment-only edits. Single-polarity positive (row marker): with the literal label 'client EAP encryption' and the 128-octet export read from the TLS session, the authenticator MSK equals octets 0-63 and the EMSK 64-127 (TLS 1.2); MSKs identical on both ends. Recorded.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| positive | TestRFC3748EAPTLSExportsASixtyFourOctetEMSK |
unit/verify | revert, verified |
| positive | TestEAPTLSMutualAuthHandshakeSucceeds |
unit/verify | revert, verified |
| positive | TestRFC5216MSKIsTheExportUnderTheRFCLabel |
unit/verify | revert, verified |
RFC5216-2.1.1-3
Once having received the peer's Identity, the EAP server MUST respond with an EAP-TLS/Start packet, which is an EAP-Request packet with EAP-Type=EAP-TLS, the Start (S) bit set, and no data. (§2.1.1)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Forbidden: after the Identity the server sends anything but a Request of Type 13 with only the S flag, or sends a Start before an Identity. TestRFC5216StartAnswersTheIdentityResponse fatals on Code != CodeRequest, Type != TypeTLS, or TypeData != {S}; TestRFC5216NoStartBeforeTheIdentityIsReceived fatals if Begin opens with an EAP-TLS packet or a stale Identity Response draws any packet.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC5216NoStartBeforeTheIdentityIsReceived |
unit/verify | revert, verified |
| positive | TestRFC5216StartAnswersTheIdentityResponse |
unit/verify | revert, verified |
RFC5216-2.1.1-4
If the peer's sessionId is null or unrecognized by the server, the server MUST choose the sessionId to establish a new session. (§2.1.1)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Re-judged 2026-10-14 (independent auditor) after 53c17be5e3. RFC 5216 Section 2.1.1: 'If the peer's sessionId is null or unrecognized by the server, the server MUST choose the sessionId to establish a new session.' Producer: the patched GOROOT doFullHandshake (internal/le/go/stdpatch/patches/crypto-tls-server-session-id.patch) writes a fresh 32-octet session_id when the TLS 1.2 full handshake issues no ticket, and eap_tls.go::tls12WithoutTickets disables tickets for every handshake negotiatesTLS13 classifies as TLS 1.2. UNRECOGNIZED half: rfc5216_session_id_test.go::TestRFC5216TLS12FullHandshakeServerHelloSessionID offers ze's peer's non-empty random id and requires a 32-octet ServerHello session_id differing from it, no SessionTicket extension and no NewSessionTicket. NULL half: TestRFC5216TLS12NullSessionIDServerChooses rewrites the TLS 1.2 ClientHello to an empty session_id (asserted), replays it at two fresh authenticators and requires each ServerHello to carry a 32-octet id, no ticket, and the two ids to differ. Its recorded break is a panic in tls12WithoutTickets, which shows reachability only, so the auditor ran targeted mutants through go test overlays: a patched doFullHandshake that chooses an id only when the client offered one ('!ticketSupported && len(clientHello.sessionId) > 0') leaves the unrecognized-id test green and turns the null-id test red on 'session_id is 0 octets'; tls12WithoutTickets without SessionTicketsDisabled turns both red. Limit: the null-id ClientHello still carries the SessionTicket extension; the null-id ClientHello without it is not sent.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC5216TLS12FullHandshakeServerHelloSessionID |
unit/verify | revert, verified |
| positive | TestRFC5216TLS12FullHandshakeServerHelloSessionID |
unit/verify | revert, verified |
| positive | TestRFC5216TLS12NullSessionIDServerChooses |
unit/verify | revert, verified |
RFC5216-2.1.1-5
If the session matches the peer's, then the ciphersuite MUST match the one negotiated during the handshake protocol execution that established the session. (§2.1.1)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Re-judged 2026-10-14 (independent auditor) after 56e67411be, where the authenticator's GetConfigForClient (eap_tls.go) answers every handshake negotiatesTLS13 classifies as TLS 1.2 with tls12WithoutTickets, so ze issues no TLS 1.2 ticket, while UnwrapSession redeemTLS13Only/refuseResumption (resumption.go) still refuses a TLS 1.2 session. Section 2.1.1 'If the session matches the peer's, then the ciphersuite MUST match' binds the resuming server. TestRFC5216ResumedFlightsCarryOnlyChangeCipherSpecAndFinished mints and resumes a TLS 1.2 session on a stock-resumption TEST server (stockTLS12Resumption) only to give the peer a live ticket encrypted under ze's own ticket keys; the suite-equality check there is a harness check on that test server and proves nothing about ze. The ze assertion is the last attempt, run with ze's unmodified authenticator config (tune nil): offered that ticket it does not resume and answers a full handshake (certificate, no change_cipher_spec). Ze therefore never matches a TLS 1.2 session, so no matched-session ciphersuite arises, and any TLS 1.2 resumption goes red. The test-server ticket does not weaken this: the subject of the assertion is ze's own authenticator and the input is the strongest one (a ticket ze's keys decrypt). Negative TestRFC5216TLSConfigRefusesWhatWouldBreakTheFlights: UnwrapSession answers no session at TLS 1.2 (on and off) and redeems at TLS 1.3, so the refusal is not vacuous.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC5216TLSConfigRefusesWhatWouldBreakTheFlights |
unit/verify | revert, verified |
| positive | TestRFC5216ResumedFlightsCarryOnlyChangeCipherSpecAndFinished |
unit/verify | revert, verified |
| positive | TestRFC5216TLSConfigBothRolesInstallWhatTheFlightsCarry |
unit/verify | revert, verified |
RFC5216-2.1.1-6
If the EAP server is not resuming a previously established session, then it MUST include a TLS server_certificate handshake message, and a server_hello_done handshake message MUST be the last handshake message encapsulated in this EAP-Request packet. (§2.1.1)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Re-judged 2026-10-14 (independent auditor) after 56e67411be, where the authenticator's GetConfigForClient (eap_tls.go) answers every handshake negotiatesTLS13 classifies as TLS 1.2 with tls12WithoutTickets, so ze issues no TLS 1.2 ticket, while UnwrapSession redeemTLS13Only/refuseResumption (resumption.go) still refuses a TLS 1.2 session. Units changed only by withdrawal of the 2.1.1-4 tag lines. TestRFC5216ServerFlightCarriesItsCertificateAndEndsWithServerHelloDone requires the authenticator's first TLS 1.2 flight to carry a certificate and end with server_hello_done; the negative refuses a config with an unparsable certificate. Both polarities hold.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC5216TLSConfigRefusesWhatWouldBreakTheFlights |
unit/verify | revert, verified |
| positive | TestRFC5216ServerFlightCarriesItsCertificateAndEndsWithServerHelloDone |
unit/verify | revert, verified |
| positive | TestRFC5216TLSConfigBothRolesInstallWhatTheFlightsCarry |
unit/verify | revert, verified |
RFC5216-2.1.1-7
The certificate message contains a public key certificate chain for either a key exchange public key (such as an RSA or Diffie-Hellman key exchange public key) or a signature public key (such as an RSA or Digital Signature Standard (DSS) signature public key). In the latter case, a TLS server_key_exchange handshake message MUST also be included to allow the key exchange to take place. (§2.1.1)
Audit verdict: not audited: no reader has judged these tests
No test carries RFC5216-2.1.1-7, so no unit is bound to it.
RFC5216-2.1.1-8
If the peer supports EAP-TLS and is configured to use it, it MUST respond to the EAP-Request with an EAP-Response packet of EAP- Type=EAP-TLS. (§2.1.1)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Forbidden: a peer configured for EAP-TLS answering the EAP-TLS Request with anything but an EAP-TLS Response (a Nak, another type). TestRFC5216ConfiguredPeerAnswersEAPTLSWithEAPTLS fatals on Code != CodeResponse or Type != TypeTLS and on TypeData without a handshake record. Negative for the condition: TestRFC5216PeerNotConfiguredForEAPTLSDoesNotAnswerWithEAPTLS fatals if an MS-CHAPv2 peer answers with Type 13 and wantLegacyNak requires the Nak.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC5216PeerNotConfiguredForEAPTLSDoesNotAnswerWithEAPTLS |
unit/verify | revert, verified |
| positive | TestRFC5216ConfiguredPeerAnswersEAPTLSWithEAPTLS |
unit/verify | revert, verified |
RFC5216-2.1.1-9
If the preceding server_hello message sent by the EAP server in the preceding EAP-Request packet did not indicate the resumption of a previous session, the data field of this packet MUST encapsulate one or more TLS records containing a TLS client_key_exchange, change_cipher_spec, and finished messages. (§2.1.1)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Re-judged 2026-10-14 (independent auditor) after 56e67411be, where the authenticator's GetConfigForClient (eap_tls.go) answers every handshake negotiatesTLS13 classifies as TLS 1.2 with tls12WithoutTickets, so ze issues no TLS 1.2 ticket, while UnwrapSession redeemTLS13Only/refuseResumption (resumption.go) still refuses a TLS 1.2 session. Units changed only by withdrawal of the 2.1.1-4 tag lines. TestRFC5216PeerFlightAnswersTheCertificateRequest requires ze's peer, after a non-resuming server_hello, to send certificate, client_key_exchange, certificate_verify, then change_cipher_spec and exactly one sealed handshake record (finished). The config negative (no session cache with resumption off) is weaker but the positive asserts the exact flight.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC5216TLSConfigRefusesWhatWouldBreakTheFlights |
unit/verify | revert, verified |
| positive | TestRFC5216PeerFlightAnswersTheCertificateRequest |
unit/verify | revert, verified |
| positive | TestRFC5216TLSConfigBothRolesInstallWhatTheFlightsCarry |
unit/verify | revert, verified |
RFC5216-2.1.1-10
If the preceding server_hello message sent by the EAP server in the preceding EAP-Request packet indicated the resumption of a previous session, then the peer MUST send only the change_cipher_spec and finished handshake messages. (§2.1.1)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Re-judged 2026-10-14 (independent auditor) after 56e67411be, where the authenticator's GetConfigForClient (eap_tls.go) answers every handshake negotiatesTLS13 classifies as TLS 1.2 with tls12WithoutTickets, so ze issues no TLS 1.2 ticket, while UnwrapSession redeemTLS13Only/refuseResumption (resumption.go) still refuses a TLS 1.2 session. The obligation binds ze's PEER. TestRFC5216ResumedFlightsCarryOnlyChangeCipherSpecAndFinished resumes against a TEST server (stockTLS12Resumption: ze's config with GetConfigForClient and UnwrapSession removed, i.e. stock crypto/tls), which is the counterpart, not the subject, and is needed because ze's authenticator never resumes TLS 1.2. It requires peer.Resumed() and that ze's peer answers the resuming server_hello with no plaintext handshake message, change_cipher_spec and one sealed record. A peer that sent client_key_exchange or certificate goes red. The test server does not weaken the proof of ze's own behaviour.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC5216TLSConfigRefusesWhatWouldBreakTheFlights |
unit/verify | revert, verified |
| positive | TestRFC5216ResumedFlightsCarryOnlyChangeCipherSpecAndFinished |
unit/verify | revert, verified |
| positive | TestRFC5216TLSConfigBothRolesInstallWhatTheFlightsCarry |
unit/verify | revert, verified |
RFC5216-2.1.2-1
If the EAP server is resuming a previously established session, then it MUST include only a TLS change_cipher_spec message and a TLS finished handshake message after the server_hello message. (§2.1.2)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Re-judged 2026-10-14 (independent auditor) after 56e67411be, where the authenticator's GetConfigForClient (eap_tls.go) answers every handshake negotiatesTLS13 classifies as TLS 1.2 with tls12WithoutTickets, so ze issues no TLS 1.2 ticket, while UnwrapSession redeemTLS13Only/refuseResumption (resumption.go) still refuses a TLS 1.2 session. Section 2.1.2: 'If the EAP server is resuming a previously established session, then it MUST include only a TLS change_cipher_spec message and a TLS finished handshake message after the server_hello message.' TestRFC5216AuthenticatorAnswersATLS12TicketWithAFullHandshake mints the TLS 1.2 ticket on a TEST server (issueTLS12Tickets: ze's config with GetConfigForClient removed, same Resumption store and ticket keys) because ze issues none, then offers it to ze's unmodified authenticator (tune nil); ticketedTLS12Attempt fails unless the ClientHello really offers the ticket. Ze must not resume and its flight must be the full one (certificate, server_hello_done last, no change_cipher_spec). The precondition 'is resuming' therefore never holds for TLS 1.2 in ze, and any TLS 1.2 resumption goes red. The minted ticket is a fixture input, not the subject, so the test still proves ze's own behaviour; it proves compliance by non-resumption, not the resumed flight's content (TLS 1.3 resumption is RFC 9190's). Negative: UnwrapSession refuses TLS 1.2 on and off and redeems TLS 1.3.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC5216TLSConfigRefusesWhatWouldBreakTheFlights |
unit/verify | revert, verified |
| positive | TestRFC5216AuthenticatorAnswersATLS12TicketWithAFullHandshake |
unit/verify | revert, verified |
| positive | TestRFC5216TLSConfigBothRolesInstallWhatTheFlightsCarry |
unit/verify | revert, verified |
RFC5216-2.1.5-2
When an EAP-TLS peer receives an EAP-Request packet with the M bit set, it MUST respond with an EAP-Response with EAP-Type=EAP-TLS and no data. (§2.1.5)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Forbidden: the peer answering an M-set Request with data or with anything but an EAP-TLS Response. TestRFC5216PeerAcksEachServerFragmentWithNoData errors unless every M-set Request is answered by bareEAPTLSResponse, and errors if the last fragment (M clear) draws the no-data ACK instead of the TLS flight; driveFragmentedFlight fatals unless both sides fragment and the MSKs match.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC5216PeerAcksEachServerFragmentWithNoData |
unit/verify | revert, verified |
| positive | TestRFC5216PeerAcksEachServerFragmentWithNoData |
unit/verify | revert, verified |
RFC5216-2.1.5-3
The EAP server MUST wait until it receives the EAP-Response before sending another fragment. (§2.1.5)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Forbidden: the server sending another fragment before the ACK for the current one arrives. driveFragmentedFlight feeds a Response carrying the previous Identifier after every server fragment and fatals if it draws any packet or moves outOffset; TestRFC5216ServerSendsTheNextFragmentOnlyInAnswerToTheAck fatals unless the packet after each fragment is the continuation Request carrying data with neither S nor L.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC5216ServerSendsTheNextFragmentOnlyInAnswerToTheAck |
unit/verify | revert, verified |
| positive | TestRFC5216ServerSendsTheNextFragmentOnlyInAnswerToTheAck |
unit/verify | revert, verified |
RFC5216-2.1.5-4
In order to prevent errors in processing of fragments, the EAP server MUST increment the Identifier field for each fragment contained within an EAP-Request, and the peer MUST include this Identifier value in the fragment ACK contained within the EAP-Response. (§2.1.5)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Clause 1 forbidden: a fragment Request whose Identifier is not the previous plus one; TestRFC5216ServerFragmentIdentifiersIncrementAndTheAckEchoesThem errors on req.Identifier != prev+1. Clause 2 forbidden: an ACK carrying another Identifier; the same test errors on ack.Identifier != req.Identifier, and driveFragmentedFlight fatals if a stale-Identifier ACK draws a packet.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC5216ServerFragmentIdentifiersIncrementAndTheAckEchoesThem |
unit/verify | revert, verified |
| positive | TestRFC5216ServerFragmentIdentifiersIncrementAndTheAckEchoesThem |
unit/verify | revert, verified |
RFC5216-2.1.5-5
Similarly, when the EAP server receives an EAP-Response with the M bit set, it MUST respond with an EAP-Request with EAP-Type=EAP-TLS and no data. (§2.1.5)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Forbidden: the server answering an M-set Response with data or with a non-EAP-TLS Request. TestRFC5216ServerAcksEachPeerFragmentWithNoData errors unless each M-set Response is followed by a Request of Type 13 with TypeData exactly 0x00, and errors if the peer's last fragment is answered with an ACK instead of the closing flight.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC5216ServerAcksEachPeerFragmentWithNoData |
unit/verify | revert, verified |
| positive | TestRFC5216ServerAcksEachPeerFragmentWithNoData |
unit/verify | revert, verified |
RFC5216-2.1.5-6
The EAP peer MUST wait until it receives the EAP-Request before sending another fragment. (§2.1.5)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Forbidden: the peer sending its next fragment before an EAP-Request arrives. driveFragmentedFlight feeds the peer an undefined-Code packet after each of its fragments and fatals if probe.Response != nil or peer.outOffset moves; TestRFC5216PeerSendsTheNextFragmentOnlyInAnswerToTheAck fatals unless the packet after each fragment is a continuation Response carrying TLS data without L.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC5216PeerSendsTheNextFragmentOnlyInAnswerToTheAck |
unit/verify | revert, verified |
| positive | TestRFC5216PeerSendsTheNextFragmentOnlyInAnswerToTheAck |
unit/verify | revert, verified |
RFC5216-2.1.5-7
In order to prevent errors in the processing of fragments, the EAP server MUST increment the Identifier value for each fragment ACK contained within an EAP-Request, and the peer MUST include this Identifier value in the subsequent fragment contained within an EAP- Response. (§2.1.5)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Clause 1 forbidden: an ACK Identifier that is not the previous Request plus one; TestRFC5216ServerAckIdentifiersIncrementAndTheNextFragmentEchoesThem errors on ack.Identifier != serverSent[i].Identifier+1. Clause 2 forbidden: the peer fragment after an ACK carries another Identifier; the same test errors on next.Identifier != ack.Identifier, and driveFragmentedFlight fatals if the authenticator accepts a fragment re-numbered with a stale Identifier.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC5216ServerAckIdentifiersIncrementAndTheNextFragmentEchoesThem |
unit/verify | revert, verified |
| positive | TestRFC5216ServerAckIdentifiersIncrementAndTheNextFragmentEchoesThem |
unit/verify | revert, verified |
RFC5216-2.4-7
EAP-TLS implementations MUST support TLS v1.0. (§2.4)
Audit verdict: not audited: no reader has judged these tests
No test carries RFC5216-2.4-7, so no unit is bound to it.
RFC5216-3-3
The Identifier field MUST be changed on each Request packet. (§3)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Forbidden: a Request that repeats the Identifier of the packet before it. TestRFC5216IdentifierChangesOnEveryRequest errors on cur.Identifier == prev.Identifier for every Request of a full TLS 1.2 conversation and fatals if fewer than 3 Requests ran; the negative pins that EAP-Success (not a Request) repeats the answered Identifier.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC5216IdentifierChangesOnEveryRequest |
unit/verify | revert, verified |
| positive | TestRFC5216IdentifierChangesOnEveryRequest |
unit/verify | revert, verified |
RFC5216-3-4
Octets outside the range of the Length field should be treated as Data Link Layer padding and MUST be ignored on reception. (§3)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Forbidden: octets past the Length field read as packet data. TestRFC5216PaddingPastTheLengthIsIgnored decodes a packet with seven padding octets shaped like flags and a TLS record header and errors if TypeData differs from the unpadded TypeData, if its length exceeds Length less 5, or if the padding bytes appear inside TypeData; the unpadded decode is the positive.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC5216PaddingPastTheLengthIsIgnored |
unit/verify | revert, verified |
| positive | TestRFC5216PaddingPastTheLengthIsIgnored |
unit/verify | revert, verified |
RFC5216-3-5
Implementations of this specification MUST set the reserved bits to zero, and MUST ignore them on reception. (§3)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Re-judged 2026-10-12 (independent auditor) after 37dbe54095, where the authenticator with resumption on installs redeemTLS13Only (resumption.go), which refuses every TLS 1.2 ticket. This unit is byte-identical; the file around it changed. Its helpers (tls12Exchange, fullTLS12Flights) gained a tune argument passed as nil to driveTunedEAPTLSFlight, which driveEAPTLSFlight itself calls with nil, and fresh stores offer no ticket, so the producer change never runs here. Verdict stands. Set clause: TestRFC5216SendersWriteZeroReservedFlagBits reads the flags octet of every EAP-TLS packet both roles sent in a full TLS 1.2 conversation and fails on any of bits 0x1f. Reception clause: all five reserved bits set on a Start and on a whole client_hello are ignored by peer and authenticator. Every tagged unit carries a record.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC5216ReservedFlagBitsAreIgnoredOnReception |
unit/verify | revert, verified |
| positive | TestRFC5216SendersWriteZeroReservedFlagBits |
unit/verify | revert, verified |
| positive | TestRFC5216ReservedFlagBitsAreIgnoredOnReception |
unit/verify | revert, verified |
RFC5216-3-6
The Identifier field is one octet and MUST match the Identifier field from the corresponding request. (§3.2)
Audit verdict: enforced (the tests do what the requirement demands), fresh. Forbidden: a Response whose Identifier differs from the Request it answers. TestRFC5216ResponseIdentifierMatchesTheRequest errors on res.Identifier != serverSent[i].Identifier over a full conversation; TestRFC5216ResponseIdentifierIsCopiedNotCounted fatals if Requests 0x80 then 0x05 are not answered 0x80 then 0x05, which refuses a peer that counts its own Identifiers.
| Polarity | Test | Kind and tier | Proof state |
|---|---|---|---|
| negative | TestRFC5216ResponseIdentifierIsCopiedNotCounted |
unit/verify | revert, verified |
| positive | TestRFC5216ResponseIdentifierMatchesTheRequest |
unit/verify | revert, verified |
RFC5216-5.2-1
Where the subjectAltName field is present in the peer or server certificate, the Peer-Id or Server-Id MUST be set to the contents of the subjectAltName. (§5.2)
Audit verdict: not audited: no reader has judged these tests
No test carries RFC5216-5.2-1, so no unit is bound to it.
RFC5216-5.2-2
If subject naming information is present only in the subjectAltName extension of a peer or server certificate, then the subject field MUST be an empty sequence and the subjectAltName extension MUST be critical. (§5.2)
Audit verdict: not audited: no reader has judged these tests
No test carries RFC5216-5.2-2, so no unit is bound to it.
RFC5216-5.2-3
Conforming implementations generating new certificates with Network Access Identifiers (NAIs) MUST use the rfc822Name in the subject alternative name field to describe such identities. (§5.2)
Audit verdict: not audited: no reader has judged these tests
No test carries RFC5216-5.2-3, so no unit is bound to it.
RFC5216-5.2-4
The use of the subject name field to contain an emailAddress Relative Distinguished Name (RDN) is deprecated, and MUST NOT be used. (§5.2)
Audit verdict: not audited: no reader has judged these tests
No test carries RFC5216-5.2-4, so no unit is bound to it.
RFC5216-5.2-5
Where it is non-empty, the subject name field MUST contain an X.500 distinguished name (DN). (§5.2)
Audit verdict: not audited: no reader has judged these tests
No test carries RFC5216-5.2-5, so no unit is bound to it.
RFC5216-5.3-2
Once a TLS session is established, EAP-TLS peer and server implementations MUST validate that the identities represented in the certificate are appropriate and authorized for use with EAP-TLS. (§5.3)
Audit verdict: not audited: no reader has judged these tests
No test carries RFC5216-5.3-2, so no unit is bound to it.
RFC5216-5.3-3
When performing this comparison, implementations MUST follow the validation rules specified in Section 3.1 of [RFC2818]. (§5.3)
Audit verdict: not audited: no reader has judged these tests
No test carries RFC5216-5.3-3, so no unit is bound to it.
Extraction sign-off
| Field | Value |
|---|---|
| Reviewer | claude |
| Signed off | 2026-09-21 |
| Register | rfc2119 |
| Source | rfc/full/rfc5216.txt |
| Source fingerprint | fbadcfb857b6d879 |
| Record | rfc/extraction/rfc5216.json |
| Mapped sentences | 45 |
| Declined as scope | 8 |
| Relocated to a spec, which Ze OWES | 0 |
| Unclassified | 0 |
Sections
| Section | Name | Sites | Disposition | Reason |
|---|---|---|---|---|
front |
not stated | 0 | walked | not stated |
1 |
not stated | 0 | walked | not stated |
1.1 |
not stated | 0 | walked | not stated |
1.2 |
not stated | 0 | walked | not stated |
2 |
not stated | 0 | walked | not stated |
2.1 |
not stated | 0 | walked | not stated |
2.1.1 |
not stated | 11 | walked | not stated |
2.1.2 |
not stated | 1 | walked | not stated |
2.1.3 |
not stated | 7 | walked | not stated |
2.1.4 |
not stated | 5 | walked | not stated |
2.1.5 |
not stated | 8 | walked | not stated |
2.2 |
not stated | 0 | walked | not stated |
2.3 |
not stated | 0 | walked | not stated |
2.4 |
not stated | 4 | walked | not stated |
3 |
not stated | 0 | walked | not stated |
3.1 |
not stated | 4 | walked | not stated |
3.2 |
not stated | 4 | walked | not stated |
4 |
not stated | 0 | walked | not stated |
5 |
not stated | 0 | walked | not stated |
5.1 |
not stated | 0 | walked | not stated |
5.2 |
not stated | 5 | walked | not stated |
5.3 |
not stated | 2 | walked | not stated |
5.4 |
not stated | 2 | walked | not stated |
5.5 |
not stated | 0 | walked | not stated |
6 |
not stated | 0 | walked | not stated |
6.1 |
not stated | 0 | walked | not stated |
6.2 |
not stated | 0 | walked | not stated |
Excluded sentences
| Site | Excluded kind | Reason | Quote |
|---|---|---|---|
2.1.4:1 |
feature-out-of-scopenever bound Ze the RFC makes a feature OPTIONAL, Ze decided not to offer it, and this obligation is conditional on offering it |
Section 2.1.4 makes privacy optional: "EAP-TLS peer and server implementations MAY support privacy." Ze declined it: internal/core/eap carries no privacy mode, no anonymous-NAI identity path and no empty-certificate-list handling, so every obligation this sentence conditions on supporting or being configured for privacy is out of scope. | In order to avoid disclosing the peer username, an EAP-TLS peer configured for privacy MUST negotiate a TLS ciphersuite supporting confidentiality and MUST provide a client certificate list containing no entries in response to the initial certificate_request from the EAP-TLS server. |
2.1.4:2 |
feature-out-of-scopenever bound Ze the RFC makes a feature OPTIONAL, Ze decided not to offer it, and this obligation is conditional on offering it |
Section 2.1.4 makes privacy optional: "EAP-TLS peer and server implementations MAY support privacy." Ze declined it: internal/core/eap carries no privacy mode, no anonymous-NAI identity path and no empty-certificate-list handling, so every obligation this sentence conditions on supporting or being configured for privacy is out of scope. | An EAP-TLS server supporting privacy MUST NOT treat a certificate list containing no entries as a terminal condition; instead, it MUST bring up the TLS session and then send a hello_request. |
2.1.4:3 |
feature-out-of-scopenever bound Ze the RFC makes a feature OPTIONAL, Ze decided not to offer it, and this obligation is conditional on offering it |
Section 2.1.4 makes privacy optional: "EAP-TLS peer and server implementations MAY support privacy." Ze declined it: internal/core/eap carries no privacy mode, no anonymous-NAI identity path and no empty-certificate-list handling, so every obligation this sentence conditions on supporting or being configured for privacy is out of scope. | An EAP-TLS peer supporting privacy MUST provide a certificate list containing at least one entry in response to the subsequent certificate_request sent by the server. |
2.1.4:4 |
feature-out-of-scopenever bound Ze the RFC makes a feature OPTIONAL, Ze decided not to offer it, and this obligation is conditional on offering it |
Section 2.1.4 makes privacy optional: "EAP-TLS peer and server implementations MAY support privacy." Ze declined it: internal/core/eap carries no privacy mode, no anonymous-NAI identity path and no empty-certificate-list handling, so every obligation this sentence conditions on supporting or being configured for privacy is out of scope. | If the EAP-TLS server supporting privacy does not receive a client certificate in response to the subsequent certificate_request, then it MUST abort the session. |
2.1.4:5 |
feature-out-of-scopenever bound Ze the RFC makes a feature OPTIONAL, Ze decided not to offer it, and this obligation is conditional on offering it |
Section 2.1.4 makes privacy optional: "EAP-TLS peer and server implementations MAY support privacy." Ze declined it: internal/core/eap carries no privacy mode, no anonymous-NAI identity path and no empty-certificate-list handling, so every obligation this sentence conditions on supporting or being configured for privacy is out of scope. | EAP-TLS servers supporting privacy MUST request a client certificate, and MUST be able to accept a client certificate offered by the EAP-TLS peer, in order to preserve interoperability with EAP- TLS peers that do not support privacy. |
2.1.5:2 |
duplicate-ofnever bound Ze the same obligation is already captured under another requirement id |
Section 2.1.5 restates the L-flag obligation that Section 3.1 states for the Request packet and RFC5216-3-1 already maps. | The L flag is set to indicate the presence of the four-octet TLS Message Length field, and MUST be set for the first fragment of a fragmented TLS message or set of messages. |
3.2:2 |
duplicate-ofnever bound Ze the same obligation is already captured under another requirement id |
Section 3.2 repeats the Section 3.1 sentence about octets outside the Length field word for word; RFC5216-3-4 maps it. | Octets outside the range of the Length field should be treated as Data Link Layer padding and MUST be ignored on reception. |
3.2:3 |
duplicate-ofnever bound Ze the same obligation is already captured under another requirement id |
Section 3.2 repeats the Section 3.1 L-bit sentence word for word; RFC5216-3-1 maps it. | The L bit (length included) is set to indicate the presence of the four-octet TLS Message Length field, and MUST be set for the first fragment of a fragmented TLS message or set of messages. |
Superseded
No document obsoletes RFC 5216, so its obligations are stated where they were written.