
.. _framework-tls-decryption:

==============
TLS Decryption
==============

.. rst-class:: opening


   Zeek has limited support for decrypting TLS connections, if the necessary key material is
   available. If decryption is possible, Zeek can forward the decrypted data to other analyzers - like
   the HTTP analyzer.

   Note that this support is currently limited to a single version of TLS and a single cipher suite.
   Zeek can currently only decrypt TLS 1.2 connections that use the TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
   cipher. Any other TLS version or cipher will not be decrypted. We do not currently plan to extend
   this support to other versions of TLS, or to other ciphersuites.

Capturing and decrypting a trace file
=====================================

The most common use-case for TLS decryption is to capture a trace file together with the necessary key material
to allow Zeek to decrypt it. In principle, it is possible to allow Zeek to decrypt TLS connections in live traffic.
However, this kind of setup is much more complex and will require the user to set up a way to transport the
key material to Zeek in real-time. We will talk a bit about the possibility of this below.

Capturing a trace file with keys
--------------------------------

To be able to decrypt the TLS connections contained in a trace file, we need access to the symmetric keys that
were used to encrypt the connection. Specifically, for our supported version of TLS, we need the pre-master secret
for the TLS connection.

Firefox and Chrome allow users to capture the key material of all TLS connections that they perform by setting
the ``SSLKEYLOGFILE`` environment variable. For example, on Mac OS, you can instruct Firefox to record the TLS
key material by starting it in the following way:

.. code-block:: console

   export SSLKEYLOGFILE=$HOME/keylogfile.txt
   open -a firefox

After running Firefox like this, and accessing some web pages, you should end up with the file ``keylogfile.txt``
in your home directory that contains lines like this::

   # SSL/TLS secrets log file, generated by NSS
   CLIENT_RANDOM 47d1becb619e0851ee363c2cf37187228227ca4e680f9a7c0bd15069aa7a5970 ad03ceda4890fa581e989f5e3862023e2a4e3e8ad81325238d908066e1d35cc875979e34c08e6fdfd9d8c6f356e385c1
   CLIENT_RANDOM 2095006fcb3f93d255cbb6562587f0dd010212fdee9d233aff64e6ed36cd5c45 0d36faaa2eadbda2a8095f951de1cbac46b81b008fbf391d91951b3485476bab73288a1e17cd0ce80e0fc0401dbe9e3f
   CLIENT_RANDOM 8f58b32bf97e7d3856e2fccbbe80798ec2e3f515251082ad63bbc7c231d8bee0 9a7cf946a04718a19f4d20c3f80c1cf8c823c3e2b1c337ef64322d751b410543315f6ecf7dbf45ec9be194a3cc7c1a0f

These log lines contain the pre-master secrets for the connections that your browser established. The secrets
are indexed with the client random of the connections. This allows applications (like Zeek) to identify which
secret to use to decrypt a connection.

If you capture this key log file together with a trace-file, you will be able to decrypt the sessions using Zeek
(assuming they use a supported TLS version and ciphersuite).

Decrypting a trace file
-----------------------

The next step is to convert the keylogfile into a format that can be ingested by the Zeek. This bash-script
will perform the conversion:

