Overview
The pre-master secret (PMS
) must be put through a PRF
(pseudo-random function) defined by the TLS spec in order to compute the symmetric TLS keys and also to compute the verify_data
for the Client_Finished
and the Server_Finished
messages.
Below we describe how the parties (N
stands for Notary
and U
stands for User
) who have their shares of PMS
can use 2PC to compute the PRF
.
Since the TLSNotary protocol already uses Garbled Circuits and Oblivious Transfer which give 128-bit computational security for the parties against each other, we argue that it is acceptable to perform some PRF computations outside of 2PC as long as it is done with at least 128-bit security. Performing some PRF computations outside of 2PC allows to save on computation and bandwidth.
Note that the User's TLS connection retains the standard TLS security guarantees against any third-party adversary.
To elaborate, recall how HMAC is computed (assuming |k| <= block size):
HMAC(k, m) = H((k ⊕ opad) | H((k ⊕ ipad) | m))
Notice that both H(k ⊕ opad) and H(k ⊕ ipad) can be computed separately prior to finalization. In this document we name these units as such:
outer hash state
: H(k ⊕ opad)inner hash state
: H(k ⊕ ipad)inner hash
: H((k ⊕ ipad) | m)
In TLS, the master secret is computed like so:
seed = "master secret" | client_random | server_random
a0 = seed
a1 = HMAC(pms, a0)
a2 = HMAC(pms, a1)
p1 = HMAC(pms, a1 | seed)
p2 = HMAC(pms, a2 | seed)
ms = (p1 | p2)[:48]
Notice that in each step the key, in this case PMS
, is constant. Thus both the outer
and inner hash state
can be reused for each step.
Below is the description of the all the steps to compute the PRF
both inside and outside the 2PC circuit.
---- Computing the master secret
-- Inside the circuit
- To evaluate the circuit, the parties input their
PMS
shares. The circuit outputs:
- H(PMS ⊕ opad) called
PMS
outer hash state
toN
and - H(PMS ⊕ ipad) called
PMS
inner hash state
toU
-- Outside the circuit
-
U
computes H((PMS ⊕ ipad) || a0) calledinner hash
ofa1
and passes it toN
. -
N
computesa1
and passes it toU
. -
U
computes theinner hash
ofa2
and passes it toN
. -
N
computesa2
and passes it toU
. -
U
computes theinner hash
of p2 and passes it toN
. -
N
computesp2
and passes it toU
. >Note that now both parties knowp2
which is the last 16 bytes of the master secret. They still don't know the other 32 bytes of the master secret, which ensures adequate security. -
U
computes theinner hash
ofp1
.
-- Inside the circuit
- To evaluate the circuit,
N
inputs thePMS outer hash state
andU
inputsp2
and theinner hash
ofp1
. The circuit computes the master secret (MS
).
---- Computing the expanded keys
The parties proceed to compute the expanded keys
. The corresponding python code is:
seed = str.encode("key expansion") + server_random + client_random
a0 = seed
a1 = hmac.new(ms , a0, hashlib.sha256).digest()
a2 = hmac.new(ms , a1, hashlib.sha256).digest()
p1 = hmac.new(ms, a1+seed, hashlib.sha256).digest()
p2 = hmac.new(ms, a2+seed, hashlib.sha256).digest()
ek = (p1 + p2)[:40]
client_write_key = ek[:16]
server_write_key = ek[16:32]
client_write_IV = ek[32:36]
server_write_IV = ek[36:40]
-- Inside the circuit
- Having computed
MS
, the circuit outputs:
- H(MS ⊕ opad) called the
MS outer hash state
toN
and - H(MS ⊕ ipad) called the
MS inner hash state
toU
-- Outside the circuit
-
U
computes theinner hash
ofa1
and sends it toN
. -
N
computesa1
and sends it toU
. -
U
computes theinner hash
ofa2
and sends it toN
. -
N
computesa2
and sends it toU
. -
U
computes theinner hash state
ofp1
and theinner hash state
ofp2
.
-- Inside the circuit
- To evaluate the circuit,
N
inputsMS outer hash state
(from Step 10) andU
inputsinner hash state
ofp1
andinner hash state
ofp2
. The circuit computesp1
andp2
. The circuit outputs xor shares of theexpanded keys
to each party.
---- Computing the encrypted Client_Finished
-- Inside the circuit
- To evaluate the circuit, the parties input their shares of the
expanded keys
. The circuit outputs data needed to encrypt and authenticate theClient_Finished
(CF
) message.
-- Outside the circuit
The parties proceed to compute verify_data
for the CF
message. The corresponding python code is:
# (handshake_hash) is a sha256 hash of all TLS handshake message up to this point
seed = str.encode('client finished') + handshake_hash
a0 = seed
a1 = hmac.new(ms, a0, hashlib.sha256).digest()
p1 = hmac.new(ms, a1+seed, hashlib.sha256).digest()
verify_data = p1[:12]
-
U
computesinner hash
ofa1
and sends it toN
. -
N
(who hasMS
outer hash state
from Step 10) computesa1
and sends it toU
. -
U
computesinner hash
ofp1
and sends it toN
. -
N
computesp1
and getsverify_data
and sends it toU
.
Note that it is safe for
N
to knowverify_data
forCF
.
Using the data from Step 17, U
proceeds to encrypt and authenticate CF
and sends it to the webserver.
---- Verifying the Server_Finished
Upon U
's receiving the encrypted Server_Finished
(SF
) from the webserver, the parties proceed to compute verify_data
for SF
, to enable U
to check that the received SF
is correct. The corresponding python code is:
# (handshake_hash) is a sha256 hash of all TLS handshake message up to this point
seed = str.encode('server finished') + handshake_hash
a0 = seed
a1 = hmac.new(ms, a0, hashlib.sha256).digest()
p1 = hmac.new(ms, a1+seed, hashlib.sha256).digest()
verify_data = p1[:12]
-- Outside the circuit
-
U
computesinner hash
ofa1
and sends it toN
. -
N
(who hasMS
outer hash state
from Step 10) computesa1
and sends it toU
. -
U
computesinner hash
ofp1
.
-- Inside the circuit
- To evaluate the circuit,
N
inputsMS
outer hash state
(from Step 10) andU
inputsinner hash
ofp1
. The circuit outputsverify_data
forSF
toU
.
The parties proceed to decrypt and authenticate the SF
in 2PC. U
checks that verify_data
from SF
matches verify_data
from Step 25.