RFC 4137

State Machines for Extensible Authentication Protocol (EAP) Peer and Authenticator

๐Ÿ“… published August 2005, from ietf

RFC 4137 ๐Ÿ’– State Machines for EAP Peer and Authenticator

Title: State Machines for Extensible Authentication Protocol (EAP) Peer and Authenticator ๐ŸŽ€

Authors:

  • J. Vollbrecht, Meetinghouse Data Communications ๐Ÿงš
  • P. Eronen, Nokia ๐Ÿ“ฑ
  • N. Petroni, University of Maryland ๐Ÿฆ‹
  • Y. Ohba, TARI ๐ŸŒธ

Category: Informational โœจ

Date: August 2005 ๐Ÿ“

Obsoletes: nothing ๐Ÿ™ˆ

Updates: nothing ๐Ÿ™ˆ


Status of This Memo โœจ

okay bestie, this memo is informational, so it's giving "here's some info for the Internet community" and not a standard of any kind ๐Ÿ’… Distribution of this memo is unlimited, so share it with everyone ๐ŸŒˆ

Copyright (C) The Internet Society (2005). โœจ

Abstract ๐Ÿ”ฎ

this doc describes a set of state machines for a few different EAP roles ๐Ÿ’œ There's the EAP peer, the EAP stand-alone authenticator (non-pass-through), the EAP backend authenticator (for use on Authentication, Authorization, and Accounting (AAA) servers), and the EAP full authenticator (for both local and pass-through). The vibe here is showing how EAP can be implemented to support deployment in either a peer/authenticator or peer/authenticator/AAA Server environment ๐ŸŽ€

The peer and stand-alone authenticator machines are illustrative of how the EAP protocol defined in RFC 3748 may be implemented ๐Ÿฆ‹ The backend and full/pass-through authenticators illustrate how EAP/AAA protocol support defined in RFC 3579 may be implemented ๐Ÿซถ Where there are differences, RFC 3748 and RFC 3579 are authoritative, so they win the argument ๐Ÿ‘‘

The state machines are based on the EAP "Switch" model ๐Ÿ”ฅ This model includes events and actions for the interaction between the EAP Switch and EAP methods. A brief description of the EAP "Switch" model is given in the Introduction section ๐Ÿงš

The state machine and associated model are informative only ๐Ÿ’… Implementations may achieve the same results using different methods, so there's room to get creative โœจ

Table of Contents ๐Ÿ“š

   1. Introduction: The EAP Switch Model ..............................3
   2. Specification of Requirements ...................................4
   3. Notational Conventions Used in State Diagrams ...................5
      3.1. Notational Specifics .......................................5
      3.2. State Machine Symbols ......................................7
      3.3. Document Authority .........................................8
   4. Peer State Machine ..............................................9
      4.1. Interface between Peer State Machine and Lower Layer .......9
      4.2. Interface between Peer State Machine and Methods ..........11
      4.3. Peer State Machine Local Variables ........................13
      4.4. Peer State Machine Procedures .............................14
      4.5. Peer State Machine States .................................15
   5. Stand-Alone Authenticator State Machine ........................17
      5.1. Interface between Stand-Alone Authenticator State
           Machine and Lower Layer ...................................17
      5.2. Interface between Stand-Alone Authenticator State
           Machine and Methods .......................................19
      5.3. Stand-Alone Authenticator State Machine Local Variables ...21
      5.4. EAP Stand-Alone Authenticator Procedures ..................22
      5.5. EAP Stand-Alone Authenticator States ......................24
   6. EAP Backend Authenticator ......................................26
      6.1. Interface between Backend Authenticator State
           Machine and Lower Layer ...................................26
      6.2. Interface between Backend Authenticator State
           Machine and Methods .......................................28
      6.3. Backend Authenticator State Machine Local Variables .......28
      6.4. EAP Backend Authenticator Procedures ......................28
      6.5. EAP Backend Authenticator States ..........................29
   7. EAP Full Authenticator .........................................29
      7.1. Interface between Full Authenticator State Machine
           and Lower Layer ...........................................30
      7.2. Interface between Full Authenticator State Machine
           and Methods ...............................................31
      7.3. Full Authenticator State Machine Local Variables ..........32
      7.4. EAP Full Authenticator Procedures .........................32
      7.5. EAP Full Authenticator States .............................32
   8. Implementation Considerations ..................................34
      8.1. Robustness ................................................34
      8.2. Method/Method and Method/Lower-Layer Interfaces ...........35
      8.3. Peer State Machine Interoperability with Deployed
           Implementations ...........................................35
   9. Security Considerations ........................................35
   10. Acknowledgements ..............................................36
   11. References ....................................................37
       11.1. Normative References ....................................37
       11.2. Informative References ..................................37

   Appendix. ASCII Versions of State Diagrams ........................38
       A.1.  EAP Peer State Machine (Figure 3) .......................38
       A.2.  EAP Stand-Alone Authenticator State Machine (Figure 4) ..41
       A.3.  EAP Backend Authenticator State Machine (Figure 5) ......44
       A.4.  EAP Full Authenticator State Machine (Figures 6 and 7) ..47

1. Introduction: The EAP Switch Model ๐Ÿฆ‹

okay so this doc offers a proposed state machine for RFCs [RFC3748] and [RFC3579] โœจ There are state machines for the peer, the stand-alone authenticator, a backend authenticator, and a full/pass-through authenticator ๐Ÿ’– Each state machine diagram comes with a description of its variables, functions, and states, and whenever possible the same notation is used in every machine, which is so clean ๐Ÿงผ

An EAP authentication consists of one or more EAP methods in sequence, followed by an EAP Success or EAP Failure sent from the authenticator to the peer ๐Ÿ”ฅ The EAP switches control negotiation of EAP methods and sequences of methods, so they're basically the main character here ๐Ÿ‘‘

      Peer             Peer  |  Authenticator       Auth
      Method                 |                      Method
              \              |                    /
               \             |                   /
                Peer         |             Auth
                EAP    <-----|---------->  EAP
                Switch       |             Switch

                    Figure 1: EAP Switch Model

At both the peer and the authenticator, one or more EAP methods exist ๐Ÿ’… The EAP switches select which methods each one is willing to use, and they negotiate between themselves to pick a method or a sequence of methods ๐Ÿซถ

Note that the methods may also have state machines ๐Ÿงš The details of these are outside the scope of this paper, so we're not diving in today ๐Ÿ™ˆ

          Peer  |  Authenticator              | Backend
                |              /   Local      |
                |             /    Method     |
          Peer  |        Auth                 |        Backend
          EAP  -|----->  EAP                  |    -->  EAP
         Switch |       Switch                |   /    Server
                |             \               |  /
                |              \ pass-through |
                |                             |

               Figure 2: EAP Pass-Through Model

The Full/Pass-Through state machine lets an NAS or edge device pass EAP Response messages to a backend server where the authentication method lives ๐ŸŒธ This paper includes a state machine for the EAP authenticator that supports both local and pass-through methods, plus a state machine for the backend authenticator sitting at the AAA server ๐Ÿ’œ A simple stand-alone authenticator is also provided to show how a basic, non-pass-through authenticator behaves โœจ

This document describes a set of state machines that can manage EAP authentication from the peer to an EAP method on the authenticator, or from the peer through the authenticator's pass-through method to the EAP method on the backend EAP server ๐ŸŽ€

Some environments where EAP is used, such as PPP, may support peer-to-peer operation ๐ŸŒˆ That means both parties act as peers and authenticators at the same time, in two simultaneous and independent EAP conversations ๐Ÿ”ฅ In this case, the implementation at each node has to demultiplex incoming EAP packets. EAP packets with code set to Response are delivered to the authenticator state machine, and EAP packets with code set to Request, Success, or Failure are delivered to the peer state machine ๐Ÿงš

The state diagrams in this document have been coordinated with the diagrams in [1X-2004] ๐Ÿฆ‹ The format of the diagrams is adapted from the format therein. The interface between the state machines defined here and the IEEE 802.1X-2004 state machines is also explained in Appendix F of [1X-2004] ๐Ÿ’–

2. Specification of Requirements ๐Ÿซถ

okay this part is the important one for the rules ๐Ÿ“ In this document, several words are used to signify the requirements of the specification, and they're often capitalized. The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" are to be interpreted as described in [RFC2119] ๐Ÿ”ฅ

3. Notational Conventions Used in State Diagrams โœจ

3.1. Notational Specifics ๐Ÿ“

okay so this part is all about how to READ the state diagrams, and it borrows its rules from [1X-2004], section 8.2.1. bestie, here's the whole vibe:

  • State diagrams show the protocol as a bunch of cooperating state machines. Each machine is a group of connected, mutually exclusive states, and only ONE state of each machine can be active at any given time ๐Ÿซถ
  • Every state is a rectangle split into two parts by a horizontal line. The top part has the state identifier in UPPERCASE letters. The bottom part has any procedures that run when you enter the state โœจ
  • Transitions are arrows, and the arrowhead shows the direction it can go. Labels on the arrows are the condition(s) that have to be met for the transition to happen. Every condition is an expression that evaluates to TRUE or FALSE, and if it's TRUE, the condition is met ๐Ÿ’…
  • UCT means unconditional transition, which always evaluates to TRUE ๐ŸŒธ
  • A global transition is one that can kick off from ANY of the states, as long as its condition is met. It's drawn with an open arrow, so no specific state is named as the origin. When a global transition's condition is met, it beats every other exit condition, including UCT ๐Ÿ‘‘
  • The special global condition BEGIN beats all the other global conditions. Once it's asserted, it stays asserted until every state block has run to the point where its variable assignments and other consequences stop changing ๐Ÿ”ฅ
  • When you enter a state, its procedures (if it has any) run exactly ONE time, in the order they appear on the page. Each action is atomic, which means a procedure finishes completely before the next one starts ๐Ÿฆ‹
  • No procedures run outside a state block. Only one state block's procedures run at a time, even if the conditions for blocks in different state machines are all satisfied. Every procedure in the running block finishes before any other block transitions and runs ๐Ÿ’œ
  • So from the outside, running a state block looks atomic compared to any other block, and the transition condition from the previous state is TRUE when the new block starts. The order that blocks in different machines run in is undefined, except for whatever their transition conditions force ๐Ÿงš
  • A variable set to a value in a state block keeps that value until a later block runs a procedure that changes it ๐Ÿ“
  • After all the procedures in a state finish, every exit condition for that state (including the global ones) gets checked over and over until one of them is met ๐ŸŒˆ
  • ELSE is the transition that fires if none of the other exit conditions from the state are met. In other words, ELSE is TRUE only when all the other exit conditions are FALSE ๐Ÿ™ˆ
  • If two or more exit conditions with the same precedence become TRUE at the same moment, the RFC says it's arbitrary which one causes the transition ๐ŸŽ€
  • If a state machine is too big for one diagram and gets split up, a transition between states on different diagrams is drawn as an exit arrow with dashed lines, plus a reference to the diagram holding the destination state. The destination diagram shows the incoming transition with dashed arrows and a dashed state box ๐Ÿ’–
  • In a split machine, any global transition that can cause entry into states on one of the diagrams counts as a potential exit condition for ALL the states of that machine, no matter which diagram the state boxes are on โญ
  • If a state diagram ever disagrees with the matching global transition tables or the text describing the state machine, the state diagram wins ๐Ÿ‘‘
  • The special symbols and operators in the diagrams mean what Section 3.2 says they mean. They're borrowed from C++ notation, from ISO/IEC 14882 ๐Ÿง
  • If a boolean variable is described as set, it's assigned TRUE. If it's described as reset or clear, it's FALSE ๐Ÿ’…

