From 7015ba09bb452452368d30fe968126c0a346e01a Mon Sep 17 00:00:00 2001 From: Christopher Chong Date: Tue, 7 May 2024 19:31:55 +0800 Subject: [PATCH 01/11] Add tlsnotary client. --- tlsn/Cargo.toml | 2 + tlsn/tlsn-notary-client/Cargo.toml | 42 ++++ tlsn/tlsn-notary-client/src/client.rs | 336 ++++++++++++++++++++++++++ tlsn/tlsn-notary-client/src/error.rs | 43 ++++ tlsn/tlsn-notary-client/src/lib.rs | 11 + 5 files changed, 434 insertions(+) create mode 100644 tlsn/tlsn-notary-client/Cargo.toml create mode 100644 tlsn/tlsn-notary-client/src/client.rs create mode 100644 tlsn/tlsn-notary-client/src/error.rs create mode 100644 tlsn/tlsn-notary-client/src/lib.rs diff --git a/tlsn/Cargo.toml b/tlsn/Cargo.toml index 13d59e5fc..a0567e204 100644 --- a/tlsn/Cargo.toml +++ b/tlsn/Cargo.toml @@ -6,6 +6,7 @@ members = [ "tlsn-prover", "tlsn-formats", "tlsn-server-fixture", + "tlsn-notary-client", "tests-integration", "examples", "benches", @@ -19,6 +20,7 @@ tlsn-prover = { path = "tlsn-prover" } tlsn-verifier = { path = "tlsn-verifier" } tlsn-server-fixture = { path = "tlsn-server-fixture" } tlsn-formats = { path = "tlsn-formats" } +tlsn-notary-client = { path = "tlsn-notary-client" } tlsn-tls-core = { path = "../components/tls/tls-core" } tlsn-tls-mpc = { path = "../components/tls/tls-mpc" } diff --git a/tlsn/tlsn-notary-client/Cargo.toml b/tlsn/tlsn-notary-client/Cargo.toml new file mode 100644 index 000000000..372236c19 --- /dev/null +++ b/tlsn/tlsn-notary-client/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "tlsn-notary-client" +version = "0.1.0-alpha.5" +edition = "2021" + +[features] +default = ["tracing"] +tracing = [ + "dep:tracing", + "tlsn-common/tracing", + "tlsn-prover/tracing" +] + +[dependencies] +notary-server = { path = "../../notary-server" } +tlsn-tls-client.workspace = true +tlsn-common.workspace = true +tlsn-prover.workspace = true + +derive_builder.workspace = true +thiserror.workspace = true +tokio = { workspace = true, features = [ + "rt", + "rt-multi-thread", + "macros", + "net", + "io-std", + "fs", +] } +tokio-util.workspace = true +tracing = { workspace = true, optional = true } +webpki-roots.workspace = true + +eyre = "0.6.8" +http-body-util = "0.1" +hyper = { version = "1.1", features = ["client", "http1"] } +hyper-util = { version = "0.1", features = ["full"] } +rustls = "0.21" +rustls-pemfile = "1.0.2" +serde = { version = "1.0.147", features = ["derive"] } +serde_json = "1.0" +tokio-rustls = "0.24.1" diff --git a/tlsn/tlsn-notary-client/src/client.rs b/tlsn/tlsn-notary-client/src/client.rs new file mode 100644 index 000000000..293f95311 --- /dev/null +++ b/tlsn/tlsn-notary-client/src/client.rs @@ -0,0 +1,336 @@ +//! This module handles setting up prover to connect to notary via TCP or TLS + +use eyre::eyre; +use http_body_util::{BodyExt as _, Either, Empty, Full}; +use hyper::{client::conn::http1::Parts, Request, StatusCode}; +use hyper_util::rt::TokioIo; +use notary_server::{ClientType, NotarizationSessionRequest, NotarizationSessionResponse}; +use rustls::{Certificate, ClientConfig, RootCertStore}; +use std::sync::Arc; +use tls_client::RootCertStore as TlsClientRootCertStore; +use tlsn_common::config::{DEFAULT_MAX_RECV_LIMIT, DEFAULT_MAX_SENT_LIMIT}; +use tlsn_prover::tls::{state::Setup, Prover, ProverConfig}; +use tokio::io::{AsyncRead, AsyncWrite}; +use tokio_rustls::TlsConnector; +use tokio_util::{bytes::Bytes, compat::TokioAsyncReadCompatExt}; + +#[cfg(feature = "tracing")] +use tracing::debug; + +use crate::error::NotaryClientError; + +/// Client that setup prover to connect to notary server +#[derive(Debug, Clone, derive_builder::Builder)] +#[builder(build_fn(error = "NotaryClientError"))] +pub struct NotaryClient { + /// Host of the notary server endpoint + #[builder(setter(into))] + host: String, + /// Port of the notary server endpoint + port: u16, + /// Maximum number of bytes that can be sent. + #[builder(default = "DEFAULT_MAX_SENT_LIMIT")] + max_sent_data: usize, + /// Maximum number of bytes that can be received. + #[builder(default = "DEFAULT_MAX_RECV_LIMIT")] + max_recv_data: usize, + /// Root certificate store used for establishing TLS connection with notary + #[builder(setter(strip_option), default = "Some(notary_default_root_store()?)")] + notary_root_cert_store: Option, + /// DNS name of notary server used for establishing TLS connection with notary + #[builder( + setter(into, strip_option), + default = "Some(\"tlsnotaryserver.io\".to_string())" + )] + notary_dns: Option, + /// API key used to call notary server endpoints if whitelisting is enabled in notary server + #[builder(setter(into, strip_option), default)] + api_key: Option, + /// TLS root certificate store + #[builder(default = "server_default_root_store()")] + server_root_cert_store: TlsClientRootCertStore, + /// Application server DNS name + #[builder(setter(into))] + server_dns: String, +} + +impl NotaryClient { + /// Returns a prover that connects to notary via TCP without TLS + pub async fn setup_tcp_prover(&self) -> Result, NotaryClientError> { + #[cfg(feature = "tracing")] + debug!("Setting up tcp connection..."); + let notary_socket = tokio::net::TcpStream::connect((self.host.as_str(), self.port)) + .await + .map_err(|err| NotaryClientError::Connection(err.to_string()))?; + + self.request_notarization(notary_socket).await + } + + /// Returns a prover that connects to notary via TCP-TLS + pub async fn setup_tls_prover(&self) -> Result, NotaryClientError> { + #[cfg(feature = "tracing")] + debug!("Setting up tls connection..."); + let notary_root_cert_store = + self.notary_root_cert_store + .clone() + .ok_or(NotaryClientError::TlsSetup( + "Notary root cert store is not provided".to_string(), + ))?; + let notary_dns = self.notary_dns.as_ref().ok_or(NotaryClientError::TlsSetup( + "Notary dns is not provided".to_string(), + ))?; + + let notary_socket = tokio::net::TcpStream::connect((self.host.as_str(), self.port)) + .await + .map_err(|err| NotaryClientError::Connection(err.to_string()))?; + + let client_notary_config = ClientConfig::builder() + .with_safe_defaults() + .with_root_certificates(notary_root_cert_store) + .with_no_client_auth(); + + let notary_connector = TlsConnector::from(Arc::new(client_notary_config)); + let notary_tls_socket = notary_connector + .connect( + notary_dns.as_str().try_into().map_err(|err| { + NotaryClientError::TlsSetup(format!("Failed to parse notary dns: {err}")) + })?, + notary_socket, + ) + .await + .map_err(|err| { + NotaryClientError::TlsSetup(format!("Failed to connect to notary via TLS: {err}")) + })?; + + self.request_notarization(notary_tls_socket).await + } + + /// Requests notarization from the Notary server. + async fn request_notarization( + &self, + notary_socket: S, + ) -> Result, NotaryClientError> { + let http_scheme = if self.notary_dns.is_some() { + "https" + } else { + "http" + }; + + // Attach the hyper HTTP client to the notary connection to send request to the /session endpoint to configure notarization and obtain session id + let (mut notary_request_sender, notary_connection) = + hyper::client::conn::http1::handshake(TokioIo::new(notary_socket)) + .await + .map_err(|err| { + NotaryClientError::Connection(format!( + "Failed to attach http client to notary socket: {err}" + )) + })?; + + // Spawn the HTTP task to be run concurrently + let notary_connection_task = tokio::spawn(notary_connection.without_shutdown()); + + // Build the HTTP request to configure notarization + let configuration_request_payload = serde_json::to_string(&NotarizationSessionRequest { + client_type: ClientType::Tcp, + max_sent_data: Some(self.max_sent_data), + max_recv_data: Some(self.max_recv_data), + }) + .map_err(|err| { + NotaryClientError::Configuration(format!( + "Failed to serialise http request for configuration: {err}" + )) + })?; + + let mut configuration_request_builder = Request::builder() + .uri(format!("{http_scheme}://{}:{}/session", self.host, self.port)) + .method("POST") + .header("Host", &self.host) + // Need to specify application/json for axum to parse it as json + .header("Content-Type", "application/json"); + + if let Some(api_key) = &self.api_key { + configuration_request_builder = + configuration_request_builder.header("Authorization", api_key); + } + + let configuration_request = configuration_request_builder + .body(Either::Left(Full::new(Bytes::from( + configuration_request_payload, + )))) + .map_err(|err| { + NotaryClientError::Configuration(format!( + "Failed to build http request for configuration: {err}" + )) + })?; + + #[cfg(feature = "tracing")] + debug!("Sending configuration request: {:?}", configuration_request); + + let configuration_response = notary_request_sender + .send_request(configuration_request) + .await + .map_err(|err| { + NotaryClientError::Configuration(format!( + "Failed to send http request for configuration: {err}" + )) + })?; + + #[cfg(feature = "tracing")] + debug!("Sent configuration request"); + + if configuration_response.status() != StatusCode::OK { + return Err(NotaryClientError::Configuration(format!( + "Configuration response is not OK: {:?}", + configuration_response + ))); + } + + let configuration_response_payload = configuration_response + .into_body() + .collect() + .await + .map_err(|err| { + NotaryClientError::Configuration(format!( + "Failed to parse configuration response: {err}" + )) + })? + .to_bytes(); + + let configuration_response_payload_parsed = + serde_json::from_str::(&String::from_utf8_lossy( + &configuration_response_payload, + )) + .map_err(|err| { + NotaryClientError::Configuration(format!( + "Failed to parse configuration response: {err}" + )) + })?; + + #[cfg(feature = "tracing")] + debug!( + "Configuration response: {:?}", + configuration_response_payload_parsed + ); + + // Send notarization request via HTTP, where the underlying TCP/TLS connection will be extracted later + let mut notarization_request_builder = Request::builder() + // Need to specify the session_id so that notary server knows the right configuration to use + // as the configuration is set in the previous HTTP call + .uri(format!( + "{http_scheme}://{}:{}/notarize?sessionId={}", + self.host, self.port, &configuration_response_payload_parsed.session_id + )) + .method("GET") + .header("Host", &self.host) + .header("Connection", "Upgrade") + // Need to specify this upgrade header for server to extract TCP/TLS connection later + .header("Upgrade", "TCP"); + + if let Some(api_key) = &self.api_key { + notarization_request_builder = + notarization_request_builder.header("Authorization", api_key); + } + + let notarization_request = notarization_request_builder + .body(Either::Right(Empty::::new())) + .map_err(|err| { + NotaryClientError::NotarizationRequest(format!( + "Failed to build http request for notarization: {err}" + )) + })?; + + #[cfg(feature = "tracing")] + debug!("Sending notarization request: {:?}", notarization_request); + + let notarization_response = notary_request_sender + .send_request(notarization_request) + .await + .map_err(|err| { + NotaryClientError::NotarizationRequest(format!( + "Failed to send http request for notarization: {err}" + )) + })?; + + #[cfg(feature = "tracing")] + debug!("Sent notarization request"); + + if notarization_response.status() != StatusCode::SWITCHING_PROTOCOLS { + return Err(NotaryClientError::NotarizationRequest(format!( + "Notarization response is not SWITCHING_PROTOCOL: {:?}", + notarization_response + ))); + } + + // Claim back notary socket after HTTP exchange is done + let Parts { + io: notary_socket, .. + } = notary_connection_task + .await + .map_err(|err| eyre!("Error when joining notary connection task: {err}"))? + .map_err(|err| { + eyre!("Failed to claim back notary socket after HTTP exchange is done: {err}") + })?; + + #[cfg(feature = "tracing")] + debug!("Setting up prover..."); + + // Basic default prover config using the session_id returned from /session endpoint just now + let prover_config = ProverConfig::builder() + .id(configuration_response_payload_parsed.session_id) + .server_dns(&self.server_dns) + .max_sent_data(self.max_sent_data) + .max_recv_data(self.max_recv_data) + .root_cert_store(self.server_root_cert_store.clone()) + .build()?; + + // Create a new prover + let prover = Prover::new(prover_config) + .setup(notary_socket.into_inner().compat()) + .await?; + + Ok(prover) + } +} + +/// Default root store using mozilla certs. +fn server_default_root_store() -> TlsClientRootCertStore { + let mut root_store = TlsClientRootCertStore::empty(); + root_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| { + tls_client::OwnedTrustAnchor::from_subject_spki_name_constraints( + ta.subject.as_ref(), + ta.subject_public_key_info.as_ref(), + ta.name_constraints.as_ref().map(|nc| nc.as_ref()), + ) + })); + + root_store +} + +/// Default root store using self signed certs. +fn notary_default_root_store() -> Result { + let pem_file = std::str::from_utf8(include_bytes!( + "../../../notary-server/fixture/tls/rootCA.crt" + )) + .map_err(|err| { + NotaryClientError::Builder(format!("Failed to parse default root CA cert: {err}")) + })?; + + let mut reader = std::io::BufReader::new(pem_file.as_bytes()); + let mut certificates: Vec = rustls_pemfile::certs(&mut reader) + .map_err(|err| { + NotaryClientError::Builder(format!("Failed to setup default root CA cert: {err}")) + })? + .into_iter() + .map(Certificate) + .collect(); + let certificate = certificates.remove(0); + + let mut root_store = RootCertStore::empty(); + root_store.add(&certificate).map_err(|err| { + NotaryClientError::Builder(format!( + "Fialed to add default root cert to root store: {err}" + )) + })?; + + Ok(root_store) +} diff --git a/tlsn/tlsn-notary-client/src/error.rs b/tlsn/tlsn-notary-client/src/error.rs new file mode 100644 index 000000000..4abc46dde --- /dev/null +++ b/tlsn/tlsn-notary-client/src/error.rs @@ -0,0 +1,43 @@ +//! This module handles errors that might occur + +use derive_builder::UninitializedFieldError; +use eyre::Report; +use std::error::Error; +use tlsn_prover::tls::{ProverConfigBuilderError, ProverError}; + +#[derive(Debug, thiserror::Error)] +#[allow(missing_docs)] +pub enum NotaryClientError { + #[error(transparent)] + Unexpected(#[from] Report), + #[error("Failed to build notary client: {0}")] + Builder(String), + #[error("Failed to connect to notary: {0}")] + Connection(String), + #[error("Error occured when setting up TLS to connect to notary: {0}")] + TlsSetup(String), + #[error("Error occurred during configuration: {0}")] + Configuration(String), + #[error("Error occurred during notarization request: {0}")] + NotarizationRequest(String), + #[error("Error occurred during prover setup: {0}")] + ProverSetup(Box), +} + +impl From for NotaryClientError { + fn from(error: ProverError) -> Self { + Self::ProverSetup(Box::new(error)) + } +} + +impl From for NotaryClientError { + fn from(error: ProverConfigBuilderError) -> Self { + Self::ProverSetup(Box::new(error)) + } +} + +impl From for NotaryClientError { + fn from(ufe: UninitializedFieldError) -> Self { + Self::Builder(ufe.to_string()) + } +} diff --git a/tlsn/tlsn-notary-client/src/lib.rs b/tlsn/tlsn-notary-client/src/lib.rs new file mode 100644 index 000000000..6eb142e00 --- /dev/null +++ b/tlsn/tlsn-notary-client/src/lib.rs @@ -0,0 +1,11 @@ +//! The notary client library. +//! +//! This library contains TLSNotary notary client implementations, which helps to setup prover that +//! connects to TLSN notary server via TCP or TLS + +#![deny(missing_docs, unreachable_pub, unused_must_use)] +#![deny(clippy::all)] +#![forbid(unsafe_code)] + +pub mod client; +pub mod error; From 6fe0dd0d2a404a55469b147d9f58217df90e1d70 Mon Sep 17 00:00:00 2001 From: Christopher Chong Date: Thu, 9 May 2024 17:58:08 +0800 Subject: [PATCH 02/11] Modify notary server test to use notary client. --- notary-server/Cargo.toml | 1 + notary-server/tests/integration_test.rs | 244 +++++++----------------- tlsn/tlsn-notary-client/src/client.rs | 32 ++-- tlsn/tlsn-notary-client/src/error.rs | 4 +- tlsn/tlsn-notary-client/src/lib.rs | 2 +- 5 files changed, 88 insertions(+), 195 deletions(-) diff --git a/notary-server/Cargo.toml b/notary-server/Cargo.toml index 13194e5c0..7c1d37da1 100644 --- a/notary-server/Cargo.toml +++ b/notary-server/Cargo.toml @@ -44,6 +44,7 @@ ws_stream_tungstenite = { version = "0.10.0", features = ["tokio_io"] } [dev-dependencies] # specify vendored feature to use statically linked copy of OpenSSL hyper-tls = { version = "0.5.0", features = ["vendored"] } +tlsn-notary-client = { path = "../tlsn/tlsn-notary-client" } tlsn-prover = { path = "../tlsn/tlsn-prover", features = ["tracing"] } tls-server-fixture = { path = "../components/tls/tls-server-fixture" } tlsn-tls-core = { path = "../components/tls/tls-core" } diff --git a/notary-server/tests/integration_test.rs b/notary-server/tests/integration_test.rs index c7ce0dbf6..a80355d82 100644 --- a/notary-server/tests/integration_test.rs +++ b/notary-server/tests/integration_test.rs @@ -2,42 +2,30 @@ use async_tungstenite::{ tokio::connect_async_with_tls_connector_and_config, tungstenite::protocol::WebSocketConfig, }; use futures::AsyncWriteExt; -use hyper::{ - body::to_bytes, - client::{conn::Parts, HttpConnector}, - Body, Client, Request, StatusCode, -}; +use hyper::{body::to_bytes, client::HttpConnector, Body, Client, Request, StatusCode}; use hyper_tls::HttpsConnector; use rstest::rstest; -use rustls::{Certificate, ClientConfig, RootCertStore}; -use std::{ - net::{IpAddr, SocketAddr}, - sync::Arc, - time::Duration, -}; +use std::time::Duration; +use tls_core::anchors::RootCertStore as TlsClientRootCertStore; use tls_server_fixture::{bind_test_server_hyper, CA_CERT_DER, SERVER_DOMAIN}; -use tlsn_prover::tls::{Prover, ProverConfig}; -use tokio::{ - io::{AsyncRead, AsyncWrite}, - net::TcpStream, -}; -use tokio_rustls::{client::TlsStream, TlsConnector}; +use tlsn_notary_client::client::NotaryClient; +use tlsn_prover::tls::{state::Setup, Prover, ProverConfig}; use tokio_util::compat::{FuturesAsyncReadCompatExt, TokioAsyncReadCompatExt}; use tracing::debug; use ws_stream_tungstenite::WsStream; use notary_server::{ - read_pem_file, run_server, AuthorizationProperties, LoggingProperties, NotarizationProperties, + run_server, AuthorizationProperties, LoggingProperties, NotarizationProperties, NotarizationSessionRequest, NotarizationSessionResponse, NotaryServerProperties, NotarySigningKeyProperties, ServerProperties, TLSProperties, }; -const NOTARY_CA_CERT_PATH: &str = "./fixture/tls/rootCA.crt"; const NOTARY_CA_CERT_BYTES: &[u8] = include_bytes!("../fixture/tls/rootCA.crt"); const MAX_SENT: usize = 1 << 13; const MAX_RECV: usize = 1 << 13; +const API_KEY: &str = "test_api_key_0"; -fn get_server_config(port: u16, tls_enabled: bool) -> NotaryServerProperties { +fn get_server_config(port: u16, tls_enabled: bool, auth_enabled: bool) -> NotaryServerProperties { NotaryServerProperties { server: ServerProperties { name: "tlsnotaryserver.io".to_string(), @@ -62,7 +50,7 @@ fn get_server_config(port: u16, tls_enabled: bool) -> NotaryServerProperties { filter: None, }, authorization: AuthorizationProperties { - enabled: false, + enabled: auth_enabled, whitelist_csv_path: "./fixture/auth/whitelist.csv".to_string(), }, } @@ -72,8 +60,9 @@ async fn setup_config_and_server( sleep_ms: u64, port: u16, tls_enabled: bool, + auth_enabled: bool, ) -> NotaryServerProperties { - let notary_config = get_server_config(port, tls_enabled); + let notary_config = get_server_config(port, tls_enabled, auth_enabled); let _ = tracing_subscriber::fmt::try_init(); @@ -90,179 +79,80 @@ async fn setup_config_and_server( notary_config } -async fn tcp_socket(notary_config: NotaryServerProperties) -> TcpStream { - tokio::net::TcpStream::connect(SocketAddr::new( - IpAddr::V4(notary_config.server.host.parse().unwrap()), - notary_config.server.port, - )) - .await - .unwrap() +fn get_server_root_cert_store() -> TlsClientRootCertStore { + let mut root_store = tls_core::anchors::RootCertStore::empty(); + root_store + .add(&tls_core::key::Certificate(CA_CERT_DER.to_vec())) + .unwrap(); + root_store } -async fn tls_socket(notary_config: NotaryServerProperties) -> TlsStream { - let notary_tcp_socket = tokio::net::TcpStream::connect(SocketAddr::new( - IpAddr::V4(notary_config.server.host.parse().unwrap()), - notary_config.server.port, - )) - .await - .unwrap(); +async fn tcp_prover( + notary_config: NotaryServerProperties, + server_root_store: TlsClientRootCertStore, +) -> Prover { + let notary_client = NotaryClient::builder() + .host(¬ary_config.server.host) + .port(notary_config.server.port) + .max_sent_data(MAX_SENT) + .max_recv_data(MAX_RECV) + // set this to None to turn off TLS + .notary_dns(None) + // set this to None to turn off TLS + .notary_root_cert_store(None) + .server_dns(SERVER_DOMAIN) + .server_root_cert_store(server_root_store) + .build() + .unwrap(); - // Connect to the Notary via TLS-TCP - let mut certificate_file_reader = read_pem_file(NOTARY_CA_CERT_PATH).await.unwrap(); - let mut certificates: Vec = rustls_pemfile::certs(&mut certificate_file_reader) - .unwrap() - .into_iter() - .map(Certificate) - .collect(); - let certificate = certificates.remove(0); - - let mut root_store = RootCertStore::empty(); - root_store.add(&certificate).unwrap(); - - let client_notary_config = ClientConfig::builder() - .with_safe_defaults() - .with_root_certificates(root_store) - .with_no_client_auth(); - - let notary_connector = TlsConnector::from(Arc::new(client_notary_config)); - notary_connector - .connect( - notary_config.server.name.as_str().try_into().unwrap(), - notary_tcp_socket, - ) - .await - .unwrap() + notary_client.setup_tcp_prover().await.unwrap() +} + +async fn tls_prover( + notary_config: NotaryServerProperties, + server_root_store: TlsClientRootCertStore, +) -> Prover { + let mut notary_client_builder = NotaryClient::builder(); + + notary_client_builder + .host(¬ary_config.server.host) + .port(notary_config.server.port) + .max_sent_data(MAX_SENT) + .max_recv_data(MAX_RECV) + .server_dns(SERVER_DOMAIN) + .server_root_cert_store(server_root_store); + + if notary_config.authorization.enabled { + notary_client_builder.api_key(API_KEY); + } + + let notary_client = notary_client_builder.build().unwrap(); + + notary_client.setup_tls_prover().await.unwrap() } #[rstest] -#[case::with_tls( - setup_config_and_server(100, 7048, true), - tls_socket(get_server_config(7048, true)) +#[case::with_tls_and_auth( + tls_prover(setup_config_and_server(100, 7047, true, true).await, get_server_root_cert_store()) +)] +#[case::with_tls_and_no_auth( + tls_prover(setup_config_and_server(100, 7048, true, false).await, get_server_root_cert_store()) )] #[case::without_tls( - setup_config_and_server(100, 7049, false), - tcp_socket(get_server_config(7049, false)) + tcp_prover(setup_config_and_server(100, 7049, false, false).await, get_server_root_cert_store()) )] #[awt] #[tokio::test] -async fn test_tcp_prover( +async fn test_tcp_prover( #[future] #[case] - notary_config: NotaryServerProperties, - #[future] - #[case] - notary_socket: S, + prover: Prover, ) { - let notary_host = notary_config.server.host; - let notary_port = notary_config.server.port; - let http_scheme = if notary_config.tls.enabled { - "https" - } else { - "http" - }; - - // Attach the hyper HTTP client to the notary connection to send request to the /session endpoint to configure notarization and obtain session id - let (mut request_sender, connection) = - hyper::client::conn::handshake(notary_socket).await.unwrap(); - - // Spawn the HTTP task to be run concurrently - let connection_task = tokio::spawn(connection.without_shutdown()); - - // Build the HTTP request to configure notarization - let payload = serde_json::to_string(&NotarizationSessionRequest { - client_type: notary_server::ClientType::Tcp, - max_sent_data: Some(MAX_SENT), - max_recv_data: Some(MAX_RECV), - }) - .unwrap(); - let request = Request::builder() - .uri(format!( - "{http_scheme}://{notary_host}:{notary_port}/session" - )) - .method("POST") - .header("Host", notary_host.clone()) - // Need to specify application/json for axum to parse it as json - .header("Content-Type", "application/json") - .body(Body::from(payload)) - .unwrap(); - - debug!("Sending configuration request"); - - let response = request_sender.send_request(request).await.unwrap(); - - debug!("Sent configuration request"); - - assert!(response.status() == StatusCode::OK); - - debug!("Response OK"); - - // Pretty printing :) - let payload = to_bytes(response.into_body()).await.unwrap().to_vec(); - let notarization_response = - serde_json::from_str::(&String::from_utf8_lossy(&payload)) - .unwrap(); - - debug!("Notarization response: {:?}", notarization_response,); - - // Send notarization request via HTTP, where the underlying TCP connection will be extracted later - let request = Request::builder() - // Need to specify the session_id so that notary server knows the right configuration to use - // as the configuration is set in the previous HTTP call - .uri(format!( - "{http_scheme}://{}:{}/notarize?sessionId={}", - notary_host, - notary_port, - notarization_response.session_id.clone() - )) - .method("GET") - .header("Host", notary_host) - .header("Connection", "Upgrade") - // Need to specify this upgrade header for server to extract tcp connection later - .header("Upgrade", "TCP") - .body(Body::empty()) - .unwrap(); - - debug!("Sending notarization request"); - - let response = request_sender.send_request(request).await.unwrap(); - - debug!("Sent notarization request"); - - assert!(response.status() == StatusCode::SWITCHING_PROTOCOLS); - - debug!("Switched protocol OK"); - - // Claim back the socket after HTTP exchange is done so that client can use it for notarization - let Parts { - io: notary_socket, .. - } = connection_task.await.unwrap().unwrap(); - // Connect to the Server let (client_socket, server_socket) = tokio::io::duplex(2 << 16); let server_task = tokio::spawn(bind_test_server_hyper(server_socket.compat())); - let mut root_store = tls_core::anchors::RootCertStore::empty(); - root_store - .add(&tls_core::key::Certificate(CA_CERT_DER.to_vec())) - .unwrap(); - - // Basic default prover config — use the responded session id from notary server - let prover_config = ProverConfig::builder() - .id(notarization_response.session_id) - .server_dns(SERVER_DOMAIN) - .max_sent_data(MAX_SENT) - .max_recv_data(MAX_RECV) - .root_cert_store(root_store) - .build() - .unwrap(); - - // Bind the Prover to the sockets - let prover = Prover::new(prover_config) - .setup(notary_socket.compat()) - .await - .unwrap(); let (tls_connection, prover_fut) = prover.connect(client_socket.compat()).await.unwrap(); - // Spawn the Prover task to be run concurrently let prover_task = tokio::spawn(prover_fut); @@ -318,7 +208,7 @@ async fn test_tcp_prover( #[tokio::test] async fn test_websocket_prover() { // Notary server configuration setup - let notary_config = setup_config_and_server(100, 7050, true).await; + let notary_config = setup_config_and_server(100, 7050, true, false).await; let notary_host = notary_config.server.host.clone(); let notary_port = notary_config.server.port; diff --git a/tlsn/tlsn-notary-client/src/client.rs b/tlsn/tlsn-notary-client/src/client.rs index 293f95311..584705828 100644 --- a/tlsn/tlsn-notary-client/src/client.rs +++ b/tlsn/tlsn-notary-client/src/client.rs @@ -1,4 +1,6 @@ -//! This module handles setting up prover to connect to notary via TCP or TLS +//! Notary client +//! +//! This module sets up prover to connect to notary via TCP or TLS use eyre::eyre; use http_body_util::{BodyExt as _, Either, Empty, Full}; @@ -35,13 +37,10 @@ pub struct NotaryClient { #[builder(default = "DEFAULT_MAX_RECV_LIMIT")] max_recv_data: usize, /// Root certificate store used for establishing TLS connection with notary - #[builder(setter(strip_option), default = "Some(notary_default_root_store()?)")] + #[builder(default = "Some(notary_default_root_store()?)")] notary_root_cert_store: Option, /// DNS name of notary server used for establishing TLS connection with notary - #[builder( - setter(into, strip_option), - default = "Some(\"tlsnotaryserver.io\".to_string())" - )] + #[builder(setter(into), default = "Some(\"tlsnotaryserver.io\".to_string())")] notary_dns: Option, /// API key used to call notary server endpoints if whitelisting is enabled in notary server #[builder(setter(into, strip_option), default)] @@ -55,6 +54,11 @@ pub struct NotaryClient { } impl NotaryClient { + /// Create a new builder for `NotaryClient`. + pub fn builder() -> NotaryClientBuilder { + NotaryClientBuilder::default() + } + /// Returns a prover that connects to notary via TCP without TLS pub async fn setup_tcp_prover(&self) -> Result, NotaryClientError> { #[cfg(feature = "tracing")] @@ -142,7 +146,10 @@ impl NotaryClient { })?; let mut configuration_request_builder = Request::builder() - .uri(format!("{http_scheme}://{}:{}/session", self.host, self.port)) + .uri(format!( + "{http_scheme}://{}:{}/session", + self.host, self.port + )) .method("POST") .header("Host", &self.host) // Need to specify application/json for axum to parse it as json @@ -213,7 +220,7 @@ impl NotaryClient { ); // Send notarization request via HTTP, where the underlying TCP/TLS connection will be extracted later - let mut notarization_request_builder = Request::builder() + let notarization_request = Request::builder() // Need to specify the session_id so that notary server knows the right configuration to use // as the configuration is set in the previous HTTP call .uri(format!( @@ -224,14 +231,7 @@ impl NotaryClient { .header("Host", &self.host) .header("Connection", "Upgrade") // Need to specify this upgrade header for server to extract TCP/TLS connection later - .header("Upgrade", "TCP"); - - if let Some(api_key) = &self.api_key { - notarization_request_builder = - notarization_request_builder.header("Authorization", api_key); - } - - let notarization_request = notarization_request_builder + .header("Upgrade", "TCP") .body(Either::Right(Empty::::new())) .map_err(|err| { NotaryClientError::NotarizationRequest(format!( diff --git a/tlsn/tlsn-notary-client/src/error.rs b/tlsn/tlsn-notary-client/src/error.rs index 4abc46dde..1b7cd4d92 100644 --- a/tlsn/tlsn-notary-client/src/error.rs +++ b/tlsn/tlsn-notary-client/src/error.rs @@ -1,4 +1,6 @@ -//! This module handles errors that might occur +//! Notary client errors. +//! +//! This module handles errors that might occur during prover setup use derive_builder::UninitializedFieldError; use eyre::Report; diff --git a/tlsn/tlsn-notary-client/src/lib.rs b/tlsn/tlsn-notary-client/src/lib.rs index 6eb142e00..edb1c6b28 100644 --- a/tlsn/tlsn-notary-client/src/lib.rs +++ b/tlsn/tlsn-notary-client/src/lib.rs @@ -1,4 +1,4 @@ -//! The notary client library. +//! Notary client library. //! //! This library contains TLSNotary notary client implementations, which helps to setup prover that //! connects to TLSN notary server via TCP or TLS From c20c7a908cf65ec6d2f7483d448dee0b6edb6f10 Mon Sep 17 00:00:00 2001 From: Christopher Chong Date: Thu, 9 May 2024 19:00:17 +0800 Subject: [PATCH 03/11] Modify examples to use notary client. --- notary-server/Cargo.toml | 4 +- notary-server/tests/integration_test.rs | 5 +- tlsn/Cargo.toml | 1 + tlsn/examples/Cargo.toml | 42 +++---- tlsn/examples/discord/discord_dm.rs | 19 +--- tlsn/examples/src/lib.rs | 140 +++--------------------- tlsn/examples/twitter/twitter_dm.rs | 19 +--- tlsn/tlsn-notary-client/Cargo.toml | 8 +- 8 files changed, 46 insertions(+), 192 deletions(-) diff --git a/notary-server/Cargo.toml b/notary-server/Cargo.toml index b99688156..daccb6cf0 100644 --- a/notary-server/Cargo.toml +++ b/notary-server/Cargo.toml @@ -20,9 +20,7 @@ http-body-util = "0.1" hyper = { version = "1.1", features = ["client", "http1", "server"] } hyper-util = {version = "0.1", features = ["full"]} notify = { version = "6.1.1", default-features = false, features = ["macos_kqueue"] } -opentelemetry = { version = "0.19" } p256 = "0.13" -rstest = "0.18" rustls = { version = "0.21" } rustls-pemfile = { version = "1.0.2" } serde = { version = "1.0.147", features = ["derive"] } @@ -39,7 +37,6 @@ tower = { version = "0.4.12", features = ["make"] } tower-http = { version = "0.5", features = ["cors"] } tower-service = "0.3.2" tracing = "0.1" -tracing-opentelemetry = "0.19" tracing-subscriber = { version = "0.3", features = ["env-filter"] } uuid = { version = "1.4.1", features = ["v4", "fast-rng"] } ws_stream_tungstenite = { version = "0.13.0", features = ["tokio_io"] } @@ -47,6 +44,7 @@ ws_stream_tungstenite = { version = "0.13.0", features = ["tokio_io"] } [dev-dependencies] # specify vendored feature to use statically linked copy of OpenSSL hyper-tls = { version = "0.6.0", features = ["vendored"] } +rstest = "0.18" tlsn-notary-client = { path = "../tlsn/tlsn-notary-client" } tlsn-prover = { path = "../tlsn/tlsn-prover", features = ["tracing"] } tls-server-fixture = { path = "../components/tls/tls-server-fixture" } diff --git a/notary-server/tests/integration_test.rs b/notary-server/tests/integration_test.rs index 3a486c334..a9ef06b0c 100644 --- a/notary-server/tests/integration_test.rs +++ b/notary-server/tests/integration_test.rs @@ -9,10 +9,7 @@ use hyper_util::{ rt::{TokioExecutor, TokioIo}, }; use rstest::rstest; -use std::{ - string::String, - time::Duration, -}; +use std::{string::String, time::Duration}; use tls_core::anchors::RootCertStore as TlsClientRootCertStore; use tls_server_fixture::{bind_test_server_hyper, CA_CERT_DER, SERVER_DOMAIN}; use tlsn_notary_client::client::NotaryClient; diff --git a/tlsn/Cargo.toml b/tlsn/Cargo.toml index 180c197e7..6e2b6498a 100644 --- a/tlsn/Cargo.toml +++ b/tlsn/Cargo.toml @@ -62,6 +62,7 @@ bincode = "1" hex = "0.4" bytes = "1.4" opaque-debug = "0.3" +eyre = "0.6.8" tracing = "0.1" tracing-subscriber = "0.3" diff --git a/tlsn/examples/Cargo.toml b/tlsn/examples/Cargo.toml index ec14c396a..b0131472c 100644 --- a/tlsn/examples/Cargo.toml +++ b/tlsn/examples/Cargo.toml @@ -5,48 +5,36 @@ publish = false version = "0.0.0" [dependencies] -mpz-core.workspace = true -notary-server = {path = "../../notary-server"} tlsn-core.workspace = true -tlsn-prover = {workspace = true, features = ["tracing"]} -tlsn-tls-client.workspace = true -tlsn-tls-core.workspace = true -tlsn-utils.workspace = true +tlsn-notary-client.workspace = true +tlsn-prover = { workspace = true, features = ["tracing"] } tlsn-verifier.workspace = true -elliptic-curve = {version = "0.13.5", features = ["pkcs8"]} -p256 = {workspace = true, features = ["ecdsa"]} -webpki-roots.workspace = true - -async-tls = {version = "0.12", default-features = false, features = [ - "client", -]} -chrono = "0.4" +eyre.workspace = true futures.workspace = true -http-body-util = "0.1" -hyper = {version = "1.1", features = ["client", "http1"]} -hyper-util = {version = "0.1", features = ["full"]} -rustls = {version = "0.21"} -rustls-pemfile = {version = "1.0.2"} -tokio = {workspace = true, features = [ +http-body-util.workspace = true +hyper = { workspace = true, features = ["client", "http1"] } +hyper-util = { workspace = true, features = ["full"] } +p256 = { workspace = true, features = ["ecdsa"] } +tokio = { workspace = true, features = [ "rt", "rt-multi-thread", "macros", "net", "io-std", "fs", -]} -tokio-rustls = {version = "0.24.1"} +] } tokio-util.workspace = true - -dotenv = "0.15.0" -eyre = "0.6.8" -serde = {version = "1.0.147", features = ["derive"]} -serde_json = "1.0" tracing-subscriber.workspace = true tracing.workspace = true +webpki-roots.workspace = true +chrono = "0.4" +dotenv = "0.15.0" +elliptic-curve = { version = "0.13.5", features = ["pkcs8"] } regex = "1.10.3" +serde = { version = "1.0.147", features = ["derive"] } +serde_json = "1.0" [[example]] name = "simple_prover" diff --git a/tlsn/examples/discord/discord_dm.rs b/tlsn/examples/discord/discord_dm.rs index df0d15e27..f2d1eeda6 100644 --- a/tlsn/examples/discord/discord_dm.rs +++ b/tlsn/examples/discord/discord_dm.rs @@ -12,8 +12,6 @@ use tokio::io::AsyncWriteExt as _; use tokio_util::compat::{FuturesAsyncReadCompatExt, TokioAsyncReadCompatExt}; use tracing::debug; -use tlsn_prover::tls::{Prover, ProverConfig}; - // Setting of the application server const SERVER_DOMAIN: &str = "discord.com"; @@ -35,27 +33,16 @@ async fn main() { let auth_token = env::var("AUTHORIZATION").unwrap(); let user_agent = env::var("USER_AGENT").unwrap(); - let (notary_tls_socket, session_id) = request_notarization( + // Create a new prover + let prover = request_notarization( NOTARY_HOST, NOTARY_PORT, Some(NOTARY_MAX_SENT), Some(NOTARY_MAX_RECV), + SERVER_DOMAIN, ) .await; - // Basic default prover config using the session_id returned from /session endpoint just now - let config = ProverConfig::builder() - .id(session_id) - .server_dns(SERVER_DOMAIN) - .build() - .unwrap(); - - // Create a new prover and set up the MPC backend. - let prover = Prover::new(config) - .setup(notary_tls_socket.compat()) - .await - .unwrap(); - let client_socket = tokio::net::TcpStream::connect((SERVER_DOMAIN, 443)) .await .unwrap(); diff --git a/tlsn/examples/src/lib.rs b/tlsn/examples/src/lib.rs index 69730bae3..f265d1c55 100644 --- a/tlsn/examples/src/lib.rs +++ b/tlsn/examples/src/lib.rs @@ -1,17 +1,8 @@ -use std::sync::Arc; - use elliptic_curve::pkcs8::DecodePrivateKey; use futures::{AsyncRead, AsyncWrite}; -use http_body_util::{BodyExt as _, Either, Empty, Full}; -use hyper::{client::conn::http1::Parts, Request, StatusCode}; -use hyper_util::rt::TokioIo; -use notary_server::{ClientType, NotarizationSessionRequest, NotarizationSessionResponse}; -use rustls::{Certificate, ClientConfig, RootCertStore}; +use tlsn_notary_client::client::NotaryClient; +use tlsn_prover::tls::{state::Setup, Prover}; use tlsn_verifier::tls::{Verifier, VerifierConfig}; -use tokio::net::TcpStream; -use tokio_rustls::TlsConnector; -use tokio_util::bytes::Bytes; -use tracing::debug; /// Runs a simple Notary with the provided connection to the Prover. pub async fn run_notary(conn: T) { @@ -38,120 +29,25 @@ pub async fn request_notarization( port: u16, max_sent_data: Option, max_recv_data: Option, -) -> (tokio_rustls::client::TlsStream, String) { - // Connect to the Notary via TLS-TCP - let pem_file = std::str::from_utf8(include_bytes!( - "../../../notary-server/fixture/tls/rootCA.crt" - )) - .unwrap(); - let mut reader = std::io::BufReader::new(pem_file.as_bytes()); - let mut certificates: Vec = rustls_pemfile::certs(&mut reader) - .unwrap() - .into_iter() - .map(Certificate) - .collect(); - let certificate = certificates.remove(0); - - let mut root_store = RootCertStore::empty(); - root_store.add(&certificate).unwrap(); - - let client_notary_config = ClientConfig::builder() - .with_safe_defaults() - .with_root_certificates(root_store) - .with_no_client_auth(); - let notary_connector = TlsConnector::from(Arc::new(client_notary_config)); - - let notary_socket = tokio::net::TcpStream::connect((host, port)).await.unwrap(); - - let notary_tls_socket = notary_connector - // Require the domain name of notary server to be the same as that in the server cert - .connect("tlsnotaryserver.io".try_into().unwrap(), notary_socket) - .await - .unwrap(); - - // Attach the hyper HTTP client to the notary TLS connection to send request to the /session endpoint to configure notarization and obtain session id - let (mut request_sender, connection) = - hyper::client::conn::http1::handshake(TokioIo::new(notary_tls_socket)) - .await - .unwrap(); - - // Spawn the HTTP task to be run concurrently - let connection_task = tokio::spawn(connection.without_shutdown()); - - // Build the HTTP request to configure notarization - let payload = serde_json::to_string(&NotarizationSessionRequest { - client_type: ClientType::Tcp, - max_sent_data, - max_recv_data, - }) - .unwrap(); - - let request = Request::builder() - .uri(format!("https://{host}:{port}/session")) - .method("POST") - .header("Host", host) - // Need to specify application/json for axum to parse it as json - .header("Content-Type", "application/json") - .body(Either::Left(Full::new(Bytes::from(payload)))) - .unwrap(); - - debug!("Sending configuration request"); - - let configuration_response = request_sender.send_request(request).await.unwrap(); - - debug!("Sent configuration request"); - - assert!(configuration_response.status() == StatusCode::OK); - - debug!("Response OK"); - - // Pretty printing :) - let payload = configuration_response - .into_body() - .collect() - .await - .unwrap() - .to_bytes(); - let notarization_response = - serde_json::from_str::(&String::from_utf8_lossy(&payload)) - .unwrap(); - - debug!("Notarization response: {:?}", notarization_response,); - - // Send notarization request via HTTP, where the underlying TCP connection will be extracted later - let request = Request::builder() - // Need to specify the session_id so that notary server knows the right configuration to use - // as the configuration is set in the previous HTTP call - .uri(format!( - "https://{host}:{port}/notarize?sessionId={}", - notarization_response.session_id.clone() - )) - .method("GET") - .header("Host", host) - .header("Connection", "Upgrade") - // Need to specify this upgrade header for server to extract tcp connection later - .header("Upgrade", "TCP") - .body(Either::Right(Empty::::new())) - .unwrap(); - - debug!("Sending notarization request"); - - let response = request_sender.send_request(request).await.unwrap(); + server_dns: &str, +) -> Prover { + let mut notary_client_builder = NotaryClient::builder(); - debug!("Sent notarization request"); + notary_client_builder + .host(host) + .port(port) + .server_dns(server_dns); - assert!(response.status() == StatusCode::SWITCHING_PROTOCOLS); + if let Some(max_sent_data) = max_sent_data { + notary_client_builder.max_sent_data(max_sent_data); + } - debug!("Switched protocol OK"); + if let Some(max_recv_data) = max_recv_data { + notary_client_builder.max_recv_data(max_recv_data); + } - // Claim back the TLS socket after HTTP exchange is done - let Parts { - io: notary_tls_socket, - .. - } = connection_task.await.unwrap().unwrap(); + let notary_client = notary_client_builder.build().unwrap(); - ( - notary_tls_socket.into_inner(), - notarization_response.session_id, - ) + // Setup tls connection to the notary server + notary_client.setup_tls_prover().await.unwrap() } diff --git a/tlsn/examples/twitter/twitter_dm.rs b/tlsn/examples/twitter/twitter_dm.rs index 7560f56ea..334693089 100644 --- a/tlsn/examples/twitter/twitter_dm.rs +++ b/tlsn/examples/twitter/twitter_dm.rs @@ -12,8 +12,6 @@ use tokio::io::AsyncWriteExt as _; use tokio_util::compat::{FuturesAsyncReadCompatExt, TokioAsyncReadCompatExt}; use tracing::debug; -use tlsn_prover::tls::{Prover, ProverConfig}; - // Setting of the application server const SERVER_DOMAIN: &str = "twitter.com"; const ROUTE: &str = "i/api/1.1/dm/conversation"; @@ -38,27 +36,16 @@ async fn main() { let access_token = env::var("ACCESS_TOKEN").unwrap(); let csrf_token = env::var("CSRF_TOKEN").unwrap(); - let (notary_tls_socket, session_id) = request_notarization( + // Create a new prover + let prover = request_notarization( NOTARY_HOST, NOTARY_PORT, Some(NOTARY_MAX_SENT), Some(NOTARY_MAX_RECV), + SERVER_DOMAIN, ) .await; - // Basic default prover config using the session_id returned from /session endpoint just now - let config = ProverConfig::builder() - .id(session_id) - .server_dns(SERVER_DOMAIN) - .build() - .unwrap(); - - // Create a new prover and set up the MPC backend. - let prover = Prover::new(config) - .setup(notary_tls_socket.compat()) - .await - .unwrap(); - let client_socket = tokio::net::TcpStream::connect((SERVER_DOMAIN, 443)) .await .unwrap(); diff --git a/tlsn/tlsn-notary-client/Cargo.toml b/tlsn/tlsn-notary-client/Cargo.toml index 372236c19..5e9d7a2ca 100644 --- a/tlsn/tlsn-notary-client/Cargo.toml +++ b/tlsn/tlsn-notary-client/Cargo.toml @@ -18,6 +18,10 @@ tlsn-common.workspace = true tlsn-prover.workspace = true derive_builder.workspace = true +eyre.workspace = true +http-body-util.workspace = true +hyper = { workspace = true, features = ["client", "http1"] } +hyper-util = { workspace = true, features = ["full"] } thiserror.workspace = true tokio = { workspace = true, features = [ "rt", @@ -31,10 +35,6 @@ tokio-util.workspace = true tracing = { workspace = true, optional = true } webpki-roots.workspace = true -eyre = "0.6.8" -http-body-util = "0.1" -hyper = { version = "1.1", features = ["client", "http1"] } -hyper-util = { version = "0.1", features = ["full"] } rustls = "0.21" rustls-pemfile = "1.0.2" serde = { version = "1.0.147", features = ["derive"] } From f8b0c9499f4d1c99b2f5bdb53afc73e2513610c7 Mon Sep 17 00:00:00 2001 From: Christopher Chong Date: Fri, 24 May 2024 11:04:30 +0800 Subject: [PATCH 04/11] Fix formatting and comments. --- notary-server/tests/integration_test.rs | 4 ++-- tlsn/examples/twitter/twitter_dm.rs | 9 +-------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/notary-server/tests/integration_test.rs b/notary-server/tests/integration_test.rs index a9ef06b0c..b76b8866c 100644 --- a/notary-server/tests/integration_test.rs +++ b/notary-server/tests/integration_test.rs @@ -100,9 +100,9 @@ async fn tcp_prover( .port(notary_config.server.port) .max_sent_data(MAX_SENT) .max_recv_data(MAX_RECV) - // set this to None to turn off TLS + // set this to None to turn off TLS with notary server .notary_dns(None) - // set this to None to turn off TLS + // set this to None to turn off TLS with notary server .notary_root_cert_store(None) .server_dns(SERVER_DOMAIN) .server_root_cert_store(server_root_store) diff --git a/tlsn/examples/twitter/twitter_dm.rs b/tlsn/examples/twitter/twitter_dm.rs index ab6a3a92a..1c3e341d7 100644 --- a/tlsn/examples/twitter/twitter_dm.rs +++ b/tlsn/examples/twitter/twitter_dm.rs @@ -33,14 +33,7 @@ async fn main() { let csrf_token = env::var("CSRF_TOKEN").unwrap(); // Create a new prover - let prover = request_notarization( - NOTARY_HOST, - NOTARY_PORT, - None, - None, - SERVER_DOMAIN, - ) - .await; + let prover = request_notarization(NOTARY_HOST, NOTARY_PORT, None, None, SERVER_DOMAIN).await; let client_socket = tokio::net::TcpStream::connect((SERVER_DOMAIN, 443)) .await From c98328993cab9c0a66815efb6ab2f0f8fac2c5d7 Mon Sep 17 00:00:00 2001 From: Christopher Chong Date: Wed, 29 May 2024 19:48:46 +0800 Subject: [PATCH 05/11] Fix PR comments. --- notary-server/Cargo.toml | 3 +- notary-server/tests/integration_test.rs | 126 ++++++---- tlsn/Cargo.toml | 1 - tlsn/examples/Cargo.toml | 3 +- tlsn/examples/discord/discord_dm.rs | 55 +++- tlsn/examples/src/lib.rs | 42 +--- tlsn/examples/twitter/twitter_dm.rs | 44 +++- tlsn/tlsn-notary-client/Cargo.toml | 8 +- tlsn/tlsn-notary-client/src/client.rs | 317 ++++++++++++------------ tlsn/tlsn-notary-client/src/error.rs | 57 +++-- tlsn/tlsn-notary-client/src/lib.rs | 11 +- 11 files changed, 375 insertions(+), 292 deletions(-) diff --git a/notary-server/Cargo.toml b/notary-server/Cargo.toml index daccb6cf0..14cb5a765 100644 --- a/notary-server/Cargo.toml +++ b/notary-server/Cargo.toml @@ -45,8 +45,9 @@ ws_stream_tungstenite = { version = "0.13.0", features = ["tokio_io"] } # specify vendored feature to use statically linked copy of OpenSSL hyper-tls = { version = "0.6.0", features = ["vendored"] } rstest = "0.18" +tls-server-fixture = { path = "../components/tls/tls-server-fixture" } tlsn-notary-client = { path = "../tlsn/tlsn-notary-client" } tlsn-prover = { path = "../tlsn/tlsn-prover", features = ["tracing"] } -tls-server-fixture = { path = "../components/tls/tls-server-fixture" } +tlsn-tls-client-async = { path = "../components/tls/tls-client-async" } tlsn-tls-core = { path = "../components/tls/tls-core" } tokio-native-tls = { version = "0.3.1", features = ["vendored"] } diff --git a/notary-server/tests/integration_test.rs b/notary-server/tests/integration_test.rs index b76b8866c..6aafb376f 100644 --- a/notary-server/tests/integration_test.rs +++ b/notary-server/tests/integration_test.rs @@ -10,23 +10,30 @@ use hyper_util::{ }; use rstest::rstest; use std::{string::String, time::Duration}; -use tls_core::anchors::RootCertStore as TlsClientRootCertStore; +use tls_client_async::TlsConnection; +use tls_core::{anchors::RootCertStore, key::Certificate}; use tls_server_fixture::{bind_test_server_hyper, CA_CERT_DER, SERVER_DOMAIN}; -use tlsn_notary_client::client::NotaryClient; -use tlsn_prover::tls::{state::Setup, Prover, ProverConfig}; -use tokio_util::compat::{FuturesAsyncReadCompatExt, TokioAsyncReadCompatExt}; +use tlsn_notary_client::{NotaryClient, NotaryConnection}; +use tlsn_prover::tls::{Prover, ProverConfig}; +use tokio::{ + io::{AsyncRead, AsyncWrite}, + net::TcpStream, +}; +use tokio_util::compat::{Compat, FuturesAsyncReadCompatExt, TokioAsyncReadCompatExt}; use tracing::debug; use ws_stream_tungstenite::WsStream; use notary_server::{ - run_server, AuthorizationProperties, LoggingProperties, NotarizationProperties, + read_pem_file, run_server, AuthorizationProperties, LoggingProperties, NotarizationProperties, NotarizationSessionRequest, NotarizationSessionResponse, NotaryServerProperties, NotarySigningKeyProperties, ServerProperties, TLSProperties, }; +const MAX_SENT_DATA: usize = 1 << 13; +const MAX_RECV_DATA: usize = 1 << 13; + +const NOTARY_CA_CERT_PATH: &str = "./fixture/tls/rootCA.crt"; const NOTARY_CA_CERT_BYTES: &[u8] = include_bytes!("../fixture/tls/rootCA.crt"); -const MAX_SENT: usize = 1 << 13; -const MAX_RECV: usize = 1 << 13; const API_KEY: &str = "test_api_key_0"; fn get_server_config(port: u16, tls_enabled: bool, auth_enabled: bool) -> NotaryServerProperties { @@ -83,48 +90,47 @@ async fn setup_config_and_server( notary_config } -fn get_server_root_cert_store() -> TlsClientRootCertStore { - let mut root_store = tls_core::anchors::RootCertStore::empty(); - root_store - .add(&tls_core::key::Certificate(CA_CERT_DER.to_vec())) - .unwrap(); - root_store -} - -async fn tcp_prover( - notary_config: NotaryServerProperties, - server_root_store: TlsClientRootCertStore, -) -> Prover { +async fn tcp_prover(notary_config: NotaryServerProperties) -> (TcpStream, String) { let notary_client = NotaryClient::builder() .host(¬ary_config.server.host) .port(notary_config.server.port) - .max_sent_data(MAX_SENT) - .max_recv_data(MAX_RECV) - // set this to None to turn off TLS with notary server + .max_sent_data(MAX_SENT_DATA) + .max_recv_data(MAX_RECV_DATA) + .root_cert_store(None) .notary_dns(None) - // set this to None to turn off TLS with notary server - .notary_root_cert_store(None) - .server_dns(SERVER_DOMAIN) - .server_root_cert_store(server_root_store) .build() .unwrap(); - notary_client.setup_tcp_prover().await.unwrap() + if let (NotaryConnection::Tcp(notary_socket), session_id) = + notary_client.request_notarization().await.unwrap() + { + (notary_socket, session_id) + } else { + panic!("Invalid notary connection received: TLS"); + } } -async fn tls_prover( - notary_config: NotaryServerProperties, - server_root_store: TlsClientRootCertStore, -) -> Prover { +async fn tls_prover(notary_config: NotaryServerProperties) -> (Compat, String) { + let mut certificate_file_reader = read_pem_file(NOTARY_CA_CERT_PATH).await.unwrap(); + let mut certificates: Vec = rustls_pemfile::certs(&mut certificate_file_reader) + .unwrap() + .into_iter() + .map(Certificate) + .collect(); + let certificate = certificates.remove(0); + + let mut root_cert_store = RootCertStore::empty(); + root_cert_store.add(&certificate).unwrap(); + let mut notary_client_builder = NotaryClient::builder(); notary_client_builder .host(¬ary_config.server.host) .port(notary_config.server.port) - .max_sent_data(MAX_SENT) - .max_recv_data(MAX_RECV) - .server_dns(SERVER_DOMAIN) - .server_root_cert_store(server_root_store); + .max_sent_data(MAX_SENT_DATA) + .max_recv_data(MAX_RECV_DATA) + .root_cert_store(Some(root_cert_store)) + .notary_dns(Some(notary_config.server.name)); if notary_config.authorization.enabled { notary_client_builder.api_key(API_KEY); @@ -132,31 +138,61 @@ async fn tls_prover( let notary_client = notary_client_builder.build().unwrap(); - notary_client.setup_tls_prover().await.unwrap() + if let (NotaryConnection::Tls(notary_socket), session_id) = + notary_client.request_notarization().await.unwrap() + { + (notary_socket, session_id) + } else { + panic!("Invalid notary connection received: TCP"); + } } #[rstest] #[case::with_tls_and_auth( - tls_prover(setup_config_and_server(100, 7047, true, true).await, get_server_root_cert_store()) + tls_prover(setup_config_and_server(100, 7047, true, true).await) )] #[case::with_tls_and_no_auth( - tls_prover(setup_config_and_server(100, 7048, true, false).await, get_server_root_cert_store()) + tls_prover(setup_config_and_server(100, 7048, true, false).await) )] #[case::without_tls( - tcp_prover(setup_config_and_server(100, 7049, false, false).await, get_server_root_cert_store()) + tcp_prover(setup_config_and_server(100, 7049, false, false).await) )] #[awt] #[tokio::test] -async fn test_tcp_prover( +async fn test_tcp_prover( #[future] #[case] - prover: Prover, + requested_notarization: (S, String), ) { + let (notary_socket, session_id) = requested_notarization; + + let mut root_cert_store = RootCertStore::empty(); + root_cert_store + .add(&tls_core::key::Certificate(CA_CERT_DER.to_vec())) + .unwrap(); + + // Prover config using the session_id returned from calling /session endpoint in notary client + let prover_config = ProverConfig::builder() + .id(session_id) + .server_dns(SERVER_DOMAIN) + .max_sent_data(MAX_SENT_DATA) + .max_recv_data(MAX_RECV_DATA) + .root_cert_store(root_cert_store) + .build() + .unwrap(); + + // Create a new prover + let prover = Prover::new(prover_config) + .setup(notary_socket.compat()) + .await + .unwrap(); + // Connect to the Server let (client_socket, server_socket) = tokio::io::duplex(2 << 16); let server_task = tokio::spawn(bind_test_server_hyper(server_socket.compat())); let (tls_connection, prover_fut) = prover.connect(client_socket.compat()).await.unwrap(); + // Spawn the Prover task to be run concurrently let prover_task = tokio::spawn(prover_fut); @@ -235,8 +271,8 @@ async fn test_websocket_prover() { // Build the HTTP request to configure notarization let payload = serde_json::to_string(&NotarizationSessionRequest { client_type: notary_server::ClientType::Websocket, - max_sent_data: Some(MAX_SENT), - max_recv_data: Some(MAX_RECV), + max_sent_data: Some(MAX_SENT_DATA), + max_recv_data: Some(MAX_RECV_DATA), }) .unwrap(); @@ -315,8 +351,8 @@ async fn test_websocket_prover() { .id(notarization_response.session_id) .server_dns(SERVER_DOMAIN) .root_cert_store(root_store) - .max_sent_data(MAX_SENT) - .max_recv_data(MAX_RECV) + .max_sent_data(MAX_SENT_DATA) + .max_recv_data(MAX_RECV_DATA) .build() .unwrap(); diff --git a/tlsn/Cargo.toml b/tlsn/Cargo.toml index 6e2b6498a..180c197e7 100644 --- a/tlsn/Cargo.toml +++ b/tlsn/Cargo.toml @@ -62,7 +62,6 @@ bincode = "1" hex = "0.4" bytes = "1.4" opaque-debug = "0.3" -eyre = "0.6.8" tracing = "0.1" tracing-subscriber = "0.3" diff --git a/tlsn/examples/Cargo.toml b/tlsn/examples/Cargo.toml index b0131472c..f2419ee78 100644 --- a/tlsn/examples/Cargo.toml +++ b/tlsn/examples/Cargo.toml @@ -8,9 +8,9 @@ version = "0.0.0" tlsn-core.workspace = true tlsn-notary-client.workspace = true tlsn-prover = { workspace = true, features = ["tracing"] } +tlsn-tls-core.workspace = true tlsn-verifier.workspace = true -eyre.workspace = true futures.workspace = true http-body-util.workspace = true hyper = { workspace = true, features = ["client", "http1"] } @@ -33,6 +33,7 @@ chrono = "0.4" dotenv = "0.15.0" elliptic-curve = { version = "0.13.5", features = ["pkcs8"] } regex = "1.10.3" +rustls-pemfile = { version = "1.0.2" } serde = { version = "1.0.147", features = ["derive"] } serde_json = "1.0" diff --git a/tlsn/examples/discord/discord_dm.rs b/tlsn/examples/discord/discord_dm.rs index 80d650d8c..a3582ea15 100644 --- a/tlsn/examples/discord/discord_dm.rs +++ b/tlsn/examples/discord/discord_dm.rs @@ -6,8 +6,11 @@ use http_body_util::{BodyExt, Empty}; use hyper::{body::Bytes, Request, StatusCode}; use hyper_util::rt::TokioIo; use std::{env, ops::Range, str}; +use tls_core::anchors::RootCertStore; use tlsn_core::proof::TlsProof; -use tlsn_examples::request_notarization; +use tlsn_examples::parse_cert; +use tlsn_notary_client::{NotaryClient, NotaryConnection}; +use tlsn_prover::tls::{Prover, ProverConfig}; use tokio::io::AsyncWriteExt as _; use tokio_util::compat::{FuturesAsyncReadCompatExt, TokioAsyncReadCompatExt}; use tracing::debug; @@ -19,6 +22,10 @@ const SERVER_DOMAIN: &str = "discord.com"; const NOTARY_HOST: &str = "127.0.0.1"; const NOTARY_PORT: u16 = 7047; +// Setting to enable connecting to local notary server via TLS +const NOTARY_CA_CERT_PATH: &str = "../../../notary-server/fixture/tls/rootCA.crt"; +const NOTARY_DNS: &str = "tlsnotaryserver.io"; + // P/S: If the following limits are increased, please ensure max-transcript-size of // the notary server's config (../../../notary-server) is increased too, where // max-transcript-size = MAX_SENT_DATA + MAX_RECV_DATA @@ -38,16 +45,44 @@ async fn main() { let auth_token = env::var("AUTHORIZATION").unwrap(); let user_agent = env::var("USER_AGENT").unwrap(); - // Create a new prover - let prover = request_notarization( - NOTARY_HOST, - NOTARY_PORT, - Some(MAX_SENT_DATA), - Some(MAX_RECV_DATA), - SERVER_DOMAIN, - ) - .await; + // Setup a client connection to the notary server via TLS + let root_ca_cert = parse_cert(NOTARY_CA_CERT_PATH).await; + let mut root_cert_store = RootCertStore::empty(); + root_cert_store.add(&root_ca_cert).unwrap(); + + let notary_client = NotaryClient::builder() + .host(NOTARY_HOST) + .port(NOTARY_PORT) + .max_sent_data(MAX_SENT_DATA) + .max_recv_data(MAX_RECV_DATA) + .root_cert_store(Some(root_cert_store)) + .notary_dns(Some(NOTARY_DNS.to_string())) + .build() + .unwrap(); + + // Send requests for configuration and notarization to the notary server + let (NotaryConnection::Tls(notary_socket), session_id) = + notary_client.request_notarization().await.unwrap() + else { + panic!("Invalid notary connection received: TCP"); + }; + + // Configure a new prover with the unique session id returned from notary client + let prover_config = ProverConfig::builder() + .id(session_id) + .server_dns(SERVER_DOMAIN) + .max_sent_data(MAX_SENT_DATA) + .max_recv_data(MAX_RECV_DATA) + .build() + .unwrap(); + + // Create a new prover and set up the MPC backend. + let prover = Prover::new(prover_config) + .setup(notary_socket.compat()) + .await + .unwrap(); + // Open a new socket to the application server let client_socket = tokio::net::TcpStream::connect((SERVER_DOMAIN, 443)) .await .unwrap(); diff --git a/tlsn/examples/src/lib.rs b/tlsn/examples/src/lib.rs index f265d1c55..42e43c53c 100644 --- a/tlsn/examples/src/lib.rs +++ b/tlsn/examples/src/lib.rs @@ -1,8 +1,9 @@ use elliptic_curve::pkcs8::DecodePrivateKey; use futures::{AsyncRead, AsyncWrite}; -use tlsn_notary_client::client::NotaryClient; -use tlsn_prover::tls::{state::Setup, Prover}; +use std::io::BufReader; +use tls_core::key::Certificate; use tlsn_verifier::tls::{Verifier, VerifierConfig}; +use tokio::fs::File; /// Runs a simple Notary with the provided connection to the Prover. pub async fn run_notary(conn: T) { @@ -23,31 +24,14 @@ pub async fn run_notary(conn .unwrap(); } -/// Requests notarization from the Notary server. -pub async fn request_notarization( - host: &str, - port: u16, - max_sent_data: Option, - max_recv_data: Option, - server_dns: &str, -) -> Prover { - let mut notary_client_builder = NotaryClient::builder(); - - notary_client_builder - .host(host) - .port(port) - .server_dns(server_dns); - - if let Some(max_sent_data) = max_sent_data { - notary_client_builder.max_sent_data(max_sent_data); - } - - if let Some(max_recv_data) = max_recv_data { - notary_client_builder.max_recv_data(max_recv_data); - } - - let notary_client = notary_client_builder.build().unwrap(); - - // Setup tls connection to the notary server - notary_client.setup_tls_prover().await.unwrap() +/// Parse certificate as tls-core's Certificate struct, so that one can use tls-client's RootCertStore to add the cert +pub async fn parse_cert(file_path: &str) -> Certificate { + let key_file = File::open(file_path).await.unwrap().into_std().await; + let mut certificate_file_reader = BufReader::new(key_file); + let mut certificates: Vec = rustls_pemfile::certs(&mut certificate_file_reader) + .unwrap() + .into_iter() + .map(Certificate) + .collect(); + certificates.remove(0) } diff --git a/tlsn/examples/twitter/twitter_dm.rs b/tlsn/examples/twitter/twitter_dm.rs index 1c3e341d7..3afe0778b 100644 --- a/tlsn/examples/twitter/twitter_dm.rs +++ b/tlsn/examples/twitter/twitter_dm.rs @@ -6,8 +6,11 @@ use http_body_util::{BodyExt, Empty}; use hyper::{body::Bytes, Request, StatusCode}; use hyper_util::rt::TokioIo; use std::{env, str}; +use tls_core::anchors::RootCertStore; use tlsn_core::{commitment::CommitmentKind, proof::TlsProof}; -use tlsn_examples::request_notarization; +use tlsn_examples::parse_cert; +use tlsn_notary_client::{NotaryClient, NotaryConnection}; +use tlsn_prover::tls::{Prover, ProverConfig}; use tokio::io::AsyncWriteExt as _; use tokio_util::compat::{FuturesAsyncReadCompatExt, TokioAsyncReadCompatExt}; use tracing::debug; @@ -21,6 +24,10 @@ const USER_AGENT: &str = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KH const NOTARY_HOST: &str = "127.0.0.1"; const NOTARY_PORT: u16 = 7047; +// Setting to enable connecting to local notary server via TLS +const NOTARY_CA_CERT_PATH: &str = "../../../notary-server/fixture/tls/rootCA.crt"; +const NOTARY_DNS: &str = "tlsnotaryserver.io"; + #[tokio::main] async fn main() { tracing_subscriber::fmt::init(); @@ -32,9 +39,40 @@ async fn main() { let access_token = env::var("ACCESS_TOKEN").unwrap(); let csrf_token = env::var("CSRF_TOKEN").unwrap(); - // Create a new prover - let prover = request_notarization(NOTARY_HOST, NOTARY_PORT, None, None, SERVER_DOMAIN).await; + // Setup a client connection to the notary server via TLS + let root_ca_cert = parse_cert(NOTARY_CA_CERT_PATH).await; + let mut root_cert_store = RootCertStore::empty(); + root_cert_store.add(&root_ca_cert).unwrap(); + + let notary_client = NotaryClient::builder() + .host(NOTARY_HOST) + .port(NOTARY_PORT) + .root_cert_store(Some(root_cert_store)) + .notary_dns(Some(NOTARY_DNS.to_string())) + .build() + .unwrap(); + + // Send requests for configuration and notarization to the notary server + let (NotaryConnection::Tls(notary_socket), session_id) = + notary_client.request_notarization().await.unwrap() + else { + panic!("Invalid notary connection received: TCP"); + }; + + // Configure a new prover with the unique session id returned from notary client + let prover_config = ProverConfig::builder() + .id(session_id) + .server_dns(SERVER_DOMAIN) + .build() + .unwrap(); + + // Create a new prover and set up the MPC backend. + let prover = Prover::new(prover_config) + .setup(notary_socket.compat()) + .await + .unwrap(); + // Open a new socket to the application server let client_socket = tokio::net::TcpStream::connect((SERVER_DOMAIN, 443)) .await .unwrap(); diff --git a/tlsn/tlsn-notary-client/Cargo.toml b/tlsn/tlsn-notary-client/Cargo.toml index 5e9d7a2ca..b1051dd1a 100644 --- a/tlsn/tlsn-notary-client/Cargo.toml +++ b/tlsn/tlsn-notary-client/Cargo.toml @@ -8,17 +8,15 @@ default = ["tracing"] tracing = [ "dep:tracing", "tlsn-common/tracing", - "tlsn-prover/tracing" ] [dependencies] notary-server = { path = "../../notary-server" } tlsn-tls-client.workspace = true +tlsn-tls-client-async.workspace = true tlsn-common.workspace = true -tlsn-prover.workspace = true derive_builder.workspace = true -eyre.workspace = true http-body-util.workspace = true hyper = { workspace = true, features = ["client", "http1"] } hyper-util = { workspace = true, features = ["full"] } @@ -33,10 +31,6 @@ tokio = { workspace = true, features = [ ] } tokio-util.workspace = true tracing = { workspace = true, optional = true } -webpki-roots.workspace = true -rustls = "0.21" -rustls-pemfile = "1.0.2" serde = { version = "1.0.147", features = ["derive"] } serde_json = "1.0" -tokio-rustls = "0.24.1" diff --git a/tlsn/tlsn-notary-client/src/client.rs b/tlsn/tlsn-notary-client/src/client.rs index 584705828..494d5decb 100644 --- a/tlsn/tlsn-notary-client/src/client.rs +++ b/tlsn/tlsn-notary-client/src/client.rs @@ -1,29 +1,40 @@ //! Notary client //! -//! This module sets up prover to connect to notary via TCP or TLS +//! This module sets up connection to notary server via TCP or TLS, and subsequent requests for notarization -use eyre::eyre; use http_body_util::{BodyExt as _, Either, Empty, Full}; use hyper::{client::conn::http1::Parts, Request, StatusCode}; use hyper_util::rt::TokioIo; use notary_server::{ClientType, NotarizationSessionRequest, NotarizationSessionResponse}; -use rustls::{Certificate, ClientConfig, RootCertStore}; use std::sync::Arc; -use tls_client::RootCertStore as TlsClientRootCertStore; +use tls_client::{ClientConfig, ClientConnection, RootCertStore, RustCryptoBackend, ServerName}; +use tls_client_async::{bind_client, TlsConnection}; use tlsn_common::config::{DEFAULT_MAX_RECV_LIMIT, DEFAULT_MAX_SENT_LIMIT}; -use tlsn_prover::tls::{state::Setup, Prover, ProverConfig}; -use tokio::io::{AsyncRead, AsyncWrite}; -use tokio_rustls::TlsConnector; -use tokio_util::{bytes::Bytes, compat::TokioAsyncReadCompatExt}; +use tokio::{ + io::{AsyncRead, AsyncWrite}, + net::TcpStream, +}; +use tokio_util::{ + bytes::Bytes, + compat::{Compat, FuturesAsyncReadCompatExt, TokioAsyncReadCompatExt}, +}; #[cfg(feature = "tracing")] use tracing::debug; -use crate::error::NotaryClientError; +use crate::error::{ClientError, ErrorKind}; -/// Client that setup prover to connect to notary server +/// A Notary connection +pub enum NotaryConnection { + /// Unencrypted TCP connection + Tcp(TcpStream), + /// TLS connection wrapped in Compat that implement tokio's AsyncRead, AsyncWrite + Tls(Compat), +} + +/// Client that setup connection to notary server #[derive(Debug, Clone, derive_builder::Builder)] -#[builder(build_fn(error = "NotaryClientError"))] +#[builder(build_fn(error = "ClientError"))] pub struct NotaryClient { /// Host of the notary server endpoint #[builder(setter(into))] @@ -36,21 +47,13 @@ pub struct NotaryClient { /// Maximum number of bytes that can be received. #[builder(default = "DEFAULT_MAX_RECV_LIMIT")] max_recv_data: usize, - /// Root certificate store used for establishing TLS connection with notary - #[builder(default = "Some(notary_default_root_store()?)")] - notary_root_cert_store: Option, - /// DNS name of notary server used for establishing TLS connection with notary - #[builder(setter(into), default = "Some(\"tlsnotaryserver.io\".to_string())")] + /// Root certificate store used for establishing TLS connection with notary server (should be None if TLS is not used) + root_cert_store: Option, + /// Notary server DNS name (should be None if TLS is not used) notary_dns: Option, /// API key used to call notary server endpoints if whitelisting is enabled in notary server #[builder(setter(into, strip_option), default)] api_key: Option, - /// TLS root certificate store - #[builder(default = "server_default_root_store()")] - server_root_cert_store: TlsClientRootCertStore, - /// Application server DNS name - #[builder(setter(into))] - server_dns: String, } impl NotaryClient { @@ -59,61 +62,67 @@ impl NotaryClient { NotaryClientBuilder::default() } - /// Returns a prover that connects to notary via TCP without TLS - pub async fn setup_tcp_prover(&self) -> Result, NotaryClientError> { - #[cfg(feature = "tracing")] - debug!("Setting up tcp connection..."); - let notary_socket = tokio::net::TcpStream::connect((self.host.as_str(), self.port)) - .await - .map_err(|err| NotaryClientError::Connection(err.to_string()))?; - - self.request_notarization(notary_socket).await - } - - /// Returns a prover that connects to notary via TCP-TLS - pub async fn setup_tls_prover(&self) -> Result, NotaryClientError> { - #[cfg(feature = "tracing")] - debug!("Setting up tls connection..."); - let notary_root_cert_store = - self.notary_root_cert_store - .clone() - .ok_or(NotaryClientError::TlsSetup( - "Notary root cert store is not provided".to_string(), - ))?; - let notary_dns = self.notary_dns.as_ref().ok_or(NotaryClientError::TlsSetup( - "Notary dns is not provided".to_string(), - ))?; - - let notary_socket = tokio::net::TcpStream::connect((self.host.as_str(), self.port)) - .await - .map_err(|err| NotaryClientError::Connection(err.to_string()))?; - - let client_notary_config = ClientConfig::builder() - .with_safe_defaults() - .with_root_certificates(notary_root_cert_store) - .with_no_client_auth(); - - let notary_connector = TlsConnector::from(Arc::new(client_notary_config)); - let notary_tls_socket = notary_connector - .connect( - notary_dns.as_str().try_into().map_err(|err| { - NotaryClientError::TlsSetup(format!("Failed to parse notary dns: {err}")) - })?, - notary_socket, + /// Configures and requests for a notarization, returning a connection to the Notary if successful. + pub async fn request_notarization(&self) -> Result<(NotaryConnection, String), ClientError> { + if let (Some(notary_dns), Some(root_cert_store)) = + (self.notary_dns.as_ref(), self.root_cert_store.clone()) + { + #[cfg(feature = "tracing")] + debug!("Setting up tls connection..."); + + // Uses tls-client's TLS setup methods to accept tls-client's RootCertStore, so that if needed + // one can just use a single RootCertStore type for both server and notary's TLS setup, instead of + // a different RootCertStore (e.g. rustls's) just for notary (server is using tls-client's RootCertStore) + let client_notary_config = ClientConfig::builder() + .with_safe_defaults() + .with_root_certificates(root_cert_store) + .with_no_client_auth(); + + let client_connection = ClientConnection::new( + Arc::new(client_notary_config), + Box::new(RustCryptoBackend::new()), + ServerName::try_from(notary_dns.as_str()).unwrap(), ) - .await .map_err(|err| { - NotaryClientError::TlsSetup(format!("Failed to connect to notary via TLS: {err}")) + ClientError::new( + ErrorKind::TlsSetup, + Some("Failed to setup tls client connection".to_string()), + Some(Box::new(err)), + ) })?; - self.request_notarization(notary_tls_socket).await + let notary_socket = tokio::net::TcpStream::connect((self.host.as_str(), self.port)) + .await + .map_err(|err| { + ClientError::new(ErrorKind::Connection, None, Some(Box::new(err))) + })?; + let (tls_conn, tls_fut) = bind_client(notary_socket.compat(), client_connection); + tokio::spawn(tls_fut); + + self.send_request(tls_conn.compat()) + .await + .map(|(connection, session_id)| (NotaryConnection::Tls(connection), session_id)) + } else { + #[cfg(feature = "tracing")] + debug!("Setting up tcp connection..."); + + let notary_socket = tokio::net::TcpStream::connect((self.host.as_str(), self.port)) + .await + .map_err(|err| { + ClientError::new(ErrorKind::Connection, None, Some(Box::new(err))) + })?; + + self.send_request(notary_socket) + .await + .map(|(connection, session_id)| (NotaryConnection::Tcp(connection), session_id)) + } } - /// Requests notarization from the Notary server. - async fn request_notarization( + /// Send notarization request to the notary server. + async fn send_request( &self, notary_socket: S, - ) -> Result, NotaryClientError> { + ) -> Result<(S, String), ClientError> { let http_scheme = if self.notary_dns.is_some() { "https" } else { @@ -125,9 +134,11 @@ impl NotaryClient { hyper::client::conn::http1::handshake(TokioIo::new(notary_socket)) .await .map_err(|err| { - NotaryClientError::Connection(format!( - "Failed to attach http client to notary socket: {err}" - )) + ClientError::new( + ErrorKind::Connection, + Some("Failed to attach http client to notary socket".to_string()), + Some(Box::new(err)), + ) })?; // Spawn the HTTP task to be run concurrently @@ -140,9 +151,11 @@ impl NotaryClient { max_recv_data: Some(self.max_recv_data), }) .map_err(|err| { - NotaryClientError::Configuration(format!( - "Failed to serialise http request for configuration: {err}" - )) + ClientError::new( + ErrorKind::Configuration, + Some("Failed to serialise http request for configuration".to_string()), + Some(Box::new(err)), + ) })?; let mut configuration_request_builder = Request::builder() @@ -165,9 +178,11 @@ impl NotaryClient { configuration_request_payload, )))) .map_err(|err| { - NotaryClientError::Configuration(format!( - "Failed to build http request for configuration: {err}" - )) + ClientError::new( + ErrorKind::Configuration, + Some("Failed to build http request for configuration".to_string()), + Some(Box::new(err)), + ) })?; #[cfg(feature = "tracing")] @@ -177,19 +192,25 @@ impl NotaryClient { .send_request(configuration_request) .await .map_err(|err| { - NotaryClientError::Configuration(format!( - "Failed to send http request for configuration: {err}" - )) + ClientError::new( + ErrorKind::Configuration, + Some("Failed to send http request for configuration".to_string()), + Some(Box::new(err)), + ) })?; #[cfg(feature = "tracing")] debug!("Sent configuration request"); if configuration_response.status() != StatusCode::OK { - return Err(NotaryClientError::Configuration(format!( - "Configuration response is not OK: {:?}", - configuration_response - ))); + return Err(ClientError::new( + ErrorKind::Configuration, + Some(format!( + "Configuration response is not OK: {:?}", + configuration_response + )), + None, + )); } let configuration_response_payload = configuration_response @@ -197,9 +218,11 @@ impl NotaryClient { .collect() .await .map_err(|err| { - NotaryClientError::Configuration(format!( - "Failed to parse configuration response: {err}" - )) + ClientError::new( + ErrorKind::Configuration, + Some("Failed to parse configuration response".to_string()), + Some(Box::new(err)), + ) })? .to_bytes(); @@ -208,9 +231,11 @@ impl NotaryClient { &configuration_response_payload, )) .map_err(|err| { - NotaryClientError::Configuration(format!( - "Failed to parse configuration response: {err}" - )) + ClientError::new( + ErrorKind::Configuration, + Some("Failed to parse configuration response".to_string()), + Some(Box::new(err)), + ) })?; #[cfg(feature = "tracing")] @@ -234,9 +259,11 @@ impl NotaryClient { .header("Upgrade", "TCP") .body(Either::Right(Empty::::new())) .map_err(|err| { - NotaryClientError::NotarizationRequest(format!( - "Failed to build http request for notarization: {err}" - )) + ClientError::new( + ErrorKind::NotarizationRequest, + Some("Failed to build http request for notarization".to_string()), + Some(Box::new(err)), + ) })?; #[cfg(feature = "tracing")] @@ -246,19 +273,25 @@ impl NotaryClient { .send_request(notarization_request) .await .map_err(|err| { - NotaryClientError::NotarizationRequest(format!( - "Failed to send http request for notarization: {err}" - )) + ClientError::new( + ErrorKind::NotarizationRequest, + Some("Failed to send http request for notarization".to_string()), + Some(Box::new(err)), + ) })?; #[cfg(feature = "tracing")] debug!("Sent notarization request"); if notarization_response.status() != StatusCode::SWITCHING_PROTOCOLS { - return Err(NotaryClientError::NotarizationRequest(format!( - "Notarization response is not SWITCHING_PROTOCOL: {:?}", - notarization_response - ))); + return Err(ClientError::new( + ErrorKind::NotarizationRequest, + Some(format!( + "Notarization response is not SWITCHING_PROTOCOL: {:?}", + notarization_response + )), + None, + )); } // Claim back notary socket after HTTP exchange is done @@ -266,71 +299,27 @@ impl NotaryClient { io: notary_socket, .. } = notary_connection_task .await - .map_err(|err| eyre!("Error when joining notary connection task: {err}"))? .map_err(|err| { - eyre!("Failed to claim back notary socket after HTTP exchange is done: {err}") + ClientError::new( + ErrorKind::Unexpected, + Some("Error when joining notary connection task".to_string()), + Some(Box::new(err)), + ) + })? + .map_err(|err| { + ClientError::new( + ErrorKind::Unexpected, + Some( + "Failed to claim back notary socket after HTTP exchange is done" + .to_string(), + ), + Some(Box::new(err)), + ) })?; - #[cfg(feature = "tracing")] - debug!("Setting up prover..."); - - // Basic default prover config using the session_id returned from /session endpoint just now - let prover_config = ProverConfig::builder() - .id(configuration_response_payload_parsed.session_id) - .server_dns(&self.server_dns) - .max_sent_data(self.max_sent_data) - .max_recv_data(self.max_recv_data) - .root_cert_store(self.server_root_cert_store.clone()) - .build()?; - - // Create a new prover - let prover = Prover::new(prover_config) - .setup(notary_socket.into_inner().compat()) - .await?; - - Ok(prover) - } -} - -/// Default root store using mozilla certs. -fn server_default_root_store() -> TlsClientRootCertStore { - let mut root_store = TlsClientRootCertStore::empty(); - root_store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| { - tls_client::OwnedTrustAnchor::from_subject_spki_name_constraints( - ta.subject.as_ref(), - ta.subject_public_key_info.as_ref(), - ta.name_constraints.as_ref().map(|nc| nc.as_ref()), - ) - })); - - root_store -} - -/// Default root store using self signed certs. -fn notary_default_root_store() -> Result { - let pem_file = std::str::from_utf8(include_bytes!( - "../../../notary-server/fixture/tls/rootCA.crt" - )) - .map_err(|err| { - NotaryClientError::Builder(format!("Failed to parse default root CA cert: {err}")) - })?; - - let mut reader = std::io::BufReader::new(pem_file.as_bytes()); - let mut certificates: Vec = rustls_pemfile::certs(&mut reader) - .map_err(|err| { - NotaryClientError::Builder(format!("Failed to setup default root CA cert: {err}")) - })? - .into_iter() - .map(Certificate) - .collect(); - let certificate = certificates.remove(0); - - let mut root_store = RootCertStore::empty(); - root_store.add(&certificate).map_err(|err| { - NotaryClientError::Builder(format!( - "Fialed to add default root cert to root store: {err}" + Ok(( + notary_socket.into_inner(), + configuration_response_payload_parsed.session_id, )) - })?; - - Ok(root_store) + } } diff --git a/tlsn/tlsn-notary-client/src/error.rs b/tlsn/tlsn-notary-client/src/error.rs index 1b7cd4d92..4cb9104f5 100644 --- a/tlsn/tlsn-notary-client/src/error.rs +++ b/tlsn/tlsn-notary-client/src/error.rs @@ -1,45 +1,48 @@ //! Notary client errors. //! -//! This module handles errors that might occur during prover setup +//! This module handles errors that might occur during connection setup and notarization requests use derive_builder::UninitializedFieldError; -use eyre::Report; -use std::error::Error; -use tlsn_prover::tls::{ProverConfigBuilderError, ProverError}; +use std::{error::Error, fmt}; #[derive(Debug, thiserror::Error)] #[allow(missing_docs)] -pub enum NotaryClientError { - #[error(transparent)] - Unexpected(#[from] Report), - #[error("Failed to build notary client: {0}")] - Builder(String), - #[error("Failed to connect to notary: {0}")] - Connection(String), - #[error("Error occured when setting up TLS to connect to notary: {0}")] - TlsSetup(String), - #[error("Error occurred during configuration: {0}")] - Configuration(String), - #[error("Error occurred during notarization request: {0}")] - NotarizationRequest(String), - #[error("Error occurred during prover setup: {0}")] - ProverSetup(Box), +pub struct ClientError { + kind: ErrorKind, + msg: Option, + #[source] + source: Option>, } -impl From for NotaryClientError { - fn from(error: ProverError) -> Self { - Self::ProverSetup(Box::new(error)) +impl ClientError { + pub(crate) fn new( + kind: ErrorKind, + msg: Option, + source: Option>, + ) -> Self { + Self { kind, msg, source } } } -impl From for NotaryClientError { - fn from(error: ProverConfigBuilderError) -> Self { - Self::ProverSetup(Box::new(error)) +impl fmt::Display for ClientError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "client error: {:?}, msg: {:?}", self.kind, self.msg) } } -impl From for NotaryClientError { +#[derive(Debug)] +#[allow(missing_docs)] +pub(crate) enum ErrorKind { + Unexpected, + Builder, + Connection, + TlsSetup, + Configuration, + NotarizationRequest, +} + +impl From for ClientError { fn from(ufe: UninitializedFieldError) -> Self { - Self::Builder(ufe.to_string()) + ClientError::new(ErrorKind::Builder, None, Some(Box::new(ufe))) } } diff --git a/tlsn/tlsn-notary-client/src/lib.rs b/tlsn/tlsn-notary-client/src/lib.rs index edb1c6b28..b48352889 100644 --- a/tlsn/tlsn-notary-client/src/lib.rs +++ b/tlsn/tlsn-notary-client/src/lib.rs @@ -1,11 +1,14 @@ //! Notary client library. //! -//! This library contains TLSNotary notary client implementations, which helps to setup prover that -//! connects to TLSN notary server via TCP or TLS +//! This library contains TLSNotary notary client implementations, which helps to setup +//! connection to TLSN notary server via TCP or TLS, and subsequent requests for notarization #![deny(missing_docs, unreachable_pub, unused_must_use)] #![deny(clippy::all)] #![forbid(unsafe_code)] -pub mod client; -pub mod error; +mod client; +mod error; + +pub use client::{NotaryClient, NotaryConnection}; +pub use error::ClientError; From 05060d2c07b1effb026723c4ae2839691a6766f0 Mon Sep 17 00:00:00 2001 From: Christopher Chong Date: Wed, 5 Jun 2024 18:38:17 +0800 Subject: [PATCH 06/11] Fix PR comments. --- .github/workflows/ci.yml | 4 + notary-server/Cargo.toml | 1 - notary-server/tests/integration_test.rs | 96 +++++------ tlsn/examples/Cargo.toml | 2 - tlsn/examples/discord/README.md | 16 +- tlsn/examples/discord/discord_dm.rs | 40 +++-- tlsn/examples/src/lib.rs | 15 -- tlsn/examples/twitter/README.md | 16 +- tlsn/examples/twitter/twitter_dm.rs | 36 ++-- tlsn/tlsn-notary-client/Cargo.toml | 4 +- tlsn/tlsn-notary-client/src/client.rs | 211 +++++++++++++++++------- tlsn/tlsn-notary-client/src/error.rs | 2 +- tlsn/tlsn-notary-client/src/lib.rs | 4 +- 13 files changed, 265 insertions(+), 182 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5dc7e8c2d..1e4fa9a39 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,6 +73,10 @@ jobs: - name: "Build" run: cargo build ${{ matrix.release && '--release' }} + - name: Add custom DNS entry to /etc/hosts for notary-server TLS test + if: matrix.package == 'notary-server' + run: echo "127.0.0.1 tlsnotaryserver.io" | sudo tee -a /etc/hosts + - name: "Test ${{ matrix.release && '--release' || '' }} ${{ matrix.all-features && '--all-features' || '' }}" env: RELEASE_OPTION: ${{ matrix.release && '--release' || '' }} diff --git a/notary-server/Cargo.toml b/notary-server/Cargo.toml index 14cb5a765..df542d68d 100644 --- a/notary-server/Cargo.toml +++ b/notary-server/Cargo.toml @@ -48,6 +48,5 @@ rstest = "0.18" tls-server-fixture = { path = "../components/tls/tls-server-fixture" } tlsn-notary-client = { path = "../tlsn/tlsn-notary-client" } tlsn-prover = { path = "../tlsn/tlsn-prover", features = ["tracing"] } -tlsn-tls-client-async = { path = "../components/tls/tls-client-async" } tlsn-tls-core = { path = "../components/tls/tls-core" } tokio-native-tls = { version = "0.3.1", features = ["vendored"] } diff --git a/notary-server/tests/integration_test.rs b/notary-server/tests/integration_test.rs index 6aafb376f..21ce87679 100644 --- a/notary-server/tests/integration_test.rs +++ b/notary-server/tests/integration_test.rs @@ -9,17 +9,13 @@ use hyper_util::{ rt::{TokioExecutor, TokioIo}, }; use rstest::rstest; +use rustls::{Certificate, RootCertStore}; use std::{string::String, time::Duration}; -use tls_client_async::TlsConnection; -use tls_core::{anchors::RootCertStore, key::Certificate}; use tls_server_fixture::{bind_test_server_hyper, CA_CERT_DER, SERVER_DOMAIN}; -use tlsn_notary_client::{NotaryClient, NotaryConnection}; +use tlsn_notary_client::{NotarizationRequest, NotaryClient, NotaryConnection}; use tlsn_prover::tls::{Prover, ProverConfig}; -use tokio::{ - io::{AsyncRead, AsyncWrite}, - net::TcpStream, -}; -use tokio_util::compat::{Compat, FuturesAsyncReadCompatExt, TokioAsyncReadCompatExt}; +use tokio::io::{AsyncRead, AsyncWrite}; +use tokio_util::compat::{FuturesAsyncReadCompatExt, TokioAsyncReadCompatExt}; use tracing::debug; use ws_stream_tungstenite::WsStream; @@ -32,6 +28,8 @@ use notary_server::{ const MAX_SENT_DATA: usize = 1 << 13; const MAX_RECV_DATA: usize = 1 << 13; +const NOTARY_HOST: &str = "127.0.0.1"; +const NOTARY_DNS: &str = "tlsnotaryserver.io"; const NOTARY_CA_CERT_PATH: &str = "./fixture/tls/rootCA.crt"; const NOTARY_CA_CERT_BYTES: &[u8] = include_bytes!("../fixture/tls/rootCA.crt"); const API_KEY: &str = "test_api_key_0"; @@ -39,8 +37,8 @@ const API_KEY: &str = "test_api_key_0"; fn get_server_config(port: u16, tls_enabled: bool, auth_enabled: bool) -> NotaryServerProperties { NotaryServerProperties { server: ServerProperties { - name: "tlsnotaryserver.io".to_string(), - host: "127.0.0.1".to_string(), + name: NOTARY_DNS.to_string(), + host: NOTARY_HOST.to_string(), port, html_info: "example html response".to_string(), }, @@ -90,27 +88,35 @@ async fn setup_config_and_server( notary_config } -async fn tcp_prover(notary_config: NotaryServerProperties) -> (TcpStream, String) { - let notary_client = NotaryClient::builder() +async fn tcp_prover(notary_config: NotaryServerProperties) -> (NotaryConnection, String) { + let mut notary_client_builder = NotaryClient::builder(); + + notary_client_builder .host(¬ary_config.server.host) .port(notary_config.server.port) + .enable_tls(false); + + if notary_config.authorization.enabled { + notary_client_builder.api_key(API_KEY); + } + + let notary_client = notary_client_builder.build().unwrap(); + + let notarization_request = NotarizationRequest::builder() .max_sent_data(MAX_SENT_DATA) .max_recv_data(MAX_RECV_DATA) - .root_cert_store(None) - .notary_dns(None) .build() .unwrap(); - if let (NotaryConnection::Tcp(notary_socket), session_id) = - notary_client.request_notarization().await.unwrap() - { - (notary_socket, session_id) - } else { - panic!("Invalid notary connection received: TLS"); - } + let accepted_request = notary_client + .request_notarization(notarization_request) + .await + .unwrap(); + + (accepted_request.io, accepted_request.id) } -async fn tls_prover(notary_config: NotaryServerProperties) -> (Compat, String) { +async fn tls_prover(notary_config: NotaryServerProperties) -> (NotaryConnection, String) { let mut certificate_file_reader = read_pem_file(NOTARY_CA_CERT_PATH).await.unwrap(); let mut certificates: Vec = rustls_pemfile::certs(&mut certificate_file_reader) .unwrap() @@ -122,39 +128,37 @@ async fn tls_prover(notary_config: NotaryServerProperties) -> (Compat " in /etc/hosts so that +// this test programme can resolve the self-named NOTARY_DNS to NOTARY_HOST IP successfully +#[case::tls_without_auth( + tls_prover(setup_config_and_server(100, 7047, true, false).await) )] -#[case::with_tls_and_no_auth( - tls_prover(setup_config_and_server(100, 7048, true, false).await) +#[case::tcp_with_auth( + tcp_prover(setup_config_and_server(100, 7048, false, true).await) )] -#[case::without_tls( +#[case::tcp_without_auth( tcp_prover(setup_config_and_server(100, 7049, false, false).await) )] #[awt] @@ -166,7 +170,7 @@ async fn test_tcp_prover( ) { let (notary_socket, session_id) = requested_notarization; - let mut root_cert_store = RootCertStore::empty(); + let mut root_cert_store = tls_core::anchors::RootCertStore::empty(); root_cert_store .add(&tls_core::key::Certificate(CA_CERT_DER.to_vec())) .unwrap(); diff --git a/tlsn/examples/Cargo.toml b/tlsn/examples/Cargo.toml index f2419ee78..9db0ca712 100644 --- a/tlsn/examples/Cargo.toml +++ b/tlsn/examples/Cargo.toml @@ -8,7 +8,6 @@ version = "0.0.0" tlsn-core.workspace = true tlsn-notary-client.workspace = true tlsn-prover = { workspace = true, features = ["tracing"] } -tlsn-tls-core.workspace = true tlsn-verifier.workspace = true futures.workspace = true @@ -33,7 +32,6 @@ chrono = "0.4" dotenv = "0.15.0" elliptic-curve = { version = "0.13.5", features = ["pkcs8"] } regex = "1.10.3" -rustls-pemfile = { version = "1.0.2" } serde = { version = "1.0.147", features = ["derive"] } serde_json = "1.0" diff --git a/tlsn/examples/discord/README.md b/tlsn/examples/discord/README.md index 34bce43f0..38bbae23f 100644 --- a/tlsn/examples/discord/README.md +++ b/tlsn/examples/discord/README.md @@ -27,11 +27,17 @@ You can find the `CHANNEL_ID` directly in the url: `https://discord.com/channels/@me/{CHANNEL_ID)` ## Start the notary server -At the root level of this repository, run -```sh -cd notary-server -cargo run --release -``` +1. Edit the notary server [config file](../../../notary-server/config/config.yaml) to turn off TLS so that self-signed certificates can be avoided. + ```yaml + tls: + enabled: false + ... + ``` +2. Run the following at the root level of this repository to start the notary server: + ```shell + cd notary-server + cargo run --release + ``` The notary server will now be running in the background waiting for connections. diff --git a/tlsn/examples/discord/discord_dm.rs b/tlsn/examples/discord/discord_dm.rs index a3582ea15..549346cc8 100644 --- a/tlsn/examples/discord/discord_dm.rs +++ b/tlsn/examples/discord/discord_dm.rs @@ -6,10 +6,8 @@ use http_body_util::{BodyExt, Empty}; use hyper::{body::Bytes, Request, StatusCode}; use hyper_util::rt::TokioIo; use std::{env, ops::Range, str}; -use tls_core::anchors::RootCertStore; use tlsn_core::proof::TlsProof; -use tlsn_examples::parse_cert; -use tlsn_notary_client::{NotaryClient, NotaryConnection}; +use tlsn_notary_client::{Accepted, NotarizationRequest, NotaryClient}; use tlsn_prover::tls::{Prover, ProverConfig}; use tokio::io::AsyncWriteExt as _; use tokio_util::compat::{FuturesAsyncReadCompatExt, TokioAsyncReadCompatExt}; @@ -22,10 +20,6 @@ const SERVER_DOMAIN: &str = "discord.com"; const NOTARY_HOST: &str = "127.0.0.1"; const NOTARY_PORT: u16 = 7047; -// Setting to enable connecting to local notary server via TLS -const NOTARY_CA_CERT_PATH: &str = "../../../notary-server/fixture/tls/rootCA.crt"; -const NOTARY_DNS: &str = "tlsnotaryserver.io"; - // P/S: If the following limits are increased, please ensure max-transcript-size of // the notary server's config (../../../notary-server) is increased too, where // max-transcript-size = MAX_SENT_DATA + MAX_RECV_DATA @@ -45,27 +39,31 @@ async fn main() { let auth_token = env::var("AUTHORIZATION").unwrap(); let user_agent = env::var("USER_AGENT").unwrap(); - // Setup a client connection to the notary server via TLS - let root_ca_cert = parse_cert(NOTARY_CA_CERT_PATH).await; - let mut root_cert_store = RootCertStore::empty(); - root_cert_store.add(&root_ca_cert).unwrap(); - + // Build a client to connect to the notary server let notary_client = NotaryClient::builder() .host(NOTARY_HOST) .port(NOTARY_PORT) + // WARNING: Always use TLS to connect to notary server, except if notary is running locally + // e.g. this example, hence `enable_tls` is set to False (else it always defaults to True) + .enable_tls(false) + .build() + .unwrap(); + + // Send requests for configuration and notarization to the notary server + let notarization_request = NotarizationRequest::builder() .max_sent_data(MAX_SENT_DATA) .max_recv_data(MAX_RECV_DATA) - .root_cert_store(Some(root_cert_store)) - .notary_dns(Some(NOTARY_DNS.to_string())) .build() .unwrap(); - // Send requests for configuration and notarization to the notary server - let (NotaryConnection::Tls(notary_socket), session_id) = - notary_client.request_notarization().await.unwrap() - else { - panic!("Invalid notary connection received: TCP"); - }; + let Accepted { + io: notary_connection, + id: session_id, + .. + } = notary_client + .request_notarization(notarization_request) + .await + .unwrap(); // Configure a new prover with the unique session id returned from notary client let prover_config = ProverConfig::builder() @@ -78,7 +76,7 @@ async fn main() { // Create a new prover and set up the MPC backend. let prover = Prover::new(prover_config) - .setup(notary_socket.compat()) + .setup(notary_connection.compat()) .await .unwrap(); diff --git a/tlsn/examples/src/lib.rs b/tlsn/examples/src/lib.rs index 42e43c53c..1e46d79f5 100644 --- a/tlsn/examples/src/lib.rs +++ b/tlsn/examples/src/lib.rs @@ -1,9 +1,6 @@ use elliptic_curve::pkcs8::DecodePrivateKey; use futures::{AsyncRead, AsyncWrite}; -use std::io::BufReader; -use tls_core::key::Certificate; use tlsn_verifier::tls::{Verifier, VerifierConfig}; -use tokio::fs::File; /// Runs a simple Notary with the provided connection to the Prover. pub async fn run_notary(conn: T) { @@ -23,15 +20,3 @@ pub async fn run_notary(conn .await .unwrap(); } - -/// Parse certificate as tls-core's Certificate struct, so that one can use tls-client's RootCertStore to add the cert -pub async fn parse_cert(file_path: &str) -> Certificate { - let key_file = File::open(file_path).await.unwrap().into_std().await; - let mut certificate_file_reader = BufReader::new(key_file); - let mut certificates: Vec = rustls_pemfile::certs(&mut certificate_file_reader) - .unwrap() - .into_iter() - .map(Certificate) - .collect(); - certificates.remove(0) -} diff --git a/tlsn/examples/twitter/README.md b/tlsn/examples/twitter/README.md index 394895e00..13d1b6710 100644 --- a/tlsn/examples/twitter/README.md +++ b/tlsn/examples/twitter/README.md @@ -27,11 +27,17 @@ Next, open the **Developer Tools**, go to the **Network** tab, and refresh the p ![Screenshot](twitter_dm_browser.png) ## Start the notary server -At the root level of this repository, run -```sh -cd notary-server -cargo run --release -``` +1. Edit the notary server [config file](../../../notary-server/config/config.yaml) to turn off TLS so that self-signed certificates can be avoided. + ```yaml + tls: + enabled: false + ... + ``` +2. Run the following at the root level of this repository to start the notary server: + ```shell + cd notary-server + cargo run --release + ``` The notary server will now be running in the background waiting for connections. diff --git a/tlsn/examples/twitter/twitter_dm.rs b/tlsn/examples/twitter/twitter_dm.rs index 3afe0778b..1796e816f 100644 --- a/tlsn/examples/twitter/twitter_dm.rs +++ b/tlsn/examples/twitter/twitter_dm.rs @@ -6,10 +6,8 @@ use http_body_util::{BodyExt, Empty}; use hyper::{body::Bytes, Request, StatusCode}; use hyper_util::rt::TokioIo; use std::{env, str}; -use tls_core::anchors::RootCertStore; use tlsn_core::{commitment::CommitmentKind, proof::TlsProof}; -use tlsn_examples::parse_cert; -use tlsn_notary_client::{NotaryClient, NotaryConnection}; +use tlsn_notary_client::{Accepted, NotarizationRequest, NotaryClient}; use tlsn_prover::tls::{Prover, ProverConfig}; use tokio::io::AsyncWriteExt as _; use tokio_util::compat::{FuturesAsyncReadCompatExt, TokioAsyncReadCompatExt}; @@ -24,10 +22,6 @@ const USER_AGENT: &str = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KH const NOTARY_HOST: &str = "127.0.0.1"; const NOTARY_PORT: u16 = 7047; -// Setting to enable connecting to local notary server via TLS -const NOTARY_CA_CERT_PATH: &str = "../../../notary-server/fixture/tls/rootCA.crt"; -const NOTARY_DNS: &str = "tlsnotaryserver.io"; - #[tokio::main] async fn main() { tracing_subscriber::fmt::init(); @@ -39,25 +33,27 @@ async fn main() { let access_token = env::var("ACCESS_TOKEN").unwrap(); let csrf_token = env::var("CSRF_TOKEN").unwrap(); - // Setup a client connection to the notary server via TLS - let root_ca_cert = parse_cert(NOTARY_CA_CERT_PATH).await; - let mut root_cert_store = RootCertStore::empty(); - root_cert_store.add(&root_ca_cert).unwrap(); - + // Build a client to connect to the notary server let notary_client = NotaryClient::builder() .host(NOTARY_HOST) .port(NOTARY_PORT) - .root_cert_store(Some(root_cert_store)) - .notary_dns(Some(NOTARY_DNS.to_string())) + // WARNING: Always use TLS to connect to notary server, except if notary is running locally + // e.g. this example, hence `enable_tls` is set to False (else it always defaults to True) + .enable_tls(false) .build() .unwrap(); // Send requests for configuration and notarization to the notary server - let (NotaryConnection::Tls(notary_socket), session_id) = - notary_client.request_notarization().await.unwrap() - else { - panic!("Invalid notary connection received: TCP"); - }; + let notarization_request = NotarizationRequest::builder().build().unwrap(); + + let Accepted { + io: notary_connection, + id: session_id, + .. + } = notary_client + .request_notarization(notarization_request) + .await + .unwrap(); // Configure a new prover with the unique session id returned from notary client let prover_config = ProverConfig::builder() @@ -68,7 +64,7 @@ async fn main() { // Create a new prover and set up the MPC backend. let prover = Prover::new(prover_config) - .setup(notary_socket.compat()) + .setup(notary_connection.compat()) .await .unwrap(); diff --git a/tlsn/tlsn-notary-client/Cargo.toml b/tlsn/tlsn-notary-client/Cargo.toml index b1051dd1a..26e1c155b 100644 --- a/tlsn/tlsn-notary-client/Cargo.toml +++ b/tlsn/tlsn-notary-client/Cargo.toml @@ -12,8 +12,6 @@ tracing = [ [dependencies] notary-server = { path = "../../notary-server" } -tlsn-tls-client.workspace = true -tlsn-tls-client-async.workspace = true tlsn-common.workspace = true derive_builder.workspace = true @@ -34,3 +32,5 @@ tracing = { workspace = true, optional = true } serde = { version = "1.0.147", features = ["derive"] } serde_json = "1.0" +tokio-rustls = "0.24.1" +webpki-roots = "0.26" diff --git a/tlsn/tlsn-notary-client/src/client.rs b/tlsn/tlsn-notary-client/src/client.rs index 494d5decb..2e6318ed9 100644 --- a/tlsn/tlsn-notary-client/src/client.rs +++ b/tlsn/tlsn-notary-client/src/client.rs @@ -1,57 +1,126 @@ //! Notary client //! -//! This module sets up connection to notary server via TCP or TLS, and subsequent requests for notarization +//! This module sets up connection to notary server via TCP or TLS, and subsequent requests for notarization. use http_body_util::{BodyExt as _, Either, Empty, Full}; use hyper::{client::conn::http1::Parts, Request, StatusCode}; use hyper_util::rt::TokioIo; use notary_server::{ClientType, NotarizationSessionRequest, NotarizationSessionResponse}; -use std::sync::Arc; -use tls_client::{ClientConfig, ClientConnection, RootCertStore, RustCryptoBackend, ServerName}; -use tls_client_async::{bind_client, TlsConnection}; +use std::{ + io::Error as IoError, + pin::Pin, + sync::Arc, + task::{Context, Poll}, +}; use tlsn_common::config::{DEFAULT_MAX_RECV_LIMIT, DEFAULT_MAX_SENT_LIMIT}; use tokio::{ - io::{AsyncRead, AsyncWrite}, + io::{AsyncRead, AsyncWrite, ReadBuf}, net::TcpStream, }; -use tokio_util::{ - bytes::Bytes, - compat::{Compat, FuturesAsyncReadCompatExt, TokioAsyncReadCompatExt}, +use tokio_rustls::{ + client::TlsStream, + rustls::{ClientConfig, OwnedTrustAnchor, RootCertStore}, + TlsConnector, }; +use tokio_util::bytes::Bytes; #[cfg(feature = "tracing")] use tracing::debug; use crate::error::{ClientError, ErrorKind}; -/// A Notary connection +/// Parameters used to configure notarization. +#[derive(Debug, Clone, derive_builder::Builder)] +pub struct NotarizationRequest { + /// Maximum number of bytes that can be sent. + #[builder(default = "DEFAULT_MAX_SENT_LIMIT")] + max_sent_data: usize, + /// Maximum number of bytes that can be received. + #[builder(default = "DEFAULT_MAX_RECV_LIMIT")] + max_recv_data: usize, +} + +impl NotarizationRequest { + /// Create a new builder for `NotarizationRequest`. + pub fn builder() -> NotarizationRequestBuilder { + NotarizationRequestBuilder::default() + } +} + +/// An accepted notarization request. +#[derive(Debug)] +#[non_exhaustive] +pub struct Accepted { + /// Session identifier. + pub id: String, + /// Connection to the notary server to be used by a prover. + pub io: NotaryConnection, +} + +/// A notary server connection. +#[derive(Debug)] pub enum NotaryConnection { - /// Unencrypted TCP connection + /// Unencrypted TCP connection. Tcp(TcpStream), - /// TLS connection wrapped in Compat that implement tokio's AsyncRead, AsyncWrite - Tls(Compat), + /// TLS connection. + Tls(TlsStream), +} + +impl AsyncRead for NotaryConnection { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + match self.get_mut() { + NotaryConnection::Tcp(stream) => Pin::new(stream).poll_read(cx, buf), + NotaryConnection::Tls(stream) => Pin::new(stream).poll_read(cx, buf), + } + } } -/// Client that setup connection to notary server +impl AsyncWrite for NotaryConnection { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + match self.get_mut() { + NotaryConnection::Tcp(stream) => Pin::new(stream).poll_write(cx, buf), + NotaryConnection::Tls(stream) => Pin::new(stream).poll_write(cx, buf), + } + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match self.get_mut() { + NotaryConnection::Tcp(stream) => Pin::new(stream).poll_flush(cx), + NotaryConnection::Tls(stream) => Pin::new(stream).poll_flush(cx), + } + } + + fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match self.get_mut() { + NotaryConnection::Tcp(stream) => Pin::new(stream).poll_shutdown(cx), + NotaryConnection::Tls(stream) => Pin::new(stream).poll_shutdown(cx), + } + } +} + +/// Client that setup connection to notary server. #[derive(Debug, Clone, derive_builder::Builder)] -#[builder(build_fn(error = "ClientError"))] pub struct NotaryClient { - /// Host of the notary server endpoint + /// Host of the notary server endpoint, either a DNS name (if TLS is used) or IP address. #[builder(setter(into))] host: String, - /// Port of the notary server endpoint + /// Port of the notary server endpoint. port: u16, - /// Maximum number of bytes that can be sent. - #[builder(default = "DEFAULT_MAX_SENT_LIMIT")] - max_sent_data: usize, - /// Maximum number of bytes that can be received. - #[builder(default = "DEFAULT_MAX_RECV_LIMIT")] - max_recv_data: usize, - /// Root certificate store used for establishing TLS connection with notary server (should be None if TLS is not used) - root_cert_store: Option, - /// Notary server DNS name (should be None if TLS is not used) - notary_dns: Option, - /// API key used to call notary server endpoints if whitelisting is enabled in notary server + /// Flag to turn on/off using TLS with notary server. + #[builder(setter(name = "enable_tls"), default = "true")] + tls: bool, + /// Root certificate store used for establishing TLS connection with notary server. + #[builder(default = "default_root_store()")] + root_cert_store: RootCertStore, + /// API key used to call notary server endpoints if whitelisting is enabled in notary server. #[builder(setter(into, strip_option), default)] api_key: Option, } @@ -62,46 +131,50 @@ impl NotaryClient { NotaryClientBuilder::default() } - /// Configures and requests for a notarization, returning a connection to the Notary if successful. - pub async fn request_notarization(&self) -> Result<(NotaryConnection, String), ClientError> { - if let (Some(notary_dns), Some(root_cert_store)) = - (self.notary_dns.as_ref(), self.root_cert_store.clone()) - { + /// Configures and requests for a notarization, returning a connection to the notary server if successful. + pub async fn request_notarization( + &self, + notarization_request: NotarizationRequest, + ) -> Result { + if self.tls { #[cfg(feature = "tracing")] debug!("Setting up tls connection..."); - // Uses tls-client's TLS setup methods to accept tls-client's RootCertStore, so that if needed - // one can just use a single RootCertStore type for both server and notary's TLS setup, instead of - // a different RootCertStore (e.g. rustls's) just for notary (server is using tls-client's RootCertStore) let client_notary_config = ClientConfig::builder() .with_safe_defaults() - .with_root_certificates(root_cert_store) + .with_root_certificates(self.root_cert_store.clone()) .with_no_client_auth(); - let client_connection = ClientConnection::new( - Arc::new(client_notary_config), - Box::new(RustCryptoBackend::new()), - ServerName::try_from(notary_dns.as_str()).unwrap(), - ) - .map_err(|err| { - ClientError::new( - ErrorKind::TlsSetup, - Some("Failed to setup tls client connection".to_string()), - Some(Box::new(err)), - ) - })?; - let notary_socket = tokio::net::TcpStream::connect((self.host.as_str(), self.port)) .await .map_err(|err| { ClientError::new(ErrorKind::Connection, None, Some(Box::new(err))) })?; - let (tls_conn, tls_fut) = bind_client(notary_socket.compat(), client_connection); - tokio::spawn(tls_fut); - self.send_request(tls_conn.compat()) + let notary_connector = TlsConnector::from(Arc::new(client_notary_config)); + let notary_tls_socket = notary_connector + .connect( + self.host.as_str().try_into().map_err(|err| { + ClientError::new( + ErrorKind::TlsSetup, + Some(format!( + "Failed to parse notary server DNS name: {:?}", + self.host + )), + Some(Box::new(err)), + ) + })?, + notary_socket, + ) + .await + .map_err(|err| ClientError::new(ErrorKind::TlsSetup, None, Some(Box::new(err))))?; + + self.send_request(notary_tls_socket, notarization_request) .await - .map(|(connection, session_id)| (NotaryConnection::Tls(connection), session_id)) + .map(|(connection, session_id)| Accepted { + id: session_id, + io: NotaryConnection::Tls(connection), + }) } else { #[cfg(feature = "tracing")] debug!("Setting up tcp connection..."); @@ -112,9 +185,12 @@ impl NotaryClient { ClientError::new(ErrorKind::Connection, None, Some(Box::new(err))) })?; - self.send_request(notary_socket) + self.send_request(notary_socket, notarization_request) .await - .map(|(connection, session_id)| (NotaryConnection::Tcp(connection), session_id)) + .map(|(connection, session_id)| Accepted { + id: session_id, + io: NotaryConnection::Tcp(connection), + }) } } @@ -122,12 +198,9 @@ impl NotaryClient { async fn send_request( &self, notary_socket: S, + notarization_request: NotarizationRequest, ) -> Result<(S, String), ClientError> { - let http_scheme = if self.notary_dns.is_some() { - "https" - } else { - "http" - }; + let http_scheme = if self.tls { "https" } else { "http" }; // Attach the hyper HTTP client to the notary connection to send request to the /session endpoint to configure notarization and obtain session id let (mut notary_request_sender, notary_connection) = @@ -147,8 +220,8 @@ impl NotaryClient { // Build the HTTP request to configure notarization let configuration_request_payload = serde_json::to_string(&NotarizationSessionRequest { client_type: ClientType::Tcp, - max_sent_data: Some(self.max_sent_data), - max_recv_data: Some(self.max_recv_data), + max_sent_data: Some(notarization_request.max_sent_data), + max_recv_data: Some(notarization_request.max_recv_data), }) .map_err(|err| { ClientError::new( @@ -323,3 +396,17 @@ impl NotaryClient { )) } } + +/// Default root store using mozilla certs. +fn default_root_store() -> RootCertStore { + let mut root_store = RootCertStore::empty(); + root_store.add_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| { + OwnedTrustAnchor::from_subject_spki_name_constraints( + ta.subject.as_ref(), + ta.subject_public_key_info.as_ref(), + ta.name_constraints.as_ref().map(|nc| nc.as_ref()), + ) + })); + + root_store +} diff --git a/tlsn/tlsn-notary-client/src/error.rs b/tlsn/tlsn-notary-client/src/error.rs index 4cb9104f5..eaedd6cd2 100644 --- a/tlsn/tlsn-notary-client/src/error.rs +++ b/tlsn/tlsn-notary-client/src/error.rs @@ -1,6 +1,6 @@ //! Notary client errors. //! -//! This module handles errors that might occur during connection setup and notarization requests +//! This module handles errors that might occur during connection setup and notarization requests. use derive_builder::UninitializedFieldError; use std::{error::Error, fmt}; diff --git a/tlsn/tlsn-notary-client/src/lib.rs b/tlsn/tlsn-notary-client/src/lib.rs index b48352889..42339a372 100644 --- a/tlsn/tlsn-notary-client/src/lib.rs +++ b/tlsn/tlsn-notary-client/src/lib.rs @@ -1,7 +1,7 @@ //! Notary client library. //! //! This library contains TLSNotary notary client implementations, which helps to setup -//! connection to TLSN notary server via TCP or TLS, and subsequent requests for notarization +//! connection to TLSN notary server via TCP or TLS, and subsequent requests for notarization. #![deny(missing_docs, unreachable_pub, unused_must_use)] #![deny(clippy::all)] @@ -10,5 +10,5 @@ mod client; mod error; -pub use client::{NotaryClient, NotaryConnection}; +pub use client::{Accepted, NotarizationRequest, NotaryClient, NotaryConnection}; pub use error::ClientError; From 6988352906c02603a6dd0de7e956eed8d037216e Mon Sep 17 00:00:00 2001 From: Christopher Chong Date: Wed, 5 Jun 2024 18:42:31 +0800 Subject: [PATCH 07/11] Fix formatting. --- tlsn/examples/twitter/twitter_dm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tlsn/examples/twitter/twitter_dm.rs b/tlsn/examples/twitter/twitter_dm.rs index 1796e816f..d31cbc609 100644 --- a/tlsn/examples/twitter/twitter_dm.rs +++ b/tlsn/examples/twitter/twitter_dm.rs @@ -45,7 +45,7 @@ async fn main() { // Send requests for configuration and notarization to the notary server let notarization_request = NotarizationRequest::builder().build().unwrap(); - + let Accepted { io: notary_connection, id: session_id, From fa598dc13e4649fb3bc5fdf1a3930edc111658b7 Mon Sep 17 00:00:00 2001 From: Christopher Chong Date: Mon, 10 Jun 2024 11:33:23 +0800 Subject: [PATCH 08/11] Fix errors, tokio and comments. --- tlsn/tlsn-notary-client/Cargo.toml | 2 +- tlsn/tlsn-notary-client/src/client.rs | 360 ++++++++++++-------------- tlsn/tlsn-notary-client/src/error.rs | 39 ++- 3 files changed, 191 insertions(+), 210 deletions(-) diff --git a/tlsn/tlsn-notary-client/Cargo.toml b/tlsn/tlsn-notary-client/Cargo.toml index 26e1c155b..702778f05 100644 --- a/tlsn/tlsn-notary-client/Cargo.toml +++ b/tlsn/tlsn-notary-client/Cargo.toml @@ -15,6 +15,7 @@ notary-server = { path = "../../notary-server" } tlsn-common.workspace = true derive_builder.workspace = true +futures.workspace = true http-body-util.workspace = true hyper = { workspace = true, features = ["client", "http1"] } hyper-util = { workspace = true, features = ["full"] } @@ -27,7 +28,6 @@ tokio = { workspace = true, features = [ "io-std", "fs", ] } -tokio-util.workspace = true tracing = { workspace = true, optional = true } serde = { version = "1.0.147", features = ["derive"] } diff --git a/tlsn/tlsn-notary-client/src/client.rs b/tlsn/tlsn-notary-client/src/client.rs index 2e6318ed9..f1043bf60 100644 --- a/tlsn/tlsn-notary-client/src/client.rs +++ b/tlsn/tlsn-notary-client/src/client.rs @@ -3,7 +3,7 @@ //! This module sets up connection to notary server via TCP or TLS, and subsequent requests for notarization. use http_body_util::{BodyExt as _, Either, Empty, Full}; -use hyper::{client::conn::http1::Parts, Request, StatusCode}; +use hyper::{body::Bytes, client::conn::http1::Parts, Request, StatusCode}; use hyper_util::rt::TokioIo; use notary_server::{ClientType, NotarizationSessionRequest, NotarizationSessionResponse}; use std::{ @@ -22,7 +22,7 @@ use tokio_rustls::{ rustls::{ClientConfig, OwnedTrustAnchor, RootCertStore}, TlsConnector, }; -use tokio_util::bytes::Bytes; +use tracing::error; #[cfg(feature = "tracing")] use tracing::debug; @@ -67,6 +67,7 @@ pub enum NotaryConnection { } impl AsyncRead for NotaryConnection { + #[inline] fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -80,6 +81,7 @@ impl AsyncRead for NotaryConnection { } impl AsyncWrite for NotaryConnection { + #[inline] fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -91,6 +93,7 @@ impl AsyncWrite for NotaryConnection { } } + #[inline] fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { match self.get_mut() { NotaryConnection::Tcp(stream) => Pin::new(stream).poll_flush(cx), @@ -98,6 +101,7 @@ impl AsyncWrite for NotaryConnection { } } + #[inline] fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { match self.get_mut() { NotaryConnection::Tcp(stream) => Pin::new(stream).poll_shutdown(cx), @@ -113,6 +117,7 @@ pub struct NotaryClient { #[builder(setter(into))] host: String, /// Port of the notary server endpoint. + #[builder(default = "self.default_port()")] port: u16, /// Flag to turn on/off using TLS with notary server. #[builder(setter(name = "enable_tls"), default = "true")] @@ -125,6 +130,19 @@ pub struct NotaryClient { api_key: Option, } +impl NotaryClientBuilder { + // Default setter of port. + fn default_port(&self) -> u16 { + // If port is not specified, set it to 80 if TLS is off, else 443 since TLS is on + // (including when self.tls = None, which means it's set to default (true)) + if let Some(false) = self.tls { + 80 + } else { + 443 + } + } +} + impl NotaryClient { /// Create a new builder for `NotaryClient`. pub fn builder() -> NotaryClientBuilder { @@ -147,27 +165,19 @@ impl NotaryClient { let notary_socket = tokio::net::TcpStream::connect((self.host.as_str(), self.port)) .await - .map_err(|err| { - ClientError::new(ErrorKind::Connection, None, Some(Box::new(err))) - })?; + .map_err(|err| ClientError::new(ErrorKind::Connection, Some(Box::new(err))))?; let notary_connector = TlsConnector::from(Arc::new(client_notary_config)); let notary_tls_socket = notary_connector .connect( self.host.as_str().try_into().map_err(|err| { - ClientError::new( - ErrorKind::TlsSetup, - Some(format!( - "Failed to parse notary server DNS name: {:?}", - self.host - )), - Some(Box::new(err)), - ) + error!("Failed to parse notary server DNS name: {:?}", self.host); + ClientError::new(ErrorKind::TlsSetup, Some(Box::new(err))) })?, notary_socket, ) .await - .map_err(|err| ClientError::new(ErrorKind::TlsSetup, None, Some(Box::new(err))))?; + .map_err(|err| ClientError::new(ErrorKind::TlsSetup, Some(Box::new(err))))?; self.send_request(notary_tls_socket, notarization_request) .await @@ -181,9 +191,7 @@ impl NotaryClient { let notary_socket = tokio::net::TcpStream::connect((self.host.as_str(), self.port)) .await - .map_err(|err| { - ClientError::new(ErrorKind::Connection, None, Some(Box::new(err))) - })?; + .map_err(|err| ClientError::new(ErrorKind::Connection, Some(Box::new(err))))?; self.send_request(notary_socket, notarization_request) .await @@ -207,193 +215,167 @@ impl NotaryClient { hyper::client::conn::http1::handshake(TokioIo::new(notary_socket)) .await .map_err(|err| { - ClientError::new( - ErrorKind::Connection, - Some("Failed to attach http client to notary socket".to_string()), - Some(Box::new(err)), - ) + error!("Failed to attach http client to notary socket"); + ClientError::new(ErrorKind::Connection, Some(Box::new(err))) })?; - // Spawn the HTTP task to be run concurrently - let notary_connection_task = tokio::spawn(notary_connection.without_shutdown()); - - // Build the HTTP request to configure notarization - let configuration_request_payload = serde_json::to_string(&NotarizationSessionRequest { - client_type: ClientType::Tcp, - max_sent_data: Some(notarization_request.max_sent_data), - max_recv_data: Some(notarization_request.max_recv_data), - }) - .map_err(|err| { - ClientError::new( - ErrorKind::Configuration, - Some("Failed to serialise http request for configuration".to_string()), - Some(Box::new(err)), - ) - })?; - - let mut configuration_request_builder = Request::builder() - .uri(format!( - "{http_scheme}://{}:{}/session", - self.host, self.port - )) - .method("POST") - .header("Host", &self.host) - // Need to specify application/json for axum to parse it as json - .header("Content-Type", "application/json"); - - if let Some(api_key) = &self.api_key { - configuration_request_builder = - configuration_request_builder.header("Authorization", api_key); - } - - let configuration_request = configuration_request_builder - .body(Either::Left(Full::new(Bytes::from( - configuration_request_payload, - )))) - .map_err(|err| { - ClientError::new( - ErrorKind::Configuration, - Some("Failed to build http request for configuration".to_string()), - Some(Box::new(err)), - ) + // Create a future to poll the notary connection to completion before extracting the socket + let notary_connection_fut = async { + // Claim back notary socket after HTTP exchange is done + let Parts { + io: notary_socket, .. + } = notary_connection.without_shutdown().await.map_err(|err| { + error!("Failed to claim back notary socket after HTTP exchange is done"); + ClientError::new(ErrorKind::Internal, Some(Box::new(err))) })?; - #[cfg(feature = "tracing")] - debug!("Sending configuration request: {:?}", configuration_request); + Ok(notary_socket) + }; + + // Create a future to send configuration and notarization requests to the notary server using the connection established above + let client_requests_fut = async { + // Build the HTTP request to configure notarization + let configuration_request_payload = + serde_json::to_string(&NotarizationSessionRequest { + client_type: ClientType::Tcp, + max_sent_data: Some(notarization_request.max_sent_data), + max_recv_data: Some(notarization_request.max_recv_data), + }) + .map_err(|err| { + error!("Failed to serialise http request for configuration"); + ClientError::new(ErrorKind::Internal, Some(Box::new(err))) + })?; - let configuration_response = notary_request_sender - .send_request(configuration_request) - .await - .map_err(|err| { - ClientError::new( - ErrorKind::Configuration, - Some("Failed to send http request for configuration".to_string()), - Some(Box::new(err)), - ) - })?; + let mut configuration_request_builder = Request::builder() + .uri(format!( + "{http_scheme}://{}:{}/session", + self.host, self.port + )) + .method("POST") + .header("Host", &self.host) + // Need to specify application/json for axum to parse it as json + .header("Content-Type", "application/json"); + + if let Some(api_key) = &self.api_key { + configuration_request_builder = + configuration_request_builder.header("Authorization", api_key); + } + + let configuration_request = configuration_request_builder + .body(Either::Left(Full::new(Bytes::from( + configuration_request_payload, + )))) + .map_err(|err| { + error!("Failed to build http request for configuration"); + ClientError::new(ErrorKind::Internal, Some(Box::new(err))) + })?; - #[cfg(feature = "tracing")] - debug!("Sent configuration request"); - - if configuration_response.status() != StatusCode::OK { - return Err(ClientError::new( - ErrorKind::Configuration, - Some(format!( - "Configuration response is not OK: {:?}", - configuration_response - )), - None, - )); - } + #[cfg(feature = "tracing")] + debug!("Sending configuration request: {:?}", configuration_request); - let configuration_response_payload = configuration_response - .into_body() - .collect() - .await - .map_err(|err| { - ClientError::new( - ErrorKind::Configuration, - Some("Failed to parse configuration response".to_string()), - Some(Box::new(err)), - ) - })? - .to_bytes(); - - let configuration_response_payload_parsed = - serde_json::from_str::(&String::from_utf8_lossy( - &configuration_response_payload, - )) - .map_err(|err| { - ClientError::new( + let configuration_response = notary_request_sender + .send_request(configuration_request) + .await + .map_err(|err| { + error!("Failed to send http request for configuration"); + ClientError::new(ErrorKind::Http, Some(Box::new(err))) + })?; + + #[cfg(feature = "tracing")] + debug!("Sent configuration request"); + + if configuration_response.status() != StatusCode::OK { + return Err(ClientError::new( ErrorKind::Configuration, - Some("Failed to parse configuration response".to_string()), - Some(Box::new(err)), - ) - })?; + Some( + format!( + "Configuration response status is not OK: {:?}", + configuration_response + ) + .into(), + ), + )); + } - #[cfg(feature = "tracing")] - debug!( - "Configuration response: {:?}", - configuration_response_payload_parsed - ); - - // Send notarization request via HTTP, where the underlying TCP/TLS connection will be extracted later - let notarization_request = Request::builder() - // Need to specify the session_id so that notary server knows the right configuration to use - // as the configuration is set in the previous HTTP call - .uri(format!( - "{http_scheme}://{}:{}/notarize?sessionId={}", - self.host, self.port, &configuration_response_payload_parsed.session_id - )) - .method("GET") - .header("Host", &self.host) - .header("Connection", "Upgrade") - // Need to specify this upgrade header for server to extract TCP/TLS connection later - .header("Upgrade", "TCP") - .body(Either::Right(Empty::::new())) - .map_err(|err| { - ClientError::new( - ErrorKind::NotarizationRequest, - Some("Failed to build http request for notarization".to_string()), - Some(Box::new(err)), - ) - })?; + let configuration_response_payload = configuration_response + .into_body() + .collect() + .await + .map_err(|err| { + error!("Failed to parse configuration response"); + ClientError::new(ErrorKind::Http, Some(Box::new(err))) + })? + .to_bytes(); + + let configuration_response_payload_parsed = + serde_json::from_str::(&String::from_utf8_lossy( + &configuration_response_payload, + )) + .map_err(|err| { + error!("Failed to parse configuration response payload"); + ClientError::new(ErrorKind::Internal, Some(Box::new(err))) + })?; - #[cfg(feature = "tracing")] - debug!("Sending notarization request: {:?}", notarization_request); - - let notarization_response = notary_request_sender - .send_request(notarization_request) - .await - .map_err(|err| { - ClientError::new( - ErrorKind::NotarizationRequest, - Some("Failed to send http request for notarization".to_string()), - Some(Box::new(err)), - ) - })?; + #[cfg(feature = "tracing")] + debug!( + "Configuration response: {:?}", + configuration_response_payload_parsed + ); + + // Send notarization request via HTTP, where the underlying TCP/TLS connection will be extracted later + let notarization_request = Request::builder() + // Need to specify the session_id so that notary server knows the right configuration to use + // as the configuration is set in the previous HTTP call + .uri(format!( + "{http_scheme}://{}:{}/notarize?sessionId={}", + self.host, self.port, &configuration_response_payload_parsed.session_id + )) + .method("GET") + .header("Host", &self.host) + .header("Connection", "Upgrade") + // Need to specify this upgrade header for server to extract TCP/TLS connection later + .header("Upgrade", "TCP") + .body(Either::Right(Empty::::new())) + .map_err(|err| { + error!("Failed to build http request for notarization"); + ClientError::new(ErrorKind::Internal, Some(Box::new(err))) + })?; - #[cfg(feature = "tracing")] - debug!("Sent notarization request"); - - if notarization_response.status() != StatusCode::SWITCHING_PROTOCOLS { - return Err(ClientError::new( - ErrorKind::NotarizationRequest, - Some(format!( - "Notarization response is not SWITCHING_PROTOCOL: {:?}", - notarization_response - )), - None, - )); - } + #[cfg(feature = "tracing")] + debug!("Sending notarization request: {:?}", notarization_request); - // Claim back notary socket after HTTP exchange is done - let Parts { - io: notary_socket, .. - } = notary_connection_task - .await - .map_err(|err| { - ClientError::new( - ErrorKind::Unexpected, - Some("Error when joining notary connection task".to_string()), - Some(Box::new(err)), - ) - })? - .map_err(|err| { - ClientError::new( - ErrorKind::Unexpected, + let notarization_response = notary_request_sender + .send_request(notarization_request) + .await + .map_err(|err| { + error!("Failed to send http request for notarization"); + ClientError::new(ErrorKind::Http, Some(Box::new(err))) + })?; + + #[cfg(feature = "tracing")] + debug!("Sent notarization request"); + + if notarization_response.status() != StatusCode::SWITCHING_PROTOCOLS { + return Err(ClientError::new( + ErrorKind::Internal, Some( - "Failed to claim back notary socket after HTTP exchange is done" - .to_string(), + format!( + "Notarization response status is not SWITCHING_PROTOCOL: {:?}", + notarization_response + ) + .into(), ), - Some(Box::new(err)), - ) - })?; + )); + } + + Ok(configuration_response_payload_parsed.session_id) + }; + + // Poll both futures simultaneously to obtain the resulting socket and session_id + let (notary_socket, session_id) = + futures::try_join!(notary_connection_fut, client_requests_fut)?; - Ok(( - notary_socket.into_inner(), - configuration_response_payload_parsed.session_id, - )) + Ok((notary_socket.into_inner(), session_id)) } } diff --git a/tlsn/tlsn-notary-client/src/error.rs b/tlsn/tlsn-notary-client/src/error.rs index eaedd6cd2..c2d615310 100644 --- a/tlsn/tlsn-notary-client/src/error.rs +++ b/tlsn/tlsn-notary-client/src/error.rs @@ -5,44 +5,43 @@ use derive_builder::UninitializedFieldError; use std::{error::Error, fmt}; +#[derive(Debug)] +#[allow(missing_docs)] +pub(crate) enum ErrorKind { + Internal, + Builder, + Connection, + TlsSetup, + Http, + Configuration, +} + #[derive(Debug, thiserror::Error)] #[allow(missing_docs)] pub struct ClientError { kind: ErrorKind, - msg: Option, #[source] source: Option>, } impl ClientError { - pub(crate) fn new( - kind: ErrorKind, - msg: Option, - source: Option>, - ) -> Self { - Self { kind, msg, source } + pub(crate) fn new(kind: ErrorKind, source: Option>) -> Self { + Self { kind, source } } } impl fmt::Display for ClientError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "client error: {:?}, msg: {:?}", self.kind, self.msg) + write!( + f, + "client error: {:?}, source: {:?}", + self.kind, self.source + ) } } -#[derive(Debug)] -#[allow(missing_docs)] -pub(crate) enum ErrorKind { - Unexpected, - Builder, - Connection, - TlsSetup, - Configuration, - NotarizationRequest, -} - impl From for ClientError { fn from(ufe: UninitializedFieldError) -> Self { - ClientError::new(ErrorKind::Builder, None, Some(Box::new(ufe))) + ClientError::new(ErrorKind::Builder, Some(Box::new(ufe))) } } From 4c9ea954b4197f30ae069d6fd090fa84854bc229 Mon Sep 17 00:00:00 2001 From: Christopher Chong Date: Thu, 13 Jun 2024 11:59:48 +0800 Subject: [PATCH 09/11] Remove optional tracing feature. --- tlsn/tlsn-notary-client/Cargo.toml | 9 +-------- tlsn/tlsn-notary-client/src/client.rs | 12 +----------- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/tlsn/tlsn-notary-client/Cargo.toml b/tlsn/tlsn-notary-client/Cargo.toml index 702778f05..0c86437b4 100644 --- a/tlsn/tlsn-notary-client/Cargo.toml +++ b/tlsn/tlsn-notary-client/Cargo.toml @@ -3,13 +3,6 @@ name = "tlsn-notary-client" version = "0.1.0-alpha.5" edition = "2021" -[features] -default = ["tracing"] -tracing = [ - "dep:tracing", - "tlsn-common/tracing", -] - [dependencies] notary-server = { path = "../../notary-server" } tlsn-common.workspace = true @@ -28,7 +21,7 @@ tokio = { workspace = true, features = [ "io-std", "fs", ] } -tracing = { workspace = true, optional = true } +tracing.workspace = true serde = { version = "1.0.147", features = ["derive"] } serde_json = "1.0" diff --git a/tlsn/tlsn-notary-client/src/client.rs b/tlsn/tlsn-notary-client/src/client.rs index f1043bf60..ac63aacfd 100644 --- a/tlsn/tlsn-notary-client/src/client.rs +++ b/tlsn/tlsn-notary-client/src/client.rs @@ -22,10 +22,7 @@ use tokio_rustls::{ rustls::{ClientConfig, OwnedTrustAnchor, RootCertStore}, TlsConnector, }; -use tracing::error; - -#[cfg(feature = "tracing")] -use tracing::debug; +use tracing::{debug, error}; use crate::error::{ClientError, ErrorKind}; @@ -155,7 +152,6 @@ impl NotaryClient { notarization_request: NotarizationRequest, ) -> Result { if self.tls { - #[cfg(feature = "tracing")] debug!("Setting up tls connection..."); let client_notary_config = ClientConfig::builder() @@ -186,7 +182,6 @@ impl NotaryClient { io: NotaryConnection::Tls(connection), }) } else { - #[cfg(feature = "tracing")] debug!("Setting up tcp connection..."); let notary_socket = tokio::net::TcpStream::connect((self.host.as_str(), self.port)) @@ -270,7 +265,6 @@ impl NotaryClient { ClientError::new(ErrorKind::Internal, Some(Box::new(err))) })?; - #[cfg(feature = "tracing")] debug!("Sending configuration request: {:?}", configuration_request); let configuration_response = notary_request_sender @@ -281,7 +275,6 @@ impl NotaryClient { ClientError::new(ErrorKind::Http, Some(Box::new(err))) })?; - #[cfg(feature = "tracing")] debug!("Sent configuration request"); if configuration_response.status() != StatusCode::OK { @@ -316,7 +309,6 @@ impl NotaryClient { ClientError::new(ErrorKind::Internal, Some(Box::new(err))) })?; - #[cfg(feature = "tracing")] debug!( "Configuration response: {:?}", configuration_response_payload_parsed @@ -341,7 +333,6 @@ impl NotaryClient { ClientError::new(ErrorKind::Internal, Some(Box::new(err))) })?; - #[cfg(feature = "tracing")] debug!("Sending notarization request: {:?}", notarization_request); let notarization_response = notary_request_sender @@ -352,7 +343,6 @@ impl NotaryClient { ClientError::new(ErrorKind::Http, Some(Box::new(err))) })?; - #[cfg(feature = "tracing")] debug!("Sent notarization request"); if notarization_response.status() != StatusCode::SWITCHING_PROTOCOLS { From 0b39fa24f39e76ad9c5275848dfe59fc278afb8c Mon Sep 17 00:00:00 2001 From: Christopher Chong Date: Fri, 14 Jun 2024 11:57:15 +0800 Subject: [PATCH 10/11] Fix comment styles. --- notary-server/tests/integration_test.rs | 12 ++++---- tlsn/examples/discord/discord_dm.rs | 10 +++---- tlsn/examples/twitter/twitter_dm.rs | 10 +++---- tlsn/tlsn-notary-client/src/client.rs | 40 ++++++++++++------------- tlsn/tlsn-notary-client/src/lib.rs | 7 +++-- 5 files changed, 40 insertions(+), 39 deletions(-) diff --git a/notary-server/tests/integration_test.rs b/notary-server/tests/integration_test.rs index 21ce87679..65fda1f63 100644 --- a/notary-server/tests/integration_test.rs +++ b/notary-server/tests/integration_test.rs @@ -150,8 +150,8 @@ async fn tls_prover(notary_config: NotaryServerProperties) -> (NotaryConnection, } #[rstest] -// For `tls_without_auth` test to pass, one need to add " " in /etc/hosts so that -// this test programme can resolve the self-named NOTARY_DNS to NOTARY_HOST IP successfully +// For `tls_without_auth` test to pass, one needs to add " " in /etc/hosts so that +// this test programme can resolve the self-named NOTARY_DNS to NOTARY_HOST IP successfully. #[case::tls_without_auth( tls_prover(setup_config_and_server(100, 7047, true, false).await) )] @@ -175,7 +175,7 @@ async fn test_tcp_prover( .add(&tls_core::key::Certificate(CA_CERT_DER.to_vec())) .unwrap(); - // Prover config using the session_id returned from calling /session endpoint in notary client + // Prover config using the session_id returned from calling /session endpoint in notary client. let prover_config = ProverConfig::builder() .id(session_id) .server_dns(SERVER_DOMAIN) @@ -185,19 +185,19 @@ async fn test_tcp_prover( .build() .unwrap(); - // Create a new prover + // Create a new Prover. let prover = Prover::new(prover_config) .setup(notary_socket.compat()) .await .unwrap(); - // Connect to the Server + // Connect to the Server. let (client_socket, server_socket) = tokio::io::duplex(2 << 16); let server_task = tokio::spawn(bind_test_server_hyper(server_socket.compat())); let (tls_connection, prover_fut) = prover.connect(client_socket.compat()).await.unwrap(); - // Spawn the Prover task to be run concurrently + // Spawn the Prover task to be run concurrently. let prover_task = tokio::spawn(prover_fut); let (mut request_sender, connection) = diff --git a/tlsn/examples/discord/discord_dm.rs b/tlsn/examples/discord/discord_dm.rs index 549346cc8..23271366d 100644 --- a/tlsn/examples/discord/discord_dm.rs +++ b/tlsn/examples/discord/discord_dm.rs @@ -39,17 +39,17 @@ async fn main() { let auth_token = env::var("AUTHORIZATION").unwrap(); let user_agent = env::var("USER_AGENT").unwrap(); - // Build a client to connect to the notary server + // Build a client to connect to the notary server. let notary_client = NotaryClient::builder() .host(NOTARY_HOST) .port(NOTARY_PORT) // WARNING: Always use TLS to connect to notary server, except if notary is running locally - // e.g. this example, hence `enable_tls` is set to False (else it always defaults to True) + // e.g. this example, hence `enable_tls` is set to False (else it always defaults to True). .enable_tls(false) .build() .unwrap(); - // Send requests for configuration and notarization to the notary server + // Send requests for configuration and notarization to the notary server. let notarization_request = NotarizationRequest::builder() .max_sent_data(MAX_SENT_DATA) .max_recv_data(MAX_RECV_DATA) @@ -65,7 +65,7 @@ async fn main() { .await .unwrap(); - // Configure a new prover with the unique session id returned from notary client + // Configure a new prover with the unique session id returned from notary client. let prover_config = ProverConfig::builder() .id(session_id) .server_dns(SERVER_DOMAIN) @@ -80,7 +80,7 @@ async fn main() { .await .unwrap(); - // Open a new socket to the application server + // Open a new socket to the application server. let client_socket = tokio::net::TcpStream::connect((SERVER_DOMAIN, 443)) .await .unwrap(); diff --git a/tlsn/examples/twitter/twitter_dm.rs b/tlsn/examples/twitter/twitter_dm.rs index d31cbc609..2faed796b 100644 --- a/tlsn/examples/twitter/twitter_dm.rs +++ b/tlsn/examples/twitter/twitter_dm.rs @@ -33,17 +33,17 @@ async fn main() { let access_token = env::var("ACCESS_TOKEN").unwrap(); let csrf_token = env::var("CSRF_TOKEN").unwrap(); - // Build a client to connect to the notary server + // Build a client to connect to the notary server. let notary_client = NotaryClient::builder() .host(NOTARY_HOST) .port(NOTARY_PORT) // WARNING: Always use TLS to connect to notary server, except if notary is running locally - // e.g. this example, hence `enable_tls` is set to False (else it always defaults to True) + // e.g. this example, hence `enable_tls` is set to False (else it always defaults to True). .enable_tls(false) .build() .unwrap(); - // Send requests for configuration and notarization to the notary server + // Send requests for configuration and notarization to the notary server. let notarization_request = NotarizationRequest::builder().build().unwrap(); let Accepted { @@ -55,7 +55,7 @@ async fn main() { .await .unwrap(); - // Configure a new prover with the unique session id returned from notary client + // Configure a new prover with the unique session id returned from notary client. let prover_config = ProverConfig::builder() .id(session_id) .server_dns(SERVER_DOMAIN) @@ -68,7 +68,7 @@ async fn main() { .await .unwrap(); - // Open a new socket to the application server + // Open a new socket to the application server. let client_socket = tokio::net::TcpStream::connect((SERVER_DOMAIN, 443)) .await .unwrap(); diff --git a/tlsn/tlsn-notary-client/src/client.rs b/tlsn/tlsn-notary-client/src/client.rs index ac63aacfd..1ff07388d 100644 --- a/tlsn/tlsn-notary-client/src/client.rs +++ b/tlsn/tlsn-notary-client/src/client.rs @@ -1,6 +1,6 @@ -//! Notary client +//! Notary client. //! -//! This module sets up connection to notary server via TCP or TLS, and subsequent requests for notarization. +//! This module sets up connection to notary server via TCP or TLS for subsequent requests for notarization. use http_body_util::{BodyExt as _, Either, Empty, Full}; use hyper::{body::Bytes, client::conn::http1::Parts, Request, StatusCode}; @@ -38,7 +38,7 @@ pub struct NotarizationRequest { } impl NotarizationRequest { - /// Create a new builder for `NotarizationRequest`. + /// Creates a new builder for `NotarizationRequest`. pub fn builder() -> NotarizationRequestBuilder { NotarizationRequestBuilder::default() } @@ -107,7 +107,7 @@ impl AsyncWrite for NotaryConnection { } } -/// Client that setup connection to notary server. +/// Client that sets up connection to notary server. #[derive(Debug, Clone, derive_builder::Builder)] pub struct NotaryClient { /// Host of the notary server endpoint, either a DNS name (if TLS is used) or IP address. @@ -131,7 +131,7 @@ impl NotaryClientBuilder { // Default setter of port. fn default_port(&self) -> u16 { // If port is not specified, set it to 80 if TLS is off, else 443 since TLS is on - // (including when self.tls = None, which means it's set to default (true)) + // (including when self.tls = None, which means it's set to default (true)). if let Some(false) = self.tls { 80 } else { @@ -141,12 +141,12 @@ impl NotaryClientBuilder { } impl NotaryClient { - /// Create a new builder for `NotaryClient`. + /// Creates a new builder for `NotaryClient`. pub fn builder() -> NotaryClientBuilder { NotaryClientBuilder::default() } - /// Configures and requests for a notarization, returning a connection to the notary server if successful. + /// Configures and requests a notarization, returning a connection to the notary server if successful. pub async fn request_notarization( &self, notarization_request: NotarizationRequest, @@ -154,7 +154,7 @@ impl NotaryClient { if self.tls { debug!("Setting up tls connection..."); - let client_notary_config = ClientConfig::builder() + let notary_client_config = ClientConfig::builder() .with_safe_defaults() .with_root_certificates(self.root_cert_store.clone()) .with_no_client_auth(); @@ -163,7 +163,7 @@ impl NotaryClient { .await .map_err(|err| ClientError::new(ErrorKind::Connection, Some(Box::new(err))))?; - let notary_connector = TlsConnector::from(Arc::new(client_notary_config)); + let notary_connector = TlsConnector::from(Arc::new(notary_client_config)); let notary_tls_socket = notary_connector .connect( self.host.as_str().try_into().map_err(|err| { @@ -197,7 +197,7 @@ impl NotaryClient { } } - /// Send notarization request to the notary server. + /// Sends notarization request to the notary server. async fn send_request( &self, notary_socket: S, @@ -205,7 +205,7 @@ impl NotaryClient { ) -> Result<(S, String), ClientError> { let http_scheme = if self.tls { "https" } else { "http" }; - // Attach the hyper HTTP client to the notary connection to send request to the /session endpoint to configure notarization and obtain session id + // Attach the hyper HTTP client to the notary connection to send request to the /session endpoint to configure notarization and obtain session id. let (mut notary_request_sender, notary_connection) = hyper::client::conn::http1::handshake(TokioIo::new(notary_socket)) .await @@ -214,9 +214,9 @@ impl NotaryClient { ClientError::new(ErrorKind::Connection, Some(Box::new(err))) })?; - // Create a future to poll the notary connection to completion before extracting the socket + // Create a future to poll the notary connection to completion before extracting the socket. let notary_connection_fut = async { - // Claim back notary socket after HTTP exchange is done + // Claim back notary socket after HTTP exchange is done. let Parts { io: notary_socket, .. } = notary_connection.without_shutdown().await.map_err(|err| { @@ -227,9 +227,9 @@ impl NotaryClient { Ok(notary_socket) }; - // Create a future to send configuration and notarization requests to the notary server using the connection established above + // Create a future to send configuration and notarization requests to the notary server using the connection established above. let client_requests_fut = async { - // Build the HTTP request to configure notarization + // Build the HTTP request to configure notarization. let configuration_request_payload = serde_json::to_string(&NotarizationSessionRequest { client_type: ClientType::Tcp, @@ -248,7 +248,7 @@ impl NotaryClient { )) .method("POST") .header("Host", &self.host) - // Need to specify application/json for axum to parse it as json + // Need to specify application/json for axum to parse it as json. .header("Content-Type", "application/json"); if let Some(api_key) = &self.api_key { @@ -314,10 +314,10 @@ impl NotaryClient { configuration_response_payload_parsed ); - // Send notarization request via HTTP, where the underlying TCP/TLS connection will be extracted later + // Send notarization request via HTTP, where the underlying TCP/TLS connection will be extracted later. let notarization_request = Request::builder() // Need to specify the session_id so that notary server knows the right configuration to use - // as the configuration is set in the previous HTTP call + // as the configuration is set in the previous HTTP call. .uri(format!( "{http_scheme}://{}:{}/notarize?sessionId={}", self.host, self.port, &configuration_response_payload_parsed.session_id @@ -325,7 +325,7 @@ impl NotaryClient { .method("GET") .header("Host", &self.host) .header("Connection", "Upgrade") - // Need to specify this upgrade header for server to extract TCP/TLS connection later + // Need to specify this upgrade header for server to extract TCP/TLS connection later. .header("Upgrade", "TCP") .body(Either::Right(Empty::::new())) .map_err(|err| { @@ -361,7 +361,7 @@ impl NotaryClient { Ok(configuration_response_payload_parsed.session_id) }; - // Poll both futures simultaneously to obtain the resulting socket and session_id + // Poll both futures simultaneously to obtain the resulting socket and session_id. let (notary_socket, session_id) = futures::try_join!(notary_connection_fut, client_requests_fut)?; diff --git a/tlsn/tlsn-notary-client/src/lib.rs b/tlsn/tlsn-notary-client/src/lib.rs index 42339a372..9444ffce0 100644 --- a/tlsn/tlsn-notary-client/src/lib.rs +++ b/tlsn/tlsn-notary-client/src/lib.rs @@ -1,8 +1,9 @@ //! Notary client library. //! -//! This library contains TLSNotary notary client implementations, which helps to setup -//! connection to TLSN notary server via TCP or TLS, and subsequent requests for notarization. - +//! A notary client's purpose is to establish a connection to the notary server via TCP or TLS, and +//! to configure and request notarization. +//! Note that the actual notarization is not performed by the notary client but by the prover of the +//! TLSNotary protocol. #![deny(missing_docs, unreachable_pub, unused_must_use)] #![deny(clippy::all)] #![forbid(unsafe_code)] From 4450b12578df5c996d27ffd6e844bb5a96f6473b Mon Sep 17 00:00:00 2001 From: Christopher Chong Date: Fri, 14 Jun 2024 16:00:35 +0800 Subject: [PATCH 11/11] Fix incorrect duplex value. --- notary-server/tests/integration_test.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/notary-server/tests/integration_test.rs b/notary-server/tests/integration_test.rs index 65fda1f63..3fb3685ac 100644 --- a/notary-server/tests/integration_test.rs +++ b/notary-server/tests/integration_test.rs @@ -192,7 +192,7 @@ async fn test_tcp_prover( .unwrap(); // Connect to the Server. - let (client_socket, server_socket) = tokio::io::duplex(2 << 16); + let (client_socket, server_socket) = tokio::io::duplex(1 << 16); let server_task = tokio::spawn(bind_test_server_hyper(server_socket.compat())); let (tls_connection, prover_fut) = prover.connect(client_socket.compat()).await.unwrap(); @@ -342,7 +342,7 @@ async fn test_websocket_prover() { let notary_ws_socket = WsStream::new(notary_ws_stream); // Connect to the Server - let (client_socket, server_socket) = tokio::io::duplex(2 << 16); + let (client_socket, server_socket) = tokio::io::duplex(1 << 16); let server_task = tokio::spawn(bind_test_server_hyper(server_socket.compat())); let mut root_store = tls_core::anchors::RootCertStore::empty();