david wong

Hey! I'm David, a security consultant at Cryptography Services, the crypto team of NCC Group . This is my blog about cryptography and security and other related topics that I find interesting.

DTLS and Finished messages

posted June 2016

Ahsan asked me:

Sorry i am using DTLS 1.2 instead TLS 1.2. Kindly explain the structure of finished message, like how many bytes for "nonce", how many bytes are encrypted data and how many bytes for authentication tag.

I'm writing this because I want to clear things up: you can ask me pretty much anything and I'll do my best to answer here. So please ask away if you have a question in crypto! There is also a contact form for that.

Differences between DTLS and TLS

TLS is an application layer protocol. Some people say that it's a transport layer protocol. This is false. It runs in whatever program you are using on top of TCP/UDP. Although it is used to transport the real content of the application: it can be seen as an intermediate transport layer running in the application layer.

The TLS we know, the one that runs in our browser, typically runs on top of TCP. But in some situations, the developer might want to use UDP to exchange packets. Since UDP forgives packet loss (think multiplayer video games or audio/video conferences), it is important that TLS is setup accordingly to forgive those packet loss as well. For this, we use a similar but different specification than TLS: DTLS.

DTLS is what you use if you need TLS on top of UDP.

The main DTLS RFC documents the differences with TLS. Most of them are modification to the protocol so that the connection doesn't terminate/break if a message is lost, duplicated, out of order, etc...:

  • records can't be split into several datagrams
  • the sequence number is written in each record
  • errors (duplicates, loss, out of order) are tolerated
  • no stream cipher can be used (no state can be used if errors are tolerated)
  • protections against DoS attacks (apparently DTLS has some problems with DoS attacks)
  • ...

Finished messages

The simplest TLS handshake goes like this:

  • the client sends its ClientHello packet
  • the server replies with his ServerHello packet
  • the client sends (his part of) the shared secret in a ClientKeyExchange packet

Now I omitted a bunch of packets that are usually part of the handshake as well. For example:

  • the server usually sends his certificate after the ServerHello message.
  • the server might also take part in the creation of the shared secret in some modes (including ephemeral modes)

But this is not what is interesting to us here.

After enough messages have been sent to compute the shared secret, a ChangeCipherSpec message is sent by both the client and the server to announce the beginning of traffic encryption. Followed directly by an encrypted Finished message authenticating all the previous handshake messages.

In my knowledge, the Finished message is the only encrypted message of a handshake. It is also the moment where the handshake is "authenticated" and where Man-In-The-Middle attacks usually stop.

Now what is in that Finished message?

Exactly the same things as in TLS. The TLS 1.2 RFC shines a bit more light on the subject:

struct {
    opaque verify_data[verify_data_length];
} Finished;

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

finished_label =
For Finished messages sent by the client, the string "client finished". For Finished messages sent by the server, the string "server finished".

Don't forget that this Finished structure is then encrypted before being sent in a record. The Hash and the PRF we already defined in previous posts, the handshake_messages value is what interest us: it is the concatenation of all the binary data received and sent during the handshake, in order, and not including this Finished one.

Now DTLS has the particularity that some messages are re-sent, out of order, etc... so duplicates must be ignored, real order must be preserved.

How do I know that?

Besides reading the RFC, you might often want to know what's happening for real. To be a bit more informative, let me tell you how I quickly get that kind of information when I'm curious:

  • I setup a server key + certificate: openssl req -x509 -new -nodes -keyout key.pem -out server.pem.
  • I start the server: openssl s_server -dtls1 -key key.pem -port 4433 -msg.
  • I connect to it with a client: openssl s_client -dtls1 -connect localhost:4433 -msg.

The -msg argument will print out the exact content of the messages sent. In the case of the Finished message, it will show the unencrypted hexadecimal data sent. If you want to see the real encrypted data that is sent, you can use the -debug option.

You might also want to have a bit more information about every records. A good way to do this is to record the traffic with tcpdump: sudo tcpdump udp -i lo0 -s 65535 -w handshake.pcap and to open the .pcap file in Wireshark and enter udp && dtls in the filter area.

udp

Well done! You've reached the end of my post. Now you can leave me a comment :)

Thomas DuBuisson

XSS testing of your blog? <br/> @mdtom wants to join the fun!

Ahsan

Thanks for your post.

Ahsan

I have a question about handshake message value. You mention above "it is the concatenation of all the binary data received and sent during the handshake" is this value include record layer header and handshake header as well?

david

Ahsan: yes, any "full" packets (so with the headers) during the handshake.

the headers contains information, like the TLS version or the length of the packet, that needs to be authenticated as well.

CryptoNewbie

Thanks so much for this article!
A question, how can you send application data using the s_client, s_server binaries after the connection is established?

CryptoNewbie

