Subject: [TLS] draft-stjohns-tls-tls13-crypto-infra

From: Michael StJohns <[email-masked]>
Date: Wed, 16 Apr 2014 21:13:31 -0400

Hi -

I submitted draft-stjohns-tls-tls13-crypto-infra-00.txt last week. I'm 
hoping to get some discussion started on updating the TLS handshake 
cryptographic constructs to something that can be implemented in a 
secure and general manner in hardware.  The current constructs have some 
issues in that its difficult to impossible to use them in a manner that 
ensures that key material can never be extracted from the hardware.

There are two reasons for the current issues: 1) the use of the master 
secret as both a secret key for a MAC operation, and as key for a key 
expansion operation; 2) The key expansion function produces both key 
material (the 4 secret keys for encryption and record mac) and public 
material (the client and server write IVs). The issues with these became 
apparent when trying to update the PKCS11 specification to support TLS1.2.

Part 1 Issue - Mac vs Key Expansion

To understand the issues, let's first compare the MAC function and the 
key expansion function first and let's assume the PRF is based on 
HMAC-SHA256.  I'm going to use the TLS1.2 definitions, but much of this 
applies to TLS1.1 and before.

The MAC function produces the verify data as

>        verify_data
>           PRF(master_secret, finished_label, Hash(handshake_messages))
>              [0..verify_data_length-1];

Given that PRF is
>        PRF(secret, label, seed) = P_<hash>(secret, label + seed)

and that P_<hash> ends up being (since you only need 12 octets of data)

>      P_hash(secret, seed) = HMAC_SHA256 (secret, HMAC_SHA256(secret, seed) + seed)

But "seed" above is "label + SHA256(handshake_messages)" - or really 
"label + random data".

verify_data = HMAC_SHA256 (master_secret, HMAC_SHA256 (master_secret, 
seed) + seed);

So calculating verify_data, if you didn't restrict how the master_secret 
was used, could be done using normal implementations of HMAC_SHA256 and 
SHA_256 and simply passing in processed data for the seed of "label + 
SHA256(handshake_messages")

But now consider how key expansion works:

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

Which reduces down to  (assuming only 32 bytes of key - easier to do the 
analysis)
seed = label + server_random + client_random
key_block = HMAC_SHA256 (master_secret, HMAC_SHA256 (master_secret, 
seed) + seed)

which is the same expansion as above - just with different values for 
the seed.   So the only output difference is that the MAC function is 
supposed to producing public data, while the expansion function is 
supposed to be producing secret data.  But since they both use the same 
key and same underlying construct, we can't  just use  a generic 
HMAC_SHA256 function in hardware.  We need to move up a couple of levels.

So let's compare this if we define a TLS_PRF function (as was done in 
PKCS11 v2.20).

key_block = PRF (master_secret, label, random_data); // random data is 
the concatenation of server and client random
verify_data = PRF (master_secret, label, random_data); // random data == 
SHA256 of the handshake messages

Still not enforceably cryptographically separate.

So finally, we get to building specific functions:

verify_data = TLS_MAC (master_secret, handshake_data, clientOrServer);

  - and -

key_block = TLS_KDF (master_secret, client_random, server_random);

Where the TLS_PRF is still the underlying function, but is not directly 
available to the user.  The processing specified as above in the 
verify_data step and the key expansion step is used to provide the two 
types of data.  The value of the label for each of these functions is 
fixed.

But we still have a problem - the TLS master key has to be marked 
specifically as a TLS master key within the hardware module, otherwise 
it can be used directly with the HMAC_SHA256 function.  So instead of 
being able to define generic functions to do this, we have to implement 
specific TLS functions, and to update and change them with each version 
of TLS.  Not ideal, but at least tractable. It has an interesting side 
effect that you may no longer be able to use the construct for key 
material exporters.

Unfortunately, that leads to the second problem.

Part 2 - the Key Expansion Issue

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

The above construct generates a key stream of length 2* (mac_key_length 
+ enc_key_length + fixed_iv_length), and that key stream is then split 
into up to six parts, 4 of which are the secret key materials for the 
mac and encryption keys, but 2 of which are used for the IV - 
initialization vectors.

>        client_write_MAC_key[SecurityParameters.mac_key_length]
>        server_write_MAC_key[SecurityParameters.mac_key_length]
>        client_write_key[SecurityParameters.enc_key_length]
>        server_write_key[SecurityParameters.enc_key_length]
>        client_write_IV[SecurityParameters.fixed_iv_length]
>        server_write_IV[SecurityParameters.fixed_iv_length

What happens if you re-run the derivation using the same master_secret, 
same label and same random data, but you change the length of the 
component keys in a manner that keeps the total length of the key 
stream  the same, but ends up with key material moving from the secret 
part of the stream to the public part of the stream?

E.g. consider a suite that requires 32 bytes of mac key, 16 bytes of 
encryption key, and 16 bytes of IV.  The total key stream length is 128 
bytes.   Re-run the derivation, but instead assign 16 bytes of key 
material to each mac key, 16 bytes to each encryption key and 32 bytes 
to each IV.  The key material originally used to form the two encryption 
keys is now the 32 bytes of the client_write_IV.  Since IVs are public, 
an attacker has successfully managed to extract key material.

TLS1.1 deprecated the production and use of the IV generated this way, 
but TLS1.2 added them back in.

The solution for this is intractable using the current construct 
definitions.  (Consider that its perfectly acceptable for a suite to not 
have encryption keys and/or mac keys, so placing minimum values for 
individual key lengths does not solve the problem).

_______________________________

The above would be a good enough reason to readdress these constructs, 
but the current constructs are also more expensive than they need to be 
for the security they provide:

1) Perhaps replace the current handshake MAC function with a simple HMAC 
or CMAC function.  Is there any security reason to do a hash of the 
handshake messages before being MAC'd?

2) The current PRF is recursive which makes it difficult to 
parallelize.  It was based on the original SSL PRF, but wasn't really 
looked at when the SHA1/MD5 construct was replaced.  Instead, use one of 
the pseudo-random functions from SP800-108 based on counters.  That 
function could be used either with a master key (for key expansion) or 
with a key of '0' (for pseudo-random data production for things like IVs).

 From my point of view, I'd really like to eliminate TLS specific 
constructs and instead define TLS in terms of cryptographic constructs 
defined and evaluated elsewhere.

Mike