The first attempt to send this didn't seem to go through, so I'm resending with the attachment inline.
Hi All, I've been asked to put together a design for a single sign on system for the KDE project (www.kde.org) to use to provide a single login server for all the various websites the project uses. If anyone has time to take a look and see if I've left any design flaws then I'd appreciate the feedback. The design is outlined at http://xmelegance.org/devel/identitiy_kde_org/DESIGN and there's a strawman python implementation (using pycrypto) at http://xmelegance.org/devel/identitiy_kde_org/identity_kde_sso-0.4.tar.gz that shows how the clients and server would work. I'll attach the design document to this email in case that makes it easier for people. Any feedback is appreciated. Regards Rich. Introduction ============ This is the design of an SSO system to be used by identity.kde.org. The idea is that all KDE websites should be able to use the IKO server to authenticate users - the client websites should never have access to the user's password. It is intended that non-KDE websites should also be able to deploy this system in future, for example to support authenticating KDE developers against IKO for login to upstream or downstream bug trackers. The client part of the SSO system will need to be implementable in a wide range of languages such as PHP, Ruby etc. in order to allow it to be integrated into the existing codebase of the various websites. Terms used in this document: Client - The website that wishes to authenticate a user. Server - The IKO website that has the credential information. Note that all random numbers refered to in this document must be taken from a secure random source. Setup ===== The design assumes that IKO and each client have a shared secret. It is not possible to authenticate arbitrary websites using this model, only those that have been pre-arranged. Creating a Request ================== When a client wishes to authenticate a user it generates an authentication request, this is a token that it will pass to the server. It does this by either redirecting the user to a specific URL on the server, or by an HTTP POST. The request has the following structure: [IV][AES [Payload [NONCE]][PADDING]][HMAC] IV - The IV used for this request. It is created by reading data from a secure random source. We use the AES block size as the length of the IV (16 bytes). Payload - The data be transfered to the server. Currently this is just the nonce. NONCE - A 16 byte random number used to ensure that the reply received is for the correct request (replay protection). The nonce must be retained by the client in order to verify the later reply from the server. PADDING - PKCS5 padding that pads the payload to the AES block size. In fact in the current implementation no padding is used here since the nonce is specified to be the length of a single AES block. The padding will however be required should further meta-data be introduced to requests. Padding is handled using the PKCS5 method. AES - The payload and padding are encrypted using AES in CBC mode. The cipher is setup using the IV and the shared secret. HMAC - An HMAC calculated using SHA1. The HMAC covers the IV and the encrypted payload with padding. Once the request has been created, it is base64 encoded to make it easy to pass over HTTP. It could be passed to the IKO using a URL parameter or via an HTTP POST. Verifying a Request =================== When the server receives a request, it must verify that it is genuine. The server knows from either the HTTP Referer header, or through a parameter in the HTTP message which client is making the request. It uses this knowledge to select the key (shared secret) for the particular client. The server first base64 decodes the request and verifies the HMAC. It then extracts the IV and the encrypted block. The encrypted block is decripted using the key and IV. The nonce is then extracted (since the length of the nonce is fixed this is easy). If more meta-data is added to requests then obviously additional steps will need to be taken to extract that too. The server then authenticates the user. This could be by asking for a username and password, using a 2 factor scheme or anything else. Persistent logins can be offered using HTTP cookies avoiding the need for users reauthenticate for each KDE website. The server now knows the identity of the user, the client they are logging into and the nonce that represents the authentication request. Creating the Reply ================== The server now creates a reply that will be passed to the client. The reply has the following structure: [IV][AES [Payload [NONCE][LENGTH][USERNAME]][PADDING]][HMAC] IV - The IV used for this reply. It is created by reading data from a secure random source. We use the AES block size as the length of the IV (16 bytes). Note that this IV is NOT the same as the IV used in the request. Payload - The data be transfered to the client. Currently, this is the nonce, the length of the username and the username itself. NONCE - The nonce that was provided in the request to which we are responding. LENGTH - The length of the username in bytes. This is stored as a 4 byte unsigned integer in network byte order. USERNAME - The name of the user. This should be encoded as UTF8. PADDING - PKCS5 padding that pads the payload to the AES block size. AES - The payload and padding are encrypted using AES in CBC mode. The cipher is setup using the IV and the shared secret. HMAC - An HMAC calculated using SHA1. The HMAC covers the IV and the encrypted payload with padding. Once the reply has been created, it is base64 encoded to make it easy to pass over HTTP. It could be passed to the client by redirecting the user to a known URL or by returning the user back to the referer of the initial authentication request. Verifying the Reply =================== The client now receives the reply and must verify that it is genuine. It first base64 decodes the reply and verifies the HMAC. It then extracts the IV and the encrypted block. The encrypted block is decrypted using the key and IV. The nonce is then extracted (since the length of the nonce is fixed this is easy). If the nonce does not correspond to the one the client sent to the server then the reply MUST be rejected as invalid. The client then reads the 4 byte length field to find the size of the username, and extracts the username from the payload. At this point the user is now authenticated and their username is known. Questions and Caveats ===================== Question: Would it be better to include the server in the token? It cannot be encrypted since we need to use it to determine which key to use. It could be protected by the HMAC however we'd need to extract it from the token before verifying the HMAC is valid since again, we need to use this information to determine which key to use. Caveat: The length of the request and reply is not hidden. A passive attacker can see the length of the payloads which could allow thing to infer information about any metadata included in the payload of the request, and the length of the username in the reply. Question: Would it be better to use a counter mode instead of CBC? Counter modes are easier to screw up and have less support in the various crypto libraries of different languages. On the other hand they avoid the need for padding and thereby avoid padding oracle attacks. The use of the HMAC around the encrypted block in this design provides protection against these attacks. Author/Contact ============== Richard J. Moore <[email protected]> _______________________________________________ cryptography mailing list [email protected] http://lists.randombit.net/mailman/listinfo/cryptography