In addition to all that, there are two clarifications that are specific to this document. First, every boolean variable is initialized to FALSE before the state machine starts running ๐Ÿซถ. Second, this document has its own shorthand notation:

<variable> = <expression1> | <expression2> | ...

Running a statement like this leaves <variable> holding exactly ONE of the expressions. The logic that decides WHICH expression gets picked lives outside the state machine. It could be environmental, configurable, or based on another state machine, like the one for the method ๐Ÿฆ‹

3.2. State Machine Symbols ๐Ÿ”ฃ

Here's the symbol cheat sheet, bestie ๐Ÿ’–

  • ( ) forces the precedence of operators in Boolean expressions, and it also delimits the argument(s) of actions inside state boxes โœจ
  • ; is the terminating delimiter for actions inside state boxes. If a state box has several actions, they run in the order you'd read English text ๐Ÿ“–
  • = is the assignment action. The value of the expression on the right gets assigned to the variable on the left. If you chain it like a = b = X, the value after the right-most assignment operator gets assigned to ALL the variables sitting to the left of that right-most operator ๐ŸŒธ
  • ! is the logical NOT operator ๐Ÿ™ˆ
  • && is the logical AND operator ๐Ÿ’œ
  • || is the logical OR operator ๐ŸŒˆ
  • if...then... is the conditional action. If the Boolean expression after the "if" evaluates to TRUE, the action after the "then" runs ๐Ÿ“
  • { statement 1, ... statement N } is the compound statement. Braces group statements so they run together, as if they were one single statement ๐ŸŽ€
  • != is inequality. It evaluates to TRUE if the expression on the left is NOT equal in value to the one on the right ๐Ÿ’…
  • == is equality. It evaluates to TRUE if the expression on the left IS equal in value to the one on the right ๐Ÿ‘‘
  • > is greater than. It evaluates to TRUE if the value on the left is greater than the value on the right ๐Ÿ”ฅ
  • <= is less than or equal to. It evaluates to TRUE if the value on the left is less than OR equal to the value on the right ๐Ÿงš
  • ++ increments the preceding integer operator by 1 โญ
  • + is the arithmetic addition operator ๐Ÿซถ
  • & is the bitwise AND operator ๐Ÿฆ‹

3.3. Document Authority ๐Ÿ“œ

bestie, this is the tiebreaker section. If a state diagram conflicts with the matching global transition tables or the text describing the state machine, the state diagram takes precedence, same as before โœจ. And if any part of this document (text or diagram) doesn't match one of the related documents, like [RFC3748], [RFC3579], etc., then the OTHER document is the authoritative one and wins ๐Ÿ‘‘


4. Peer State Machine ๐ŸŽ€

Okay so this is the EAP peer state machine! The diagram comes first, and then there's an explanation of the primitives and procedures it uses, plus a clarification of the notation ๐Ÿ’–

            (see the .pdf version for missing diagram or
         refer to Appendix A.1 if reading the .txt version)

                  Figure 3: EAP Peer State Machine

4.1. Interface between Peer State Machine and Lower Layer ๐Ÿ”Œ

This is how the lower layer talks to the peer state machine ๐Ÿซถ. The lower layer hands over messages by storing the packet in eapReqData and setting the eapReq signal to TRUE. Here's the twist: despite what the signal's name suggests, the lower layer does NOT actually look inside the EAP packet. It could be a Success or Failure message instead of a Request ๐Ÿ™ˆ

Once the EAP peer state machine is done processing the message, it sets EITHER eapResp OR eapNoResp. If it sets eapResp, the matching response packet gets stored in eapRespData. The lower layer is the one responsible for actually transmitting that message ๐Ÿฆ‹

When the EAP peer state machine's authentication is complete, it sets eapSuccess or eapFailure to tell the lower layer whether authentication succeeded or failed ๐ŸŒˆ

4.1.1. Variables (Lower Layer to Peer) ๐Ÿ’œ

eapReq (boolean) โœจ : Set to TRUE in the lower layer and FALSE in the peer state machine. It indicates that a request is available in the lower layer.

eapReqData (EAP packet) ๐Ÿ“ฆ : Set in the lower layer when eapReq is set to TRUE. It holds the contents of the available request.

portEnabled (boolean) ๐Ÿ”“ : Tells the EAP peer state machine it should be ready for communication. It gets set to TRUE when the lower layer starts the EAP conversation. If at ANY point the communication port or session isn't available, portEnabled is set to FALSE and the state machine transitions to DISABLED. To avoid unnecessary resets, the lower layer MAY dampen link-down indications when it believes the link is only temporarily down and will soon be back up (see [RFC3748], Section 7.12). In that case, portEnabled may not always be equal to the "link up" flag of the lower layer ๐Ÿ’…

Hold up, I need to be careful here: the sentence above uses the word MAY in capitals, and the original has it in lowercase as "may" in "the lower layer may dampen". So for the record, the original says "may", not "MAY". Rewritten precisely below to avoid any mismatch.

portEnabled (boolean) ๐Ÿ”“ : Tells the EAP peer state machine it should be ready for communication. It gets set to TRUE when the lower layer starts the EAP conversation. If at any point the communication port or session isn't available, portEnabled is set to FALSE and the state machine transitions to DISABLED. To avoid unnecessary resets, the lower layer may dampen link-down indications when it believes the link is only temporarily down and will soon be back up (see [RFC3748], Section 7.12). In that case, portEnabled may not always be equal to the "link up" flag of the lower layer ๐Ÿ’…

idleWhile (integer) โณ : An outside timer that shows how much time is left before the peer times out while waiting for a valid request.

eapRestart (boolean) ๐Ÿ”„ : Indicates that the lower layer would like to restart authentication.

altAccept (boolean) ๐ŸŽ‰ : An alternate indication of success, as described in [RFC3748].

altReject (boolean) ๐Ÿ’€ : An alternate indication of failure, as described in [RFC3748].

4.1.2. Variables (peer to lower layer)

Okay, bestie, now we're flowing from the peer back down to the lower layer ๐Ÿ’–

eapResp (boolean) is set to TRUE in the peer state machine and FALSE in the lower layer. It's the "hey, send this" flag: it tells you a response is ready to go out ๐Ÿ“ฎ

eapNoResp (boolean) is set to TRUE in the peer state machine and FALSE in the lower layer. It means the request got processed, but there's nothing to send back. Quiet but valid ๐Ÿคซ

eapSuccess (boolean) is set to TRUE in the peer state machine and FALSE in the lower layer. It indicates the peer has reached the SUCCESS state. Iconic ๐Ÿ‘‘

eapFail (boolean) is set to TRUE in the peer state machine and FALSE in the lower layer. It indicates the peer has reached the FAILURE state. Rough, but we survive ๐Ÿฅ€

eapRespData (EAP packet) is set in the peer state machine when eapResp is set to TRUE. It's the actual EAP packet that's the response to send ๐Ÿ“ฆ

eapKeyData (EAP key) is set in the peer state machine when keying material becomes available, and it's set during the METHOD state โœจ Now, ngl, this doc does NOT define the structure of the "EAP key" type. That's getting defined in [Keying], so go check there ๐Ÿ”‘

eapKeyAvailable (boolean) is set to TRUE in the SUCCESS state if keying material is available. The actual key lives in eapKeyData ๐Ÿ—๏ธ

4.1.3. Constants

ClientTimeout (integer) is the configurable amount of time to wait for a valid request before aborting. It gets initialized by implementation-specific means, like a configuration setting โฐ

4.2. Interface between Peer State Machine and Methods

Here's the hype: this is where the state machine and EAP methods actually talk to each other ๐Ÿฆ‹ The inputs and outputs look like this:

IN: eapReqData (includes reqId)

OUT: ignore, eapRespData, allowNotifications, decision

IN/OUT: methodState, (method-specific state)

The following describes the interaction between the state machine and EAP methods.

If methodState==INIT, the method starts by initializing its own method-specific state ๐ŸŒฑ

Next, the method must decide whether to process the packet or to discard it silently. If the packet appears to have been sent by someone other than the legitimate authenticator (for instance, if message integrity check fails) and the method is capable of treating such situations as non-fatal, the method can set ignore=TRUE. In this case, the method should not modify any other variables. Main character energy: knowing when to just let it go ๐Ÿ™ˆ

If the method decides to process the packet, it behaves as follows:

  • It updates its own method-specific state.
  • If the method has derived keying material it wants to export, it stores the keying material to eapKeyData.
  • It creates a response packet (with the same identifier as the request) and stores it to eapRespData.
  • It sets ignore=FALSE.

Next, the method must update methodState and decision according to the following rules, and this part is so satisfying ๐Ÿ’…

methodState=CONT: The method always continues at this point, and the peer wants to continue it. The decision variable is always set to FAIL. Not the vibe for the decision, but the flow is clean โœจ

methodState=MAY_CONT: At this point, the authenticator can decide either to continue the method or to end the conversation. The decision variable tells us what to do if the conversation ends. If the current situation does not satisfy the peer's security policy (that is, if the authenticator now decides to allow access, the peer will not use it), set decision=FAIL. Otherwise, set decision=COND_SUCC. Ya gotta protect your boundaries ๐Ÿ›ก๏ธ

