Eventually this will document the full handshake protocol, sufficient for a clean-room implementation. This doc isn't that far yet, but whatever. There is a four-way exchange: ClientHello - A nonce - An RSA public key - A hash derived from the nonce, the client's public key, and the server's public key. If the server cares, it can check this and ignore any clients that don't know it's public key (to prevent mass scanning). ServerHello - A nonce - The server's public key ClientKeyExchange - The client's empheral DH public key - A signature for same, generated from the RSA key ServerKeyExchange - The server's empheral DH public key (same group as the clients) - A signature on same By the time all this junk has been exchanged, we have generated a shared secret via DH (or have bailed due to bad sigs or whatever). Calling the shared key generated by DH DH_X, we compute MASTER_SECRET = SHA-256(DH_X || CLIENT_RANDOM || SERVER_RANDOM); Then we generate two cipher keys (for AES-256/CTR) and two MAC keys (for HMAC/SHA-1) by KEY = KDF2/SHA-256(MASTER_SECRET, STRING) where STRING is "client cipher" "server cipher" "client MAC" "server MAC" (in ASCII). These really could be just single bytes with values 1-4, but whatever. KDF2 (nominally) makes a distinction between the secret value and the derivation parameter; obviously in this case the secret value is the master secret, and the strings are the derivation paramters. Notes ---------------------- Might be better to use SHA-512 to derive the master secret, which would prevent anyone from working backwords if they manage to recover a cipher key. The 160-bit MAC keys mean noone can work back from those (unless, maybe, they get both of them).