refactor(tlsn): mpz upgrade (#512)

* refactor(tlsn): mpz upgrade

* PR feedback
This commit is contained in:
sinu.eth
2024-06-25 10:46:57 -07:00
parent aa189ca884
commit 1741741945
34 changed files with 810 additions and 994 deletions

View File

@@ -19,13 +19,13 @@ mock = ["mpz-common/test-utils", "dep:mpz-ot"]
tlsn-block-cipher = { path = "../cipher/block-cipher" }
tlsn-stream-cipher = { path = "../cipher/stream-cipher" }
tlsn-universal-hash = { path = "../universal-hash" }
mpz-core = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c" }
mpz-ot = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c", optional = true, features = [
mpz-core = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
mpz-ot = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e", optional = true, features = [
"ideal",
] }
mpz-garble = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c" }
mpz-common = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c" }
serio = { git = "https://github.com/tlsnotary/tlsn-utils", rev = "f8d4533" }
mpz-garble = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
mpz-common = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
serio = "0.1"
async-trait = "0.1"
derive_builder = "0.12"

View File

@@ -4,9 +4,9 @@ resolver = "2"
[workspace.dependencies]
# tlsn
mpz-circuits = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c" }
mpz-garble = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c" }
tlsn-utils = { git = "https://github.com/tlsnotary/tlsn-utils", rev = "f8d4533" }
mpz-circuits = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
mpz-garble = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
tlsn-utils = { git = "https://github.com/tlsnotary/tlsn-utils", rev = "bb9769d" }
# crypto
aes = "0.8"

View File

@@ -13,9 +13,9 @@ lto = true
[dev-dependencies]
mpz-garble = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c" }
mpz-ot = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c" }
mpz-share-conversion = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c" }
mpz-garble = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
mpz-ot = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
mpz-share-conversion = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
tlsn-block-cipher = { path = "../cipher/block-cipher" }
tlsn-stream-cipher = { path = "../cipher/stream-cipher" }
tlsn-universal-hash = { path = "../universal-hash" }
@@ -23,7 +23,7 @@ tlsn-aead = { path = "../aead" }
tlsn-key-exchange = { path = "../key-exchange" }
tlsn-point-addition = { path = "../point-addition" }
tlsn-hmac-sha256 = { path = "../prf/hmac-sha256" }
tlsn-utils-aio = { git = "https://github.com/tlsnotary/tlsn-utils", rev = "f8d4533" }
tlsn-utils-aio = { git = "https://github.com/tlsnotary/tlsn-utils", rev = "bb9769d" }
uid-mux = { path = "../uid-mux" }

View File

@@ -16,27 +16,27 @@ default = ["mock"]
mock = []
[dependencies]
mpz-garble = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c" }
mpz-common = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c" }
mpz-fields = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c" }
mpz-ot = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c" }
mpz-share-conversion = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c", features = [
mpz-garble = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
mpz-common = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
mpz-fields = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
mpz-ot = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
mpz-share-conversion = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e", features = [
"ideal",
] }
mpz-circuits = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c" }
mpz-circuits = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
p256 = { version = "0.13", features = ["ecdh", "serde"] }
async-trait = "0.1"
thiserror = "1"
serde = "1"
futures = "0.3"
serio = { git = "https://github.com/tlsnotary/tlsn-utils", rev = "f8d4533" }
serio = "0.1"
derive_builder = "0.12"
tracing = "0.1"
rand = "0.8"
[dev-dependencies]
mpz-share-conversion = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c", features = [
mpz-share-conversion = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e", features = [
"ideal",
] }

View File

@@ -4,10 +4,10 @@ resolver = "2"
[workspace.dependencies]
# tlsn
mpz-circuits = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c" }
mpz-common = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c" }
mpz-ot = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c" }
mpz-garble = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c" }
mpz-circuits = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
mpz-common = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
mpz-ot = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
mpz-garble = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
# async
async-trait = "0.1"

View File

@@ -9,11 +9,6 @@ members = [
]
resolver = "2"
[patch."https://github.com/tlsnotary/tlsn-utils"]
tlsn-utils-aio = { git = "https://github.com/tlsnotary//tlsn-utils", rev = "f8d4533" }
uid-mux = { git = "https://github.com/tlsnotary//tlsn-utils", rev = "f8d4533" }
serio = { git = "https://github.com/tlsnotary//tlsn-utils", rev = "f8d4533" }
[workspace.dependencies]
# rand
rand = "0.8"

View File

@@ -18,13 +18,13 @@ default = []
tlsn-tls-core = { path = "../tls-core", features = ["serde"] }
tlsn-tls-backend = { path = "../tls-backend" }
mpz-core = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c" }
mpz-common = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c" }
mpz-fields = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c" }
mpz-garble = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c" }
mpz-ot = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c" }
mpz-ole = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c" }
mpz-share-conversion = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c" }
mpz-core = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
mpz-common = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
mpz-fields = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
mpz-garble = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
mpz-ot = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
mpz-ole = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
mpz-share-conversion = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
tlsn-block-cipher = { path = "../../cipher/block-cipher" }
tlsn-stream-cipher = { path = "../../cipher/stream-cipher" }
@@ -33,10 +33,8 @@ tlsn-aead = { path = "../../aead" }
tlsn-key-exchange = { path = "../../key-exchange" }
tlsn-hmac-sha256 = { path = "../../prf/hmac-sha256" }
tlsn-utils-aio = { git = "https://github.com/tlsnotary/tlsn-utils", rev = "f8d4533" }
uid-mux = { git = "https://github.com/tlsnotary/tlsn-utils", rev = "f8d4533", features = [
"serio",
] }
tlsn-utils-aio = { git = "https://github.com/tlsnotary/tlsn-utils", rev = "bb9769d" }
uid-mux = { version = "0.1", features = ["serio"] }
p256.workspace = true
rand.workspace = true
@@ -53,9 +51,7 @@ ludi = { git = "https://github.com/sinui0/ludi", rev = "b590de5" }
tlsn-tls-client = { path = "../tls-client" }
tlsn-tls-client-async = { path = "../tls-client-async" }
tls-server-fixture = { path = "../tls-server-fixture" }
serio = { git = "https://github.com/tlsnotary/tlsn-utils", rev = "f8d4533", features = [
"compat",
] }
serio = { version = "0.1", features = ["compat"] }
tracing-subscriber.workspace = true
tokio = { workspace = true, features = ["macros", "rt", "rt-multi-thread"] }

View File

@@ -15,13 +15,13 @@ ideal = ["dep:ghash_rc"]
[dependencies]
# tlsn
mpz-core = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c" }
mpz-common = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c", features = [
mpz-core = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
mpz-common = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e", features = [
"ideal",
] }
mpz-fields = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c" }
mpz-share-conversion-core = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c" }
mpz-share-conversion = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c" }
mpz-fields = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
mpz-share-conversion-core = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
mpz-share-conversion = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
ghash_rc = { package = "ghash", version = "0.5", optional = true }
@@ -39,10 +39,10 @@ tracing = "0.1"
derive_builder = "0.12"
[dev-dependencies]
mpz-common = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c", features = [
mpz-common = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e", features = [
"test-utils",
] }
mpz-share-conversion = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "544cf5c", features = [
mpz-share-conversion = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e", features = [
"ideal",
] }

View File

@@ -1,22 +1,18 @@
[workspace]
members = [
"server",
"client",
"integration-tests",
]
members = ["server", "client", "integration-tests"]
resolver = "2"
[workspace.dependencies]
notary-server = { path = "server" }
notary-client = { path = "client" }
tlsn-prover = { path = "../tlsn/tlsn-prover", features = ["tracing"] }
tlsn-prover = { path = "../tlsn/tlsn-prover" }
async-tungstenite = { version = "0.25", features = ["tokio-native-tls"] }
http = "1.1"
http-body-util = "0.1"
hyper = { version = "1.1", features = ["client", "http1", "server"] }
hyper-util = {version = "0.1", features = ["full"]}
hyper-util = { version = "0.1", features = ["full"] }
rustls = { version = "0.21" }
rustls-pemfile = { version = "1.0.2" }
serde_json = "1.0"

View File

@@ -14,8 +14,8 @@ notary-client.workspace = true
notary-server.workspace = true
rustls.workspace = true
rustls-pemfile.workspace = true
serde_json.workspace =true
tlsn-prover = { workspace = true, features = ["tracing"] }
serde_json.workspace = true
tlsn-prover = { workspace = true }
tokio = { workspace = true, features = ["full"] }
tokio-util = { workspace = true, features = ["compat"] }
tracing.workspace = true

View File

@@ -11,7 +11,7 @@ hyper = { workspace = true, features = ["client", "http1", "server"] }
hyper-util = { workspace = true, features = ["full"] }
rustls.workspace = true
rustls-pemfile.workspace = true
serde_json.workspace =true
serde_json.workspace = true
thiserror.workspace = true
tokio = { workspace = true, features = ["full"] }
tokio-rustls.workspace = true
@@ -31,13 +31,15 @@ csv = "1.3.0"
eyre = "0.6.8"
futures = "0.3"
futures-util = "0.3.28"
notify = { version = "6.1.1", default-features = false, features = ["macos_kqueue"] }
notify = { version = "6.1.1", default-features = false, features = [
"macos_kqueue",
] }
p256 = "0.13"
serde = { version = "1.0.147", features = ["derive"] }
serde_yaml = "0.9.21"
sha1 = "0.10"
structopt = "0.3.26"
tlsn-verifier = { path = "../../tlsn/tlsn-verifier", features = ["tracing"] }
tlsn-verifier = { path = "../../tlsn/tlsn-verifier" }
tower = { version = "0.4.12", features = ["make"] }
tower-http = { version = "0.5", features = ["cors"] }
tower-service = "0.3.2"

View File

@@ -26,18 +26,21 @@ tlsn-tls-mpc = { path = "../components/tls/tls-mpc" }
tlsn-tls-client = { path = "../components/tls/tls-client" }
tlsn-tls-client-async = { path = "../components/tls/tls-client-async" }
tls-server-fixture = { path = "../components/tls/tls-server-fixture" }
uid-mux = { path = "../components/uid-mux" }
tlsn-utils = { git = "https://github.com/tlsnotary/tlsn-utils", rev = "51f313d" }
tlsn-utils-aio = { git = "https://github.com/tlsnotary/tlsn-utils", rev = "51f313d" }
spansy = { git = "https://github.com/tlsnotary/tlsn-utils", rev = "51f313d" }
tlsn-utils = { git = "https://github.com/tlsnotary/tlsn-utils", rev = "bb9769d" }
tlsn-utils-aio = { git = "https://github.com/tlsnotary/tlsn-utils", rev = "bb9769d" }
spansy = { git = "https://github.com/tlsnotary/tlsn-utils", rev = "bb9769d" }
serio = "0.1"
uid-mux = { version = "0.1", features = ["serio"] }
mpz-core = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "477448c " }
mpz-circuits = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "477448c " }
mpz-garble-core = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "477448c " }
mpz-garble = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "477448c " }
mpz-ot = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "477448c " }
mpz-share-conversion = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "477448c " }
mpz-core = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
mpz-common = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
mpz-circuits = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
mpz-garble-core = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
mpz-garble = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
mpz-ot = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
mpz-ole = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
mpz-share-conversion = { git = "https://github.com/privacy-scaling-explorations/mpz", rev = "435526e" }
futures = "0.3"
tokio-util = "0.7"

View File

@@ -7,7 +7,7 @@ version = "0.0.0"
[dependencies]
notary-client.workspace = true
tlsn-core.workspace = true
tlsn-prover = { workspace = true, features = ["tracing"] }
tlsn-prover = { workspace = true }
tlsn-verifier.workspace = true
futures.workspace = true