methodState=DONE: The method never continues at this point, or the peer sees no point in continuing it.

If either (a) the authenticator has informed us that it will not allow access, or (b) we're not willing to talk to this authenticator (e.g., our security policy is not satisfied), set decision=FAIL. Note that this state can occur even if the method still has additional messages left, if continuing it cannot change the peer's decision to success. Honestly, knowing when to call it is a whole skill ๐Ÿฅฒ

If both (a) the server has informed us that it will allow access, and the next packet will be EAP Success, and (b) we're willing to use this access, set decision=UNCOND_SUCC. Big W ๐Ÿ†

Otherwise, we do not know what the server's decision is, but we are willing to use the access if the server allows. In this case, set decision=COND_SUCC. Cautiously optimistic, iykyk ๐ŸŒˆ

Finally, the method must set the allowNotifications variable. If the new methodState is either CONT or MAY_CONT, and if the method specification does not forbid the use of Notification messages, set allowNotifications=TRUE. Otherwise, set allowNotifications=FALSE. The end of the method's to-do list, and it's giving organized ๐Ÿ“‹

4.3. Peer State Machine Local Variables

4.3.1. Long-Term (Maintained between Packets)

selectMethod (EAP type) is set in the GET_METHOD state. It's the method the peer believes is currently "in progress" ๐ŸŽฏ

methodState (enumeration) works exactly as described above ๐Ÿ”

lastId (integer) is 0-255 or NONE. It's set in the SEND_RESPONSE state and holds the EAP identifier value of the last request ๐Ÿชช

lastRespData (EAP packet) is set in the SEND_RESPONSE state. It's the EAP packet last sent from the peer ๐Ÿ“ค

decision (enumeration) works exactly as described above โš–๏ธ

NOTE: EAP type can be a normal type (0..253,255), or an extended type consisting of type 254, Vendor-Id, and Vendor-Type. Extended types are a whole other flavor, so don't sleep on them ๐Ÿ’€

4.3.2. Short-Term (Not Maintained between Packets)

rxReq (boolean) is set in the RECEIVED state. It indicates that the current received packet is an EAP request ๐Ÿ“ฅ

rxSuccess (boolean) is set in the RECEIVED state. It indicates that the current received packet is an EAP Success ๐ŸŽ‰

rxFailure (boolean) is set in the RECEIVED state. It indicates that the current received packet is an EAP Failure ๐Ÿ’”

reqId (integer) is set in the RECEIVED state. It's the identifier value associated with the current EAP request ๐Ÿช„

reqMethod (EAP type) is set in the RECEIVED state. It's the method type of the current EAP request ๐Ÿงฉ

ignore (boolean) is set in the METHOD state. It indicates whether the method has decided to drop the current packet ๐Ÿ—‘๏ธ

4.4. Peer State Machine Procedures

NOTE: For method procedures, the method uses its internal state in addition to the information provided by the EAP layer. The only arguments that are explicitly shown as inputs to the procedures are those provided to the method by EAP. Those inputs provided by the method's internal state remain implicit. Shh, the method's private diary stays private ๐Ÿคซ

parseEapReq() determines the code, identifier value, and type of the current request. In the case of a parsing error (e.g., the length field is longer than the received packet), rxReq, rxSuccess, and rxFailure will all be set to FALSE. The values of reqId and reqMethod may be undefined as a result. It returns three booleans, one integer, and one EAP type ๐Ÿ”

processNotify() processes the contents of a Notification Request, for instance by displaying it to the user or logging it. The return value is undefined ๐Ÿ“ฃ

buildNotify() creates the appropriate notification response. It returns an EAP packet ๐Ÿ’Œ

processIdentity() processes the contents of an Identity Request. The return value is undefined ๐Ÿชช

buildIdentity() creates the appropriate identity response. It returns an EAP packet ๐Ÿ™‹

m.check() is a method-specific procedure that tests for the validity of a message. It returns a boolean โœ…

m.process() is a method procedure that parses and processes a request for that method. It returns a methodState enumeration, a decision enumeration, and a boolean ๐Ÿ”ง

m.buildResp() is a method procedure that creates a response message. It returns an EAP packet ๐Ÿ“จ

m.getKey() is a method procedure that obtains key material for use by EAP or lower layers. It returns an EAP key ๐Ÿ—๏ธ

4.5. Peer State Machine States

DISABLED is reached whenever service from the lower layer is interrupted or unavailable. There's an immediate transition to INITIALIZE when the port becomes enabled. Dramatic exit, dramatic return ๐ŸŽญ

INITIALIZE initializes variables when the state machine is activated ๐ŸŒ…

IDLE is where the state machine spends most of its time, waiting for something to happen. Lowkey the most relatable state ๐Ÿ˜ด

RECEIVED is entered when an EAP packet is received. The packet header is parsed here ๐Ÿ“ฌ

GET_METHOD is entered when a request for a new type comes in. Either the correct method is started, or a Nak response is built ๐Ÿšฆ

METHOD is where the method processing happens. The request from the authenticator is processed, and an appropriate response packet is built ๐Ÿ› ๏ธ

SEND_RESPONSE signals the lower layer that a response packet is ready to be sent ๐Ÿš€

DISCARD signals the lower layer that the request was discarded, and no response packet will be sent at this time ๐Ÿซฅ

IDENTITY handles requests for the Identity method and builds a response ๐Ÿชž

NOTIFICATION handles requests for the Notification method and builds a response ๐Ÿ””

RETRANSMIT retransmits the previous response packet ๐Ÿ”

SUCCESS is a final state indicating success ๐Ÿ

FAILURE is a final state indicating failure. It's not the end of the world though ๐ŸŒท


5. Stand-Alone Authenticator State Machine

The following is a diagram of the stand-alone EAP authenticator state machine. This diagram should be used for those interested in a self-contained, or non-pass-through, authenticator. It also comes with an explanation of the primitives and procedures referenced in the diagram, plus a clarification of notation. Let's get into it ๐Ÿง

            (see the .pdf version for missing diagram or
         refer to Appendix A.2 if reading the .txt version)

        Figure 4: EAP Stand-Alone Authenticator State Machine

5.1. Interface between Stand-Alone Authenticator State Machine and Lower Layer

The lower layer presents messages to the EAP authenticator state machine by storing the packet in eapRespData and setting the eapResp signal to TRUE. Passing the baton ๐Ÿฅข

When the EAP authenticator state machine has finished processing the message, it sets one of the signals eapReq, eapNoReq, eapSuccess, and eapFail. If it sets eapReq, eapSuccess, or eapFail, the corresponding request (or success/failure) packet is stored in eapReqData. The lower layer is responsible for actually transmitting this message ๐Ÿ“ก

5.1.1. Variables (Lower Layer to Stand-Alone Authenticator)

eapResp (boolean) is set to TRUE in the lower layer and FALSE in the authenticator state machine. It indicates that an EAP response is available for processing ๐Ÿ“ฌ

eapRespData (EAP packet) is set in the lower layer when eapResp is set to TRUE. It's the EAP packet to be processed ๐Ÿ“ฆ

portEnabled (boolean) indicates that the EAP authenticator state machine should be ready for communication. It's set to TRUE when the EAP conversation is started by the lower layer. If at any point the communication port or session is not available, portEnabled is set to FALSE, and the state machine transitions to DISABLED. To avoid unnecessary resets, the lower layer may dampen link down indications when it believes that the link is only temporarily down and that it will soon be back up (see [RFC3748], Section 7.12). In this case, portEnabled may not always be equal to the "link up" flag of the lower layer. Patience is a virtue, and so is not panicking over a blip ๐Ÿซง

retransWhile (integer) is an outside timer used to indicate how long the authenticator has waited for a new (valid) response โณ

eapRestart (boolean) indicates that the lower layer would like to restart authentication ๐Ÿ”„

eapSRTT (integer) is the smoothed round-trip time. (See [RFC3748], Section 4.3.) ๐Ÿ“Š

eapRTTVAR (integer) is the round-trip time variation. (See [RFC3748], Section 4.3.) ๐Ÿ“ˆ

5.1.2. Variables (Stand-Alone Authenticator To Lower Layer) ๐Ÿ’Œ

eapReq (boolean)

Set to TRUE in the authenticator state machine, FALSE in the lower layer. This one says a brand-new EAP request is ready to go out ๐Ÿ“ฎโœจ

eapNoReq (boolean)

Set to TRUE in the authenticator state machine, FALSE in the lower layer. This means the latest response was processed, but there's no new request to send ๐Ÿคทโ€โ™€๏ธ

eapSuccess (boolean)

Set to TRUE in the authenticator state machine, FALSE in the lower layer. It tells everyone the state machine has reached the SUCCESS state ๐ŸŽ‰๐Ÿ‘‘

eapFail (boolean)

Set to TRUE in the authenticator state machine, FALSE in the lower layer. It tells everyone the state machine has reached the FAILURE state ๐Ÿ’€

eapTimeout (boolean)

Set to TRUE in the TIMEOUT_FAILURE state if the authenticator has hit its maximum number of retransmissions without getting a response back. Ghosted, fully ๐Ÿ˜ญ

eapReqData (EAP packet)

Set in the authenticator state machine when eapReq, eapSuccess, or eapFail is set to TRUE. This is the actual EAP request to be sent, or the success/failure packet ๐Ÿ“ฆ

eapKeyData (EAP key)

Set in the authenticator state machine when keying material becomes available, and set during the METHOD state ๐Ÿ”‘. Real talk: this document does not define the structure of the "EAP key" type. We expect it'll be defined in [Keying], so keep that in your back pocket ๐Ÿง 

eapKeyAvailable (boolean)

Set to TRUE in the SUCCESS state if keying material is available. The actual key lives in eapKeyData ๐Ÿ—๏ธ

5.1.3. Constants ๐Ÿงฎ

MaxRetrans (integer)

This is the configurable maximum for how many retransmissions get attempted before the whole thing aborts. Know your limit, bestie ๐Ÿ›‘

5.2. Interface between Stand-Alone Authenticator State Machine and Methods ๐Ÿค

Here's the handshake, the inputs and outputs and shared stuff:

IN: eapRespData, methodState

OUT: ignore, eapReqData

IN/OUT: currentId, (method-specific state), (policy)

This section spells out how the state machine and EAP methods talk to each other ๐Ÿ’ฌ

m.init (in: -, out: -)

When a method first kicks off, it must initialize its own method-specific state, and it can pull some info from Policy (like the identity) to do it ๐Ÿชช

