This commit is contained in:
sinu
2025-09-09 14:32:49 -07:00
parent 9d8124ac9d
commit 2dcde0d954
36 changed files with 4276 additions and 724 deletions

1647
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -22,7 +22,10 @@ members = [
"crates/harness/executor",
"crates/harness/runner",
"crates/harness/plot",
"crates/sdk-core",
"crates/pdk",
"crates/tlsn",
"crates/sdk-plugin-test",
]
resolver = "2"
@@ -51,6 +54,7 @@ tlsn-formats = { path = "crates/formats" }
tlsn-hmac-sha256 = { path = "crates/components/hmac-sha256" }
tlsn-key-exchange = { path = "crates/components/key-exchange" }
tlsn-mpc-tls = { path = "crates/mpc-tls" }
tlsn-pdk = { path = "crates/pdk" }
tlsn-server-fixture = { path = "crates/server-fixture/server" }
tlsn-server-fixture-certs = { path = "crates/server-fixture/certs" }
tlsn-tls-backend = { path = "crates/tls/backend" }
@@ -83,6 +87,7 @@ serio = { version = "0.2" }
spansy = { git = "https://github.com/tlsnotary/tlsn-utils", rev = "6168663" }
uid-mux = { version = "0.2" }
websocket-relay = { git = "https://github.com/tlsnotary/tlsn-utils", rev = "6168663" }
futures-plex = { git = "https://github.com/tlsnotary/tlsn-utils", rev = "eb48413" }
aes = { version = "0.8" }
aes-gcm = { version = "0.9" }
@@ -164,3 +169,4 @@ webpki-root-certs = { version = "1.0" }
# Use the patched ws_stream_wasm to fix the issue https://github.com/najamelan/ws_stream_wasm/issues/12#issuecomment-1711902958
ws_stream_wasm = { git = "https://github.com/tlsnotary/ws_stream_wasm", rev = "2ed12aad9f0236e5321f577672f309920b2aef51" }
zeroize = { version = "1.8" }
zerocopy = { version = "0.8" }

View File

@@ -23,6 +23,7 @@ rangeset = { workspace = true, features = ["serde"] }
bimap = { version = "0.6", features = ["serde"] }
blake3 = { workspace = true }
derive_builder = { workspace = true }
hex = { workspace = true, optional = true }
opaque-debug = { workspace = true }
rand = { workspace = true }
@@ -31,15 +32,15 @@ rand_chacha = { workspace = true }
rs_merkle = { workspace = true, features = ["serde"] }
rstest = { workspace = true, optional = true }
serde = { workspace = true }
semver = { workspace = true, features = ["serde"] }
sha2 = { workspace = true }
thiserror = { workspace = true }
tiny-keccak = { workspace = true, features = ["keccak"] }
web-time = { workspace = true }
webpki-roots = { workspace = true }
rustls-webpki = { workspace = true, features = ["ring"] }
rustls-pki-types = { workspace = true }
itybity = { workspace = true }
zeroize = { workspace = true }
zeroize = { workspace = true, features = ["derive"] }
[dev-dependencies]
bincode = { workspace = true }

View File

