6

I'm trying to decrypt a message sent over a secure communication channel using TLS_PSK_WITH_3DES_EDE_CBC_SHA. I now have the client random number, server random number, PSK identity and PSK value. My problem is how to derive the key that was used for encryption and decryption.

I have read RFC 2246 TLS 1.0, and RFC 4279 Pre-Shared Key Ciphersuites for TLS standards. My understanding is that during the PSK TLS handshake between the client and server, the client and server agree on which PSK (pre-shared key) to use. This PSK will be used to derive the session key. This session key is used for encryption and decryption of messages. However, during my reading of the standards, I could not find a formula or procedure to derive this. I found a formula to compute the pre-master secret but I am not sure if this is the one used for encryption or decryption.

  1. In PSK TLS, is the pre-master secret the same as the key used for encryption and decryption?
  2. If the pre-shared secret is not the same as the key used for encryption and decryption, how is this key derived?
bookhuntress
  • 163
  • 1
  • 6

1 Answers1

5

The pre-master secret is encrypted and exchanged in the TLS handshake. This value is used to derive the master key. The master key is used to derive all other keying material by passing it into a pseudo-random function (PRF), and combining other pieces of data from the handshake.


Deriving the Pre-Master Secret for a PSK

From RFC427: Section 2

The premaster secret is formed as follows: if the PSK is N octets long, concatenate a uint16 with the value N, N zero octets, a second uint16 with the value N, and the PSK itself.

If using Diffie Hellman w/ PSK, Section 3:

The premaster secret is formed as follows. First, perform the Diffie-Hellman computation in the same way as for other Diffie-Hellman-based ciphersuites in [TLS]. Let Z be the value produced by this computation (with leading zero bytes stripped as in other Diffie-Hellman-based ciphersuites). Concatenate a uint16 containing the length of Z (in octets), Z itself, a uint16 containing the length of the PSK (in octets), and the PSK itself.

If using RSA w/ PSK, Section 4

The EncryptedPreMasterSecret field sent from the client to the server contains a 2-byte version number and a 46-byte random value, encrypted using the server's RSA public key as described in Section 7.4.7.1 of [TLS]. The actual premaster secret is formed by both parties as follows: concatenate a uint16 with the value 48, the 2-byte version number and the 46-byte random value, a uint16 containing the length of the PSK (in octets), and the PSK itself. (The premaster secret is thus 52 octets longer than the PSK.)


Deriving Master Key

Everything that follows does not just apply to TLS PSK; it applies to all key exchange types. From RFC 2246: Section 8.1

For all key exchange methods, the same algorithm is used to convert the pre_master_secret into the master_secret. The pre_master_secret should be deleted from memory once the master_secret has been computed. The master secret is always exactly 48 bytes in length. The length of the premaster secret will vary depending on key exchange method.

   master_secret = PRF(pre_master_secret, "master secret",
                       ClientHello.random + ServerHello.random)
   [0..47];

The PRF is defined as combining two different hashing functions. Section 5 of RFC 2246:

First, we define a data expansion function, P_hash(secret, data) which uses a single hash function to expand a secret and seed into an arbitrary quantity of output:

   P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
                          HMAC_hash(secret, A(2) + seed) +
                          HMAC_hash(secret, A(3) + seed) + ...

   Where + indicates concatenation.

   A() is defined as:
       A(0) = seed
       A(i) = HMAC_hash(secret, A(i-1))

The function described above is really P_MD5 or P_SHA1. The TLS PRF is described as follows:

TLS's PRF is created by splitting the secret into two halves and using one half to generate data with P_MD5 and the other half to generate data with P_SHA-1, then exclusive-or'ing (XOR) the outputs of these two expansion functions together.

So lets claim the following:

L_S = length in bytes of secret;
L_S1 = L_S2 = ceil(L_S / 2);

The PRF is then:

PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR
                              P_SHA-1(S2, label + seed);

Derive Encryption Keys

The key derivation is described in RFC 2246: Section 6.3

The entire keyblock is derived as follows:

key_block = PRF(SecurityParameters.master_secret,
                "key expansion",
                SecurityParameters.server_random +
                SecurityParameters.client_random);

Once enough material is generated and stored in the key_block, the key_block is split into the encryption/decryption keys. Encryption or decryption depends on the direction. As they're all referenced as write keys. The key_block is sequentially split into the arrays:

client_write_MAC_secret[SecurityParameters.hash_size]
server_write_MAC_secret[SecurityParameters.hash_size]
client_write_key[SecurityParameters.key_material_length]
server_write_key[SecurityParameters.key_material_length]
client_write_IV[SecurityParameters.IV_size]
server_write_IV[SecurityParameters.IV_size]  

There's some additional computations performed for exportable block ciphers. You can read those details in Section 6.3 of RFC2246 as well. Section 6.3.1 gives an example of these computations without the extra descriptions.

RoraΖ
  • 12,347
  • 4
  • 51
  • 83
  • Thanks, @raz. Your answer has helped a lot. :) Just an additional info for the next reader, BouncyCastle provides a utility class that can perform PRF, TlsUtils (http://http://www.eecs.berkeley.edu/~jonah/bc/index.html?org/bouncycastle/crypto/tls/TlsUtils.html). – bookhuntress Feb 25 '15 at 05:36
  • Just to emphasize, PRF has to be performed 2x. First to generate the master secret and second to generate the key_block, aka key_material in some documents. My biggest mistake was to perform PRF only once. For first PRF, pass the pre-master secret as the secret, "master secret" as the label, and the concatenation of client_random and server_random as the seed. The first PRF will generate the master_secret. For second PRF, pass the master_secret as the secret, "key expansion" as the label, and the concatenation of server_random and client_random as the seed. This will generate the key_block. – bookhuntress Feb 25 '15 at 05:57