I'm implementing a TLS 1.2 server, comparing against OpenSSL to ensure I'm getting things right, and I've noticed that when the cipher is TLS_RSA_WITH_AES_256_CBC_SHA the IV isn't in the FINISH message. OpenSSL is taking it from the keyblock data, derived via PRF from the master secret.

My understanding of the RFC (https://tools.ietf.org/html/rfc5246#appendix-A.1) is that GenericBlockCipher types have the IV as part of the fragment data. So, AES_256_CBC is apparently a GenericStreamCipher, not a GenericBlockCipher. But that's not what appendix C says. Can anyone explain why OpenSSL isn't putting the IV in a FINISHED, v 1.2 message?

===== (To confirm that I'm doing things right)

My SERVER_HELLO looks like this:

00000000: 16 03 03 00 31 02 00 00  2D 03 03 6E 97 FF 27 00  ....1...-..n..'.
00000010: 01 02 03 04 05 06 07 08  09 0A 0B 0C 0D 0E 0F 10  ................
00000020: 11 12 13 14 15 16 17 18  19 1A 1B 00 00 35 00 00  .............5..
00000030: 05 FF 01 00 01 00  

type = 'HANDSHAKE'
tls_plaintext_maj = 3
tls_plaintext_min = 3
fragment =
    msg_type = 'SERVER_HELLO'
    body =
        server_version_major = 3
        server_version_minor = 3
        gmt_unix_time = 1855455015
        random_bytes = b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b'
        session_id = b''
        cipher_suite = 'TLS_RSA_WITH_AES_256_CBC_SHA'
        compression_method = 0
        extensions = [Container({'extention_type': 'RENEGOTIATION_INFO', 'extension_data': b'\x00'})]

And OpenSSL seems fine with it, sending me CLIENT_KEY_EXCHANGE, CHANGE_CIPHER_SPEC and then this HANDSHAKE:

type = 'HANDSHAKE'
tls_ciphertext_maj = 3
tls_ciphertext_min = 3
fragment = b"\x87E\x08\xf4\xc9A\xadw\xe8i\xdb\xd6\xcb\x9d\x82\xa9\xdd\xa9\xee\x8d\xaa\x94\xfd\xc3\xc9\xf5\x94\xb4\x1d\xb4@\xd6\xc1\x8d\x16\xf0\x05\xbb!\xeb\x14HY\xe0\xb9\xa9i0+\xda\n\xe1\x87'V\x16\x1b\xce\xc41\xe6\x81\xe5\xb3"

That fragment can be decrypted with AES256, using:

client_iv = faaa0de35870f9041e40471b02d5dfd5
client_write_key = 702ca8bd8cc39a707b297f3ef164cb5c7a0f90f39a20fd86e3dfcdd2e01eeb62

Those keys are generated on my side via PRF and the shared master secret, and after decrypting it looks valid (there are 11 \0x0b bytes at the end, which is the expected padding).

In [8]: AES.new(bytes.fromhex('702ca8bd8cc39a707b297f3ef164cb5c7a0f90f39a20f
   ...: d86e3dfcdd2e01eeb62'), AES.MODE_CBC, bytes.fromhex('faaa0de35870f904
   ...: 1e40471b02d5dfd5')
   ...: )
Out[8]: <Crypto.Cipher.AES.AESCipher at 0x99fada7208>

In [9]: _.decrypt(b"\x87E\x08\xf4\xc9A\xadw\xe8i\xdb\xd6\xcb\x9d\x82\xa9\xdd
   ...: \xa9\xee\x8d\xaa\x94\xfd\xc3\xc9\xf5\x94\xb4\x1d\xb4@\xd6\xc1\x8d\x1
   ...: 6\xf0\x05\xbb!\xeb\x14HY\xe0\xb9\xa9i0+\xda\n\xe1\x87'V\x16\x1b\xce\
   ...: xc41\xe6\x81\xe5\xb3")
Out[9]: b'\x1c\xe3<\x88\xf4\xc0\x98\xe3\x8f\xbf\xc9\xa9"\'\x1bq\x14\x00\x00\x0c]\x95\xb8+l\x10 \xad\x99\xd8\xe2\x16=\xbf\x9e{\xc7\xd5\xbdLm\x04n\xa6/\xfb

So, I'm pretty sure I'm doing it right.


  • $\begingroup$ The title of the question does not match the actual question in your post. As for the question in your post, the expected length of a TLS 1.2 AES_256_CBC_SHA cipher text Finished fragment is 16 (for the IV) plus 4 (for the Handshake header) plus 12 (for the Finished hash) plus 20 (for the SHA-1 HMAC) plus 11+1 (for the padding), equaling 64 octets in total. Is that what you got? $\endgroup$ – Henrick Hellström Nov 17 '16 at 1:34
  • $\begingroup$ How embarrassing. The title is corrected - thank you. $\endgroup$ – Mark W Nov 17 '16 at 12:17
  • $\begingroup$ I do see 64 bytes, but I don't see the plaintext IV as the first 16 bytes. Isn't that what I should expect? I am confident that the IV above is correct - I can decode the 64 byte fragment using it and the client_write_key. $\endgroup$ – Mark W Nov 17 '16 at 12:20

Nothing is wrong, it is just that when decrypting CBC cipher text, the IV will only affect the decryption of the first block. The first cipher text block will be used as IV when decrypting the second cipher text block, the second cipher text block will be used as IV when decrypting the third cipher text block, etc.

Consequently, if the IV is prepended to the CBC cipher text, you may actually use any arbitrary IV you want when decrypting the cipher text, as long as you "decrypt" the IV as well and discard the first block of decrypted text prior to presenting the plain text to the next layer.

Hence, none of the information you provide in your question is inconsistent with the cipher text fragment really being a GenericBlockCipher.

Also note that in TLS 1.2, the IV of a GenericBlockCipher fragment will typically be generated randomly. In particular, it must not be identical to the IV optionally generated by the KDF in TLS 1.0 compatibility mode.

  • $\begingroup$ Thank you so much! Visually confirming the plaintext by looking at the last few bytes was not a great idea with CBC... $\endgroup$ – Mark W Nov 17 '16 at 13:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.