mirror of
https://github.com/tlsnotary/tlsn.git
synced 2026-01-09 13:27:59 -05:00
wip
This commit is contained in:
1647
Cargo.lock
generated
1647
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -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" }
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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::*;
|
||||
@@ -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)]
|
||||
|
||||
@@ -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
16
crates/pdk/Cargo.toml
Normal 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
74
crates/pdk/src/abi.rs
Normal 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
21
crates/pdk/src/lib.rs
Normal 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
257
crates/pdk/src/prover.rs
Normal 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
131
crates/pdk/src/verifier.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
19
crates/sdk-core/Cargo.toml
Normal file
19
crates/sdk-core/Cargo.toml
Normal 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
|
||||
155
crates/sdk-core/src/instance.rs
Normal file
155
crates/sdk-core/src/instance.rs
Normal 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
262
crates/sdk-core/src/io.rs
Normal 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
105
crates/sdk-core/src/lib.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
388
crates/sdk-core/src/prover.rs
Normal file
388
crates/sdk-core/src/prover.rs
Normal 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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
18
crates/sdk-core/src/provider.rs
Normal file
18
crates/sdk-core/src/provider.rs
Normal 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>>;
|
||||
}
|
||||
309
crates/sdk-core/src/verifier.rs
Normal file
309
crates/sdk-core/src/verifier.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
24
crates/sdk-core/src/wasm.rs
Normal file
24
crates/sdk-core/src/wasm.rs
Normal 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>;
|
||||
}
|
||||
399
crates/sdk-core/src/wasmtime.rs
Normal file
399
crates/sdk-core/src/wasmtime.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
84
crates/sdk-core/tests/test.rs
Normal file
84
crates/sdk-core/tests/test.rs
Normal 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) })
|
||||
}
|
||||
}
|
||||
115
crates/sdk-core/wit/tlsn.wit
Normal file
115
crates/sdk-core/wit/tlsn.wit
Normal 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;
|
||||
}
|
||||
}
|
||||
5
crates/sdk-plugin-test/.cargo/config.toml
Normal file
5
crates/sdk-plugin-test/.cargo/config.toml
Normal file
@@ -0,0 +1,5 @@
|
||||
[build]
|
||||
#target = "wasm32-unknown-unknown"
|
||||
|
||||
[target.wasm32-unknown-unknown]
|
||||
rustflags = ["--cfg", 'getrandom_backend="custom"']
|
||||
15
crates/sdk-plugin-test/Cargo.toml
Normal file
15
crates/sdk-plugin-test/Cargo.toml
Normal 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
|
||||
106
crates/sdk-plugin-test/src/lib.rs
Normal file
106
crates/sdk-plugin-test/src/lib.rs
Normal 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![])
|
||||
}
|
||||
@@ -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 }
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
#[cfg(not(feature = "web"))]
|
||||
use std::time::SystemTime;
|
||||
#[cfg(feature = "web")]
|
||||
use web_time::SystemTime;
|
||||
|
||||
use crate::{
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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 {}
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
|
||||
Reference in New Issue
Block a user