View File

@@ -7,8 +7,8 @@ publish = false
[dev-dependencies]
tlsn-core.workspace = true
tlsn-tls-core.workspace = true
tlsn-prover = { workspace = true, features = ["tracing"] }
tlsn-verifier = { workspace = true, features = ["tracing"] }
tlsn-prover.workspace = true
tlsn-verifier.workspace = true
tlsn-server-fixture.workspace = true
tlsn-utils.workspace = true

View File

@@ -5,11 +5,14 @@ version = "0.1.0-alpha.5"
edition = "2021"
[features]
default = ["tracing"]
tracing = ["uid-mux/tracing"]
default = []
[dependencies]
tlsn-utils-aio.workspace = true
mpz-garble.workspace = true
mpz-ot.workspace = true
mpz-common.workspace = true
futures.workspace = true
uid-mux.workspace = true
serio = { workspace = true, features = ["codec", "bincode"] }
uid-mux = { workspace = true, features = ["serio"] }
tracing.workspace = true

View File

@@ -7,10 +7,6 @@ pub const DEFAULT_MAX_SENT_LIMIT: usize = 1 << 12;
/// Default for the maximum number of bytes that can be received (16Kb).
pub const DEFAULT_MAX_RECV_LIMIT: usize = 1 << 14;
// Determined experimentally, will be subject to change if underlying protocols are modified.
const KE_OTS: usize = 3360;
// Secret-sharing the GHASH blocks.
const GHASH_OTS: usize = 65664 * 2;
// Extra cushion room, eg. for sharing J0 blocks.
const EXTRA_OTS: usize = 16384;
const OTS_PER_BYTE_SENT: usize = 8;
@@ -20,12 +16,9 @@ const OTS_PER_BYTE_RECV: usize = 16;
/// Returns an estimate of the number of OTs that will be sent.
pub fn ot_send_estimate(role: Role, max_sent_data: usize, max_recv_data: usize) -> usize {
match role {
Role::Prover => KE_OTS + GHASH_OTS + EXTRA_OTS,
Role::Prover => EXTRA_OTS,
Role::Verifier => {
KE_OTS
+ EXTRA_OTS
+ (max_sent_data * OTS_PER_BYTE_SENT)
+ (max_recv_data * OTS_PER_BYTE_RECV)
EXTRA_OTS + (max_sent_data * OTS_PER_BYTE_SENT) + (max_recv_data * OTS_PER_BYTE_RECV)
}
}
}
@@ -34,11 +27,8 @@ pub fn ot_send_estimate(role: Role, max_sent_data: usize, max_recv_data: usize)
pub fn ot_recv_estimate(role: Role, max_sent_data: usize, max_recv_data: usize) -> usize {
match role {
Role::Prover => {
KE_OTS
+ EXTRA_OTS
+ (max_sent_data * OTS_PER_BYTE_SENT)
+ (max_recv_data * OTS_PER_BYTE_RECV)
EXTRA_OTS + (max_sent_data * OTS_PER_BYTE_SENT) + (max_recv_data * OTS_PER_BYTE_RECV)
}
Role::Verifier => KE_OTS + GHASH_OTS + EXTRA_OTS,
Role::Verifier => EXTRA_OTS,
}
}

View File

@@ -7,6 +7,27 @@
pub mod config;
pub mod mux;
use serio::codec::Codec;
use crate::mux::MuxControl;
/// IO type.
pub type Io = <serio::codec::Bincode as Codec<uid_mux::yamux::Stream>>::Framed;
/// Base OT sender.
pub type BaseOTSender = mpz_ot::chou_orlandi::Sender;
/// Base OT receiver.
pub type BaseOTReceiver = mpz_ot::chou_orlandi::Receiver;
/// OT sender.
pub type OTSender = mpz_ot::kos::SharedSender<BaseOTReceiver>;
/// OT receiver.
pub type OTReceiver = mpz_ot::kos::SharedReceiver<BaseOTSender>;
/// MPC executor.
pub type Executor = mpz_common::executor::MTExecutor<MuxControl>;
/// MPC thread context.
pub type Context = mpz_common::executor::MTContext<MuxControl, Io>;
/// DEAP thread.
pub type DEAPThread = mpz_garble::protocol::deap::DEAPThread<Context, OTSender, OTReceiver>;
/// The party's role in the TLSN protocol.
///
/// A Notary is classified as a Verifier.

View File

@@ -1,19 +1,63 @@
//! Multiplexer used in the TLSNotary protocol.
use utils_aio::codec::BincodeMux;
use std::future::IntoFuture;
use futures::{AsyncRead, AsyncWrite};
use uid_mux::{yamux, UidYamux, UidYamuxControl};
use futures::{
future::{FusedFuture, FutureExt},
AsyncRead, AsyncWrite, Future,
};
use serio::codec::Bincode;
use tracing::error;
use uid_mux::{yamux, FramedMux};
use crate::Role;
/// Multiplexer supporting unique deterministic stream IDs.
pub type Mux<T> = UidYamux<T>;
pub type Mux<Io> = yamux::Yamux<Io>;
/// Multiplexer controller providing streams with a codec attached.
pub type MuxControl = BincodeMux<UidYamuxControl>;
pub type MuxControl = FramedMux<yamux::YamuxCtrl, Bincode>;
const KB: usize = 1024;
const MB: usize = 1024 * KB;
/// Multiplexer future which must be polled for the muxer to make progress.
pub struct MuxFuture(
Box<dyn FusedFuture<Output = Result<(), yamux::ConnectionError>> + Send + Unpin>,
);
impl MuxFuture {
/// Returns true if the muxer is complete.
pub fn is_complete(&self) -> bool {
self.0.is_terminated()
}
/// Awaits a future, polling the muxer future concurrently.
pub async fn poll_with<F, R>(&mut self, fut: F) -> R
where
F: Future<Output = R>,
{
let mut fut = Box::pin(fut.fuse());
// Poll the future concurrently with the muxer future.
// If the muxer returns an error, continue polling the future
// until it completes.
loop {
futures::select! {
res = fut => return res,
res = &mut self.0 => if let Err(e) = res {
error!("mux error: {:?}", e);
},
}
}
}
}
impl Future for MuxFuture {
type Output = Result<(), yamux::ConnectionError>;
fn poll(
mut self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
self.0.as_mut().poll_unpin(cx)
}
}
/// Attaches a multiplexer to the provided socket.
///
@@ -26,20 +70,21 @@ const MB: usize = 1024 * KB;
pub fn attach_mux<T: AsyncWrite + AsyncRead + Send + Unpin + 'static>(
socket: T,
role: Role,
) -> (Mux<T>, MuxControl) {
) -> (MuxFuture, MuxControl) {
let mut mux_config = yamux::Config::default();
// See PR #418
mux_config.set_max_num_streams(40);
mux_config.set_max_buffer_size(16 * MB);
mux_config.set_receive_window(16 * MB as u32);
mux_config.set_max_num_streams(64);
let mux_role = match role {
Role::Prover => yamux::Mode::Client,
Role::Verifier => yamux::Mode::Server,
};
let mux = UidYamux::new(mux_config, socket, mux_role);
let ctrl = BincodeMux::new(mux.control());
let mux = Mux::new(socket, mux_config, mux_role);
let ctrl = FramedMux::new(mux.control(), Bincode);
(mux, ctrl)
if let Role::Prover = role {
ctrl.mux().alloc(64);
}
(MuxFuture(Box::new(mux.into_future().fuse())), ctrl)
}

View File

@@ -9,14 +9,10 @@ version = "0.1.0-alpha.5"
edition = "2021"
[features]
default = ["formats"]
default = ["formats", "rayon"]
formats = ["dep:tlsn-formats"]
tracing = [
"dep:tracing",
"tlsn-tls-client-async/tracing",
"tlsn-tls-mpc/tracing",
"tlsn-common/tracing",
]
rayon = ["mpz-common/rayon"]
force-st = ["mpz-common/force-st"]
[dependencies]
tlsn-tls-core.workspace = true
@@ -29,12 +25,16 @@ tlsn-tls-mpc.workspace = true
tlsn-utils.workspace = true
tlsn-utils-aio.workspace = true
serio = { workspace = true, features = ["compat"] }
uid-mux = { workspace = true, features = ["serio"] }
mpz-share-conversion.workspace = true
mpz-garble.workspace = true
mpz-garble-core.workspace = true
mpz-ot.workspace = true
mpz-ole.workspace = true
mpz-core.workspace = true
mpz-common.workspace = true
rand.workspace = true
futures.workspace = true
@@ -43,8 +43,7 @@ webpki-roots.workspace = true
derive_builder.workspace = true
opaque-debug.workspace = true
bytes.workspace = true
tracing = { workspace = true, optional = true }
tracing.workspace = true
web-time.workspace = true

View File

