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://www.rfc-editor.org/rfc/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
\xfd\x8f\x96\x92\xe5\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b'
So, I'm pretty sure I'm doing it right.
Thanks...