m.buildReq (in: integer, out: EAP packet)

Next up, the method whips up a new EAP Request packet using the identifier value it's handed, and then updates its method-specific state to match ๐Ÿ“

m.getTimeout (in: -, out: integer or NONE)

The method can also drop a hint for the retransmission timeout using m.getTimeout โฐ

m.check (in: EAP packet, out: boolean)

When a new EAP Response arrives, the method has to decide first whether to process it or just silently toss it ๐Ÿ—‘๏ธ. If the packet looks like it didn't come from the legit peer (for example, a bad Message Integrity Check (MIC), which should never happen), the method can say so by returning FALSE. In that case, the method should not change its own method-specific state, bestie ๐Ÿšซ

m.process (in: EAP packet, out: -)

m.isDone (in: -, out: boolean)

m.getKey (in: -, out: EAP key or NONE)

Okay, so next the method processes the EAP Response and updates its own method-specific state ๐Ÿงฉ. Now it has two options: keep the convo going by sending another request, or wrap up that method ๐ŸŽ€

If the method wants to end the conversation, it:

  • Tells Policy about the outcome of the method, plus any other info that's relevant ๐Ÿ“ฃ
  • If the method has derived keying material it wants to export, it returns that from m.getKey() ๐Ÿ—๏ธ
  • Indicates it wants to end by returning TRUE from m.isDone() ๐Ÿ

Otherwise, the method keeps going by sending another request, like we covered earlier ๐Ÿ”

5.3. Stand-Alone Authenticator State Machine Local Variables ๐Ÿงธ

5.3.1. Long-Term (Maintained between Packets) ๐Ÿ—ƒ๏ธ

currentMethod (EAP type)

This is the EAP type, or IDENTITY, or NOTIFICATION ๐Ÿท๏ธ

currentId (integer)

This is either a value from 0-255, or NONE. It's usually updated in the PROPOSE_METHOD state, and it tracks the identifier value of the request that's currently outstanding ๐Ÿ”ข

methodState (enumeration)

As described above ๐ŸŒ€

retransCount (integer)

This gets reset in the SEND_REQUEST state and updated in the RETRANSMIT state. It's the current number of retransmissions so far ๐Ÿ”

lastReqData (EAP packet)

This gets set in the SEND_REQUEST state. It holds the EAP packet with the last request that was sent ๐Ÿ“ค

methodTimeout (integer)

This is a method-provided hint for a good retransmission timeout, or NONE ๐Ÿ’ญ

5.3.2. Short-Term (Not Maintained between Packets) โšก

rxResp (boolean)

Set in the RECEIVED state. It says the packet that just came in is an EAP response ๐Ÿ“ฅ

respId (integer)

Set in the RECEIVED state. This is the identifier from the current EAP response ๐Ÿชช

respMethod (EAP type)

Set in the RECEIVED state. This is the method type of the current EAP response ๐Ÿงพ

ignore (boolean)

Set in the METHOD state. It says whether the method decided to drop the current packet ๐Ÿ™ˆ

decision (enumeration)

Set in the SELECT_ACTION state. This temporarily stores the policy decision, which is to succeed, fail, or continue ๐Ÿค”

5.4. EAP Stand-Alone Authenticator Procedures ๐Ÿ› ๏ธ

NOTE: For method procedures, the method uses its internal state in addition to the information provided by the EAP layer. The only arguments that are explicitly shown as inputs to the procedures are those provided to the method by EAP. Those inputs provided by the method's internal state remain implicit.

calculateTimeout()

This works out the retransmission timeout, taking into account the retransmission count, round-trip time measurements, and the method-specific timeout hint (see [RFC3748], Section 4.3). It returns an integer โฑ๏ธ

parseEapResp()

This figures out the code, identifier value, and type of the current response. If there's a parsing error (like the length field being longer than the received packet), rxResp gets set to FALSE. The values of respId and respMethod may be undefined as a result ๐Ÿ’…. It returns a boolean, an integer, and an EAP type.

buildSuccess()

This creates an EAP Success Packet and returns an EAP packet ๐ŸŽ‰

buildFailure()

This creates an EAP Failure Packet and returns an EAP packet ๐Ÿ’”

nextId()

This determines the next identifier value to use, based on the previous one. It returns an integer ๐Ÿ”ข

Policy.update()

This updates all the variables related to internal policy state. The return value is undefined ๐Ÿง 

Policy.getNextMethod()

This determines the method that should be used at this point in the conversation, based on predefined policy. Here's the important part: Policy.getNextMethod() MUST comply with [RFC3748] (Section 2.1), which forbids the use of sequences of authentication methods within an EAP conversation. So if an authentication method has already been run within an EAP dialog, Policy.getNextMethod() MUST NOT propose another authentication method within that same EAP dialog. No sneaky back-to-back methods, periodt ๐Ÿšซ๐Ÿ”. It returns an EAP type.

Policy.getDecision()

This determines whether the policy will allow SUCCESS, allow FAIL, or is still undecided (CONTINUE). It returns a decision enumeration ๐Ÿ—ณ๏ธ

m.check()

This is the method-specific procedure that tests whether a message is valid. It returns a boolean โœ…

m.process()

This is the method procedure that parses and processes a response for that method. The return value is undefined ๐Ÿงฉ

m.init()

This is the method procedure that initializes state right before use. The return value is undefined ๐ŸŒฑ

m.reset()

This is the method procedure that signals the method is ending in the middle of, or before, completion. The return value is undefined ๐Ÿงน

m.isDone()

This is the method procedure that checks whether the method is complete. It returns a boolean ๐Ÿ

m.getTimeout()

This is the method procedure that works out a good timeout hint for that method. It returns an integer โณ

m.getKey()

This is the method procedure that gets key material for EAP or lower layers to use. It returns an EAP key ๐Ÿ”‘

m.buildReq()

This is the method procedure that produces the next request. It returns an EAP packet ๐Ÿ“จ

5.5. EAP Stand-Alone Authenticator States ๐ŸŒŸ

DISABLED

The authenticator stays disabled until the lower layer enables the port ๐Ÿšช

INITIALIZE

This initializes variables when the state machine gets activated ๐ŸŒฑ

IDLE

The state machine spends most of its time here, just waiting for something to happen ๐Ÿ˜ด

RECEIVED

This state gets entered when an EAP packet comes in. The packet header gets parsed here ๐Ÿ“ฌ

INTEGRITY_CHECK

This is a method state where the method verifies the integrity of the incoming packet from the peer ๐Ÿ”

METHOD_RESPONSE

This is a method state where the incoming packet gets processed ๐Ÿงƒ

METHOD_REQUEST

This is a method state where a new request gets formulated, if it's needed ๐Ÿ“

PROPOSE_METHOD

The authenticator decides which method to try next in the authentication here ๐ŸŽฏ

SELECT_ACTION

Between methods, the state machine rechecks whether its policy is satisfied. It either succeeds, fails, or stays undecided ๐Ÿง

SEND_REQUEST

This state tells the lower layer that a request packet is ready to be sent ๐Ÿ“ค

DISCARD

This state tells the lower layer that the response got discarded, and no new request packet will be sent at this time ๐Ÿ—‘๏ธ

NAK

This state processes Nak responses from the peer ๐Ÿ™…โ€โ™€๏ธ

RETRANSMIT

This state retransmits the previous request packet ๐Ÿ”

SUCCESS

This is a final state that means success ๐Ÿ†

FAILURE

This is a final state that means failure ๐Ÿ’”

TIMEOUT_FAILURE

This is a final state that means failure, because no response was received. Since no response came back, no new message (including a failure message) should be sent to the peer. This is different from the FAILURE state, where a message saying "failure" does get sent to the peer ๐Ÿ‘ป

6. EAP Backend Authenticator ๐Ÿ–ฅ๏ธ

When operating in pass-through mode, the authenticator is conceptually made of two parts: the part that passes packets through, and the backend that actually implements the EAP method ๐Ÿ”€. The diagram below shows a state machine for the backend part of this model when using a AAA server. It's basically the same as Figure 4, except the IDLE state doesn't include a retransmit, because with RADIUS, retransmit is handled by the NAS. There's also a new PICK_UP_METHOD state, plus a variable in the INITIALIZE state, so the Method can "pick up" a method that was started in a NAS ๐Ÿงท. The primitives and procedures in the diagram are explained below, and many of them are the same as before. Note that the "lower layer" here is some AAA protocol (for example, RADIUS) ๐ŸŒˆ

            (see the .pdf version for missing diagram or
         refer to Appendix A.3 if reading the .txt version)

          Figure 5: EAP Backend Authenticator State Machine

6.1. Interface between Backend Authenticator State Machine and Lower Layer ๐Ÿ”Œ

The lower layer hands messages to the EAP backend authenticator state machine by storing the packet in aaaEapRespData and setting the aaaEapResp signal to TRUE ๐Ÿ“ฉ.

When the EAP backend authenticator state machine finishes processing the message, it sets one of the signals aaaEapReq, aaaEapNoReq, aaaSuccess, and aaaFail. If it sets eapReq, eapSuccess, or eapFail, the matching request (or success/failure) packet gets stored in aaaEapReqData. The lower layer is the one responsible for actually transmitting that message ๐Ÿš€

6.1.1. Variables (AAA Interface to Backend Authenticator) ๐ŸŽ€

aaaEapResp (boolean) ๐Ÿ’Œ Set to TRUE in lower layer, FALSE in authenticator state machine. Usually it means an EAP response, stored in aaaEapRespData, is ready for the AAA server to process. If aaaEapRespData is set to NONE, that's the AAA server's cue to send the initial EAP request. ๐Ÿซถ

aaaEapRespData (EAP packet) ๐Ÿ“ฆ Set in lower layer when eapResp is set to TRUE. This is the EAP packet to be processed, or NONE. โœจ

backendEnabled (boolean) ๐Ÿ”Œ This one tells you there's a valid link to use for the communication. If the port ever becomes unavailable, backendEnabled gets set to FALSE and the state machine transitions to DISABLED. Literally the bouncer at the door of the whole operation ๐Ÿ’€

6.1.2. Variables (Backend Authenticator to AAA Interface) ๐Ÿฆ‹

aaaEapReq (boolean) ๐Ÿ“ค Set to TRUE in authenticator state machine, FALSE in lower layer. It's giving "a new EAP request is ready to go out" ๐Ÿ’…

