In this demonstration a client connects to a server, negotiates a TLS 1.2 session, sends "ping", receives "pong", and then terminates the session. Click below to begin exploring.
if vers == 0 {
// Some TLS servers fail if the record version is
// greater than TLS 1.0 for the initial ClientHello.
vers = VersionTLS10
}
$ openssl x509 -outform der < server.crt | hexdump
0000000 30 82 03 21 30 82 02 09 a0 03 02 01 02 02 08 15
0000010 5a 92 ad c2 04 8f 90 30 0d 06 09 2a 86 48 86 f7
... snip ...
The server calculates a private/public keypair for key exchange. Key exchange is a technique where two parties can agree on the same number without an eavesdropper being able to tell what it is.
An explanation of the key exchange can be found on my X25519 site, but doesn't need to be understood in depth for the rest of this page.
The private key is chosen by selecting an integer between 0 and 2256-1. The server does this by generating 32 bytes (256 bits) of random data. The private key selected is:
909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafThe public key is created from the private key as explained on the X25519 site. The public key calculated is:
9fd7ad6dcff4298dd3f96d5b1b2af910a0535b1488d7f8fabb349a982880b615The public key calculation can be confirmed at the command line:
### requires openssl 1.1.0 or higher
$ openssl pkey -noout -text < server-ephemeral-private.key
X25519 Private-Key:
priv:
90:91:92:93:94:95:96:97:98:99:9a:9b:9c:9d:9e:
9f:a0:a1:a2:a3:a4:a5:a6:a7:a8:a9:aa:ab:ac:ad:
ae:af
pub:
9f:d7:ad:6d:cf:f4:29:8d:d3:f9:6d:5b:1b:2a:f9:
10:a0:53:5b:14:88:d7:f8:fa:bb:34:9a:98:28:80:
b6:15
### client random from Client Hello
$ echo -en '\x00\x01\x02\x03\x04\x05\x06\x07' > /tmp/compute
$ echo -en '\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f' >> /tmp/compute
$ echo -en '\x10\x11\x12\x13\x14\x15\x16\x17' >> /tmp/compute
$ echo -en '\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f' >> /tmp/compute
### server random from Server Hello
$ echo -en '\x70\x71\x72\x73\x74\x75\x76\x77' >> /tmp/compute
$ echo -en '\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f' >> /tmp/compute
$ echo -en '\x80\x81\x82\x83\x84\x85\x86\x87' >> /tmp/compute
$ echo -en '\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f' >> /tmp/compute
### the curve info section from this message
$ echo -en '\x03\x00\x1d' >> /tmp/compute
### the public key sections from this msg
$ echo -en '\x20\x9f\xd7\xad\x6d\xcf\xf4\x29' >> /tmp/compute
$ echo -en '\x8d\xd3\xf9\x6d\x5b\x1b\x2a\xf9' >> /tmp/compute
$ echo -en '\x10\xa0\x53\x5b\x14\x88\xd7\xf8' >> /tmp/compute
$ echo -en '\xfa\xbb\x34\x9a\x98\x28\x80\xb6\x15' >> /tmp/compute
$ openssl dgst -sign server.key -sha256 /tmp/compute | hexdump
0000000 04 02 b6 61 f7 c1 91 ee 59 be 45 37 66 39 bd c3
... snip ...
00000f0 7d 87 dc 33 18 64 35 71 22 6c 4d d2 c2 ac 41 fb
The client calculates a private/public keypair for key exchange. Key exchange is a technique where two parties can agree on the same number without an eavesdropper being able to tell what the number is.
An explanation of the key exchange can be found on my X25519 site, but doesn't need to be understood in depth for the rest of this page.
The private key is chosen by selecting an integer between 0 and 2256-1. The client does this by generating 32 bytes (256 bits) of random data. The private key selected is:
202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3fThe public key is created from the private key as explained on the X25519 site. The public key calculated is:
358072d6365880d1aeea329adf9121383851ed21a28e3b75e965d0d2cd166254The public key calculation can be confirmed at the command line:
### requires openssl 1.1.0 or higher
$ openssl pkey -noout -text < client-ephemeral-private.key
X25519 Private-Key:
priv:
20:21:22:23:24:25:26:27:28:29:2a:2b:2c:2d:2e:
2f:30:31:32:33:34:35:36:37:38:39:3a:3b:3c:3d:
3e:3f
pub:
35:80:72:d6:36:58:80:d1:ae:ea:32:9a:df:91:21:
38:38:51:ed:21:a2:8e:3b:75:e9:65:d0:d2:cd:16:
62:54
df4a291baa1eb7cfa6934b29b474baad2697e29f1f920dcc77c8a0a088447624I've provided a tool to perform this calculation:
$ gcc -o curve25519-mult curve25519-mult.c
$ ./curve25519-mult client-ephemeral-private.key \
server-ephemeral-public.key | hexdump
0000000 df 4a 29 1b aa 1e b7 cf a6 93 4b 29 b4 74 ba ad
0000010 26 97 e2 9f 1f 92 0d cc 77 c8 a0 a0 88 44 76 24
seed = "master secret" + client_random + server_random a0 = seed a1 = HMAC-SHA256(key=PreMasterSecret, data=a0) a2 = HMAC-SHA256(key=PreMasterSecret, data=a1) p1 = HMAC-SHA256(key=PreMasterSecret, data=a1 + seed) p2 = HMAC-SHA256(key=PreMasterSecret, data=a2 + seed) MasterSecret = p1[all 32 bytes] + p2[first 16 bytes]Here we demonstrate on the command line:
### set up our PreMasterSecret as a hex string
$ pmshex=df4a291baa1eb7cfa6934b29b474baad
$ pmshex=${pmshex}2697e29f1f920dcc77c8a0a088447624
### client random from Client Hello
$ echo -en '\x00\x01\x02\x03\x04\x05\x06\x07' > /tmp/c_rand
$ echo -en '\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f' >> /tmp/c_rand
$ echo -en '\x10\x11\x12\x13\x14\x15\x16\x17' >> /tmp/c_rand
$ echo -en '\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f' >> /tmp/c_rand
### server random from Server Hello
$ echo -en '\x70\x71\x72\x73\x74\x75\x76\x77' > /tmp/s_rand
$ echo -en '\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f' >> /tmp/s_rand
$ echo -en '\x80\x81\x82\x83\x84\x85\x86\x87' >> /tmp/s_rand
$ echo -en '\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f' >> /tmp/s_rand
### build the seed
$ echo -en 'master secret' > /tmp/seed
$ cat /tmp/c_rand /tmp/s_rand >> /tmp/seed
### a0 is the same as the seed
$ cat /tmp/seed > /tmp/a0
### a(n) is hmac-sha256(key=secret, data=a(n-1))
$ cat /tmp/a0 | openssl dgst -sha256 \
-mac HMAC -macopt hexkey:$pmshex -binary > /tmp/a1
$ cat /tmp/a1 | openssl dgst -sha256 \
-mac HMAC -macopt hexkey:$pmshex -binary > /tmp/a2
### p(n) is hmac-sha256(key=secret, data=a(n)+seed)
$ cat /tmp/a1 /tmp/seed | openssl dgst -sha256 \
-mac HMAC -macopt hexkey:$pmshex -binary > /tmp/p1
$ cat /tmp/a2 /tmp/seed | openssl dgst -sha256 \
-mac HMAC -macopt hexkey:$pmshex -binary > /tmp/p2
### first 48 bytes is MasterSecret
$ cat /tmp/p1 /tmp/p2 | head -c 48 > /tmp/mastersecret
$ hexdump /tmp/mastersecret
0000000 91 6a bf 9d a5 59 73 e1 36 14 ae 0a 3f 5d 3f 37
0000010 b0 23 ba 12 9a ee 02 cc 91 34 33 81 27 cd 70 49
0000020 78 1c 8e 19 fc 1e b2 a7 38 7a c0 6a e2 37 34 4c
916abf9da55973e13614ae0a3f5d3f37b023ba129aee02cc9134338127cd7049781c8e19fc1eb2a7387ac06ae237344cWe then generate the final encryption keys using a key expansion:
seed = "key expansion" + server_random + client_random a0 = seed a1 = HMAC-SHA256(key=MasterSecret, data=a0) a2 = HMAC-SHA256(key=MasterSecret, data=a1) a3 = HMAC-SHA256(key=MasterSecret, data=a2) a4 = ... p1 = HMAC-SHA256(key=MasterSecret, data=a1 + seed) p2 = HMAC-SHA256(key=MasterSecret, data=a2 + seed) p3 = HMAC-SHA256(key=MasterSecret, data=a3 + seed) p4 = ... p = p1 + p2 + p3 + p4 ... client write mac key = [first 20 bytes of p] server write mac key = [next 20 bytes of p] client write key = [next 16 bytes of p] server write key = [next 16 bytes of p] client write IV = [next 16 bytes of p] server write IV = [next 16 bytes of p]We can demonstrate this on the command line:
### continued from above command line example
### set up our MasterSecret as a hex string
$ mshex=$(hexdump -ve '/1 "%02x"' /tmp/mastersecret)
### build the seed
$ echo -en 'key expansion' > /tmp/seed
$ cat /tmp/s_rand /tmp/c_rand >> /tmp/seed
### a0 is the same as the seed
$ cat /tmp/seed > /tmp/a0
### a(n) is hmac-sha256(key=secret, data=a(n-1))
$ cat /tmp/a0 | openssl dgst -sha256 \
-mac HMAC -macopt hexkey:$mshex -binary > /tmp/a1
$ cat /tmp/a1 | openssl dgst -sha256 \
-mac HMAC -macopt hexkey:$mshex -binary > /tmp/a2
$ cat /tmp/a2 | openssl dgst -sha256 \
-mac HMAC -macopt hexkey:$mshex -binary > /tmp/a3
$ cat /tmp/a3 | openssl dgst -sha256 \
-mac HMAC -macopt hexkey:$mshex -binary > /tmp/a4
### p(n) is hmac-sha256(key=secret, data=a(n)+seed)
$ cat /tmp/a1 /tmp/seed | openssl dgst -sha256 \
-mac HMAC -macopt hexkey:$mshex -binary > /tmp/p1
$ cat /tmp/a2 /tmp/seed | openssl dgst -sha256 \
-mac HMAC -macopt hexkey:$mshex -binary > /tmp/p2
$ cat /tmp/a3 /tmp/seed | openssl dgst -sha256 \
-mac HMAC -macopt hexkey:$mshex -binary > /tmp/p3
$ cat /tmp/a4 /tmp/seed | openssl dgst -sha256 \
-mac HMAC -macopt hexkey:$mshex -binary > /tmp/p4
### combine them into a single stream
$ cat /tmp/p1 /tmp/p2 /tmp/p3 /tmp/p4 > /tmp/p
$ dd if=/tmp/p of=/tmp/client_mac_key bs=1 skip=0 count=20
$ dd if=/tmp/p of=/tmp/server_mac_key bs=1 skip=20 count=20
$ dd if=/tmp/p of=/tmp/client_key bs=1 skip=40 count=16
$ dd if=/tmp/p of=/tmp/server_key bs=1 skip=56 count=16
$ dd if=/tmp/p of=/tmp/client_iv bs=1 skip=72 count=16
$ dd if=/tmp/p of=/tmp/server_iv bs=1 skip=88 count=16
$ hexdump /tmp/client_mac_key
0000000 1b 7d 11 7c 7d 5f 69 0b c2 63 ca e8 ef 60 af 0f
0000010 18 78 ac c2
$ hexdump /tmp/server_mac_key
0000000 2a d8 bd d8 c6 01 a6 17 12 6f 63 54 0e b2 09 06
0000010 f7 81 fa d2
$ hexdump /tmp/client_key
0000000 f6 56 d0 37 b1 73 ef 3e 11 16 9f 27 23 1a 84 b6
$ hexdump /tmp/server_key
0000000 75 2a 18 e7 a9 fc b7 cb cd d8 f9 8d d8 f7 69 eb
$ hexdump /tmp/client_iv
0000000 a0 d2 55 0c 92 38 ee bf ef 5c 32 25 1a bb 67 d6
$ hexdump /tmp/server_iv
0000000 43 45 28 db 49 37 d5 40 d3 93 13 5e 06 a1 1b b8
### client key
$ hexkey=f656d037b173ef3e11169f27231a84b6
### IV for this record
$ hexiv=404142434445464748494a4b4c4d4e4f
### encrypted data
$ echo '22 7b c9 ba 81 ef 30 f2 a8 a7 8f f1 df 50 84 4d' > /tmp/msg1
$ echo '58 04 b7 ee b2 e2 14 c3 2b 68 92 ac a3 db 7b 78' >> /tmp/msg1
$ echo '07 7f dd 90 06 7c 51 6b ac b3 ba 90 de df 72 0f' >> /tmp/msg1
$ xxd -r -p /tmp/msg1 \
| openssl enc -d -nopad -aes-128-cbc -K $hexkey -iv $hexiv | hexdump
0000000 14 00 00 0c cf 91 96 26 f1 36 0c 53 6a aa d7 3a
0000010 a5 a0 3d 23 30 56 e4 ac 6e ba 7f d9 e5 31 7f ac
0000020 2d b5 b7 0e 0b 0b 0b 0b 0b 0b 0b 0b 0b 0b 0b 0b
The last 32 bytes contain a 20-byte MAC and padding to bring the data to a
multiple of 16 bytes. The 20-byte MAC can be reproduced as follows:
### from https://tools.ietf.org/html/rfc2246#section-6.2.3.1
$ sequence='0000000000000000'
$ rechdr='16 03 03'
$ datalen='00 10'
$ data='14 00 00 0c cf 91 96 26 f1 36 0c 53 6a aa d7 3a'
### from "Encryption Keys Calculation"
$ mackey=1b7d117c7d5f690bc263cae8ef60af0f1878acc2
$ echo $sequence $rechdr $datalen $data | xxd -r -p \
| openssl dgst -sha1 -mac HMAC -macopt hexkey:$mackey
a5a03d233056e4ac6eba7fd9e5317fac2db5b70e
### combine the payload (without the 5-byte header) of each handshake message, in order
$ tail -c +6 clienthello > test.bin
$ tail -c +6 serverhello >> test.bin
$ tail -c +6 servercert >> test.bin
$ tail -c +6 serverkeyexchange >> test.bin
$ tail -c +6 serverhellodone >> test.bin
$ tail -c +6 clientkeyexchange >> test.bin
$ openssl sha256 test.bin
061dda04b3c2217ff73bd79b9cf88a2bb6ec505404aac8722db03ef417b54cb4
seed = "client finished" + SHA256(all handshake messages) a0 = seed a1 = HMAC-SHA256(key=MasterSecret, data=a0) p1 = HMAC-SHA256(key=MasterSecret, data=a1 + seed) verify_data = p1[first 12 bytes]The verify data calculated from this hash is cf919626f1360c536aaad73a. We can show this on the command line:
### set up our MasterSecret as a hex string
$ mshex=$(hexdump -ve '/1 "%02x"' /tmp/mastersecret)
### build the seed
$ echo -en 'client finished' > /tmp/seed
### add SHA256(all_messages) to seed
$ echo -en '\x06\x1d\xda\x04\xb3\xc2\x21\x7f' >> /tmp/seed
$ echo -en '\xf7\x3b\xd7\x9b\x9c\xf8\x8a\x2b' >> /tmp/seed
$ echo -en '\xb6\xec\x50\x54\x04\xaa\xc8\x72' >> /tmp/seed
$ echo -en '\x2d\xb0\x3e\xf4\x17\xb5\x4c\xb4' >> /tmp/seed
### a0 is the same as the seed
$ cat /tmp/seed > /tmp/a0
### a(n) is hmac-sha256(key=secret, data=a(n-1))
$ cat /tmp/a0 | openssl dgst -sha256 \
-mac HMAC -macopt hexkey:$mshex -binary > /tmp/a1
### p(n) is hmac-sha256(key=secret, data=a(n)+seed)
$ cat /tmp/a1 /tmp/seed | openssl dgst -sha256 \
-mac HMAC -macopt hexkey:$mshex -binary > /tmp/p1
$ head -c 12 /tmp/p1 > /tmp/verify_data
$ hexdump /tmp/verify_data
0000000 cf 91 96 26 f1 36 0c 53 6a aa d7 3a
df4a291baa1eb7cfa6934b29b474baad2697e29f1f920dcc77c8a0a088447624I've provided a tool to perform this calculation:
$ gcc -o curve25519-mult curve25519-mult.c
$ ./curve25519-mult server-ephemeral-private.key \
client-ephemeral-public.key | hexdump
0000000 df 4a 29 1b aa 1e b7 cf a6 93 4b 29 b4 74 ba ad
0000010 26 97 e2 9f 1f 92 0d cc 77 c8 a0 a0 88 44 76 24
seed = "master secret" + client_random + server_random a0 = seed a1 = HMAC-SHA256(key=PreMasterSecret, data=a0) a2 = HMAC-SHA256(key=PreMasterSecret, data=a1) p1 = HMAC-SHA256(key=PreMasterSecret, data=a1 + seed) p2 = HMAC-SHA256(key=PreMasterSecret, data=a2 + seed) MasterSecret = p1[all 32 bytes] + p2[first 16 bytes]Here we demonstrate on the command line:
### set up our PreMasterSecret as a hex string
$ pmshex=df4a291baa1eb7cfa6934b29b474baad
$ pmshex=${pmshex}2697e29f1f920dcc77c8a0a088447624
### client random from Client Hello
$ echo -en '\x00\x01\x02\x03\x04\x05\x06\x07' > /tmp/c_rand
$ echo -en '\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f' >> /tmp/c_rand
$ echo -en '\x10\x11\x12\x13\x14\x15\x16\x17' >> /tmp/c_rand
$ echo -en '\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f' >> /tmp/c_rand
### server random from Server Hello
$ echo -en '\x70\x71\x72\x73\x74\x75\x76\x77' > /tmp/s_rand
$ echo -en '\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f' >> /tmp/s_rand
$ echo -en '\x80\x81\x82\x83\x84\x85\x86\x87' >> /tmp/s_rand
$ echo -en '\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f' >> /tmp/s_rand
### build the seed
$ echo -en 'master secret' > /tmp/seed
$ cat /tmp/c_rand /tmp/s_rand >> /tmp/seed
### a0 is the same as the seed
$ cat /tmp/seed > /tmp/a0
### a(n) is hmac-sha256(key=secret, data=a(n-1))
$ cat /tmp/a0 | openssl dgst -sha256 \
-mac HMAC -macopt hexkey:$pmshex -binary > /tmp/a1
$ cat /tmp/a1 | openssl dgst -sha256 \
-mac HMAC -macopt hexkey:$pmshex -binary > /tmp/a2
### p(n) is hmac-sha256(key=secret, data=a(n)+seed)
$ cat /tmp/a1 /tmp/seed | openssl dgst -sha256 \
-mac HMAC -macopt hexkey:$pmshex -binary > /tmp/p1
$ cat /tmp/a2 /tmp/seed | openssl dgst -sha256 \
-mac HMAC -macopt hexkey:$pmshex -binary > /tmp/p2
### first 48 bytes is MasterSecret
$ cat /tmp/p1 /tmp/p2 | head -c 48 > /tmp/mastersecret
$ hexdump /tmp/mastersecret
0000000 91 6a bf 9d a5 59 73 e1 36 14 ae 0a 3f 5d 3f 37
0000010 b0 23 ba 12 9a ee 02 cc 91 34 33 81 27 cd 70 49
0000020 78 1c 8e 19 fc 1e b2 a7 38 7a c0 6a e2 37 34 4c
916abf9da55973e13614ae0a3f5d3f37b023ba129aee02cc9134338127cd7049781c8e19fc1eb2a7387ac06ae237344cWe then generate the final encryption keys using a key expansion:
seed = "key expansion" + server_random + client_random a0 = seed a1 = HMAC-SHA256(key=MasterSecret, data=a0) a2 = HMAC-SHA256(key=MasterSecret, data=a1) a3 = HMAC-SHA256(key=MasterSecret, data=a2) a4 = ... p1 = HMAC-SHA256(key=MasterSecret, data=a1 + seed) p2 = HMAC-SHA256(key=MasterSecret, data=a2 + seed) p3 = HMAC-SHA256(key=MasterSecret, data=a3 + seed) p4 = ... p = p1 + p2 + p3 + p4 ... client write mac key = [first 20 bytes of p] server write mac key = [next 20 bytes of p] client write key = [next 16 bytes of p] server write key = [next 16 bytes of p] client write IV = [next 16 bytes of p] server write IV = [next 16 bytes of p]We can demonstrate this on the command line:
### continued from above command line example
### set up our MasterSecret as a hex string
$ mshex=$(hexdump -ve '/1 "%02x"' /tmp/mastersecret)
### build the seed
$ echo -en 'key expansion' > /tmp/seed
$ cat /tmp/s_rand /tmp/c_rand >> /tmp/seed
### a0 is the same as the seed
$ cat /tmp/seed > /tmp/a0
### a(n) is hmac-sha256(key=secret, data=a(n-1))
$ cat /tmp/a0 | openssl dgst -sha256 \
-mac HMAC -macopt hexkey:$mshex -binary > /tmp/a1
$ cat /tmp/a1 | openssl dgst -sha256 \
-mac HMAC -macopt hexkey:$mshex -binary > /tmp/a2
$ cat /tmp/a2 | openssl dgst -sha256 \
-mac HMAC -macopt hexkey:$mshex -binary > /tmp/a3
$ cat /tmp/a3 | openssl dgst -sha256 \
-mac HMAC -macopt hexkey:$mshex -binary > /tmp/a4
### p(n) is hmac-sha256(key=secret, data=a(n)+seed)
$ cat /tmp/a1 /tmp/seed | openssl dgst -sha256 \
-mac HMAC -macopt hexkey:$mshex -binary > /tmp/p1
$ cat /tmp/a2 /tmp/seed | openssl dgst -sha256 \
-mac HMAC -macopt hexkey:$mshex -binary > /tmp/p2
$ cat /tmp/a3 /tmp/seed | openssl dgst -sha256 \
-mac HMAC -macopt hexkey:$mshex -binary > /tmp/p3
$ cat /tmp/a4 /tmp/seed | openssl dgst -sha256 \
-mac HMAC -macopt hexkey:$mshex -binary > /tmp/p4
$ cat /tmp/p1 /tmp/p2 /tmp/p3 /tmp/p4 > /tmp/p
$ dd if=/tmp/p of=/tmp/client_mac_key bs=1 skip=0 count=20
$ dd if=/tmp/p of=/tmp/server_mac_key bs=1 skip=20 count=20
$ dd if=/tmp/p of=/tmp/client_key bs=1 skip=40 count=16
$ dd if=/tmp/p of=/tmp/server_key bs=1 skip=56 count=16
$ dd if=/tmp/p of=/tmp/client_iv bs=1 skip=72 count=16
$ dd if=/tmp/p of=/tmp/server_iv bs=1 skip=88 count=16
$ hexdump /tmp/client_mac_key
0000000 1b 7d 11 7c 7d 5f 69 0b c2 63 ca e8 ef 60 af 0f
0000010 18 78 ac c2
$ hexdump /tmp/server_mac_key
0000000 2a d8 bd d8 c6 01 a6 17 12 6f 63 54 0e b2 09 06
0000010 f7 81 fa d2
$ hexdump /tmp/client_key
0000000 f6 56 d0 37 b1 73 ef 3e 11 16 9f 27 23 1a 84 b6
$ hexdump /tmp/server_key
0000000 75 2a 18 e7 a9 fc b7 cb cd d8 f9 8d d8 f7 69 eb
$ hexdump /tmp/client_iv
0000000 a0 d2 55 0c 92 38 ee bf ef 5c 32 25 1a bb 67 d6
$ hexdump /tmp/server_iv
0000000 43 45 28 db 49 37 d5 40 d3 93 13 5e 06 a1 1b b8
### server key
$ hexkey=752a18e7a9fcb7cbcdd8f98dd8f769eb
### IV for this record
$ hexiv=5152535455565758595a5b5c5d5e5f60
### encrypted data
$ echo '18 e0 75 31 7b 10 03 15 f6 08 1f cb f3 13 78 1a' > /tmp/msg1
$ echo 'ac 73 ef e1 9f e2 5b a1 af 59 c2 0b e9 4f c0 1b' >> /tmp/msg1
$ echo 'da 2d 68 00 29 8b 73 a7 e8 49 d7 4b d4 94 cf 7d' >> /tmp/msg1
$ xxd -r -p /tmp/msg1 \
| openssl enc -d -nopad -aes-128-cbc -K $hexkey -iv $hexiv | hexdump
0000000 14 00 00 0c 84 4d 3c 10 74 6d d7 22 f9 2f 0c 7e
0000010 20 c4 97 46 d2 a3 0f 23 57 39 90 58 07 53 52 43
0000020 af f2 bf e0 0b 0b 0b 0b 0b 0b 0b 0b 0b 0b 0b 0b
The last 32 bytes contain a 20-byte MAC and padding to bring the data to a
multiple of 16 bytes. The 20-byte MAC can be reproduced as follows:
### from https://tools.ietf.org/html/rfc2246#section-6.2.3.1
$ sequence='0000000000000000'
$ rechdr='16 03 03'
$ datalen='00 10'
$ data='14 00 00 0c 84 4d 3c 10 74 6d d7 22 f9 2f 0c 7e'
### from "Encryption Keys Calculation"
$ mackey=2ad8bdd8c601a617126f63540eb20906f781fad2
$ echo $sequence $rechdr $datalen $data | xxd -r -p \
| openssl dgst -sha1 -mac HMAC -macopt hexkey:$mackey
20c49746d2a30f235739905807535243aff2bfe0
### combine the payload (without the 5-byte header) of each
### plaintext handshake message, in order
$ tail -c +6 clienthello > test.bin
$ tail -c +6 serverhello >> test.bin
$ tail -c +6 servercert >> test.bin
$ tail -c +6 serverkeyexchange >> test.bin
$ tail -c +6 serverhellodone >> test.bin
$ tail -c +6 clientkeyexchange >> test.bin
### add the decrypted payload of the client finished message
$ cat clientfinishedplain >> test.bin
$ openssl sha256 test.bin
b2017ba28d0e27f03ae327456b6ff00b4d5bbf0ef7cda83ce1029b521c3e7c35
seed = "server finished" + SHA256(all handshake messages) a0 = seed a1 = HMAC-SHA256(key=MasterSecret, data=a0) p1 = HMAC-SHA256(key=MasterSecret, data=a1 + seed) verify_data = p1[first 12 bytes]The verify data calculated from this hash is 844d3c10746dd722f92f0c7e. We can show this on the command line:
### set up our MasterSecret as a hex string
$ mshex=$(hexdump -ve '/1 "%02x"' /tmp/mastersecret)
### build the seed
$ echo -en 'server finished' > /tmp/seed
### add SHA256(all_messages) to seed
$ echo -en '\xb2\x01\x7b\xa2\x8d\x0e\x27\xf0' >> /tmp/seed
$ echo -en '\x3a\xe3\x27\x45\x6b\x6f\xf0\x0b' >> /tmp/seed
$ echo -en '\x4d\x5b\xbf\x0e\xf7\xcd\xa8\x3c' >> /tmp/seed
$ echo -en '\xe1\x02\x9b\x52\x1c\x3e\x7c\x35' >> /tmp/seed
### a0 is the same as the seed
$ cat /tmp/seed > /tmp/a0
### a(n) is hmac-sha256(key=secret, data=a(n-1))
$ cat /tmp/a0 | openssl dgst -sha256 \
-mac HMAC -macopt hexkey:$mshex -binary > /tmp/a1
### p(n) is hmac-sha256(key=secret, data=a(n)+seed)
$ cat /tmp/a1 /tmp/seed | openssl dgst -sha256 \
-mac HMAC -macopt hexkey:$mshex -binary > /tmp/p1
$ head -c 12 /tmp/p1 > /tmp/verify_data
$ hexdump /tmp/verify_data
0000000 84 4d 3c 10 74 6d d7 22 f9 2f 0c 7e
### client key
$ hexkey=f656d037b173ef3e11169f27231a84b6
### IV for this record
$ hexiv=000102030405060708090a0b0c0d0e0f
### encrypted data
$ echo '6c 42 1c 71 c4 2b 18 3b fa 06 19 5d 13 3d 0a 09' > /tmp/msg1
$ echo 'd0 0f c7 cb 4e 0f 5d 1c da 59 d1 47 ec 79 0c 99' >> /tmp/msg1
$ xxd -r -p /tmp/msg1 \
| openssl enc -d -nopad -aes-128-cbc -K $hexkey -iv $hexiv | hexdump
0000000 70 69 6e 67 60 10 12 49 f7 4a 03 77 c9 ca cf 63
0000010 09 75 13 70 d8 0c fc aa 07 07 07 07 07 07 07 07
The last 28 bytes contain a 20-byte MAC and padding to bring the data to a
multiple of 16 bytes. The 20-byte MAC can be reproduced as follows:
### from https://tools.ietf.org/html/rfc2246#section-6.2.3.1
$ sequence='0000000000000001'
$ rechdr='17 03 03'
$ datalen='00 04'
$ data='70 69 6e 67'
### from "Encryption Keys Calculation"
$ mackey=1b7d117c7d5f690bc263cae8ef60af0f1878acc2
$ echo $sequence $rechdr $datalen $data | xxd -r -p \
| openssl dgst -sha1 -mac HMAC -macopt hexkey:$mackey
60101249f74a0377c9cacf6309751370d80cfcaa
### server key
$ hexkey=752a18e7a9fcb7cbcdd8f98dd8f769eb
### IV for this record
$ hexiv=6162636465666768696a6b6c6d6e6f70
### encrypted data
$ echo '97 83 48 8a f5 fa 20 bf 7a 2e f6 9d eb b5 34 db' > /tmp/msg1
$ echo '9f b0 7a 8c 27 21 de e5 40 9f 77 af 0c 3d de 56' >> /tmp/msg1
$ xxd -r -p /tmp/msg1 \
| openssl enc -d -nopad -aes-128-cbc -K $hexkey -iv $hexiv | hexdump
0000000 70 6f 6e 67 5a c7 99 dc cf dc 0f af 95 2b dc 91
0000010 18 af 20 0e e3 1c 51 05 07 07 07 07 07 07 07 07
The last 28 bytes contain a 20-byte MAC and padding to bring the data to a
multiple of 16 bytes. The 20-byte MAC can be reproduced as follows:
### from https://tools.ietf.org/html/rfc2246#section-6.2.3.1
$ sequence='0000000000000001'
$ rechdr='17 03 03'
$ datalen='00 04'
$ data='70 6f 6e 67'
### from "Encryption Keys Calculation"
$ mackey=2ad8bdd8c601a617126f63540eb20906f781fad2
$ echo $sequence $rechdr $datalen $data | xxd -r -p \
| openssl dgst -sha1 -mac HMAC -macopt hexkey:$mackey
5ac799dccfdc0faf952bdc9118af200ee31c5105
### client key
$ hexkey=f656d037b173ef3e11169f27231a84b6
### IV for this record
$ hexiv=101112131415161718191a1b1c1d1e1f
### encrypted data
$ echo '0d 83 f9 79 04 75 0d d8 fd 8a a1 30 21 86 32 63' > /tmp/msg1
$ echo '4f d0 65 e4 62 83 79 b8 8b bf 9e fd 12 87 a6 2d' >> /tmp/msg1
$ xxd -r -p /tmp/msg1 \
| openssl enc -d -nopad -aes-128-cbc -K $hexkey -iv $hexiv | hexdump
0000000 01 00 92 79 9c ba 81 9f 31 07 44 c5 59 62 2b e4
0000010 2b ce 3d 6a 41 fb 09 09 09 09 09 09 09 09 09 09
The last 30 bytes contain a 20-byte MAC and padding to bring the data to a
multiple of 16 bytes. The 20-byte MAC can be reproduced as follows:
### from https://tools.ietf.org/html/rfc2246#section-6.2.3.1
$ sequence='0000000000000002'
$ rechdr='15 03 03'
$ datalen='00 02'
$ data='01 00'
### from "Encryption Keys Calculation"
$ mackey=1b7d117c7d5f690bc263cae8ef60af0f1878acc2
$ echo $sequence $rechdr $datalen $data | xxd -r -p \
| openssl dgst -sha1 -mac HMAC -macopt hexkey:$mackey
92799cba819f310744c559622be42bce3d6a41fb
The code for this project, including packet captures, can be found on GitHub.
You may also be interested in the TLS 1.3 version of this document.
If you found this page useful or interesting let me know via Twitter @XargsNotBombs.