@@ -1,11 +1,16 @@
//! TLSNotary protocol config and config utilities.
//! Configuration types.
use core::fmt;
use once_cell::sync::Lazy;
use rangeset::ToRangeSet;
use semver::Version;
use serde::{Deserialize, Serialize};
use std::error::Error;
use std::{error::Error, sync::LazyLock};
pub use tlsn_core::webpki::{CertificateDer, PrivateKeyDer, RootCertStore};
use crate::{
connection::ServerName,
transcript::{Direction, Idx, PartialTranscript, Transcript, TranscriptCommitConfig},
webpki::{CertificateDer, PrivateKeyDer, RootCertStore},
};
// Default is 32 bytes to decrypt the TLS protocol messages.
const DEFAULT_MAX_RECV_ONLINE: usize = 32;
@@ -15,12 +20,160 @@ const DEFAULT_MAX_RECV_ONLINE: usize = 32;
const DEFAULT_RECORDS_LIMIT: usize = 256;
// Current version that is running.
static VERSION: Lazy<Version> = Lazy::new(|| {
static VERSION: LazyLock<Version> = LazyLock::new(|| {
Version::parse(env!("CARGO_PKG_VERSION"))
.map_err(|err| ProtocolConfigError::new(ErrorKind::Version, err))
.unwrap()
});
/// Configuration for the prover.
#[derive(Debug, Clone, derive_builder::Builder, Serialize, Deserialize)]
pub struct ProverConfig {
/// The server DNS name.
#[builder(setter(into))]
server_name: ServerName,
/// Protocol configuration to be checked with the verifier.
protocol_config: ProtocolConfig,
/// TLS configuration.
#[builder(default)]
tls_config: TlsConfig,
}
impl ProverConfig {
/// Creates a new builder for `ProverConfig`.
pub fn builder() -> ProverConfigBuilder {
ProverConfigBuilder::default()
}
/// Returns the server DNS name.
pub fn server_name(&self) -> &ServerName {
&self.server_name
}
/// Returns the protocol configuration.
pub fn protocol_config(&self) -> &ProtocolConfig {
&self.protocol_config
}
/// Returns the TLS configuration.
pub fn tls_config(&self) -> &TlsConfig {
&self.tls_config
}
}
/// Configuration for the [`Verifier`](crate::tls::Verifier).
#[allow(missing_docs)]
#[derive(derive_builder::Builder, Serialize, Deserialize)]
#[builder(pattern = "owned")]
pub struct VerifierConfig {
protocol_config_validator: ProtocolConfigValidator,
#[builder(setter(strip_option), default)]
root_store: Option<RootCertStore>,
}
impl std::fmt::Debug for VerifierConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VerifierConfig")
.field("protocol_config_validator", &self.protocol_config_validator)
.finish_non_exhaustive()
}
}
impl VerifierConfig {
/// Creates a new configuration builder.
pub fn builder() -> VerifierConfigBuilder {
VerifierConfigBuilder::default()
}
/// Returns the protocol configuration validator.
pub fn protocol_config_validator(&self) -> &ProtocolConfigValidator {
&self.protocol_config_validator
}
/// Returns the root certificate store.
pub fn root_store(&self) -> Option<&RootCertStore> {
self.root_store.as_ref()
}
}
/// Configuration for the prover's TLS connection.
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct TlsConfig {
/// Root certificates.
root_store: Option<RootCertStore>,
/// Certificate chain and a matching private key for client
/// authentication.
client_auth: Option<(Vec<CertificateDer>, PrivateKeyDer)>,
}
impl TlsConfig {
/// Creates a new builder for `TlsConfig`.
pub fn builder() -> TlsConfigBuilder {
TlsConfigBuilder::default()
}
/// Returns the root certificate store.
pub fn root_store(&self) -> Option<&RootCertStore> {
self.root_store.as_ref()
}
/// Returns a certificate chain and a matching private key for client
/// authentication.
pub fn client_auth(&self) -> &Option<(Vec<CertificateDer>, PrivateKeyDer)> {
&self.client_auth
}
}
/// Builder for [`TlsConfig`].
#[derive(Debug, Default)]
pub struct TlsConfigBuilder {
root_store: Option<RootCertStore>,
client_auth: Option<(Vec<CertificateDer>, PrivateKeyDer)>,
}
impl TlsConfigBuilder {
/// Sets the root certificates to use for verifying the server's
/// certificate.
pub fn root_store(&mut self, store: RootCertStore) -> &mut Self {
self.root_store = Some(store);
self
}
/// Sets a DER-encoded certificate chain and a matching private key for
/// client authentication.
///
/// Often the chain will consist of a single end-entity certificate.
///
/// # Arguments
///
/// * `cert_key` - A tuple containing the certificate chain and the private
/// key.
///
/// - Each certificate in the chain must be in the X.509 format.
/// - The key must be in the ASN.1 format (either PKCS#8 or PKCS#1).
pub fn client_auth(&mut self, cert_key: (Vec<CertificateDer>, PrivateKeyDer)) -> &mut Self {
self.client_auth = Some(cert_key);
self
}
/// Builds the TLS configuration.
pub fn build(self) -> Result<TlsConfig, TlsConfigError> {
Ok(TlsConfig {
root_store: self.root_store,
client_auth: self.client_auth,
})
}
}
/// TLS configuration error.
#[derive(Debug, thiserror::Error)]
#[error(transparent)]
pub struct TlsConfigError(#[from] ErrorRepr);
#[derive(Debug, thiserror::Error)]
#[error("tls config error")]
enum ErrorRepr {}
/// Protocol configuration to be set up initially by prover and verifier.
#[derive(derive_builder::Builder, Clone, Debug, Deserialize, Serialize)]
#[builder(build_fn(validate = "Self::validate"))]
@@ -192,22 +345,22 @@ impl ProtocolConfigValidator {
max_sent_records: Option<usize>,
max_recv_records_online: Option<usize>,
) -> Result<(), ProtocolConfigError> {
if let Some(max_sent_records) = max_sent_records
&& max_sent_records > self.max_sent_records
{
return Err(ProtocolConfigError::max_record_count(format!(
"max_sent_records {} is greater than the configured limit {}",
max_sent_records, self.max_sent_records,
)));
if let Some(max_sent_records) = max_sent_records {
if max_sent_records > self.max_sent_records {
return Err(ProtocolConfigError::max_record_count(format!(
"max_sent_records {} is greater than the configured limit {}",
max_sent_records, self.max_sent_records,
)));
}
}
if let Some(max_recv_records_online) = max_recv_records_online
&& max_recv_records_online > self.max_recv_records_online
{
return Err(ProtocolConfigError::max_record_count(format!(
"max_recv_records_online {} is greater than the configured limit {}",
max_recv_records_online, self.max_recv_records_online,
)));
if let Some(max_recv_records_online) = max_recv_records_online {
if max_recv_records_online > self.max_recv_records_online {
return Err(ProtocolConfigError::max_record_count(format!(
"max_recv_records_online {} is greater than the configured limit {}",
max_recv_records_online, self.max_recv_records_online,
)));
}
}
Ok(())
@@ -309,6 +462,180 @@ enum ErrorKind {
Version,
}
/// Configuration to prove information to the verifier.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProveConfig {
server_identity: bool,
transcript: Option<PartialTranscript>,
transcript_commit: Option<TranscriptCommitConfig>,
}
impl ProveConfig {
/// Creates a new builder.
pub fn builder(transcript: &Transcript) -> ProveConfigBuilder<'_> {
ProveConfigBuilder::new(transcript)
}
/// Returns `true` if the server identity is to be proven.
pub fn server_identity(&self) -> bool {
self.server_identity
}
/// Returns the transcript to be proven.
pub fn transcript(&self) -> Option<&PartialTranscript> {
self.transcript.as_ref()
}
/// Returns the transcript commitment configuration.
pub fn transcript_commit(&self) -> Option<&TranscriptCommitConfig> {
self.transcript_commit.as_ref()
}
}
/// Builder for [`ProveConfig`].
#[derive(Debug)]
pub struct ProveConfigBuilder<'a> {
transcript: &'a Transcript,
server_identity: bool,
reveal_sent: Idx,
reveal_recv: Idx,
transcript_commit: Option<TranscriptCommitConfig>,
}
impl<'a> ProveConfigBuilder<'a> {
/// Creates a new builder.
pub fn new(transcript: &'a Transcript) -> Self {
Self {
transcript,
server_identity: false,
reveal_sent: Idx::default(),
reveal_recv: Idx::default(),
transcript_commit: None,
}
}
/// Proves the server identity.
pub fn server_identity(&mut self) -> &mut Self {
self.server_identity = true;
self
}
/// Configures transcript commitments.
pub fn transcript_commit(&mut self, transcript_commit: TranscriptCommitConfig) -> &mut Self {
self.transcript_commit = Some(transcript_commit);
self
}
/// Reveals the given ranges of the transcript.
pub fn reveal(
&mut self,
direction: Direction,
ranges: &dyn ToRangeSet<usize>,
) -> Result<&mut Self, ProveConfigBuilderError> {
let idx = Idx::new(ranges.to_range_set());
if idx.end() > self.transcript.len_of_direction(direction) {
return Err(ProveConfigBuilderError(
ProveConfigBuilderErrorRepr::IndexOutOfBounds {
direction,
actual: idx.end(),
len: self.transcript.len_of_direction(direction),
},
));
}
match direction {
Direction::Sent => self.reveal_sent.union_mut(&idx),
Direction::Received => self.reveal_recv.union_mut(&idx),
}
Ok(self)
}
/// Reveals the given ranges of the sent data transcript.
pub fn reveal_sent(
&mut self,
ranges: &dyn ToRangeSet<usize>,
) -> Result<&mut Self, ProveConfigBuilderError> {
self.reveal(Direction::Sent, ranges)
}
/// Reveals the given ranges of the received data transcript.
pub fn reveal_recv(
&mut self,
ranges: &dyn ToRangeSet<usize>,
) -> Result<&mut Self, ProveConfigBuilderError> {
self.reveal(Direction::Received, ranges)
}
/// Builds the configuration.
pub fn build(self) -> Result<ProveConfig, ProveConfigBuilderError> {
let transcript = if !self.reveal_sent.is_empty() || !self.reveal_recv.is_empty() {
Some(
self.transcript
.to_partial(self.reveal_sent, self.reveal_recv),
)
} else {
None
};
Ok(ProveConfig {
server_identity: self.server_identity,
transcript,
transcript_commit: self.transcript_commit,
})
}
}
/// Error for [`ProveConfigBuilder`].
#[derive(Debug, thiserror::Error)]
#[error(transparent)]
pub struct ProveConfigBuilderError(#[from] ProveConfigBuilderErrorRepr);
#[derive(Debug, thiserror::Error)]
enum ProveConfigBuilderErrorRepr {
#[error("range is out of bounds of the transcript ({direction}): {actual} > {len}")]
IndexOutOfBounds {
direction: Direction,
actual: usize,
len: usize,
},
}
/// Configuration to verify information from the prover.
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct VerifyConfig {}
impl VerifyConfig {
/// Creates a new builder.
pub fn builder() -> VerifyConfigBuilder {
VerifyConfigBuilder::new()
}
}
/// Builder for [`VerifyConfig`].
#[derive(Debug, Default)]
pub struct VerifyConfigBuilder {}
impl VerifyConfigBuilder {
/// Creates a new builder.
pub fn new() -> Self {
Self {}
}
/// Builds the configuration.
pub fn build(self) -> Result<VerifyConfig, VerifyConfigBuilderError> {
Ok(VerifyConfig {})
}
}
/// Error for [`VerifyConfigBuilder`].
#[derive(Debug, thiserror::Error)]
#[error(transparent)]
pub struct VerifyConfigBuilderError(#[from] VerifyConfigBuilderErrorRepr);
#[derive(Debug, thiserror::Error)]
enum VerifyConfigBuilderErrorRepr {}
#[cfg(test)]
mod test {
use super::*;

View File

@@ -4,6 +4,7 @@
#![deny(clippy::all)]
#![forbid(unsafe_code)]
pub mod config;
pub mod connection;
#[cfg(any(test, feature = "fixtures"))]
pub mod fixtures;
@@ -12,191 +13,15 @@ pub mod merkle;
pub mod transcript;
pub mod webpki;
use rangeset::ToRangeSet;
use serde::{Deserialize, Serialize};
use crate::{
connection::{HandshakeData, ServerName},
transcript::{
Direction, Idx, PartialTranscript, Transcript, TranscriptCommitConfig,
TranscriptCommitRequest, TranscriptCommitment, TranscriptSecret,
PartialTranscript, TranscriptCommitRequest, TranscriptCommitment, TranscriptSecret,
},
};
/// Configuration to prove information to the verifier.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProveConfig {
server_identity: bool,
transcript: Option<PartialTranscript>,
transcript_commit: Option<TranscriptCommitConfig>,
}
impl ProveConfig {
/// Creates a new builder.
pub fn builder(transcript: &Transcript) -> ProveConfigBuilder<'_> {
ProveConfigBuilder::new(transcript)
}
/// Returns `true` if the server identity is to be proven.
pub fn server_identity(&self) -> bool {
self.server_identity
}
/// Returns the transcript to be proven.
pub fn transcript(&self) -> Option<&PartialTranscript> {
self.transcript.as_ref()
}
/// Returns the transcript commitment configuration.
pub fn transcript_commit(&self) -> Option<&TranscriptCommitConfig> {
self.transcript_commit.as_ref()
}
}
/// Builder for [`ProveConfig`].
#[derive(Debug)]
pub struct ProveConfigBuilder<'a> {
transcript: &'a Transcript,
server_identity: bool,
reveal_sent: Idx,
reveal_recv: Idx,
transcript_commit: Option<TranscriptCommitConfig>,
}
impl<'a> ProveConfigBuilder<'a> {
/// Creates a new builder.
pub fn new(transcript: &'a Transcript) -> Self {
Self {
transcript,
server_identity: false,
reveal_sent: Idx::default(),
reveal_recv: Idx::default(),
transcript_commit: None,
}
}
/// Proves the server identity.
pub fn server_identity(&mut self) -> &mut Self {
self.server_identity = true;
self
}
/// Configures transcript commitments.
pub fn transcript_commit(&mut self, transcript_commit: TranscriptCommitConfig) -> &mut Self {
self.transcript_commit = Some(transcript_commit);
self
}
/// Reveals the given ranges of the transcript.
pub fn reveal(
&mut self,
direction: Direction,
ranges: &dyn ToRangeSet<usize>,
) -> Result<&mut Self, ProveConfigBuilderError> {
let idx = Idx::new(ranges.to_range_set());
if idx.end() > self.transcript.len_of_direction(direction) {
return Err(ProveConfigBuilderError(
ProveConfigBuilderErrorRepr::IndexOutOfBounds {
direction,
actual: idx.end(),
len: self.transcript.len_of_direction(direction),
},
));
}
match direction {
Direction::Sent => self.reveal_sent.union_mut(&idx),
Direction::Received => self.reveal_recv.union_mut(&idx),
}
Ok(self)
}
/// Reveals the given ranges of the sent data transcript.
pub fn reveal_sent(
&mut self,
ranges: &dyn ToRangeSet<usize>,
) -> Result<&mut Self, ProveConfigBuilderError> {
self.reveal(Direction::Sent, ranges)
}
/// Reveals the given ranges of the received data transcript.
pub fn reveal_recv(
&mut self,
ranges: &dyn ToRangeSet<usize>,
) -> Result<&mut Self, ProveConfigBuilderError> {
self.reveal(Direction::Received, ranges)
}
/// Builds the configuration.
pub fn build(self) -> Result<ProveConfig, ProveConfigBuilderError> {
let transcript = if !self.reveal_sent.is_empty() || !self.reveal_recv.is_empty() {
Some(
self.transcript
.to_partial(self.reveal_sent, self.reveal_recv),
)
} else {
None
};
Ok(ProveConfig {
server_identity: self.server_identity,
transcript,
transcript_commit: self.transcript_commit,
})
}
}
/// Error for [`ProveConfigBuilder`].
#[derive(Debug, thiserror::Error)]
#[error(transparent)]
pub struct ProveConfigBuilderError(#[from] ProveConfigBuilderErrorRepr);
#[derive(Debug, thiserror::Error)]
enum ProveConfigBuilderErrorRepr {
#[error("range is out of bounds of the transcript ({direction}): {actual} > {len}")]
IndexOutOfBounds {
direction: Direction,
actual: usize,
len: usize,
},
}
/// Configuration to verify information from the prover.
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct VerifyConfig {}
impl VerifyConfig {
/// Creates a new builder.
pub fn builder() -> VerifyConfigBuilder {
VerifyConfigBuilder::new()
}
}
/// Builder for [`VerifyConfig`].
#[derive(Debug, Default)]
pub struct VerifyConfigBuilder {}
impl VerifyConfigBuilder {
/// Creates a new builder.
pub fn new() -> Self {
Self {}
}
/// Builds the configuration.
pub fn build(self) -> Result<VerifyConfig, VerifyConfigBuilderError> {
Ok(VerifyConfig {})
}
}
/// Error for [`VerifyConfigBuilder`].
#[derive(Debug, thiserror::Error)]
#[error(transparent)]
pub struct VerifyConfigBuilderError(#[from] VerifyConfigBuilderErrorRepr);
#[derive(Debug, thiserror::Error)]
enum VerifyConfigBuilderErrorRepr {}
/// Payload sent to the verifier.
#[doc(hidden)]
#[derive(Debug, Serialize, Deserialize)]

View File

@@ -7,6 +7,7 @@ use crate::{
transcript::{Direction, Transcript},
webpki::CertificateDer,
};
use serde::{Deserialize, Serialize};
use tls_core::msgs::{
alert::AlertMessagePayload,
codec::{Codec, Reader},
@@ -15,7 +16,7 @@ use tls_core::msgs::{
};
/// A transcript of TLS records sent and received by the prover.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TlsTranscript {
time: u64,
version: TlsVersion,
@@ -291,7 +292,7 @@ impl TlsTranscript {
}
/// A TLS record.
#[derive(Clone)]
#[derive(Clone, Serialize, Deserialize)]
pub struct Record {
/// Sequence number.
pub seq: u64,

16
crates/pdk/Cargo.toml Normal file
View File

@@ -0,0 +1,16 @@
[package]
name = "tlsn-pdk"
version = "0.1.0"
edition = "2024"
[dependencies]
tlsn-core = { workspace = true }
futures = { workspace = true }
bincode = { workspace = true }
wit-bindgen = "0.44"
getrandom = { version = "0.3" }
getrandom02 = { package = "getrandom", version = "0.2", features = ["custom"] }
[lints]
workspace = true

74
crates/pdk/src/abi.rs Normal file
View File

@@ -0,0 +1,74 @@
mod generated {
wit_bindgen::generate!({
world: "plugin",
path: "../sdk-core/wit/tlsn.wit",
pub_export_macro: true,
});
impl From<std::task::Poll<Result<Vec<u8>, String>>> for PollReturn {
#[inline]
fn from(value: std::task::Poll<Result<Vec<u8>, String>>) -> Self {
match value {
std::task::Poll::Ready(ret) => PollReturn::Ready(ret),
std::task::Poll::Pending => PollReturn::Pending,
}
}
}
unsafe extern "Rust" {
fn __tlsn_entry_trampoline(
arg: Vec<u8>,
) -> ::std::pin::Pin<Box<dyn ::std::future::Future<Output = Result<Vec<u8>, String>>>>;
}
thread_local! {
static MAIN: ::std::cell::RefCell<Option<::std::pin::Pin<Box<dyn ::std::future::Future<Output = Result<Vec<u8>, String>>>>>> = ::std::cell::RefCell::new(None);
}
struct Plugin;
impl Guest for Plugin {
fn start(arg: Vec<u8>) -> () {
MAIN.with_borrow_mut(|fut| {
if fut.is_some() {
panic!("main future already set");
}
*fut = Some(unsafe { __tlsn_entry_trampoline(arg) });
})
}
fn poll() -> PollReturn {
MAIN.with_borrow_mut(|fut| {
let Some(fut) = fut.as_mut() else {
panic!("main future not set, must call start first");
};
let mut cx = ::std::task::Context::from_waker(::std::task::Waker::noop());
fut.as_mut().poll(&mut cx).into()
})
}
}
export!(Plugin);
}
pub(crate) use generated::tlsn::tlsn::*;
#[macro_export]
macro_rules! entry {
($path:path) => {
#[unsafe(no_mangle)]
extern "Rust" fn __tlsn_entry_trampoline(
arg: Vec<u8>,
) -> ::std::pin::Pin<Box<dyn ::std::future::Future<Output = Result<Vec<u8>, String>>>> {
#[inline(always)]
fn assert_async<F>(f: F) -> F
where
F: Future<Output = Result<Vec<u8>, String>>,
{
f
}
Box::pin(assert_async($path(arg)))
}
};
}

21
crates/pdk/src/lib.rs Normal file
View File

@@ -0,0 +1,21 @@
mod abi;
pub mod prover;
pub mod verifier;
pub use tlsn_core::{config, connection, webpki};
#[unsafe(no_mangle)]
unsafe extern "Rust" fn __getrandom_v03_custom(
dest: *mut u8,
len: usize,
) -> Result<(), getrandom::Error> {
Err(getrandom::Error::UNSUPPORTED)
}
fn always_fail(buf: &mut [u8]) -> Result<(), getrandom02::Error> {
let code = core::num::NonZeroU32::new(1).unwrap();
Err(getrandom02::Error::from(code))
}
getrandom02::register_custom_getrandom!(always_fail);

257
crates/pdk/src/prover.rs Normal file
View File

@@ -0,0 +1,257 @@
use std::{
future::poll_fn,
io,
pin::Pin,
task::{Context, Poll},
};
use futures::{AsyncRead, AsyncWrite};
use tlsn_core::transcript::{TlsTranscript, Transcript};
use crate::abi;
pub use tlsn_core::{
ProverOutput,
config::{ProveConfig, ProverConfig},
};
#[derive(Debug)]
pub struct ProverError {}
impl std::error::Error for ProverError {}
impl std::fmt::Display for ProverError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "ProverError")
}
}
pub mod state {
use tlsn_core::transcript::{TlsTranscript, Transcript};
mod sealed {
pub trait Sealed {}
}
pub trait ProverState: sealed::Sealed {}
pub struct Initialized {}
pub struct Setup {}
pub struct Committed {
pub(super) tls_transcript: TlsTranscript,
pub(super) transcript: Transcript,
}
impl sealed::Sealed for Initialized {}
impl sealed::Sealed for Setup {}
impl sealed::Sealed for Committed {}
impl ProverState for Initialized {}
impl ProverState for Setup {}
impl ProverState for Committed {}
}
pub struct Prover<T: state::ProverState = state::Initialized> {
handle: abi::prove::Prover,
state: T,
}
impl Prover {
/// Creates a new prover.
///
/// # Arguments
///
/// * `config` - The configuration for the prover.
pub fn new(config: ProverConfig) -> Self {
let config = bincode::serialize(&config).unwrap();
let handle = abi::prove::Prover::new(&config);
Self {
handle,
state: state::Initialized {},
}
}
pub async fn setup(self) -> Result<Prover<state::Setup>, ProverError> {
poll_fn(|_| {
if let abi::prove::SetupReturn::Ready = self.handle.setup() {
Poll::Ready(())
} else {
Poll::Pending
}
})
.await;
Ok(Prover {
handle: self.handle,
state: state::Setup {},
})
}
}
impl Prover<state::Setup> {
pub async fn connect(self) -> Result<(TlsConnection, ProverFuture), ProverError> {
let io = poll_fn(|_| {
if let abi::prove::ConnectReturn::Ready(io) = self.handle.connect() {
Poll::Ready(io)
} else {
Poll::Pending
}
})
.await;
Ok((
TlsConnection { handle: io },
ProverFuture {
handle: Some(self.handle),
},
))
}
}
impl Prover<state::Committed> {
pub fn tls_transcript(&self) -> &TlsTranscript {
&self.state.tls_transcript
}
pub fn transcript(&self) -> &Transcript {
&self.state.transcript
}
pub async fn prove(&mut self, config: &ProveConfig) -> Result<ProverOutput, ProverError> {
let config = bincode::serialize(&config).unwrap();
self.handle.prove(&config);
let res = poll_fn(|_| {
if let abi::prove::ProveReturn::Ready(res) = self.handle.finish_prove() {
Poll::Ready(res)
} else {
Poll::Pending
}
})
.await;
res.map(|output| bincode::deserialize(&output).unwrap())
.map_err(|_| todo!())
}
pub async fn close(self) -> Result<(), ProverError> {
poll_fn(|_| {
if let abi::prove::CloseReturn::Ready = self.handle.close() {
Poll::Ready(())
} else {
Poll::Pending
}
})
.await;
Ok(())
}
}
pub struct ProverFuture {
handle: Option<abi::prove::Prover>,
}
impl Future for ProverFuture {
type Output = Result<Prover<state::Committed>, ProverError>;
fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
let handle = self
.handle
.take()
.expect("future should not be polled after completion.");
if let abi::prove::CommitReturn::Ready(res) = handle.finish_commit() {
Poll::Ready(
res.map(|data| {
let (tls_transcript, transcript) = bincode::deserialize(&data).unwrap();
Prover {
handle: handle,
state: state::Committed {
tls_transcript,
transcript,
},
}
})
.map_err(|_| todo!()),
)
} else {
self.handle = Some(handle);
Poll::Pending
}
}
}
pub struct TlsConnection {
handle: abi::io::Io,
}
impl AsyncWrite for TlsConnection {
fn poll_write(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
let n = match self.handle.check_write() {
abi::io::CheckWriteReturn::Pending => {
return Poll::Pending;
}
abi::io::CheckWriteReturn::Ready(Ok(n)) => (n as usize).min(buf.len()),
abi::io::CheckWriteReturn::Ready(Err(e)) => {
return Poll::Ready(Err(match e {
abi::io::Error::Closed => {
io::Error::new(io::ErrorKind::ConnectionAborted, "connection closed")
}
abi::io::Error::Other(e) => io::Error::new(io::ErrorKind::Other, e),
}));
}
};
self.handle.write(&buf[..n]).unwrap();
Poll::Ready(Ok(n))
}
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
match self.handle.close() {
abi::io::CloseReturn::Pending => Poll::Pending,
abi::io::CloseReturn::Ready(Ok(())) => Poll::Ready(Ok(())),
abi::io::CloseReturn::Ready(Err(e)) => Poll::Ready(Err(match e {
abi::io::Error::Closed => {
io::Error::new(io::ErrorKind::ConnectionAborted, "connection closed")
}
abi::io::Error::Other(e) => io::Error::new(io::ErrorKind::Other, e),
})),
}
}
}
impl AsyncRead for TlsConnection {
fn poll_read(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
match self.handle.read(buf.len() as u32) {
abi::io::ReadReturn::Pending => Poll::Pending,
abi::io::ReadReturn::Ready(Ok(data)) => {
assert!(data.len() <= buf.len());
buf[..data.len()].copy_from_slice(&data);
Poll::Ready(Ok(data.len()))
}
abi::io::ReadReturn::Ready(Err(abi::io::Error::Closed)) => Poll::Ready(Ok(0)),
abi::io::ReadReturn::Ready(Err(e)) => {
Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, e)))
}
}
}
}

