RFC 1510

The Kerberos Network Authentication Service (V5)

๐Ÿ“… published September 1993, from ietf

RFC 1510: The Kerberos Network Authentication Service (V5) ๐ŸŽ€

part 1 of 24 ๐Ÿ’…


Title Block ๐Ÿ“œ

okay so this is RFC 1510 ๐Ÿฆ‹, and it came out in September 1993. The authors are J. Kohl (Digital Equipment Corporation) and C. Neuman (ISI) ๐Ÿ‘ฉโ€๐Ÿ’ป. It's an Internet standards track protocol ๐Ÿ”ฅ. The title block doesn't list any RFCs it obsoletes or updates, so no drama there โœจ.

Status of This Memo โœจ

so this RFC is an Internet standards track protocol, and it's basically asking the Internet community to chat about it and drop suggestions ๐Ÿ’ฌ. If you want the official standardization state and status, check the current edition of the "Internet Official Protocol Standards" ๐Ÿ“š. Distribution of this memo is unlimited ๐ŸŒˆ.

Abstract ๐ŸŒธ

this doc gives an overview and spec of Version 5 of the Kerberos network authentication system ๐Ÿ”. Version 4 is described elsewhere [1,2], and it's still actually in production use at MIT's Project Athena and other Internet sites ๐Ÿซ. iconic longevity ๐Ÿ‘‘.

Overview ๐Ÿงš

first, the legal sparkle ๐Ÿ’…: Project Athena, Athena, Athena MUSE, Discuss, Hesiod, Kerberos, Moira, and Zephyr are trademarks of the Massachusetts Institute of Technology (MIT). You can't use them commercially without MIT's prior written permission โœ‹ (don't be that person).

this RFC explains the concepts and model Kerberos is built on, and it specifies Version 5 of the protocol ๐Ÿฆ‹.

the motivations, goals, assumptions, and reasoning behind most design choices only get a quick mention here ๐Ÿ™ˆ. For Version 4 they're fully explained in the Kerberos section of the Athena Technical Plan [1]. The protocols are still under review and aren't being submitted as an Internet standard right now, but comments are very much encouraged ๐Ÿซถ.

if you want to join the discussion mailing list for Kerberos, kerberos@MIT.EDU, ask to be added by writing to kerberos-request@MIT.EDU ๐Ÿ“ฌ. The list is also mirrored onto Usenet as comp.protocols.kerberos ๐ŸŒธ. For further info, including documents and code availability, email info-kerberos@MIT.EDU ๐Ÿ’œ.

Background ๐ŸŒˆ

the Kerberos model is partly based on Needham and Schroeder's trusted third-party authentication protocol [3], plus some modifications Denning and Sacco suggested [4] ๐Ÿง . The original design and implementation of Kerberos Versions 1 through 4 was the work of two former Project Athena staff members: Steve Miller of Digital Equipment Corporation, and Clifford Neuman (now at the Information Sciences Institute of the University of Southern California) ๐Ÿ‘‘. Jerome Saltzer (Technical Director of Project Athena) and Jeffrey Schiller (MIT Campus Network Manager) were part of it too, and lots of other Athena people contributed to the work on Kerberos ๐Ÿซถ. Version 4 is publicly available and has seen wide use across the Internet ๐ŸŒ.

Version 5 (the one this document is about) evolved from Version 4 because of new requirements and a desire for features Version 4 didn't have ๐Ÿฆ‹. The differences between Kerberos Versions 4 and 5 are covered in [5] ๐Ÿ“–.

Table of Contents ๐Ÿ“‘

Table of Contents

   1. Introduction .......................................    5
   1.1. Cross-Realm Operation ............................    7
   1.2. Environmental assumptions ........................    8
   1.3. Glossary of terms ................................    9
   2. Ticket flag uses and requests ......................   12
   2.1. Initial and pre-authenticated tickets ............   12
   2.2. Invalid tickets ..................................   12
   2.3. Renewable tickets ................................   12
   2.4. Postdated tickets ................................   13
   2.5. Proxiable and proxy tickets ......................   14
   2.6. Forwardable tickets ..............................   15
   2.7. Other KDC options ................................   15
   3. Message Exchanges ..................................   16
   3.1. The Authentication Service Exchange ..............   16
   3.1.1. Generation of KRB_AS_REQ message ...............   17
   3.1.2. Receipt of KRB_AS_REQ message ..................   17
   3.1.3. Generation of KRB_AS_REP message ...............   17
   3.1.4. Generation of KRB_ERROR message ................   19
   3.1.5. Receipt of KRB_AS_REP message ..................   19
   3.1.6. Receipt of KRB_ERROR message ...................   20
   3.2. The Client/Server Authentication Exchange ........   20
   3.2.1. The KRB_AP_REQ message .........................   20
   3.2.2. Generation of a KRB_AP_REQ message .............   20
   3.2.3. Receipt of KRB_AP_REQ message ..................   21
   3.2.4. Generation of a KRB_AP_REP message .............   23
   3.2.5. Receipt of KRB_AP_REP message ..................   23
   3.2.6. Using the encryption key .......................   24
   3.3. The Ticket-Granting Service (TGS) Exchange .......   24
   3.3.1. Generation of KRB_TGS_REQ message ..............   25
   3.3.2. Receipt of KRB_TGS_REQ message .................   26
   3.3.3. Generation of KRB_TGS_REP message ..............   27
   3.3.3.1. Encoding the transited field .................   29
   3.3.4. Receipt of KRB_TGS_REP message .................   31
   3.4. The KRB_SAFE Exchange ............................   31
   3.4.1. Generation of a KRB_SAFE message ...............   31
   3.4.2. Receipt of KRB_SAFE message ....................   32
   3.5. The KRB_PRIV Exchange ............................   33
   3.5.1. Generation of a KRB_PRIV message ...............   33
   3.5.2. Receipt of KRB_PRIV message ....................   33
   3.6. The KRB_CRED Exchange ............................   34
   3.6.1. Generation of a KRB_CRED message ...............   34
   3.6.2. Receipt of KRB_CRED message ....................   34
   4. The Kerberos Database ..............................   35
   4.1. Database contents ................................   35
   4.2. Additional fields ................................   36
   4.3. Frequently Changing Fields .......................   37
   4.4. Site Constants ...................................   37
   5. Message Specifications .............................   38
   5.1. ASN.1 Distinguished Encoding Representation ......   38
   5.2. ASN.1 Base Definitions ...........................   38
   5.3. Tickets and Authenticators .......................   42
   5.3.1. Tickets ........................................   42
   5.3.2. Authenticators .................................   47
   5.4. Specifications for the AS and TGS exchanges ......   49
   5.4.1. KRB_KDC_REQ definition .........................   49
   5.4.2. KRB_KDC_REP definition .........................   56
   5.5. Client/Server (CS) message specifications ........   58
   5.5.1. KRB_AP_REQ definition ..........................   58
   5.5.2. KRB_AP_REP definition ..........................   60
   5.5.3. Error message reply ............................   61
   5.6. KRB_SAFE message specification ...................   61
   5.6.1. KRB_SAFE definition ............................   61
   5.7. KRB_PRIV message specification ...................   62
   5.7.1. KRB_PRIV definition ............................   62
   5.8. KRB_CRED message specification ...................   63
   5.8.1. KRB_CRED definition ............................   63
   5.9. Error message specification ......................   65
   5.9.1. KRB_ERROR definition ...........................   66
   6. Encryption and Checksum Specifications .............   67
   6.1. Encryption Specifications ........................   68
   6.2. Encryption Keys ..................................   71
   6.3. Encryption Systems ...............................   71
   6.3.1. The NULL Encryption System (null) ..............   71
   6.3.2. DES in CBC mode with a CRC-32 checksum (descbc-crc)71
   6.3.3. DES in CBC mode with an MD4 checksum (descbc-md4)  72
   6.3.4. DES in CBC mode with an MD5 checksum (descbc-md5)  72
   6.4. Checksums ........................................   74
   6.4.1. The CRC-32 Checksum (crc32) ....................   74
   6.4.2. The RSA MD4 Checksum (rsa-md4) .................   75
   6.4.3. RSA MD4 Cryptographic Checksum Using DES
   (rsa-md4-des) .........................................   75
   6.4.4. The RSA MD5 Checksum (rsa-md5) .................   76
   6.4.5. RSA MD5 Cryptographic Checksum Using DES
   (rsa-md5-des) .........................................   76
   6.4.6. DES cipher-block chained checksum (des-mac)
   6.4.7. RSA MD4 Cryptographic Checksum Using DES
   alternative (rsa-md4-des-k) ...........................   77
   6.4.8. DES cipher-block chained checksum alternative
   (des-mac-k) ...........................................   77
   7. Naming Constraints .................................   78
   7.1. Realm Names ......................................   77
   7.2. Principal Names ..................................   79
   7.2.1. Name of server principals ......................   80
   8. Constants and other defined values .................   80
   8.1. Host address types ...............................   80
   8.2. KDC messages .....................................   81
   8.2.1. IP transport ...................................   81
   8.2.2. OSI transport ..................................   82
   8.2.3. Name of the TGS ................................   82
   8.3. Protocol constants and associated values .........   82
   9. Interoperability requirements ......................   86
   9.1. Specification 1 ..................................   86
   9.2. Recommended KDC values ...........................   88
   10. Acknowledgments ...................................   88
   11. References ........................................   89
   12. Security Considerations ...........................   90
   13. Authors' Addresses ................................   90
   A. Pseudo-code for protocol processing ................   91
   A.1. KRB_AS_REQ generation ............................   91
   A.2. KRB_AS_REQ verification and KRB_AS_REP generation    92
   A.3. KRB_AS_REP verification ..........................   95
   A.4. KRB_AS_REP and KRB_TGS_REP common checks .........   96
   A.5. KRB_TGS_REQ generation ...........................   97
   A.6. KRB_TGS_REQ verification and KRB_TGS_REP generation  98
   A.7. KRB_TGS_REP verification .........................  104
   A.8. Authenticator generation .........................  104
   A.9. KRB_AP_REQ generation ............................  105
   A.10. KRB_AP_REQ verification .........................  105
   A.11. KRB_AP_REP generation ...........................  106
   A.12. KRB_AP_REP verification .........................  107
   A.13. KRB_SAFE generation .............................  107
   A.14. KRB_SAFE verification ...........................  108
   A.15. KRB_SAFE and KRB_PRIV common checks .............  108
   A.16. KRB_PRIV generation .............................  109
   A.17. KRB_PRIV verification ...........................  110
   A.18. KRB_CRED generation .............................  110
   A.19. KRB_CRED verification ...........................  111
   A.20. KRB_ERROR generation ............................  112

1. Introduction โœจ

okay bestie, so Kerberos is the thing that lets us verify who people (and servers) actually are ๐Ÿ” on an open, unprotected network. And it's iconic because it does this without trusting the host's own OS to vouch for anyone ๐Ÿ’…, without trusting host addresses, and without needing every machine on the network to be physically locked down ๐Ÿ”’. It even assumes packets can be read, modified, and inserted whenever an attacker wants ๐Ÿ˜ญ. Kerberos is literally built for a messy internet.

Quick note: lots of apps only use Kerberos when they first open a stream-based connection, and they assume no "hijackers" are going to take over that connection ๐Ÿ™ˆ. That kind of setup quietly trusts the host addresses involved, so it's not the full Kerberos experience.

Kerberos does all this as a trusted third-party authentication service using conventional cryptography, aka a shared secret key ๐Ÿ—๏ธ. Quick vocab moment: "secret" and "private" get mixed up all the time in the literature, but here a secret takes two (or more) people to share it. So a shared DES key counts as a secret key ๐Ÿ“. Something is only private when nobody except its owner knows it, which is why in public key cryptosystems you get a public key and a private key ๐ŸŒˆ.

Now the vibe check on how authentication goes down ๐ŸŽ€. A client sends a request to the authentication server (AS) asking for "credentials" for a given server. The AS sends those credentials back encrypted in the client's key ๐Ÿ”‘. The credentials have two parts: 1) a "ticket" for the server, and 2) a temporary encryption key (usually called a "session key") โœจ. The client then sends the ticket to the server. The ticket holds the client's identity and a copy of the session key, all encrypted in the server's key ๐Ÿงš. Now the session key is shared by the client and server, and it's used to authenticate the client. It can optionally authenticate the server too ๐Ÿ‘‘. It can also encrypt further chatter between them, or be used to swap a separate sub-session key for encrypting even more messages ๐Ÿ’œ.

Behind the scenes, the implementation is one or more authentication servers running on physically secure hosts ๐Ÿฐ. These servers keep a database of principals (users and servers) and their secret keys. Code libraries handle the encryption and run the Kerberos protocol ๐Ÿ“š. To add authentication to its transactions, a typical network app just adds one or two calls to the Kerberos library, and boom, the necessary messages get sent for authentication ๐Ÿ’–.

The Kerberos protocol is made of several sub-protocols (or "exchanges") ๐Ÿฆ‹. There are two ways a client can ask a Kerberos server for credentials:

  • First approach: the client sends a cleartext request for a ticket for the desired server to the AS. The reply is encrypted in the client's secret key. Usually this request is for a ticket-granting ticket (TGT), which can later be used with the ticket-granting server (TGS) ๐ŸŽŸ๏ธ.
  • Second approach: the client sends a request to the TGS. It sends the TGT the same way it would contact any other application server that needs Kerberos credentials. The reply is encrypted in the session key from the TGT ๐Ÿ’….

Once you've got credentials, you can use them to verify the identities of principals in a transaction, to make sure messages between them haven't been messed with, or to keep messages private ๐Ÿซถ. The application gets to choose whatever protection it needs.

Here's how identity verification works ๐Ÿ‘€. The client sends the ticket to the server. The ticket goes over the wire "in the clear" (parts of it are encrypted, but that encryption doesn't stop replay ๐Ÿ˜ฌ). Since an attacker could grab it and reuse it, extra info gets sent to prove the message came from the principal the ticket was issued to. That info is called the authenticator, it's encrypted in the session key, and it includes a timestamp โฐ. The timestamp proves the message was made recently and isn't a replay. Encrypting the authenticator in the session key proves it came from someone who has the session key ๐Ÿ”. Since only the requesting principal and the server know the session key (it's never sent over the network in the clear), this guarantees the client's identity ๐Ÿฅฐ.

Integrity works with the session key too ๐Ÿ”ฅ. This approach catches both replay attacks and message stream modification attacks. It works by generating and sending a collision-proof checksum (elsewhere called a hash or digest function) of the client's message, keyed with the session key. Privacy and integrity can also be locked down by encrypting the data with the session key from the ticket and credentials ๐ŸŒธ.

The authentication exchanges above only need read-only access to the Kerberos database ๐Ÿ“–. But sometimes the database has to change, like when adding new principals or changing a principal's key ๐Ÿ› ๏ธ. That's done through a protocol between a client and a third Kerberos server, the Kerberos Administration Server (KADM). This document does not describe the administration protocol ๐Ÿ™ˆ. There's also a protocol for keeping multiple copies of the Kerberos database in sync. That one counts as an implementation detail, and it can vary to support different database technologies ๐Ÿงš.

1.1. Cross-Realm Operation

Kerberos is designed to work across organizational boundaries ๐ŸŒ. A client in one organization can be authenticated to a server in another, which is so main-character energy ๐Ÿ‘‘. Each organization that wants to run a Kerberos server sets up its own "realm". The name of the realm where a client is registered is part of the client's name, and the end-service can use it to decide whether to honor a request ๐Ÿ’œ.

By setting up "inter-realm" keys, the admins of two realms can let a client who's authenticated locally use that authentication remotely ๐Ÿฆ‹. (Sure, with the right permission a client could register a separately-named principal in a remote realm and do normal exchanges with that realm's services. But even with just a few clients, that gets super cumbersome, so the more automatic methods described here are necessary ๐Ÿ˜ฉ.) Exchanging inter-realm keys (a separate key can be used for each direction) registers each realm's ticket-granting service as a principal in the other realm ๐ŸŽ€. That means a client can get a ticket-granting ticket for the remote realm's TGS from its local realm ๐ŸŽŸ๏ธ.

