mirror of
https://github.com/tlsnotary/circuits.git
synced 2026-01-08 22:37:59 -05:00
166 lines
6.3 KiB
Plaintext
166 lines
6.3 KiB
Plaintext
This repo contains macros to be assembled into circuits for use in TLSNotary's 2PC.
|
|
The main entrypoints are 6 circuits c1.casm ... c7.casm.
|
|
Each macro contains a description about what it does.
|
|
|
|
To assemble the macros into Bristol Fashion circuits
|
|
(https://homes.esat.kuleuven.be/~nsmart/MPC/), run:
|
|
|
|
node assemble.js
|
|
|
|
|
|
SOURCES:
|
|
|
|
casmbundle.js was created from:
|
|
git clone https://github.com/wyatt-howe/macro-circuit-assembler
|
|
cd macro-circuit-assembler/
|
|
git pull origin pull/3/head
|
|
# (this PR hasn't been merged at the time of writing)
|
|
# then comment out line 4 in casm.js --> //const fs = require('fs');
|
|
browserify --ignore fs --standalone CASM casm.js > casmbundle.js
|
|
|
|
adder64.txt --> https://homes.esat.kuleuven.be/~nsmart/MPC/adder64.txt
|
|
aes-128-reverse.txt --> https://github.com/multiparty/jigg/blob/master/circuits/bristol/aes-128-reverse.txt
|
|
sha256.txt --> https://homes.esat.kuleuven.be/~nsmart/MPC/sha256.txt
|
|
|
|
|
|
## License
|
|
All circuits in this repository are licensed under either of
|
|
|
|
- [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)
|
|
- [MIT license](http://opensource.org/licenses/MIT)
|
|
|
|
at your option.
|
|
|
|
|
|
The circuits c1, c2, c3, c4, c5 compute TLS's PRF starting from the pre-master
|
|
secret and computing "master secret", "expanded keys", "verify_data" for
|
|
Client_Finished, "verify_data" for Server_Finished.
|
|
The reason why there are separate circuits instead of one monolithic circuit is
|
|
that some intermediate computations can be done outside of the circuit,
|
|
significantly reducing the size of the circuit.
|
|
Here is the description of the all the steps to compute TLS PRF both inside and
|
|
outside the circuit:
|
|
|
|
1. Parties, having their shares of PMS (the pre-master secret) will compute MS
|
|
(master secret). The corresponding python code is:
|
|
|
|
seed = str.encode("master secret") + client_random + server_random
|
|
a0 = seed
|
|
a1 = hmac.new(pms, a0, hashlib.sha256).digest()
|
|
a2 = hmac.new(pms, a1, hashlib.sha256).digest()
|
|
p1 = hmac.new(pms, a1+seed, hashlib.sha256).digest()
|
|
p2 = hmac.new(pms, a2+seed, hashlib.sha256).digest()
|
|
ms = (p1+p2)[0:48]
|
|
|
|
We will be using the notation from https://en.wikipedia.org/wiki/HMAC to explain
|
|
the internals of the HMAC function. HMAC computes an inner hash and an outer hash.
|
|
|
|
2. Parties provide their inputs for circuit c1. N provides his PMS share and C
|
|
provides his PMS share. The circuit outputs H(pms xor opad) called "pms outer
|
|
hash state" to N and also outputs H(pms xor ipad) called "pms inner hash state"
|
|
to C.
|
|
|
|
3. The following steps happen outside of the circuit: C computes
|
|
H((pms xor ipad) || a0) called "inner hash" of a1 and passes it to N.
|
|
|
|
4. N computes a1 and passes it to C.
|
|
|
|
5. C computes inner hash of a2 and passes it to N.
|
|
|
|
6. N computes a2 and passes it to C.
|
|
|
|
7. C computes inner hash of p2 and passes it to N.
|
|
|
|
8. N computes p2 and passes it to C.
|
|
Note that p2 is obtained in the clear, thus both parties know the last 16 bytes
|
|
of MS. It is possible to put this step inside the circuit if needed. But we
|
|
think that revealing 16 bytes of MS while keeping the other 32 bytes secret
|
|
provides adequate security.
|
|
|
|
9. C computes inner hash of p1.
|
|
|
|
10. Parties provide their inputs for circuit c2. N provides "pms outer hash
|
|
state" and C provides p2 and inner hash of p1. The circuit computes the master
|
|
secret (MS).
|
|
|
|
11. The parties proceed to compute the expanded keys (EK). 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]
|
|
|
|
|
|
12. While still inside the circuit, having computed MS, the circuit now outputs
|
|
H(ms xor opad) called "ms outer hash state" to N and also outputs H(ms xor ipad)
|
|
called "ms inner hash state" to C.
|
|
|
|
13. The following steps happen outside of the circuit: C computes inner hash of
|
|
a1 and sends it to N.
|
|
|
|
14. N computes a1 and sends it to C.
|
|
|
|
15. C computes inner hash of a2 and sends it to N.
|
|
|
|
16. N computes a2 and sends it to C.
|
|
|
|
17. C computes inner state of p1 and inner state of p2.
|
|
|
|
18. The parties provide their inputs to circuit c3. N provides "ms outer hash
|
|
state" (from Step 10) and C provides inner state of p1 and inner state of p2.
|
|
Inside the circuit, p1 and p2 are computed. The circuit outputs xor shares of
|
|
keys and IVs to each party.
|
|
The parties provide their shares of keys and IVs to circuit c4. The circuit
|
|
outputs data needed to encrypt and authenticate the Client_Finished.
|
|
|
|
19. The parties proceed to compute verify_data for the Client_Finished message
|
|
outside of the circuit. 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]
|
|
|
|
20. C computes inner hash of a1 and sends it to N.
|
|
|
|
21. N (who has "ms outer hash state" from Step 10) computes a1 and sends it to C.
|
|
|
|
22. C computes inner hash of p1 and sends it to N.
|
|
|
|
23. N computes p1 and gets verify_data and sends it to C.
|
|
Note that N's knowing the verify_data for CF does not pose a threat of N
|
|
impersonating the webserver. In the next step C will check the Server_Finished
|
|
for correctness. Since N does not know server_write_key and server_write_iv,
|
|
N cannot forge the Server_Finished.
|
|
|
|
24. Upon C's receiving the Server_Finished (SF), the parties proceed to compute
|
|
verify_data for SF 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]
|
|
|
|
25. Outside of the circuit, C computes inner hash of a1 and sends it to N.
|
|
|
|
26. N (who has "ms outer hash state" from Step 10) computes a1 and sends it to C.
|
|
|
|
27. C computes inner hash of p1.
|
|
|
|
28. The parties provide their inputs to circuit c5. N provides "ms outer hash
|
|
state" (from Step 10) and C provides inner hash of p1. The circuit outputs
|
|
verify_data for Server_Finished to C.
|