Universal Authentication

A design for universal authentication
This is a crude outline for a design for universal authentication over the Internet.

It's based on Kerberos, but updated for the wider world. It presumes the existence and deployment of DNSSEC, which provides a way for servers to verify one another.

I'll divide this up into three parts: A brief (and hopefully mostly correct) description of how Kerberos works now, a discussion of why that doesn't scale, a description of my proposed system, a discussion of why that does scale, and finally some potential bumps that I can spot already.

Kerberos
Kerberos provides an authentication system which allows a client and a server to authenticate one another without the need for the two of them to share a secret beforehand, or for either to have access to the other's 'key'. To accomplish this, a third entity, the Kerberos server, is needed.

Both the client (we'll call him a 'user', to make it clear that it can be a person) and the server share a secret with the Kerberos server beforehand. In the case of the former, this secret probably takes the form of a password. In the server's case, it's a randomly-generated key--a certificate, essentially. This certificate must be moved to the server in question separately, by some secure channel. Actually, by 'server', here, I might mean either a physical machine or a particular daemon on a machine.

Now, if a user wants to authenticate with a server, they send a request to the Kerberos server. The Kerberos server generates a new secret key, which we'll call K. It generates a bundle for the user by encrypting K with the user's key (i.e. their password). We'll designate this USER{K}. It then wraps this bundle up with a bundle intended for the server, which includes K, but also the time of the request, the user's username, the IP of the host the user is on, and finally USER{K}. We'll designate this SERVER{username, time, ip, K, USER{K}}. It passes this bundle to the user.

To the user, this bundle is worthless; they cannot read the contents, because it is encrypted with the server's key. So, the user must pass the bundle on to the server they wish to authenticate with.

The server receives SERVER{username, time, ip, K, USER{K}} and immediately decrypts it, extracting the data contained within. Using the information about the user, the server can immediately check whether or not the user should be allowed access (if they are, in fact, who they say they are). If the username is unknown to the system: screw off! If the IP is different from the one the user is communicating from: screw off! If the time is more than a few minutes different than the server's time: screw off!

If, on the other hand, the user's information checks out, this means that if the user is indeed who they claim to be, they can be allowed access. The server thus passes USER{K} to the user.

If the user is able to successfully decrypt K (that is, if they know their password), then the user and the server now share a common secret, which they can use to encrypt communication to one another. The user knows the server is what it claims to be, and the server knows the user is who they claim to be. Mutual authentication is accomplished!

Ticket-granting Tickets
The above description allows for mutual authentication, but it requires the user to enter their password for each service on every host that they use. This isn't ideal... So instead, one extra step is added to the above process.

When the user authenticates for the first time, they are actually given a packet that looks like: USER{TGT}, where TGT--like K--is simply a randomly-generated key. This key, however, the user stores only on his own machine. It can be used like an auto-password; the Kerberos server remembers which keys it has passed out to each user (and the time to expiry for each); when the user requests access to another server, the Kerberos server encrypts the reply using the TGT, instead of the user's password. The user's system is thus able to authenticate automatically, without the intervention of the user. Additionally, a third party who manages to intercept the key cannot use it to crack the user's password.

Attacks?
In order for Kerberos to be secure, many potential attacks must be considered. The most dangerous of these involve enemies eavesdropping on the network. Of course, it also assumes the relative security of the host on which the user is working; if another user on the same host can access the first user's keys, there's not much that can be done.

The fake request
I can claim to be 'reaper' when I am, in point of fact, 'niten'. I then try to crack the bundle I've been given. If I do manage to crack it successfully, I now know reaper's password.

This isn't a problem, of course, if all users use strong passwords. Haw!

A key could be used instead, but this pushes key management to the user.

Intercepted request
If I manage to intercept a request (of the form SERVER{...TGT{K}}), I could pass it to the server. Since an IP is included in the bundle, the server will quickly discover that I'm not the original user and ignore me, unless I manage to spoof the IP in question. If I manage to do that, the server will pass me TGT{K}, which is still of little use, since I don't have a copy of the TGT. The same problem exists if I intercept the later step--I'd still get TGT{K}.

Stealing keys
I might steal a user's keys, if I could compromise their host.

This one is really hard to fight. I could grab the user's TGT, which must be stored indefinitely on the user's host. This would allow me to authenticate as the user with any service. However, there is a time limit.

Of course, if I have well and thoroughly compromised the host, I could also steal the user's password, allowing me to identify as that user at will. This is not altogether different from the situation now, of course, but is still disturbing.

Other notes about Kerberos
Kerberos requires host domain names and IP addresses to be consistent. This seems like host-based authentication, which is regarded as a bad idea, but it's more than that.

The user must figure out what SERVER key the Kerberos server should use in the creation of it's bundle. A kerberos client does this by reverse-lookup of the IP address. You can't really use a user-entered hostname, because then, for example, www.fudo.org and athens.fudo.org would require two different keys (or one key, replicated for each hostname). Asking the user is ugly. In the context of university networks in the 1980's, a reverse lookup was very elegant.

Of course, the server could simply tell the user it's name. From a 1980's perspective, this was a wasted opportunity for slightly better security for free (essentially). Now, though, it seems like the better option.

Another option would be some sort of TXT record in DNS.

The server also uses the IP to identify the user. This is to prevent man-in-the-middle attacks, where someone intercepts a bundle and uses it to attempt to authenticate with the user. If the intercepting user manages to submit first, then how do you know who the real user is?

(Answer: the one who can decrypt the ticket. So it almost seems like a moot question.)

Anyway, I have a suggestion of my own. I should mention that Kerberos predates SSL, and that public-key encryption was uncommon back in the day. Having said that, here's my suggestion:

The user asks the server for it's name. It then generates a small key--it doesn't have to be giant. It passes this--over a secure connection--to the Kerberos server (or even just encrypts with the Kerberos server's public key). The Kerberos server includes this key in the server's bundle. Then, if the user attempting to log in can include a copy of this key along with the server's bundle, then it must be the original user, right?

Shortcomings of Kerberos
So why won't Kerberos work over the modern Internet?

In some ways it's perfect: it allows a user and a server to authenticate over an insecure network without sharing any sensitive information.

On the other hand, it requires one central server, which shares a secret with every host and every user who takes part in authentication. That's obviously unrealistic.

It allows for cross-domain authentication...if the domains share a secret key. Also unrealistic.

It expects all hosts to have real IPs, with a sane and consistent reverse lookup. Totally unrealistic!

Global authentication
So here's my proposed protocol.

As a user, I start by generating a public/private key pair. I upload my public key to my desired key manager; this might be Google, it might be my ISP, my University--or it might be Fudo. Each of these potential managers has one or more keyservers, identified by a SRV record.

Now, I try to authenticate with a service--maybe athens, for SSH login, or maybe Facebook or another website. I tell them I am, say, niten@fudo.org. They pass back their name and domain (which would default to their real actual domain--though they could provide another).

First, if I don't have a TGT, I contact my key manager (needs a catchier name...). It encrypts a TGT for me, using my public key, and passes it to me. If I am able to decrypt it, future authentication will be automated.

Now, I tell my key manager that I wish to authenticate against athens.fudo.org, passing along at the same time a simple randomly-generated key (call it r) encrypted with my key manager's public key. My key manager looks up the desired host's public key. It needs to get this from a verifiable location--it should be sure this really is that host's key. This is where DNSSEC comes in; the key manager looks up the domain's key manager (in the SRV record) and passes it the name provided by the service to which I wish to connect. The remote key manager will pass back the necessary public key.

My key server generates a new random key (the K from Kerberos, above) and encrypts it with my public key. It then encrypts K, the current time, my full user string (niten@fudo.org), and the key I initially passed, r, along with USER{K}, with the server's key. So: SERVER{r, K, username, time, USER-TGT{K}}. It passes this bundle to me.

I can't do anything with it yet, so I pass it to the server with whom I am attempting to authenticate--along with my copy of r (encrypted with SSL or something similar). The server decrypts it's bundle and compares the provided r with the bundled one, to verify that I am the original user, and not an impostor. It checks the time to make sure this isn't a replay (the key manager could also add a unique identifier, which the server could watch for to make sure it didn't get the same request twice--could this be even more clever, though?). Finally, it checks whether this user exists in the system or not.

Having verified all this, the server passes USER-TGT{K} back to me. If I am able to decrypt it, then the server and I now share a secret, K; by exchanging encrypted messages, we can mutually establish our identities.