aaaEapNoReq (boolean) ๐Ÿ™…โ€โ™€๏ธ Set to TRUE in authenticator state machine, FALSE in lower layer. This means the most recent response has been processed, but there's no new request to send. Quiet period, no drama ๐Ÿคซ

aaaSuccess (boolean) ๐ŸŽ‰ Set to TRUE in authenticator state machine, FALSE in lower layer. Indicates that the state machine has reached the SUCCESS state. Iconic behavior ๐Ÿ‘‘

aaaFail (boolean) ๐Ÿ’” Set to TRUE in authenticator state machine, FALSE in lower layer. Indicates that the state machine has reached the FAILURE state. Not the vibe, but we still track it ๐Ÿ˜ญ

aaaEapReqData (EAP packet) ๐Ÿ“จ Set in authenticator state machine when aaaEapReq, aaaSuccess, or aaaFail is set to TRUE. It holds the actual EAP request to be sent (or the success/failure).

aaaEapKeyData (EAP key) ๐Ÿ—๏ธ Set in authenticator state machine when keying material becomes available, during the METHOD_RESPONSE state. Ngl, this document doesn't define the structure of the type "EAP key" ๐Ÿ™ˆ We expect that it'll be defined in [Keying], so keep that one on your radar.

aaaEapKeyAvailable (boolean) ๐Ÿ”‘ Set to TRUE in the SUCCESS state if keying material is available. The actual key lives in aaaEapKeyData.

aaaMethodTimeout (integer) โฐ This is a method-provided hint for a suitable retransmission timeout, or NONE. (Quick side note: this hint is for the EAP retransmissions done by the pass-through authenticator, NOT for retransmissions of AAA packets. Don't mix them up, bestie ๐Ÿ’œ)


6.2. Interface between Backend Authenticator State Machine and Methods ๐Ÿงฉ

So the backend method interface is almost the same as the stand-alone one in Section 5.2. The only real difference is that some methods on the backend can "pick up" a conversation that was started by the pass-through. In other words, the EAP Request packet was sent by the pass-through, but the backend has to process the matching EAP Response. Usually only the Identity method does this, but others are possible ๐ŸŒธ

When picking up a conversation, m.initPickUp() gets called instead of m.init(). Next, m.process() has to examine eapRespData and update its own method-specific state so it matches what it would have been if it had actually sent the corresponding request. (Obviously this only works for methods that can figure out what the initial request contained. Identity and EAP-TLS are great examples ๐ŸคŒ)

After that, processing continues as described in Section 5.2. โœจ

6.3. Backend Authenticator State Machine Local Variables ๐Ÿ“

For definitions of the variables used in the Backend Authenticator, see Section 5.3. Easy, we're just borrowing them ๐Ÿซถ

6.4. EAP Backend Authenticator Procedures โš™๏ธ

Most of the backend authenticator's procedures were already defined in Section 5.4. This section covers the ones that don't exist in the stand-alone version, plus the ones that are defined differently ๐Ÿ’…

NOTE: For method procedures, the method uses its internal state in addition to the information provided by the EAP layer. The only arguments explicitly shown as inputs to the procedures are the ones provided to the method by EAP. Inputs coming from the method's internal state stay implicit. ๐Ÿซฃ

Policy.doPickUp() Notifies the policy that an already-chosen method is being picked up and will be completed. Returns a boolean. ๐Ÿ™Œ

m.initPickUp() Method procedure to initialize state when continuing from an already-started method. The return value is undefined. Shrug emoji energy ๐Ÿคทโ€โ™€๏ธ

6.5. EAP Backend Authenticator States ๐ŸŒˆ

Most of the backend authenticator's states were already defined in Section 5.5. This section covers the ones that don't exist in the stand-alone version, plus the ones that are defined differently ๐Ÿงš

PICK_UP_METHOD Sets an initial state for a method that's being continued but was started somewhere else. Think of it as a warm handoff ๐Ÿ“


7. EAP Full Authenticator ๐Ÿ‘‘

Okay so the next two diagrams show the state machine for a complete authenticator. The first one is identical to the stand-alone state machine (shown in Figure 4), with one difference: the SELECT_ACTION state gets an extra transition to PASSTHROUGH. The second diagram keeps most of the logic, except the four method states, and shows how things work once it goes into pass-through mode ๐Ÿฆ‹

The first diagram is pretty much a reproduction of the one above, with the added hooks for a transition to PASSTHROUGH mode ๐Ÿ’–

           (see the .pdf version for missing diagram or
        refer to Appendix A.4 if reading the .txt version)

      Figure 6: EAP Full Authenticator State Machine (Part 1)

The second diagram describes the functionality needed for an authenticator operating in pass-through mode. This section is the counterpart of the backend diagram above ๐ŸŒท

           (see the .pdf version for missing diagram or
        refer to Appendix A.4 if reading the .txt version)

      Figure 7: EAP Full Authenticator State Machine (Part 2)

7.1. Interface between Full Authenticator State Machine and Lower Layers ๐Ÿ”—

The full authenticator is unique because it interfaces with multiple lower layers in order to support pass-through mode. Its interface to the primary EAP transport layer is the same as in Section 5. The part below describes the interface to the second lower layer, which represents an interface to AAA. Note that there isn't necessarily a direct interaction between the EAP layer and the AAA layer, like in the case of [1X-2004] ๐Ÿซ 

7.1.1. Variables (AAA Interface to Full Authenticator) ๐ŸŽ€

aaaEapReq (boolean) ๐Ÿ“ฅ Set to TRUE in lower layer, FALSE in authenticator state machine. Indicates that a new EAP request is available from the AAA server.

aaaEapNoReq (boolean) ๐Ÿ›‘ Set to TRUE in lower layer, FALSE in authenticator state machine. Indicates that the most recent response has been processed, but there's no new request to send.

aaaSuccess (boolean) ๐Ÿฅณ Set to TRUE in lower layer. Indicates that the AAA backend authenticator has reached the SUCCESS state.

aaaFail (boolean) ๐Ÿฅ€ Set to TRUE in lower layer. Indicates that the AAA backend authenticator has reached the FAILURE state.

aaaEapReqData (EAP packet) ๐Ÿ“ฌ Set in the lower layer when aaaEapReq, aaaSuccess, or aaaFail is set to TRUE. It's the actual EAP request to be sent (or success/failure).

aaaEapKeyData (EAP key) ๐Ÿ” Set in the lower layer when keying material becomes available from the AAA server. Again, this document doesn't define the structure of "EAP key". We expect it'll be defined in [Keying] ๐Ÿ‘€

aaaEapKeyAvailable (boolean) ๐Ÿ—๏ธ Set to TRUE in the lower layer if keying material is available. The actual key is stored in aaaEapKeyData.

aaaMethodTimeout (integer) โŒ› Method-provided hint for a suitable retransmission timeout, or NONE. (Same reminder as before: this hint is for the EAP retransmissions done by the pass-through authenticator, NOT for retransmissions of AAA packets ๐Ÿ’œ)

7.1.2. Variables (full authenticator to AAA interface) ๐Ÿ“ค

aaaEapResp (boolean) ๐Ÿ™‹โ€โ™€๏ธ Set to TRUE in authenticator state machine, FALSE in the lower layer. Indicates that an EAP response is available for processing by the AAA server.

aaaEapRespData (EAP packet) ๐ŸŽ Set in authenticator state machine when eapResp is set to TRUE. It's the EAP packet to be processed.

aaaIdentity (EAP packet) ๐Ÿชช Set in authenticator state machine when an IDENTITY response is received. It makes that identity available to the AAA lower layer.

aaaTimeout (boolean) ๐Ÿ•ฐ๏ธ Set in AAA_IDLE if, after a configurable amount of time, there's no response from the AAA layer. The AAA layer in the NAS is itself alive and OK, but for some reason it hasn't received a valid Access-Accept/Reject indication from the backend. Lowkey a whole mystery ๐Ÿ˜ฉ

7.1.3. Constants ๐Ÿงฎ

Same as Section 5. Copy-paste bestie ๐Ÿ’…

7.2. Interface between Full Authenticator State Machine and Methods ๐Ÿค

Same as the stand-alone authenticator (Section 5.2) โœจ

7.3. Full Authenticator State Machine Local Variables ๐Ÿง 

Many of the full authenticator's variables were already defined in Section 5. This section covers the ones that don't exist in the stand-alone version, plus the ones that are defined differently ๐ŸŒˆ

7.3.1. Short-Term (Not Maintained between Packets) โณ

decision (enumeration) โš–๏ธ Set in SELECT_ACTION state. It temporarily stores the policy decision to succeed, fail, continue with a local method, or continue in pass-through mode.

7.4. EAP Full Authenticator Procedures ๐Ÿ› ๏ธ

All the procedures defined in Section 5 exist in the full version. On top of those, the following procedure is defined ๐Ÿ’–

getId() Determines the identifier value chosen by the AAA server for the current EAP request. The return value is an integer. ๐Ÿ”ข

7.5. EAP Full Authenticator States ๐Ÿšฆ

All the states defined in Section 5 exist in the full version. On top of those, the following states are defined ๐ŸŽ€

INITIALIZE_PASSTHROUGH Initializes variables when the pass-through portion of the state machine is activated. ๐Ÿš€

IDLE2 The state machine waits for a response from the primary lower layer, which transports EAP traffic from the peer. โณ

IDLE The state machine spends most of its time here, waiting for something to happen. Main character waiting energy ๐Ÿงโ€โ™€๏ธ

RECEIVED2 This state is entered when an EAP packet is received and the authenticator is in PASSTHROUGH mode. The packet header gets parsed here. ๐Ÿ“ฅ

AAA_REQUEST The incoming EAP packet is parsed for sending to the AAA server. ๐Ÿ“ค

AAA_IDLE Idle state that tells the AAA layer it has a response, then waits for a new request, a no-request signal, or success/failure. ๐Ÿซ 

AAA_RESPONSE State in which the request from the AAA interface is processed into an EAP request. ๐Ÿ”„

SEND_REQUEST2 This state signals the lower layer that a request packet is ready to be sent. ๐Ÿš€

DISCARD2 This state signals the lower layer that the response was discarded, and that no new request packet will be sent at this time. Bye bye ๐Ÿ—‘๏ธ

RETRANSMIT2 Retransmits the previous request packet. Encore! ๐Ÿ”

SUCCESS2 A final state indicating success. ๐Ÿ†

FAILURE2 A final state indicating failure. ๐Ÿ’€

TIMEOUT_FAILURE2 A final state indicating failure because no response has been received. Because no response was received, no new message (including failure) should be sent to the peer. Note that this is different from the FAILURE2 state, in which a message indicating failure is sent to the peer. Quiet exit, no texts sent ๐Ÿ™Š


8. Implementation Considerations โœจ

8.1. Robustness ๐Ÿ›ก๏ธ

okay so, implementations might need some extra protection for errors that have nothing to do with the protocol itself ๐Ÿ’… ๐Ÿซถ

For example, a state machine can get stuck in one state for ages doing its job without sending anything back ๐Ÿ˜ญ On a multithreaded system, the work can go on a separate thread so the implementation can still take action instead of freezing on that state for a long time (or forever, if it never finishes because a user never responds or an application callback has a bug) ๐Ÿ’€

These are the states where blocking can happen:

  • IDENTITY state in the peer state machine. Handling an Identity request can take a while when the user has to type in an identity. The user may never type one ๐Ÿ™ˆ An implementation may add an extra transition from IDENTITY state to FAILURE state, so authentication fails if no identity shows up before the ClientTimeout timer expires โœจ
  • METHOD state in the peer state machine and METHOD_RESPONSE state in the authenticator state machines. Method-specific work can take some time in these states ๐ŸŒธ An implementation may add extra transitions from METHOD state and METHOD_RESPONSE state to FAILURE or TIMEOUT_FAILURE state, so authentication fails if the method doesn't give back a result before the methodTimeout timer expires ๐Ÿฆ‹

8.2. Method/Method and Method/Lower-Layer Interfaces ๐Ÿงš

implementations may define extra interfaces to pass method-specific info between methods and lower layers ๐ŸŽ€ Those interfaces are beyond the scope of this document, so we're leaving them alone ๐Ÿ’–

8.3. Peer State Machine Interoperability with Deployed Implementations ๐ŸŒˆ

ngl this one is a little messy ๐Ÿ˜ญ A bunch of EAP authenticator implementations out there, mostly in RADIUS authentication servers, have been seen incrementing the Identifier field incorrectly when they generate EAP Success and EAP Failure packets. That's against the MUST requirement in RFC 3748 section 4.2 ๐Ÿ™…โ€โ™€๏ธ The peer state machine is based on RFC 3748, so it will discard those EAP Success and EAP Failure packets ๐Ÿ’”

As a workaround for this possible interoperability issue with existing implementations, the conditions for peer state machine transitions from RECEIVED state to SUCCESS and FAILURE states MAY be changed from "(reqId == lastId)" to "((reqId == lastId) || (reqId == (lastId + 1) & 255))". Heads up though: because this behavior doesn't conform to RFC 3748, such a workaround is not recommended. If it's included, it should be implemented as an optional workaround that can be disabled ๐Ÿšฉ

9. Security Considerations ๐Ÿ”

this document's whole intent is to describe the EAP state machine fully ๐Ÿง  So any security concerns in here are likely a reflection of security concerns with EAP itself ๐Ÿซ 

An accurate state machine can help cut down on implementation errors โœจ Even so, [RFC3748] is still the normative protocol description, and this state machine should just help with that ๐Ÿ’œ

As noted in [RFC3748], some security concerns come up because of these EAP packets:

  1. EAP-Request/Response Identity
  2. EAP-Response/NAK
  3. EAP-Success/Failure

Because these packets aren't cryptographically protected by themselves, an attacker can modify or insert them without being immediately detected by the peer or the authenticator ๐Ÿ˜ฑ

Following the Figure 3 specification, an attacker can cause denial of service by:

  • Sending an EAP-Failure to the peer before the peer has started an EAP authentication method. As long as the peer hasn't modified the methodState variable (initialized to NONE), the peer MUST accept an EAP-Failure ๐Ÿ’€
  • Forcing the peer into endless EAP-Request/Response Identity exchanges before it has started an EAP authentication method. As long as the peer hasn't modified the selectedMethod variable (initialized to NONE), the peer MUST accept an EAP-Request/Identity and respond to it with an EAP-Response/Identity ๐Ÿ™ƒ

Following the Figure 4 specification, an attacker can also cause denial of service by:

  • Sending a NAK to the authenticator after the authenticator first proposes an EAP authentication method to the peer. When the methodState variable has the value PROPOSED, the authenticator is obliged to process a NAK that is received in response to its first packet of an EAP authentication method ๐Ÿ˜ค

There MAY be some cases where it's desired to prevent these attacks ๐Ÿ›‘ You can do that by changing the initial values of some variables in the EAP state machines. However, such modifications are NOT RECOMMENDED โš ๏ธ

There's a trade-off between blocking these denial-of-service attacks and being able to deal with EAP peers and authenticators in general ๐Ÿฅบ For example, if a NAK gets ignored when it's sent to the authenticator right after it proposed an EAP authentication method to the peer, then a legitimate peer that can't or won't process the proposed method would just fail, with no chance to negotiate another EAP method ๐Ÿ˜ญ

10. Acknowledgements ๐Ÿ’Œ

the work in this document was done as part of the EAP Design Team ๐Ÿ‘ฏโ€โ™€๏ธ It was done mainly by Nick Petroni, John Vollbrecht, Pasi Eronen, and Yoshihiro Ohba ๐Ÿ‘‘ Nick started this work with Bryan Payne and Chuk Seng at the University of Maryland. John Vollbrecht of Meetinghouse Data Communications started on his own with help from Dave Spence at Interlink Networks. John and Nick teamed up to make one shared document, and then Pasi Eronen of Nokia joined in, who made huge contributions to building coherent state machines ๐Ÿ™Œ Yoshihiro Ohba of Toshiba also joined, who insisted on including pass-through documentation and gave major support for understanding implementation issues ๐Ÿซถ

The design team also gave tons of response and conversation, especially Jari Arkko of Ericsson and Bernard Aboba of Microsoft, plus the rest of the team ๐ŸŽ€ It was reviewed by IEEE 802.1 too, and got input from Jim Burns of Meetinghouse and Paul Congdon of Hewlett Packard โœจ

11. References ๐Ÿ“š

11.1. Normative References

[RFC2119]   Bradner, S., "Key words for use in RFCs to Indicate
            Requirement Levels", BCP 14, RFC 2119, March 1997.

[RFC3579]   Aboba, B. and P. Calhoun, "RADIUS (Remote Authentication
            Dial In User Service) Support For Extensible
            Authentication Protocol (EAP)", RFC 3579, September 2003.

