I am implementing TLS 1.2 and I'm stuck on the client finished message.

My question is: what is the size and structure of a clients finished message in TLS 1.2 when using the ECDHE_RSA_AES_128_GCM_SHA256 cipher suite.

I searched for this question and someone somewhere stated its size is 48 bytes; more specifically:

  • 12 byte verify data
  • 1 byte handshake type
  • 3 byte verify data length
  • and 32 byte MAC

Is that really correct?

I mean: according to RFC 5246 finished messages have {verify_data[verify_data_length]} and verify_data = PRF(master_secret, finished_label, hash(handshake_messages). Also, according to RFC 5246 section 5 PRF(secret, label, seed) = P_<hash>(secret, label+seed) and P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed). So, when I compute HMAC using SHA256 for verify_data its size is 32 byte.

As a result, I'm a bit confused in relation to the verify_data size and the finished message structure. Can anyone help me to understand this and/or maybe put me on the right track somehow?

  • $\begingroup$ @dave_thompson_085 According to this link blogs.msdn.microsoft.com/dsnotes/2015/12/03/… Crypto API’s or CAPI2 does not support authenticated encryption mode. This means there are no API’s in CAPI2 that can be used to implement authenticated encryption. It can only be done using CNG where BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO structure can be passed to CNG API’s for example BCryptEncrypt() to implement authenticated encryption. and in detail of BCryptEncryption According to this link msdn.microsoft.com $\endgroup$ Commented Apr 28, 2016 at 9:07
  • $\begingroup$ I updated my answer to provide an example for expected finished message from the server side. At first, i can see that your iv(first 8 bytes) should be all zero if this is the initial handshake(not a resumption). Then, the server expects 16 byte of encrypted finished message and then 16 byte of authentication tag from the encryption of the finished message as well. $\endgroup$
    – Makif
    Commented May 4, 2016 at 6:31
  • $\begingroup$ Sorry, in RFC 5288 it is stated that The nonce_explicit MAY be the 64-bit sequence number. So, i guess it is not forced to use the sequence number as nonce-explicit. So you do not need to worry about the iv part of your message above. Also note that, Each value of the nonce_explicit MUST be distinct for each distinct invocation of the GCM encrypt function for any fixed key. Failure to meet this uniqueness requirement can significantly degrade security. $\endgroup$
    – Makif
    Commented May 4, 2016 at 8:04
  • $\begingroup$ Now you moved the goal again. DTLS1.2 is not the same as TLS1.2 in particular HandshakeMessage is 8 bytes longer; see tools.ietf.org/html/rfc6347#section-4.2.2 . @Makif: even for resumption sequence number starts at zero, which as you note may be used for record-nonce. (But resumption has new working keys due to new hello-nonces used in KDF.) $\endgroup$ Commented May 4, 2016 at 11:43
  • $\begingroup$ I dont know the last 8 bytes of your message. You should check dave's comment. It seems that you are doing DTLS1.2 instead of TLS1.2 . You should provide the way that you generate that message, are you using a library which generates the finished message for you? $\endgroup$
    – Makif
    Commented May 5, 2016 at 5:49

2 Answers 2


rfc5246 7.4.9 defines verify_data as

     PRF(master_secret, finished_label, Hash(handshake_messages))

Note the second line; this effectively truncates the PRF output to verify_data_length octets. It goes on to say that verify_data size

depends on the cipher suite. Any cipher suite which does not explicitly specify verify_data_length has a verify_data_length equal to 12.

ECDHE_RSA_AES_128_GCM_SHA256 is defined in rfc 5289 which does not explicitly specify, so this default applies.

Second, someone somewhere is wrong. GCM and CCM ciphersuites do not have a HMAC on the (plaintext) record, although stream and 'block' (CBC) suites do; instead an authentication tag is added to the encrypted record by the AEAD process. Compare to and

  • $\begingroup$ Thanks for your reply. According to AEAD AEADEncrypted = AEAD-Encrypt(write_key, nonce, plaintext, additional_data) i know about write_key can you tell me about "nonce" and according to my search plaintext is sum of all the handshake message before client finished messages, am i right? And additional_data = seq_num + TLSCompressed.type + TLSCompressed.version + TLSCompressed.length) In my case compression method is "null", can you tell me little bit about additional_data? $\endgroup$ Commented Apr 27, 2016 at 5:20
  • $\begingroup$ I'd say: the data to encrypt is 12 bytes, as Dave says, i.e. the truncated output of the PRF-SHA256 output. But GCM being used, means the ciphertext will be longer and include the nonce (12 bytes) and the tag (16 bytes), as to RFC5288. The tag also authenticates the TLS header data (type and length). $\endgroup$ Commented Apr 27, 2016 at 9:02
  • 1
    $\begingroup$ @HennoBrandsma not quite: almost per the Q, plaintext is 1 byte handshake type, 3 bytes length, 12 bytes truncated PRF value; for GCM the AEAD-encrypted body is 8 bytes 'explicit' nonce (which is combined with 4 bytes 'implicit' nonce from the KDF), then the CTRish-encrypted data plus 16-byte tag; see and (more of) rfc5288. Ahsan: if compression is null, TLSCompressed is the same as TLSPlaintext (and vice versa), see 6.2.2. $\endgroup$ Commented Apr 28, 2016 at 4:05