My goals is to decrypt my self the packets. Do you think is possible to get the random iv used and the tag for the ahead suite from this binaries?

david

CryptoNewbie:

1) just write what you want to send. For example if you do

```
openssl s_client -connect google.com:443
```

once it is connected, just write something like

```
GET /
```

and you will receive google's response.

2) using s_client? You can modify the OpenSSL code to make it print out the shared secret or the random IV. Or you can make the client authenticate + use a private key you know and derive the random IV from the key exchange yourself.

CryptoNewbie

Thanks for the reply.
Any hints for the second suggestion on how to derive the symmetric key using the packets and the known private key?

I start the server using a generated key as you suggest:
openssl s_server -dtls1_2 -key key.pem -port 4433 -msg -cipher AES128-GCM-SHA256
And I connect a client using:
openssl s_client -dtls1_2 -connect localhost:4433 -msg

I have the output of the communication.

CryptoNewbie

If the -msg outputs the cleartext should I find "test" in the bytes printed? Is there any other way using the s_client to get the packet bytes unencrypted? For some reason wireshark fails to decrypt the dtls1.2 traffic.
test
>>> ??? [length 000d]
16 fe fd 00 00 00 00 00 00 00 04 01 35
>>> ??? [length 0135]
01 00 01 29 00 00 00 00 00 00 01 29 fe fd cf ec
2b 53 e5 4c ad 37 2e e7 fb 3f 9b 56 5f e3 21 7c
e9 11 a8 ea ff 38 0b 28 ba e4 3b 98 17 06 00 00
00 aa c0 30 c0 2c c0 28 c0 24 c0 14 c0 0a 00 a5
00 a3 00 a1 00 9f 00 6b 00 6a 00 69 00 68 00 39
00 38 00 37 00 36 00 88 00 87 00 86 00 85 c0 32
c0 2e c0 2a c0 26 c0 0f c0 05 00 9d 00 3d 00 35
00 84 c0 2f c0 2b c0 27 c0 23 c0 13 c0 09 00 a4
00 a2 00 a0 00 9e 00 67 00 40 00 3f 00 3e 00 33
00 32 00 31 00 30 00 9a 00 99 00 98 00 97 00 45
00 44 00 43 00 42 c0 31 c0 2d c0 29 c0 25 c0 0e
c0 04 00 9c 00 3c 00 2f 00 96 00 41 00 07 c0 12
c0 08 00 16 00 13 00 10 00 0d c0 0d c0 03 00 0a
00 15 00 12 00 0f 00 0c 00 09 00 ff 01 00 00 55
00 0b 00 04 03 00 01 02 00 0a 00 1c 00 1a 00 17
00 19 00 1c 00 1b 00 18 00 1a 00 16 00 0e 00 0d
00 0b 00 0c 00 09 00 0a 00 23 00 00 00 0d 00 20
00 1e 06 01 06 02 06 03 05 01 05 02 05 03 04 01
04 02 04 03 03 01 03 02 03 03 02 01 02 02 02 03
00 0f 00 01 01

david

I see, I did not know you could control the server. But yes if you control the server it's easier to just setup a key on the server side.

You need to read the TLS spec (whatever version you're using) on how these things are computed. If you are using an RSA key it will be different than if you're using a DH key for the server. So you need to understand how to compute the key exchange offline to get the master secret. I talk about it here: https://www.cryptologie.net/article/340/tls-pre-master-secrets-and-master-secrets/

You will need to find out where is the client nonce, the server nonce (in these messages in wireshark) and a bunch of other "public" things that influence the outcome of the shared secret.

From there you will need to read about how the transport keys and IVs are derived from the shared secret.

It's definitely not an easy thing to do. You can't shy away from reading the TLS spec and it will be time consuming.

If I were you I would just cheat and modify the source code to just print out the IV.

Note that you can use the "-debug" and "-trace" options for openssl's s_client and s_server.

Note that using a different library might get you there quickly (golang's tls standard library allows you to export a bunch of things, and it might just be easier to modify the code to print out the IV there as well)

david

actually, there might be an easier way through the -keymatexport option!

https://www.douglas.stebila.ca/code/keying-material-exporters/

darkangel

@david, i am having trouble with tls finish handshake message too, and end up here(thanks for your infos), but when i read the rcf about the handshake messages, rcf said, the record layer doesn't count, i am not sure which one is correct, if you are correct, then that might be why i am having trouble decrypting the finish handshake message.

below is from the rcf:
handshake_messages
All of the data from all handshake messages up to but not
including this message. This is only data visible at the
handshake layer and does not include record layer headers.

quote [[
david
Ahsan: yes, any "full" packets (so with the headers) during the handshake.

the headers contains information, like the TLS version or the length of the packet, that needs to be authenticated as well.]]