skip to content

Why must a RADIUS Access-Request carrying an EAP-Message attribute also carry Message-Authenticator (80)?

level: middleimportance: must knowfreq 44%

answer

  1. one direction had nothing keyed
  2. a request nonce proves nothing
  3. mandatory on every EAP-bearing packet
  4. attribute 80, Length 18
  5. MUST verify, SHOULD discard when absent

basics

~20 s

Nothing else in an EAP-bearing Access-Request proves the sender knows the shared secret: the Request Authenticator is a nonce and there is no User-Password (2) to bind it. Message-Authenticator (80) is that per-packet integrity check, and RFC 3579 requires it.

solid answer

~50 s

In an ordinary password login, the shared secret between the access device and the RADIUS server touches the packet through `User-Password` (2), whose value is hidden using the secret. An EAP-bearing `Access-Request (Code 1)` has no such attribute: the credential is inside the `EAP-Message` (79) payload, and the 16-octet Request Authenticator in the header is only a nonce. Without more, that packet carries nothing a receiver can check. RFC 3579 closes the gap by requiring `Message-Authenticator` (80) — an 18-octet attribute with a 16-octet keyed value — on any `Access-Request`, `Access-Accept (Code 2)`, `Access-Reject (Code 3)` or `Access-Challenge (Code 11)` that includes an `EAP-Message`. A receiver that sees the attribute MUST compute the value and silently discard the packet on a mismatch; an access device that receives an EAP-bearing reply with no `Message-Authenticator` at all SHOULD silently discard it.

code

pseudocode · 14 lines
pseudocode
function on_radius_packet_received(packet, shared_secret):

    if packet contains attribute EAP-Message (79):
        if packet has no attribute Message-Authenticator (80):
            discard silently          // RFC 3579 writes this one as SHOULD
            return

    if packet has attribute Message-Authenticator (80):
        expected = keyed_digest_over(packet, shared_secret)
        if expected is not equal to packet.Message-Authenticator:
            discard silently          // RFC 3579 writes this one as MUST
            return

    hand packet to the EAP conversation state machine

go deeper

for a junior

Recall that an EAP login adds a second, mandatory attribute alongside the EAP payload, and that its job is to prove the packet came from a device that holds the shared secret.

for a middle

Explain the gap: an Access-Request with no User-Password (2) has only a nonce in its header, so attribute 80 is what makes it checkable. Get the MUST and the SHOULD the right way round.

for a senior

Recognise the symptom in production: a mismatched secret gives a silent discard and a hanging login, not a clean reject, so read it as a hop problem rather than a policy decision.

for a principal

The angle worth owning is that the integrity of every EAP login in the estate reduces to one symmetric secret per device, which makes secret rotation and transport choice a programme rather than a setting.