131
crates/pdk/src/verifier.rs Normal file
View File

@@ -0,0 +1,131 @@
use std::{future::poll_fn, task::Poll};
pub use tlsn_core::{
VerifierOutput,
config::{VerifierConfig, VerifyConfig},
};
use crate::abi;
#[derive(Debug)]
pub struct VerifierError {}
impl std::error::Error for VerifierError {}
impl std::fmt::Display for VerifierError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "VerifierError")
}
}
pub mod state {
use tlsn_core::transcript::TlsTranscript;
mod sealed {
pub trait Sealed {}
}
pub trait VerifierState: sealed::Sealed {}
pub struct Initialized {}
pub struct Setup {}
pub struct Committed {
pub(super) tls_transcript: TlsTranscript,
}
impl sealed::Sealed for Initialized {}
impl sealed::Sealed for Setup {}
impl sealed::Sealed for Committed {}
impl VerifierState for Initialized {}
impl VerifierState for Setup {}
impl VerifierState for Committed {}
}
pub struct Verifier<T: state::VerifierState = state::Initialized> {
handle: abi::verify::Verifier,
state: T,
}
impl Verifier {
pub fn new(config: VerifierConfig) -> Self {
let config = bincode::serialize(&config).unwrap();
let handle = abi::verify::Verifier::new(&config);
Self {
handle,
state: state::Initialized {},
}
}
pub async fn setup(self) -> Result<Verifier<state::Setup>, VerifierError> {
poll_fn(|_| {
if let abi::verify::SetupReturn::Ready = self.handle.setup() {
Poll::Ready(())
} else {
Poll::Pending
}
})
.await;
Ok(Verifier {
handle: self.handle,
state: state::Setup {},
})
}
}
impl Verifier<state::Setup> {
pub async fn run(self) -> Result<Verifier<state::Committed>, VerifierError> {
let res = poll_fn(|_| {
if let abi::verify::CommitReturn::Ready(res) = self.handle.commit() {
Poll::Ready(res)
} else {
Poll::Pending
}
})
.await;
res.map(|data| Verifier {
handle: self.handle,
state: state::Committed {
tls_transcript: bincode::deserialize(&data).unwrap(),
},
})
.map_err(|_| todo!())
}
}
impl Verifier<state::Committed> {
pub async fn verify(&mut self, config: &VerifyConfig) -> Result<VerifierOutput, VerifierError> {
let config = bincode::serialize(&config).unwrap();
self.handle.verify(&config);
let res = poll_fn(|_| {
if let abi::verify::VerifyReturn::Ready(res) = self.handle.finish_verify() {
Poll::Ready(res)
} else {
Poll::Pending
}
})
.await;
res.map(|output| bincode::deserialize(&output).unwrap())
.map_err(|_| todo!())
}
pub async fn close(self) -> Result<(), VerifierError> {
poll_fn(|_| {
if let abi::verify::CloseReturn::Ready = self.handle.close() {
Poll::Ready(())
} else {
Poll::Pending
}
})
.await;
Ok(())
}
}

View File

@@ -0,0 +1,19 @@
[package]
name = "tlsn-sdk-core"
version = "0.1.0"
edition = "2024"
[dependencies]
tlsn = { workspace = true }
wasmtime = { version = "34.0", features = ["component-model"] }
bincode = { workspace = true }
futures = { workspace = true }
pin-project-lite = { workspace = true }
[dev-dependencies]
futures-plex = { workspace = true }
tlsn-server-fixture = { workspace = true }
tlsn-server-fixture-certs = { workspace = true }
[lints]
workspace = true

View File

@@ -0,0 +1,155 @@
use std::{
collections::HashMap,
task::{Context as StdContext, Poll},
};
use crate::{
Error, IoProvider,
io::{IoId, IoInstance},
prover::{ProverId, ProverInstance},
verifier::{VerifierId, VerifierInstance},
};
#[derive(Default)]
pub struct Instance {
pub state: State,
pub cx: Context,
}
pub struct Context {
pub waker: Waker,
}
impl Default for Context {
fn default() -> Self {
Self {
waker: Waker::new(),
}
}
}
pub struct Waker {
wants_wake: bool,
wants_call: bool,
}
impl Waker {
pub fn new() -> Self {
Self {
wants_wake: false,
wants_call: true,
}
}
pub fn set_wake(&mut self) {
self.wants_wake = true;
}
pub fn clear_wake(&mut self) {
self.wants_wake = false;
}
pub fn set_call(&mut self) {
self.wants_call = true;
}
pub fn clear_call(&mut self) {
self.wants_call = false;
}
pub fn wants_wake(&self) -> bool {
self.wants_wake
}
pub fn wants_call(&self) -> bool {
self.wants_call
}
}
/// Plugin instance.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct InstanceId(pub usize);
#[derive(Default)]
pub struct State {
provers: HashMap<ProverId, ProverInstance>,
verifiers: HashMap<VerifierId, VerifierInstance>,
}
impl State {
pub fn new() -> Self {
Self {
provers: HashMap::new(),
verifiers: HashMap::new(),
}
}
pub fn new_prover(&mut self, config: Vec<u8>) -> Result<ProverId, Error> {
let instance = ProverInstance::new(config)?;
let id = ProverId(self.provers.len());
self.provers.insert(id, instance);
Ok(id)
}
pub fn new_verifier(&mut self, config: Vec<u8>) -> Result<VerifierId, Error> {
let instance = VerifierInstance::new(config)?;
let id = VerifierId(self.verifiers.len());
self.verifiers.insert(id, instance);
Ok(id)
}
pub fn get_prover_mut(&mut self, id: ProverId) -> Result<&mut ProverInstance, Error> {
self.provers.get_mut(&id).ok_or_else(|| todo!())
}
pub fn get_io_mut(&mut self, id: IoId) -> Result<&mut IoInstance, Error> {
self.get_prover_mut(ProverId(id.0))?
.io_mut()
.ok_or_else(|| todo!())
}
pub fn get_verifier_mut(&mut self, id: VerifierId) -> Result<&mut VerifierInstance, Error> {
self.verifiers.get_mut(&id).ok_or_else(|| todo!())
}
pub fn poll(
&mut self,
cx_std: &mut StdContext<'_>,
cx: &mut Context,
io: &mut impl IoProvider,
) -> Poll<Result<(), Error>> {
let mut ready = Vec::new();
for (&id, prover) in self.provers.iter_mut() {
if let Poll::Ready(res) = prover.poll(cx_std, cx, io) {
res.unwrap();
ready.push(id);
}
}
for id in ready {
self.provers.remove(&id);
}
let mut ready = Vec::new();
for (&id, verifier) in self.verifiers.iter_mut() {
if let Poll::Ready(res) = verifier.poll(cx_std, cx, io) {
res.unwrap();
ready.push(id);
}
}
for id in ready {
self.verifiers.remove(&id);
}
if self.provers.is_empty() && self.verifiers.is_empty() {
return Poll::Ready(Ok(()));
} else {
Poll::Pending
}
}
}