[RFC3748]   Aboba, B., Blunk, L., Vollbrecht, J., Carlson, J., and H.
            Levkowetz, Ed., "Extensible Authentication Protocol
            (EAP)", RFC 3748, June 2004.

11.2. Informative References

[Keying]    Aboba, B., Simon, D., Arkko, J., Eronen, P., Levkowetz,
            H., "Extensible Authentication Protocol (EAP) Key
            Management Framework", Work in Progress, July 2005.

[1X-2004]   Institute of Electrical and Electronics Engineers,
            "Standard for Local and Metropolitan Area Networks:
            Port-Based Network Access Control", IEEE 802.1X-2004,
            December 2004.

Appendix A. ASCII versions of state diagrams ๐ŸŽจ

this appendix has the state diagrams in ASCII format ๐Ÿ–ฅ๏ธ Please use the PDF version whenever you can, because it's way easier to understand ๐Ÿ’–

The notation goes like this: the state name and the pseudocode that runs when entering it are on the left, and outgoing transitions with their conditions are on the right ๐Ÿฆ‹

A.1. EAP Peer State Machine (Figure 3)

---------------------------------------------------------------------
(global transitions)         |      !portEnabled      |      DISABLED
                             |------------------------+--------------
                             |     eapRestart &&      |    INITIALIZE
                             |      portEnabled       |
-----------------------------+------------------------+--------------
DISABLED                     |      portEnabled       |    INITIALIZE
-----------------------------+------------------------+--------------
INITIALIZE                   |                        |
                             |                        |
selectedMethod = NONE        |                        |
methodState = NONE           |                        |
allowNotifications = TRUE    |                        |
decision = FAIL              |          UCT           |          IDLE
idleWhile = ClientTimeout    |                        |
lastId = NONE                |                        |
eapSuccess = FALSE           |                        |
eapFail = FALSE              |                        |
eapKeyData = NONE            |                        |
eapKeyAvailable = FALSE      |                        |
eapRestart = FALSE           |                        |
-----------------------------+------------------------+--------------
IDLE                         |         eapReq         |      RECEIVED
                             |------------------------+--------------
                             |     (altAccept &&      |
                             |  decision != FAIL) ||  |
                             |   (idleWhile == 0 &&   |       SUCCESS
                             |      decision ==       |
                             |      UNCOND_SUCC)      |
                             |------------------------+--------------
                             |------------------------+--------------
                             |      altReject ||      |
                             |   (idleWhile == 0 &&   |
                             |      decision !=       |
                             |    UNCOND_SUCC) ||     |       FAILURE
                             |     (altAccept &&      |
                             | methodState != CONT && |
                             |   decision == FAIL)    |
-----------------------------+------------------------+--------------
RECEIVED                     |        rxReq &&        |        METHOD
                             |  (reqId != lastId) &&  |
(rxReq,rxSuccess,rxFailure,  |     (reqMethod ==      |
  reqId,reqMethod) =         |   selectedMethod) &&   |
  parseEapReq(eapReqData)    | (methodState != DONE)  |
                             |------------------------+--------------
                             |        rxReq &&        |
                             |  (reqId != lastId) &&  |
                             |   (selectedMethod ==   |
                             |        NONE) &&        |    GET_METHOD
                             |     (reqMethod !=      |
                             |      IDENTITY) &&      |
                             |     (reqMethod !=      |
                             |     NOTIFICATION)      |
                             |------------------------+--------------
                             |        rxReq &&        |
                             |  (reqId != lastId) &&  |
                             |   (selectedMethod ==   |      IDENTITY
                             |        NONE) &&        |
                             |     (reqMethod ==      |
                             |       IDENTITY)        |
                             |------------------------+--------------
                             |        rxReq &&        |
                             |  (reqId != lastId) &&  |
                             |   (reqMethod ==        |  NOTIFICATION
                             |    NOTIFICATION) &&    |
                             |   allowNotifications   |
                             |------------------------+--------------
                             |        rxReq &&        |    RETRANSMIT
                             |   (reqId == lastId)    |
                             |------------------------+--------------
                             |      rxSuccess &&      |
                             |  (reqId == lastId) &&  |       SUCCESS
                             |   (decision != FAIL)   |
                             |------------------------+--------------
                             |------------------------+--------------
                             | (methodState!=CONT) && |
                             |     ((rxFailure &&     |
                             |      decision !=       |
                             |    UNCOND_SUCC) ||     |       FAILURE
                             |     (rxSuccess &&      |
                             | decision == FAIL)) &&  |
                             |   (reqId == lastId)    |
                             |------------------------+--------------
                             |          else          |       DISCARD