In the Finished Message FOR TLS, verify data is 12 byte long unless it is stated otherwise in the ciphersuite, so in your case it is 12 byte long. It is in the following handshake message form:

struct {
      HandshakeType msg_type;    /* handshake type */
      uint24 length;             /* bytes in message */
      select (HandshakeType) {
          case finished:            Finished;
      } body;
  } Handshake;

However, since the question turned out to be DTLS NOT TLS this is modified to

struct {
     HandshakeType msg_type;
     uint24 length;
     uint16 message_seq;                               // New field
     uint24 fragment_offset;                           // New field
     uint24 fragment_length;                           // New field
     select (HandshakeType) {
       case finished: Finished;
     } body;
   } Handshake;

Finished message is created using the PRF of the agreed cipher suite. After creating Handshake message you need to convert it to a TLSCipherText structure, in the form of

struct {
          ContentType type;
          ProtocolVersion version;
          uint16 length;
          select (SecurityParameters.cipher_type) {
              case stream: GenericStreamCipher;
              case block:  GenericBlockCipher;
              case aead:   GenericAEADCipher;
          } fragment;
      } TLSCiphertext;

but again DTLS is different

struct {
    ContentType type;
    ProtocolVersion version;
    uint16 epoch;                                     // New field
    uint48 sequence_number;                           // New field
    uint16 length;
    select (CipherSpec.cipher_type) {
      case block:  GenericBlockCipher;
      case aead:   GenericAEADCipher;                 // New field [sic]
    } fragment;
  } DTLSCiphertext;

Here you are using ECDHE_RSA_AES_128_GCM_SHA256 which is an AEAD Cipher, so it is in the form of GenericAEADCipher:

struct {
     opaque nonce_explicit[SecurityParameters.record_iv_length];
     aead-ciphered struct {
         opaque content[TLSCompressed.length];
  } GenericAEADCipher;

Here, the nonce_explicit contains 8 byte nonce, and you combine this with the 'fixed IV' from key derivation (4 bytes) then you get 12 byte IV for AES-GCM mode encryption. (Note that you only sent the last 8 bytes of the IV).

In this structure, the aead-ciphered result includes encryption output || authentication tag. So you have sent the authentication tag by concatenating it to encrypted message. In the case for finished message, encryption output and authentication tag are outputs of the encryption of Finished message.

At the end, expected finished message from server side for cipher suite ECDHE_RSA_AES_128_GCM_SHA256 should look like this for TLS:

16 --ContentType(hanshake)
03 03 --protocolVersion(tls 1.2)
00 28 -- message length(40)
  --finished message--
  00 00 00 00 00 00 00 00 --nonce_explicit. (8 byte) (this is write sequence number, for initial handshake this should be all zero)
  xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx -- encrypted finished message. (16 byte) (Note that finished message is of length 16(1 byte finished message type + 3 byte handshake message length + 12 byte verify_data) )
  yy yy yy yy yy yy yy yy yy yy yy yy yy yy yy yy -- authentication tag. (16 byte) (this is also output of the encrption of finished message using AES-GCM)

but this for DTLS

16 -- type=handshake
fe fd -- version=DTLS1.2
nn nn -- epoch, 1 for the initial negotiation, more if renegotiation
00 00 00 00 00 00 -- seqnum, always zero because Finished is first after CCS
00 30 -- length=48
  (8 bytes) -- explicit nonce
  (24 bytes) -- encryption of Finished handshake message
  (16 bytes) -- authentication tag
  • $\begingroup$ @dave_thompson_085 Thank's for your answer but there is a problem. Block size of AES_128_GCM is 16 bytes. So the "encryption of finished handshake message" can be 16 bytes or 32 bytes(multiple of 16). You are saying cipher text is 24 bytes how is it possible? $\endgroup$ Commented May 12, 2016 at 9:46
  • $\begingroup$ @Ahsan GCM is a stream(ish) mode that is not restricted to block boundaries. It is based on counter mode (CTR) which (like OFB* and CFB1) can encrypt any bit string. The GCM construction would actually work for any bits, but SP-800-38D 5.2.1 restricts it to 8-bit bytes for no obvious reason, perhaps just for implementation/validation and interop like AES-vs-Rijndael and SHA3-vs-Keccak. That doesn't matter to TLS and DTLS which already restrict data to 8-bit bytes. $\endgroup$ Commented May 12, 2016 at 15:34
  • $\begingroup$ @dave_thompson_085 I know gcm is counter based but AES is block based. I'm using BCryptDecrypt API link for decryption and when i give the size of buffer 24 then error occur"The size of buffer is invalid for specified operation" but it run successfully when i gave buffer size 16. $\endgroup$ Commented May 13, 2016 at 12:15
  • $\begingroup$ @Ahsan I don't code Microsoft crypto and don't know what your problem is there; that might be a good question for stackoverflow. But GCM definitely does not require full-block data; the spec is very clear, and the implementations I can easily test (OpenSSL and Java Oracle and BC) definitely handle 24 bytes (+16 tag). In fact the page you link says "dwFlags ... value BCRYPT_BLOCK_PADDING ... must not be used with the authenticated encryption modes (AES-CCM and AES-GCM)." and that makes sense because CCM and GCM do not require full blocks and thus never use padding. $\endgroup$ Commented May 13, 2016 at 17:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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