262
crates/sdk-core/src/io.rs Normal file
View File

@@ -0,0 +1,262 @@
use std::{
pin::pin,
task::{Context as StdContext, Poll},
};
use futures::{AsyncRead, AsyncWrite};
use tlsn::prover::TlsConnection;
use crate::{Error, instance::Context};
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct IoId(pub usize);
pub struct IoInstance {
conn: TlsConnection,
write_buf: Buf,
wants_write_close: bool,
write_closed: bool,
read_buf: Buf,
read_closed: bool,
wants_write: bool,
wants_read: bool,
}
impl IoInstance {
pub(crate) fn new(conn: TlsConnection) -> Self {
const BUF_SIZE: usize = 8192;
Self {
conn,
write_buf: Buf::new(BUF_SIZE),
wants_write_close: false,
write_closed: false,
read_buf: Buf::new(BUF_SIZE),
read_closed: false,
wants_write: false,
wants_read: false,
}
}
pub fn check_write(&mut self, cx: &mut Context) -> Poll<Result<usize, std::io::Error>> {
if self.write_closed {
return Poll::Ready(Err(std::io::ErrorKind::BrokenPipe.into()));
}
match self.write_buf.remaining_mut() {
0 => {
self.wants_write = true;
cx.waker.set_wake();
Poll::Pending
}
n => Poll::Ready(Ok(n)),
}
}
pub fn write(&mut self, buf: &[u8]) -> Result<(), std::io::Error> {
if self.write_closed {
return Err(std::io::ErrorKind::BrokenPipe.into());
}
let remaining_capacity = self.write_buf.remaining_mut();
if buf.len() > remaining_capacity {
todo!()
}
let n = buf.len().min(remaining_capacity);
self.write_buf.chunk_mut()[..n].copy_from_slice(&buf[..n]);
self.write_buf.advance_mut(n);
self.wants_write = false;
Ok(())
}
pub fn flush(&mut self) -> Result<(), std::io::Error> {
Ok(())
}
pub fn available(&self) -> usize {
self.read_buf.remaining()
}
pub fn read_closed(&self) -> bool {
self.read_closed
}
pub fn read(&mut self, len: usize, cx: &mut Context) -> Poll<Result<Vec<u8>, std::io::Error>> {
let chunk = self.read_buf.chunk();
let available = chunk.len();
if available == 0 && !self.read_closed {
self.wants_read = true;
cx.waker.set_wake();
return Poll::Pending;
}
let len = available.min(len);
let out = chunk[..len].to_vec();
self.read_buf.advance(len);
self.wants_read = false;
Poll::Ready(Ok(out))
}
pub fn close(&mut self, cx: &mut Context) -> Poll<Result<(), std::io::Error>> {
if self.write_closed {
Poll::Ready(Ok(()))
} else {
self.wants_write_close = true;
cx.waker.set_wake();
Poll::Pending
}
}
pub fn poll(
&mut self,
cx_std: &mut StdContext<'_>,
cx: &mut Context,
) -> Poll<Result<(), Error>> {
while self.read_buf.remaining_mut() > 0 && !self.read_closed {
if let Poll::Ready(res) =
pin!(&mut self.conn).poll_read(cx_std, self.read_buf.chunk_mut())
{
let n = res.unwrap();
self.read_buf.advance_mut(n);
if n == 0 {
println!("server closed conn");
self.read_closed = true;
}
if self.wants_read {
cx.waker.set_call();
}
} else {
break;
}
}
while self.write_buf.remaining() > 0 {
if let Poll::Ready(res) =
pin!(&mut self.conn).poll_write(cx_std, self.write_buf.chunk())
{
let n = res.unwrap();
println!("prover wrote {n} bytes to server");
self.write_buf.advance(n);
if self.wants_write {
cx.waker.set_call();
}
} else {
break;
}
}
if self.write_buf.remaining() == 0 && self.wants_write_close && !self.write_closed {
if let Poll::Ready(res) = pin!(&mut self.conn).poll_close(cx_std) {
res.unwrap();
self.write_closed = true;
if self.wants_write_close {
cx.waker.set_call();
}
println!("prover closed conn");
}
}
Poll::Pending
}
}
/// A fixed-size buffer that is guaranteed to be initialized.
pub(crate) struct Buf {
data: Box<[u8]>,
len: usize,
pos: usize,
cap: usize,
}
impl Buf {
pub(crate) fn new(size: usize) -> Self {
// SAFETY: It is critical that memory of the buffer is initialized.
#[allow(unused_unsafe)]
let buf = unsafe { vec![0; size].into_boxed_slice() };
Self {
data: buf,
len: 0,
pos: 0,
cap: size,
}
}
/// Remaining bytes in the buffer.
pub(crate) fn remaining(&self) -> usize {
self.len - self.pos
}
/// Returns a reference to the bytes in the buffer.
pub(crate) fn chunk(&self) -> &[u8] {
&self.data[self.pos..self.len]
}
/// Advance the position of the buffer.
pub(crate) fn advance(&mut self, cnt: usize) {
assert!(cnt <= self.remaining(), "advance past end of buffer");
self.pos += cnt;
if self.pos == self.len {
self.pos = 0;
self.len = 0;
}
}
/// Remaining room in the buffer.
pub(crate) fn remaining_mut(&self) -> usize {
self.cap - self.len
}
/// Advance the length of the buffer.
pub(crate) fn advance_mut(&mut self, cnt: usize) {
assert!(self.len + cnt <= self.cap, "advance past end of buffer");
self.len += cnt;
}
/// Returns a mutable reference to the remaining room in the buffer.
pub(crate) fn chunk_mut(&mut self) -> &mut [u8] {
&mut self.data[self.len..]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fixed_buf() {
let mut buf = Buf::new(10);
assert_eq!(buf.remaining(), 0);
assert_eq!(buf.remaining_mut(), 10);
assert_eq!(buf.chunk(), &[] as &[u8]);
assert_eq!(buf.chunk_mut(), &[0; 10]);
}
#[test]
fn test_fixed_buf_advance() {
let mut buf = Buf::new(10);
buf.advance_mut(5);
assert_eq!(buf.remaining_mut(), 5);
assert_eq!(buf.remaining(), 5);
buf.advance(3);
assert_eq!(buf.remaining_mut(), 5);
assert_eq!(buf.remaining(), 2);
buf.advance(buf.remaining());
assert_eq!(buf.remaining(), 0);
// Buffer should reset.
assert_eq!(buf.remaining_mut(), 10);
}
}

105
crates/sdk-core/src/lib.rs Normal file
View File

@@ -0,0 +1,105 @@
pub(crate) mod instance;
pub(crate) mod io;
pub(crate) mod prover;
pub(crate) mod provider;
pub(crate) mod verifier;
pub(crate) mod wasm;
mod wasmtime;
pub use provider::IoProvider;
pub use wasmtime::Wasmtime;
use std::{
future::poll_fn,
sync::{Arc, Mutex},
task::Poll,
};
use crate::{instance::State, wasm::WasmRuntime};
#[derive(Debug)]
pub struct Error;
pub struct Plugin {
pub manifest: Manifest,
pub binary: Binary,
}
pub struct Manifest {}
/// Plugin WASM binary.
pub struct Binary(pub Vec<u8>);
pub struct Runtime<T, I> {
wasm: T,
io_provider: I,
}
impl<T, I> Runtime<T, I> {
pub fn new(wasm: T, io_provider: I) -> Self {
Self { wasm, io_provider }
}
}
impl<T, I> Runtime<T, I>
where
T: WasmRuntime,
I: IoProvider,
{
pub async fn run_plugin(&mut self, plugin: &Plugin, input: &[u8]) -> Vec<u8> {
let wasm = self.wasm.load(&plugin.binary).unwrap();
let instance = Arc::new(Mutex::new(instance::Instance::default()));
let id = self
.wasm
.instantiate(wasm, instance.clone(), input)
.unwrap();
let mut output = None;
let mut ready_0 = false;
let mut ready_1 = false;
let output = poll_fn(|cx_std| {
let wants_call = instance.lock().unwrap().cx.waker.wants_call();
if !ready_0
&& wants_call
&& let Poll::Ready(res) = self.wasm.poll(id).unwrap()
{
output = Some(res.inspect(|data| {
println!("plugin output: {}", String::from_utf8_lossy(data));
}));
ready_0 = true;
}
let mut instance = instance.lock().unwrap();
if !ready_0 {
if instance.cx.waker.wants_wake() {
cx_std.waker().wake_by_ref();
} else {
panic!("plugin isn't waiting for anything");
}
}
if !ready_1 {
let instance = &mut (*instance);
if let Poll::Ready(res) =
instance
.state
.poll(cx_std, &mut instance.cx, &mut self.io_provider)
{
res.unwrap();
ready_1 = true;
}
}
if ready_0 && ready_1 {
return Poll::Ready(Ok::<_, Error>(output.take().unwrap()));
} else {
Poll::Pending
}
})
.await
.unwrap();
output.unwrap()
}
}

View File

@@ -0,0 +1,388 @@
use std::{
pin::Pin,
task::{Context as StdContext, Poll},
};
use futures::FutureExt;
use tlsn::{
config::ProverConfig,
connection::ServerName,
prover::{ProveConfig, Prover, ProverFuture, ProverOutput, TlsConnection, state},
transcript::{TlsTranscript, Transcript},
};
use crate::{Error, IoProvider, instance::Context, io::IoInstance};
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct ProverId(pub usize);
pub struct ProverInstance {
state: State,
server_name: ServerName,
server_io: Option<IoInstance>,
wants_state_update: bool,
}
impl ProverInstance {
pub fn new(config: Vec<u8>) -> Result<Self, Error> {
println!("prover new");
let config: ProverConfig = bincode::deserialize(&config).unwrap();
let server_name = config.server_name().clone();
let prover = Prover::new(config);
Ok(Self {
state: State::Init(prover),
server_name,
server_io: None,
wants_state_update: false,
})
}
pub fn io_mut(&mut self) -> Option<&mut IoInstance> {
self.server_io.as_mut()
}
pub fn setup(&mut self, cx: &mut Context) -> Result<Poll<()>, Error> {
match self.state.take() {
State::Init(prover) => {
self.state = State::Preprocess(prover);
}
State::Preprocess(prover) => {
self.state = State::Preprocess(prover);
}
State::Preprocessing(fut) => {
self.state = State::Preprocessing(fut);
}
State::Setup(prover) => {
self.state = State::Setup(prover);
return Ok(Poll::Ready(()));
}
state => todo!(),
};
self.wants_state_update = true;
cx.waker.set_wake();
Ok(Poll::Pending)
}
pub fn connect(&mut self, cx: &mut Context) -> Result<Poll<()>, Error> {
match self.state.take() {
State::Setup(prover) => {
self.state = State::Connect(prover);
}
State::Connect(prover) => {
self.state = State::Connect(prover);
}
State::Connecting(fut) => {
self.state = State::Connecting(fut);
}
State::Online(fut) => {
self.state = State::Online(fut);
return Ok(Poll::Ready(()));
}
state => todo!(),
}
self.wants_state_update = true;
cx.waker.set_wake();
Ok(Poll::Pending)
}
pub fn finish_commit(
&mut self,
cx: &mut Context,
) -> Result<Poll<(TlsTranscript, Transcript)>, Error> {
match self.state.take() {
State::Online(fut) => {
self.state = State::Online(fut);
}
State::FinishCommit(prover) => {
let tls_transcript = prover.tls_transcript().clone();
let transcript = prover.transcript().clone();
self.state = State::Committed(prover);
return Ok(Poll::Ready((tls_transcript, transcript)));
}
state => todo!(),
}
self.wants_state_update = true;
cx.waker.set_wake();
Ok(Poll::Pending)
}
pub fn prove(&mut self, config: ProveConfig) -> Result<(), Error> {
match self.state.take() {
State::Committed(prover) => {
self.state = State::StartProve(prover, config);
}
state => todo!(),
}
Ok(())
}
pub fn finish_prove(&mut self, cx: &mut Context) -> Result<Poll<ProverOutput>, Error> {
match self.state.take() {
State::StartProve(prover, config) => {
self.state = State::StartProve(prover, config);
}
State::Proving(fut) => {
self.state = State::Proving(fut);
}
State::FinishProve(prover, output) => {
self.state = State::Committed(prover);
return Ok(Poll::Ready(output));
}
state => todo!(),
}
self.wants_state_update = true;
cx.waker.set_wake();
Ok(Poll::Pending)
}
pub fn close(&mut self, cx: &mut Context) -> Result<Poll<()>, Error> {
match self.state.take() {
State::Committed(prover) => {
self.state = State::Close(prover);
}
State::Close(prover) => {
self.state = State::Close(prover);
}
State::Closing(fut) => {
self.state = State::Closing(fut);
}
State::FinishClose => {
self.state = State::Done;
return Ok(Poll::Ready(()));
}
state => {
dbg!(state);
todo!()
}
}
self.wants_state_update = true;
cx.waker.set_wake();
Ok(Poll::Pending)
}
pub fn poll(
&mut self,
cx_std: &mut StdContext<'_>,
cx: &mut Context,
io: &mut impl IoProvider,
) -> Poll<Result<(), Error>> {
if let Some(io) = self.server_io.as_mut() {
if let Poll::Ready(res) = io.poll(cx_std, cx) {
res?;
}
}
match self.state.take() {
State::Init(prover) => {
self.state = State::Init(prover);
}
State::Preprocess(prover) => {
let io_fut = io.connect_peer();
self.state = State::Preprocessing(Box::pin(async move {
prover
.setup(io_fut.await.unwrap())
.await
.map_err(|_| todo!())
}));
return self.poll(cx_std, cx, io);
}
State::Preprocessing(mut fut) => {
if let Poll::Ready(res) = fut.poll_unpin(cx_std) {
let prover = res.unwrap();
self.state = State::Setup(prover);
if self.wants_state_update {
self.wants_state_update = false;
cx.waker.set_call();
}
println!("prover setup");
} else {
self.state = State::Preprocessing(fut);
}
}
State::Setup(prover) => {
self.state = State::Setup(prover);
}
State::Connect(prover) => {
let io_fut = io.connect_server(&self.server_name);
self.state = State::Connecting(Box::pin(async move {
prover
.connect(io_fut.await.unwrap())
.await
.map_err(|_| todo!())
}));
println!("prover connect");
return self.poll(cx_std, cx, io);
}
State::Connecting(mut fut) => {
if let Poll::Ready(res) = fut.poll_unpin(cx_std) {
let (conn, fut) = res.unwrap();
self.state = State::Online(fut);
self.server_io = Some(IoInstance::new(conn));
if self.wants_state_update {
self.wants_state_update = false;
cx.waker.set_call();
}
println!("prover online");
return self.poll(cx_std, cx, io);
} else {
self.state = State::Connecting(fut);
}
}
State::Online(mut fut) => {
if let Poll::Ready(res) = fut.poll_unpin(cx_std) {
let prover = res.unwrap();
self.state = State::FinishCommit(prover);
if self.wants_state_update {
self.wants_state_update = false;
cx.waker.set_call();
}
println!("prover committed");
} else {
self.state = State::Online(fut);
}
}
State::FinishCommit(prover) => {
self.state = State::FinishCommit(prover);
}
State::Committed(prover) => {
self.state = State::Committed(prover);
}
State::StartProve(mut prover, config) => {
self.state = State::Proving(Box::pin(async move {
let output = prover.prove(&config).await.map_err(|_| todo!())?;
Ok((prover, output))
}));
return self.poll(cx_std, cx, io);
}
State::Proving(mut fut) => {
if let Poll::Ready(res) = fut.poll_unpin(cx_std) {
let (prover, output) = res.unwrap();
self.state = State::FinishProve(prover, output);
if self.wants_state_update {
self.wants_state_update = false;
cx.waker.set_call();
}
} else {
self.state = State::Proving(fut);
}
}
State::FinishProve(prover, output) => {
self.state = State::FinishProve(prover, output);
}
State::Close(prover) => {
self.state = State::Closing(Box::pin(async move {
prover.close().await.map_err(|_| todo!())
}));
return self.poll(cx_std, cx, io);
}
State::Closing(mut fut) => {
if let Poll::Ready(res) = fut.poll_unpin(cx_std) {
res?;
println!("prover closed");
self.state = State::FinishClose;
if self.wants_state_update {
self.wants_state_update = false;
cx.waker.set_call();
}
} else {
self.state = State::Closing(fut);
}
}
State::FinishClose => {
self.state = State::FinishClose;
}
State::Done => {
self.state = State::Done;
return Poll::Ready(Ok(()));
}
State::Error => todo!(),
}
Poll::Pending
}
}
enum State {
Init(Prover<state::Initialized>),
Preprocess(Prover<state::Initialized>),
Preprocessing(Pin<Box<dyn Future<Output = Result<Prover<state::Setup>, Error>>>>),
Setup(Prover<state::Setup>),
Connect(Prover<state::Setup>),
Connecting(Pin<Box<dyn Future<Output = Result<(TlsConnection, ProverFuture), Error>>>>),
Online(ProverFuture),
FinishCommit(Prover<state::Committed>),
Committed(Prover<state::Committed>),
StartProve(Prover<state::Committed>, ProveConfig),
Proving(Pin<Box<dyn Future<Output = Result<(Prover<state::Committed>, ProverOutput), Error>>>>),
FinishProve(Prover<state::Committed>, ProverOutput),
Close(Prover<state::Committed>),
Closing(Pin<Box<dyn Future<Output = Result<(), Error>>>>),
FinishClose,
Done,
Error,
}
impl State {
fn take(&mut self) -> Self {
std::mem::replace(self, Self::Error)
}
}
impl std::fmt::Debug for State {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Init(_) => f.debug_tuple("Init").finish_non_exhaustive(),
Self::Preprocess(_) => f.debug_tuple("Preprocess").finish_non_exhaustive(),
Self::Preprocessing(_) => f.debug_tuple("Preprocessing").finish_non_exhaustive(),
Self::Setup(_) => f.debug_tuple("Setup").finish_non_exhaustive(),
Self::Connect(_) => f.debug_tuple("Connect").finish_non_exhaustive(),
Self::Connecting(_) => f.debug_tuple("Connecting").finish_non_exhaustive(),
Self::Online(_) => f.debug_tuple("Online").finish_non_exhaustive(),
Self::FinishCommit(_) => f.debug_tuple("FinishCommit").finish_non_exhaustive(),
Self::Committed(_) => f.debug_tuple("Committed").finish_non_exhaustive(),
Self::StartProve(_, _) => f.debug_tuple("StartProve").finish_non_exhaustive(),
Self::Proving(_) => f.debug_tuple("Proving").finish_non_exhaustive(),
Self::FinishProve(_, _) => f.debug_tuple("FinishProve").finish_non_exhaustive(),
Self::Close(_) => f.debug_tuple("Close").finish_non_exhaustive(),
Self::Closing(_) => f.debug_tuple("Closing").finish_non_exhaustive(),
Self::FinishClose => f.write_str("FinishClose"),
Self::Done => f.write_str("Done"),
Self::Error => f.write_str("Error"),
}
}
}