When that TGT gets used, the remote TGS uses the inter-realm key to decrypt it. (That key usually differs from the TGS's own normal key.) This way the remote TGS knows for sure the TGT was issued by the client's own TGS ๐Ÿซถ. Tickets the remote TGS issues will tell the end-service that the client was authenticated from another realm โœจ.

A realm "communicates" with another realm if the two share an inter-realm key, or if the local realm shares one with an intermediate realm that communicates with the remote realm ๐ŸŒˆ. An authentication path is the sequence of intermediate realms transited when going from one realm to another ๐Ÿ—บ๏ธ.

Realms are usually organized hierarchically ๐ŸŒณ. Each realm shares a key with its parent and a different key with each child. If two realms don't directly share an inter-realm key, the hierarchy makes it easy to build an authentication path ๐Ÿชœ. If there's no hierarchy, you might have to consult some database to construct an authentication path between realms ๐Ÿ˜ตโ€๐Ÿ’ซ.

Realms are typically hierarchical, but intermediate realms can be bypassed to do cross-realm authentication through alternate authentication paths (these might be set up to make communication between two realms more efficient โšก). It matters a lot for the end-service to know which realms were transited when deciding how much faith to put in the authentication ๐Ÿง. So a field in each ticket holds the names of the realms that were involved in authenticating the client ๐Ÿ“.

1.2. Environmental assumptions

Kerberos has a few assumptions about the environment it needs in order to work properly ๐Ÿงธ:

  • Denial of service attacks aren't solved by Kerberos. There are spots in these protocols where an intruder can stop an application from taking part in the proper authentication steps ๐Ÿšซ. Spotting and fixing these attacks (some can look like totally normal failure modes for the system ๐Ÿ™ƒ) is usually best left to the human administrators and users ๐Ÿซ .
  • Principals have to keep their secret keys secret ๐Ÿคซ. If an intruder somehow steals a principal's key, they can masquerade as that principal, or impersonate any server to the legit principal ๐Ÿ’€.
  • Password guessing attacks aren't solved by Kerberos ๐Ÿ˜ค. If a user picks a poor password, an attacker can successfully run an offline dictionary attack. They do this by repeatedly trying dictionary entries to decrypt messages they've captured that were encrypted under a key derived from the user's password ๐Ÿ”“.
  • Every host on the network needs a clock that's "loosely synchronized" with the other hosts' clocks โฑ๏ธ. This synchronization is used to cut down on the bookkeeping application servers need for replay detection. The degree of "looseness" can be configured on a per-server basis ๐ŸŽš๏ธ. If clocks are synchronized over the network, the clock synchronization protocol itself has to be secured from network attackers ๐Ÿ›ก๏ธ.
  • Principal identifiers aren't recycled on a short-term basis ๐Ÿ™…โ€โ™€๏ธ. A typical access control setup uses access control lists (ACLs) to grant permissions to particular principals ๐Ÿ“‹. If a stale ACL entry sticks around for a deleted principal, and that principal identifier gets reused, the new principal inherits the rights in the stale entry ๐Ÿ˜ฑ. Not reusing principal identifiers removes the danger of accidental access ๐Ÿ’–.

1.3. Glossary of terms ๐Ÿ“–โœจ

Okay bestie, here's the vocab list you'll see all over this doc ๐Ÿ’…

  • Authentication: verifying the claimed identity of a principal ๐Ÿชช
  • Authentication header: a record with a Ticket and an Authenticator, presented to a server as part of the authentication process ๐ŸŽซ
  • Authentication path: a sequence of intermediate realms transited in the authentication process when communicating from one realm to another ๐Ÿ›ฃ๏ธ
  • Authenticator: a record with info that can be shown to have been recently generated using the session key known only by the client and server ๐Ÿ”‘
  • Authorization: the process of determining whether a client may use a service, which objects the client is allowed to access, and the type of access allowed for each ๐Ÿšช
  • Capability: a token that grants the bearer permission to access an object or service. In Kerberos, this might be a ticket whose use is restricted by the contents of the authorization data field, but which lists no network addresses, together with the session key necessary to use the ticket ๐ŸŽŸ๏ธ
  • Ciphertext: the output of an encryption function. Encryption transforms plaintext into ciphertext ๐Ÿ”
  • Client: a process that makes use of a network service on behalf of a user. Note that in some cases a Server may itself be a client of some other server (e.g., a print server may be a client of a file server) ๐Ÿ–จ๏ธ
  • Credentials: a ticket plus the secret session key necessary to successfully use that ticket in an authentication exchange ๐Ÿ—๏ธ
  • KDC: Key Distribution Center, a network service that supplies tickets and temporary session keys; or an instance of that service or the host on which it runs. The KDC services both initial ticket and ticket-granting ticket requests. The initial ticket portion is sometimes referred to as the Authentication Server (or service). The ticket-granting ticket portion is sometimes referred to as the ticket-granting server (or service) ๐Ÿฐ
  • Kerberos: aside from the 3-headed dog guarding Hades ๐Ÿ•, the name given to Project Athena's authentication service, the protocol used by that service, or the code used to implement the authentication service ๐ŸŒˆโœจ
  • Plaintext: the input to an encryption function or the output of a decryption function. Decryption transforms ciphertext into plaintext ๐Ÿ“œ
  • Principal: a uniquely named client or server instance that participates in a network communication ๐Ÿ‘ค
  • Principal identifier: the name used to uniquely identify each different principal ๐Ÿท๏ธ
  • Seal: to encipher a record containing several fields in such a way that the fields cannot be individually replaced without either knowledge of the encryption key or leaving evidence of tampering ๐Ÿง
  • Secret key: an encryption key shared by a principal and the KDC, distributed outside the bounds of the system, with a long lifetime. In the case of a human user's principal, the secret key is derived from a password ๐Ÿคซ
  • Server: a particular principal which provides a resource to network clients ๐Ÿ–ฅ๏ธ
  • Service: a resource provided to network clients; often provided by more than one server (for example, remote file service) ๐Ÿ›Ž๏ธ
  • Session key: a temporary encryption key used between two principals, with a lifetime limited to the duration of a single login "session" โณ
  • Sub-session key: a temporary encryption key used between two principals, selected and exchanged by the principals using the session key, and with a lifetime limited to the duration of a single association ๐Ÿงฉ
  • Ticket: a record that helps a client authenticate itself to a server; it contains the client's identity, a session key, a timestamp, and other information, all sealed using the server's secret key. It only serves to authenticate a client when presented along with a fresh Authenticator ๐Ÿ’–

2. Ticket flag uses and requests ๐Ÿšฉ

Every Kerberos ticket carries a set of flags that show off its various attributes ๐Ÿ’… Most flags can be requested by a client when the ticket is obtained, while some get turned on and off automatically by a Kerberos server whenever it's needed. The next sections spell out what each flag means and give examples of why you'd want to use it ๐Ÿฆ‹

2.1. Initial and pre-authenticated tickets

The INITIAL flag tells you a ticket was issued using the AS protocol, and not based on a ticket-granting ticket ๐ŸŒธ Application servers that want to require knowledge of a client's secret key (e.g., a passwordchanging program) can insist that this flag be set in any tickets they accept, so they can be sure the client's key was recently presented to the application client ๐Ÿ”ฅ

The PRE-AUTHENT and HW-AUTHENT flags give you additional info about the initial authentication ๐Ÿง  This is true whether the current ticket was issued directly (in which case INITIAL will also be set) or issued based on a ticket-granting ticket (in which case the INITIAL flag is clear, but PRE-AUTHENT and HW-AUTHENT are carried forward from the ticket-granting ticket) ๐ŸŽ€

2.2. Invalid tickets

The INVALID flag means a ticket is invalid, and it's giving "do not use" ๐Ÿ™…โ€โ™€๏ธ Application servers must reject tickets with this flag set. A postdated ticket will usually be issued in this form ๐Ÿงš Invalid tickets must be validated by the KDC before use, by presenting them to the KDC in a TGS request with the VALIDATE option specified. The KDC will only validate tickets after their starttime has passed. The validation is required so that postdated tickets which have been stolen before their starttime can be rendered permanently invalid (through a hot-list mechanism) ๐Ÿšจ

2.3. Renewable tickets

Okay, so apps may want to hold tickets that stay valid for long periods ๐Ÿ•ฐ๏ธ But that exposes their credentials to potential theft for just as long, and stolen credentials would stay valid until the ticket's expiration time ๐Ÿ˜ญ Simply using short-lived tickets and grabbing new ones periodically would mean the client needs long-term access to its secret key, which is an even bigger risk ๐Ÿ’€ So renewable tickets to the rescue, they help limit the consequences of theft ๐Ÿ›ก๏ธ

Renewable tickets have two "expiration times" ๐Ÿ‘‘ The first is when the current instance of the ticket expires, and the second is the latest permissible value for an individual expiration time. An application client must periodically (i.e., before it expires) present a renewable ticket to the KDC, with the RENEW option set in the KDC request. The KDC will issue a new ticket with a new session key and a later expiration time. All other fields of the ticket are left unmodified by the renewal process. When the latest permissible expiration time arrives, the ticket expires permanently. At each renewal, the KDC may consult a hot-list to determine if the ticket had been reported stolen since its last renewal; it will refuse to renew such stolen tickets, so the usable lifetime of stolen tickets is reduced ๐Ÿ”’

The RENEWABLE flag in a ticket is normally only interpreted by the ticket-granting service (discussed below in section 3.3) ๐ŸŽง It can usually be ignored by application servers. However, some particularly careful application servers may wish to disallow renewable tickets ๐Ÿ™ˆ

If a renewable ticket is not renewed by its expiration time, the KDC will not renew the ticket ๐Ÿฅ€ The RENEWABLE flag is reset by default, but a client may request it be set by setting the RENEWABLE option in the KRB_AS_REQ message. If it is set, then the renew-till field in the ticket contains the time after which the ticket may not be renewed โฐ

2.4. Postdated tickets

Sometimes apps need tickets for use much later ๐Ÿ“… For example, a batch submission system would need tickets to be valid at the time the batch job is serviced. But it's dangerous to hold valid tickets in a batch queue, since they'd be online longer and more prone to theft ๐Ÿ˜ฌ Postdated tickets solve this: you get them from the KDC at job submission time, but they stay "dormant" until they're activated and validated by a further request to the KDC. If a ticket theft were reported in the interim, the KDC would refuse to validate the ticket, and the thief would be foiled ๐Ÿ•ต๏ธโ€โ™€๏ธ๐Ÿšซ

The MAY-POSTDATE flag in a ticket is normally only interpreted by the ticket-granting service, and application servers can ignore it ๐ŸŽ€ This flag must be set in a ticket-granting ticket in order to issue a postdated ticket based on the presented ticket. It is reset by default; a client may request it by setting the ALLOW-POSTDATE option in the KRB_AS_REQ message. This flag does not let a client obtain a postdated ticket-granting ticket. Postdated ticket-granting tickets can only be obtained by requesting the postdating in the KRB_AS_REQ message ๐Ÿ™…โ€โ™€๏ธ The life (endtime-starttime) of a postdated ticket will be the remaining life of the ticket-granting ticket at the time of the request, unless the RENEWABLE option is also set, in which case it can be the full life (endtime-starttime) of the ticket-granting ticket ๐Ÿง The KDC may limit how far in the future a ticket may be postdated ๐Ÿ”ฎ

The POSTDATED flag indicates that a ticket has been postdated ๐Ÿ’œ The application server can check the authtime field in the ticket to see when the original authentication happened. Some services may choose to reject postdated tickets, or they may only accept them within a certain period after the original authentication ๐Ÿซถ When the KDC issues a POSTDATED ticket, it will also be marked as INVALID, so the application client must present the ticket to the KDC to be validated before use ๐ŸŒˆ

2.5. Proxiable and proxy tickets โœจ

okay so sometimes a principal needs a service to do something for them ๐Ÿงš The service has to be able to take on the client's identity, but only for one specific purpose ๐Ÿ’… A principal makes that happen by handing over a proxy ๐ŸŽ€

The PROXIABLE flag in a ticket is mostly the ticket-granting service's business. Application servers can just ignore it ๐Ÿ™ˆ When it's set, it tells the ticket-granting server that it is OK to issue a new ticket (but not a ticket-granting ticket ๐Ÿšซ) with a different network address based on this ticket. This flag is set by default ๐Ÿ’–

This flag lets a client pass a proxy to a server so the server can do a remote request on its behalf ๐Ÿซถ For example, a print service client can give the print server a proxy so it can reach the client's files on a particular file server and finish a print job ๐Ÿ“

Kerberos tickets are usually only valid from the network addresses specifically included in the ticket, which makes stolen credentials way less useful ๐Ÿ”’ (It is permissible to request or issue tickets with no network addresses specified, but we do not recommend it ๐Ÿ™….) So a client that wants to grant a proxy has to request a new ticket that is valid for the network address of the service getting the proxy.

The PROXY flag is set in a ticket by the TGS when it issues a proxy ticket. Application servers may check this flag and require additional authentication from the agent presenting the proxy, so there's an audit trail ๐Ÿ“


2.6. Forwardable tickets ๐Ÿฆ‹

Authentication forwarding is an instance of the proxy case where the service gets complete use of the client's identity ๐Ÿ‘‘ It's giving full main character energy. An example is when a user logs in to a remote system and wants authentication to work from that system as if the login were local ๐Ÿ’ป

The FORWARDABLE flag in a ticket is normally only interpreted by the ticket-granting service. Application servers can ignore it. It has an interpretation similar to PROXIABLE, except that ticket-granting tickets may also be issued with different network addresses. This flag is reset by default, but users can ask for it to be set by using the FORWARDABLE option in the AS request when they request their initial ticket-granting ticket ๐ŸŒธ

This flag allows authentication forwarding without making the user enter a password again ๐Ÿ”‘ If the flag is not set, then authentication forwarding is not permitted. The same end result can still happen if the user goes through the AS exchange with the requested network addresses and supplies a password, so it's not a dead end ๐Ÿซ 

The FORWARDED flag is set by the TGS when a client presents a ticket with the FORWARDABLE flag set and requests it be set by specifying the FORWARDED KDC option and supplying a set of addresses for the new ticket. It's also set in all tickets issued based on tickets that have the FORWARDED flag set. Application servers may wish to process FORWARDED tickets differently than non-FORWARDED tickets ๐Ÿง


2.7. Other KDC options ๐Ÿ’œ

There are two more options a client can set in its request to the KDC. The RENEWABLE-OK option means the client will accept a renewable ticket if a ticket with the requested life can't be provided any other way. If that happens, the KDC may issue a renewable ticket with a renew-till equal to the requested endtime. The renew-till value may still be adjusted by site-determined limits or by limits imposed by the individual principal or server ๐Ÿ“

The ENC-TKT-IN-SKEY option is honored only by the ticket-granting service. It means the to-be-issued ticket for the end server is encrypted in the session key from the additional ticket-granting ticket sent with the request. See section 3.3.3 for the specific details ๐Ÿ”


3. Message Exchanges ๐Ÿ’ฌ

These sections are all about the interactions between network clients and servers, and the messages involved in those exchanges. Let's get into it ๐ŸŒˆ

3.1. The Authentication Service Exchange ๐ŸŽ€

Summary

         Message direction       Message type    Section
         1. Client to Kerberos   KRB_AS_REQ      5.4.1
         2. Kerberos to client   KRB_AS_REP or   5.4.2
                                 KRB_ERROR       5.9.1

The Authentication Service (AS) Exchange between the client and the Kerberos Authentication Server is usually started by a client when it wants authentication credentials for a given server but doesn't have any credentials yet ๐Ÿงš The client's secret key is used for encryption and decryption. This exchange is typically used at the start of a login session to get credentials for a Ticket-Granting Server. Those credentials can then be used to get credentials for other servers (see section 3.3) without needing the client's secret key again ๐Ÿ” This exchange is also used to request credentials for services that must not be mediated through the Ticket-Granting Service, but instead need a principal's secret key, such as the password-changing service. (The password-changing request must not be honored unless the requester can provide the old password, which is the user's current secret key. Otherwise, someone could walk up to an unattended session and change another user's password ๐Ÿ™…โ€โ™€๏ธ) This exchange does not, by itself, provide any assurance of the identity of the user. (To authenticate a user logging on to a local system, the credentials obtained in the AS exchange may first be used in a TGS exchange to get credentials for a local server. Those credentials must then be verified by the local server by successfully completing the Client/Server exchange ๐Ÿ”)

The exchange has two messages: KRB_AS_REQ from the client to Kerberos, and KRB_AS_REP or KRB_ERROR in reply. The formats for these messages are described in sections 5.4.1, 5.4.2, and 5.9.1 ๐Ÿ“š

In the request, the client sends (in cleartext) its own identity and the identity of the server it's requesting credentials for. The response, KRB_AS_REP, contains a ticket for the client to present to the server, plus a session key that the client and the server will share ๐Ÿค The session key and additional information are encrypted in the client's secret key. The KRB_AS_REP message contains information that can be used to detect replays and to match it to the message it replies to. Various errors can happen, and they show up as an error response (KRB_ERROR) instead of KRB_AS_REP. The error message is not encrypted. The KRB_ERROR message also contains information that can be used to match it to the message it replies to. Since KRB_ERROR isn't encrypted, it can't be used to detect replays or fabrications of those messages ๐Ÿ˜ฌ

In the normal case, the authentication server doesn't know whether the client is actually the principal named in the request. It just sends a reply without knowing or caring whether they're the same ๐Ÿคทโ€โ™€๏ธ This is fine because nobody except the principal whose identity was given in the request can use the reply. Its critical information is encrypted in that principal's key ๐Ÿ” The initial request supports an optional field that can be used to pass extra information that might be needed for the initial exchange. This field may be used for preauthentication if you want, but the mechanism isn't currently specified ๐Ÿซถ

3.1.1. Generation of KRB_AS_REQ message ๐Ÿ› ๏ธ

The client can specify a bunch of options in the initial request. Among them: whether preauthentication is to be performed; whether the requested ticket is to be renewable, proxiable, or forwardable; whether it should be postdated or allow postdating of derivative tickets; and whether a renewable ticket will be accepted in place of a non-renewable ticket if the requested ticket expiration date can't be met by a non-renewable ticket (because of configuration constraints; see section 4). See section A.1 for pseudocode ๐Ÿ’–

The client prepares the KRB_AS_REQ message and sends it to the KDC โœˆ๏ธ

3.1.2. Receipt of KRB_AS_REQ message ๐Ÿ“ฌ

If all goes well, processing the KRB_AS_REQ message results in the creation of a ticket for the client to present to the server. The format for the ticket is described in section 5.3.1. The contents of the ticket are determined as follows.

3.1.3. Generation of KRB_AS_REP message โœจ

okay so the authentication server looks up the client and server principals named in the KRB_AS_REQ in its database and pulls out their keys ๐Ÿ”‘. if pre-authentication is required, the server checks it, and if the check fails, it returns an error with the code KDC_ERR_PREAUTH_FAILED ๐Ÿ™…โ€โ™€๏ธ. if the server can't handle the requested encryption type, it returns KDC_ERR_ETYPE_NOSUPP. otherwise it generates a "random" session key ๐ŸŽฒ. "random" here means, among other things, that it should be impossible to guess the next session key from knowing past session keys. a pseudo-random number generator can only get there if it's built on cryptographic principles. a truly random number generator, like one based on measurements of random physical phenomena, would be even better ๐Ÿ’….

if the requested start time is absent or points to the past, the ticket's start time is set to the authentication server's current time โฐ. if it points to the future but the POSTDATED option wasn't specified, the server returns KDC_ERR_CANNOT_POSTDATE ๐Ÿšซ. otherwise the requested start time gets checked against the local realm's policy, because the administrator might ban certain types or ranges of postdated tickets ๐Ÿ™ƒ. if it passes, the ticket's start time is set as requested and the INVALID flag is set on the new ticket ๐Ÿฆ‹. a postdated ticket has to be validated before use by presenting it to the KDC after the start time has been reached.

the expiration time of the ticket gets set to the minimum of these:

  • The expiration time (endtime) requested in the KRB_AS_REQ message.
  • The ticket's start time plus the maximum allowable lifetime associated with the client principal (the authentication server's database includes a maximum ticket lifetime field in each principal's record; see section 4).
  • The ticket's start time plus the maximum allowable lifetime associated with the server principal.
  • The ticket's start time plus the maximum lifetime set by the policy of the local realm.

if the requested expiration time minus the start time (as worked out above) is less than a site-determined minimum lifetime, the server returns KDC_ERR_NEVER_VALID ๐Ÿ’€. if the requested expiration time goes past what was worked out above and the RENEWABLE-OK option was requested, then the RENEWABLE flag gets set on the new ticket and the renew-till value is set as if RENEWABLE had been requested (the field and option names are fully described in section 5.4.1) ๐Ÿ”. when RENEWABLE was requested, or RENEWABLE-OK was set and a renewable ticket is being issued, the renew-till field gets set to the minimum of:

  • Its requested value.
  • The start time of the ticket plus the minimum of the two maximum renewable lifetimes associated with the principals' database entries.
  • The start time of the ticket plus the maximum renewable lifetime set by the policy of the local realm.

the flags field of the new ticket will have these options set if they were requested and the local realm's policy allows it: FORWARDABLE, MAY-POSTDATE, POSTDATED, PROXIABLE, RENEWABLE ๐ŸŒธ. if the new ticket is postdated (the start time is in the future), its INVALID flag gets set too.

if all of the above succeeds, the server formats a KRB_AS_REP message (see section 5.4.2) ๐Ÿ“ฆ. it copies the addresses from the request into the caddr of the response, puts any required pre-authentication data into the padata of the response, encrypts the ciphertext part with the client's key using the requested encryption method, and sends it to the client ๐Ÿš€. see section A.2 for pseudocode.

3.1.4. Generation of KRB_ERROR message

several errors can happen, and in each case the Authentication Server responds by sending back an error message, KRB_ERROR, with the error-code and e-text fields filled in with the right values ๐Ÿ™ˆ. the contents and details of the error message are described in Section 5.9.1.

3.1.5. Receipt of KRB_AS_REP message

if the reply message type is KRB_AS_REP, the client checks that the cname and crealm fields in the cleartext part of the reply match what it asked for ๐Ÿ‘€. if any padata fields are present, they may be used to derive the right secret key for decrypting the message ๐Ÿ—๏ธ. the client decrypts the encrypted part of the response with its secret key and checks that the nonce inside matches the nonce it sent in its request, which catches replays ๐Ÿ”. it also checks that the sname and srealm in the response match the ones in the request, and that the host address field is correct too ๐Ÿง. then it stores the ticket, session key, start and expiration times, and other info for later use ๐Ÿ“. the key-expiration field from the encrypted part of the response may be checked to warn the user that their key is about to expire (the client program could then suggest remedial action, such as a password change) โณ. see section A.3 for pseudocode.

bestie, this part is SO important: decrypting the KRB_AS_REP properly is NOT enough to prove who the user is ๐Ÿ˜ญ. the user and an attacker could team up to make a KRB_AS_REP-format message that decrypts fine but didn't come from the real KDC. if the host wants to verify the user's identity, it has to make the user present application credentials that can be checked against a securely-stored secret key ๐Ÿ”. only once those credentials check out can the user's identity be assured ๐Ÿ’–.

3.1.6. Receipt of KRB_ERROR message

if the reply message type is KRB_ERROR, the client treats it as an error and does whatever application-specific steps are needed to recover ๐Ÿ› ๏ธ.


3.2. The Client/Server Authentication Exchange ๐ŸŽ€

Summary โœจ

Message direction                         Message type    Section
Client to Application server              KRB_AP_REQ      5.5.1
[optional] Application server to client   KRB_AP_REP or   5.5.2
                                          KRB_ERROR       5.9.1

the client/server authentication (CS) exchange is how network applications authenticate the client to the server and the server back to the client ๐Ÿค. the client must already have credentials for the server, which it got through the AS or TGS exchange ๐ŸŽŸ๏ธ.

3.2.1. The KRB_AP_REQ message

the KRB_AP_REQ carries the authentication info that should be part of the very first message in an authenticated transaction ๐Ÿšช. it holds a ticket, an authenticator, and some extra bookkeeping info (see section 5.5.1 for the exact format). a ticket on its own can't authenticate a client, because tickets cross the network in cleartext (tickets have both an encrypted and an unencrypted portion, so "cleartext" here refers to the whole unit, which anyone can copy from one message and replay in another with zero cryptographic skill ๐Ÿ˜ณ). that's why the authenticator exists: it stops invalid replay of tickets by proving to the server that the client knows the ticket's session key, and so is entitled to use it ๐Ÿ”‘๐Ÿ’…. the KRB_AP_REQ message is also called the "authentication header" elsewhere ๐Ÿงš.

3.2.2. Generation of a KRB_AP_REQ message

when a client wants to start authenticating to a server, it gets a ticket and session key for the service it wants (from a credentials cache, the AS exchange, or the TGS exchange) ๐ŸŽซ. the client can reuse any ticket it holds until that ticket expires โ™ป๏ธ. then it builds a new Authenticator out of the system time, its name, and, optionally, an application-specific checksum, an initial sequence number for use in KRB_SAFE or KRB_PRIV messages, and/or a session subkey for negotiating a session key unique to this particular session ๐ŸŒˆ. authenticators may not be reused, and they'll be rejected if replayed to a server (note that this can make applications on unreliable transports tricky to code correctly, if the transport might deliver duplicate messages; in those cases, a new authenticator must be generated for each retry ๐Ÿ™ƒ). if a sequence number is included, it should be chosen at random, so that even after lots of messages have been exchanged it's unlikely to collide with other sequence numbers in use ๐ŸŽฒ.

the client can ask for mutual authentication, or for a session-key-based ticket, by setting the matching flag(s) in the ap-options field of the message ๐Ÿšฉ.

the Authenticator gets encrypted with the session key and combined with the ticket to form the KRB_AP_REQ message, which is then sent to the end server along with any extra application-specific info ๐Ÿ“ฌ. see section A.9 for pseudocode.

3.2.3. Receipt of KRB_AP_REQ message โœจ

okay bestie, so the server's whole auth decision is based on its current time of day (clocks must be loosely synchronized ๐Ÿ•ฐ๏ธ), the authenticator, and the ticket ๐ŸŽŸ๏ธ. Lots of things can go wrong, and when one does, the server is expected to reply to the client with a KRB_ERROR message ๐Ÿšจ. That message can get wrapped inside the application protocol too if its "raw" form isn't acceptable to that protocol. The format of error messages is in section 5.9.1 ๐Ÿ’….

Here's how the server checks the auth info, step by step ๐Ÿ‘€:

If the message type isn't KRB_AP_REQ, the server returns the KRB_AP_ERR_MSG_TYPE error ๐Ÿ™…โ€โ™€๏ธ. If the key version the Ticket in the KRB_AP_REQ points to isn't one the server can use (like an old key the server no longer has a copy of), it returns KRB_AP_ERR_BADKEYVER ๐Ÿ”‘.

Now the USE-SESSION-KEY flag in the ap-options field is lowkey a plot twist ๐ŸŽญ. When it's set, it tells the server the ticket was encrypted with the session key from the server's ticket-granting ticket instead of the server's secret key. This is used for user-to-user authentication, as described in [6] ๐Ÿค.

Servers can be registered in multiple realms, and each realm can have different keys for them. So the srealm field in the unencrypted part of the ticket tells the server which secret key to use to decrypt it ๐Ÿ—๏ธ. If the server doesn't have the right key to decipher the ticket, it returns KRB_AP_ERR_NOKEY ๐Ÿ˜ญ.

The ticket gets decrypted using the version of the server's key the ticket specifies. If the decryption routines spot a modification in the ticket, the server returns KRB_AP_ERR_BAD_INTEGRITY. Each encryption system has to include safeguards that catch modified ciphertext (see section 6 for more on that ๐Ÿ›ก๏ธ). The chances are good that different keys were used to encrypt and decrypt, too ๐Ÿ‘€.

Next up, the authenticator gets decrypted using the session key pulled out of the decrypted ticket. If it turns out to have been modified, that's another KRB_AP_ERR_BAD_INTEGRITY ๐Ÿ’€. The client's name and realm from the ticket get compared against the same fields in the authenticator. If they don't match, the server returns KRB_AP_ERR_BADMATCH. They might not match if the wrong session key was used to encrypt the authenticator, which is very much a "not meโ€ฆ" moment ๐Ÿ™ˆ.

Then the addresses in the ticket (if there are any) get searched for one that matches the client's operating-system-reported address ๐Ÿ”. If nothing matches, or the server insists on ticket addresses but the ticket doesn't have any, you get KRB_AP_ERR_BADADDR ๐Ÿšซ.

If the server's time and the client's time in the authenticator are off by more than the allowable clock skew (say, 5 minutes โฑ๏ธ), the server returns KRB_AP_ERR_SKEW. Ugh, so messy.

If the server name, plus the client name, plus the time and microsecond fields from the authenticator match any tuple seen recently, the server returns KRB_AP_ERR_REPEAT ๐Ÿ”. The rejection only applies to authenticators from the same principal going to the same server. Other client principals talking to the same server principal should not have their authenticators rejected just because their time and microsecond fields happen to match somebody else's. Main character energy means nobody gets caught in the crossfire ๐Ÿ’œ.

The server must remember any authenticator presented within the allowable clock skew, so a replay attempt is guaranteed to fail ๐Ÿ”. And if a server loses track of any authenticator presented within that window, it must reject all requests until the clock skew interval has passed. This makes sure that any lost or replayed authenticators fall outside the allowable clock skew and can't be successfully replayed anymore ๐Ÿง . (If this isn't done, an attacker could record the ticket and authenticator sent over the network to a server, then disable the client's host, pose as the disabled host, and replay the ticket and authenticator to subvert the authentication. Yikes ๐Ÿ˜ฑ.)

If the authenticator includes a sequence number, the server saves it for later when it processes KRB_SAFE and/or KRB_PRIV messages ๐Ÿ“. If there's a subkey, the server either saves it for later use or uses it to help generate its own choice of subkey to send back in a KRB_AP_REP message ๐Ÿ—๏ธโœจ.

The server also figures out the age of the ticket: local (server) time minus the start time inside the Ticket โณ. If the start time is later than the current time by more than the allowable clock skew, or the INVALID flag is set in the ticket, the server returns KRB_AP_ERR_TKT_NYV ๐Ÿ™…โ€โ™€๏ธ. Otherwise, if the current time is later than the end time by more than the allowable clock skew, it returns KRB_AP_ERR_TKT_EXPIRED ๐Ÿฅ€.

If all of these checks pass, the server is assured that the client has the credentials of the principal named in the ticket. That means the client has been authenticated to the server ๐ŸŽ‰๐Ÿ‘‘. See section A.10 for pseudocode ๐Ÿงš.

3.2.4. Generation of a KRB_AP_REP message ๐Ÿฆ‹

Usually, a client's request includes both the auth info and its initial request in the same message, so the server doesn't need to explicitly reply to the KRB_AP_REQ ๐Ÿ™Œ. But if mutual authentication is happening (that's where the server authenticates to the client too, not just the client to the server ๐Ÿค), the KRB_AP_REQ message will have MUTUAL-REQUIRED set in its ap-options field, and a KRB_AP_REP message is required in response ๐Ÿ’–. Like the error message, this one can get encapsulated in the application protocol if its "raw" form isn't acceptable to that protocol.

The timestamp and microsecond field in the reply must be the client's timestamp and microsecond field (as provided in the authenticator) ๐Ÿ•ฐ๏ธ. Here's a fun little note ๐Ÿค“:

[Note: In the Kerberos version 4 protocol, the timestamp in the reply was the client's timestamp plus one. This is not necessary in version 5 because version 5 messages are formatted in such a way that it is not possible to create the reply by judicious message surgery (even in encrypted form) without knowledge of the appropriate encryption keys.]

If a sequence number is included, it should be randomly chosen, as described above for the authenticator ๐ŸŽฒ. A subkey may be included if the server wants to negotiate a different subkey ๐Ÿ—๏ธ. The KRB_AP_REP message is encrypted in the session key extracted from the ticket ๐Ÿ”’. See section A.11 for pseudocode ๐Ÿงš.

3.2.5. Receipt of KRB_AP_REP message ๐Ÿ’Œ

If a KRB_AP_REP message comes back, the client uses the session key from the credentials it got for the server to decrypt it. (Quick tea โ˜•: for encrypting the KRB_AP_REP message, the sub-session key isn't used, even if it's present in the Authenticator ๐Ÿ™…โ€โ™€๏ธ.) Then the client checks that the timestamp and microsecond fields match the ones in the Authenticator it sent to the server โฐ. If they match, the client is assured the server is genuine ๐Ÿซถ. The sequence number and subkey (if present) are kept for later use ๐Ÿ“Œ. See section A.12 for pseudocode ๐Ÿงš.

3.2.6. Using the encryption key ๐Ÿ”‘

After the KRB_AP_REQ/KRB_AP_REP exchange has happened, the client and server share an encryption key that the application can use ๐Ÿฅณ. The "true session key" for KRB_PRIV, KRB_SAFE, or other application-specific uses can be picked by the application, based on the subkeys in the KRB_AP_REP message and the authenticator. (Implementations may want to provide routines that choose subkeys based on session keys and random numbers, and that orchestrate a negotiated key to be returned in the KRB_AP_REP message ๐Ÿงฉ.)

Sometimes using this session key is implicit in the protocol. Other times, the method of use has to be chosen from several alternatives ๐Ÿค”. The protocol negotiations for how to use the key (like picking an encryption or checksum type) are left to the application programmer. The Kerberos protocol doesn't constrain the implementation options, so you get total creative freedom ๐Ÿ’…โœจ.

With both the one-way and mutual authentication exchanges, the peers should take care not to send each other sensitive information without proper assurances ๐Ÿšซ๐Ÿ“จ. Applications that need privacy or integrity should use the KRB_AP_REP or KRB_ERROR responses from the server to the client to assure both client and server of their peer's identity ๐Ÿชช. If an application protocol needs privacy for its messages, it can use the KRB_PRIV message (section 3.5) ๐Ÿ”. The KRB_SAFE message (section 3.4) can be used to assure integrity ๐Ÿ›ก๏ธ.


3.3. The Ticket-Granting Service (TGS) Exchange ๐ŸŽ€

Summary

         Message direction       Message type     Section
         1. Client to Kerberos   KRB_TGS_REQ      5.4.1
         2. Kerberos to client   KRB_TGS_REP or   5.4.2
                                 KRB_ERROR        5.9.1

The TGS exchange is when a client talks to the Kerberos Ticket-Granting Server ๐ŸŽŸ๏ธ. A client starts it when it wants auth credentials for a given server (which might be registered in a remote realm), when it wants to renew or validate an existing ticket, or when it wants to get a proxy ticket ๐Ÿงš. In the first case, the client must already have a ticket for the Ticket-Granting Service from the AS exchange. (Usually the ticket-granting ticket shows up when a client first authenticates to the system, like when a user logs in ๐Ÿ”“.)

The TGS message format is almost identical to the AS exchange's. The big difference is that encryption and decryption in the TGS exchange don't happen under the client's key. Instead, the session key from the ticket-granting ticket or renewable ticket is used, or the sub-session key from an Authenticator ๐Ÿ—๏ธ. Like all application servers, the TGS won't accept expired tickets, so once a renewable or ticket-granting ticket expires, the client has to use a separate exchange to get valid tickets ๐Ÿ’€โžก๏ธ๐ŸŽซ.

The TGS exchange has two messages: a request (KRB_TGS_REQ) from the client to the Kerberos Ticket-Granting Server, and a reply (KRB_TGS_REP or KRB_ERROR) ๐Ÿ’Œ. The KRB_TGS_REQ message includes info that authenticates the client, plus a request for credentials ๐Ÿ™‹โ€โ™€๏ธ. The auth info is the authentication header (KRB_AP_REQ), which includes the client's previously obtained ticket-granting, renewable, or invalid ticket ๐ŸŽซ. For the ticket-granting ticket and proxy cases, the request may include one or more of these: a list of network addresses, a collection of typed authorization data to be sealed in the ticket for authorization use by the application server, or additional tickets (we'll get to how those work later ๐Ÿ‘€).

The TGS reply (KRB_TGS_REP) carries the requested credentials, encrypted in the session key from the ticket-granting ticket or renewable ticket. Or, if one is present, it's encrypted in the subsession key from the Authenticator (which is part of the authentication header) ๐Ÿ”. The KRB_ERROR message has an error code and text explaining what went wrong ๐Ÿšจ. It isn't encrypted ๐Ÿ˜ฌ. The KRB_TGS_REP message has info that can be used to detect replays and to match it up with the message it replies to ๐Ÿ”. The KRB_ERROR message also has info for matching it to the message it replies to. But since it isn't encrypted, it can't be used to detect replays or fabrications of such messages ๐Ÿ™ˆโš ๏ธ.

3.3.1. Generation of KRB_TGS_REQ message โœจ

Okay bestie, before the client can even ask the ticket-granting service for anything, it has to figure out which realm the application server is registered in ๐ŸŒ

[Note: This can be accomplished in several ways. It might be known beforehand (since the realm is part of the principal identifier), or it might be stored in a nameserver. Presently, however, this information is obtained from a configuration file. If the realm to be used is obtained from a nameserver, there is a danger of being spoofed if the nameservice providing the realm name is not authenticated. This might result in the use of a realm which has been compromised, and would result in an attacker's ability to compromise the authentication of the application server to the client.] ๐Ÿ™ˆ

If the client doesn't already have a ticket-granting ticket for that realm, it must get one ๐ŸŽŸ๏ธ The first move is to ask the local Kerberos server for a TGT for the destination realm, and it does this by sending a KRB_TGS_REQ message recursively. The server may hand back a TGT for the desired realm, and then we're good to go ๐Ÿ’– Or it may give back a TGT for a realm that's "closer" to the one we want (further along the standard hierarchical path). In that case this step must be repeated, this time with a Kerberos server in the realm named in the TGT we just got ๐Ÿชœ If neither of those happens, the request must be retried with a Kerberos server for a realm higher up in the hierarchy. And that request will need its own TGT for the higher realm, which you get by recursively applying these same directions. It's giving russian doll energy, honestly ๐Ÿช†

Once the client has a TGT for the right realm, it works out which Kerberos servers serve that realm and contacts one ๐Ÿ“ž The list might come from a configuration file or a network service. As long as the secret keys exchanged by realms stay secret, a false Kerberos server can only cause denial of service. Nothing scarier than that, which is a relief ๐Ÿ˜Œ

Just like in the AS exchange, the client may specify a bunch of options in the KRB_TGS_REQ message. It prepares the message with an authentication header tucked into the padata field, and it includes the same fields that KRB_AS_REQ uses, plus a few optional extras: the enc-authorization-data field for application server use, and additional tickets required by some options ๐ŸŽ€

Now the sub-session key part, which is lowkey the most important bit ๐Ÿ” When preparing the authentication header, the client can pick a sub-session key that the Kerberos server's response will be encrypted under. If the client does pick one, care must be taken to make sure the sub-session key is actually random. One approach would be to generate a random number and XOR it with the session key from the ticket-granting ticket. (Don't skip the randomness, bestie, it's literally the whole point ๐Ÿ™…โ€โ™€๏ธ) If the sub-session key isn't specified, the session key from the ticket-granting ticket gets used instead. And if enc-authorization-data is present, it must be encrypted in the sub-session key if one is present in the authenticator portion of the authentication header. If there isn't one, it gets encrypted in the session key from the ticket-granting ticket instead ๐Ÿ”‘

Once it's all prepared, the message gets sent to a Kerberos server for the destination realm ๐Ÿ“ฌ See section A.5 for pseudocode ๐Ÿ’…

3.3.2. Receipt of KRB_TGS_REQ message โœจ

The KRB_TGS_REQ message gets processed in a way that's similar to KRB_AS_REQ, but ngl there are way more checks to do ๐Ÿ˜ญ First up, the Kerberos server has to figure out which server the accompanying ticket is for, and then it picks the right key to decrypt it with. For a normal KRB_TGS_REQ message, the ticket is for the ticket granting service, so the TGS's key gets used. If the TGT was issued by another realm, then the appropriate inter-realm key must be used ๐Ÿ—๏ธ If the accompanying ticket isn't a ticket granting ticket for the current realm, but it IS for an application server in the current realm, and the request has the RENEW, VALIDATE, or PROXY options set, and the server being asked for is the one named in the accompanying ticket, then the KDC decrypts the ticket in the authentication header using the key of the server it was issued for. If no ticket can be found in the padata field at all, the KDC_ERR_PADATA_TYPE_NOSUPP error is returned ๐Ÿšซ

Once the accompanying ticket has been decrypted, the user-supplied checksum in the Authenticator must be verified against the contents of the request. If the checksums don't match, the message gets rejected with the error code KRB_AP_ERR_MODIFIED ๐Ÿ™…โ€โ™€๏ธ The message also gets rejected if the checksum isn't keyed or isn't collision-proof, and that one comes back as KRB_AP_ERR_INAPP_CKSUM. If the checksum type isn't supported, the KDC_ERR_SUMTYPE_NOSUPP error is returned. And if the authorization-data are present, they get decrypted using the sub-session key from the Authenticator ๐Ÿ”“

If any of the decryptions show failed integrity checks, the KRB_AP_ERR_BAD_INTEGRITY error is returned ๐Ÿ’€

3.3.3. Generation of KRB_TGS_REP message โœจ

The KRB_TGS_REP message shares its format with KRB_AS_REP (KRB_KDC_REP), the only difference being that its type field is set to KRB_TGS_REP ๐Ÿฆ‹ The detailed spec lives in section 5.4.2 ๐Ÿ“–

The response will include a ticket for the requested server. The Kerberos database gets queried to pull up the record for that server, including the key the ticket will be encrypted with ๐Ÿ” If the request is for a ticket granting ticket for a remote realm, and no key is shared with that requested realm, then the Kerberos server picks the realm "closest" to the requested one that it DOES share a key with, and uses that realm instead. This is the only case where the response from the KDC will be for a different server than the one the client asked for ๐Ÿ”€

By default, the address field, the client's name and realm, the list of transited realms, the time of initial authentication, the expiration time, and the authorization data of the newly-issued ticket all get copied over from the ticket-granting ticket (TGT) or renewable ticket ๐Ÿ“‹ If the transited field needs to be updated but the transited type isn't supported, the KDC_ERR_TRTYPE_NOSUPP error is returned ๐Ÿ™ƒ

If the request specifies an endtime, then the endtime of the new ticket gets set to the minimum of three things: (a) that request, (b) the endtime from the TGT, and (c) the starttime of the TGT plus the minimum of the maximum life for the application server and the maximum life for the local realm. (The maximum life for the requesting principal was already applied when the TGT got issued, so we don't redo that one ๐Ÿ™Œ) If the new ticket is going to be a renewal, then the endtime above gets replaced by the minimum of (a) the value of the renew_till field of the ticket and (b) the starttime for the new ticket plus the life (endtimestarttime) of the old ticket โณ

If the FORWARDED option has been requested, the resulting ticket will contain the addresses specified by the client ๐Ÿงณ But this option only gets honored if the FORWARDABLE flag is set in the TGT. The PROXY option is kind of similar, since the resulting ticket will also contain the addresses specified by the client. It only gets honored if the PROXIABLE flag in the TGT is set. And the PROXY option will NOT be honored on requests for additional ticket-granting tickets ๐Ÿšท

If the requested start time is absent or points to a time in the past, then the ticket's start time is set to the authentication server's current time โฐ If it points to a time in the future, but the POSTDATED option hasn't been specified or the MAY-POSTDATE flag isn't set in the TGT, then the error KDC_ERR_CANNOT_POSTDATE is returned ๐Ÿ˜ค Otherwise, if the ticket-granting ticket has the MAYPOSTDATE flag set, the resulting ticket gets postdated, and the requested starttime gets checked against the policy of the local realm. If that's acceptable, the ticket's start time is set as requested and the INVALID flag is set ๐ŸงŠ The postdated ticket must be validated before use, which means presenting it to the KDC after the starttime has been reached. But no matter what, the starttime, endtime, or renew_till time of a newly-issued postdated ticket can never extend beyond the renew_till time of the ticket-granting ticket ๐Ÿ›‘

If the ENC-TKT-IN-SKEY option has been specified and an additional ticket was included in the request, the KDC decrypts that additional ticket using the key for the server it was issued to, and it verifies that the ticket really is a ticket-granting ticket ๐Ÿ”Ž If the name of the requested server is missing from the request, the name of the client in the additional ticket gets used. Otherwise, the requested server's name gets compared to the client's name in the additional ticket, and if they're different, the request gets rejected ๐Ÿšซ If the request succeeds, the session key from the additional ticket is used to encrypt the new ticket that gets issued, instead of the key of the server the new ticket is for. This makes user-to-user authentication [6] easy to implement, since it uses ticket-granting ticket session keys in place of secret server keys in situations where those secret keys could be easily compromised ๐Ÿซถ

If the name of the server in the ticket presented to the KDC as part of the authentication header is NOT the ticket-granting server itself, and that server is registered in the realm of the KDC, then the following checks apply. If the RENEW option is requested, the KDC verifies that the RENEWABLE flag is set in the ticket and that the renew_till time is still in the future. If the VALIDATE option is requested, the KDC checks that the starttime has passed and that the INVALID flag is set. If the PROXY option is requested, the KDC checks that the PROXIABLE flag is set in the ticket. If the tests succeed, the KDC issues the appropriate new ticket ๐ŸŽ‰

Whenever a request is made to the ticket-granting server, the presented ticket(s) gets checked against a hot-list of tickets that have been canceled ๐Ÿšจ This hot-list might be implemented by storing a range of issue dates for "suspect tickets." If a presented ticket had an authtime in that range, it gets rejected. That's how a stolen ticket-granting ticket or renewable ticket is kept from being used to get extra tickets (renewals or otherwise) once the theft has been reported ๐Ÿ•ต๏ธโ€โ™€๏ธ Any normal ticket that was obtained before it got reported stolen will still be valid, since those don't need any interaction with the KDC. They just last until their normal expiration time โณ

The ciphertext part of the response in the KRB_TGS_REP message is encrypted in the sub-session key from the Authenticator if one is present. If not, it's encrypted in the session key from the ticket-granting ticket. It is NOT encrypted using the client's secret key ๐Ÿ” Also, the client's key's expiration date and the key version number fields get left out, since those values are stored with the client's database record, and that record isn't needed to satisfy a request based on a ticket-granting ticket ๐Ÿงพ See section A.6 for pseudocode ๐Ÿ’–

3.3.3.1. Encoding the transited field ๐Ÿงš

okay so bestie, when the TGT the client shows the KDC is for the ticket-granting service but was issued from a different realm ๐Ÿ‘€, the KDC looks up the inter-realm key it shares with that realm and uses it to decrypt the ticket. If the ticket is valid, the KDC honors the request, subject to the constraints outlined above in the section describing the AS exchange. The realm part of the client's identity gets taken from the ticket-granting ticket. The name of the realm that issued the TGT gets added to the transited field of the ticket being issued. This is done by reading the transited field from the TGT (which is treated as an unordered set of realm names ๐Ÿซถ), adding the new realm to the set, and then building and writing out its encoded (shorthand) form. That might mean rearranging the existing encoding โœจ.

Note that the ticket-granting service does not add the name of its own realm. Its job is to add the name of the previous realm. This stops a malicious Kerberos server from intentionally leaving out its own name (it could, however, omit other realms' names) ๐Ÿ’€.

The names of neither the local realm nor the principal's realm go in the transited field. They show up elsewhere in the ticket, and both are known to have taken part in authenticating the principal. Since the endpoints aren't included, both local and single-hop inter-realm authentication end up with an empty transited field ๐ŸŒธ.

Because the name of each realm transited gets added, this field could get really long ๐Ÿ˜ญ, so its contents are encoded. The initially supported encoding is optimized for the normal case of inter-realm communication: a hierarchical arrangement of realms using either domain or X.500 style realm names. This encoding, called DOMAIN-X500-COMPRESS, is described now.

Realm names in the transited field are separated by ",". The ",", "\", trailing "."s, and leading spaces " " are special characters. If they're part of a realm name, they must be quoted in the transited field by preceding them with a "\".

A realm name ending with a "." is interpreted as being prepended to the previous realm. For example, the traversal of EDU, MIT.EDU, ATHENA.MIT.EDU, WASHINGTON.EDU, and CS.WASHINGTON.EDU can be encoded as:

"EDU,MIT.,ATHENA.,WASHINGTON.EDU,CS.".

Note that if ATHENA.MIT.EDU or CS.WASHINGTON.EDU were endpoints, they would not be included in this field, and we would have:

"EDU,MIT.,WASHINGTON.EDU"

A realm name beginning with a "/" is interpreted as being appended to the previous realm. (For the purpose of appending, the realm preceding the first listed realm is considered to be the null realm "".) If it's going to stand by itself, it should be preceded by a space " ". For example, the traversal of /COM/HP/APOLLO, /COM/HP, /COM, and /COM/DEC can be encoded as:

"/COM,/HP,/APOLLO, /COM/DEC"

Like the example above, if /COM/HP/APOLLO and /COM/DEC are endpoints, they would not be included in this field, and we would have:

"/COM,/HP"

A null subfield preceding or following a "," indicates that all realms between the previous realm and the next realm have been traversed. (For the purpose of interpreting null subfields, the client's realm is considered to precede those in the transited field, and the server's realm is considered to follow them.) So "," means that all realms along the path between the client and the server have been traversed. ",EDU, /COM," means that all realms from the client's realm up to EDU (in a domain style hierarchy) have been traversed, and that everything from /COM down to the server's realm in an X.500 style has also been traversed. This could happen if the EDU realm in one hierarchy shares an inter-realm key directly with the /COM realm in another hierarchy ๐Ÿฆ‹.

3.3.4. Receipt of KRB_TGS_REP message

When the client receives a KRB_TGS_REP, it's processed the same way as the KRB_AS_REP processing described above. The main difference is that the ciphertext part of the response must be decrypted using the session key from the ticket-granting ticket, not the client's secret key. See section A.7 for pseudocode ๐Ÿ’….

3.4. The KRB_SAFE Exchange

The KRB_SAFE message may be used by clients who need to detect modifications to the messages they exchange. It does this by including a keyed collisionproof checksum of the user data and some control information. The checksum is keyed with an encryption key (usually the last key negotiated via subkeys, or the session key if no negotiation has occurred) ๐Ÿ”.

3.4.1. Generation of a KRB_SAFE message

When an application wants to send a KRB_SAFE message, it collects its data and the appropriate control information, then computes a checksum over them. The checksum algorithm should be some sort of keyed one-way hash function (such as the RSA-MD5-DES checksum algorithm specified in section 6.4.5, or the DES MAC), generated using the sub-session key if present, or the session key. Different algorithms may be selected by changing the checksum type in the message. Unkeyed or non-collision-proof checksums are not suitable for this use ๐Ÿšซ.

The control information for the KRB_SAFE message includes both a timestamp and a sequence number. The designer of an application using the KRB_SAFE message must choose at least one of the two mechanisms. This choice should be based on the needs of the application protocol โœจ.

Sequence numbers are useful when all messages sent will be received by one's peer. Connection state is presently required to maintain the session key, so maintaining the next sequence number shouldn't be an extra problem ๐ŸŽ€.

If the application protocol is expected to tolerate lost messages without them being resent, the timestamp is the appropriate replay detection mechanism. Timestamps are also the appropriate mechanism for multi-cast protocols where all of one's peers share a common sub-session key, but some messages will be sent to a subset of one's peers ๐Ÿงš.

After computing the checksum, the client transmits the information and checksum to the recipient in the message format specified in section 5.6.1 ๐Ÿ“ฆ.

3.4.2. Receipt of KRB_SAFE message

When an application receives a KRB_SAFE message, it verifies it as follows. If any error occurs, an error code is reported for use by the application ๐Ÿ•ต๏ธโ€โ™€๏ธ.

The message is first checked by verifying that the protocol version and type fields match the current version and KRB_SAFE, respectively. A mismatch generates a KRB_AP_ERR_BADVERSION or KRB_AP_ERR_MSG_TYPE error. The application verifies that the checksum used is a collisionproof keyed checksum. If it isn't, a KRB_AP_ERR_INAPP_CKSUM error is generated. The recipient verifies that the operating system's report of the sender's address matches the sender's address in the message, and (if a recipient address is specified or the recipient requires an address) that one of the recipient's addresses appears as the recipient's address in the message. A failed match in either case generates a KRB_AP_ERR_BADADDR error. Then the timestamp and usec and/or the sequence number fields are checked. If timestamp and usec are expected and not present, or they are present but not current, the KRB_AP_ERR_SKEW error is generated. If the server name, along with the client name, time, and microsecond fields from the Authenticator match any recently-seen such tuples, the KRB_AP_ERR_REPEAT error is generated. If an incorrect sequence number is included, or a sequence number is expected but not present, the KRB_AP_ERR_BADORDER error is generated. If neither a timestamp and usec nor a sequence number is present, a KRB_AP_ERR_MODIFIED error is generated ๐Ÿ˜ค.

Finally, the checksum is computed over the data and control information. If it doesn't match the received checksum, a KRB_AP_ERR_MODIFIED error is generated.

If all the checks succeed, the application is assured that the message was generated by its peer and was not modified in transit ๐Ÿ’–.

3.5. The KRB_PRIV Exchange

The KRB_PRIV message may be used by clients who need confidentiality and the ability to detect modifications to exchanged messages. It does this by encrypting the messages and adding control information ๐Ÿ”’.

3.5.1. Generation of a KRB_PRIV message

When an application wants to send a KRB_PRIV message, it collects its data and the appropriate control information (specified in section 5.7.1), then encrypts them under an encryption key (usually the last key negotiated via subkeys, or the session key if no negotiation has occurred). As part of the control information, the client must choose to use either a timestamp or a sequence number (or both). See the discussion in section 3.4.1 for guidelines on which to use ๐ŸŒˆ. After the user data and control information are encrypted, the client transmits the ciphertext and some "envelope" information to the recipient ๐Ÿ’Œ.

3.5.2. Receipt of KRB_PRIV message

When an application receives a KRB_PRIV message, it verifies it as follows. If any error occurs, an error code is reported for use by the application ๐Ÿซ .

The message is first checked by verifying that the protocol version and type fields match the current version and KRB_PRIV, respectively. A mismatch generates a KRB_AP_ERR_BADVERSION or KRB_AP_ERR_MSG_TYPE error. The application then decrypts the ciphertext and processes the resultant plaintext. If decryption shows the data to have been modified, a KRB_AP_ERR_BAD_INTEGRITY error is generated. The recipient verifies that the operating system's report of the sender's address matches the sender's address in the message, and (if a recipient address is specified or the recipient requires an address) that one of the recipient's addresses appears as the recipient's address in the message. A failed match in either case generates a KRB_AP_ERR_BADADDR error. Then the timestamp and usec and/or the sequence number fields are checked. If timestamp and usec are expected and not present, or they are present but not current, the KRB_AP_ERR_SKEW error is generated. If the server name, along with the client name, time, and microsecond fields from the Authenticator match any recently-seen such tuples, the KRB_AP_ERR_REPEAT error is generated. If an incorrect sequence number is included, or a sequence number is expected but not present, the KRB_AP_ERR_BADORDER error is generated. If neither a timestamp and usec nor a sequence number is present, a KRB_AP_ERR_MODIFIED error is generated ๐Ÿšจ.

If all the checks succeed, the application can assume the message was generated by its peer and was transmitted securely, with no intruders able to see the unencrypted contents ๐Ÿ‘‘.

3.6. The KRB_CRED Exchange

The KRB_CRED message may be used by clients who need to send Kerberos credentials from one host to another. It does this by sending the tickets together with encrypted data containing the session keys and other information associated with the tickets ๐Ÿ“.

3.6.1. Generation of a KRB_CRED message

okay so when an app wants to send a KRB_CRED message ๐Ÿ’Œ, it first gets credentials for the remote host using the KRB_TGS exchange โœจ. Then it builds the KRB_CRED message with the ticket or tickets it got back. The session key needed to use each ticket goes in the key field of the matching KrbCredInfo sequence, inside the encrypted part of the message ๐Ÿ”.

Other info about each ticket, from the KRB_TGS exchange, also goes in its matching KrbCredInfo sequence in that encrypted part. Then the current time ๐Ÿ•ฐ๏ธ goes in too, plus the nonce, s-address, and raddress fields, but only if the app specifically needs them. The whole encrypted part gets encrypted under a key that was already agreed on in the KRB_AP exchange ๐Ÿ”’. That's usually the last key negotiated via subkeys, or the session key if no negotiation has occurred.

3.6.2. Receipt of KRB_CRED message

When an app gets a KRB_CRED message, it checks it ๐Ÿง. If anything goes wrong, an error code gets reported so the app can deal with it. First, it checks that the protocol version and type fields match the current version and KRB_CRED, respectively. If they don't match, you get a KRB_AP_ERR_BADVERSION or KRB_AP_ERR_MSG_TYPE error ๐Ÿ™ˆ. Then the app decrypts the ciphertext and processes the plaintext. If decryption shows the data was modified, you get a KRB_AP_ERR_BAD_INTEGRITY error ๐Ÿ’€.

Next up, if present or required, the recipient checks two things ๐Ÿ‘€. First, that the operating system's report of the sender's address matches the sender's address in the message. Second, that one of the recipient's addresses shows up as the recipient's address in the message. A failed match for either one gives a KRB_AP_ERR_BADADDR error. Then the timestamp and usec fields get checked, plus the nonce field if it's required. If the timestamp and usec are missing, or they're there but not current, you get a KRB_AP_ERR_SKEW error โฐ.

If all the checks pass โœ…, the app stores each new ticket in its ticket cache. It stores the session key and the other info from the matching KrbCredInfo sequence in the encrypted part right alongside it ๐Ÿ’œ.


4. The Kerberos Database ๐Ÿ—„๏ธ

The Kerberos server MUST have access to a database holding the principal identifiers and secret keys of the principals to be authenticated. (The Kerberos server doesn't have to live on the same machine as its database ๐Ÿ–ฅ๏ธ. It's doable to store the principal database in something like a network name service, as long as the entries are protected from being disclosed to or modified by unauthorized parties. But we recommend against that strategy, since it can make system management and threat analysis super complicated ๐Ÿ˜ตโ€๐Ÿ’ซ.)

4.1. Database contents

A database entry SHOULD contain at least the following fields ๐Ÿ“‹:

Field                Value

name                 Principal's identifier
key                  Principal's secret key
p_kvno               Principal's key version
max_life             Maximum lifetime for Tickets
max_renewable_life   Maximum total lifetime for renewable
                     Tickets

The name field is an encoding of the principal's identifier ๐Ÿชช. The key field holds an encryption key, which is the principal's secret key ๐Ÿ—๏ธ. (The key can be encrypted before storage under a Kerberos "master key" to protect it in case the database is compromised but the master key is not. In that case, an extra field MUST be added to indicate the master key version used, see below.) The p_kvno field is the key version number of the principal's secret key. The max_life field holds the maximum allowable lifetime (endtime - starttime) for any Ticket issued for this principal. The max_renewable_life field holds the maximum allowable total lifetime for any renewable Ticket issued for this principal. (See section 3.1 for a description of how these lifetimes are used in determining the lifetime of a given Ticket.)

A server MAY provide KDC service to several realms, as long as the database representation gives a way to tell apart principal records whose identifiers differ only in the realm name ๐ŸŒ.

When an application server's key changes, if the change is routine (i.e., not the result of disclosure of the old key), the old key SHOULD be kept by the server until all tickets issued with that key have expired ๐Ÿ•ฐ๏ธ. Because of this, several keys can be active for one principal at the same time. Ciphertext encrypted in a principal's key is always tagged with the version of the key that was used, so the recipient can find the right key for decryption ๐Ÿท๏ธ.

When more than one key is active for a particular principal, that principal has more than one record in the Kerberos database ๐Ÿ“š. The keys and key version numbers differ between the records, while the rest of the fields MAY or may not be the same. Whenever Kerberos issues a ticket, or responds to a request for initial authentication, the most recent key (the one the Kerberos server knows about) is used for encryption โœจ. That's the key with the highest key version number ๐Ÿ‘‘.

4.2. Additional fields

Project Athena's KDC implementation uses additional fields in its database ๐ŸŽ€:

Field        Value

K_kvno       Kerberos' key version
expiration   Expiration date for entry
attributes   Bit field of attributes
mod_date     Timestamp of last modification
mod_name     Modifying principal's identifier

The K_kvno field indicates the key version of the Kerberos master key that the principal's secret key is encrypted under ๐Ÿ”‘.

After an entry's expiration date has passed, the KDC returns an error to any client trying to get tickets as or for the principal ๐Ÿšซ. (A database MAY want two expiration dates: one for the principal, and one for the principal's current key. That lets password aging work independently of the principal's expiration date. However, since responses have limited space, the KDC MUST combine the key expiration and principal expiration date into a single value called key_exp, which is used as a hint to the user to take administrative action.)

The attributes field is a bitfield used to govern operations involving the principal ๐Ÿงฉ. This field MAY be useful alongside user registration procedures for site-specific policy implementations. (Project Athena currently uses it for their user registration process, which is controlled by the system-wide database service, Moira [7].) It can also identify the "string to key" conversion algorithm used for a principal's key. (See the discussion of the padata field in section 5.4.2 for details on why this can be useful.) Other bits indicate that certain ticket options SHOULD NOT be allowed in tickets encrypted under a principal's key, with one bit each ๐Ÿšง:

  • Disallow issuing postdated tickets
  • Disallow issuing forwardable tickets
  • Disallow issuing tickets based on TGT authentication
  • Disallow issuing renewable tickets
  • Disallow issuing proxiable tickets
  • Disallow issuing tickets for which the principal is the server

The mod_date field contains the time of last modification of the entry โฑ๏ธ, and the mod_name field contains the name of the principal which last modified the entry ๐Ÿ™‹โ€โ™€๏ธ.

4.3. Frequently Changing Fields

Some KDC implementations MAY wish to keep track of the last time a particular principal made a request ๐Ÿ‘€. The information they keep can include the time of the last request, the time of the last request for a ticket-granting ticket, the time of the last use of a ticket-granting ticket, or other times ๐Ÿ—“๏ธ. This information can then be returned to the user in the last-req field (see section 5.2).

Other frequently changing information that can be kept is the latest expiration time for any tickets issued using each key โณ. This field would be used to show how long old keys MUST remain valid so outstanding tickets can keep working ๐ŸŽŸ๏ธ.

4.4. Site Constants

The KDC implementation SHOULD have the following configurable constants or options, so an administrator can make and enforce policy decisions ๐Ÿ› ๏ธ:

  • The minimum supported lifetime. This is used to determine whether the KDC_ERR_NEVER_VALID error SHOULD be returned. This constant SHOULD reflect reasonable expectations of round-trip time to the KDC, encryption/decryption time, and processing time by the client and target server. It SHOULD also allow for a minimum "useful" lifetime โฒ๏ธ.
  • The maximum allowable total (renewable) lifetime of a ticket (renew_till - starttime).
  • The maximum allowable lifetime of a ticket (endtime - starttime).
  • Whether to allow the issue of tickets with empty address fields. This includes the ability to specify that such tickets MAY only be issued if the request specifies some authorization_data ๐Ÿ .
  • Whether proxiable, forwardable, renewable or post-datable tickets are to be issued ๐ŸŽซ.

5. Message Specifications

These sections are where the exact contents and encoding of the protocol messages and objects live ๐Ÿ“œ, so details matter here.

The following sections describe the exact contents and encoding of protocol messages and objects ๐Ÿ’Ž. The ASN.1 base definitions come first, in the opening subsection. The remaining subsections specify the protocol objects (tickets and authenticators) and messages ๐Ÿงพ. Specification of encryption and checksum techniques, and the fields related to them, appear in section 6 ๐Ÿ”.

5.1. ASN.1 Distinguished Encoding Representation

All uses of ASN.1 in Kerberos SHALL use the Distinguished Encoding Representation of the data elements, as described in the X.509 specification, section 8.7 [8] ๐ŸŒธ.

5.2. ASN.1 Base Definitions

okay bestie, here come the ASN.1 base definitions ๐Ÿงš we use these all over the rest of this section. quick heads up: ASN.1 names can't have underscores (_), so the hyphen (-) fills in for them ๐Ÿซถ

Realm ::=           GeneralString
PrincipalName ::=   SEQUENCE {
                    name-type[0]     INTEGER,
                    name-string[1]   SEQUENCE OF GeneralString
}

Realms are encoded as GeneralStrings ๐ŸŽ€ A realm shall not contain a character with the code 0 (the ASCII NUL). Most realms have a few components separated by periods (.), kinda like Internet Domain Names, or by slashes (/), like X.500 names ๐Ÿ’… The acceptable forms for realm names are in section 7. A PrincipalName is a typed sequence of components with these sub-fields:

  • name-type tells you what kind of name comes next ๐Ÿฆ‹ The predefined values are in section 7.2. It should be treated as a hint. Ignoring it, no two names can be the same (at least one of the components, or the realm, must be different). This constraint may be eliminated in the future.
  • name-string is a sequence of components that form a name, and each component is encoded as a GeneralString ๐ŸŒธ A PrincipalName and a Realm together form a principal identifier. Most PrincipalNames only have a few components, usually one or two.
KerberosTime ::=   GeneralizedTime
                   -- Specifying UTC time zone (Z)

Timestamps in Kerberos are encoded as GeneralizedTimes โฐ An encoding shall specify the UTC time zone (Z), shall not include any fractional portions of the seconds, and further shall not include any separators. Example: the only valid format for UTC time 6 minutes, 27 seconds after 9 pm on 6 November 1985 is 19851106210627Z ๐Ÿ“

HostAddress ::=     SEQUENCE  {
                    addr-type[0]             INTEGER,
                    address[1]               OCTET STRING
}

HostAddresses ::=   SEQUENCE OF SEQUENCE {
                    addr-type[0]             INTEGER,
                    address[1]               OCTET STRING
}

Host address encodings have two fields ๐ŸŒˆ

  • addr-type says what type of address follows. The predefined values are in section 8.1.
  • address encodes a single address of type addr-type.

The two forms are a little different. HostAddress contains exactly one address, while HostAddresses contains a sequence of possibly many addresses ๐Ÿ‘‘

AuthorizationData ::=   SEQUENCE OF SEQUENCE {
                        ad-type[0]               INTEGER,
                        ad-data[1]               OCTET STRING
}
  • ad-data contains authorization data, to be interpreted according to the value of the matching ad-type field.
  • ad-type specifies the format of the ad-data subfield. All negative values are reserved for local use. Non-negative values are reserved for registered use ๐Ÿ”ฅ
APOptions ::=   BIT STRING {
                reserved(0),
                use-session-key(1),
                mutual-required(2)
}

TicketFlags ::=   BIT STRING {
                  reserved(0),
                  forwardable(1),
                  forwarded(2),
                  proxiable(3),
                  proxy(4),
                  may-postdate(5),
                  postdated(6),
                  invalid(7),
                  renewable(8),
                  initial(9),
                  pre-authent(10),
                  hw-authent(11)
}

KDCOptions ::=   BIT STRING {
                 reserved(0),
                 forwardable(1),
                 forwarded(2),
                 proxiable(3),
                 proxy(4),
                 allow-postdate(5),
                 postdated(6),
                 unused7(7),
                 renewable(8),
                 unused9(9),
                 unused10(10),
                 unused11(11),
                 renewable-ok(27),
                 enc-tkt-in-skey(28),
                 renew(30),
                 validate(31)
}

these three bit strings are giving main character energy ๐Ÿ’œ they're just lists of named bit positions, so the flags you'll see later all come from here โœจ

LastReq ::=   SEQUENCE OF SEQUENCE {
              lr-type[0]               INTEGER,
              lr-value[1]              KerberosTime
}
  • lr-type tells you how to read the lr-value field ๐Ÿฆ‹ Negative values mean the info only applies to the responding server. Non-negative values apply to all servers in the realm.

If lr-type is zero (0), then lr-value conveys no information. If the absolute value of lr-type is one (1), then lr-value is the time of last initial request for a TGT. If it's two (2), lr-value is the time of last initial request. If it's three (3), lr-value is the time of issue for the newest ticket-granting ticket used. If it's four (4), lr-value is the time of the last renewal. If it's five (5), lr-value is the time of last request of any type ๐Ÿ˜ญ

  • lr-value is the time of the last request. You have to read it according to the matching lr-type ๐Ÿ’…

See section 6 for the definitions of Checksum, ChecksumType, EncryptedData, EncryptionKey, EncryptionType, and KeyType ๐ŸŽ€


5.3. Tickets and Authenticators

this section is about the format and encryption parameters for tickets and authenticators ๐Ÿฆ‹ When a ticket or authenticator shows up in a protocol message, it's treated as an opaque object, so nobody pokes inside it ๐Ÿ™ˆ

5.3.1. Tickets

a ticket is a record that helps a client authenticate to a service ๐Ÿซถ A Ticket has the following information:

Ticket ::=                    [APPLICATION 1] SEQUENCE {
                              tkt-vno[0]                   INTEGER,
                              realm[1]                     Realm,
                              sname[2]                     PrincipalName,
                              enc-part[3]                  EncryptedData
}
-- Encrypted part of ticket
EncTicketPart ::=     [APPLICATION 3] SEQUENCE {
                      flags[0]             TicketFlags,
                      key[1]               EncryptionKey,
                      crealm[2]            Realm,
                      cname[3]             PrincipalName,
                      transited[4]         TransitedEncoding,
                      authtime[5]          KerberosTime,
                      starttime[6]         KerberosTime OPTIONAL,
                      endtime[7]           KerberosTime,
                      renew-till[8]        KerberosTime OPTIONAL,
                      caddr[9]             HostAddresses OPTIONAL,
                      authorization-data[10]   AuthorizationData OPTIONAL
}
-- encoded Transited field
TransitedEncoding ::=         SEQUENCE {
                              tr-type[0]  INTEGER, -- must be registered
                              contents[1]          OCTET STRING
}

the EncTicketPart encoding is encrypted with the key shared by Kerberos and the end server (the server's secret key) ๐Ÿ” Check section 6 for the format of the ciphertext ๐Ÿ’–

  • tkt-vno is the version number for the ticket format. This document describes version number 5 โœจ
  • realm is the realm that issued the ticket ๐ŸŒธ It also identifies the realm part of the server's principal identifier. A Kerberos server can only issue tickets for servers in its own realm, so the two will always be identical ๐Ÿ‘‘
  • sname is the name part of the server's identity ๐Ÿงš
  • enc-part holds the encrypted encoding of the EncTicketPart sequence ๐Ÿ”ฅ
  • flags shows which options were used or requested when the ticket was issued ๐ŸŒˆ It's a bit-field: a selected option has its bit set (1), and unselected options and reserved fields are reset (0). Bit 0 is the most significant bit. The encoding of the bits is in section 5.2. The flags are described in more detail above in section 2. Here's what they mean ๐Ÿ’…
Bit(s)    Name        Description

0         RESERVED    Reserved for future expansion of this
                      field.

1         FORWARDABLE The FORWARDABLE flag is normally only
                      interpreted by the TGS, and can be
                      ignored by end servers.  When set,
                      this flag tells the ticket-granting
                      server that it is OK to issue a new
                      ticket- granting ticket with a
                      different network address based on
                      the presented ticket.

2         FORWARDED   When set, this flag indicates that
                      the ticket has either been forwarded
                      or was issued based on authentication
                      involving a forwarded ticket-granting
                      ticket.

3         PROXIABLE   The PROXIABLE flag is normally only
                      interpreted by the TGS, and can be
                      ignored by end servers. The PROXIABLE
                      flag has an interpretation identical
                      to that of the FORWARDABLE flag,
                      except that the PROXIABLE flag tells
                      the ticket-granting server that only
                      non- ticket-granting tickets may be
                      issued with different network
                      addresses.

4         PROXY       When set, this flag indicates that a
                      ticket is a proxy.
             5         MAY-POSTDATE The MAY-POSTDATE flag is normally
                                   only interpreted by the TGS, and can
                                   be ignored by end servers.  This flag
                                   tells the ticket-granting server that
                                   a post- dated ticket may be issued
                                   based on this ticket-granting ticket.

             6         POSTDATED   This flag indicates that this ticket
                                   has been postdated.  The end-service
                                   can check the authtime field to see
                                   when the original authentication
                                   occurred.

             7         INVALID     This flag indicates that a ticket is
                                   invalid, and it must be validated by
                                   the KDC before use.  Application
                                   servers must reject tickets which
                                   have this flag set.

             8         RENEWABLE   The RENEWABLE flag is normally only
                                   interpreted by the TGS, and can
                                   usually be ignored by end servers
                                   (some particularly careful servers
                                   may wish to disallow renewable
                                   tickets).  A renewable ticket can be
                                   used to obtain a replacement ticket
                                   that expires at a later date.

             9         INITIAL     This flag indicates that this ticket
                                   was issued using the AS protocol, and
                                   not issued based on a ticket-granting
                                   ticket.

             10        PRE-AUTHENT This flag indicates that during
                                   initial authentication, the client
                                   was authenticated by the KDC before a
                                   ticket was issued.  The strength of
                                   the preauthentication method is not
                                   indicated, but is acceptable to the
                                   KDC.

             11        HW-AUTHENT  This flag indicates that the protocol
                                   employed for initial authentication
                                   required the use of hardware expected
                                   to be possessed solely by the named
                                   client.  The hardware authentication
                                   method is selected by the KDC and the
                                   strength of the method is not
                                   indicated.

             12-31     RESERVED    Reserved for future use.

okay so the flags are done, and the rest of the ticket fields are next ๐ŸŽ€โœจ

  • key ๐Ÿ”‘: this one lives in the ticket and the KDC response, and it's how the session key gets passed from Kerberos to the application server and the client. Its encoding is described in section 6.2.
  • crealm ๐Ÿ : the name of the realm where the client is registered and where the initial authentication happened.
  • cname ๐Ÿชช: the name part of the client's principal identifier, so basically the "who" ๐Ÿ’…
  • transited ๐Ÿ—บ๏ธ: lists the names of the Kerberos realms that took part in authenticating the user this ticket was issued to. It does NOT tell you the order the realms were transited, and see section 3.3.3.1 for how this field encodes the traversed realms.
  • authtime โฐ: the time of initial authentication for the named principal, which is also the time of issue for the original ticket this one is based on. It's in the ticket to give the end service extra info and to give the KDC what it needs for a hot list service. A super paranoid end service could refuse tickets whose initial authentication happened "too far" in the past, and honestly iconic behaviour ๐Ÿ˜ญ

This field also comes back in the KDC's response. In a KRB_AS_REP (the response to initial authentication), it's the current time on the Kerberos server. It is NOT recommended that this time value be used to adjust the workstation's clock, since the workstation can't reliably tell that a KRB_AS_REP actually came from the proper KDC in a timely manner. ๐Ÿšซโฐ

  • starttime ๐ŸŒ…: the time after which the ticket is valid. Together with endtime, it sets the life of the ticket. If it's absent from the ticket, its value should be treated as that of authtime.
  • endtime ๐ŸŒ™: the time after which the ticket won't be honored, aka its expiration time. Individual services can put their own limits on a ticket's life and may reject tickets that haven't expired yet, so really this is just an upper bound on the expiration time ngl ๐Ÿ’€
  • renew-till ๐Ÿ”: this one only shows up in tickets with the RENEWABLE flag set in the flags field. It's the maximum endtime that can be included in a renewal, so think of it as the absolute expiration time for the ticket, renewals and all ๐Ÿ‘‘
  • caddr ๐Ÿ“: this holds zero (if omitted) or more (if present) host addresses, which are the addresses the ticket can be used from. If there are no addresses, the ticket can be used from any location. Whether the KDC issues, or an end server accepts, zero-address tickets is a policy decision left to the Kerberos and end-service administrators, and they may refuse to issue or accept them. The suggested and default policy is that these tickets are only issued or accepted when extra info that restricts the ticket's use is included in the authorization_data field. Such a ticket is a capability ๐Ÿฆ‹

Network addresses go in the ticket to make it harder for an attacker to use stolen credentials. The session key never gets sent over the network in cleartext, so you can't just steal creds by listening to the wire ๐Ÿ‘‚ An attacker has to get hold of the session key too (maybe through OS security breaches or a careless user's unattended session) before stolen tickets are any use to them.

It's important to note that the network address a connection came from can't be reliably determined. Even if it could, an attacker who compromised the client's workstation could use the credentials from there. Including network addresses only makes it more difficult, not impossible, for an attacker to walk off with stolen credentials and then use them from a "safe" location. ๐Ÿ™ˆ
  • authorization-data ๐ŸŽŸ๏ธ: this field passes authorization data from the principal the ticket was issued on behalf of over to the application service. If no authorization data is included, the field is left out. The data is specific to the end service, and it's expected to contain the names of service-specific objects and the rights to those objects. The format is described in section 5.2. Kerberos doesn't care what the subfields contain, but it does carry type info (ad-type) ๐Ÿ’–

Here's where it gets fun: a principal can use authorization-data to issue a proxy that's valid for one specific purpose. Say a client wants to print a file, so it grabs a file server proxy to pass to the print server. By putting the file's name in authorization-data, the file server knows the print server can only use the client's rights for that particular file ๐Ÿ–จ๏ธโœจ

It's also interesting that if you specify authorization-data on a proxy and leave the host addresses blank, the resulting ticket and session key can be treated as a capability. See [9] for some suggested uses of this field ๐Ÿงš

The authorization-data field is optional, so it doesn't have to be included in a ticket ๐ŸŒธ


5.3.2. Authenticators

An authenticator is a record sent with a ticket to a server ๐Ÿ’œ It's how the client proves it knows the encryption key in the ticket, helps the server catch replays, and helps pick a "true session key" to use for that particular session. The encoding is encrypted in the ticket's session key shared by the client and the server:

Authenticator ::=    [APPLICATION 2] SEQUENCE    {
               authenticator-vno[0]          INTEGER,
               crealm[1]                     Realm,
               cname[2]                      PrincipalName,
               cksum[3]                      Checksum OPTIONAL,
               cusec[4]                      INTEGER,
               ctime[5]                      KerberosTime,
               subkey[6]                     EncryptionKey OPTIONAL,
               seq-number[7]                 INTEGER OPTIONAL,
               authorization-data[8]         AuthorizationData OPTIONAL
                     }

Okay so here's the breakdown of what's in the authenticator ๐Ÿ”โœจ

  • authenticator-vno: this is the version number for the format of the authenticator. This document specifies version 5. ๐ŸŽ€
  • crealm and cname: these are the same as what we already covered for the ticket in section 5.3.1. ๐Ÿ’…
  • cksum: this is a checksum of the the application data that rides along with the KRB_AP_REQ. ๐Ÿงพ
  • cusec: this is the microsecond part of the client's timestamp. Before encryption, its value ranges from 0 to 999999. It usually shows up right alongside ctime, and the two of them work together to give you a reasonably accurate timestamp โฑ๏ธ๐Ÿ’–
  • ctime: this is the current time on the client's host. ๐Ÿ•ฐ๏ธ
  • subkey: this is the client's pick for an encryption key that's used to protect this specific application session. Unless an application says otherwise, if you leave this field out, the session key from the ticket gets used instead. ๐Ÿ—๏ธ
  • seq-number: this optional field carries the initial sequence number for KRB_PRIV or KRB_SAFE messages, for when sequence numbers are used to detect replays (it can also be used by application specific messages too ๐Ÿ™ˆ). When it's in the authenticator, it sets the initial sequence number for messages from the client to the server. When it's in the AP-REP message, the initial sequence number is for messages going from the server to the client. When it's used in KRB_PRIV or KRB_SAFE messages, it goes up by one after each message is sent. ๐Ÿ”ข

Now the serious bit about seq-number ๐Ÿ‘€ For sequence numbers to properly support replay detection, they should be non-repeating, even across connection boundaries. The initial sequence number should be random and uniformly distributed across the full space of possible sequence numbers, so an attacker can't guess it, and so it and the successive sequence numbers don't repeat other sequences. Main character energy for randomness, no cap ๐Ÿ’œ

  • authorization-data: this field is the same as what's described for the ticket in section 5.3.1. It's optional and only shows up when you want extra restrictions placed on using a ticket, on top of the ones already inside the ticket itself. ๐Ÿ”’

5.4. Specifications for the AS and TGS exchanges โœจ

This section lays out the format of the messages that get exchanged between the client and the Kerberos server. If you want the format of the possible error messages, that's over in section 5.9.1 ๐Ÿฆ‹

5.4.1. KRB_KDC_REQ definition

Okay bestie, so the KRB_KDC_REQ message doesn't have a type of its own ๐Ÿ™ƒ Instead, its type is either KRB_AS_REQ or KRB_TGS_REQ, depending on whether the request is for an initial ticket or an additional ticket. Either way, the message gets sent from the client to the Authentication Server to ask for credentials for a service ๐Ÿ’Œ

The message fields are:

AS-REQ ::=         [APPLICATION 10] KDC-REQ
TGS-REQ ::=        [APPLICATION 12] KDC-REQ

KDC-REQ ::=        SEQUENCE {
           pvno[1]               INTEGER,
           msg-type[2]           INTEGER,
           padata[3]             SEQUENCE OF PA-DATA OPTIONAL,
           req-body[4]           KDC-REQ-BODY
}

PA-DATA ::=        SEQUENCE {
           padata-type[1]        INTEGER,
           padata-value[2]       OCTET STRING,
                         -- might be encoded AP-REQ
}

KDC-REQ-BODY ::=   SEQUENCE {
            kdc-options[0]       KDCOptions,
            cname[1]             PrincipalName OPTIONAL,
                         -- Used only in AS-REQ
            realm[2]             Realm, -- Server's realm
                         -- Also client's in AS-REQ
            sname[3]             PrincipalName OPTIONAL,
            from[4]              KerberosTime OPTIONAL,
            till[5]              KerberosTime,
            rtime[6]             KerberosTime OPTIONAL,
            nonce[7]             INTEGER,
            etype[8]             SEQUENCE OF INTEGER, -- EncryptionType,
                         -- in preference order
            addresses[9]         HostAddresses OPTIONAL,
            enc-authorization-data[10]   EncryptedData OPTIONAL,
                         -- Encrypted AuthorizationData encoding
            additional-tickets[11]       SEQUENCE OF Ticket OPTIONAL
}

The fields in this message are:

  • pvno: this is included in each message and specifies the protocol version number. This document specifies protocol version 5. ๐ŸŒธ
  • msg-type: this indicates the type of a protocol message. It's almost always the same as the application identifier associated with a message. It's included to make the identifier more readily accessible to the application. For the KDC-REQ message, this type will be KRB_AS_REQ or KRB_TGS_REQ. ๐Ÿท๏ธ
  • padata: the padata (pre-authentication data) field holds a of authentication information that may be needed before credentials can be issued or decrypted. For requests for additional tickets (KRB_TGS_REQ), this field will include an element with padata-type of PA-TGS-REQ, and its data is an authentication header (a ticket-granting ticket and an authenticator). The checksum in the authenticator (which must be collisionproof) is computed over the KDC-REQ-BODY encoding. In most requests for initial authentication (KRB_AS_REQ) and most replies (KDC-REP), the padata field is left out. ๐Ÿง

The padata field can also carry info for certain extensions to the Kerberos protocol. For example, it might be used to verify a client's identity before any response comes back. That happens with a padata field whose padata-type is PA-ENC-TIMESTAMP, and whose padata-value is defined like this:

padata-type     ::= PA-ENC-TIMESTAMP
padata-value    ::= EncryptedData -- PA-ENC-TS-ENC

PA-ENC-TS-ENC   ::= SEQUENCE {
        patimestamp[0]               KerberosTime, -- client's time
        pausec[1]                    INTEGER OPTIONAL
}

So patimestamp holds the client's time, and pausec holds the microseconds, which can be left out if a client won't make more than one request per second โฐ. The ciphertext (the padata-value) is the PA-ENC-TS-ENC sequence, encrypted with the client's secret key ๐Ÿ”

The padata field can also hold info that helps the KDC or the client pick the key needed to generate or decrypt the response. This form of padata is useful for supporting certain "smartcards" with Kerberos ๐Ÿ’ณ The details of those extensions are beyond the scope of this specification. See [10] for additional uses of this field.

  • padata-type: this tells you how the padata-value element should be interpreted. Negative values of padata-type are reserved for unregistered use, and non-negative values are used for a registered interpretation of the element type. ๐Ÿงฉ
  • req-body: this is a placeholder that marks off the extent of the remaining fields. If a checksum is going to be calculated over the request, it's calculated over an encoding of the KDC-REQ-BODY sequence, which is enclosed within the req-body field. ๐Ÿ“ฆ
  • kdc-options: this field shows up in the KRB_AS_REQ and KRB_TGS_REQ requests to the KDC. It indicates the flags the client wants set on the tickets, plus other info that modifies how the KDC behaves. When it makes sense, an option's name may match the flag that option sets. Even though in most cases the bit in the options field matches the bit in the flags field, that's not guaranteed, so you can't just copy the options field over to the flags field ๐Ÿ™…โ€โ™€๏ธ There are various checks that have to happen before an option gets honored anyway. ๐Ÿซถ

The kdc_options field is a bit-field. Selected options are shown by the bit being set (1), and unselected options and reserved fields are reset (0). The encoding of the bits is specified in section 5.2. The options are described in more detail above in section 2. Here's what the meanings look like:

Bit(s)  Name         Description

0       RESERVED     Reserved for future expansion of this
                     field.

1       FORWARDABLE  The FORWARDABLE option indicates that
                     the ticket to be issued is to have its
                     forwardable flag set.  It may only be
                     set on the initial request, or in a
                     subsequent request if the ticket-
                     granting ticket on which it is based
                     is also forwardable.

2       FORWARDED    The FORWARDED option is only specified
                     in a request to the ticket-granting
                     server and will only be honored if the
                     ticket-granting ticket in the request
                     has its FORWARDABLE bit set.  This
                     option indicates that this is a
                     request for forwarding. The
                     address(es) of the host from which the
                     resulting ticket is to be valid are
                     included in the addresses field of the
                     request.


3       PROXIABLE    The PROXIABLE option indicates that
                     the ticket to be issued is to have its
                     proxiable flag set. It may only be set
                     on the initial request, or in a
                     subsequent request if the ticket-
                     granting ticket on which it is based
                     is also proxiable.

Okay the bit table is giving everything ๐Ÿ’œ Bit 0 is just reserved, bit 1 is FORWARDABLE, bit 2 is FORWARDED, and bit 3 is PROXIABLE. Keep up, we're getting to the rest ๐Ÿงš

             4       PROXY        The PROXY option indicates that this
                                  is a request for a proxy.  This option
                                  will only be honored if the ticket-
                                  granting ticket in the request has its
                                  PROXIABLE bit set.  The address(es) of
                                  the host from which the resulting
                                  ticket is to be valid are included in
                                  the addresses field of the request.

             5       ALLOW-POSTDATE The ALLOW-POSTDATE option indicates
                                  that the ticket to be issued is to
                                  have its MAY-POSTDATE flag set.  It
                                  may only be set on the initial
                                  request, or in a subsequent request if
                                  the ticket-granting ticket on which it
                                  is based also has its MAY-POSTDATE
                                  flag set.

             6       POSTDATED    The POSTDATED option indicates that
                                  this is a request for a postdated
                                  ticket.  This option will only be
                                  honored if the ticket-granting ticket
                                  on which it is based has its MAY-
                                  POSTDATE flag set.  The resulting
                                  ticket will also have its INVALID flag
                                  set, and that flag may be reset by a
                                  subsequent request to the KDC after
                                  the starttime in the ticket has been
                                  reached.

             7       UNUSED       This option is presently unused.

             8       RENEWABLE    The RENEWABLE option indicates that
                                  the ticket to be issued is to have its
                                  RENEWABLE flag set.  It may only be
                                  set on the initial request, or when
                                  the ticket-granting ticket on which
                                  the request is based is also
                                  renewable.  If this option is
                                  requested, then the rtime field in the
                                  request contains the desired absolute
                                  expiration time for the ticket.

             9-26    RESERVED     Reserved for future use.

             27      RENEWABLE-OK The RENEWABLE-OK option indicates that
                                  a renewable ticket will be acceptable
                                  if a ticket with the requested life
                                  cannot otherwise be provided.  If a
                                  ticket with the requested life cannot
                                  be provided, then a renewable ticket
                                  may be issued with a renew-till equal
                                  to the the requested endtime.  The
                                  value of the renew-till field may
                                  still be limited by local limits, or
                                  limits selected by the individual
                                  principal or server.

             28      ENC-TKT-IN-SKEY This option is used only by the
                                  ticket-granting service.  The ENC-
                                  TKT-IN-SKEY option indicates that the
                                  ticket for the end server is to be
                                  encrypted in the session key from the
                                  additional ticket-granting ticket
                                  provided.

             29      RESERVED     Reserved for future use.

             30      RENEW        This option is used only by the
                                  ticket-granting service.  The RENEW
                                  option indicates that the present
                                  request is for a renewal.  The ticket
                                  provided is encrypted in the secret
                                  key for the server on which it is
                                  valid.  This option will only be
                                  honored if the ticket to be renewed
                                  has its RENEWABLE flag set and if the
                                  time in its renew till field has not
                                  passed.  The ticket to be renewed is
                                  passed in the padata field as part of
                                  the authentication header.

             31      VALIDATE     This option is used only by the
                                  ticket-granting service.  The VALIDATE
                                  option indicates that the request is
                                  to validate a postdated ticket.  It
                                  will only be honored if the ticket
                                  presented is postdated, presently has
                                  its INVALID flag set, and would be
                                  otherwise usable at this time.  A
                                  ticket cannot be validated before its
                                  starttime.  The ticket presented for
                                  validation is encrypted in the key of
                                  the server for which it is valid and
                                  is passed in the padata field as part
                                  of the authentication header.

okay so now we get to the field-by-field bits ๐Ÿ’…โœจ these are the ones that tell the KDC what you actually want, bestie ๐ŸŽ€

  • cname and sname ๐Ÿฆ‹ these work exactly like they do for the ticket in section 5.3.1. sname can only be left out when the ENC-TKT-IN-SKEY option is set. If it's missing, the server name gets pulled from the client name inside the ticket you passed in additional-tickets. iykyk ๐Ÿซถ
  • enc-authorization-data ๐Ÿ” this one only shows up in the TGS_REQ form. If it's there, it's the authorization data you want, encrypted under the sub-session key (if one is in the Authenticator) or otherwise under the session key from the ticket-granting ticket. Both of those keys come from the padata field in the KRB_AP_REQ. main character energy, no cap ๐Ÿ‘‘
  • realm ๐ŸŒธ this is the realm part of the server's principal identifier. In the AS exchange, it's also the realm part of the client's principal identifier. โœจ
  • from โฐ this shows up in KRB_AS_REQ and KRB_TGS_REQ ticket requests when you want the ticket postdated. It's the start time you'd like the ticket to have. ๐Ÿงš
  • till ๐Ÿ“… this is the expiration date the client is asking for in a ticket request. ๐Ÿ’œ
  • rtime ๐Ÿ“ this is the requested renew-till time, sent from the client to the KDC in a ticket request. It's optional, so you can skip it. ๐ŸŒˆ
  • nonce ๐ŸŽฒ this one is part of both the KDC request and response. It's meant to hold a random number the client generates. If that same number shows up in the KDC's encrypted response, it proves the response is fresh and nobody replayed it. Nonces must never be re-used, periodt ๐Ÿ’€ Ideally you'd make it random, but if you know the correct time, the time can be good enough.
ngl this part is a whole vibe check โš ๏ธ if you use the time as your nonce, your workstation time has to keep going forward, always (monotonically increasing). If the clock ever gets reset backwards, there's a small but finite chance a nonce gets reused. not me panicking over a clock ๐Ÿ˜ญ
  • etype ๐Ÿ”ฅ this is the encryption algorithm you want used in the response. ๐Ÿ’–
  • addresses ๐Ÿก this is included in the initial request for tickets, and it's optional in requests for additional tickets from the ticket-granting server. It lists the addresses the requested ticket is going to be valid from. Normally that's your client's host. If you're asking for a proxy, this field holds other addresses instead. The KDC usually copies these into the caddr field of the ticket it hands back. ๐ŸŒท
  • additional-tickets ๐ŸŽ€ you can optionally include these in a request to the ticket-granting server. If the ENC-TKT-IN-SKEY option is set, the session key from the additional ticket gets used instead of the server's key to encrypt the new ticket. If you ask for more than one option that needs additional tickets, they get used in the order set by the ordering of the options bits (see kdc-options, above). ๐Ÿงฉ

The application code is going to be either ten (10) or twelve (12), depending on whether you're asking for an initial ticket (AS-REQ) or an additional ticket (TGS-REQ). ๐Ÿฆ‹

The optional fields (addresses, authorization-data and additional-tickets) only get included if they're actually needed to do what the kdc-options field asks for. Less is more, bestie ๐Ÿ’…๐ŸŒˆ

Also, in KRB_TGS_REQ, the protocol version number shows up twice and two different message types show up too ๐Ÿคฏ the KRB_TGS_REQ message has these fields, and so does the authentication header (KRB_AP_REQ) that gets passed in the padata field. double the paperwork, but honestly iconic ๐Ÿซถโœจ

5.4.2. KRB_KDC_REP definition ๐Ÿ’–

okay so the KRB_KDC_REP message is the KDC's reply, and it shows up for both the initial (AS) request and the later (TGS) request ๐ŸŽ€ there's no message type literally called KRB_KDC_REP tho. it's always either KRB_AS_REP or KRB_TGS_REP โœจ and which key gets used to encrypt the ciphertext depends on which one you're getting:

  • KRB_AS_REP: the ciphertext is encrypted in the client's secret key, and the client's key version number goes in the key version number for the encrypted data ๐Ÿ”‘
  • KRB_TGS_REP: the ciphertext is encrypted in the sub-session key from the Authenticator, or if that's missing, the session key from the ticket-granting ticket used in the request ๐Ÿฆ‹ in this case there's no version number in the EncryptedData sequence

the KRB_KDC_REP message contains these fields ๐Ÿ‘‘

AS-REP ::=    [APPLICATION 11] KDC-REP
TGS-REP ::=   [APPLICATION 13] KDC-REP

KDC-REP ::=   SEQUENCE {
              pvno[0]                    INTEGER,
              msg-type[1]                INTEGER,
              padata[2]                  SEQUENCE OF PA-DATA OPTIONAL,
              crealm[3]                  Realm,
              cname[4]                   PrincipalName,
              ticket[5]                  Ticket,
              enc-part[6]                EncryptedData
}

EncASRepPart ::=    [APPLICATION 25[25]] EncKDCRepPart
EncTGSRepPart ::=   [APPLICATION 26] EncKDCRepPart

EncKDCRepPart ::=   SEQUENCE {
            key[0]                       EncryptionKey,
            last-req[1]                  LastReq,
            nonce[2]                     INTEGER,
            key-expiration[3]            KerberosTime OPTIONAL,
            flags[4]                     TicketFlags,
            authtime[5]                  KerberosTime,
            starttime[6]                 KerberosTime OPTIONAL,
            endtime[7]                   KerberosTime,
            renew-till[8]                KerberosTime OPTIONAL,
            srealm[9]                    Realm,
            sname[10]                    PrincipalName,
            caddr[11]                    HostAddresses OPTIONAL
}
NOTE: In EncASRepPart, the application code in the encrypted part of a message provides an additional check that the message was decrypted properly. ๐Ÿ™ˆ

here's the field breakdown, bestie ๐Ÿงš

pvno and msg-type: these are explained above in section 5.4.1. msg-type is either KRB_AS_REP or KRB_TGS_REP. ๐ŸŒธ

padata: this one gets the full treatment in section 5.4.1. one possible use is encoding an alternate "mix-in" string for a string-to-key algorithm (like the one in section 6.3.2) ๐Ÿ“ this is so clutch for transitions when a realm name has to change, like when a company gets acquired. in that case, every existing password-derived entry in the KDC database gets flagged as needing a special mix-in string until the next password change ๐Ÿ˜ญ

crealm, cname, srealm and sname: these are the same as what's described for the ticket in section 5.3.1 ๐Ÿ’…

ticket: the newly-issued ticket, from section 5.3.1 ๐ŸŽ€

enc-part: this is just a placeholder for the ciphertext and related info that makes up the encrypted part of a message. the description of the encrypted part comes right after each appearance of this field, and it's encoded as described in section 6.1 ๐Ÿฆ‹

key: same deal as the ticket in section 5.3.1 ๐Ÿ”‘

last-req: the KDC returns this one, and it tells you the time(s) of a principal's last request โœจ depending on what info is around, that could be the last time a ticket-granting ticket was requested, or the last time a request based on a ticket-granting ticket worked. it might cover all servers in a realm, or just one particular server. some implementations may show this to the user to help spot unauthorized use of their identity ๐Ÿ‘€ it's giving "last login time" vibes from timesharing systems ๐Ÿ’œ

nonce: described above in section 5.4.1 ๐ŸŒˆ

key-expiration: part of the KDC's response, and it says when the client's secret key is due to expire ๐Ÿ—“๏ธ that could be because of password aging or an account expiration. this usually gets left out of the TGS reply, since that response is encrypted in a session key and there's no client info that needs pulling from the KDC database ๐Ÿ™Œ if the expiration is coming up soon, it's up to the application client (usually the login program) to do something about it, like notifying the user โญ

flags, authtime, starttime, endtime, renew-till and caddr: these are copies of the ones in the encrypted part of the attached ticket (see section 5.3.1). they're here so the client can check that they match the request it actually sent, and to help with proper ticket caching ๐Ÿงš if the message is KRB_TGS_REP, caddr only gets filled in if the request was for a proxy or forwarded ticket, or if the user is swapping in a subset of the addresses from the ticket-granting ticket ๐Ÿซถ if the client-requested addresses aren't there or aren't used, the addresses in the ticket will match the ones in the ticket-granting ticket ๐Ÿ’–


5.5. Client/Server (CS) message specifications ๐Ÿฆ‹

this section lays out the format of the messages used to authenticate the client to the application server ๐Ÿ”ฅ

5.5.1. KRB_AP_REQ definition ๐Ÿ’…

the KRB_AP_REQ message holds the Kerberos protocol version number, the message type KRB_AP_REQ, an options field for any options in use, and the ticket and authenticator themselves โœจ people often call the KRB_AP_REQ message the "authentication header" ๐ŸŽ€

AP-REQ ::=      [APPLICATION 14] SEQUENCE {
                pvno[0]                       INTEGER,
                msg-type[1]                   INTEGER,
                ap-options[2]                 APOptions,
                ticket[3]                     Ticket,
                authenticator[4]              EncryptedData
}

APOptions ::=   BIT STRING {
                reserved(0),
                use-session-key(1),
                mutual-required(2)
}

pvno and msg-type: explained above in section 5.4.1. msg-type is KRB_AP_REQ ๐ŸŒธ

ap-options: this field shows up in the application request (KRB_AP_REQ) and changes how the request gets processed ๐Ÿงš it's a bit-field. a selected option has its bit set to 1, and unselected options and reserved fields are reset to 0 ๐Ÿ’œ the bits are encoded as described in section 5.2. here's what the options mean:

Bit(s)  Name           Description

0       RESERVED       Reserved for future expansion of
                     this field.

1       USE-SESSION-KEYThe USE-SESSION-KEY option indicates
                     that the ticket the client is
                     presenting to a server is encrypted in
                     the session key from the server's
                     ticket-granting ticket. When this
                     option is not specified, the ticket is
                     encrypted in the server's secret key.

2       MUTUAL-REQUIREDThe MUTUAL-REQUIRED option tells the
                     server that the client requires mutual
                     authentication, and that it has to
                     respond with a KRB_AP_REP message.

3-31    RESERVED       Reserved for future use.

ticket: this is the ticket that authenticates the client to the server ๐ŸŽ€

authenticator: this holds the authenticator, which includes the client's choice of a subkey ๐Ÿฆ‹ its encoding is described in section 5.3.2 โœจ

5.5.2. KRB_AP_REP definition ๐ŸŒˆ

the KRB_AP_REP message has the Kerberos protocol version number, the message type, and an encrypted timestamp ๐Ÿ” it gets sent back in response to an application request (KRB_AP_REQ) where the mutual authentication option was selected in the ap-options field ๐Ÿซถ

AP-REP ::=         [APPLICATION 15] SEQUENCE {
           pvno[0]                   INTEGER,
           msg-type[1]               INTEGER,
           enc-part[2]               EncryptedData
}

EncAPRepPart ::=   [APPLICATION 27]     SEQUENCE {
           ctime[0]                  KerberosTime,
           cusec[1]                  INTEGER,
           subkey[2]                 EncryptionKey OPTIONAL,
           seq-number[3]             INTEGER OPTIONAL
}
NOTE: in EncAPRepPart, the application code in the encrypted part of a message provides an additional check that the message was decrypted properly. ๐Ÿ™ˆ

the encoded EncAPRepPart is encrypted in the shared session key of the ticket ๐Ÿ”‘ the optional subkey field can be used in an application-arranged negotiation to pick a per-association session key ๐Ÿ“

pvno and msg-type: explained above in section 5.4.1. msg-type is KRB_AP_REP ๐ŸŒธ

enc-part: described above in section 5.4.2 ๐ŸŽ€

ctime: the current time on the client's host โฐ

cusec: the microsecond part of the client's timestamp ๐Ÿงš

subkey: an encryption key used to protect this specific application session ๐Ÿ” see section 3.2.6 for the specifics on how this field negotiates a key. unless an application specifies otherwise, if this field is left out, the sub-session key from the authenticator gets used, or if that's also left out, the session key from the ticket ๐Ÿ’–

5.5.3. Error message reply ๐Ÿฅบ

if something goes wrong while the server processes the application request, a KRB_ERROR message gets sent back ๐Ÿ˜ฌ see section 5.9.1 for its format โœจ the cname and crealm fields can be left out if the server can't work out their right values from the corresponding KRB_AP_REQ message ๐Ÿคทโ€โ™€๏ธ and if the authenticator could be deciphered, the ctime and cusec fields will hold the values from it ๐Ÿ’…

5.6. KRB_SAFE message specification ๐Ÿ›ก๏ธ

this section lays out the format of a message either side of an application (client or server) can use to send a tamper-proof message to its peer ๐Ÿ”ฅ it assumes a session key was already exchanged, for example through the KRB_AP_REQ/KRB_AP_REP messages ๐Ÿซถ

5.6.1. KRB_SAFE definition

โœจ Okay bestie, the KRB_SAFE message is how you send user data together with a collision-proof checksum keyed with the session key. So the data stays readable, but nobody can mess with it without you noticing ๐Ÿ”’๐Ÿ’– Here are the fields:

   KRB-SAFE ::=        [APPLICATION 20] SEQUENCE {
               pvno[0]               INTEGER,
               msg-type[1]           INTEGER,
               safe-body[2]          KRB-SAFE-BODY,
               cksum[3]              Checksum
   }

   KRB-SAFE-BODY ::=   SEQUENCE {
               user-data[0]          OCTET STRING,
               timestamp[1]          KerberosTime OPTIONAL,
               usec[2]               INTEGER OPTIONAL,
               seq-number[3]         INTEGER OPTIONAL,
               s-address[4]          HostAddress,
               r-address[5]          HostAddress OPTIONAL
   }
  • pvno and msg-type: these are described above in section 5.4.1 ๐ŸŽ€ For this message, msg-type is KRB_SAFE.
  • safe-body: this is a placeholder for the body of the KRB-SAFE message ๐Ÿซถ You encode the body separately, then compute the checksum over it for the cksum field.
  • cksum: this holds the checksum of the application data ๐Ÿ” Checksum details live in section 6.4. The checksum is computed over the encoding of the KRB-SAFE-BODY sequence, so the body is the star of the show โœจ
  • user-data: this is the application-specific data being passed from sender to recipient ๐Ÿ’Œ It shows up in both KRB_SAFE and KRB_PRIV messages.
  • timestamp: this is the sender's current time ๐Ÿ•ฐ๏ธ By checking it, the recipient can tell whether the message was recently made, which helps catch replays ๐Ÿšซ It's part of both KRB_SAFE and KRB_PRIV.
  • usec: this is the microsecond part of the timestamp โฑ๏ธ It's part of the KRB_SAFE and KRB_PRIV headers.
  • seq-number: this one is described above in section 5.3.2 ๐Ÿฆ‹
  • s-address: this specifies the address the sender is using ๐Ÿ“
  • r-address: this specifies the address the recipient is using ๐ŸŽฏ It may be left out for some uses (such as broadcast protocols), but the recipient may arbitrarily reject such messages. Together with s-address, it can help spot messages that were delivered to the wrong recipient, or that were tampered with, which is giving detective energy ๐Ÿ•ต๏ธโ€โ™€๏ธ๐Ÿ’…

5.7. KRB_PRIV message specification

๐ŸŒธ This section covers a message either side of an application (client or server) can use to send something to its peer that is both secure and private ๐Ÿ” It assumes a session key has already been exchanged, for example through the KRB_AP_REQ/KRB_AP_REP messages.

5.7.1. KRB_PRIV definition

๐Ÿ’œ The KRB_PRIV message contains user data encrypted in the Session Key. Here are the fields:

   KRB-PRIV ::=         [APPLICATION 21] SEQUENCE {
                pvno[0]                   INTEGER,
                msg-type[1]               INTEGER,
                enc-part[3]               EncryptedData
   }
   EncKrbPrivPart ::=   [APPLICATION 28] SEQUENCE {
                user-data[0]              OCTET STRING,
                timestamp[1]              KerberosTime OPTIONAL,
                usec[2]                   INTEGER OPTIONAL,
                seq-number[3]             INTEGER OPTIONAL,
                s-address[4]              HostAddress, -- sender's addr
                r-address[5]              HostAddress OPTIONAL
                                                      -- recip's addr
   }
๐Ÿšจ NOTE: In EncKrbPrivPart, the application code inside the encrypted part gives an extra check that the message was decrypted properly. Iconic double-check behaviour ๐Ÿ‘‘
  • pvno and msg-type: these are described above in section 5.4.1 ๐Ÿ’– For this message, msg-type is KRB_PRIV.
  • enc-part: this holds an encoding of the EncKrbPrivPart sequence, encrypted under the session key ๐Ÿ”‘ If the encryption method in use supports it, an initialization vector may be passed to the encryption procedure so proper cipher chaining happens ๐Ÿง That IV might come from the last block of the ciphertext in the previous KRB_PRIV message, but the application gets to decide whether to use one ๐Ÿ’… If it's left out, the encryption algorithm's default IV is used. This encrypted encoding goes in the enc-part field of the KRB-PRIV message, and section 6 has the ciphertext format ๐Ÿฆ‹
  • user-data, timestamp, usec, s-address and r-address: these are described above in section 5.6.1 ๐ŸŒˆ
  • seq-number: this one is described above in section 5.3.2 โœจ

5.8. KRB_CRED message specification

๐ŸŽ€ This section covers a message used to send Kerberos credentials from one principal to another ๐Ÿงš It's here to encourage a common mechanism that applications can use when forwarding tickets or providing proxies to subordinate servers. It presumes a session key has already been exchanged, perhaps by using the KRB_AP_REQ/KRB_AP_REP messages.

5.8.1. KRB_CRED definition

๐Ÿ“ The KRB_CRED message holds a sequence of tickets to be sent, plus the info needed to use them, including the session key from each. That info is encrypted under an encryption key that was previously exchanged. Here are the fields:

   KRB-CRED         ::= [APPLICATION 22]   SEQUENCE {
                    pvno[0]                INTEGER,
                    msg-type[1]            INTEGER, -- KRB_CRED
                    tickets[2]             SEQUENCE OF Ticket,
                    enc-part[3]            EncryptedData
   }

   EncKrbCredPart   ::= [APPLICATION 29]   SEQUENCE {
                    ticket-info[0]         SEQUENCE OF KrbCredInfo,
                    nonce[1]               INTEGER OPTIONAL,
                    timestamp[2]           KerberosTime OPTIONAL,
                    usec[3]                INTEGER OPTIONAL,
                    s-address[4]           HostAddress OPTIONAL,
                    r-address[5]           HostAddress OPTIONAL
   }

   KrbCredInfo      ::=                    SEQUENCE {
                    key[0]                 EncryptionKey,
                    prealm[1]              Realm OPTIONAL,
                    pname[2]               PrincipalName OPTIONAL,
                    flags[3]               TicketFlags OPTIONAL,
                    authtime[4]            KerberosTime OPTIONAL,
                    starttime[5]           KerberosTime OPTIONAL,
                    endtime[6]             KerberosTime OPTIONAL
                    renew-till[7]          KerberosTime OPTIONAL,
                    srealm[8]              Realm OPTIONAL,
                    sname[9]               PrincipalName OPTIONAL,
                    caddr[10]              HostAddresses OPTIONAL
   }
  • pvno and msg-type: these are described above in section 5.4.1 ๐Ÿ’ซ For this message, msg-type is KRB_CRED.
  • tickets: these are the tickets obtained from the KDC specifically for the intended recipient ๐ŸŽซ Each successive ticket is paired with the matching KrbCredInfo sequence from the enc-part of the KRB-CRED message.
  • enc-part: this holds an encoding of the EncKrbCredPart sequence, encrypted under the session key shared between the sender and the intended recipient ๐Ÿ” This encrypted encoding goes in the enc-part field of the KRB-CRED message. Section 6 has the ciphertext format.
  • nonce: if it's practical, an application can require a nonce generated by the recipient of the message ๐ŸŽฒ If that same value comes back in the message, it's evidence the message is fresh and wasn't replayed by an attacker. A nonce must never be re-used. It should be generated randomly by the recipient and given to the sender of the message in an application-specific way ๐Ÿ™…โ€โ™€๏ธ
  • timestamp and usec: these specify the time the KRB-CRED message was generated โฐ The time is used to give assurance that the message is fresh.
  • s-address and r-address: these are described above in section 5.6.1 ๐ŸŒธ They're used, optionally, to give extra assurance of the integrity of the KRB-CRED message.
  • key: this field exists in the corresponding ticket passed by the KRB-CRED message ๐Ÿ—๏ธ It's used to pass the session key from sender to intended recipient. Its encoding is described in section 6.2.

The following fields are optional ๐ŸŒท If they're present, they can be associated with the credentials in the remote ticket file. If they're left out, it's assumed the recipient already knows their values.

  • prealm and pname: the name and realm of the delegated principal identity ๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘
  • flags, authtime, starttime, endtime, renew-till, srealm, sname, and caddr: these contain the values of the corresponding fields from the ticket in the ticket field ๐ŸŽ€ Their descriptions are identical to the ones in the KDC-REP message.

5.9. Error message specification

๐Ÿšจ This section specifies the format for the KRB_ERROR message ๐Ÿ’… The fields are meant to return as much information as possible about an error. It's not expected that all the information the fields need will be available for every type of error ๐Ÿ™ˆ If the right info isn't available when the message gets composed, the matching field is left out of the message.

โš ๏ธ Heads up: since the KRB_ERROR message isn't protected by any encryption, it's totally possible for an intruder to make up or modify one ๐Ÿ˜ฑ That means the client should not use any fields in this message for security-critical purposes, like setting a system clock or generating a fresh authenticator. It can still be useful for telling a user why something failed ๐Ÿ’œ

5.9.1. KRB_ERROR definition

okay so the KRB_ERROR message is basically the server's "ope, that didn't work" text ๐Ÿ’… and it's got these fields:

   KRB-ERROR ::=   [APPLICATION 30] SEQUENCE {
                   pvno[0]               INTEGER,
                   msg-type[1]           INTEGER,
                   ctime[2]              KerberosTime OPTIONAL,
                   cusec[3]              INTEGER OPTIONAL,
                   stime[4]              KerberosTime,
                   susec[5]              INTEGER,
                   error-code[6]         INTEGER,
                   crealm[7]             Realm OPTIONAL,
                   cname[8]              PrincipalName OPTIONAL,
                   realm[9]              Realm, -- Correct realm
                   sname[10]             PrincipalName, -- Correct name
                   e-text[11]            GeneralString OPTIONAL,
                   e-data[12]            OCTET STRING OPTIONAL
   }

Let's go field by field, bestie ๐Ÿซถ

pvno and msg-type ๐ŸŽ€ These fields are described above in section 5.4.1. msg-type is KRB_ERROR.

ctime โฐ This field is described above in section 5.4.1.

cusec ๐Ÿฆ‹ This field is described above in section 5.5.2.

stime โญ This field contains the current time on the server. It is of type KerberosTime.

susec ๐ŸŒธ This field contains the microsecond part of the server's timestamp. Its value ranges from 0 to 999. It appears along with stime. The two fields are used in conjunction to specify a reasonably accurate timestamp. so stime plus susec is the combo ๐Ÿงš

error-code ๐Ÿ’€ This field contains the error code returned by Kerberos or the server when a request fails. To interpret the value of this field see the list of error codes in section 8. Implementations are encouraged to provide for national language support in the display of error messages. the people deserve to read their errors in their own language, periodt ๐ŸŒ

crealm, cname, srealm and sname ๐Ÿ“ These fields are described above in section 5.3.1.

e-text ๐Ÿ’ฌ This field contains additional text to help explain the error code associated with the failed request (for example, it might include a principal name which was unknown). basically the "wait, who even is this?" hint ๐Ÿ™ˆ

e-data ๐Ÿ”ฅ This field contains additional data about the error for use by the application to help it recover from or handle the error. If the errorcode is KDC_ERR_PREAUTH_REQUIRED, then the e-data field will contain an encoding of a sequence of padata fields, each corresponding to an acceptable pre-authentication method and optionally containing data for the method:

   METHOD-DATA ::=    SEQUENCE of PA-DATA

If the error-code is KRB_AP_ERR_METHOD, then the e-data field will contain an encoding of the following sequence:

   METHOD-DATA ::=    SEQUENCE {
                      method-type[0]   INTEGER,
                      method-data[1]   OCTET STRING OPTIONAL
    }

method-type will indicate the required alternate method; method-data will contain any required additional information. โœจ


6. Encryption and Checksum Specifications โœจ

okay this section is where the crypto gets serious ๐Ÿ” The Kerberos protocols described in this document are designed to use stream encryption ciphers, which can be simulated using commonly available block encryption ciphers, such as the Data Encryption Standard [11], in conjunction with block chaining and checksum methods [12]. Encryption is used to prove the identities of the network entities participating in message exchanges. The Key Distribution Center for each realm is trusted by all principals registered in that realm to store a secret key in confidence. Proof of knowledge of this secret key is used to verify the authenticity of a principal. the vibe is "prove you know the secret, and you're real" ๐Ÿ‘‘

The KDC uses the principal's secret key (in the AS exchange) or a shared session key (in the TGS exchange) to encrypt responses to ticket requests; the ability to obtain the secret key or session key implies the knowledge of the appropriate keys and the identity of the KDC. The ability of a principal to decrypt the KDC response and present a Ticket and a properly formed Authenticator (generated with the session key from the KDC response) to a service verifies the identity of the principal; likewise the ability of the service to extract the session key from the Ticket and prove its knowledge thereof in a response verifies the identity of the service. so it goes both ways, the client proves itself and the service proves itself ๐Ÿค

The Kerberos protocols generally assume that the encryption used is secure from cryptanalysis; however, in some cases, the order of fields in the encrypted portions of messages are arranged to minimize the effects of poorly chosen keys. It is still important to choose good keys. If keys are derived from user-typed passwords, those passwords need to be well chosen to make brute force attacks more difficult. Poorly chosen keys still make easy targets for intruders. โš ๏ธ bestie, pick a strong password, the internet is watching ๐Ÿ‘€

The following sections specify the encryption and checksum mechanisms currently defined for Kerberos. The encodings, chaining, and padding requirements for each are described. For encryption methods, it is often desirable to place random information (often referred to as a confounder) at the start of the message. The requirements for a confounder are specified with each encryption mechanism. ๐ŸŽฒ

Some encryption systems use a block-chaining method to improve the the security characteristics of the ciphertext. However, these chaining methods often don't provide an integrity check upon decryption. Such systems (such as DES in CBC mode) must be augmented with a checksum of the plaintext which can be verified at decryption and used to detect any tampering or damage. Such checksums should be good at detecting burst errors in the input. If any damage is detected, the decryption routine is expected to return an error indicating the failure of an integrity check. Each encryption type is expected to provide and verify an appropriate checksum. The specification of each encryption method sets out its checksum requirements. the checksum is the bouncer that catches the tampering ๐Ÿšจ

Finally, where a key is to be derived from a user's password, an algorithm for converting the password to a key of the appropriate type is included. It is desirable for the string to key function to be one-way, and for the mapping to be different in different realms. This is important because users who are registered in more than one realm will often use the same password in each, and it is desirable that an attacker compromising the Kerberos server in one realm not obtain or derive the user's key in another. ugh, reusing passwords across realms is a whole vibe nobody wants ๐Ÿ’”

For a discussion of the integrity characteristics of the candidate encryption and checksum methods considered for Kerberos, the the reader is referred to [13]. ๐Ÿ“š

6.1. Encryption Specifications

The following ASN.1 definition describes all encrypted messages. The enc-part field which appears in the unencrypted part of messages in section 5 is a sequence consisting of an encryption type, an optional key version number, and the ciphertext.

   EncryptedData ::=   SEQUENCE {
                       etype[0]     INTEGER, -- EncryptionType
                       kvno[1]      INTEGER OPTIONAL,
                       cipher[2]    OCTET STRING -- ciphertext
   }

etype ๐Ÿง This field identifies which encryption algorithm was used to encipher the cipher. Detailed specifications for selected encryption types appear later in this section.

kvno ๐Ÿ”‘ This field contains the version number of the key under which data is encrypted. It is only present in messages encrypted under long lasting keys, such as principals' secret keys.

cipher ๐ŸŒ™ This field contains the enciphered text, encoded as an OCTET STRING.

The cipher field is generated by applying the specified encryption algorithm to data composed of the message and algorithm-specific inputs. Encryption mechanisms defined for use with Kerberos must take sufficient measures to guarantee the integrity of the plaintext, and we recommend they also take measures to protect against precomputed dictionary attacks. If the encryption algorithm is not itself capable of doing so, the protections can often be enhanced by adding a checksum and a confounder. ๐Ÿ›ก๏ธ

The suggested format for the data to be encrypted includes a confounder, a checksum, the encoded plaintext, and any necessary padding. The msg-seq field contains the part of the protocol message described in section 5 which is to be encrypted. The confounder, checksum, and padding are all untagged and untyped, and their length is exactly sufficient to hold the appropriate item. The type and length is implicit and specified by the particular encryption type being used (etype). The format for the data to be encrypted is described in the following diagram:

         +-----------+----------+-------------+-----+
         |confounder |   check  |   msg-seq   | pad |
         +-----------+----------+-------------+-----+

The format cannot be described in ASN.1, but for those who prefer an ASN.1-like notation:

CipherText ::=   ENCRYPTED       SEQUENCE {
         confounder[0]   UNTAGGED OCTET STRING(conf_length)     OPTIONAL,
         check[1]        UNTAGGED OCTET STRING(checksum_length) OPTIONAL,
         msg-seq[2]      MsgSequence,
         pad             UNTAGGED OCTET STRING(pad_length) OPTIONAL
}

In the above specification, UNTAGGED OCTET STRING(length) is the notation for an octet string with its tag and length removed. It is not a valid ASN.1 type. The tag bits and length must be removed from the confounder since the purpose of the confounder is so that the message starts with random data, but the tag and its length are fixed. For other fields, the length and tag would be redundant if they were included because they are specified by the encryption type. ๐Ÿงฉ

One generates a random confounder of the appropriate length, placing it in confounder; zeroes out check; calculates the appropriate checksum over confounder, check, and msg-seq, placing the result in check; adds the necessary padding; then encrypts using the specified encryption type and the appropriate key. ๐Ÿณ

Unless otherwise specified, a definition of an encryption algorithm that specifies a checksum, a length for the confounder field, or an octet boundary for padding uses this ciphertext format (The ordering of the fields in the CipherText is important. Additionally, messages encoded in this format must include a length as part of the msg-seq field. This allows the recipient to verify that the message has not been truncated. Without a length, an attacker could use a chosen plaintext attack to generate a message which could be truncated, while leaving the checksum intact. Note that if the msg-seq is an encoding of an ASN.1 SEQUENCE or OCTET STRING, then the length is part of that encoding.). Those fields which are not specified will be omitted. ๐Ÿ“ the length is the anti-truncation shield, so don't skip it ๐Ÿ›‘

In the interest of allowing all implementations using a particular encryption type to communicate with all others using that type, the specification of an encryption type defines any checksum that is needed as part of the encryption process. If an alternative checksum is to be used, a new encryption type must be defined. ๐Ÿค

Some cryptosystems require additional information beyond the key and the data to be encrypted. For example, DES, when used in cipher-block-chaining mode, requires an initialization vector. If required, the description for each encryption type must specify the source of such additional information. ๐Ÿฆ‹

6.2. Encryption Keys ๐Ÿ”

Okay bestie, here's how an encryption key gets packed up for the trip! ๐Ÿ’– The sequence below shows the encoding:

EncryptionKey ::=   SEQUENCE {
                    keytype[0]    INTEGER,
                    keyvalue[1]   OCTET STRING
}

keytype tells you what kind of key is sitting in the keyvalue field. It almost always lines up with the encryption algorithm that made the EncryptedData, but more than one algorithm can share the same key type, so it's many to one ๐Ÿฆ‹. That happens when an algorithm swaps in a different checksum for its integrity check, or uses a different chaining mechanism.

keyvalue is the key itself, encoded as an octet string. โœจ

All negative values for the encryption key type are reserved for local use. All non-negative values are reserved for officially assigned type fields and interpretations. Iykyk, the ranges are split on purpose ๐ŸŽ€

6.3. Encryption Systems ๐Ÿง

6.3.1. The NULL Encryption System (null) ๐Ÿซง

If no encryption is in use, the encryption system is called the NULL encryption system. There's no checksum, confounder, or padding in it at all. The ciphertext is literally just the plaintext, no cap ๐Ÿ™ˆ The NULL Key is used by the null encryption system. It's zero octets long, and its keytype is zero (0).

6.3.2. DES in CBC mode with a CRC-32 checksum (des-cbc-crc) ๐Ÿ“

This mode encrypts information under the Data Encryption Standard [11], using the cipher block chaining mode [12]. A CRC-32 checksum (described in ISO 3309 [14]) gets applied to the confounder and message sequence (msg-seq) and then goes into the cksum field. DES blocks are 8 bytes. So the data you're encrypting (the confounder, the checksum, and the message, all stuck together) must be padded out to an 8 byte boundary before encryption. The actual encryption details are identical to those for the des-cbc-md5 mode.

Now the tea โ˜• Since the CRC-32 checksum isn't collision-proof, an attacker could use a probabilistic chosen-plaintext attack to generate a valid message, even if a confounder is used [13]. The use of collision-proof checksums is recommended for environments where these attacks are a real threat. The use of CRC-32 as the checksum for a ticket or authenticator is no longer mandated as an interoperability requirement for Kerberos Version 5 Specification 1 (See section 9.1 for specific details). It's giving "we've moved on" ๐Ÿ’…

6.3.3. DES in CBC mode with an MD4 checksum (des-cbc-md4) ๐ŸŒธ

This mode also encrypts information under the Data Encryption Standard [11], using the cipher block chaining mode [12]. An MD4 checksum (described in [15]) is applied to the confounder and message sequence (msg-seq) and placed in the cksum field. DES blocks are 8 bytes. That means the data to be encrypted (the confounder, checksum, and message combined) must be padded to an 8 byte boundary before encryption. The details of encrypting this data are identical to those for the descbc-md5 mode.

6.3.4. DES in CBC mode with an MD5 checksum (des-cbc-md5) ๐ŸŒˆ

Same squad, different checksum! ๐Ÿ’œ This mode encrypts information under the Data Encryption Standard [11], using the cipher block chaining mode [12]. An MD5 checksum (described in [16]) gets applied to the confounder and message sequence (msg-seq) and placed in the cksum field. DES blocks are 8 bytes, so the data to be encrypted (the confounder, checksum, and message together) must be padded to an 8 byte boundary before encryption.

Plaintext and DES ciphertext are encoded as 8-octet blocks, and those blocks get concatenated to make the 64-bit inputs for the DES algorithms. The first octet supplies the 8 most significant bits (the octet's MSbit becomes the DES input block's MSbit, and so on). The second octet supplies the next 8 bits, and this keeps going until the eighth octet, which supplies the 8 least significant bits ๐Ÿงš

Encryption under DES using cipher block chaining needs an extra input called an initialization vector. Unless otherwise specified, zero should be used as the initialization vector. Kerberos' use of DES requires an 8-octet confounder.

The DES specifications identify some "weak" and "semiweak" keys. Those keys shall not be used for encrypting messages for use in Kerberos. There's also a sneaky extra rule: because of the way keys get derived for encrypting checksums, keys shall not be used if they yield "weak" or "semi-weak" keys when eXclusive-ORed with the constant F0F0F0F0F0F0F0F0. Not today, sus keys ๐Ÿ™…โ€โ™€๏ธ

A DES key is 8 octets of data, and its keytype is one (1). It's made of 56 bits of key plus 8 parity bits, one per octet. The key is encoded as a series of 8 octets written in MSB-first order, and the bits inside the key are also encoded in MSB order. For example, if the encryption key is (B1,B2,...,B7,P1,B8,...,B14,P2,B15,...,B49,P7,B50,...,B56,P8), where B1 through B56 are the key bits in MSB order and P1 through P8 are the parity bits, then the first octet of the key is B1,B2,...,B7,P1 (with B1 as the MSbit). [See the FIPS 81 introduction for reference.] ๐Ÿ“š

To generate a DES key from a text string (password), the string normally needs the realm and each component of the principal's name appended. (In some cases, you might need a different "mix-in" string for compatibility reasons. See the discussion of padata in section 5.4.2.) Then you pad it with ASCII nulls to an 8 byte boundary. That string gets fan-folded and eXclusive-ORed with itself to form an 8 byte DES key. The parity is corrected on that key, and then it's used to generate a DES CBC checksum on the initial string (with the realm and name appended). Next, the parity is corrected on the CBC checksum. If the result matches a "weak" or "semiweak" key from the DES specification, it gets eXclusive-ORed with the constant 00000000000000F0. Finally, the result is returned as the key. Here's the pseudocode, bestie ๐Ÿซถ

string_to_key(string,realm,name) {
     odd = 1;
     s = string + realm;
     for(each component in name) {
          s = s + component;
     }
     tempkey = NULL;
     pad(s); /* with nulls to 8 byte boundary */
     for(8byteblock in s) {
          if(odd == 0)  {
              odd = 1;
              reverse(8byteblock)
          }
          else odd = 0;
          tempkey = tempkey XOR 8byteblock;
     }
     fixparity(tempkey);
     key = DES-CBC-check(s,tempkey);
     fixparity(key);
     if(is_weak_key_key(key))
          key = key XOR 0xF0;
     return(key);
}

6.4. Checksums ๐ŸŽ€

Here's the ASN.1 definition used for a checksum, periodt:

Checksum ::=   SEQUENCE {
               cksumtype[0]   INTEGER,
               checksum[1]    OCTET STRING
}

cksumtype tells you which algorithm generated the accompanying checksum. checksum is the checksum itself, encoded as an octet string.

Detailed specs for selected checksum types show up later in this section. Negative values for the checksum type are reserved for local use. All non-negative values are reserved for officially assigned type fields and interpretations.

Kerberos checksums can be sorted by two properties: whether they're collision-proof, and whether they're keyed. For a collision-proof checksum, it's infeasible to find two plaintexts that produce the same checksum value. A keyed checksum needs a key to perturb or initialize the algorithm. To stop an active attacker from modifying the message stream, unkeyed checksums should only be used when the checksum and message will be encrypted afterwards (for example, the checksums defined as part of the encryption algorithms covered earlier in this section). Collision-proof checksums can be made tamper-proof too, as long as the checksum value gets encrypted before it's put in a message. In that case, the combination of the checksum and the encryption algorithm must be considered a separate checksum algorithm. (For example, RSA-MD5 encrypted using DES is a new checksum algorithm of type RSA-MD5-DES.) For most keyed checksums, plus the encrypted forms of collision-proof checksums, Kerberos prepends a confounder before the checksum is calculated ๐Ÿ’…

6.4.1. The CRC-32 Checksum (crc32) ๐Ÿซง

The CRC-32 checksum calculates a checksum based on a cyclic redundancy check, as described in ISO 3309 [14]. The result is four (4) octets long. CRC-32 is neither keyed nor collision-proof, so its use is not recommended ๐Ÿ™ˆ An attacker using a probabilistic chosen-plaintext attack, as described in [13], might be able to generate an alternative message that satisfies the checksum. If these attacks are a significant threat, collision-proof checksums are recommended.

6.4.2. The RSA MD4 Checksum (rsa-md4) ๐ŸŒธ

The RSA-MD4 checksum calculates a checksum using the RSA MD4 algorithm [15]. The algorithm takes an input message of arbitrary length and produces a 128-bit (16 octet) checksum as output. RSA-MD4 is believed to be collision-proof. ๐Ÿงš

6.4.3. RSA MD4 Cryptographic Checksum Using DES (rsa-md4des) ๐Ÿ‘‘

This checksum is the keyed, collision-proof glow-up of the one above ๐Ÿ’– It prepends an 8 octet confounder before the text, applies the RSA MD4 checksum algorithm, and then encrypts the confounder and the checksum with DES in cipher-block-chaining (CBC) mode. It uses a variant of the key, made by eXclusive-ORing the key with the constant F0F0F0F0F0F0F0F0. (A key variant limits a key to one particular function, which keeps generating a checksum separate from other encryption done with the session key. The constant F0F0F0F0F0F0F0F0 was picked because it keeps key parity intact. DES's properties ruled out using the complement. The same constant is used for a similar purpose in the Message Integrity Check of the Privacy Enhanced Mail standard.) The initialization vector should be zero. The result is 24 octets long, and 8 of those octets are redundant. This checksum is tamper-proof and believed to be collision-proof โœจ

The DES specifications identify some "weak keys". Those keys shall not be used for generating RSA-MD4 checksums for use in Kerberos.

Here's the format for the checksum, in diagram form:

      +--+--+--+--+--+--+--+--
      |  des-cbc(confounder
      +--+--+--+--+--+--+--+--

                    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
                        rsa-md4(confounder+msg),key=var(key),iv=0)  |
                    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

The format can't be described in ASN.1, but here's an ASN.1-like version for those who prefer it:

rsa-md4-des-checksum ::=   ENCRYPTED       UNTAGGED SEQUENCE {
                           confounder[0]   UNTAGGED OCTET STRING(8),
                           check[1]        UNTAGGED OCTET STRING(16)
}

6.4.4. The RSA MD5 Checksum (rsa-md5) ๐Ÿฆ‹

The RSA-MD5 checksum calculates a checksum using the RSA MD5 algorithm [16]. The algorithm takes an input message of arbitrary length and produces a 128-bit (16 octet) checksum as output. RSA-MD5 is believed to be collision-proof. Main character energy ๐Ÿ‘‘๐ŸŒˆ

6.4.5. RSA MD5 Cryptographic Checksum Using DES (rsa-md5des) โœจ

okay bestie, so the RSA-MD5-DES checksum is a keyed collisionproof checksum ๐Ÿ” and it's giving secure. you stick an 8 octet confounder on the front of the text, run it through the RSA MD5 checksum algorithm, then encrypt the confounder and the checksum with DES in cipher-block-chaining (CBC) mode, using a variant of the key. that variant is made by XORing the key with the constant F0F0F0F0F0F0F0F0. the initialization vector SHOULD be zero. the result is 24 octets long, and 8 of those are redundant ๐Ÿซถ this checksum is tamper-proof and believed to be collision-proof ๐Ÿ’…

heads up ๐Ÿ™ˆ the DES specs identify some "weak keys", and those keys SHALL NOT be used for encrypting RSA-MD5 checksums for use in Kerberos.

the format for the checksum looks like this:

   +--+--+--+--+--+--+--+--
   |  des-cbc(confounder
   +--+--+--+--+--+--+--+--

                  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
                      rsa-md5(confounder+msg),key=var(key),iv=0)  |
                  +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

the format can't be written in ASN.1, but if you like an ASN.1-ish vibe, here it is:

rsa-md5-des-checksum ::=   ENCRYPTED       UNTAGGED SEQUENCE {
                           confounder[0]   UNTAGGED OCTET STRING(8),
                           check[1]        UNTAGGED OCTET STRING(16)
}

6.4.6. DES cipher-block chained checksum (des-mac) ๐Ÿ“

the DES-MAC checksum is a whole vibe ๐ŸŒธ you prepend an 8 octet confounder to the plaintext, run DES CBC-mode encryption on the result using the key and an initialization vector of zero, and keep the last block of that ciphertext. then you prepend the same confounder and encrypt the pair with DES in cipher-block-chaining (CBC) mode, using a variant of the key. the variant is the key XORed with the constant F0F0F0F0F0F0F0F0. the initialization vector SHOULD be zero. the result is 128 bits (16 octets) long, and 64 of those bits are redundant. this checksum is tamper-proof and collision-proof ๐Ÿ‘‘

the format for the checksum looks like this:

   +--+--+--+--+--+--+--+--
   |   des-cbc(confounder
   +--+--+--+--+--+--+--+--

                  +-----+-----+-----+-----+-----+-----+-----+-----+
                    des-mac(conf+msg,iv=0,key),key=var(key),iv=0) |
                  +-----+-----+-----+-----+-----+-----+-----+-----+

the format can't be written in ASN.1, but for the ASN.1 fans:

des-mac-checksum ::=    ENCRYPTED       UNTAGGED SEQUENCE {
                        confounder[0]   UNTAGGED OCTET STRING(8),
                        check[1]        UNTAGGED OCTET STRING(8)
}
the DES specs identify some "weak" and "semiweak" keys ๐Ÿ˜ญ those keys SHALL NOT be used for generating DES-MAC checksums for use in Kerberos, and NOR SHALL a key be used whose variant is "weak" or "semi-weak".

6.4.7. RSA MD4 Cryptographic Checksum Using DES alternative (rsa-md4-des-k)

the RSA-MD4-DES-K checksum is a keyed collision-proof checksum ๐Ÿงš you run the RSA MD4 checksum algorithm, then encrypt the result with DES in cipher-block-chaining (CBC) mode, using a DES key as both the key and the initialization vector. the result is 16 octets long. this checksum is tamper-proof and believed to be collision-proof. ngl, this one is the old way of encoding the RSA-MD4-DES checksum, so it's no longer RECOMMENDED ๐Ÿ’€

6.4.8. DES cipher-block chained checksum alternative (desmac-k) ๐Ÿฆ‹

the DES-MAC-K checksum is computed by running DES CBC-mode encryption on the plaintext and using the last block of the ciphertext as the checksum value. it's keyed with an encryption key and an initialization vector. any uses that don't specify an extra initialization vector will just use the key as both key and initialization vector ๐ŸŽ€ the result is 64 bits (8 octets) long. this checksum is tamper-proof and collision-proof. this one is also the old method for encoding the DES-MAC checksum, so it's no longer RECOMMENDED.

the DES specs identify some "weak keys" ๐Ÿ’œ those keys SHALL NOT be used for generating DES-MAC checksums for use in Kerberos.

7. Naming Constraints ๐ŸŒˆ

7.1. Realm Names

okay so realm names are encoded as GeneralStrings, and a realm can technically pick whatever name it wants ๐Ÿ™ƒ but interoperability across realm boundaries needs everyone to agree on how realm names get assigned and what info they imply.

to keep this consistent, each realm MUST conform to these conventions itself, and it MUST require that any realms it shares inter-realm keys with also conform to the conventions, and MUST require the same from its neighbors.

there are currently four styles of realm names: domain, X500, other, and reserved. here's an example of each:

     domain:   host.subdomain.domain (example)
       X500:   C=US/O=OSF (example)
      other:   NAMETYPE:rest/of.name=without-restrictions (example)
   reserved:   reserved, but will not conflict with above

domain names MUST look like domain names: they're made of components separated by periods (.), and they contain neither colons (:) nor slashes (/) ๐Ÿ“

X.500 names contain an equal (=) and can't have a colon (:) before the equal. the realm names for X.500 names are string representations of the names, with components separated by slashes. leading and trailing slashes are not included โœจ

names in the other category MUST begin with a prefix that has no equal (=) or period (.). that prefix MUST be followed by a colon (:) and the rest of the name. all prefixes MUST be assigned before they MAY be used. right now, none are assigned ๐Ÿ™ˆ

the reserved category has strings that don't fall into the first three categories. every name in this category is reserved. it's unlikely any names will get assigned here unless there's a really strong reason not to use the "other" category ๐Ÿ’…

these rules guarantee there won't be conflicts between the different name styles. there are also some extra constraints for realm names in the domain and X.500 categories: a realm name in the domain or X.500 format MUST either be used by the organization that owns the Internet domain name or X.500 name (the one it was assigned to), or, if no such name is registered, the authority to use a realm name MAY be derived from the authority of the parent realm. for example, if there's no domain name for E40.MIT.EDU, then the administrator of the MIT.EDU realm can authorize creating a realm with that name ๐Ÿ‘‘

this works because the organization the parent is assigned to is presumably the one allowed to assign names to its children in the X.500 and domain name systems too ๐Ÿซถ if the parent assigns a realm name without also registering it in the domain name or X.500 hierarchy, it's the parent's job to make sure that a name identical to the child's realm name doesn't show up in the future, unless it's assigned to the same entity as the realm name ๐Ÿ˜ญ

7.2. Principal Names

just like with realm names, we need conventions so everyone agrees on what info a principal name implies. the name-type field in a principal name tells you what kind of info the name implies. the name-type SHOULD be treated as a hint ๐ŸŽ€ ignoring the name type, no two names can be the same (at least one of the components, or the realm, must be different). this constraint MAY be eliminated in the future. these name types are defined:

   name-type      value   meaning
   NT-UNKNOWN       0     Name type not known
   NT-PRINCIPAL     1     Just the name of the principal as in
                          DCE, or for users
   NT-SRV-INST      2     Service and other unique instance (krbtgt)
   NT-SRV-HST       3     Service with host name as instance
                          (telnet, rcommands)
   NT-SRV-XHST      4     Service with host as remaining components
   NT-UID           5     Unique ID

when a name implies nothing beyond being unique at a particular time, the name type PRINCIPAL SHOULD be used. the principal name type SHOULD be used for users, and it might also be used for a unique server ๐ŸŒธ if the name is a unique machine-generated ID that's guaranteed never to be reassigned, the name type UID SHOULD be used. (fair warning: it's generally a bad idea to reassign names of any type, since stale entries might stick around in access control lists ๐Ÿ™ˆ)

if the first component of a name identifies a service, and the remaining components identify an instance of that service in a server-specified way, then the name type SRV-INST SHOULD be used. the Kerberos ticket-granting ticket is a perfect example: its first component is krbtgt, and its second component identifies the realm the ticket is valid for ๐Ÿ‘‘

if the instance is a single component after the service name and it identifies the host the server is running on, then the name type SRV-HST SHOULD be used. this is typically used for Internet services like telnet and the Berkeley R commands ๐Ÿงš if the separate components of the host name show up as successive components after the service name, then the name type SRV-XHST SHOULD be used. this might identify servers on hosts with X.500 names, where the slash (/) could otherwise be ambiguous ๐Ÿฆ‹

the name type UNKNOWN SHOULD be used when the form of the name isn't known. when comparing names, a name of type UNKNOWN will match principals authenticated with names of any type. a principal authenticated with a name of type UNKNOWN, though, will only match other names of type UNKNOWN ๐Ÿ’…

names of any type with an initial component of "krbtgt" are reserved for the Kerberos ticket granting service. check out section 8.2.3 for the form those names take โœจ

7.2.1. Name of server principals

the principal identifier for a server on a host is usually made of two parts ๐ŸŒˆ (1) the realm of the KDC the server is registered with, and (2) a two-component name of type NT-SRV-HST if the host name is an Internet domain name, or a multi-component name of type NT-SRV-XHST if the host name is in a form like X.500 that uses slash (/) separators. the first component of the two- or multi-component name identifies the service, and the later components identify the host. where the host name isn't case sensitive (like Internet domain names), the host name MUST be lower case ๐Ÿ’– for services like telnet and the Berkeley R commands, which run with system privileges, the first component is the string "host" instead of a service-specific identifier ๐Ÿซถ

8. Constants and other defined values

8.1. Host address types ๐ŸŒธ

okay so all the negative values for the host address type are reserved for local use ๐Ÿ™ˆ and every non-negative value is saved for officially assigned type fields and interpretations โœจ

the type values for the addresses below match the defined address family constants in the Berkeley Standard Distributions of Unix. you can find those in <sys/socket.h> with the symbolic names AF_xxx, where xxx is an abbreviation of the address family name ๐Ÿ’…

Internet addresses

internet addresses are 32-bit (4-octet) quantities, encoded in MSB order. the type of internet addresses is two (2) ๐Ÿ’–

CHAOSnet addresses

CHAOSnet addresses are 16-bit (2-octet) quantities, encoded in MSB order. the type of CHAOSnet addresses is five (5) ๐Ÿฆ‹

ISO addresses

ISO addresses are variable-length. the type of ISO addresses is seven (7) ๐Ÿงš

Xerox Network Services (XNS) addresses

XNS addresses are 48-bit (6-octet) quantities, encoded in MSB order. the type of XNS addresses is six (6) ๐ŸŽ€

AppleTalk Datagram Delivery Protocol (DDP) addresses

AppleTalk DDP addresses consist of an 8-bit node number and a 16-bit network number. the first octet of the address is the node number, and the remaining two octets encode the network number in MSB order. the type of AppleTalk DDP addresses is sixteen (16) ๐ŸŒˆ

DECnet Phase IV addresses

DECnet Phase IV addresses are 16-bit addresses, encoded in LSB order. the type of DECnet Phase IV addresses is twelve (12) ๐Ÿซถ

8.2. KDC messages โœจ

8.2.1. IP transport

okay this one is so important bestie ๐Ÿ˜ญ when a client is contacting a Kerberos server (KDC) for a KRB_KDC_REQ request over IP transport, the client SHALL send a UDP datagram that contains only an encoding of the request to port 88 (decimal) at the KDC's IP address. the KDC will then reply with a datagram that contains only an encoding of the reply message, which is either a KRB_ERROR or a KRB_KDC_REP, sent to the sending port at the sender's IP address ๐Ÿ’Œ

8.2.2. OSI transport

during authentication of an OSI client to an OSI server, the mutual authentication of an OSI server to an OSI client, the transfer of credentials from an OSI client to an OSI server, or during exchange of private or integrity checked messages, Kerberos protocol messages MAY be treated as opaque objects. the type of the authentication mechanism will be:

OBJECT IDENTIFIER ::= {iso (1), org(3), dod(5),internet(1),
                       security(5), kerberosv5(2)}

depending on the situation, the opaque object will be an authentication header (KRB_AP_REQ), an authentication reply (KRB_AP_REP), a safe message (KRB_SAFE), a private message (KRB_PRIV), or a credentials message (KRB_CRED). the opaque data contains an application code as specified in the ASN.1 description for each message. Kerberos MAY use the application code to determine the message type ๐Ÿ”ฅ

8.2.3. Name of the TGS

the principal identifier of the ticket-granting service SHALL be composed of three parts: (1) the realm of the KDC issuing the TGS ticket, and (2) a two-part name of type NT-SRVINST, where the first part is "krbtgt" and the second part is the name of the realm that will accept the ticket-granting ticket ๐Ÿ‘‘

for example, a ticket-granting ticket issued by the ATHENA.MIT.EDU realm to be used to get tickets from the ATHENA.MIT.EDU KDC has a principal identifier of "ATHENA.MIT.EDU" (realm) and ("krbtgt", "ATHENA.MIT.EDU") (name). a ticket-granting ticket issued by the ATHENA.MIT.EDU realm to be used to get tickets from the MIT.EDU realm has a principal identifier of "ATHENA.MIT.EDU" (realm) and ("krbtgt", "MIT.EDU") (name) ๐Ÿ“

8.3. Protocol constants and associated values ๐Ÿ’œ

the following tables list the constants used in the protocol and define what they mean. let's go ๐ŸŽ€

---------------+-----------+----------+----------------+---------------
Encryption type|etype value|block size|minimum pad size|confounder size
---------------+-----------+----------+----------------+---------------
NULL                0            1              0              0
des-cbc-crc         1            8              4              8
des-cbc-md4         2            8              0              8
des-cbc-md5         3            8              0              8

-------------------------------+-------------------+-------------
Checksum type                  |sumtype value      |checksum size
-------------------------------+-------------------+-------------
CRC32                           1                   4
rsa-md4                         2                   16
rsa-md4-des                     3                   24
des-mac                         4                   16
des-mac-k                       5                   8
rsa-md4-des-k                   6                   16
rsa-md5                         7                   16
rsa-md5-des                     8                   24

-------------------------------+-----------------
padata type                    |padata-type value
-------------------------------+-----------------
PA-TGS-REQ                      1
PA-ENC-TIMESTAMP                2
PA-PW-SALT                      3

-------------------------------+-------------
authorization data type        |ad-type value
-------------------------------+-------------
reserved values                 0-63
OSF-DCE                         64
SESAME                          65

-------------------------------+-----------------
alternate authentication type  |method-type value
-------------------------------+-----------------
reserved values                 0-63
ATT-CHALLENGE-RESPONSE          64

-------------------------------+-------------
transited encoding type        |tr-type value
-------------------------------+-------------
DOMAIN-X500-COMPRESS            1
reserved values                 all others
--------------+-------+-----------------------------------------
Label         |Value  |Meaning or MIT code
--------------+-------+-----------------------------------------

pvno             5     current Kerberos protocol version number

message types

KRB_AS_REQ      10     Request for initial authentication
KRB_AS_REP      11     Response to KRB_AS_REQ request
KRB_TGS_REQ     12     Request for authentication based on TGT
KRB_TGS_REP     13     Response to KRB_TGS_REQ request
KRB_AP_REQ      14     application request to server
KRB_AP_REP      15     Response to KRB_AP_REQ_MUTUAL
KRB_SAFE        20     Safe (checksummed) application message
KRB_PRIV        21     Private (encrypted) application message
KRB_CRED        22     Private (encrypted) message to forward
                       credentials
KRB_ERROR       30     Error response

name types

KRB_NT_UNKNOWN   0   Name type not known
KRB_NT_PRINCIPAL 1   Just the name of the principal as in DCE, or
                     for users
KRB_NT_SRV_INST  2   Service and other unique instance (krbtgt)
KRB_NT_SRV_HST   3   Service with host name as instance (telnet,
                     rcommands)
KRB_NT_SRV_XHST  4   Service with host as remaining components
KRB_NT_UID       5   Unique ID

error codes

KDC_ERR_NONE                   0   No error
KDC_ERR_NAME_EXP               1   Client's entry in database has
                                   expired
KDC_ERR_SERVICE_EXP            2   Server's entry in database has
                                   expired
KDC_ERR_BAD_PVNO               3   Requested protocol version number
                                   not supported
KDC_ERR_C_OLD_MAST_KVNO        4   Client's key encrypted in old
                                   master key
KDC_ERR_S_OLD_MAST_KVNO        5   Server's key encrypted in old
                                   master key
KDC_ERR_C_PRINCIPAL_UNKNOWN    6   Client not found in Kerberos database
KDC_ERR_S_PRINCIPAL_UNKNOWN    7   Server not found in Kerberos database
KDC_ERR_PRINCIPAL_NOT_UNIQUE   8   Multiple principal entries in
                                   database

KDC_ERR_NULL_KEY               9   The client or server has a null key
KDC_ERR_CANNOT_POSTDATE       10   Ticket not eligible for postdating
KDC_ERR_NEVER_VALID           11   Requested start time is later than
                                   end time
KDC_ERR_POLICY                12   KDC policy rejects request
KDC_ERR_BADOPTION             13   KDC cannot accommodate requested
                                   option
KDC_ERR_ETYPE_NOSUPP          14   KDC has no support for encryption
                                   type
KDC_ERR_SUMTYPE_NOSUPP        15   KDC has no support for checksum type
KDC_ERR_PADATA_TYPE_NOSUPP    16   KDC has no support for padata type
KDC_ERR_TRTYPE_NOSUPP         17   KDC has no support for transited type
KDC_ERR_CLIENT_REVOKED        18   Clients credentials have been revoked
KDC_ERR_SERVICE_REVOKED       19   Credentials for server have been
                                   revoked
KDC_ERR_TGT_REVOKED           20   TGT has been revoked
KDC_ERR_CLIENT_NOTYET         21   Client not yet valid - try again
                                   later
KDC_ERR_SERVICE_NOTYET        22   Server not yet valid - try again
                                   later
KDC_ERR_KEY_EXPIRED           23   Password has expired - change
                                   password to reset
KDC_ERR_PREAUTH_FAILED        24   Pre-authentication information
                                   was invalid
KDC_ERR_PREAUTH_REQUIRED      25   Additional pre-authentication
                                   required*
KRB_AP_ERR_BAD_INTEGRITY      31   Integrity check on decrypted field
                                   failed
KRB_AP_ERR_TKT_EXPIRED        32   Ticket expired
KRB_AP_ERR_TKT_NYV            33   Ticket not yet valid
KRB_AP_ERR_REPEAT             34   Request is a replay
KRB_AP_ERR_NOT_US             35   The ticket isn't for us
KRB_AP_ERR_BADMATCH           36   Ticket and authenticator don't match
KRB_AP_ERR_SKEW               37   Clock skew too great
KRB_AP_ERR_BADADDR            38   Incorrect net address
KRB_AP_ERR_BADVERSION         39   Protocol version mismatch
KRB_AP_ERR_MSG_TYPE           40   Invalid msg type
KRB_AP_ERR_MODIFIED           41   Message stream modified
KRB_AP_ERR_BADORDER           42   Message out of order
KRB_AP_ERR_BADKEYVER          44   Specified version of key is not
                                   available
KRB_AP_ERR_NOKEY              45   Service key not available
KRB_AP_ERR_MUT_FAIL           46   Mutual authentication failed
KRB_AP_ERR_BADDIRECTION       47   Incorrect message direction
KRB_AP_ERR_METHOD             48   Alternative authentication method
                                   required*
KRB_AP_ERR_BADSEQ             49   Incorrect sequence number in message
KRB_AP_ERR_INAPP_CKSUM        50   Inappropriate type of checksum in

                                   message
KRB_ERR_GENERIC               60   Generic error (description in e-text)
KRB_ERR_FIELD_TOOLONG         61   Field is too long for this
                                   implementation

*this error carries additional information in the e-data field. the contents of the e-data field for this message are described in section 5.9.1 ๐Ÿฅบ

9. Interoperability requirements โœจ

okay so Kerberos v5 is literally packed with options ๐Ÿ’… like multiple encryption and checksum types, different encodings for the transited field, optional pre-auth stuff, what to do with tickets that have no addresses, mutual auth, user to user auth, proxies, forwarding, postdating, renewing tickets, the format of realm names, and how authorization data gets handled ๐Ÿซถ. Since everyone's doing their own thing, realms need a minimal configuration that all implementations MUST support so they can actually talk to each other ๐Ÿฆ‹. This minimal config is allowed to change as tech does ๐Ÿ”„. Like, if one of the required encryption or checksum algorithms turns out to be insecure, it'll get replaced ๐Ÿ’€.

9.1. Specification 1

this section defines the first spec of these options ๐Ÿ“œ. Implementations that are configured this way can be said to support Kerberos Version 5 Specification 1 (5.1) ๐ŸŽ€.

Encryption and checksum methods

the following mechanisms MUST be supported ๐Ÿ”. Implementations MAY support other mechanisms as well, but the additional mechanisms MAY only be used when communicating with principals known to also support them:

  • Encryption: DES-CBC-MD5
  • Checksums: CRC-32, DES-MAC, DES-MAC-K, and DES-MD5

Realm Names

All implementations MUST understand hierarchical realms in both the Internet Domain and the X.500 style ๐ŸŒธ. When a ticket granting ticket for an unknown realm is requested, the KDC MUST be able to determine the names of the intermediate realms between the KDC's realm and the requested realm ๐Ÿ—บ๏ธ.

Transited field encoding

DOMAIN-X500-COMPRESS (described in section 3.3.3.1) MUST be supported ๐Ÿงš. Alternative encodings MAY be supported, but they MAY be used only when that encoding is supported by all intermediate realms ๐Ÿ“.

Pre-authentication methods

The TGS-REQ method MUST be supported ๐Ÿ’œ. The TGS-REQ method is not used on the initial request. The PA-ENC-TIMESTAMP method MUST be supported by clients, but whether it is enabled by default MAY be determined on a realm by realm basis ๐ŸŽ€. If it is not used in the initial request and the error KDC_ERR_PREAUTH_REQUIRED is returned specifying PA-ENCTIMESTAMP as an acceptable method, the client SHOULD retry the initial request using the PA-ENC-TIMESTAMP preauthentication method ๐Ÿ”. Servers need not support the PAENC-TIMESTAMP method, but if it is not supported, the server SHOULD ignore the presence of PA-ENC-TIMESTAMP pre-authentication in a request ๐Ÿ™ˆ.

Mutual authentication

Mutual authentication (via the KRB_AP_REP message) MUST be supported ๐Ÿซถ.

Ticket addresses and flags

All KDC's MUST pass on tickets that carry no addresses (i.e., if a TGT contains no addresses, the KDC will return derivative tickets) ๐ŸŽŸ๏ธ. Each realm MAY set its own policy for issuing such tickets, and each application server will set its own policy with respect to accepting them. By default, servers SHOULD NOT accept them ๐Ÿšซ.

Proxies and forwarded tickets MUST be supported ๐Ÿฆ‹. Individual realms and application servers can set their own policy on when such tickets will be accepted โœจ.

All implementations MUST recognize renewable and postdated tickets, but need not actually implement them ๐Ÿ‘‘. If these options are not supported, the starttime and endtime in the ticket SHALL specify a ticket's entire useful life โณ. When a postdated ticket is decoded by a server, all implementations SHALL make the presence of the postdated flag visible to the calling server ๐Ÿ‘€.

User-to-user authentication

Support for user to user authentication (via the ENC-TKTIN-SKEY KDC option) MUST be provided by implementations ๐Ÿค. However, individual realms MAY decide, as a matter of policy, to reject such requests on a per-principal or realm-wide basis ๐Ÿ™…โ€โ™€๏ธ.

Authorization data

Implementations MUST pass all authorization data subfields from ticket-granting tickets to any derivative tickets, unless directed to suppress a subfield as part of the definition of that registered subfield type ๐ŸŽ€. (It is never incorrect to pass on a subfield, and no registered subfield types presently specify suppression at the KDC.)

Implementations MUST make the contents of any authorization data subfields available to the server when a ticket is used ๐Ÿ”. Implementations are not REQUIRED to allow clients to specify the contents of the authorization data fields ๐Ÿ’….

here's a list of recommended values for a KDC implementation, based on the list of suggested configuration constants (see section 4.4) ๐Ÿ“.

minimum lifetime                5 minutes

maximum renewable lifetime      1 week

maximum ticket lifetime         1 day

empty addresses                 only when suitable restrictions appear
                                in authorization data

proxiable, etc.                 Allowed.

10. Acknowledgments ๐ŸŽ€

okay the early versions of this doc, which described version 4 of the protocol, were written by Jennifer Steiner (formerly at Project Athena) ๐ŸŒท. Those drafts gave us an excellent starting point for this version 5 spec ๐Ÿ’–. Tons of people in the Internet community have contributed ideas and suggested protocol changes for version 5 ๐ŸŒˆ. Notable contributions came from Ted Anderson, Steve Bellovin and Michael Merritt [17], Daniel Bernstein, Mike Burrows, Donald Davis, Ravi Ganesan, Morrie Gasser, Virgil Gligor, Bill Griffeth, Mark Lillibridge, Mark Lomas, Steve Lunt, Piers McMahon, Joe Pato, William Sommerfeld, Stuart Stubblebine, Ralph Swick, Ted T'so, and Stanley Zanarotti ๐Ÿ‘‘. Many others commented and helped shape this spec into its current form ๐Ÿซถ.

11. References ๐Ÿ“š

[1]  Miller, S., Neuman, C., Schiller, J., and  J. Saltzer, "Section
     E.2.1: Kerberos  Authentication and Authorization System",
     M.I.T. Project Athena, Cambridge, Massachusetts, December 21,
     1987.

[2]  Steiner, J., Neuman, C., and J. Schiller, "Kerberos: An
     Authentication Service for Open Network Systems", pp. 191-202 in
     Usenix Conference Proceedings, Dallas, Texas, February, 1988.

[3]  Needham, R., and M. Schroeder, "Using Encryption for
     Authentication in Large Networks of Computers", Communications
     of the ACM, Vol. 21 (12), pp. 993-999, December 1978.

[4]  Denning, D., and G. Sacco, "Time stamps in Key Distribution
     Protocols", Communications of the ACM, Vol. 24 (8), pp. 533-536,
     August 1981.

[5]  Kohl, J., Neuman, C., and T. Ts'o, "The Evolution of the
     Kerberos Authentication Service", in an IEEE Computer Society
     Text soon to be published, June 1992.

[6]  Davis, D., and R. Swick, "Workstation Services and Kerberos
     Authentication at Project Athena", Technical Memorandum TM-424,
     MIT Laboratory for Computer Science, February 1990.

[7]  Levine, P., Gretzinger, M, Diaz, J., Sommerfeld, W., and K.
     Raeburn, "Section E.1: Service Management System, M.I.T.
     Project Athena, Cambridge, Mas sachusetts (1987).

[8]  CCITT, Recommendation X.509: The Directory Authentication
     Framework, December 1988.

[9]  Neuman, C., "Proxy-Based Authorization and Accounting for
     Distributed Systems," in Proceedings of the 13th International
     Conference on Distributed Computing Systems", Pittsburgh, PA,
     May 1993.

[10] Pato, J., "Using Pre-Authentication to Avoid Password Guessing
     Attacks", Open Software Foundation DCE Request for Comments 26,
     December 1992.

[11] National Bureau of Standards, U.S. Department of Commerce, "Data
     Encryption Standard", Federal Information Processing Standards
     Publication 46, Washington, DC (1977).

[12] National Bureau of Standards, U.S. Department of Commerce, "DES
     Modes of Operation", Federal Information Processing Standards
     Publication 81, Springfield, VA, December 1980.

[13] Stubblebine S., and V. Gligor, "On Message Integrity in
     Cryptographic Protocols", in Proceedings of the IEEE Symposium
     on Research in Security and Privacy, Oakland, California, May
     1992.

[14] International Organization for Standardization, "ISO Information
     Processing Systems - Data Communication High-Level Data Link
     Control Procedure - Frame Structure", IS 3309, October 1984, 3rd
     Edition.

[15] Rivest, R., "The MD4 Message Digest Algorithm", RFC 1320, MIT
     Laboratory for Computer Science, April 1992.

[16] Rivest, R., "The MD5 Message Digest Algorithm", RFC 1321, MIT
     Laboratory for Computer Science, April 1992.

[17] Bellovin S., and M. Merritt, "Limitations of the Kerberos
     Authentication System", Computer Communications Review, Vol.
     20(5), pp. 119-132, October 1990.

12. Security Considerations ๐Ÿ”

okay this one is short but SO important ๐Ÿ’€: security issues are discussed throughout this memo ๐Ÿšจ.

13. Authors' Addresses ๐Ÿ’Œ

John Kohl
Digital Equipment Corporation
110 Spit Brook Road, M/S ZKO3-3/U14
Nashua, NH  03062

Phone: 603-881-2481
EMail: jtkohl@zk3.dec.com


B. Clifford Neuman
USC/Information Sciences Institute
4676 Admiralty Way #1001
Marina del Rey, CA 90292-6695

Phone: 310-822-1511
EMail: bcn@isi.edu

A. Pseudo-code for protocol processing โœจ

this appendix is the pseudo-code for how clients and servers build and read the messages ๐Ÿง‘โ€๐Ÿ’ป๐Ÿ’–.

A.1. KRB_AS_REQ generation

      request.pvno := protocol version; /* pvno = 5 */
      request.msg-type := message type; /* type = KRB_AS_REQ */

      if(pa_enc_timestamp_required) then
              request.padata.padata-type = PA-ENC-TIMESTAMP;
              get system_time;
              padata-body.patimestamp,pausec = system_time;
              encrypt padata-body into request.padata.padata-value
                      using client.key; /* derived from password */
      endif

      body.kdc-options := users's preferences;
      body.cname := user's name;
      body.realm := user's realm;
      body.sname := service's name; /* usually "krbtgt",
                                       "localrealm" */
      if (body.kdc-options.POSTDATED is set) then
              body.from := requested starting time;
      else
              omit body.from;
      endif
      body.till := requested end time;
      if (body.kdc-options.RENEWABLE is set) then
              body.rtime := requested final renewal time;
      endif
      body.nonce := random_nonce();
      body.etype := requested etypes;
      if (user supplied addresses) then
              body.addresses := user's addresses;
      else
              omit body.addresses;
      endif
      omit body.enc-authorization-data;
      request.req-body := body;

      kerberos := lookup(name of local kerberos server (or servers));
      send(packet,kerberos);

      wait(for response);
      if (timed_out) then
              retry or use alternate server;
      endif

A.2. KRB_AS_REQ verification and KRB_AS_REP generation โœจ

Okay bestie, this is where the KDC gets the AS request and does all the checking ๐Ÿ”๐Ÿ’… Let's look at the full pseudo-code.

        decode message into req;

        client := lookup(req.cname,req.realm);
        server := lookup(req.sname,req.realm);
        get system_time;
        kdc_time := system_time.seconds;

        if (!client) then
                /* no client in Database */
                error_out(KDC_ERR_C_PRINCIPAL_UNKNOWN);
        endif
        if (!server) then
                /* no server in Database */
                error_out(KDC_ERR_S_PRINCIPAL_UNKNOWN);
        endif

        if(client.pa_enc_timestamp_required and
           pa_enc_timestamp not present) then
                error_out(KDC_ERR_PREAUTH_REQUIRED(PA_ENC_TIMESTAMP));
        endif

        if(pa_enc_timestamp present) then
                decrypt req.padata-value into decrypted_enc_timestamp
                        using client.key;
                        using auth_hdr.authenticator.subkey;
                if (decrypt_error()) then
                        error_out(KRB_AP_ERR_BAD_INTEGRITY);
                if(decrypted_enc_timestamp is not within allowable
                        skew) then error_out(KDC_ERR_PREAUTH_FAILED);
                endif
                if(decrypted_enc_timestamp and usec is replay)
                        error_out(KDC_ERR_PREAUTH_FAILED);
                endif
                add decrypted_enc_timestamp and usec to replay cache;
        endif

        use_etype := first supported etype in req.etypes;

        if (no support for req.etypes) then
                error_out(KDC_ERR_ETYPE_NOSUPP);
        endif

        new_tkt.vno := ticket version; /* = 5 */
        new_tkt.sname := req.sname;
        new_tkt.srealm := req.srealm;
        reset all flags in new_tkt.flags;
        /* It should be noted that local policy may affect the  */
        /* processing of any of these flags.  For example, some */
        /* realms may refuse to issue renewable tickets         */

        if (req.kdc-options.FORWARDABLE is set) then
                set new_tkt.flags.FORWARDABLE;
        endif
        if (req.kdc-options.PROXIABLE is set) then
                set new_tkt.flags.PROXIABLE;
        endif
        if (req.kdc-options.ALLOW-POSTDATE is set) then
                set new_tkt.flags.ALLOW-POSTDATE;
        endif
        if ((req.kdc-options.RENEW is set) or
            (req.kdc-options.VALIDATE is set) or
            (req.kdc-options.PROXY is set) or
            (req.kdc-options.FORWARDED is set) or
            (req.kdc-options.ENC-TKT-IN-SKEY is set)) then
                error_out(KDC_ERR_BADOPTION);
        endif

        new_tkt.session := random_session_key();
        new_tkt.cname := req.cname;
        new_tkt.crealm := req.crealm;
        new_tkt.transited := empty_transited_field();

        new_tkt.authtime := kdc_time;

        if (req.kdc-options.POSTDATED is set) then
           if (against_postdate_policy(req.from)) then
                error_out(KDC_ERR_POLICY);
           endif
           set new_tkt.flags.INVALID;
           new_tkt.starttime := req.from;
        else
           omit new_tkt.starttime; /* treated as authtime when
                                      omitted */
        endif
        if (req.till = 0) then
                till := infinity;
        else
                till := req.till;
        endif

        new_tkt.endtime := min(till,
                              new_tkt.starttime+client.max_life,
                              new_tkt.starttime+server.max_life,
                              new_tkt.starttime+max_life_for_realm);

        if ((req.kdc-options.RENEWABLE-OK is set) and
            (new_tkt.endtime < req.till)) then
                /* we set the RENEWABLE option for later processing */
                set req.kdc-options.RENEWABLE;
                req.rtime := req.till;
        endif

        if (req.rtime = 0) then
                rtime := infinity;
        else
                rtime := req.rtime;
        endif

        if (req.kdc-options.RENEWABLE is set) then
                set new_tkt.flags.RENEWABLE;
                new_tkt.renew-till := min(rtime,
                new_tkt.starttime+client.max_rlife,
                new_tkt.starttime+server.max_rlife,
                new_tkt.starttime+max_rlife_for_realm);
        else
                omit new_tkt.renew-till; /* only present if RENEWABLE */
        endif

        if (req.addresses) then
                new_tkt.caddr := req.addresses;
        else
                omit new_tkt.caddr;
        endif

        new_tkt.authorization_data := empty_authorization_data();

        encode to-be-encrypted part of ticket into OCTET STRING;
        new_tkt.enc-part := encrypt OCTET STRING
            using etype_for_key(server.key), server.key, server.p_kvno;


        /* Start processing the response */

        resp.pvno := 5;
        resp.msg-type := KRB_AS_REP;
        resp.cname := req.cname;
        resp.crealm := req.realm;
        resp.ticket := new_tkt;

        resp.key := new_tkt.session;
        resp.last-req := fetch_last_request_info(client);
        resp.nonce := req.nonce;
        resp.key-expiration := client.expiration;
        resp.flags := new_tkt.flags;

        resp.authtime := new_tkt.authtime;
        resp.starttime := new_tkt.starttime;
        resp.endtime := new_tkt.endtime;

        if (new_tkt.flags.RENEWABLE) then
                resp.renew-till := new_tkt.renew-till;
        endif

        resp.realm := new_tkt.realm;
        resp.sname := new_tkt.sname;

        resp.caddr := new_tkt.caddr;

        encode body of reply into OCTET STRING;

        resp.enc-part := encrypt OCTET STRING
                         using use_etype, client.key, client.p_kvno;
        send(resp);

A.3. KRB_AS_REP verification ๐ŸŽ€

Now the client side gets to check the reply ๐Ÿ‘€ Here's the pseudo-code for that.

        decode response into resp;

        if (resp.msg-type = KRB_ERROR) then
                if(error = KDC_ERR_PREAUTH_REQUIRED(PA_ENC_TIMESTAMP))
                        then set pa_enc_timestamp_required;
                        goto KRB_AS_REQ;
                endif
                process_error(resp);
                return;
        endif

        /* On error, discard the response, and zero the session key */
        /* from the response immediately */

        key = get_decryption_key(resp.enc-part.kvno, resp.enc-part.etype,
                                 resp.padata);
        unencrypted part of resp := decode of decrypt of resp.enc-part
                                using resp.enc-part.etype and key;
        zero(key);

        if (common_as_rep_tgs_rep_checks fail) then
                destroy resp.key;
                return error;
        endif

        if near(resp.princ_exp) then

                print(warning message);
        endif
        save_for_later(ticket,session,client,server,times,flags);

A.4. KRB_AS_REP and KRB_TGS_REP common checks ๐Ÿงš

These checks are shared by both the AS and TGS replies, so they get their own spot ๐Ÿ’œ Here's the full set.

        if (decryption_error() or
            (req.cname != resp.cname) or
            (req.realm != resp.crealm) or
            (req.sname != resp.sname) or
            (req.realm != resp.realm) or
            (req.nonce != resp.nonce) or
            (req.addresses != resp.caddr)) then
                destroy resp.key;
                return KRB_AP_ERR_MODIFIED;
        endif

        /* make sure no flags are set that shouldn't be, and that  */
        /* all that should be are set                              */
        if (!check_flags_for_compatability(req.kdc-options,resp.flags))
                then destroy resp.key;
                return KRB_AP_ERR_MODIFIED;
        endif

        if ((req.from = 0) and
            (resp.starttime is not within allowable skew)) then
                destroy resp.key;
                return KRB_AP_ERR_SKEW;
        endif
        if ((req.from != 0) and (req.from != resp.starttime)) then
                destroy resp.key;
                return KRB_AP_ERR_MODIFIED;
        endif
        if ((req.till != 0) and (resp.endtime > req.till)) then
                destroy resp.key;
                return KRB_AP_ERR_MODIFIED;
        endif

        if ((req.kdc-options.RENEWABLE is set) and
            (req.rtime != 0) and (resp.renew-till > req.rtime)) then
                destroy resp.key;
                return KRB_AP_ERR_MODIFIED;
        endif
        if ((req.kdc-options.RENEWABLE-OK is set) and
            (resp.flags.RENEWABLE) and
            (req.till != 0) and
            (resp.renew-till > req.till)) then
                destroy resp.key;
                return KRB_AP_ERR_MODIFIED;

        endif

A.5. KRB_TGS_REQ generation ๐Ÿฆ‹

Last one for this part: how a TGS request gets built, step by step ๐ŸŒธ

        /* Note that make_application_request might have to     */
        /* recursivly call this routine to get the appropriate  */
        /* ticket-granting ticket                               */

        request.pvno := protocol version; /* pvno = 5 */
        request.msg-type := message type; /* type = KRB_TGS_REQ */

        body.kdc-options := users's preferences;
        /* If the TGT is not for the realm of the end-server  */
        /* then the sname will be for a TGT for the end-realm */
        /* and the realm of the requested ticket (body.realm) */
        /* will be that of the TGS to which the TGT we are    */
        /* sending applies                                    */
        body.sname := service's name;
        body.realm := service's realm;

        if (body.kdc-options.POSTDATED is set) then
                body.from := requested starting time;
        else
                omit body.from;
        endif
        body.till := requested end time;
        if (body.kdc-options.RENEWABLE is set) then
                body.rtime := requested final renewal time;
        endif
        body.nonce := random_nonce();
        body.etype := requested etypes;
        if (user supplied addresses) then
                body.addresses := user's addresses;
        else
                omit body.addresses;
        endif

        body.enc-authorization-data := user-supplied data;
        if (body.kdc-options.ENC-TKT-IN-SKEY) then
                body.additional-tickets_ticket := second TGT;
        endif

        request.req-body := body;
        check := generate_checksum (req.body,checksumtype);

        request.padata[0].padata-type := PA-TGS-REQ;
        request.padata[0].padata-value := create a KRB_AP_REQ using
                                      the TGT and checksum

        /* add in any other padata as required/supplied */

        kerberos := lookup(name of local kerberose server (or servers));
        send(packet,kerberos);

        wait(for response);
        if (timed_out) then
                retry or use alternate server;
        endif

A.6. KRB_TGS_REQ verification and KRB_TGS_REP generation โœจ

okay bestie, this is where the KDC checks a KRB_TGS_REQ and builds the KRB_TGS_REP ๐Ÿ’– the comments are the good part ๐Ÿ™ˆ

        /* note that reading the application request requires first
        determining the server for which a ticket was issued, and
        choosing the correct key for decryption.  The name of the
        server appears in the plaintext part of the ticket. */

        if (no KRB_AP_REQ in req.padata) then
                error_out(KDC_ERR_PADATA_TYPE_NOSUPP);
        endif
        verify KRB_AP_REQ in req.padata;

        /* Note that the realm in which the Kerberos server is
        operating is determined by the instance from the
        ticket-granting ticket.  The realm in the ticket-granting
        ticket is the realm under which the ticket granting ticket was
        issued.  It is possible for a single Kerberos server to
        support more than one realm. */

        auth_hdr := KRB_AP_REQ;
        tgt := auth_hdr.ticket;

        if (tgt.sname is not a TGT for local realm and is not
                req.sname) then error_out(KRB_AP_ERR_NOT_US);

        realm := realm_tgt_is_for(tgt);

        decode remainder of request;

        if (auth_hdr.authenticator.cksum is missing) then
                error_out(KRB_AP_ERR_INAPP_CKSUM);
        endif
        if (auth_hdr.authenticator.cksum type is not supported) then
                error_out(KDC_ERR_SUMTYPE_NOSUPP);
        endif
        if (auth_hdr.authenticator.cksum is not both collision-proof
            and keyed)  then
                error_out(KRB_AP_ERR_INAPP_CKSUM);
        endif
        set computed_checksum := checksum(req);
        if (computed_checksum != auth_hdr.authenticatory.cksum) then
                error_out(KRB_AP_ERR_MODIFIED);
        endif

        server := lookup(req.sname,realm);

        if (!server) then
                if (is_foreign_tgt_name(server)) then
                        server := best_intermediate_tgs(server);
                else
                        /* no server in Database */
                        error_out(KDC_ERR_S_PRINCIPAL_UNKNOWN);
                endif
        endif

        session := generate_random_session_key();


        use_etype := first supported etype in req.etypes;

        if (no support for req.etypes) then
                error_out(KDC_ERR_ETYPE_NOSUPP);
        endif

        new_tkt.vno := ticket version; /* = 5 */
        new_tkt.sname := req.sname;
        new_tkt.srealm := realm;
        reset all flags in new_tkt.flags;

        /* It should be noted that local policy may affect the  */
        /* processing of any of these flags.  For example, some */
        /* realms may refuse to issue renewable tickets         */

        new_tkt.caddr := tgt.caddr;
        resp.caddr := NULL; /* We only include this if they change */
        if (req.kdc-options.FORWARDABLE is set) then
                if (tgt.flags.FORWARDABLE is reset) then
                        error_out(KDC_ERR_BADOPTION);
                endif
                set new_tkt.flags.FORWARDABLE;
        endif
        if (req.kdc-options.FORWARDED is set) then
                if (tgt.flags.FORWARDABLE is reset) then
                        error_out(KDC_ERR_BADOPTION);
                endif
                set new_tkt.flags.FORWARDED;
                new_tkt.caddr := req.addresses;
                resp.caddr := req.addresses;
        endif
        if (tgt.flags.FORWARDED is set) then
                set new_tkt.flags.FORWARDED;
        endif

        if (req.kdc-options.PROXIABLE is set) then
                if (tgt.flags.PROXIABLE is reset)
                        error_out(KDC_ERR_BADOPTION);
                endif
                set new_tkt.flags.PROXIABLE;
        endif
        if (req.kdc-options.PROXY is set) then
                if (tgt.flags.PROXIABLE is reset) then
                        error_out(KDC_ERR_BADOPTION);
                endif
                set new_tkt.flags.PROXY;
                new_tkt.caddr := req.addresses;
                resp.caddr := req.addresses;
        endif

        if (req.kdc-options.POSTDATE is set) then
                if (tgt.flags.POSTDATE is reset)
                        error_out(KDC_ERR_BADOPTION);
                endif
                set new_tkt.flags.POSTDATE;
        endif
        if (req.kdc-options.POSTDATED is set) then
                if (tgt.flags.POSTDATE is reset) then
                        error_out(KDC_ERR_BADOPTION);
                endif
                set new_tkt.flags.POSTDATED;
                set new_tkt.flags.INVALID;
                if (against_postdate_policy(req.from)) then
                        error_out(KDC_ERR_POLICY);
                endif
                new_tkt.starttime := req.from;
        endif


        if (req.kdc-options.VALIDATE is set) then
                if (tgt.flags.INVALID is reset) then
                        error_out(KDC_ERR_POLICY);
                endif
                if (tgt.starttime > kdc_time) then
                        error_out(KRB_AP_ERR_NYV);
                endif
                if (check_hot_list(tgt)) then

                        error_out(KRB_AP_ERR_REPEAT);
                endif
                tkt := tgt;
                reset new_tkt.flags.INVALID;
        endif

        if (req.kdc-options.(any flag except ENC-TKT-IN-SKEY, RENEW,
                             and those already processed) is set) then
                error_out(KDC_ERR_BADOPTION);
        endif

        new_tkt.authtime := tgt.authtime;

        if (req.kdc-options.RENEW is set) then
          /* Note that if the endtime has already passed, the ticket */
          /* would have been rejected in the initial authentication  */
          /* stage, so there is no need to check again here          */
                if (tgt.flags.RENEWABLE is reset) then
                        error_out(KDC_ERR_BADOPTION);
                endif
                if (tgt.renew-till >= kdc_time) then
                        error_out(KRB_AP_ERR_TKT_EXPIRED);
                endif
                tkt := tgt;
                new_tkt.starttime := kdc_time;
                old_life := tgt.endttime - tgt.starttime;
                new_tkt.endtime := min(tgt.renew-till,
                                       new_tkt.starttime + old_life);
        else
                new_tkt.starttime := kdc_time;
                if (req.till = 0) then
                        till := infinity;
                else
                        till := req.till;
                endif
                new_tkt.endtime := min(till,
                                   new_tkt.starttime+client.max_life,
                                   new_tkt.starttime+server.max_life,
                                   new_tkt.starttime+max_life_for_realm,
                                   tgt.endtime);

                if ((req.kdc-options.RENEWABLE-OK is set) and
                    (new_tkt.endtime < req.till) and
                    (tgt.flags.RENEWABLE is set) then
                        /* we set the RENEWABLE option for later  */
                        /* processing                             */
                        set req.kdc-options.RENEWABLE;
                        req.rtime := min(req.till, tgt.renew-till);

                endif
        endif

        if (req.rtime = 0) then
                rtime := infinity;
        else
                rtime := req.rtime;
        endif

        if ((req.kdc-options.RENEWABLE is set) and
            (tgt.flags.RENEWABLE is set)) then
                set new_tkt.flags.RENEWABLE;
                new_tkt.renew-till := min(rtime,
                new_tkt.starttime+client.max_rlife,
                new_tkt.starttime+server.max_rlife,
                new_tkt.starttime+max_rlife_for_realm,
                tgt.renew-till);
        else
                new_tkt.renew-till := OMIT;
                              /* leave the renew-till field out */
        endif
        if (req.enc-authorization-data is present) then
                decrypt req.enc-authorization-data
                        into    decrypted_authorization_data
                        using auth_hdr.authenticator.subkey;
                if (decrypt_error()) then
                        error_out(KRB_AP_ERR_BAD_INTEGRITY);
                endif
        endif
        new_tkt.authorization_data :=
        req.auth_hdr.ticket.authorization_data +
                                 decrypted_authorization_data;

        new_tkt.key := session;
        new_tkt.crealm := tgt.crealm;
        new_tkt.cname := req.auth_hdr.ticket.cname;

        if (realm_tgt_is_for(tgt) := tgt.realm) then
                /* tgt issued by local realm */
                new_tkt.transited := tgt.transited;
        else
                /* was issued for this realm by some other realm */
                if (tgt.transited.tr-type not supported) then
                        error_out(KDC_ERR_TRTYPE_NOSUPP);
                endif
                new_tkt.transited
                   := compress_transited(tgt.transited + tgt.realm)
        endif
        encode encrypted part of new_tkt into OCTET STRING;
        if (req.kdc-options.ENC-TKT-IN-SKEY is set) then
                if (server not specified) then
                        server = req.second_ticket.client;
                endif
                if ((req.second_ticket is not a TGT) or
                    (req.second_ticket.client != server)) then
                        error_out(KDC_ERR_POLICY);
                endif

                new_tkt.enc-part := encrypt OCTET STRING using
                        using etype_for_key(second-ticket.key),
                                                      second-ticket.key;
        else
                new_tkt.enc-part := encrypt OCTET STRING
                        using etype_for_key(server.key), server.key,
                                                      server.p_kvno;
        endif

        resp.pvno := 5;
        resp.msg-type := KRB_TGS_REP;
        resp.crealm := tgt.crealm;
        resp.cname := tgt.cname;
        resp.ticket := new_tkt;

        resp.key := session;
        resp.nonce := req.nonce;
        resp.last-req := fetch_last_request_info(client);
        resp.flags := new_tkt.flags;

        resp.authtime := new_tkt.authtime;
        resp.starttime := new_tkt.starttime;
        resp.endtime := new_tkt.endtime;

        omit resp.key-expiration;

        resp.sname := new_tkt.sname;
        resp.realm := new_tkt.realm;

        if (new_tkt.flags.RENEWABLE) then
                resp.renew-till := new_tkt.renew-till;
        endif


        encode body of reply into OCTET STRING;

        if (req.padata.authenticator.subkey)
                resp.enc-part := encrypt OCTET STRING using use_etype,

                        req.padata.authenticator.subkey;
        else resp.enc-part := encrypt OCTET STRING
                              using use_etype, tgt.key;

        send(resp);

A.7. KRB_TGS_REP verification ๐ŸŽ€

now the other side ๐Ÿฆ‹ the client decodes the response and checks it, and if something's off it tosses the response and zeroes that session key right away ๐Ÿ”ฅ

        decode response into resp;

        if (resp.msg-type = KRB_ERROR) then
                process_error(resp);
                return;
        endif

        /* On error, discard the response, and zero the session key from
        the response immediately */

        if (req.padata.authenticator.subkey)
                unencrypted part of resp :=
                        decode of decrypt of resp.enc-part
                        using resp.enc-part.etype and subkey;
        else unencrypted part of resp :=
                        decode of decrypt of resp.enc-part
                        using resp.enc-part.etype and tgt's session key;
        if (common_as_rep_tgs_rep_checks fail) then
                destroy resp.key;
                return error;
        endif

        check authorization_data as necessary;
        save_for_later(ticket,session,client,server,times,flags);

A.8. Authenticator generation โœจ

okay so this is where the authenticator gets built, bestie ๐ŸŽ€ the client fills in its name and realm, then adds the optional bits like a checksum, timestamp, sub-session key, and sequence number. literally so pretty ๐Ÿ’…

body.authenticator-vno := authenticator vno; /* = 5 */
body.cname, body.crealm := client name;
if (supplying checksum) then
        body.cksum := checksum;
endif
get system_time;
body.ctime, body.cusec := system_time;
if (selecting sub-session key) then
        select sub-session key;
        body.subkey := sub-session key;
endif
if (using sequence numbers) then
        select initial sequence number;
        body.seq-number := initial sequence;
endif

A.9. KRB_AP_REQ generation ๐Ÿฆ‹

this one is the client's request to a service, and it's giving organized ๐Ÿ“‹ grab the ticket and session key from the cache first, then set the version and message type. the MUTUAL-REQUIRED and USE-SESSION-KEY flags get set or reset depending on what the client wants, then the authenticator gets generated, encoded, and encrypted with the session key ๐Ÿ”

obtain ticket and session_key from cache;

packet.pvno := protocol version; /* 5 */
packet.msg-type := message type; /* KRB_AP_REQ */

if (desired(MUTUAL_AUTHENTICATION)) then
        set packet.ap-options.MUTUAL-REQUIRED;
else
        reset packet.ap-options.MUTUAL-REQUIRED;
endif
if (using session key for ticket) then
        set packet.ap-options.USE-SESSION-KEY;
else
        reset packet.ap-options.USE-SESSION-KEY;
endif
packet.ticket := ticket; /* ticket */
generate authenticator;
encode authenticator into OCTET STRING;
encrypt OCTET STRING into packet.authenticator
                     using session_key;

A.10. KRB_AP_REQ verification ๐Ÿ”

this is the service checking the request, and it's a whole gauntlet ๐Ÿ˜ญ version numbers, message type, the ticket version, which key to use, decryption, name matching, addresses, clock skew, replays, and then whether the ticket is valid right now. every check has its own error, so nothing slips through ๐Ÿซถ

receive packet;
if (packet.pvno != 5) then
        either process using other protocol spec
        or error_out(KRB_AP_ERR_BADVERSION);
endif
if (packet.msg-type != KRB_AP_REQ) then
        error_out(KRB_AP_ERR_MSG_TYPE);
endif
if (packet.ticket.tkt_vno != 5) then
        either process using other protocol spec
        or error_out(KRB_AP_ERR_BADVERSION);
endif
if (packet.ap_options.USE-SESSION-KEY is set) then
        retrieve session key from ticket-granting ticket for
         packet.ticket.{sname,srealm,enc-part.etype};
else
   retrieve service key for
   packet.ticket.{sname,srealm,enc-part.etype,enc-part.skvno};
endif
if (no_key_available) then
        if (cannot_find_specified_skvno) then
                error_out(KRB_AP_ERR_BADKEYVER);
        else
                error_out(KRB_AP_ERR_NOKEY);
        endif

endif
decrypt packet.ticket.enc-part into decr_ticket
                               using retrieved key;
if (decryption_error()) then
        error_out(KRB_AP_ERR_BAD_INTEGRITY);
endif
decrypt packet.authenticator into decr_authenticator
        using decr_ticket.key;
if (decryption_error()) then
        error_out(KRB_AP_ERR_BAD_INTEGRITY);
endif
if (decr_authenticator.{cname,crealm} !=
    decr_ticket.{cname,crealm}) then
        error_out(KRB_AP_ERR_BADMATCH);
endif
if (decr_ticket.caddr is present) then
        if (sender_address(packet) is not in decr_ticket.caddr)
                then error_out(KRB_AP_ERR_BADADDR);
        endif
elseif (application requires addresses) then
        error_out(KRB_AP_ERR_BADADDR);
endif
if (not in_clock_skew(decr_authenticator.ctime,
                      decr_authenticator.cusec)) then
        error_out(KRB_AP_ERR_SKEW);
endif
if (repeated(decr_authenticator.{ctime,cusec,cname,crealm}))
        then error_out(KRB_AP_ERR_REPEAT);
endif
save_identifier(decr_authenticator.{ctime,cusec,cname,crealm});
get system_time;
if ((decr_ticket.starttime-system_time > CLOCK_SKEW) or
    (decr_ticket.flags.INVALID is set)) then
        /* it hasn't yet become valid */
        error_out(KRB_AP_ERR_TKT_NYV);
endif
if (system_time-decr_ticket.endtime > CLOCK_SKEW) then
        error_out(KRB_AP_ERR_TKT_EXPIRED);
endif
/* caller must check decr_ticket.flags for any pertinent */
/* details */
return(OK, decr_ticket, packet.ap_options.MUTUAL-REQUIRED);

A.11. KRB_AP_REP generation ๐ŸŒธ

the service's reply, which is basically "yep, I got you" ๐Ÿ™ˆ it copies the client's timestamp and microseconds into the body, optionally adds a sub-session key and sequence number, then encodes and encrypts the whole thing.

packet.pvno := protocol version; /* 5 */
packet.msg-type := message type; /* KRB_AP_REP */
body.ctime := packet.ctime;
body.cusec := packet.cusec;
if (selecting sub-session key) then
        select sub-session key;
        body.subkey := sub-session key;
endif
if (using sequence numbers) then
        select initial sequence number;
        body.seq-number := initial sequence;
endif

encode body into OCTET STRING;

select encryption type;
encrypt OCTET STRING into packet.enc-part;

A.12. KRB_AP_REP verification ๐Ÿ’–

now the client checks the reply. it decrypts with the ticket's session key, makes sure the timestamps match what it sent, then saves the sub-session key and sequence number if they're there. if it all lines up, mutual auth succeeds โœจ

receive packet;
if (packet.pvno != 5) then
        either process using other protocol spec
        or error_out(KRB_AP_ERR_BADVERSION);
endif
if (packet.msg-type != KRB_AP_REP) then
        error_out(KRB_AP_ERR_MSG_TYPE);
endif
cleartext := decrypt(packet.enc-part)
             using ticket's session key;
if (decryption_error()) then
        error_out(KRB_AP_ERR_BAD_INTEGRITY);
endif
if (cleartext.ctime != authenticator.ctime) then
        error_out(KRB_AP_ERR_MUT_FAIL);
endif
if (cleartext.cusec != authenticator.cusec) then
        error_out(KRB_AP_ERR_MUT_FAIL);
endif
if (cleartext.subkey is present) then
        save cleartext.subkey for future use;
endif
if (cleartext.seq-number is present) then
        save cleartext.seq-number for future verifications;
endif
return(AUTHENTICATION_SUCCEEDED);

A.13. KRB_SAFE generation ๐Ÿ”ฅ

KRB_SAFE is for sending user data with integrity protection, so it's a checksum situation ๐Ÿง the sender collects the data, optionally adds a timestamp and sequence number, lists its own addresses, and adds a recipient address if there's only one recipient. then it computes the checksum over the body and attaches it.

collect user data in buffer;

/* assemble packet: */
packet.pvno := protocol version; /* 5 */
packet.msg-type := message type; /* KRB_SAFE */
body.user-data := buffer; /* DATA */
if (using timestamp) then
        get system_time;
        body.timestamp, body.usec := system_time;
endif
if (using sequence numbers) then
        body.seq-number := sequence number;
endif
body.s-address := sender host addresses;
if (only one recipient) then
        body.r-address := recipient host address;
endif
checksum.cksumtype := checksum type;
compute checksum over body;
checksum.checksum := checksum value; /* checksum.checksum */
packet.cksum := checksum;
packet.safe-body := body;

A.14. KRB_SAFE verification ๐Ÿง

the receiver does the usual version and message type checks, then makes sure the checksum type is both collision-proof and keyed. after that it runs the common checks, recomputes the checksum, and compares. if the numbers don't match, it's giving modified ๐Ÿšจ

receive packet;
if (packet.pvno != 5) then
        either process using other protocol spec
        or error_out(KRB_AP_ERR_BADVERSION);
endif
if (packet.msg-type != KRB_SAFE) then
        error_out(KRB_AP_ERR_MSG_TYPE);
endif
if (packet.checksum.cksumtype is not both collision-proof
                                     and keyed) then
        error_out(KRB_AP_ERR_INAPP_CKSUM);
endif
if (safe_priv_common_checks_ok(packet)) then
        set computed_checksum := checksum(packet.body);
        if (computed_checksum != packet.checksum) then
                error_out(KRB_AP_ERR_MODIFIED);
        endif
        return (packet, PACKET_IS_GENUINE);
else
        return common_checks_error;
endif

A.15. KRB_SAFE and KRB_PRIV common checks ๐ŸŽ€

these are the shared checks that both KRB_SAFE and KRB_PRIV run, and they're thorough bestie ๐Ÿ’… the sender address has to match, the recipient address has to match if it's there, the timestamp needs to be inside clock skew, repeats get caught, the sequence number needs to be in order, and a message with neither a timestamp nor a sequence number counts as modified. the identifier then gets saved so it can't be replayed ๐Ÿฆ‹

if (packet.s-address != O/S_sender(packet)) then
    /* O/S report of sender not who claims to have sent it */
    error_out(KRB_AP_ERR_BADADDR);
endif
if ((packet.r-address is present) and
    (packet.r-address != local_host_address)) then

        /* was not sent to proper place */
        error_out(KRB_AP_ERR_BADADDR);
endif
if (((packet.timestamp is present) and
     (not in_clock_skew(packet.timestamp,packet.usec))) or
    (packet.timestamp is not present and timestamp expected))
        then error_out(KRB_AP_ERR_SKEW);
endif
if (repeated(packet.timestamp,packet.usec,packet.s-address))
        then error_out(KRB_AP_ERR_REPEAT);
endif
if (((packet.seq-number is present) and
     ((not in_sequence(packet.seq-number)))) or
    (packet.seq-number is not present and sequence expected))
        then error_out(KRB_AP_ERR_BADORDER);
endif
if (packet.timestamp not present and
    packet.seq-number not present) then
        error_out(KRB_AP_ERR_MODIFIED);
endif

save_identifier(packet.{timestamp,usec,s-address},
                sender_principal(packet));

return PACKET_IS_OK;

A.16. KRB_PRIV generation ๐Ÿงš

KRB_PRIV is the encrypted version of the KRB_SAFE idea, so it's the private one ๐Ÿคซ it sets the encryption type, builds the same kind of body with the user data, optional timestamp and sequence number, and addresses, then encodes it and encrypts it into the packet.

collect user data in buffer;

/* assemble packet: */
packet.pvno := protocol version; /* 5 */
packet.msg-type := message type; /* KRB_PRIV */

packet.enc-part.etype := encryption type;

body.user-data := buffer;
if (using timestamp) then
        get system_time;
        body.timestamp, body.usec := system_time;
endif
if (using sequence numbers) then
        body.seq-number := sequence number;
endif
body.s-address := sender host addresses;
if (only one recipient) then
        body.r-address := recipient host address;
endif
encode body into OCTET STRING;

select encryption type;
encrypt OCTET STRING into packet.enc-part.cipher;

A.17. KRB_PRIV verification ๐Ÿ‘‘

last one in this batch, and it's the receiver's final check ๐Ÿ’– it validates the version and message type, decrypts with the negotiated key, then runs the common checks. if everything passes, the user data comes back marked as genuine and unmodified โœจ

receive packet;
if (packet.pvno != 5) then
        either process using other protocol spec
        or error_out(KRB_AP_ERR_BADVERSION);
endif
if (packet.msg-type != KRB_PRIV) then
        error_out(KRB_AP_ERR_MSG_TYPE);
endif

cleartext := decrypt(packet.enc-part) using negotiated key;
if (decryption_error()) then
        error_out(KRB_AP_ERR_BAD_INTEGRITY);
endif

if (safe_priv_common_checks_ok(cleartext)) then
    return(cleartext.DATA, PACKET_IS_GENUINE_AND_UNMODIFIED);
else
        return common_checks_error;
endif

A.18. KRB_CRED generation ๐ŸŽ€

okay bestie, this is how you pack up tickets to send to a peer ๐Ÿ’Œ first you go get the tickets you want to forward, then you stack them into the packet and wrap the details up in an encrypted envelope ๐ŸŽโœจ

        invoke KRB_TGS; /* obtain tickets to be provided to peer */

        /* assemble packet: */
        packet.pvno := protocol version; /* 5 */
        packet.msg-type := message type; /* KRB_CRED */

        for (tickets[n] in tickets to be forwarded) do
                packet.tickets[n] = tickets[n].ticket;
        done

        packet.enc-part.etype := encryption type;

        for (ticket[n] in tickets to be forwarded) do
                body.ticket-info[n].key = tickets[n].session;
                body.ticket-info[n].prealm = tickets[n].crealm;
                body.ticket-info[n].pname = tickets[n].cname;
                body.ticket-info[n].flags = tickets[n].flags;
                body.ticket-info[n].authtime = tickets[n].authtime;
                body.ticket-info[n].starttime = tickets[n].starttime;
                body.ticket-info[n].endtime = tickets[n].endtime;
                body.ticket-info[n].renew-till = tickets[n].renew-till;
                body.ticket-info[n].srealm = tickets[n].srealm;
                body.ticket-info[n].sname = tickets[n].sname;
                body.ticket-info[n].caddr = tickets[n].caddr;
        done

        get system_time;
        body.timestamp, body.usec := system_time;

        if (using nonce) then
                body.nonce := nonce;
        endif

        if (using s-address) then
                body.s-address := sender host addresses;
        endif
        if (limited recipients) then
                body.r-address := recipient host address;
        endif

        encode body into OCTET STRING;

        select encryption type;
        encrypt OCTET STRING into packet.enc-part.cipher
        using negotiated encryption key;

A.19. KRB_CRED verification ๐Ÿ”

this is the receiver's checklist, and it's giving very thorough ๐Ÿ˜ค check the version, check the message type, decrypt, then make sure the addresses, the timestamp, the repeats, and the nonce all line up before saving the tickets for later ๐Ÿฆ‹

        receive packet;
        if (packet.pvno != 5) then
                either process using other protocol spec
                or error_out(KRB_AP_ERR_BADVERSION);
        endif
        if (packet.msg-type != KRB_CRED) then
                error_out(KRB_AP_ERR_MSG_TYPE);
        endif

        cleartext := decrypt(packet.enc-part) using negotiated key;
        if (decryption_error()) then
                error_out(KRB_AP_ERR_BAD_INTEGRITY);
        endif
        if ((packet.r-address is present or required) and
           (packet.s-address != O/S_sender(packet)) then
            /* O/S report of sender not who claims to have sent it */
            error_out(KRB_AP_ERR_BADADDR);
        endif
        if ((packet.r-address is present) and
            (packet.r-address != local_host_address)) then
                /* was not sent to proper place */
                error_out(KRB_AP_ERR_BADADDR);

        endif
        if (not in_clock_skew(packet.timestamp,packet.usec)) then
                error_out(KRB_AP_ERR_SKEW);
        endif
        if (repeated(packet.timestamp,packet.usec,packet.s-address))
                then error_out(KRB_AP_ERR_REPEAT);
        endif
        if (packet.nonce is required or present) and
           (packet.nonce != expected-nonce) then
                error_out(KRB_AP_ERR_MODIFIED);
        endif

        for (ticket[n] in tickets that were forwarded) do
                save_for_later(ticket[n],key[n],principal[n],
                               server[n],times[n],flags[n]);
        return

A.20. KRB_ERROR generation ๐Ÿšจ

and last but absolutely not least, the error packet ๐Ÿ’€ it stamps the server name and the time, then fills in whatever extra info it actually has, like the client's time, name, text, or data. so cute, so organized, no notes ๐Ÿซถ

        /* assemble packet: */
        packet.pvno := protocol version; /* 5 */
        packet.msg-type := message type; /* KRB_ERROR */

        get system_time;
        packet.stime, packet.susec := system_time;
        packet.realm, packet.sname := server name;

        if (client time available) then
                packet.ctime, packet.cusec := client_time;
        endif
        packet.error-code := error code;
        if (client name available) then
                packet.cname, packet.crealm := client name;
        endif
        if (error text available) then
                packet.e-text := error text;
        endif
        if (error data available) then
                packet.e-data := error data;
        endif
๐Ÿ“ฆ where this page comes from