-----------------------------+------------------------+--------------
METHOD                       |                        |
                             |                        |
ignore = m.check(eapReqData) |         ignore         |       DISCARD
if (!ignore) {               |                        |
  (methodState, decision,    |                        |
  allowNotifications) =      |------------------------+--------------
  m.process(eapReqData)      |                        |
  /* methodState is CONT,    |                        |
     MAY_CONT, or DONE */    | (methodState==DONE) && |       FAILURE
  /* decision is FAIL,       |   (decision == FAIL)   |
     COND_SUCC, or           |                        |
     UNCOND_SUCC */          |                        |
  eapRespData =              |------------------------+--------------
    m.buildResp(reqId)       |                        |
  if (m.isKeyAvailable())    |          else          | SEND_RESPONSE
    eapKeyData = m.getKey()  |                        |
}                            |                        |
-----------------------------+------------------------+--------------
GET_METHOD                   |                        |
                             |   selectedMethod ==    |
if (allowMethod(reqMethod)) {|       reqMethod        |        METHOD
  selectedMethod = reqMethod |                        |
  methodState = INIT         |                        |
} else {                     |------------------------+--------------
  eapRespData =              |                        |
    buildNak(reqId)          |          else          | SEND_RESPONSE
}                            |                        |
-----------------------------+------------------------+--------------
IDENTITY                     |                        |
                             |                        |
processIdentity(eapReqData)  |          UCT           | SEND_RESPONSE
eapRespData =                |                        |
  buildIdentity(reqId)       |                        |
-----------------------------+------------------------+--------------
-----------------------------+------------------------+--------------
NOTIFICATION                 |                        |
                             |                        |
processNotify(eapReqData)    |          UCT           | SEND_RESPONSE
eapRespData =                |                        |
  buildNotify(reqId)         |                        |
-----------------------------+------------------------+--------------
RETRANSMIT                   |                        |
                             |          UCT           | SEND_RESPONSE
eapRespData = lastRespData   |                        |
-----------------------------+------------------------+--------------
DISCARD                      |                        |
                             |          UCT           |          IDLE
eapReq = FALSE               |                        |
eapNoResp = TRUE             |                        |
-----------------------------+------------------------+--------------
SEND_RESPONSE                |                        |
                             |                        |
lastId = reqId               |                        |
lastRespData = eapRespData   |          UCT           |          IDLE
eapReq = FALSE               |                        |
eapResp = TRUE               |                        |
idleWhile = ClientTimeout    |                        |
-----------------------------+------------------------+--------------
SUCCESS                      |                        |
                             |                        |
if (eapKeyData != NONE)      |                        |
  eapKeyAvailable = TRUE     |                        |
eapSuccess = TRUE            |                        |
-----------------------------+------------------------+--------------
FAILURE                      |                        |
                             |                        |
eapFail = TRUE               |                        |
---------------------------------------------------------------------
                                Figure 8

okay so that's the whole peer decision tree, and honestly it's giving main character energy ๐Ÿงš๐Ÿ’œ

A.2. EAP Stand-Alone Authenticator State Machine (Figure 4) ๐Ÿฆ‹

---------------------------------------------------------------------
(global transitions)          |    !portEnabled     |        DISABLED
                              |---------------------+----------------
                              |    eapRestart &&    |      INITIALIZE
                              |     portEnabled     |
------------------------------+---------------------+----------------
DISABLED                      |     portEnabled     |      INITIALIZE
------------------------------+---------------------+----------------
------------------------------+---------------------+----------------
INITIALIZE                    |                     |
                              |                     |
currentId = NONE              |                     |
eapSuccess = FALSE            |                     |
eapFail = FALSE               |         UCT         |   SELECT_ACTION
eapTimeout = FALSE            |                     |
eapKeyData = NONE             |                     |
eapKeyAvailable = FALSE       |                     |
eapRestart = FALSE            |                     |
------------------------------+---------------------+----------------
IDLE                          |                     |
                              |  retransWhile == 0  |      RETRANSMIT
retransWhile =                |                     |
  calculateTimeout(           |---------------------+----------------
   retransCount, eapSRTT,     |       eapResp       |        RECEIVED
   eapRTTVAR, methodTimeout)  |                     |
------------------------------+---------------------+----------------
RETRANSMIT                    |                     |
                              |   retransCount >    | TIMEOUT_FAILURE
retransCount++                |     MaxRetrans      |
if (retransCount<=MaxRetrans){|                     |
  eapReqData = lastReqData    |---------------------+----------------
  eapReq = TRUE               |        else         |            IDLE
}                             |                     |
------------------------------+---------------------+----------------
RECEIVED                      |      rxResp &&      |
                              |     (respId ==      |
(rxResp,respId,respMethod)=   |    currentId) &&    |
  parseEapResp(eapRespData)   | (respMethod == NAK  |
                              |         ||          |             NAK
                              |    respMethod ==    |
                              |  EXPANDED_NAK) &&   |
                              |   (methodState ==   |
                              |      PROPOSED)      |
                              |---------------------+----------------
                              |      rxResp &&      |
                              |     (respId ==      |
                              |    currentId) &&    | INTEGRITY_CHECK
                              |   (respMethod ==    |
                              |   currentMethod)    |
                              |---------------------+----------------
                              |        else         |         DISCARD
------------------------------+---------------------+----------------
------------------------------+---------------------+----------------
NAK                           |                     |
                              |         UCT         |   SELECT_ACTION
m.reset()                     |                     |
Policy.update(<...>)          |                     |
------------------------------+---------------------+----------------
SELECT_ACTION                 | decision == FAILURE |         FAILURE
                              |                     |
decision =                    |---------------------+----------------
  Policy.getDecision()        | decision == SUCCESS |         SUCCESS
/* SUCCESS, FAILURE, or       |---------------------+----------------
   CONTINUE */                |        else         |  PROPOSE_METHOD
------------------------------+---------------------+----------------
INTEGRITY_CHECK               |       ignore        |         DISCARD
                              |---------------------+----------------
ignore = m.check(eapRespData) |       !ignore       | METHOD_RESPONSE
------------------------------+---------------------+----------------
METHOD_RESPONSE               |                     |
                              | methodState == END  |   SELECT_ACTION
m.process(eapRespData)        |                     |
if (m.isDone()) {             |                     |
  Policy.update(<...>)        |---------------------+----------------
  eapKeyData = m.getKey()     |                     |
  methodState = END           |        else         |  METHOD_REQUEST
} else                        |                     |
  methodState = CONTINUE      |                     |

more of this coming up, bestie ๐Ÿ’…๐ŸŽ€

------------------------------+---------------------+----------------
PROPOSE_METHOD                |                     |
                              |                     |
currentMethod =               |                     |
  Policy.getNextMethod()      |                     |
m.init()                      |         UCT         |  METHOD_REQUEST
if (currentMethod==IDENTITY |||                     |
  currentMethod==NOTIFICATION)|                     |
  methodState = CONTINUE      |                     |
else                          |                     |
  methodState = PROPOSED      |                     |
------------------------------+---------------------+----------------
METHOD_REQUEST                |                     |
                              |                     |
currentId = nextId(currentId) |         UCT         |    SEND_REQUEST
eapReqData =                  |                     |
  m.buildReq(currentId)       |                     |
methodTimeout = m.getTimeout()|                     |
------------------------------+---------------------+----------------
------------------------------+---------------------+----------------
DISCARD                       |                     |
                              |         UCT         |            IDLE
eapResp = FALSE               |                     |
eapNoReq = TRUE               |                     |
------------------------------+---------------------+----------------
SEND_REQUEST                  |                     |
                              |                     |
retransCount = 0              |         UCT         |            IDLE
lastReqData = eapReqData      |                     |
eapResp = FALSE               |                     |
eapReq = TRUE                 |                     |
------------------------------+---------------------+----------------
TIMEOUT_FAILURE               |                     |
                              |                     |
eapTimeout = TRUE             |                     |
------------------------------+---------------------+----------------
FAILURE                       |                     |
                              |                     |
eapReqData =                  |                     |
  buildFailure(currentId)     |                     |
eapFail = TRUE                |                     |
------------------------------+---------------------+----------------
SUCCESS                       |                     |
                              |                     |
eapReqData =                  |                     |
  buildSuccess(currentId)     |                     |
if (eapKeyData != NONE)       |                     |
  eapKeyAvailable = TRUE      |                     |
eapSuccess = TRUE             |                     |
---------------------------------------------------------------------
                                Figure 9

A.3. EAP Backend Authenticator State Machine (Figure 5) ๐Ÿฆ‹

Here's the backend authenticator's state machine, and it's giving clean little transitions all the way down ๐Ÿ’…

---------------------------------------------------------------------
(global transitions)          |   !backendEnabled   |        DISABLED
------------------------------+---------------------+----------------
DISABLED                      |  backendEnabled &&  |      INITIALIZE
                              |     aaaEapResp      |
------------------------------+---------------------+----------------
------------------------------+---------------------+----------------
INITIALIZE                    |       !rxResp       |   SELECT_ACTION
                              |---------------------+----------------
currentMethod = NONE          |      rxResp &&      |
(rxResp,respId,respMethod)=   | (respMethod == NAK  |
  parseEapResp(aaaEapRespData)|         ||          |             NAK
if (rxResp)                   |    respMethod ==    |
  currentId = respId          |    EXPANDED_NAK)    |
else                          |---------------------+----------------
  currentId = NONE            |        else         |  PICK_UP_METHOD
------------------------------+---------------------+----------------
PICK_UP_METHOD                |                     |
                              |                     |