View File

@@ -0,0 +1,18 @@
use std::pin::Pin;
use futures::{AsyncRead, AsyncWrite};
use tlsn::connection::ServerName;
pub trait IoProvider {
type Io: AsyncRead + AsyncWrite + Send + Unpin + 'static;
type Error: std::error::Error + Send + Sync + 'static;
fn connect_server(
&mut self,
name: &ServerName,
) -> Pin<Box<dyn Future<Output = Result<Self::Io, Self::Error>> + Send>>;
fn connect_peer(
&mut self,
) -> Pin<Box<dyn Future<Output = Result<Self::Io, Self::Error>> + Send>>;
}

View File

@@ -0,0 +1,309 @@
use std::{
pin::Pin,
task::{Context as StdContext, Poll},
};
use futures::FutureExt;
use tlsn::{
config::{VerifierConfig, VerifyConfig},
transcript::TlsTranscript,
verifier::{Verifier, VerifierOutput, state},
};
use crate::{Error, IoProvider, instance::Context};
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct VerifierId(pub usize);
pub struct VerifierInstance {
state: State,
wants_state_update: bool,
}
impl VerifierInstance {
pub fn new(config: Vec<u8>) -> Result<Self, Error> {
let config: VerifierConfig = bincode::deserialize(&config).unwrap();
let verifier = Verifier::new(config);
Ok(Self {
state: State::Init(verifier),
wants_state_update: false,
})
}
pub fn setup(&mut self, cx: &mut Context) -> Result<Poll<()>, Error> {
match self.state.take() {
State::Init(verifier) => {
self.state = State::Preprocess(verifier);
}
State::Preprocess(verifier) => {
self.state = State::Preprocess(verifier);
}
State::Preprocessing(fut) => {
self.state = State::Preprocessing(fut);
}
State::Setup(verifier) => {
self.state = State::Setup(verifier);
return Ok(Poll::Ready(()));
}
state => todo!(),
};
self.wants_state_update = true;
cx.waker.set_wake();
Ok(Poll::Pending)
}
pub fn commit(&mut self, cx: &mut Context) -> Result<Poll<TlsTranscript>, Error> {
match self.state.take() {
State::Setup(verifier) => {
self.state = State::StartCommit(verifier);
}
State::StartCommit(verifier) => {
self.state = State::StartCommit(verifier);
}
State::Online(fut) => {
self.state = State::Online(fut);
}
State::FinishCommit(verifier) => {
let tls_transcript = verifier.tls_transcript().clone();
self.state = State::Committed(verifier);
println!("verifier committed");
return Ok(Poll::Ready(tls_transcript));
}
state => todo!(),
}
self.wants_state_update = true;
cx.waker.set_wake();
Ok(Poll::Pending)
}
pub fn verify(&mut self, config: VerifyConfig) -> Result<(), Error> {
match self.state.take() {
State::Committed(verifier) => {
self.state = State::StartVerify(verifier, config);
}
State::StartVerify(verifier, config) => {
self.state = State::StartVerify(verifier, config);
}
state => todo!(),
}
Ok(())
}
pub fn finish_verify(&mut self, cx: &mut Context) -> Result<Poll<VerifierOutput>, Error> {
match self.state.take() {
State::StartVerify(verifier, config) => {
self.state = State::StartVerify(verifier, config);
}
State::Verifying(fut) => {
self.state = State::Verifying(fut);
}
State::FinishVerify(verifier, output) => {
self.state = State::Committed(verifier);
return Ok(Poll::Ready(output));
}
state => todo!(),
}
self.wants_state_update = true;
cx.waker.set_wake();
Ok(Poll::Pending)
}
pub fn close(&mut self, cx: &mut Context) -> Result<Poll<()>, Error> {
match self.state.take() {
State::Committed(verifier) => {
self.state = State::Close(verifier);
}
State::Close(verifier) => {
self.state = State::Close(verifier);
}
State::Closing(fut) => {
self.state = State::Closing(fut);
}
State::FinishClose => {
self.state = State::Done;
println!("verifier closed");
return Ok(Poll::Ready(()));
}
state => todo!(),
}
self.wants_state_update = true;
cx.waker.set_wake();
Ok(Poll::Pending)
}
pub fn poll(
&mut self,
cx_std: &mut StdContext<'_>,
cx: &mut Context,
io: &mut impl IoProvider,
) -> Poll<Result<(), Error>> {
match self.state.take() {
State::Init(verifier) => {
self.state = State::Init(verifier);
}
State::Preprocess(verifier) => {
let io_fut = io.connect_peer();
self.state = State::Preprocessing(Box::pin(async move {
verifier
.setup(io_fut.await.unwrap())
.await
.map_err(|_| todo!())
}));
return self.poll(cx_std, cx, io);
}
State::Preprocessing(mut fut) => {
if let Poll::Ready(res) = fut.poll_unpin(cx_std) {
let verifier = res.unwrap();
println!("verifier setup");
self.state = State::Setup(verifier);
if self.wants_state_update {
self.wants_state_update = false;
cx.waker.set_call();
}
} else {
self.state = State::Preprocessing(fut);
}
}
State::Setup(verifier) => {
self.state = State::Setup(verifier);
}
State::StartCommit(verifier) => {
self.state = State::Online(Box::pin(async move {
verifier.run().await.map_err(|_| todo!())
}));
println!("verifier start commit");
return self.poll(cx_std, cx, io);
}
State::Online(mut fut) => {
if let Poll::Ready(res) = fut.poll_unpin(cx_std) {
let verifier = res.unwrap();
self.state = State::FinishCommit(verifier);
if self.wants_state_update {
self.wants_state_update = false;
cx.waker.set_call();
}
println!("verifier finish commit");
} else {
self.state = State::Online(fut);
}
}
State::FinishCommit(verifier) => {
self.state = State::FinishCommit(verifier);
}
State::Committed(verifier) => {
self.state = State::Committed(verifier);
}
State::StartVerify(mut verifier, config) => {
self.state = State::Verifying(Box::pin(async move {
let output = verifier.verify(&config).await.map_err(|_| todo!())?;
Ok((verifier, output))
}));
return self.poll(cx_std, cx, io);
}
State::Verifying(mut fut) => {
if let Poll::Ready(res) = fut.poll_unpin(cx_std) {
let (verifier, output) = res.unwrap();
self.state = State::FinishVerify(verifier, output);
if self.wants_state_update {
self.wants_state_update = false;
cx.waker.set_call();
}
println!("verifier finish verify");
} else {
self.state = State::Verifying(fut);
}
}
State::FinishVerify(verifier, output) => {
self.state = State::FinishVerify(verifier, output);
}
State::Close(verifier) => {
let fut = Box::pin(async move { verifier.close().await.map_err(|_| todo!()) });
self.state = State::Closing(fut);
println!("verifier start close");
return self.poll(cx_std, cx, io);
}
State::Closing(mut fut) => {
if let Poll::Ready(res) = fut.poll_unpin(cx_std) {
res?;
println!("verifier closed");
self.state = State::FinishClose;
if self.wants_state_update {
self.wants_state_update = false;
cx.waker.set_call();
}
} else {
self.state = State::Closing(fut);
}
}
State::FinishClose => {
self.state = State::FinishClose;
}
State::Done => {
self.state = State::Done;
return Poll::Ready(Ok(()));
}
State::Error => todo!(),
}
Poll::Pending
}
}
enum State {
Init(Verifier<state::Initialized>),
Preprocess(Verifier<state::Initialized>),
Preprocessing(Pin<Box<dyn Future<Output = Result<Verifier<state::Setup>, Error>>>>),
Setup(Verifier<state::Setup>),
StartCommit(Verifier<state::Setup>),
Online(Pin<Box<dyn Future<Output = Result<Verifier<state::Committed>, Error>>>>),
FinishCommit(Verifier<state::Committed>),
Committed(Verifier<state::Committed>),
StartVerify(Verifier<state::Committed>, VerifyConfig),
Verifying(
Pin<Box<dyn Future<Output = Result<(Verifier<state::Committed>, VerifierOutput), Error>>>>,
),
FinishVerify(Verifier<state::Committed>, VerifierOutput),
Close(Verifier<state::Committed>),
Closing(Pin<Box<dyn Future<Output = Result<(), Error>>>>),
FinishClose,
Done,
Error,
}
impl State {
fn take(&mut self) -> Self {
std::mem::replace(self, Self::Error)
}
}