@@ -1,5 +1,4 @@
use mpz_ot::{chou_orlandi, kos};
use mpz_share_conversion::{ReceiverConfig, SenderConfig};
use tls_client::RootCertStore;
use tls_mpc::{MpcTlsCommonConfig, MpcTlsLeaderConfig, TranscriptConfig};
use tlsn_common::{
@@ -102,18 +101,6 @@ impl ProverConfig {
pub(crate) fn ot_receiver_setup_count(&self) -> usize {
ot_recv_estimate(Role::Prover, self.max_sent_data, self.max_recv_data)
}
pub(crate) fn build_p256_sender_config(&self) -> SenderConfig {
SenderConfig::builder().id("p256/0").build().unwrap()
}
pub(crate) fn build_p256_receiver_config(&self) -> ReceiverConfig {
ReceiverConfig::builder().id("p256/1").build().unwrap()
}
pub(crate) fn build_gf2_config(&self) -> SenderConfig {
SenderConfig::builder().id("gf2").record().build().unwrap()
}
}
/// Default root store using mozilla certs.

View File

@@ -12,8 +12,6 @@ pub enum ProverError {
AsyncClientError(#[from] tls_client_async::ConnectionError),
#[error(transparent)]
IOError(#[from] std::io::Error),
#[error(transparent)]
MuxerError(#[from] utils_aio::mux::MuxerError),
#[error("notarization error: {0}")]
NotarizationError(String),
#[error(transparent)]
@@ -30,6 +28,21 @@ pub enum ProverError {
InvalidRange,
}
impl From<uid_mux::yamux::ConnectionError> for ProverError {
fn from(e: uid_mux::yamux::ConnectionError) -> Self {
Self::IOError(std::io::Error::new(
std::io::ErrorKind::ConnectionAborted,
e,
))
}
}
impl From<mpz_common::ContextError> for ProverError {
fn from(e: mpz_common::ContextError) -> Self {
Self::MpcError(Box::new(e))
}
}
impl From<MpcTlsError> for ProverError {
fn from(e: MpcTlsError) -> Self {
Self::MpcError(Box::new(e))
@@ -42,12 +55,36 @@ impl From<mpz_ot::OTError> for ProverError {
}
}
impl From<mpz_ot::kos::SenderError> for ProverError {
fn from(e: mpz_ot::kos::SenderError) -> Self {
Self::MpcError(Box::new(e))
}
}
impl From<mpz_ole::OLEError> for ProverError {
fn from(e: mpz_ole::OLEError) -> Self {
Self::MpcError(Box::new(e))
}
}
impl From<mpz_ot::kos::ReceiverError> for ProverError {
fn from(e: mpz_ot::kos::ReceiverError) -> Self {
Self::MpcError(Box::new(e))
}
}
impl From<mpz_garble::VmError> for ProverError {
fn from(e: mpz_garble::VmError) -> Self {
Self::MpcError(Box::new(e))
}
}
impl From<mpz_garble::protocol::deap::DEAPError> for ProverError {
fn from(e: mpz_garble::protocol::deap::DEAPError) -> Self {
Self::MpcError(Box::new(e))
}
}
impl From<mpz_garble::MemoryError> for ProverError {
fn from(e: mpz_garble::MemoryError) -> Self {
Self::MpcError(Box::new(e))
@@ -60,35 +97,6 @@ impl From<mpz_garble::ProveError> for ProverError {
}
}
impl From<mpz_ot::actor::kos::SenderActorError> for ProverError {
fn from(value: mpz_ot::actor::kos::SenderActorError) -> Self {
Self::MpcError(Box::new(value))
}
}
impl From<mpz_ot::actor::kos::ReceiverActorError> for ProverError {
fn from(value: mpz_ot::actor::kos::ReceiverActorError) -> Self {
Self::MpcError(Box::new(value))
}
}
#[derive(Debug)]
pub struct OTShutdownError;
impl std::fmt::Display for OTShutdownError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("ot shutdown prior to completion")
}
}
impl Error for OTShutdownError {}
impl From<OTShutdownError> for ProverError {
fn from(e: OTShutdownError) -> Self {
Self::MpcError(Box::new(e))
}
}
impl From<tlsn_core::merkle::MerkleError> for ProverError {
fn from(e: tlsn_core::merkle::MerkleError) -> Self {
Self::CommitmentError(e.into())

View File

@@ -1,7 +1,7 @@
//! This module collects futures which are used by the [Prover].
use super::{state, Prover, ProverControl, ProverError};
use futures::{future::FusedFuture, Future};
use futures::Future;
use std::pin::Pin;
/// Prover future which must be polled for the TLS connection to make progress.
@@ -29,47 +29,3 @@ impl Future for ProverFuture {
self.fut.as_mut().poll(cx)
}
}
/// A future which must be polled for the muxer to make progress.
pub(crate) struct MuxFuture {
pub(crate) fut: Pin<Box<dyn FusedFuture<Output = Result<(), ProverError>> + Send + 'static>>,
}
impl Future for MuxFuture {
type Output = Result<(), ProverError>;
fn poll(
mut self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
self.fut.as_mut().poll(cx)
}
}
impl FusedFuture for MuxFuture {
fn is_terminated(&self) -> bool {
self.fut.is_terminated()
}
}
/// A future which must be polled for the Oblivious Transfer protocol to make progress.
pub(crate) struct OTFuture {
pub(crate) fut: Pin<Box<dyn FusedFuture<Output = Result<(), ProverError>> + Send + 'static>>,
}
impl Future for OTFuture {
type Output = Result<(), ProverError>;
fn poll(
mut self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
self.fut.as_mut().poll(cx)
}
}
impl FusedFuture for OTFuture {
fn is_terminated(&self) -> bool {
self.fut.is_terminated()
}
}

View File

@@ -16,33 +16,28 @@ pub mod state;
pub use config::{ProverConfig, ProverConfigBuilder, ProverConfigBuilderError};
pub use error::ProverError;
pub use future::ProverFuture;
use tlsn_common::{
mux::{attach_mux, MuxControl},
Role,
};
use error::OTShutdownError;
use future::{MuxFuture, OTFuture};
use futures::{AsyncRead, AsyncWrite, FutureExt, StreamExt, TryFutureExt};
use mpz_garble::{config::Role as DEAPRole, protocol::deap::DEAPVm};
use mpz_ot::{
actor::kos::{ReceiverActor, SenderActor, SharedReceiver, SharedSender},
chou_orlandi, kos,
};
use mpz_share_conversion as ff;
use rand::Rng;
use state::{Notarize, Prove};
use futures::{AsyncRead, AsyncWrite, TryFutureExt};
use mpz_common::Allocate;
use mpz_garble::config::Role as DEAPRole;
use mpz_ot::{chou_orlandi, kos};
use rand::Rng;
use serio::StreamExt;
use std::sync::Arc;
use tls_client::{ClientConnection, ServerName as TlsServerName};
use tls_client_async::{bind_client, ClosedConnection, TlsConnection};
use tls_mpc::{setup_components, LeaderCtrl, MpcTlsLeader, TlsRole};
use tls_mpc::{build_components, LeaderCtrl, MpcTlsLeader, TlsRole};
use tlsn_common::{
mux::{attach_mux, MuxControl},
DEAPThread, Executor, OTReceiver, OTSender, Role,
};
use tlsn_core::transcript::Transcript;
use utils_aio::mux::MuxChannel;
use uid_mux::FramedUidMux as _;
#[cfg(feature = "formats")]
use crate::http::{state as http_state, HttpProver, HttpProverError};
#[cfg(feature = "tracing")]
use tracing::{debug, debug_span, instrument, Instrument};
/// A prover instance.
@@ -73,31 +68,43 @@ impl Prover<state::Initialized> {
/// # Arguments
///
/// * `socket` - The socket to the TLS verifier.
#[instrument(level = "debug", skip_all, err)]
pub async fn setup<S: AsyncWrite + AsyncRead + Send + Unpin + 'static>(
self,
socket: S,
) -> Result<Prover<state::Setup>, ProverError> {
let (mut mux, mux_ctrl) = attach_mux(socket, Role::Prover);
let (mut mux_fut, mux_ctrl) = attach_mux(socket, Role::Prover);
let mut mux_fut = MuxFuture {
fut: Box::pin(async move { mux.run().await.map_err(ProverError::from) }.fuse()),
};
// Maximum thread forking concurrency of 8.
// TODO: Determine the optimal number of threads.
let mut exec = Executor::new(mux_ctrl.clone(), 8);
let mpc_setup_fut = setup_mpc_backend(&self.config, mux_ctrl.clone());
let (mpc_tls, vm, _, gf2, ot_fut) = futures::select! {
res = mpc_setup_fut.fuse() => res?,
_ = (&mut mux_fut).fuse() => return Err(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))?,
};
let (mpc_tls, vm, ot_recv) = mux_fut
.poll_with(setup_mpc_backend(&self.config, &mux_ctrl, &mut exec))
.await?;
let io = mux_fut
.poll_with(
mux_ctrl
.open_framed(b"tlsnotary")
.map_err(ProverError::from),
)
.await?;
let ctx = mux_fut
.poll_with(exec.new_thread().map_err(ProverError::from))
.await?;
Ok(Prover {
config: self.config,
state: state::Setup {
io,
mux_ctrl,
mux_fut,
mpc_tls,
vm,
ot_fut,
gf2,
ot_recv,
ctx,
},
})
}
@@ -112,21 +119,19 @@ impl Prover<state::Setup> {
/// # Arguments
///
/// * `socket` - The socket to the server.
#[cfg_attr(
feature = "tracing",
instrument(level = "debug", skip(self, socket), err)
)]
#[instrument(level = "debug", skip_all, err)]
pub async fn connect<S: AsyncWrite + AsyncRead + Send + Unpin + 'static>(
self,
socket: S,
) -> Result<(TlsConnection, ProverFuture), ProverError> {
let state::Setup {
io,
mux_ctrl,
mut mux_fut,
mpc_tls,
vm,
mut ot_fut,
gf2,
ot_recv,
ctx,
} = self.state;
let (mpc_ctrl, mpc_fut) = mpc_tls.run();
@@ -145,31 +150,31 @@ impl Prover<state::Setup> {
let fut = Box::pin({
let mpc_ctrl = mpc_ctrl.clone();
#[allow(clippy::let_and_return)]
let fut = async move {
async move {
let conn_fut = async {
let ClosedConnection { sent, recv, .. } = futures::select! {
res = conn_fut.fuse() => res?,
_ = ot_fut => return Err(OTShutdownError)?,
_ = mux_fut => return Err(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))?,
};
let ClosedConnection { sent, recv, .. } = mux_fut
.poll_with(conn_fut.map_err(ProverError::from))
.await?;
mpc_ctrl.close_connection().await?;
Ok::<_, ProverError>((sent, recv))
};
let ((sent, recv), mpc_tls_data) =
futures::try_join!(conn_fut, mpc_fut.map_err(ProverError::from))?;
let ((sent, recv), mpc_tls_data) = futures::try_join!(
conn_fut,
mpc_fut.in_current_span().map_err(ProverError::from)
)?;
Ok(Prover {
config: self.config,
state: state::Closed {
io,
mux_ctrl,
mux_fut,
vm,
ot_fut,
gf2,
ot_recv,
ctx,
start_time,
handshake_decommitment: mpc_tls_data
.handshake_decommitment
@@ -179,10 +184,8 @@ impl Prover<state::Setup> {
transcript_rx: Transcript::new(recv),
},
})
};
#[cfg(feature = "tracing")]
let fut = fut.instrument(debug_span!("prover_tls_connection"));
fut
}
.instrument(debug_span!("prover"))
});
Ok((
@@ -236,122 +239,116 @@ impl Prover<state::Closed> {
}
/// Performs a setup of the various MPC subprotocols.
#[cfg_attr(feature = "tracing", instrument(level = "debug", skip_all, err))]
#[allow(clippy::type_complexity)]
#[instrument(level = "debug", skip_all, err)]
async fn setup_mpc_backend(
config: &ProverConfig,
mut mux: MuxControl,
) -> Result<
(
MpcTlsLeader,
DEAPVm<SharedSender, SharedReceiver>,
SharedReceiver,
ff::ConverterSender<ff::Gf2_128, SharedSender>,
OTFuture,
),
ProverError,
> {
let (ot_send_sink, ot_send_stream) = mux.get_channel("ot/0").await?.split();
let (ot_recv_sink, ot_recv_stream) = mux.get_channel("ot/1").await?.split();
let mut ot_sender_actor = SenderActor::new(
kos::Sender::new(
config.build_ot_sender_config(),
chou_orlandi::Receiver::new(config.build_base_ot_receiver_config()),
),
ot_send_sink,
ot_send_stream,
mux: &MuxControl,
exec: &mut Executor,
) -> Result<(MpcTlsLeader, DEAPThread, OTReceiver), ProverError> {
let mut ot_sender = kos::Sender::new(
config.build_ot_sender_config(),
chou_orlandi::Receiver::new(config.build_base_ot_receiver_config()),
);
ot_sender.alloc(config.ot_sender_setup_count());
let mut ot_receiver_actor = ReceiverActor::new(
kos::Receiver::new(
config.build_ot_receiver_config(),
chou_orlandi::Sender::new(config.build_base_ot_sender_config()),
),
ot_recv_sink,
ot_recv_stream,
let mut ot_receiver = kos::Receiver::new(
config.build_ot_receiver_config(),
chou_orlandi::Sender::new(config.build_base_ot_sender_config()),
);
ot_receiver.alloc(config.ot_receiver_setup_count());
let ot_send = ot_sender_actor.sender();
let ot_recv = ot_receiver_actor.receiver();
let ot_sender = OTSender::new(ot_sender);
let ot_receiver = OTReceiver::new(ot_receiver);
#[cfg(feature = "tracing")]
debug!("Starting OT setup");
futures::try_join!(
ot_sender_actor
.setup(config.ot_sender_setup_count())
.map_err(ProverError::from),
ot_receiver_actor
.setup(config.ot_receiver_setup_count())
.map_err(ProverError::from)
let (
ctx_vm,
ctx_ke_0,
ctx_ke_1,
ctx_prf_0,
ctx_prf_1,
ctx_encrypter_block_cipher,
ctx_encrypter_stream_cipher,
ctx_encrypter_ghash,
ctx_encrypter,
ctx_decrypter_block_cipher,
ctx_decrypter_stream_cipher,
ctx_decrypter_ghash,
ctx_decrypter,
) = futures::try_join!(
exec.new_thread(),
exec.new_thread(),
exec.new_thread(),
exec.new_thread(),
exec.new_thread(),
exec.new_thread(),
exec.new_thread(),
exec.new_thread(),
exec.new_thread(),
exec.new_thread(),
exec.new_thread(),
exec.new_thread(),
exec.new_thread(),
)?;
#[cfg(feature = "tracing")]
debug!("OT setup complete");
let ot_fut = OTFuture {
fut: Box::pin(
async move {
futures::try_join!(
ot_sender_actor.run().map_err(ProverError::from),
ot_receiver_actor.run().map_err(ProverError::from)
)?;
Ok(())
}
.fuse(),
),
};
let mut vm = DEAPVm::new(
"vm",
let vm = DEAPThread::new(
DEAPRole::Leader,
rand::rngs::OsRng.gen(),
mux.get_channel("vm").await?,
Box::new(mux.clone()),
ot_send.clone(),
ot_recv.clone(),
ctx_vm,
ot_sender.clone(),
ot_receiver.clone(),
);
let p256_sender_config = config.build_p256_sender_config();
let channel = mux.get_channel(p256_sender_config.id()).await?;
let p256_send =
ff::ConverterSender::<ff::P256, _>::new(p256_sender_config, ot_send.clone(), channel);
let p256_receiver_config = config.build_p256_receiver_config();
let channel = mux.get_channel(p256_receiver_config.id()).await?;
let p256_recv =
ff::ConverterReceiver::<ff::P256, _>::new(p256_receiver_config, ot_recv.clone(), channel);
let gf2_config = config.build_gf2_config();
let channel = mux.get_channel(gf2_config.id()).await?;
let gf2 = ff::ConverterSender::<ff::Gf2_128, _>::new(gf2_config, ot_send.clone(), channel);
let mpc_tls_config = config.build_mpc_tls_config();
let (ke, prf, encrypter, decrypter) = setup_components(
mpc_tls_config.common(),
let (ke, prf, encrypter, decrypter) = build_components(
TlsRole::Leader,
&mut mux,
&mut vm,
p256_send,
p256_recv,
gf2.handle()
.map_err(|e| ProverError::MpcError(Box::new(e)))?,
)
.await
.map_err(|e| ProverError::MpcError(Box::new(e)))?;
mpc_tls_config.common(),
ctx_ke_0,
ctx_encrypter,
ctx_decrypter,
ctx_encrypter_ghash,
ctx_decrypter_ghash,
vm.new_thread(ctx_ke_1, ot_sender.clone(), ot_receiver.clone())?,
vm.new_thread(ctx_prf_0, ot_sender.clone(), ot_receiver.clone())?,
vm.new_thread(ctx_prf_1, ot_sender.clone(), ot_receiver.clone())?,
vm.new_thread(
ctx_encrypter_block_cipher,
ot_sender.clone(),
ot_receiver.clone(),
)?,
vm.new_thread(
ctx_decrypter_block_cipher,
ot_sender.clone(),
ot_receiver.clone(),
)?,
vm.new_thread(
ctx_encrypter_stream_cipher,
ot_sender.clone(),
ot_receiver.clone(),
)?,
vm.new_thread(
ctx_decrypter_stream_cipher,
ot_sender.clone(),
ot_receiver.clone(),
)?,
ot_sender.clone(),
ot_receiver.clone(),
);
let channel = mux.get_channel(mpc_tls_config.common().id()).await?;
let mut mpc_tls = MpcTlsLeader::new(mpc_tls_config, channel, ke, prf, encrypter, decrypter);
let channel = mux.open_framed(b"mpc_tls").await?;
let mut mpc_tls = MpcTlsLeader::new(
mpc_tls_config,
Box::new(StreamExt::compat_stream(channel)),
ke,
prf,
encrypter,
decrypter,
);
mpc_tls.setup().await?;
#[cfg(feature = "tracing")]
debug!("MPC backend setup complete");
Ok((mpc_tls, vm, ot_recv, gf2, ot_fut))
Ok((mpc_tls, vm, ot_receiver))
}
/// A controller for the prover.

View File

@@ -2,19 +2,14 @@
//!
//! The prover deals with a TLS verifier that is only a notary.
use crate::tls::error::OTShutdownError;
use super::{ff::ShareConversionReveal, state::Notarize, Prover, ProverError};
use futures::{FutureExt, SinkExt, StreamExt};
use super::{state::Notarize, Prover, ProverError};
use mpz_ot::VerifiableOTReceiver;
use serio::{stream::IoStreamExt as _, SinkExt as _};
use tlsn_core::{
commitment::TranscriptCommitmentBuilder,
msg::{SignedSessionHeader, TlsnMessage},
transcript::Transcript,
commitment::TranscriptCommitmentBuilder, msg::SignedSessionHeader, transcript::Transcript,
NotarizedSession, ServerName, SessionData,
};
#[cfg(feature = "tracing")]
use tracing::instrument;
use utils_aio::{expect_msg_or_err, mux::MuxChannel};
use tracing::{debug, instrument};
impl Prover<Notarize> {
/// Returns the transcript of the sent data.
@@ -33,14 +28,15 @@ impl Prover<Notarize> {
}
/// Finalizes the notarization returning a [`NotarizedSession`].
#[cfg_attr(feature = "tracing", instrument(level = "info", skip(self), err))]
#[instrument(level = "debug", skip_all, err)]
pub async fn finalize(self) -> Result<NotarizedSession, ProverError> {
let Notarize {
mut mux_ctrl,
mut io,
mux_ctrl,
mut mux_fut,
mut vm,
mut ot_fut,
mut gf2,
mut ot_recv,
mut ctx,
start_time,
handshake_decommitment,
server_public_key,
@@ -61,39 +57,33 @@ impl Prover<Notarize> {
let merkle_root = session_data.commitments().merkle_root();
let mut notarize_fut = Box::pin(async move {
let mut channel = mux_ctrl.get_channel("notarize").await?;
let (notary_encoder_seed, SignedSessionHeader { header, signature }) = mux_fut
.poll_with(async {
debug!("starting finalization");
channel
.send(TlsnMessage::TranscriptCommitmentRoot(merkle_root))
.await?;
io.send(merkle_root).await?;
let notary_encoder_seed = vm
.finalize()
.await
.map_err(|e| ProverError::MpcError(Box::new(e)))?
.expect("encoder seed returned");
ot_recv.accept_reveal(&mut ctx).await?;
// This is a temporary approach until a maliciously secure share conversion protocol is implemented.
// The prover is essentially revealing the TLS MAC key. In some exotic scenarios this allows a malicious
// TLS verifier to modify the prover's sent data.
gf2.reveal()
.await
.map_err(|e| ProverError::MpcError(Box::new(e)))?;
debug!("received OT secret");
let signed_header = expect_msg_or_err!(channel, TlsnMessage::SignedSessionHeader)?;
let notary_encoder_seed = vm
.finalize()
.await
.map_err(|e| ProverError::MpcError(Box::new(e)))?
.expect("encoder seed returned");
Ok::<_, ProverError>((notary_encoder_seed, signed_header))
})
.fuse();
let signed_header: SignedSessionHeader = io.expect_next().await?;
Ok::<_, ProverError>((notary_encoder_seed, signed_header))
})
.await?;
let (notary_encoder_seed, SignedSessionHeader { header, signature }) = futures::select_biased! {
res = notarize_fut => res?,
_ = ot_fut => return Err(OTShutdownError)?,
_ = &mut mux_fut => return Err(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))?,
};
// Wait for the notary to correctly close the connection.
mux_fut.await?;
if !mux_fut.is_complete() {
mux_ctrl.mux().close();
mux_fut.await?;
}
// Check the header is consistent with the Prover's view.
header

View File

@@ -4,19 +4,13 @@
//! the verifier directly verifies parts of the transcript.
use super::{state::Prove as ProveState, Prover, ProverError};
use crate::tls::error::OTShutdownError;
use futures::{FutureExt, SinkExt};
use mpz_garble::{Memory, Prove, Vm};
use mpz_share_conversion::ShareConversionReveal;
use tlsn_core::{
msg::TlsnMessage, proof::SessionInfo, transcript::get_value_ids, Direction, ServerName,
Transcript,
};
use mpz_garble::{Memory, Prove};
use mpz_ot::VerifiableOTReceiver;
use serio::SinkExt as _;
use tlsn_core::{proof::SessionInfo, transcript::get_value_ids, Direction, ServerName, Transcript};
use utils::range::{RangeSet, RangeUnion};
use utils_aio::mux::MuxChannel;
#[cfg(feature = "tracing")]
use tracing::info;
use tracing::{info, instrument};
impl Prover<ProveState> {
/// Returns the transcript of the sent requests
@@ -66,99 +60,82 @@ impl Prover<ProveState> {
}
/// Prove transcript values
#[instrument(level = "debug", skip_all, err)]
pub async fn prove(&mut self) -> Result<(), ProverError> {
let mut proving_info = std::mem::take(&mut self.state.proving_info);
let mut prove_fut = Box::pin(async {
// Create a new channel and vm thread if not already present
let channel = if let Some(ref mut channel) = self.state.channel {
channel
} else {
self.state.channel = Some(self.state.mux_ctrl.get_channel("prove-verify").await?);
self.state.channel.as_mut().unwrap()
};
self.state
.mux_fut
.poll_with(async {
// Now prove the transcript parts which have been marked for reveal
let sent_value_ids = proving_info
.sent_ids
.iter_ranges()
.map(|r| get_value_ids(&r.into(), Direction::Sent).collect::<Vec<String>>());
let recv_value_ids = proving_info.recv_ids.iter_ranges().map(|r| {
get_value_ids(&r.into(), Direction::Received).collect::<Vec<String>>()
});
let prove_thread = if let Some(ref mut prove_thread) = self.state.prove_thread {
prove_thread
} else {
self.state.prove_thread = Some(self.state.vm.new_thread("prove-verify").await?);
self.state.prove_thread.as_mut().unwrap()
};
let value_refs = sent_value_ids
.chain(recv_value_ids)
.map(|ids| {
let inner_refs = ids
.iter()
.map(|id| {
self.state
.vm
.get_value(id.as_str())
.expect("Byte should be in VM memory")
})
.collect::<Vec<_>>();
// Now prove the transcript parts which have been marked for reveal
let sent_value_ids = proving_info
.sent_ids
.iter_ranges()
.map(|r| get_value_ids(&r.into(), Direction::Sent).collect::<Vec<String>>());
let recv_value_ids = proving_info
.recv_ids
.iter_ranges()
.map(|r| get_value_ids(&r.into(), Direction::Received).collect::<Vec<String>>());
self.state
.vm
.array_from_values(inner_refs.as_slice())
.expect("Byte should be in VM Memory")
})
.collect::<Vec<_>>();
let value_refs = sent_value_ids
.chain(recv_value_ids)
.map(|ids| {
let inner_refs = ids
.iter()
.map(|id| {
prove_thread
.get_value(id.as_str())
.expect("Byte should be in VM memory")
})
.collect::<Vec<_>>();
// Extract cleartext we want to reveal from transcripts
let mut cleartext =
Vec::with_capacity(proving_info.sent_ids.len() + proving_info.recv_ids.len());
proving_info
.sent_ids
.iter_ranges()
.for_each(|r| cleartext.extend_from_slice(&self.state.transcript_tx.data()[r]));
proving_info
.recv_ids
.iter_ranges()
.for_each(|r| cleartext.extend_from_slice(&self.state.transcript_rx.data()[r]));
proving_info.cleartext = cleartext;
prove_thread
.array_from_values(inner_refs.as_slice())
.expect("Byte should be in VM Memory")
})
.collect::<Vec<_>>();
// Send the proving info to the verifier
self.state.io.send(proving_info).await?;
// Extract cleartext we want to reveal from transcripts
let mut cleartext =
Vec::with_capacity(proving_info.sent_ids.len() + proving_info.recv_ids.len());
proving_info
.sent_ids
.iter_ranges()
.for_each(|r| cleartext.extend_from_slice(&self.state.transcript_tx.data()[r]));
proving_info
.recv_ids
.iter_ranges()
.for_each(|r| cleartext.extend_from_slice(&self.state.transcript_rx.data()[r]));
proving_info.cleartext = cleartext;
info!("Sent proving info to verifier");
// Send the proving info to the verifier
channel.send(TlsnMessage::ProvingInfo(proving_info)).await?;
// Prove the revealed transcript parts
self.state.vm.prove(value_refs.as_slice()).await?;
#[cfg(feature = "tracing")]
info!("Sent proving info to verifier");
info!("Successfully proved cleartext");
// Prove the revealed transcript parts
prove_thread.prove(value_refs.as_slice()).await?;
#[cfg(feature = "tracing")]
info!("Successfully proved cleartext");
Ok::<_, ProverError>(())
})
.fuse();
futures::select_biased! {
res = prove_fut => res?,
_ = &mut self.state.ot_fut => return Err(OTShutdownError)?,
_ = &mut self.state.mux_fut => return Err(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))?,
};
Ok::<_, ProverError>(())
})
.await?;
Ok(())
}
/// Finalize the proving
#[instrument(level = "debug", skip_all, err)]
pub async fn finalize(self) -> Result<(), ProverError> {
let ProveState {
mut mux_ctrl,
mut io,
mux_ctrl,
mut mux_fut,
mut vm,
mut ot_fut,
mut gf2,
mut ot_recv,
mut ctx,
handshake_decommitment,
..
} = self.state;
@@ -169,38 +146,29 @@ impl Prover<ProveState> {
handshake_decommitment,
};
let mut finalize_fut = Box::pin(async move {
let mut channel = mux_ctrl.get_channel("finalize").await?;
mux_fut
.poll_with(async move {
ot_recv.accept_reveal(&mut ctx).await?;
_ = vm
.finalize()
.await
.map_err(|e| ProverError::MpcError(Box::new(e)))?
.expect("encoder seed returned");
_ = vm
.finalize()
.await
.map_err(|e| ProverError::MpcError(Box::new(e)))?
.expect("encoder seed returned");
// This is a temporary approach until a maliciously secure share conversion protocol is implemented.
// The prover is essentially revealing the TLS MAC key. In some exotic scenarios this allows a malicious
// TLS verifier to modify the prover's request.
gf2.reveal()
.await
.map_err(|e| ProverError::MpcError(Box::new(e)))?;
// Send session_info to the verifier
io.send(session_info).await?;
// Send session_info to the verifier
channel.send(TlsnMessage::SessionInfo(session_info)).await?;
Ok::<_, ProverError>(())
})
.await?;
Ok::<_, ProverError>(())
})
.fuse();
// Wait for the verifier to correctly close the connection.
if !mux_fut.is_complete() {
mux_ctrl.mux().close();
mux_fut.await?;
}
futures::select_biased! {
res = finalize_fut => res?,
_ = ot_fut => return Err(OTShutdownError)?,
_ = &mut mux_fut => return Err(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))?,
};
// We need to wait for the verifier to correctly close the connection. Otherwise the prover
// would rush ahead and close the connection before the verifier has finished.
mux_fut.await?;
Ok(())
}
}

View File

@@ -1,21 +1,16 @@
//! TLS prover states.
use crate::tls::{MuxFuture, OTFuture};
use mpz_core::commit::Decommitment;
use mpz_garble::protocol::deap::{DEAPThread, DEAPVm, PeerEncodings};
use mpz_garble::protocol::deap::PeerEncodings;
use mpz_garble_core::{encoding_state, EncodedValue};
use mpz_ot::actor::kos::{SharedReceiver, SharedSender};
use mpz_share_conversion::{ConverterSender, Gf2_128};
use std::collections::HashMap;
use tls_core::{handshake::HandshakeData, key::PublicKey};
use tls_mpc::MpcTlsLeader;
use tlsn_common::mux::MuxControl;
use tlsn_core::{
commitment::TranscriptCommitmentBuilder,
msg::{ProvingInfo, TlsnMessage},
Transcript,
use tlsn_common::{
mux::{MuxControl, MuxFuture},
Context, DEAPThread, Io, OTReceiver,
};
use utils_aio::duplex::Duplex;
use tlsn_core::{commitment::TranscriptCommitmentBuilder, msg::ProvingInfo, Transcript};
/// Entry state
pub struct Initialized;
@@ -24,26 +19,27 @@ opaque_debug::implement!(Initialized);
/// State after MPC setup has completed.
pub struct Setup {
/// A muxer for communication with the TLS verifier.
pub(crate) io: Io,
pub(crate) mux_ctrl: MuxControl,
pub(crate) mux_fut: MuxFuture,
pub(crate) mpc_tls: MpcTlsLeader,
pub(crate) vm: DEAPVm<SharedSender, SharedReceiver>,
pub(crate) ot_fut: OTFuture,
pub(crate) gf2: ConverterSender<Gf2_128, SharedSender>,
pub(crate) vm: DEAPThread,
pub(crate) ot_recv: OTReceiver,
pub(crate) ctx: Context,
}
opaque_debug::implement!(Setup);
/// State after the TLS connection has been closed.
pub struct Closed {
pub(crate) io: Io,
pub(crate) mux_ctrl: MuxControl,
pub(crate) mux_fut: MuxFuture,
pub(crate) vm: DEAPVm<SharedSender, SharedReceiver>,
pub(crate) ot_fut: OTFuture,
pub(crate) gf2: ConverterSender<Gf2_128, SharedSender>,
pub(crate) vm: DEAPThread,
pub(crate) ot_recv: OTReceiver,
pub(crate) ctx: Context,
pub(crate) start_time: u64,
pub(crate) handshake_decommitment: Decommitment<HandshakeData>,
@@ -57,13 +53,13 @@ opaque_debug::implement!(Closed);
/// Notarizing state.
pub struct Notarize {
/// A muxer for communication with the Notary
pub(crate) io: Io,
pub(crate) mux_ctrl: MuxControl,
pub(crate) mux_fut: MuxFuture,
pub(crate) vm: DEAPVm<SharedSender, SharedReceiver>,
pub(crate) ot_fut: OTFuture,
pub(crate) gf2: ConverterSender<Gf2_128, SharedSender>,
pub(crate) vm: DEAPThread,
pub(crate) ot_recv: OTReceiver,
pub(crate) ctx: Context,
pub(crate) start_time: u64,
pub(crate) handshake_decommitment: Decommitment<HandshakeData>,
@@ -92,11 +88,12 @@ impl From<Closed> for Notarize {
);
Self {
io: state.io,
mux_ctrl: state.mux_ctrl,
mux_fut: state.mux_fut,
vm: state.vm,
ot_fut: state.ot_fut,
gf2: state.gf2,
ot_recv: state.ot_recv,
ctx: state.ctx,
start_time: state.start_time,
handshake_decommitment: state.handshake_decommitment,
server_public_key: state.server_public_key,
@@ -109,12 +106,13 @@ impl From<Closed> for Notarize {
/// Proving state.
pub struct Prove {
pub(crate) io: Io,
pub(crate) mux_ctrl: MuxControl,
pub(crate) mux_fut: MuxFuture,
pub(crate) vm: DEAPVm<SharedSender, SharedReceiver>,
pub(crate) ot_fut: OTFuture,
pub(crate) gf2: ConverterSender<Gf2_128, SharedSender>,
pub(crate) vm: DEAPThread,
pub(crate) ot_recv: OTReceiver,
pub(crate) ctx: Context,
pub(crate) handshake_decommitment: Decommitment<HandshakeData>,
@@ -122,24 +120,21 @@ pub struct Prove {
pub(crate) transcript_rx: Transcript,
pub(crate) proving_info: ProvingInfo,
pub(crate) channel: Option<Box<dyn Duplex<TlsnMessage>>>,
pub(crate) prove_thread: Option<DEAPThread<SharedSender, SharedReceiver>>,
}
impl From<Closed> for Prove {
fn from(state: Closed) -> Self {
Self {
io: state.io,
mux_ctrl: state.mux_ctrl,
mux_fut: state.mux_fut,
vm: state.vm,
ot_fut: state.ot_fut,
gf2: state.gf2,
ot_recv: state.ot_recv,
ctx: state.ctx,
handshake_decommitment: state.handshake_decommitment,
transcript_tx: state.transcript_tx,
transcript_rx: state.transcript_rx,
proving_info: ProvingInfo::default(),
channel: None,
prove_thread: None,
}
}
}
@@ -163,7 +158,7 @@ mod sealed {
}
fn collect_encodings(
vm: &DEAPVm<SharedSender, SharedReceiver>,
vm: &impl PeerEncodings,
transcript_tx: &Transcript,
transcript_rx: &Transcript,
) -> HashMap<String, EncodedValue<encoding_state::Active>> {

View File

@@ -9,22 +9,27 @@ version = "0.1.0-alpha.5"
edition = "2021"
[features]
tracing = ["dep:tracing", "tlsn-tls-mpc/tracing", "tlsn-common/tracing"]
default = ["rayon"]
rayon = ["mpz-common/rayon"]
force-st = ["mpz-common/force-st"]
[dependencies]
tlsn-core.workspace = true
tlsn-common.workspace = true
tlsn-tls-core.workspace = true
tlsn-tls-mpc.workspace = true
uid-mux.workspace = true
tlsn-utils-aio.workspace = true
serio = { workspace = true, features = ["compat"] }
uid-mux = { workspace = true, features = ["serio"] }
mpz-core.workspace = true
mpz-garble.workspace = true
mpz-ot.workspace = true
mpz-ole.workspace = true
mpz-share-conversion.workspace = true
mpz-circuits.workspace = true
mpz-common.workspace = true
futures.workspace = true
thiserror.workspace = true
@@ -32,5 +37,4 @@ derive_builder.workspace = true
rand.workspace = true
signature.workspace = true
opaque-debug.workspace = true
tracing = { workspace = true, optional = true }
tracing.workspace = true

View File

@@ -1,5 +1,4 @@
use mpz_ot::{chou_orlandi, kos};
use mpz_share_conversion::{ReceiverConfig, SenderConfig};
use std::fmt::{Debug, Formatter, Result};
use tls_core::verify::{ServerCertVerifier, WebPkiVerifier};
use tls_mpc::{MpcTlsCommonConfig, MpcTlsFollowerConfig, TranscriptConfig};
@@ -123,20 +122,4 @@ impl VerifierConfig {
pub(crate) fn ot_receiver_setup_count(&self) -> usize {
ot_recv_estimate(Role::Verifier, self.max_sent_data, self.max_recv_data)
}
pub(crate) fn build_p256_sender_config(&self) -> SenderConfig {
SenderConfig::builder().id("p256/1").build().unwrap()
}
pub(crate) fn build_p256_receiver_config(&self) -> ReceiverConfig {
ReceiverConfig::builder().id("p256/0").build().unwrap()
}
pub(crate) fn build_gf2_config(&self) -> ReceiverConfig {
ReceiverConfig::builder()
.id("gf2")
.record()
.build()
.unwrap()
}
}

View File

@@ -15,6 +15,21 @@ pub enum VerifierError {
InvalidRange,
}
impl From<uid_mux::yamux::ConnectionError> for VerifierError {
fn from(e: uid_mux::yamux::ConnectionError) -> Self {
Self::IOError(std::io::Error::new(
std::io::ErrorKind::ConnectionAborted,
e,
))
}
}
impl From<mpz_common::ContextError> for VerifierError {
fn from(e: mpz_common::ContextError) -> Self {
Self::MpcError(Box::new(e))
}
}
impl From<MpcTlsError> for VerifierError {
fn from(e: MpcTlsError) -> Self {
Self::MpcError(Box::new(e))
@@ -27,14 +42,20 @@ impl From<mpz_ot::OTError> for VerifierError {
}
}
impl From<mpz_ot::actor::kos::SenderActorError> for VerifierError {
fn from(e: mpz_ot::actor::kos::SenderActorError) -> Self {
impl From<mpz_ot::kos::SenderError> for VerifierError {
fn from(e: mpz_ot::kos::SenderError) -> Self {
Self::MpcError(Box::new(e))
}
}
impl From<mpz_ot::actor::kos::ReceiverActorError> for VerifierError {
fn from(e: mpz_ot::actor::kos::ReceiverActorError) -> Self {
impl From<mpz_ot::kos::ReceiverError> for VerifierError {
fn from(e: mpz_ot::kos::ReceiverError) -> Self {
Self::MpcError(Box::new(e))
}
}
impl From<mpz_garble::protocol::deap::DEAPError> for VerifierError {
fn from(e: mpz_garble::protocol::deap::DEAPError) -> Self {
Self::MpcError(Box::new(e))
}
}

View File

@@ -1,50 +0,0 @@
//! This module collects futures which are used by the [Verifier](crate::tls::Verifier).
use super::{OTSenderActor, VerifierError};
use futures::{future::FusedFuture, Future};
use std::pin::Pin;
/// A future which must be polled for the muxer to make progress.
pub(crate) struct MuxFuture {
pub(crate) fut: Pin<Box<dyn FusedFuture<Output = Result<(), VerifierError>> + Send + 'static>>,
}
impl Future for MuxFuture {
type Output = Result<(), VerifierError>;
fn poll(
mut self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
self.fut.as_mut().poll(cx)
}
}
impl FusedFuture for MuxFuture {
fn is_terminated(&self) -> bool {
self.fut.is_terminated()
}
}
/// A future which must be polled for the Oblivious Transfer protocol to make progress.
pub(crate) struct OTFuture {
pub(crate) fut:
Pin<Box<dyn FusedFuture<Output = Result<OTSenderActor, VerifierError>> + Send + 'static>>,
}
impl Future for OTFuture {
type Output = Result<OTSenderActor, VerifierError>;
fn poll(
mut self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
self.fut.as_mut().poll(cx)
}
}
impl FusedFuture for OTFuture {
fn is_terminated(&self) -> bool {
self.fut.is_terminated()
}
}

View File

@@ -2,53 +2,33 @@
pub(crate) mod config;
mod error;
mod future;
mod notarize;
pub mod state;
mod verify;
pub use config::{VerifierConfig, VerifierConfigBuilder, VerifierConfigBuilderError};
pub use error::VerifierError;
use mpz_common::Allocate;
use serio::StreamExt;
use uid_mux::FramedUidMux;
use std::time::{SystemTime, UNIX_EPOCH};
use crate::tls::future::OTFuture;
use future::MuxFuture;
use futures::{
stream::{SplitSink, SplitStream},
AsyncRead, AsyncWrite, FutureExt, StreamExt, TryFutureExt,
};
use mpz_garble::{config::Role as GarbleRole, protocol::deap::DEAPVm};
use mpz_ot::{
actor::kos::{
msgs::Message as ActorMessage, ReceiverActor, SenderActor, SharedReceiver, SharedSender,
},
chou_orlandi, kos,
};
use mpz_share_conversion as ff;
use futures::{AsyncRead, AsyncWrite, TryFutureExt};
use mpz_garble::config::Role as DEAPRole;
use mpz_ot::{chou_orlandi, kos};
use rand::Rng;
use signature::Signer;
use state::{Notarize, Verify};
use tls_mpc::{setup_components, MpcTlsFollower, MpcTlsFollowerData, TlsRole};
use tls_mpc::{build_components, MpcTlsFollower, MpcTlsFollowerData, TlsRole};
use tlsn_common::{
mux::{attach_mux, MuxControl},
Role,
DEAPThread, Executor, OTReceiver, OTSender, Role,
};
use tlsn_core::{proof::SessionInfo, RedactedTranscript, SessionHeader, Signature};
use utils_aio::{duplex::Duplex, mux::MuxChannel};
#[cfg(feature = "tracing")]
use tracing::{debug, info, instrument};
type OTSenderActor = SenderActor<
chou_orlandi::Receiver,
SplitSink<
Box<dyn Duplex<ActorMessage<chou_orlandi::msgs::Message>>>,
ActorMessage<chou_orlandi::msgs::Message>,
>,
SplitStream<Box<dyn Duplex<ActorMessage<chou_orlandi::msgs::Message>>>>,
>;
/// A Verifier instance.
pub struct Verifier<T: state::VerifierState> {
config: VerifierConfig,
@@ -75,30 +55,44 @@ impl Verifier<state::Initialized> {
self,
socket: S,
) -> Result<Verifier<state::Setup>, VerifierError> {
let (mut mux, mux_ctrl) = attach_mux(socket, Role::Verifier);
let (mut mux_fut, mux_ctrl) = attach_mux(socket, Role::Verifier);
let mut mux_fut = MuxFuture {
fut: Box::pin(async move { mux.run().await.map_err(VerifierError::from) }.fuse()),
};
// Maximum thread forking concurrency of 8.
// TODO: Determine the optimal number of threads.
let mut exec = Executor::new(mux_ctrl.clone(), 8);
let encoder_seed: [u8; 32] = rand::rngs::OsRng.gen();
let mpc_setup_fut = setup_mpc_backend(&self.config, mux_ctrl.clone(), encoder_seed);
let (mpc_tls, vm, ot_send, ot_recv, gf2, ot_fut) = futures::select! {
res = mpc_setup_fut.fuse() => res?,
_ = &mut mux_fut => return Err(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))?,
};
let (mpc_tls, vm, ot_send) = mux_fut
.poll_with(setup_mpc_backend(
&self.config,
&mux_ctrl,
&mut exec,
encoder_seed,
))
.await?;
let io = mux_fut
.poll_with(
mux_ctrl
.open_framed(b"tlsnotary")
.map_err(VerifierError::from),
)
.await?;
let ctx = mux_fut
.poll_with(exec.new_thread().map_err(VerifierError::from))
.await?;
Ok(Verifier {
config: self.config,
state: state::Setup {
io,
mux_ctrl,
mux_fut,
mpc_tls,
vm,
ot_send,
ot_recv,
ot_fut,
gf2,
ctx,
encoder_seed,
},
})
@@ -152,14 +146,13 @@ impl Verifier<state::Setup> {
/// Runs the verifier until the TLS connection is closed.
pub async fn run(self) -> Result<Verifier<state::Closed>, VerifierError> {
let state::Setup {
io,
mux_ctrl,
mut mux_fut,
mpc_tls,
vm,
ot_send,
ot_recv,
mut ot_fut,
gf2,
ctx,
encoder_seed,
} = self.state;
@@ -168,20 +161,15 @@ impl Verifier<state::Setup> {
.unwrap()
.as_secs();
let (_, mpc_fut) = mpc_tls.run();
let MpcTlsFollowerData {
handshake_commitment,
server_key: server_ephemeral_key,
bytes_sent: sent_len,
bytes_recv: recv_len,
} = futures::select! {
res = mpc_fut.fuse() => res?,
_ = &mut mux_fut => return Err(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))?,
res = ot_fut => return Err(res.map(|_| ()).expect_err("future will not return Ok here"))
};
} = mux_fut
.poll_with(mpc_tls.run().1.map_err(VerifierError::from))
.await?;
#[cfg(feature = "tracing")]
info!("Finished TLS session");
// TODO: We should be able to skip this commitment and verify the handshake directly.
@@ -190,13 +178,12 @@ impl Verifier<state::Setup> {
Ok(Verifier {
config: self.config,
state: state::Closed {
io,
mux_ctrl,
mux_fut,
vm,
ot_send,
ot_recv,
ot_fut,
gf2,
ctx,
encoder_seed,
start_time,
server_ephemeral_key,
@@ -233,122 +220,115 @@ impl Verifier<state::Closed> {
}
/// Performs a setup of the various MPC subprotocols.
#[cfg_attr(feature = "tracing", instrument(level = "debug", skip_all, err))]
#[allow(clippy::type_complexity)]
#[instrument(level = "debug", skip_all, err)]
async fn setup_mpc_backend(
config: &VerifierConfig,
mut mux_ctrl: MuxControl,
mux: &MuxControl,
exec: &mut Executor,
encoder_seed: [u8; 32],
) -> Result<
(
MpcTlsFollower,
DEAPVm<SharedSender, SharedReceiver>,
SharedSender,
SharedReceiver,
ff::ConverterReceiver<ff::Gf2_128, SharedReceiver>,
OTFuture,
),
VerifierError,
> {
let (ot_send_sink, ot_send_stream) = mux_ctrl.get_channel("ot/1").await?.split();
let (ot_recv_sink, ot_recv_stream) = mux_ctrl.get_channel("ot/0").await?.split();
let mut ot_sender_actor = OTSenderActor::new(
kos::Sender::new(
config.build_ot_sender_config(),
chou_orlandi::Receiver::new(config.build_base_ot_receiver_config()),
),
ot_send_sink,
ot_send_stream,
) -> Result<(MpcTlsFollower, DEAPThread, OTSender), VerifierError> {
let mut ot_sender = kos::Sender::new(
config.build_ot_sender_config(),
chou_orlandi::Receiver::new(config.build_base_ot_receiver_config()),
);
ot_sender.alloc(config.ot_sender_setup_count());
let mut ot_receiver_actor = ReceiverActor::new(
kos::Receiver::new(
config.build_ot_receiver_config(),
chou_orlandi::Sender::new(config.build_base_ot_sender_config()),
),
ot_recv_sink,
ot_recv_stream,
let mut ot_receiver = kos::Receiver::new(
config.build_ot_receiver_config(),
chou_orlandi::Sender::new(config.build_base_ot_sender_config()),
);
ot_receiver.alloc(config.ot_receiver_setup_count());
let ot_send = ot_sender_actor.sender();
let ot_recv = ot_receiver_actor.receiver();
let ot_sender = OTSender::new(ot_sender);
let ot_receiver = OTReceiver::new(ot_receiver);
#[cfg(feature = "tracing")]
debug!("Starting OT setup");
futures::try_join!(
ot_sender_actor
.setup(config.ot_sender_setup_count())
.map_err(VerifierError::from),
ot_receiver_actor
.setup(config.ot_receiver_setup_count())
.map_err(VerifierError::from)
let (
ctx_vm,
ctx_ke_0,
ctx_ke_1,
ctx_prf_0,
ctx_prf_1,
ctx_encrypter_block_cipher,
ctx_encrypter_stream_cipher,
ctx_encrypter_ghash,
ctx_encrypter,
ctx_decrypter_block_cipher,
ctx_decrypter_stream_cipher,
ctx_decrypter_ghash,
ctx_decrypter,
) = futures::try_join!(
exec.new_thread(),
exec.new_thread(),
exec.new_thread(),
exec.new_thread(),
exec.new_thread(),
exec.new_thread(),
exec.new_thread(),
exec.new_thread(),
exec.new_thread(),
exec.new_thread(),
exec.new_thread(),
exec.new_thread(),
exec.new_thread(),
)?;
#[cfg(feature = "tracing")]
debug!("OT setup complete");
let ot_fut = OTFuture {
fut: Box::pin(
async move {
futures::try_join!(
ot_sender_actor.run().map_err(VerifierError::from),
ot_receiver_actor.run().map_err(VerifierError::from)
)?;
Ok(ot_sender_actor)
}
.fuse(),
),
};
let mut vm = DEAPVm::new(
"vm",
GarbleRole::Follower,
let vm = DEAPThread::new(
DEAPRole::Follower,
encoder_seed,
mux_ctrl.get_channel("vm").await?,
Box::new(mux_ctrl.clone()),
ot_send.clone(),
ot_recv.clone(),
ctx_vm,
ot_sender.clone(),
ot_receiver.clone(),
);
let p256_sender_config = config.build_p256_sender_config();
let channel = mux_ctrl.get_channel(p256_sender_config.id()).await?;
let p256_send =
ff::ConverterSender::<ff::P256, _>::new(p256_sender_config, ot_send.clone(), channel);
let p256_receiver_config = config.build_p256_receiver_config();
let channel = mux_ctrl.get_channel(p256_receiver_config.id()).await?;
let p256_recv =
ff::ConverterReceiver::<ff::P256, _>::new(p256_receiver_config, ot_recv.clone(), channel);
let gf2_config = config.build_gf2_config();
let channel = mux_ctrl.get_channel(gf2_config.id()).await?;
let gf2 = ff::ConverterReceiver::<ff::Gf2_128, _>::new(gf2_config, ot_recv.clone(), channel);
let mpc_tls_config = config.build_mpc_tls_config();
let (ke, prf, encrypter, decrypter) = setup_components(
mpc_tls_config.common(),
let (ke, prf, encrypter, decrypter) = build_components(
TlsRole::Follower,
&mut mux_ctrl,
&mut vm,
p256_send,
p256_recv,
gf2.handle()
.map_err(|e| VerifierError::MpcError(Box::new(e)))?,
)
.await
.map_err(|e| VerifierError::MpcError(Box::new(e)))?;
mpc_tls_config.common(),
ctx_ke_0,
ctx_encrypter,
ctx_decrypter,
ctx_encrypter_ghash,
ctx_decrypter_ghash,
vm.new_thread(ctx_ke_1, ot_sender.clone(), ot_receiver.clone())?,
vm.new_thread(ctx_prf_0, ot_sender.clone(), ot_receiver.clone())?,
vm.new_thread(ctx_prf_1, ot_sender.clone(), ot_receiver.clone())?,
vm.new_thread(
ctx_encrypter_block_cipher,
ot_sender.clone(),
ot_receiver.clone(),
)?,
vm.new_thread(
ctx_decrypter_block_cipher,
ot_sender.clone(),
ot_receiver.clone(),
)?,
vm.new_thread(
ctx_encrypter_stream_cipher,
ot_sender.clone(),
ot_receiver.clone(),
)?,
vm.new_thread(
ctx_decrypter_stream_cipher,
ot_sender.clone(),
ot_receiver.clone(),
)?,
ot_sender.clone(),
ot_receiver.clone(),
);
let channel = mux_ctrl.get_channel(mpc_tls_config.common().id()).await?;
let mut mpc_tls = MpcTlsFollower::new(mpc_tls_config, channel, ke, prf, encrypter, decrypter);
let channel = mux.open_framed(b"mpc_tls").await?;
let mut mpc_tls = MpcTlsFollower::new(
mpc_tls_config,
Box::new(StreamExt::compat_stream(channel)),
ke,
prf,
encrypter,
decrypter,
);
mpc_tls.setup().await?;
#[cfg(feature = "tracing")]
debug!("MPC backend setup complete");
Ok((mpc_tls, vm, ot_send, ot_recv, gf2, ot_fut))
Ok((mpc_tls, vm, ot_sender))
}

View File

@@ -3,18 +3,15 @@
//! The TLS verifier is only a notary.
use super::{state::Notarize, Verifier, VerifierError};
use futures::{FutureExt, SinkExt, StreamExt, TryFutureExt};
use mpz_core::serialize::CanonicalSerialize;
use mpz_share_conversion::ShareConversionVerify;
use mpz_ot::CommittedOTSender;
use serio::{stream::IoStreamExt, SinkExt as _};
use signature::Signer;
use tlsn_core::{
msg::{SignedSessionHeader, TlsnMessage},
HandshakeSummary, SessionHeader, Signature,
merkle::MerkleRoot, msg::SignedSessionHeader, HandshakeSummary, SessionHeader, Signature,
};
use utils_aio::{expect_msg_or_err, mux::MuxChannel};
#[cfg(feature = "tracing")]
use tracing::info;
use tracing::{debug, info, instrument};
impl Verifier<Notarize> {
/// Notarizes the TLS session.
@@ -22,18 +19,18 @@ impl Verifier<Notarize> {
/// # Arguments
///
/// * `signer` - The signer used to sign the notarization result.
#[instrument(level = "debug", skip_all, err)]
pub async fn finalize<T>(self, signer: &impl Signer<T>) -> Result<SessionHeader, VerifierError>
where
T: Into<Signature>,
{
let Notarize {
mut mux_ctrl,
mut io,
mux_ctrl,
mut mux_fut,
mut vm,
ot_send,
ot_recv,
ot_fut,
mut gf2,
mut ot_send,
mut ctx,
encoder_seed,
start_time,
server_ephemeral_key,
@@ -42,69 +39,52 @@ impl Verifier<Notarize> {
recv_len,
} = self.state;
let notarize_fut = async {
let mut notarize_channel = mux_ctrl.get_channel("notarize").await?;
let session_header = mux_fut
.poll_with(async {
let merkle_root: MerkleRoot = io.expect_next().await?;
let merkle_root =
expect_msg_or_err!(notarize_channel, TlsnMessage::TranscriptCommitmentRoot)?;
// Finalize all MPC before signing the session header.
ot_send.reveal(&mut ctx).await?;
// Finalize all MPC before signing the session header.
let (mut ot_sender_actor, _, _) = futures::try_join!(
ot_fut,
ot_send.shutdown().map_err(VerifierError::from),
ot_recv.shutdown().map_err(VerifierError::from)
)?;
debug!("revealed OT secret");
ot_sender_actor.reveal().await?;
vm.finalize()
.await
.map_err(|e| VerifierError::MpcError(Box::new(e)))?;
vm.finalize()
.await
.map_err(|e| VerifierError::MpcError(Box::new(e)))?;
info!("Finalized all MPC");
gf2.verify()
.await
.map_err(|e| VerifierError::MpcError(Box::new(e)))?;
let handshake_summary =
HandshakeSummary::new(start_time, server_ephemeral_key, handshake_commitment);
#[cfg(feature = "tracing")]
info!("Finalized all MPC");
let session_header = SessionHeader::new(
encoder_seed,
merkle_root,
sent_len,
recv_len,
handshake_summary,
);
let handshake_summary =
HandshakeSummary::new(start_time, server_ephemeral_key, handshake_commitment);
let signature = signer.sign(&session_header.to_bytes());
let session_header = SessionHeader::new(
encoder_seed,
merkle_root,
sent_len,
recv_len,
handshake_summary,
);
info!("Signed session header");
let signature = signer.sign(&session_header.to_bytes());
#[cfg(feature = "tracing")]
info!("Signed session header");
notarize_channel
.send(TlsnMessage::SignedSessionHeader(SignedSessionHeader {
io.send(SignedSessionHeader {
header: session_header.clone(),
signature: signature.into(),
}))
})
.await?;
#[cfg(feature = "tracing")]
info!("Sent session header");
info!("Sent session header");
Ok::<_, VerifierError>(session_header)
};
Ok::<_, VerifierError>(session_header)
})
.await?;
let session_header = futures::select! {
res = notarize_fut.fuse() => res?,
_ = &mut mux_fut => Err(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))?,
};
let mut mux_ctrl = mux_ctrl.into_inner();
futures::try_join!(mux_ctrl.close().map_err(VerifierError::from), mux_fut)?;
if !mux_fut.is_complete() {
mux_ctrl.mux().close();
mux_fut.await?;
}
Ok(session_header)
}

View File

@@ -1,16 +1,12 @@
//! TLS Verifier state.
use mpz_core::hash::Hash;
use mpz_garble::protocol::deap::{DEAPThread, DEAPVm};
use mpz_ot::actor::kos::{SharedReceiver, SharedSender};
use mpz_share_conversion::{ConverterReceiver, Gf2_128};
use tls_core::key::PublicKey;
use tls_mpc::MpcTlsFollower;
use tlsn_common::mux::MuxControl;
use tlsn_core::msg::TlsnMessage;
use utils_aio::duplex::Duplex;
use crate::tls::future::{MuxFuture, OTFuture};
use tlsn_common::{
mux::{MuxControl, MuxFuture},
Context, DEAPThread, Io, OTSender,
};
/// TLS Verifier state.
pub trait VerifierState: sealed::Sealed {}
@@ -22,29 +18,27 @@ opaque_debug::implement!(Initialized);
/// State after MPC setup has completed.
pub struct Setup {
pub(crate) io: Io,
pub(crate) mux_ctrl: MuxControl,
pub(crate) mux_fut: MuxFuture,
pub(crate) mpc_tls: MpcTlsFollower,
pub(crate) vm: DEAPVm<SharedSender, SharedReceiver>,
pub(crate) ot_send: SharedSender,
pub(crate) ot_recv: SharedReceiver,
pub(crate) ot_fut: OTFuture,
pub(crate) gf2: ConverterReceiver<Gf2_128, SharedReceiver>,
pub(crate) vm: DEAPThread,
pub(crate) ot_send: OTSender,
pub(crate) ctx: Context,
pub(crate) encoder_seed: [u8; 32],
}
/// State after the TLS connection has been closed.
pub struct Closed {
pub(crate) io: Io,
pub(crate) mux_ctrl: MuxControl,
pub(crate) mux_fut: MuxFuture,
pub(crate) vm: DEAPVm<SharedSender, SharedReceiver>,
pub(crate) ot_send: SharedSender,
pub(crate) ot_recv: SharedReceiver,
pub(crate) ot_fut: OTFuture,
pub(crate) gf2: ConverterReceiver<Gf2_128, SharedReceiver>,
pub(crate) vm: DEAPThread,
pub(crate) ot_send: OTSender,
pub(crate) ctx: Context,
pub(crate) encoder_seed: [u8; 32],
pub(crate) start_time: u64,
@@ -58,14 +52,13 @@ opaque_debug::implement!(Closed);
/// Notarizing state.
pub struct Notarize {
pub(crate) io: Io,
pub(crate) mux_ctrl: MuxControl,
pub(crate) mux_fut: MuxFuture,
pub(crate) vm: DEAPVm<SharedSender, SharedReceiver>,
pub(crate) ot_send: SharedSender,
pub(crate) ot_recv: SharedReceiver,
pub(crate) ot_fut: OTFuture,
pub(crate) gf2: ConverterReceiver<Gf2_128, SharedReceiver>,
pub(crate) vm: DEAPThread,
pub(crate) ot_send: OTSender,
pub(crate) ctx: Context,
pub(crate) encoder_seed: [u8; 32],
pub(crate) start_time: u64,
@@ -80,13 +73,12 @@ opaque_debug::implement!(Notarize);
impl From<Closed> for Notarize {
fn from(value: Closed) -> Self {
Self {
io: value.io,
mux_ctrl: value.mux_ctrl,
mux_fut: value.mux_fut,
vm: value.vm,
ot_send: value.ot_send,
ot_recv: value.ot_recv,
ot_fut: value.ot_fut,
gf2: value.gf2,
ctx: value.ctx,
encoder_seed: value.encoder_seed,
start_time: value.start_time,
server_ephemeral_key: value.server_ephemeral_key,
@@ -99,23 +91,19 @@ impl From<Closed> for Notarize {
/// Verifying state.
pub struct Verify {
pub(crate) io: Io,
pub(crate) mux_ctrl: MuxControl,
pub(crate) mux_fut: MuxFuture,
pub(crate) vm: DEAPVm<SharedSender, SharedReceiver>,
pub(crate) ot_send: SharedSender,
pub(crate) ot_recv: SharedReceiver,
pub(crate) ot_fut: OTFuture,
pub(crate) gf2: ConverterReceiver<Gf2_128, SharedReceiver>,
pub(crate) vm: DEAPThread,
pub(crate) ot_send: OTSender,
pub(crate) ctx: Context,
pub(crate) start_time: u64,
pub(crate) server_ephemeral_key: PublicKey,
pub(crate) handshake_commitment: Hash,
pub(crate) sent_len: usize,
pub(crate) recv_len: usize,
pub(crate) channel: Option<Box<dyn Duplex<TlsnMessage>>>,
pub(crate) verify_thread: Option<DEAPThread<SharedSender, SharedReceiver>>,
}
opaque_debug::implement!(Verify);
@@ -123,20 +111,17 @@ opaque_debug::implement!(Verify);
impl From<Closed> for Verify {
fn from(value: Closed) -> Self {
Self {
io: value.io,
mux_ctrl: value.mux_ctrl,
mux_fut: value.mux_fut,
vm: value.vm,
ot_send: value.ot_send,
ot_recv: value.ot_recv,
ot_fut: value.ot_fut,
gf2: value.gf2,
ctx: value.ctx,
start_time: value.start_time,
server_ephemeral_key: value.server_ephemeral_key,
handshake_commitment: value.handshake_commitment,
sent_len: value.sent_len,
recv_len: value.recv_len,
channel: None,
verify_thread: None,
}
}
}

View File

@@ -3,17 +3,15 @@
//! The TLS verifier is an application-specific verifier.
use super::{state::Verify as VerifyState, Verifier, VerifierError};
use futures::{FutureExt, StreamExt, TryFutureExt};
use mpz_circuits::types::Value;
use mpz_garble::{Memory, Verify, Vm};
use mpz_share_conversion::ShareConversionVerify;
use mpz_garble::{Memory, Verify};
use mpz_ot::CommittedOTSender;
use serio::stream::IoStreamExt;
use tlsn_core::{
msg::TlsnMessage, proof::SessionInfo, transcript::get_value_ids, Direction, HandshakeSummary,
msg::ProvingInfo, proof::SessionInfo, transcript::get_value_ids, Direction, HandshakeSummary,
RedactedTranscript, TranscriptSlice,
};
use utils_aio::{expect_msg_or_err, mux::MuxChannel};
#[cfg(feature = "tracing")]
use tracing::info;
impl Verifier<VerifyState> {
@@ -25,159 +23,123 @@ impl Verifier<VerifyState> {
pub async fn receive(
&mut self,
) -> Result<(RedactedTranscript, RedactedTranscript), VerifierError> {
let verify_fut = async {
// Create a new channel and vm thread if not already present
let channel = if let Some(ref mut channel) = self.state.channel {
channel
} else {
self.state.channel = Some(self.state.mux_ctrl.get_channel("prove-verify").await?);
self.state.channel.as_mut().unwrap()
};
self.state
.mux_fut
.poll_with(async {
// Receive the proving info from the prover
let mut proving_info: ProvingInfo = self.state.io.expect_next().await?;
let mut cleartext = proving_info.cleartext.clone();
let verify_thread = if let Some(ref mut verify_thread) = self.state.verify_thread {
verify_thread
} else {
self.state.verify_thread = Some(self.state.vm.new_thread("prove-verify").await?);
self.state.verify_thread.as_mut().unwrap()
};
info!("Received proving info from prover");
// Receive the proving info from the prover
let mut proving_info = expect_msg_or_err!(channel, TlsnMessage::ProvingInfo)?;
let mut cleartext = proving_info.cleartext.clone();
// Check ranges
if proving_info.sent_ids.max().unwrap_or_default() > self.state.sent_len
|| proving_info.recv_ids.max().unwrap_or_default() > self.state.recv_len
{
return Err(VerifierError::InvalidRange);
}
#[cfg(feature = "tracing")]
info!("Received proving info from prover");
// Now verify the transcript parts which the prover wants to reveal
let sent_value_ids = proving_info
.sent_ids
.iter_ranges()
.map(|r| get_value_ids(&r.into(), Direction::Sent).collect::<Vec<String>>());
let recv_value_ids = proving_info.recv_ids.iter_ranges().map(|r| {
get_value_ids(&r.into(), Direction::Received).collect::<Vec<String>>()
});
// Check ranges
if proving_info.sent_ids.max().unwrap_or_default() > self.state.sent_len
|| proving_info.recv_ids.max().unwrap_or_default() > self.state.recv_len
{
return Err(VerifierError::InvalidRange);
}
let value_refs = sent_value_ids
.chain(recv_value_ids)
.map(|ids| {
let inner_refs = ids
.iter()
.map(|id| {
self.state
.vm
.get_value(id.as_str())
.expect("Byte should be in VM memory")
})
.collect::<Vec<_>>();
// Now verify the transcript parts which the prover wants to reveal
let sent_value_ids = proving_info
.sent_ids
.iter_ranges()
.map(|r| get_value_ids(&r.into(), Direction::Sent).collect::<Vec<String>>());
let recv_value_ids = proving_info
.recv_ids
.iter_ranges()
.map(|r| get_value_ids(&r.into(), Direction::Received).collect::<Vec<String>>());
self.state
.vm
.array_from_values(inner_refs.as_slice())
.expect("Byte should be in VM Memory")
})
.collect::<Vec<_>>();
let value_refs = sent_value_ids
.chain(recv_value_ids)
.map(|ids| {
let inner_refs = ids
.iter()
.map(|id| {
verify_thread
.get_value(id.as_str())
.expect("Byte should be in VM memory")
})
.collect::<Vec<_>>();
let values = proving_info
.sent_ids
.iter_ranges()
.chain(proving_info.recv_ids.iter_ranges())
.map(|range| {
Value::Array(cleartext.drain(..range.len()).map(|b| (b).into()).collect())
})
.collect::<Vec<_>>();
verify_thread
.array_from_values(inner_refs.as_slice())
.expect("Byte should be in VM Memory")
})
.collect::<Vec<_>>();
// Check that purported values are correct
self.state.vm.verify(&value_refs, &values).await?;
let values = proving_info
.sent_ids
.iter_ranges()
.chain(proving_info.recv_ids.iter_ranges())
.map(|range| {
Value::Array(cleartext.drain(..range.len()).map(|b| (b).into()).collect())
})
.collect::<Vec<_>>();
info!("Successfully verified purported cleartext");
// Check that purported values are correct
verify_thread.verify(&value_refs, &values).await?;
// Create redacted transcripts
let mut transcripts = proving_info
.sent_ids
.iter_ranges()
.chain(proving_info.recv_ids.iter_ranges())
.map(|range| {
TranscriptSlice::new(
range.clone(),
proving_info.cleartext.drain(..range.len()).collect(),
)
})
.collect::<Vec<_>>();
#[cfg(feature = "tracing")]
info!("Successfully verified purported cleartext");
let recv_transcripts =
transcripts.split_off(proving_info.sent_ids.iter_ranges().count());
let (sent_redacted, recv_redacted) = (
RedactedTranscript::new(self.state.sent_len, transcripts),
RedactedTranscript::new(self.state.recv_len, recv_transcripts),
);
// Create redacted transcripts
let mut transcripts = proving_info
.sent_ids
.iter_ranges()
.chain(proving_info.recv_ids.iter_ranges())
.map(|range| {
TranscriptSlice::new(
range.clone(),
proving_info.cleartext.drain(..range.len()).collect(),
)
})
.collect::<Vec<_>>();
info!("Successfully created redacted transcripts");
let recv_transcripts =
transcripts.split_off(proving_info.sent_ids.iter_ranges().count());
let (sent_redacted, recv_redacted) = (
RedactedTranscript::new(self.state.sent_len, transcripts),
RedactedTranscript::new(self.state.recv_len, recv_transcripts),
);
#[cfg(feature = "tracing")]
info!("Successfully created redacted transcripts");
Ok::<_, VerifierError>((sent_redacted, recv_redacted))
};
futures::select! {
res = verify_fut.fuse() => res,
_ = &mut self.state.mux_fut => Err(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))?,
}
Ok::<_, VerifierError>((sent_redacted, recv_redacted))
})
.await
}
/// Verifies the TLS session.
pub async fn finalize(self) -> Result<SessionInfo, VerifierError> {
let VerifyState {
mut mux_ctrl,
mut io,
mux_ctrl,
mut mux_fut,
mut vm,
ot_send,
ot_recv,
ot_fut,
mut gf2,
mut ot_send,
mut ctx,
start_time,
server_ephemeral_key,
handshake_commitment,
..
} = self.state;
let finalize_fut = async {
let mut channel = mux_ctrl.get_channel("finalize").await?;
let session_info = mux_fut
.poll_with(async {
// Finalize all MPC
ot_send.reveal(&mut ctx).await?;
// Finalize all MPC
let (mut ot_sender_actor, _, _) = futures::try_join!(
ot_fut,
ot_send.shutdown().map_err(VerifierError::from),
ot_recv.shutdown().map_err(VerifierError::from)
)?;
vm.finalize()
.await
.map_err(|e| VerifierError::MpcError(Box::new(e)))?;
ot_sender_actor.reveal().await?;
let session_info: SessionInfo = io.expect_next().await?;
vm.finalize()
.await
.map_err(|e| VerifierError::MpcError(Box::new(e)))?;
info!("Finalized all MPC");
gf2.verify()
.await
.map_err(|e| VerifierError::MpcError(Box::new(e)))?;
let session_info = expect_msg_or_err!(channel, TlsnMessage::SessionInfo)?;
#[cfg(feature = "tracing")]
info!("Finalized all MPC");
Ok::<_, VerifierError>(session_info)
};
let session_info = futures::select! {
res = finalize_fut.fuse() => res?,
_ = &mut mux_fut => Err(std::io::Error::from(std::io::ErrorKind::UnexpectedEof))?,
};
Ok::<_, VerifierError>(session_info)
})
.await?;
let handshake_summary =
HandshakeSummary::new(start_time, server_ephemeral_key, handshake_commitment);
@@ -185,12 +147,12 @@ impl Verifier<VerifyState> {
// Verify the TLS session
session_info.verify(&handshake_summary, self.config.cert_verifier())?;
#[cfg(feature = "tracing")]
info!("Successfully verified session");
let mut mux_ctrl = mux_ctrl.into_inner();
futures::try_join!(mux_ctrl.close().map_err(VerifierError::from), mux_fut)?;
if !mux_fut.is_complete() {
mux_ctrl.mux().close();
mux_fut.await?;
}
Ok(session_info)
}