Vulnerability – CVE-2014-0224
This vulnerability exposes a corner case within the OpenSSL implementation [versions ()] allowing an attacker (MITM) to inject change cipher spec messages at any point during the TLS handshake, altering the key generation process.
Some background on TLS :
TLS record Protocol is layered protocol i.e. it is consists 4 different protocols ( the handshake protocol, the alert protocol, the change cipher spec protocol and the application data protocol) – each designed for a specific function.
This vulnerability is to do with the Change cipher spec protocol (one of the four).
Here’s a typical TLS workflow. This is what it looks like (from RFC 2246)
Application Data <——-> Application Data
Message flow for a full handshake
* Indicates optional or situation-dependent messages that are not
A deeper look
The handshake protocol allows for negotiating cryptographic parameters like the encryption, compression algorithms acceptable to both parties, exchanging random values (for use later in the key generation process) and sending an encrypted ‘pre-master secret’ in the ‘client key exchange message’.
master_secret = PRF(pre_master_secret, “master secret”, ClientHello.random + ServerHello.random) [0..47];
* the second argument ‘master secret’ is just an ASCII label
which is in-turn used by each recipient to verify the final ‘Finished message‘ – a PRF digest of all the previous handshake messages.
verify_data = PRF(master_secret, finished_label, MD5(handshake_messages) + SHA-1(handshake_messages)) [0..11];
* PRF is a “Pseudo-Random Function” defined in the RFC. It takes 3 arguments – a secret, an ASCII label, the seed data, runs its through a keyed-Hash Message Authentication Code (HMAC) and produces an output of arbitrary length. Essentially – a secure digest of some data protected by a secret. TLS’s PRF is a bit unique in that it uses two hashing functions (MD5 and SHA1) for additional security i.e. the secret is halved and each half provided as input to one of the hash functions. The PRF is then defined as the result of mixing the two pseudorandom streams by exclusive-or’ing them together.
PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed);
Assuming that the check succeeds, both generate session keys (or rather a block of cryptographic key material) from the master secret along with the random nonces and proceed to send cryptographically-protected application data.
* To generate the key material, we compute
key_block = PRF(SecurityParameters.master_secret, “key expansion”, SecurityParameters.server_random + SecurityParameters.client_random);
Now for the implications :
Essentially handshake protocol is a state machine which starts out with a connection state that does not use any encryption and switches to connection state that uses encryption.
When starting a new handshake, messages used in the negotiation of the security parameters are sent in the clear i.e. the initial current state always specifies that no encryption, compression, or MAC. The change cipher spec message (sent by both client and server) signals to the recipient that all future messages/data from the sender will now be encrypted with negotiated security parameters (keys, ciphers, MAC etc.)
Although the RFC explicitly calls out the need for the change cipher spec message to be sent during the handshake, after the security parameters have been agreed upon, but before the finished message , it doesn’t say much on when a recipient should/can accept and process a change cipher spec message.
Now if an attacker (MITM) injects CCS messages into a connection just before master key is generated, he could alter the key generation process and bypass the finished message verification. Put it simply – the injected CCS message would signal a state change to the recipient, leading to the premature computation of session keys and finished messages with information available to a MITM attacker i.e. without the ‘master secret’ – the only unique parameter in the above PRF calculations.
OpenSSL’s Change cipher spec implementation :
int ssl3_do_change_cipher_spec(SSL *s) # ‘CSS messages invoke this function’
const char *sender;
if (s->state & SSL_ST_ACCEPT)
if (s->s3->tmp.key_block == NULL)
if (s->session == NULL)
/* might happen if dtls1_read_bytes() calls this */
if (!s->method->ssl3_enc->setup_key_block(s)) return(0); # ends up setting the key_block (includes session keys) prematurely
/* we have to record the message digest at
* this point so we can get it before we read
* the finished message */
if (s->state & SSL_ST_CONNECT)
i = s->method->ssl3_enc->final_finish_mac(s, sender,slen,s->s3->tmp.peer_finish_md); # and premature verifying the expected ‘finished’ hashes
if (i == 0)
s->s3->tmp.peer_finish_md_len = i;
To be fair, I think the cause for this vulnerability is more to do with the ambiguity in the RFC than a lapse in the OpenSSL implementation, considering that the RFC is pretty much mute on ‘WHEN’ recipients should accept ‘Change cipher spec’ messages.
Only accept change cipher spec when it is expected instead of at any time. This prevents premature setting of session keys before the master secret is determined which an attacker could use as a MITM attack.