View File

@@ -0,0 +1,24 @@
use std::{
sync::{Arc, Mutex},
task::Poll,
};
use crate::{
Binary, Error,
instance::{Instance, InstanceId},
};
pub struct WasmId(pub usize);
pub trait WasmRuntime {
fn load(&mut self, bin: &Binary) -> Result<WasmId, Error>;
fn instantiate(
&mut self,
id: WasmId,
instance: Arc<Mutex<Instance>>,
arg: &[u8],
) -> Result<InstanceId, Error>;
fn poll(&mut self, id: InstanceId) -> Result<Poll<Result<Vec<u8>, String>>, Error>;
}

View File

@@ -0,0 +1,399 @@
use std::{
sync::{Arc, Mutex},
task::Poll,
};
use wasmtime::{
Engine, Store,
component::{Component, HasSelf, Linker, Resource},
};
use crate::{
Error, WasmRuntime,
instance::{self, Context, InstanceId},
io::IoId,
prover::ProverId,
verifier::VerifierId,
wasm::WasmId,
};
mod generated {
wasmtime::component::bindgen!({
world: "plugin",
path: "wit/tlsn.wit",
trappable_imports: true,
with: {
"tlsn:tlsn/prove/prover": crate::prover::ProverId,
"tlsn:tlsn/verify/verifier": crate::verifier::VerifierId,
"tlsn:tlsn/io/io": crate::io::IoId,
}
});
}
use generated::{Plugin, tlsn::tlsn as abi};
struct InstanceState {
inner: Arc<Mutex<instance::Instance>>,
}
impl abi::io::Host for InstanceState {}
impl abi::io::HostIo for InstanceState {
fn check_write(
&mut self,
self_: Resource<IoId>,
) -> Result<abi::io::CheckWriteReturn, wasmtime::Error> {
let id = self_.rep();
let mut guard = self.inner.lock().unwrap();
let instance = &mut (*guard);
let io = instance.state.get_io_mut(IoId(id as usize)).unwrap();
match io.check_write(&mut instance.cx) {
Poll::Pending => Ok(abi::io::CheckWriteReturn::Pending),
Poll::Ready(Ok(n)) => Ok(abi::io::CheckWriteReturn::Ready(Ok(n as u32))),
Poll::Ready(Err(e)) => Ok(abi::io::CheckWriteReturn::Ready(Err(
abi::io::Error::Other(e.to_string()),
))),
}
}
fn write(
&mut self,
self_: Resource<IoId>,
buf: Vec<u8>,
) -> Result<Result<(), abi::io::Error>, wasmtime::Error> {
let id = self_.rep();
let mut guard = self.inner.lock().unwrap();
let instance = &mut (*guard);
let io = instance.state.get_io_mut(IoId(id as usize)).unwrap();
Ok(io.write(buf.as_slice()).map_err(|_| todo!()))
}
fn close(&mut self, self_: Resource<IoId>) -> Result<abi::io::CloseReturn, wasmtime::Error> {
let id = self_.rep();
let mut guard = self.inner.lock().unwrap();
let instance = &mut (*guard);
let io = instance.state.get_io_mut(IoId(id as usize)).unwrap();
match io.close(&mut instance.cx) {
Poll::Pending => Ok(abi::io::CloseReturn::Pending),
Poll::Ready(Ok(())) => Ok(abi::io::CloseReturn::Ready(Ok(()))),
Poll::Ready(Err(e)) => Ok(abi::io::CloseReturn::Ready(Err(abi::io::Error::Other(
e.to_string(),
)))),
}
}
fn read(
&mut self,
self_: Resource<IoId>,
len: u32,
) -> Result<abi::io::ReadReturn, wasmtime::Error> {
let id = self_.rep();
let mut guard = self.inner.lock().unwrap();
let instance = &mut (*guard);
let io = instance.state.get_io_mut(IoId(id as usize)).unwrap();
match io.read(len as usize, &mut instance.cx) {
Poll::Pending => Ok(abi::io::ReadReturn::Pending),
Poll::Ready(Ok(data)) => Ok(abi::io::ReadReturn::Ready(Ok(data))),
Poll::Ready(Err(e)) => Ok(abi::io::ReadReturn::Ready(Err(abi::io::Error::Other(
e.to_string(),
)))),
}
}
fn drop(&mut self, rep: Resource<IoId>) -> wasmtime::Result<()> {
Ok(())
}
}
impl abi::prove::Host for InstanceState {}
impl abi::prove::HostProver for InstanceState {
fn new(&mut self, config: Vec<u8>) -> wasmtime::Result<Resource<ProverId>> {
let id = self.inner.lock().unwrap().state.new_prover(config).unwrap();
Ok(Resource::new_own(id.0 as u32))
}
fn setup(&mut self, self_: Resource<ProverId>) -> wasmtime::Result<abi::prove::SetupReturn> {
let id = ProverId(self_.rep() as usize);
let mut guard = self.inner.lock().unwrap();
let instance = &mut (*guard);
let prover = instance.state.get_prover_mut(id).unwrap();
if let Poll::Ready(()) = prover.setup(&mut instance.cx).unwrap() {
Ok(abi::prove::SetupReturn::Ready)
} else {
Ok(abi::prove::SetupReturn::Pending)
}
}
fn connect(
&mut self,
self_: Resource<ProverId>,
) -> wasmtime::Result<abi::prove::ConnectReturn> {
let id = ProverId(self_.rep() as usize);
let mut guard = self.inner.lock().unwrap();
let instance = &mut (*guard);
let prover = instance.state.get_prover_mut(id).unwrap();
if let Poll::Ready(()) = prover.connect(&mut instance.cx).unwrap() {
Ok(abi::prove::ConnectReturn::Ready(Resource::new_own(
id.0 as u32,
)))
} else {
Ok(abi::prove::ConnectReturn::Pending)
}
}
fn finish_commit(
&mut self,
self_: Resource<ProverId>,
) -> wasmtime::Result<abi::prove::CommitReturn> {
let id = ProverId(self_.rep() as usize);
let mut guard = self.inner.lock().unwrap();
let instance = &mut (*guard);
let prover = instance.state.get_prover_mut(id).unwrap();
if let Poll::Ready((tls_transcript, transcript)) =
prover.finish_commit(&mut instance.cx).unwrap()
{
Ok(abi::prove::CommitReturn::Ready(Ok(bincode::serialize(&(
tls_transcript,
transcript,
))
.unwrap())))
} else {
Ok(abi::prove::CommitReturn::Pending)
}
}
fn prove(&mut self, self_: Resource<ProverId>, config: Vec<u8>) -> wasmtime::Result<()> {
let config = bincode::deserialize(&config).unwrap();
let id = ProverId(self_.rep() as usize);
let mut guard = self.inner.lock().unwrap();
let instance = &mut (*guard);
let prover = instance.state.get_prover_mut(id).unwrap();
prover.prove(config).unwrap();
Ok(())
}
fn finish_prove(
&mut self,
self_: Resource<ProverId>,
) -> wasmtime::Result<abi::prove::ProveReturn> {
let id = ProverId(self_.rep() as usize);
let mut guard = self.inner.lock().unwrap();
let instance = &mut (*guard);
let prover = instance.state.get_prover_mut(id).unwrap();
if let Poll::Ready(output) = prover.finish_prove(&mut instance.cx).unwrap() {
Ok(abi::prove::ProveReturn::Ready(Ok(bincode::serialize(
&output,
)
.unwrap())))
} else {
Ok(abi::prove::ProveReturn::Pending)
}
}
fn close(&mut self, self_: Resource<ProverId>) -> wasmtime::Result<abi::prove::CloseReturn> {
let id = ProverId(self_.rep() as usize);
let mut guard = self.inner.lock().unwrap();
let instance = &mut (*guard);
let prover = instance.state.get_prover_mut(id).unwrap();
if let Poll::Ready(()) = prover.close(&mut instance.cx).unwrap() {
Ok(abi::prove::CloseReturn::Ready)
} else {
Ok(abi::prove::CloseReturn::Pending)
}
}
fn drop(&mut self, rep: Resource<ProverId>) -> wasmtime::Result<()> {
Ok(())
}
}
impl abi::verify::Host for InstanceState {}
impl abi::verify::HostVerifier for InstanceState {
fn new(&mut self, config: Vec<u8>) -> wasmtime::Result<Resource<VerifierId>> {
let id = self
.inner
.lock()
.unwrap()
.state
.new_verifier(config)
.unwrap();
Ok(Resource::new_own(id.0 as u32))
}
fn setup(&mut self, self_: Resource<VerifierId>) -> wasmtime::Result<abi::verify::SetupReturn> {
let id = VerifierId(self_.rep() as usize);
let mut guard = self.inner.lock().unwrap();
let instance = &mut (*guard);
let verifier = instance.state.get_verifier_mut(VerifierId(id.0)).unwrap();
if let Poll::Ready(()) = verifier.setup(&mut instance.cx).unwrap() {
Ok(abi::verify::SetupReturn::Ready)
} else {
Ok(abi::verify::SetupReturn::Pending)
}
}
fn commit(
&mut self,
self_: Resource<VerifierId>,
) -> wasmtime::Result<abi::verify::CommitReturn> {
let id = VerifierId(self_.rep() as usize);
let mut guard = self.inner.lock().unwrap();
let instance = &mut (*guard);
let verifier = instance.state.get_verifier_mut(VerifierId(id.0)).unwrap();
if let Poll::Ready(tls_transcript) = verifier.commit(&mut instance.cx).unwrap() {
Ok(abi::verify::CommitReturn::Ready(Ok(bincode::serialize(
&tls_transcript,
)
.unwrap())))
} else {
Ok(abi::verify::CommitReturn::Pending)
}
}
fn verify(&mut self, self_: Resource<VerifierId>, config: Vec<u8>) -> wasmtime::Result<()> {
let config = bincode::deserialize(&config).unwrap();
let id = VerifierId(self_.rep() as usize);
let mut guard = self.inner.lock().unwrap();
let instance = &mut (*guard);
let verifier = instance.state.get_verifier_mut(VerifierId(id.0)).unwrap();
verifier.verify(config).unwrap();
Ok(())
}
fn finish_verify(
&mut self,
self_: Resource<VerifierId>,
) -> wasmtime::Result<abi::verify::VerifyReturn> {
let id = VerifierId(self_.rep() as usize);
let mut guard = self.inner.lock().unwrap();
let instance = &mut (*guard);
let verifier = instance.state.get_verifier_mut(VerifierId(id.0)).unwrap();
if let Poll::Ready(output) = verifier.finish_verify(&mut instance.cx).unwrap() {
Ok(abi::verify::VerifyReturn::Ready(Ok(bincode::serialize(
&output,
)
.unwrap())))
} else {
Ok(abi::verify::VerifyReturn::Pending)
}
}
fn close(&mut self, self_: Resource<VerifierId>) -> wasmtime::Result<abi::verify::CloseReturn> {
let id = VerifierId(self_.rep() as usize);
let mut guard = self.inner.lock().unwrap();
let instance = &mut (*guard);
let verifier = instance.state.get_verifier_mut(VerifierId(id.0)).unwrap();
if let Poll::Ready(()) = verifier.close(&mut instance.cx).unwrap() {
Ok(abi::verify::CloseReturn::Ready)
} else {
Ok(abi::verify::CloseReturn::Pending)
}
}
fn drop(&mut self, rep: Resource<VerifierId>) -> wasmtime::Result<()> {
Ok(())
}
}
use crate::Binary;
pub struct Wasmtime {
engine: Engine,
components: Vec<Component>,
instances: Vec<(Plugin, Store<InstanceState>)>,
}
impl Wasmtime {
pub fn new() -> Self {
Self {
engine: Engine::default(),
components: vec![],
instances: vec![],
}
}
}
impl WasmRuntime for Wasmtime {
fn load(&mut self, bin: &Binary) -> Result<WasmId, Error> {
let id = self.components.len();
let component = Component::from_binary(&self.engine, &bin.0).unwrap();
self.components.push(component);
Ok(WasmId(id))
}
fn instantiate(
&mut self,
id: WasmId,
instance: Arc<Mutex<instance::Instance>>,
arg: &[u8],
) -> Result<InstanceId, Error> {
let state = InstanceState { inner: instance };
let mut store = Store::new(&self.engine, state);
let mut linker = Linker::new(&self.engine);
generated::tlsn::tlsn::io::add_to_linker::<_, HasSelf<_>>(&mut linker, |state| state)
.unwrap();
generated::tlsn::tlsn::prove::add_to_linker::<_, HasSelf<_>>(&mut linker, |state| state)
.unwrap();
generated::tlsn::tlsn::verify::add_to_linker::<_, HasSelf<_>>(&mut linker, |state| state)
.unwrap();
let component = self.components.get(id.0).unwrap();
let instance = Plugin::instantiate(&mut store, component, &linker).unwrap();
instance.call_start(&mut store, arg).unwrap();
let id = self.instances.len();
self.instances.push((instance, store));
Ok(InstanceId(id))
}
fn poll(&mut self, id: InstanceId) -> Result<Poll<Result<Vec<u8>, String>>, Error> {
let (instance, store) = self.instances.get_mut(id.0).unwrap();
let res = match instance.call_poll(store).unwrap() {
generated::PollReturn::Pending => Poll::Pending,
generated::PollReturn::Ready(ret) => Poll::Ready(ret),
};
Ok(res)
}
}