.. code-block:: bash

   #!/usr/bin/env bash

   if [ $# -ne 1 ]; then
   	echo "Script expects one argument (key log filename)" >/dev/stderr
   	exit -1
   fi

   FILE=$1

   if [ ! -f ${FILE} ]; then
   	echo "${FILE} does not exist or is not readable" >/dev/stderr
   	exit -1
   fi

   echo "#fields	client_random	secret"
   grep CLIENT_RANDOM ${FILE} | sed 's/^CLIENT_RANDOM ........\(.*\) \(.*\)$/\1	\2/' | sed 's/[A-Za-z0-9][A-Za-z0-9]/\\x&/g'

Note that the script just converts the keylog file in a standard Zeek tsv-file. Furthermore, it removes
the first 16 characters of the CLIENT_RANDOM; this is needed due to a design-choice of Zeek that makes accessing
the first 8 bytes (equivalent to 16 hex-characters) of the client random inconvenient - thus these bytes are not
used for matching.

If you run the bash script on the ``keylogfile.txt`` you created earlier, you will get a Zeek tsv-file.

.. code-block:: console

   ./convert-keylog.sh ~/keylogfile.txt > ~/keylogfile.log

   cat ~/keylogfile.log
   #fields	client_random	secret
   \x0e\x78\x2d\x35\x63\x95\x5d\x8a\x30\xa9\xcf\xb6\x4f\x47\xf3\x96\x34\x8a\x1e\x79\x1a\xa2\x32\x55\xe2\x2f\xc5\x7a	\x34\x4f\x12\x65\xbf\x43\x40\xb3\x61\x6b\xa0\x16\x5d\x2b\x4d\xb9\xb1\xe8\x4a\x3d\xa2\x42\x0e\x38\xab\x01\x50\x62\x84\xcc\x34\xcd\xe0\x34\x10\xfe\x1a\x02\x30\x49\x74\x6c\x46\x43\xa7\x0c\x67\x0d
   \x24\x8c\x7e\x24\xee\xfb\x13\xcd\xee\xde\xb1\xf4\xb6\xd6\xd5\xee\x67\x8d\xd3\xff\xc7\xe7\x39\x23\x18\x3f\x99\xb4	\xe7\xed\x24\x26\x0d\x25\xd9\xfd\xf5\x0f\xc0\xf4\x56\x51\x0e\x4e\xec\x7f\x58\x9c\xaf\x39\x25\x14\x16\xa6\x71\xdd\xea\xfe\xe9\xc0\x93\xbe\x89\x4c\xab\xcc\xff\xb2\xf0\x9a\xea\x98\xf5\xb2\x53\x1e
   \x57\xd7\xc7\x7a\x2d\x5e\x35\x29\x2c\xd7\xe7\x94\xee\xf8\x6f\x31\x45\xf6\xbe\x25\x08\xed\x1d\x92\xd2\x0b\x9b\x04	\xc1\x93\x17\x93\xd9\x7d\xd2\x98\xb3\xe0\xdb\x2c\x5d\xbe\x71\x31\xa7\x9a\xf5\x91\xf9\x87\x90\xee\xb7\x79\x9f\x6b\xb4\x1f\x47\xa7\x69\x62\x4b\xa3\x99\x0c\xa9\x43\xf9\xea\x3b\x4d\x5f\x2f\xfe\xfb

Now we can run Zeek on the trace-file that we recorded. We need a small additional script for this, which
stops processing while the TLS keylog file is loaded. It also loads the required policy script.

.. literalinclude:: tls_decryption-1-suspend-processing.zeek
   :caption:
   :language: zeek
   :linenos:
   :tab-width: 4

.. code-block:: console

   $ export ZEEK_TLS_KEYLOG_FILE=~/keylogfile.log
   $ zeek -C -r tls/tls-1.2-stream-keylog.pcap tls_decryption-1-suspend-processing.zeek

   $ cat conn.log
   #separator \x09
   #set_separator	,
   #empty_field	(empty)
   #unset_field	-
   #path	conn
   #open	2022-03-01-16-57-26
   #fields	ts	uid	id.orig_h	id.orig_p	id.resp_h	id.resp_p	proto	service	duration	orig_bytes	resp_bytes	conn_state	local_orig	local_resp	missed_bytes	history	orig_pkts	orig_ip_bytes	resp_pkts	resp_ip_bytes	tunnel_parents
   #types	time	string	addr	port	addr	port	enum	string	interval	count	count	string	bool	bool	count	string	count	count	count	count	set[string]
   1646150638.631834	CTy5Us4OUaTOcyrPvc	192.168.20.12	60679	193.99.144.85	443	tcp	http,ssl	7.246461	10853	151695	SF	-	-	0	ShADadFf	98	15961	139	158931	-
   #close	2022-03-01-16-57-26

   $ cat http.log
   #separator \x09
   #set_separator	,
   #empty_field	(empty)
   #unset_field	-
   #path	http
   #open	2022-03-01-16-57-25
   #fields	ts	uid	id.orig_h	id.orig_p	id.resp_h	id.resp_p	trans_depth	method	host	uri	referrer	version	user_agent	origin	request_body_len	response_body_len	status_code	status_msg	info_code	info_msg	tags	username	password	proxied	orig_fuids	orig_filenames	orig_mime_types	resp_fuids	resp_filenames	resp_mime_types
   #types	time	string	addr	port	addr	port	count	string	string	string	string	string	string	string	count	count	count	string	count	string	set[enum]	string	string	set[string]	vector[string]	vector[string]	vector[string]	vector[string]	vector[string]	vector[string]
   1646150638.735969	CTy5Us4OUaTOcyrPvc	192.168.20.12	60679	193.99.144.85	443	1	GET	www.heise.de	/assets/akwa/v24/js/akwa.js?.ltc.c61e84978682308f631c	https://www.heise.de/	1.1	Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:97.0) Gecko/20100101 Firefox/97.0	-	0	375340	200	OK	-	-	(empty)	-	-	-	-	-	-	FSJiWr34wfIujxxtm3	-	text/plain
   1646150638.944774	CTy5Us4OUaTOcyrPvc	192.168.20.12	60679	193.99.144.85	443	2	GET	www.heise.de	/assets/heise/images/mit_technology_review_singleline.b768.ltc.svg	https://www.heise.de/	1.1	Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:97.0) Gecko/20100101 Firefox/97.0	-	0	3430	200	OK	-	-	(empty)	-	-	-	-	-	-	FgivhC1pvnYeQS4u18	-	text/plain
   1646150638.976118	CTy5Us4OUaTOcyrPvc	192.168.20.12	60679	193.99.144.85	443	3	GET	www.heise.de	/assets/heise/hobell/css/hobell.css?.ltc.3746e7e49abafa23b5fb	https://www.heise.de/	1.1	Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:97.0) Gecko/20100101 Firefox/97.0	-	0	85280	200	OK	-	-	(empty)	-	-	-	-	-	-	FyvBkl2nwRXf0hkDO1	-	text/plain
   ...

Now :file:`conn.log` shows that the HTTP as well as the SSL analyzers were attached. :file:`http.log` shows the
information from the decrypted HTTP session.

If you try this yourself note that today a lot of encrypted Internet traffic uses HTTP/2. Zeek currently does not
ship with an HTTP/2 parser by default. If you capture your own traffic make sure that your browser uses
HTTP/1. Alternatively, you can add an HTTP/2 analyzer to Zeek, e.g. using a package.

Decrypting live traffic
=======================

In principle, it is possible to decrypt live traffic using this approach. When you want to do this, you have to supply the secrets
to Zeek as the connections are happening. Note that there are timing constraints here - the secrets should arrive at the Zeek instance
that will decrypt the traffic before encrypted application data is exchanged.

The :doc:`/scripts/policy/protocols/ssl/decryption.zeek` policy script sets up a two events for this purpose. You can send key material
to the Zeek worker in question via Broker, using the ``/zeek/tls/decryption`` topic. The two events used for this are
:zeek:see:`SSL::add_keys` and :zeek:see:`SSL::add_secret`.

TLS Decryption API
==================

If the policy script does not suit your use-case, you can use the TLS decryption API directly to decrypt a connection. You can use either the
:zeek:see:`set_secret` or the :zeek:see:`set_keys` functions to provide the decryption keys for an ongoing SSL connection.

Note that you will have to make sure to set :zeek:see:`SSL::disable_analyzer_after_detection` to false if you use this functionality directly.