if (Policy.doPickUp(          |  currentMethod ==   |   SELECT_ACTION
    respMethod)) {            |        NONE         |
  currentMethod = respMethod  |                     |
  m.initPickUp()              |---------------------+----------------
}                             |        else         | METHOD_RESPONSE
------------------------------+---------------------+----------------
IDLE                          |     aaaEapResp      |        RECEIVED
------------------------------+---------------------+----------------
RECEIVED                      |      rxResp &&      |
                              |     (respId ==      |
(rxResp,respId,respMethod)=   |    currentId) &&    |
  parseEapResp(aaaEapRespData)| (respMethod == NAK  |
                              |         ||          |
                              |    respMethod ==    |             NAK
                              |  EXPANDED_NAK) &&   |
                              |   (methodState ==   |
                              |      PROPOSED)      |
                              |---------------------+----------------
                              |      rxResp &&      |
                              |     (respId ==      |
                              |    currentId) &&    | INTEGRITY_CHECK
                              |   (respMethod ==    |
                              |   currentMethod)    |
                              |---------------------+----------------
                              |        else         |         DISCARD
------------------------------+---------------------+----------------
NAK                           |                     |
                              |         UCT         |   SELECT_ACTION
m.reset()                     |                     |
Policy.update(<...>)          |                     |
------------------------------+---------------------+----------------
------------------------------+---------------------+----------------
SELECT_ACTION                 | decision == FAILURE |         FAILURE
                              |                     |
decision =                    |---------------------+----------------
  Policy.getDecision()        | decision == SUCCESS |         SUCCESS
/* SUCCESS, FAILURE, or       |---------------------+----------------
   CONTINUE */                |        else         |  PROPOSE_METHOD
------------------------------+---------------------+----------------
INTEGRITY_CHECK               |       ignore        |         DISCARD
                              |                     |
ignore =                      |---------------------+----------------
  m.check(aaaEapRespData)     |       !ignore       | METHOD_RESPONSE
------------------------------+---------------------+----------------
METHOD_RESPONSE               |                     |
                              | methodState == END  |   SELECT_ACTION
m.process(aaaEapRespData)     |                     |
if (m.isDone()) {             |                     |
  Policy.update(<...>)        |---------------------+----------------
  aaaEapKeyData = m.getKey()  |                     |
  methodState = END           |        else         |  METHOD_REQUEST
} else                        |                     |
  methodState = CONTINUE      |                     |
------------------------------+---------------------+----------------
PROPOSE_METHOD                |                     |
                              |                     |
currentMethod =               |                     |
  Policy.getNextMethod()      |                     |
m.init()                      |         UCT         |  METHOD_REQUEST
if (currentMethod==IDENTITY |||                     |
  currentMethod==NOTIFICATION)|                     |
  methodState = CONTINUE      |                     |
else                          |                     |
  methodState = PROPOSED      |                     |
------------------------------+---------------------+----------------
METHOD_REQUEST                |                     |
                              |                     |
currentId = nextId(currentId) |                     |
aaaEapReqData =               |         UCT         |    SEND_REQUEST
  m.buildReq(currentId)       |                     |
aaaMethodTimeout =            |                     |
  m.getTimeout()              |                     |
------------------------------+---------------------+----------------
DISCARD                       |                     |
                              |         UCT         |            IDLE
aaaEapResp = FALSE            |                     |
aaaEapNoReq = TRUE            |                     |
------------------------------+---------------------+----------------
------------------------------+---------------------+----------------
SEND_REQUEST                  |                     |
                              |         UCT         |            IDLE
aaaEapResp = FALSE            |                     |
aaaEapReq = TRUE              |                     |
------------------------------+---------------------+----------------
FAILURE                       |                     |
                              |                     |
aaaEapReqData =               |                     |
  buildFailure(currentId)     |                     |
aaaEapFail = TRUE             |                     |
------------------------------+---------------------+----------------
SUCCESS                       |                     |
                              |                     |
aaaEapReqData =               |                     |
  buildSuccess(currentId)     |                     |
if (aaaEapKeyData != NONE)    |                     |
  aaaEapKeyAvailable = TRUE   |                     |
aaaEapSuccess = TRUE          |                     |
---------------------------------------------------------------------
                               Figure 10

A.4. EAP Full Authenticator State Machine (Figures 6 and 7) ๐ŸŒˆ

okay so this state machine has every single state from the EAP stand-alone authenticator state machine, except the SELECT_ACTION state gets swapped out for this glow-up version ๐Ÿ’–

---------------------------------------------------------------------
SELECT_ACTION                 | decision == FAILURE |         FAILURE
                              |                     |
decision =                    |---------------------+----------------
  Policy.getDecision()        | decision == SUCCESS |         SUCCESS
/* SUCCESS, FAILURE, CONTINUE,|---------------------+----------------
   or PASSTHROUGH */          |     decision ==     |     INITIALIZE_
                              |     PASSTHROUGH     |     PASSTHROUGH
                              |---------------------+----------------
                              |        else         |  PROPOSE_METHOD
---------------------------------------------------------------------
                               Figure 11

and then we get these brand new states, bestie ๐Ÿงšโœจ

---------------------------------------------------------------------
INITIALIZE_PASSTHROUGH        |  currentId != NONE  |     AAA_REQUEST
                              |---------------------+----------------
aaaEapRespData = NONE         |  currentId == NONE  |        AAA_IDLE
------------------------------+---------------------+----------------
------------------------------+---------------------+----------------
IDLE2                         |                     |
                              |  retransWhile == 0  |     RETRANSMIT2
retransWhile =                |                     |
  calculateTimeout(           |---------------------+----------------
   retransCount, eapSRTT,     |       eapResp       |       RECEIVED2
   eapRTTVAR, methodTimeout)  |                     |
------------------------------+---------------------+----------------
RETRANSMIT2                   |                     |
                              |   retransCount >    |        TIMEOUT_
retransCount++                |     MaxRetrans      |        FAILURE2
if (retransCount<=MaxRetrans){|                     |
  eapReqData = lastReqData    |---------------------+----------------
  eapReq = TRUE               |        else         |           IDLE2
}                             |                     |
------------------------------+---------------------+----------------
RECEIVED2                     |      rxResp &&      |
                              |     (respId ==      |     AAA_REQUEST
(rxResp,respId,respMethod)=   |     currentId)      |
  parseEapResp(eapRespData)   |---------------------+----------------
                              |        else         |        DISCARD2
------------------------------+---------------------+----------------
AAA_REQUEST                   |                     |
                              |                     |
if (respMethod == IDENTITY) { |         UCT         |        AAA_IDLE
  aaaIdentity = eapRespData   |                     |
aaaEapRespData = eapRespData  |                     |
------------------------------+---------------------+----------------
AAA_IDLE                      |     aaaEapNoReq     |        DISCARD2
                              |---------------------+----------------
aaaFail = FALSE               |      aaaEapReq      |    AAA_RESPONSE
aaaSuccess = FALSE            |---------------------+----------------
aaaEapReq = FALSE             |     aaaTimeout      |        TIMEOUT_
aaaEapNoReq = FALSE           |                     |        FAILURE2
aaaEapResp = TRUE             |---------------------+----------------
                              |       aaaFail       |        FAILURE2
                              |---------------------+----------------
                              |     aaaSuccess      |        SUCCESS2
------------------------------+---------------------+----------------
AAA_RESPONSE                  |                     |
                              |                     |
eapReqData = aaaEapReqData    |         UCT         |   SEND_REQUEST2
currentId = getId(eapReqData) |                     |
methodTimeout =               |                     |
  aaaMethodTimeout            |                     |
------------------------------+---------------------+----------------
------------------------------+---------------------+----------------
DISCARD2                      |                     |
                              |         UCT         |           IDLE2
eapResp = FALSE               |                     |
eapNoReq = TRUE               |                     |
------------------------------+---------------------+----------------
SEND_REQUEST2                 |                     |
                              |                     |
retransCount = 0              |         UCT         |           IDLE2
lastReqData = eapReqData      |                     |
eapResp = FALSE               |                     |
eapReq = TRUE                 |                     |
------------------------------+---------------------+----------------
TIMEOUT_FAILURE2              |                     |
                              |                     |
eapTimeout = TRUE             |                     |
------------------------------+---------------------+----------------
FAILURE2                      |                     |
                              |                     |
eapReqData = aaaEapReqData    |                     |
eapFail = TRUE                |                     |
------------------------------+---------------------+----------------
SUCCESS2                      |                     |
                              |                     |
eapReqData = aaaEapReqData    |                     |
eapKeyData = aaaEapKeyData    |                     |
eapKeyAvailable =             |                     |
  aaaEapKeyAvailable          |                     |
eapSuccess = TRUE             |                     |
---------------------------------------------------------------------
                               Figure 12

Authors' Addresses ๐Ÿงš

Okay, bestie, here are the four people who put this whole thing together ๐Ÿ’–

John Vollbrecht
Meetinghouse Data Communications
9682 Alice Hill Drive
Dexter, MI  48130
USA

EMail: jrv@mtghouse.com
Pasi Eronen
Nokia Research Center
P.O. Box 407
FIN-00045 Nokia Group,
Finland

EMail: pasi.eronen@nokia.com
Nick L. Petroni, Jr.
University of Maryland, College Park
A.V. Williams Building
College Park, MD  20742
USA

EMail: npetroni@cs.umd.edu
Yoshihiro Ohba
Toshiba America Research, Inc.
1 Telcordia Drive
Piscataway, NJ  08854
USA

EMail: yohba@tari.toshiba.com

Copyright (C) The Internet Society (2005).

This document is subject to the rights, licenses and restrictions contained in BCP 78, and except as set forth therein, the authors retain all their rights.

This document and the information contained herein are provided on an "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.

Intellectual Property ๐Ÿ”ฅ

The IETF takes no position regarding the validity or scope of any Intellectual Property Rights or other rights that might be claimed to pertain to the implementation or use of the technology described in this document or the extent to which any license under such rights might or might not be available; nor does it represent that it has made any independent effort to identify any such rights. Information on the procedures with respect to rights in RFC documents can be found in BCP 78 and BCP 79.

Copies of IPR disclosures made to the IETF Secretariat and any assurances of licenses to be made available, or the result of an attempt made to obtain a general license or permission for the use of such proprietary rights by implementers or users of this specification can be obtained from the IETF on-line IPR repository at http://www.ietf.org/ipr.

The IETF invites any interested party to bring to its attention any copyrights, patents or patent applications, or other proprietary rights that may cover technology that may be required to implement this standard. Please address the information to the IETF at ietf-ipr@ietf.org.


Acknowledgement ๐Ÿซถ

Funding for the RFC Editor function is currently provided by the Internet Society. Thank you for reading all the way to the end, bestie ๐ŸŒˆโœจ

๐Ÿ“ฆ where this page comes from