View File

@@ -0,0 +1,84 @@
use std::{fmt::Display, pin::Pin};
use futures_plex::{DuplexStream, duplex};
use tlsn_sdk_core::{Binary, IoProvider, Manifest, Plugin, Runtime, Wasmtime};
use tlsn_server_fixture::bind;
#[test]
fn test_plugin() {
futures::executor::block_on(async {
let plugin = include_bytes!("../../sdk-plugin-test/sdk_plugin_test-component.wasm");
let plugin = Plugin {
manifest: Manifest {},
binary: Binary(plugin.to_vec()),
};
let (server_io_0, server_io_1) = duplex(1024);
let (io_0, io_1) = duplex(1024);
let mut rt_p = Runtime::new(
Wasmtime::new(),
DummyIo {
server_io: Some(server_io_0),
io: Some(io_0),
},
);
let mut rt_v = Runtime::new(
Wasmtime::new(),
DummyIo {
server_io: None,
io: Some(io_1),
},
);
let server_fut = bind(server_io_1);
futures::join!(
async {
let output = rt_p.run_plugin(&plugin, &[0]).await;
},
async {
let output = rt_v.run_plugin(&plugin, &[1]).await;
},
async {
server_fut.await.unwrap();
},
);
});
}
pub struct DummyIo {
server_io: Option<DuplexStream>,
io: Option<DuplexStream>,
}
#[derive(Debug)]
pub struct Error;
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Error")
}
}
impl std::error::Error for Error {}
impl IoProvider for DummyIo {
type Io = DuplexStream;
type Error = Error;
fn connect_server(
&mut self,
_name: &tlsn::connection::ServerName,
) -> Pin<Box<dyn Future<Output = Result<Self::Io, Self::Error>> + Send>> {
let io = self.server_io.take().unwrap();
Box::pin(async move { Ok(io) })
}
fn connect_peer(
&mut self,
) -> Pin<Box<dyn Future<Output = Result<Self::Io, Self::Error>> + Send>> {
let io = self.io.take().unwrap();
Box::pin(async move { Ok(io) })
}
}

View File

@@ -0,0 +1,115 @@
package tlsn:tlsn;
world plugin {
import prove;
import verify;
variant poll-return {
pending,
ready(result<list<u8>, string>)
}
export start: func(arg: list<u8>);
export poll: func() -> poll-return;
}
interface prove {
use io.{io};
variant setup-return {
pending,
ready
}
variant connect-return {
pending,
ready(io)
}
variant commit-return {
pending,
ready(result<list<u8>, string>)
}
variant prove-return {
pending,
ready(result<list<u8>, string>)
}
variant close-return {
pending,
ready,
}
resource prover {
constructor(config: list<u8>);
setup: func() -> setup-return;
connect: func() -> connect-return;
finish-commit: func() -> commit-return;
prove: func(config: list<u8>);
finish-prove: func() -> prove-return;
close: func() -> close-return;
}
}
interface verify {
variant setup-return {
pending,
ready
}
variant commit-return {
pending,
ready(result<list<u8>, string>)
}
variant verify-return {
pending,
ready(result<list<u8>, string>)
}
variant close-return {
pending,
ready
}
resource verifier {
constructor(config: list<u8>);
setup: func() -> setup-return;
commit: func() -> commit-return;
verify: func(config: list<u8>);
finish-verify: func() -> verify-return;
close: func() -> close-return;
}
}
interface io {
variant error {
closed,
other(string),
}
variant check-write-return {
pending,
ready(result<u32, error>)
}
variant close-return {
pending,
ready(result<_, error>)
}
variant read-return {
pending,
ready(result<list<u8>, error>)
}
resource io {
check-write: func() -> check-write-return;
write: func(buf: list<u8>) -> result<_, error>;
close: func() -> close-return;
read: func(len: u32) -> read-return;
}
}

View File

@@ -0,0 +1,5 @@
[build]
#target = "wasm32-unknown-unknown"
[target.wasm32-unknown-unknown]
rustflags = ["--cfg", 'getrandom_backend="custom"']

View File

@@ -0,0 +1,15 @@
[package]
name = "sdk-plugin-test"
version = "0.1.0"
edition = "2024"
[lib]
crate-type = ["cdylib"]
[dependencies]
tlsn-pdk = { workspace = true }
futures = { workspace = true }
tlsn-server-fixture-certs = { workspace = true }
[lints]
workspace = true

View File

@@ -0,0 +1,106 @@
use futures::{AsyncReadExt, AsyncWriteExt};
use tlsn_pdk::{
config::{
ProtocolConfig, ProtocolConfigValidator, ProverConfig, TlsConfig, VerifierConfig,
VerifyConfig,
},
connection::ServerName,
entry,
prover::{ProveConfig, Prover},
verifier::Verifier,
webpki::{CertificateDer, RootCertStore},
};
use tlsn_server_fixture_certs::CA_CERT_DER;
async fn main(arg: Vec<u8>) -> Result<Vec<u8>, String> {
if arg[0] == 0 {
prover().await
} else {
verifier().await
}
}
entry!(main);
async fn prover() -> Result<Vec<u8>, String> {
let name = ServerName::Dns("test-server.io".try_into().unwrap());
let mut builder = TlsConfig::builder();
builder.root_store(RootCertStore {
roots: vec![CertificateDer(CA_CERT_DER.to_vec())],
});
let tls_config = builder.build().unwrap();
let config = ProverConfig::builder()
.server_name(name)
.tls_config(tls_config)
.protocol_config(
ProtocolConfig::builder()
.max_sent_data(1024)
.max_recv_data(1024)
.build()
.unwrap(),
)
.build()
.unwrap();
let mut prover = Prover::new(config).setup().await.unwrap();
let (mut conn, prover_fut) = prover.connect().await.unwrap();
let (response, prover) = futures::join!(
async {
conn.write_all(b"GET / HTTP/1.1\r\nConnection: close\r\n\r\n")
.await
.unwrap();
conn.close().await.unwrap();
let mut response = vec![0u8; 1024];
conn.read_to_end(&mut response).await.unwrap();
response
},
prover_fut,
);
let mut prover = prover.unwrap();
let mut builder = ProveConfig::builder(prover.transcript());
let output = prover.prove(&builder.build().unwrap()).await.unwrap();
prover.close().await.unwrap();
Ok(response)
}
async fn verifier() -> Result<Vec<u8>, String> {
let config = VerifierConfig::builder()
.root_store(RootCertStore {
roots: vec![CertificateDer(CA_CERT_DER.to_vec())],
})
.protocol_config_validator(
ProtocolConfigValidator::builder()
.max_sent_data(1024)
.max_recv_data(4096)
.build()
.unwrap(),
)
.build()
.unwrap();
let mut verifier = Verifier::new(config)
.setup()
.await
.unwrap()
.run()
.await
.unwrap();
let output = verifier.verify(&VerifyConfig::default()).await.unwrap();
verifier.close().await.unwrap();
Ok(vec![])
}

View File

@@ -22,6 +22,7 @@ handshake = []
ghash = []
logging = ["tracing"]
prf = ["dep:hmac", "dep:sha2"]
web = ["dep:web-time"]
[dependencies]
futures = { workspace = true }
@@ -34,6 +35,6 @@ serde = { workspace = true, optional = true, features = ["derive"] }
sha2 = { workspace = true, optional = true }
thiserror = { workspace = true }
tracing = { workspace = true, optional = true }
web-time = { workspace = true }
web-time = { workspace = true, optional = true }
rustls-webpki = { workspace = true, features = ["ring"] }
rustls-pki-types = { workspace = true }

View File

@@ -1,5 +1,9 @@
use crate::msgs::enums::{AlertDescription, ContentType, HandshakeType};
use std::{error::Error as StdError, fmt};
#[cfg(not(feature = "web"))]
use std::time::SystemTimeError;
#[cfg(feature = "web")]
use web_time::SystemTimeError;
/// rustls reports protocol errors using this type.

View File

@@ -1,3 +1,6 @@
#[cfg(not(feature = "web"))]
use std::time::SystemTime;
#[cfg(feature = "web")]
use web_time::SystemTime;
use crate::{

View File

@@ -10,6 +10,10 @@ use crate::{
};
use ring::digest::Digest;
use rustls_pki_types as pki_types;
#[cfg(not(feature = "web"))]
use std::time::{SystemTime, UNIX_EPOCH};
#[cfg(feature = "web")]
use web_time::{SystemTime, UNIX_EPOCH};
type SignatureAlgorithms = &'static [&'static dyn pki_types::SignatureVerificationAlgorithm];