## The gap this attribute fills RADIUS between an access device and a server rests on a **shared secret** configured on both sides — not the user's password, and not a per-session key. Different packet types put that secret to work in different places, and an EAP conversation happens to land in the one place where the base protocol left a hole. - In a password login, the secret hides `User-Password` (2). A server that decodes a plausible password has evidence the sender held the secret. - In every reply, the header's **Response Authenticator** is a digest computed over the reply *and* the secret, so the access device can check that the answer came from the server it asked. - In an `Access-Request (Code 1)`, the header's **Request Authenticator** is a nonce. It is unpredictable, which matters elsewhere, but it is not keyed by anything. An EAP-bearing `Access-Request` carries the credential inside `EAP-Message` (79), so it has no `User-Password` (2). That leaves a request whose every field a stranger could have written. `Message-Authenticator` (80) is the fix: a keyed value computed over the packet using the shared secret, carried as an attribute with Length 18 — two octets of Type and Length plus a 16-octet value. How that value is constructed, and what the strength of the secret behind it is worth, is the shared-secret story rather than this one; what belongs here is **when it has to be present, and what a receiver does about it.** ## The rule, at the strength the specification writes it RFC 3579 requires `Message-Authenticator` (80) on any packet that includes an `EAP-Message` (79) — that is all four of `Access-Request (Code 1)`, `Access-Accept (Code 2)`, `Access-Reject (Code 3)` and `Access-Challenge (Code 11)`. Both directions, every leg of the conversation, from the first Identity response to the final Success or Failure. The two receive-side rules are **not the same strength**, and quoting them as if they were is a common error: | Situation | What the specification says | |---|---| | A packet arrives **with** `Message-Authenticator` (80) | The receiver **MUST** calculate the correct value and **silently discard** the packet if it does not match | | An EAP-bearing reply arrives **without** `Message-Authenticator` (80) | The access device **SHOULD** silently discard it | "Silently" is load-bearing in both rows: there is no error reply to send, because a packet that fails this check is a packet whose origin is unproven. The sender simply never hears back and eventually runs its retransmission timer. ## What it does and does not give you 1. **Integrity and origin on the hop.** A receiver learns that whoever composed this packet held the shared secret for this device–server pair, and that the octets were not edited on the way. 2. **Coverage of the EAP payload.** Because the check spans the packet, the relayed EAP bytes are inside it — an on-path editor cannot swap an EAP Request for another one and leave the packet checkable. 3. **No confidentiality whatsoever.** The `EAP-Message` (79) payload, `User-Name` (1) and every other attribute travel in the clear. Anything hidden from a passive observer is hidden by the *method* inside EAP, or by a transport that encrypts the whole exchange — not by attribute 80. 4. **No protection of an inner credential from the endpoints.** The server that terminates a tunnelled method sees the inner exchange by definition; attribute 80 is about the hop, not about who is trusted at the ends. ## Why the reply direction needs it too A fair question is why an `Access-Challenge (Code 11)` needs `Message-Authenticator` (80) when replies already carry a Response Authenticator in the header. Two reasons stand up. The first is uniformity: every EAP-bearing packet in either direction carries the same check, so an implementation has one rule rather than a per-code table. The second is that the attribute is a keyed digest over the packet in its own right, so the reply's integrity does not rest solely on the header field and the request nonce it was computed against. ## The failure you will actually see When a device and a server disagree about the shared secret, an EAP login does not produce a clean rejection. The server computes a different value, the check fails, the packet is dropped without an answer, and the device retransmits until it gives up. On the peer's screen this looks like a login that hangs and then fails with nothing to read — the signature of a discard rule, not of a policy decision. That difference is the practical value of knowing the rule is *silent discard*: an authentication that produces no `Access-Reject (Code 3)` at all is usually not an authentication failure.

  • On which RADIUS packets does the requirement apply, and does it ever apply without EAP?
    It applies to `Access-Request (Code 1)`, `Access-Accept (Code 2)`, `Access-Reject (Code 3)` and `Access-Challenge (Code 11)` whenever the packet includes `EAP-Message` (79). It is also permitted on an `Access-Request` that carries no EAP at all, where it gives the same hop integrity to an ordinary login — EAP is what makes it mandatory, not what makes it possible.
  • An EAP-bearing Access-Challenge arrives at the access device with no Message-Authenticator (80) — what happens?
    RFC 3579 says the device SHOULD silently discard it: no error is returned and the peer is told nothing. The device's own retransmission timer then fires and it sends its `Access-Request (Code 1)` again. Note the strength — this rule is a SHOULD, while calculating and checking an attribute that *is* present is a MUST.
  • Does Message-Authenticator (80) keep the relayed EAP payload secret from a forwarding server?
    No. It is an integrity check, not encryption: the `EAP-Message` (79) octets, `User-Name` (1) and the rest of the attributes are readable by any party on the path. Secrecy for what is inside comes from the EAP method's own tunnel, or from encrypting the transport that carries RADIUS.

saying these in an interview costs you the question

  • Says Message-Authenticator (80) encrypts the EAP payload.
  • Thinks the Request Authenticator already proves knowledge of the secret.
  • Treats the attribute as optional when EAP-Message (79) is present.
  • Quotes the missing-on-reply rule as MUST when it is written SHOULD.
  • Expects an Access-Reject when the integrity check fails.
  • Confuses the shared secret with the end user's password.