When Six Apart announced TypeKey earlier this year, it caught a lot of flak for being a centralized service. Not being a MoveableType user, I didn't pay much attention at the time, but I have a strong background interest in Single Sign On systems, and so I eventually sat down and read the specs. In the end, I was pleasantly surprised: as you'd hope, they've prevented all of the attacks I know of that typically work on such systems, but they also make very nice use of DSA to get rid of the background interaction between application and authentication service that's usually required. If it weren't for the centralization, it's a protocol I'd be interested in implementing and promoting, and not just for blogs. So I've been wondering: what modifications would be necessary to the TypeKey protocol to make it truly decentralized? Is it possible to do this without sacrificing either simplicity or security?
There are two ways in which, in TypeKey, the applications are coupled to the authentication service. To turn TypeKey into a decentralized system, both of these would have to be removed. The first is that all applications using TypeKey need to be registered with the authentication service - they are issued a "TypeKey token" which needs to be included as one of the query parameters when linking to the login page. The second is that the applications must all know where to retrieve the public key of the authentication service for use in verifying the response. So, there's coupling in both directions: the auth service knows about the apps, and the apps all know about the auth service. Let's address each of these separately.
If we're going to get rid of the requirement for applications to register with the authentication service, first we have to understand why it's there in the first place. For one thing, it might be that TypeKey wants to keep some control over who's using their service and for what, but that's not an interesting reason in a decentralized system - if that were the only justification we could just remove the application token entirely. There is, however, a sound security reason for it as well. When you register an application with TypeKey, you specify a set of URLs that your application lives at, and TypeKey will refuse to redirect a user who is logging in to your application to any other URL. This means that you can be pretty sure that the user was sent directly from the TypeKey login page to your application, and not to some external URL, which might collect the login token and then turn around and impersonate the user to you. (I described one example of this "application in the middle" attack here).
But what's the fundamental security mechanism here? It's that TypeKey is making an assertion, that your application can check, about which URL it sent the user to. In the standard TypeKey protocol, it does that by including the application's TypeKey token in the digitally signed portion of the response. The application can check that the token is indeed its own, and knows that TypeKey could only have sent the user to a URL that was indeed part of that application. But there's a more direct way of making that same assertion: the authentication service could simply include the URL, or a portion of it (the domain name would probably be enough) directly in the signed message. The authentication service doesn't have to know anything about that domain name, it's simply asserting that this is the domain of the return URL it was given, and where it redirected the user to: "This is where the user was supposed to go, and if it's not where they ended up, we have a problem." So, revision number one: remove the t site token parameter from the login interface, and replace the site-token in the signed message with the domain name of the _return URL.
The second coupling, that the applications know where to find the public key of the authentication server, seems more difficult to remove. In fact, it's much easier: the authentication service just includes its public key as part of the response that goes back to the application. Verification is very simple: you just make sure that the provided signed message matches the provided public key. The keys used by DSA are large, but they're not that large - they're 256 bytes, which you can represent in 341 characters of Base64 encoded text, which is well under the 2048 byte limit IE imposes on URLs. So, revision number 2: include an extra parameter key in the TypeKey response that contains an encoded version of the public key for the authentication service, and ignore the bit about fetching the key from typekey.com.
Now, hang on, you're saying. If the public key is included in the TypeKey response, then anyone could generate one. And, well, yeah - anyone could. That's what makes it decentralized. But although anyone can generate a response that validates as coming from some authentication service, only the holder of a particular private key can generate a response that validates as coming from a particular authentication server. So the public key serves a dual purpose: it's used for decryption, but also for identification - on its own, of a specific authentication service, and when paired with the login name, of a specific user. How a particular application chooses to make use of this information is, of course, up to it. For example, it might consult some central list of public keys that asserts that these authentication services are legitimate and not controlled by spambots. Or, it might use the (public key, login name) pair to recognize a user across multiple visits - asking them to prove themselves to be human the first time they post a comment, and letting them through unchallenged on subsequent logins. You might even combine these ideas and only challenge every nth user that comes from a particular authentication service, on the theory that if an authentication service has a good number of legitimate users it probably doesn't have too many spambots (and you can blacklist that server as soon as you find out it does). And of course in a non-blog context you can use these primitives to build whatever federated identity system you like on top...
I could go on, but I suspect I've already gone beyond the limits of what most people are interested in reading. It's quite possible I've overlooked some security issues here, so I'd welcome comments pointing out any flaws. But if I haven't, it seems like with a couple of small modifications, the main criticism of TypeKey can be removed - and I think we end up with a pretty nice protocol. Now to find time to do an implementation in Squeak...