View File

@@ -5,7 +5,6 @@
#![forbid(unsafe_code)]
pub(crate) mod commit;
pub mod config;
pub(crate) mod context;
pub(crate) mod encoding;
pub(crate) mod ghash;
@@ -17,7 +16,7 @@ pub mod verifier;
pub(crate) mod zk_aes_ctr;
pub use tlsn_attestation as attestation;
pub use tlsn_core::{connection, hash, transcript};
pub use tlsn_core::{config, connection, hash, transcript, webpki};
/// The party's role in the TLSN protocol.
///
@@ -29,3 +28,30 @@ pub(crate) enum Role {
/// The verifier.
Verifier,
}
use mpc_tls::Config;
use tlsn_core::config::{NetworkSetting, ProtocolConfig};
pub(crate) fn build_mpc_tls_config(config: &ProtocolConfig) -> Config {
let mut builder = Config::builder();
builder
.defer_decryption(config.defer_decryption_from_start())
.max_sent(config.max_sent_data())
.max_recv_online(config.max_recv_data_online())
.max_recv(config.max_recv_data());
if let Some(max_sent_records) = config.max_sent_records() {
builder.max_sent_records(max_sent_records);
}
if let Some(max_recv_records_online) = config.max_recv_records_online() {
builder.max_recv_records_online(max_recv_records_online);
}
if let NetworkSetting::Latency = config.network() {
builder.low_bandwidth();
}
builder.build().unwrap()
}

View File

@@ -1,15 +1,17 @@
//! Prover.
mod config;
mod error;
mod future;
pub mod state;
pub use config::{ProverConfig, ProverConfigBuilder, TlsConfig, TlsConfigBuilder};
pub use error::ProverError;
pub use future::ProverFuture;
use rustls_pki_types::CertificateDer;
pub use tlsn_core::{ProveConfig, ProveConfigBuilder, ProveConfigBuilderError, ProverOutput};
pub use tls_client_async::TlsConnection;
pub use tlsn_core::{
ProverOutput,
config::{ProveConfig, ProveConfigBuilder, ProveConfigBuilderError},
};
use mpz_common::Context;
use mpz_core::Block;
@@ -18,7 +20,7 @@ use mpz_vm_core::prelude::*;
use webpki::anchor_from_trusted_cert;
use crate::{
Role,
Role, build_mpc_tls_config,
commit::{
commit_records,
hash::prove_hash,
@@ -37,10 +39,11 @@ use rand::Rng;
use serio::SinkExt;
use std::sync::Arc;
use tls_client::{ClientConnection, ServerName as TlsServerName};
use tls_client_async::{TlsConnection, bind_client};
use tls_client_async::bind_client;
use tls_core::msgs::enums::ContentType;
use tlsn_core::{
ProvePayload,
config::ProverConfig,
connection::{HandshakeData, ServerName},
hash::{Blake3, HashAlgId, HashAlgorithm, Keccak256, Sha256},
transcript::{TlsTranscript, Transcript, TranscriptCommitment, TranscriptSecret},
@@ -530,7 +533,7 @@ fn build_mpc_tls(config: &ProverConfig, ctx: Context) -> (Arc<Mutex<Deap<Mpc, Zk
(
vm.clone(),
MpcTlsLeader::new(
config.build_mpc_tls_config(),
build_mpc_tls_config(config.protocol_config()),
ctx,
vm,
(rcot_send.clone(), rcot_send.clone(), rcot_send),

View File

@@ -1,144 +0,0 @@
use mpc_tls::Config;
use serde::{Deserialize, Serialize};
use tlsn_core::{
connection::ServerName,
webpki::{CertificateDer, PrivateKeyDer, RootCertStore},
};
use crate::config::{NetworkSetting, ProtocolConfig};
/// Configuration for the prover.
#[derive(Debug, Clone, derive_builder::Builder, Serialize, Deserialize)]
pub struct ProverConfig {
/// The server DNS name.
#[builder(setter(into))]
server_name: ServerName,
/// Protocol configuration to be checked with the verifier.
protocol_config: ProtocolConfig,
/// TLS configuration.
#[builder(default)]
tls_config: TlsConfig,
}
impl ProverConfig {
/// Creates a new builder for `ProverConfig`.
pub fn builder() -> ProverConfigBuilder {
ProverConfigBuilder::default()
}
/// Returns the server DNS name.
pub fn server_name(&self) -> &ServerName {
&self.server_name
}
/// Returns the protocol configuration.
pub fn protocol_config(&self) -> &ProtocolConfig {
&self.protocol_config
}
/// Returns the TLS configuration.
pub fn tls_config(&self) -> &TlsConfig {
&self.tls_config
}
pub(crate) fn build_mpc_tls_config(&self) -> Config {
let mut builder = Config::builder();
builder
.defer_decryption(self.protocol_config.defer_decryption_from_start())
.max_sent(self.protocol_config.max_sent_data())
.max_recv_online(self.protocol_config.max_recv_data_online())
.max_recv(self.protocol_config.max_recv_data());
if let Some(max_sent_records) = self.protocol_config.max_sent_records() {
builder.max_sent_records(max_sent_records);
}
if let Some(max_recv_records_online) = self.protocol_config.max_recv_records_online() {
builder.max_recv_records_online(max_recv_records_online);
}
if let NetworkSetting::Latency = self.protocol_config.network() {
builder.low_bandwidth();
}
builder.build().unwrap()
}
}
/// Configuration for the prover's TLS connection.
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct TlsConfig {
/// Root certificates.
root_store: Option<RootCertStore>,
/// Certificate chain and a matching private key for client
/// authentication.
client_auth: Option<(Vec<CertificateDer>, PrivateKeyDer)>,
}
impl TlsConfig {
/// Creates a new builder for `TlsConfig`.
pub fn builder() -> TlsConfigBuilder {
TlsConfigBuilder::default()
}
pub(crate) fn root_store(&self) -> Option<&RootCertStore> {
self.root_store.as_ref()
}
/// Returns a certificate chain and a matching private key for client
/// authentication.
pub fn client_auth(&self) -> &Option<(Vec<CertificateDer>, PrivateKeyDer)> {
&self.client_auth
}
}
/// Builder for [`TlsConfig`].
#[derive(Debug, Default)]
pub struct TlsConfigBuilder {
root_store: Option<RootCertStore>,
client_auth: Option<(Vec<CertificateDer>, PrivateKeyDer)>,
}
impl TlsConfigBuilder {
/// Sets the root certificates to use for verifying the server's
/// certificate.
pub fn root_store(&mut self, store: RootCertStore) -> &mut Self {
self.root_store = Some(store);
self
}
/// Sets a DER-encoded certificate chain and a matching private key for
/// client authentication.
///
/// Often the chain will consist of a single end-entity certificate.
///
/// # Arguments
///
/// * `cert_key` - A tuple containing the certificate chain and the private
/// key.
///
/// - Each certificate in the chain must be in the X.509 format.
/// - The key must be in the ASN.1 format (either PKCS#8 or PKCS#1).
pub fn client_auth(&mut self, cert_key: (Vec<CertificateDer>, PrivateKeyDer)) -> &mut Self {
self.client_auth = Some(cert_key);
self
}
/// Builds the TLS configuration.
pub fn build(self) -> Result<TlsConfig, TlsConfigError> {
Ok(TlsConfig {
root_store: self.root_store,
client_auth: self.client_auth,
})
}
}
/// TLS configuration error.
#[derive(Debug, thiserror::Error)]
#[error(transparent)]
pub struct TlsConfigError(#[from] ErrorRepr);
#[derive(Debug, thiserror::Error)]
#[error("tls config error")]
enum ErrorRepr {}

View File

@@ -1,26 +1,24 @@
//! Verifier.
pub(crate) mod config;
mod error;
pub mod state;
use std::sync::Arc;
pub use config::{VerifierConfig, VerifierConfigBuilder, VerifierConfigBuilderError};
pub use error::VerifierError;
pub use tlsn_core::{
VerifierOutput, VerifyConfig, VerifyConfigBuilder, VerifyConfigBuilderError,
VerifierOutput,
config::{VerifyConfig, VerifyConfigBuilder, VerifyConfigBuilderError},
webpki::ServerCertVerifier,
};
use crate::{
Role,
Role, build_mpc_tls_config,
commit::{
commit_records,
hash::verify_hash,
transcript::{TranscriptRefs, decode_transcript, verify_transcript},
},
config::ProtocolConfig,
context::build_mt_context,
encoding,
mux::attach_mux,
@@ -37,6 +35,7 @@ use serio::stream::IoStreamExt;
use tls_core::msgs::enums::ContentType;
use tlsn_core::{
ProvePayload,
config::{ProtocolConfig, VerifierConfig},
connection::{ConnectionInfo, ServerName},
transcript::{TlsTranscript, TranscriptCommitment},
};
@@ -481,7 +480,7 @@ fn build_mpc_tls(
(
vm.clone(),
MpcTlsFollower::new(
config.build_mpc_tls_config(protocol_config),
build_mpc_tls_config(protocol_config),
ctx,
vm,
rcot_send,

View File

@@ -1,65 +0,0 @@
use std::fmt::{Debug, Formatter, Result};
use mpc_tls::Config;
use serde::{Deserialize, Serialize};
use tlsn_core::webpki::RootCertStore;
use crate::config::{NetworkSetting, ProtocolConfig, ProtocolConfigValidator};
/// Configuration for the [`Verifier`](crate::tls::Verifier).
#[allow(missing_docs)]
#[derive(derive_builder::Builder, Serialize, Deserialize)]
#[builder(pattern = "owned")]
pub struct VerifierConfig {
protocol_config_validator: ProtocolConfigValidator,
#[builder(setter(strip_option))]
root_store: Option<RootCertStore>,
}
impl Debug for VerifierConfig {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
f.debug_struct("VerifierConfig")
.field("protocol_config_validator", &self.protocol_config_validator)
.finish_non_exhaustive()
}
}
impl VerifierConfig {
/// Creates a new configuration builder.
pub fn builder() -> VerifierConfigBuilder {
VerifierConfigBuilder::default()
}
/// Returns the protocol configuration validator.
pub fn protocol_config_validator(&self) -> &ProtocolConfigValidator {
&self.protocol_config_validator
}
/// Returns the root certificate store.
pub fn root_store(&self) -> Option<&RootCertStore> {
self.root_store.as_ref()
}
pub(crate) fn build_mpc_tls_config(&self, protocol_config: &ProtocolConfig) -> Config {
let mut builder = Config::builder();
builder
.max_sent(protocol_config.max_sent_data())
.max_recv_online(protocol_config.max_recv_data_online())
.max_recv(protocol_config.max_recv_data());
if let Some(max_sent_records) = protocol_config.max_sent_records() {
builder.max_sent_records(max_sent_records);
}
if let Some(max_recv_records_online) = protocol_config.max_recv_records_online() {
builder.max_recv_records_online(max_recv_records_online);
}
if let NetworkSetting::Latency = protocol_config.network() {
builder.low_bandwidth();
}
builder.build().unwrap()
}
}

View File

@@ -86,8 +86,8 @@ impl From<std::io::Error> for VerifierError {
}
}
impl From<crate::config::ProtocolConfigError> for VerifierError {
fn from(e: crate::config::ProtocolConfigError) -> Self {
impl From<tlsn_core::config::ProtocolConfigError> for VerifierError {
fn from(e: tlsn_core::config::ProtocolConfigError) -> Self {
Self::new(ErrorKind::Config, e)
}
}

View File

@@ -1,10 +1,11 @@
use futures::{AsyncReadExt, AsyncWriteExt};
use tlsn::{
config::{CertificateDer, ProtocolConfig, ProtocolConfigValidator, RootCertStore},
config::{ProtocolConfig, ProtocolConfigValidator, ProverConfig, TlsConfig, VerifierConfig},
connection::ServerName,
prover::{ProveConfig, Prover, ProverConfig, TlsConfig},
prover::{ProveConfig, Prover},
transcript::{TranscriptCommitConfig, TranscriptCommitment},
verifier::{Verifier, VerifierConfig, VerifierOutput, VerifyConfig},
verifier::{Verifier, VerifierOutput, VerifyConfig},
webpki::{CertificateDer, RootCertStore},
};
use tlsn_server_fixture::bind;
use tlsn_server_fixture_certs::{CA_CERT_DER, SERVER_DOMAIN};