Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
305 changes: 223 additions & 82 deletions Cargo.lock

Large diffs are not rendered by default.

21 changes: 14 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
[workspace]
members =["client", "notary", "core", "tests"]
members =["client", "notary", "core", "tests", "executor"]
resolver="2"

[workspace.dependencies]
# Local re-exporting
web-prover-client={ path="client" }
web-prover-core ={ path="core" }
web-prover-notary={ path="notary" }
web-prover-client ={ path="client" }
web-prover-core ={ path="core" }
web-prover-executor={ path="executor" }
web-prover-notary ={ path="notary" }
# Serde
serde ={ version="1.0.204", features=["derive"] }
serde_json="1.0.120"
Expand All @@ -27,9 +28,12 @@ hyper ={ version="1.6", features=["full"] }
hyper-util ={ version="0.1", features=["full"] }

# Async
tokio ={ version="1.39.1", features=["full"] }
tokio-rustls={ version="0.26.0", default-features=false, features=["logging", "tls12"] }
tokio-util ={ version="0.7" }
axum ={ version="0.7", features=["ws", "json"] }
axum-core ="0.4"
tokio ={ version="1.39.1", features=["full"] }
tokio-rustls ={ version="0.26.0", default-features=false, features=["logging", "tls12"] }
tokio-tungstenite={ version="0.26.2", features=["native-tls", "rustls"] }
tokio-util ={ version="0.7" }

chrono ="0.4"
derive_more={ version="2.0.1", features=["full"] }
Expand All @@ -38,6 +42,9 @@ uuid ={ version="1.10.0", default-features=false, features=["v4", "serde"]

tracing-test="0.2"

tempfile ="3.18.0"
wait-timeout="0.2.1"

[profile.dev]
incremental =true
opt-level =1
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,13 @@ If you have any questions, please reach out to any of Pluto's [team members](htt
- `client`: contains components for the client that are shared across both WASM and iOS targets.
- `fixture`: contains testing artifacts such as TLS certificates and configuration files.
- `notary`: notary server which can notarize TEE proofs.
- `core`: core features of web proofs, i.e. manifest validation, parser, extraction.

### Usage

```
cargo run -p notary -- --config ./fixture/notary-config.toml
cargo run -p client -- --config ./fixture/client.proxy.json
cargo run -p web-prover-notary -- --config ./fixture/notary-config.toml
cargo run -p web-prover-client -- --config ./fixture/client.proxy.json
```

## Security Status
Expand Down
19 changes: 12 additions & 7 deletions client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ unsafe_skip_cert_verification=[]

# Shared dependencies for all targets
[dependencies]
bytes ="1"
pki-types ={ package="rustls-pki-types", version="1.7" }
web-prover-core={ workspace=true }
webpki-roots ="0.26.1"

bytes ="1"
pki-types ={ package="rustls-pki-types", version="1.7" }
webpki-roots="0.26.1"
# Serde
serde ={ workspace=true }
serde_json={ workspace=true }
Expand Down Expand Up @@ -45,8 +46,12 @@ uuid ={ workspace=true }
# Web
hyper-util={ workspace=true }
# Async
rustls ={ version="0.23", default-features=false, features=["ring"] }
tokio ={ workspace=true, features=["rt", "rt-multi-thread", "macros", "net", "io-std", "fs"] }
tokio-rustls={ version="0.26", default-features=false, features=["logging", "tls12"] }
rustls ={ version="0.23", default-features=false, features=["ring"] }
tokio ={ workspace=true, features=["rt", "rt-multi-thread", "macros", "net", "io-std", "fs"] }
tokio-rustls ={ version="0.26", default-features=false, features=["logging", "tls12"] }
tokio-tungstenite={ workspace=true }
# TLSN
reqwest={ version="0.12", features=["json", "rustls-tls"] }
native-tls="0.2.14"
reqwest ={ version="0.12", features=["json", "rustls-tls"] }

[dev-dependencies]
5 changes: 0 additions & 5 deletions client/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::collections::HashMap;

use serde::Deserialize;
use serde_with::{
base64::{Base64, Standard},
Expand All @@ -20,9 +18,6 @@ pub struct Config {
// this is helpful for local debugging with self-signed certs
#[serde_as(as = "Option<Base64<Standard, Padded>>")]
pub notary_ca_cert: Option<Vec<u8>>,
pub target_method: String,
pub target_url: String,
pub target_headers: HashMap<String, String>,
pub target_body: String,
pub manifest: Manifest,
#[serde(skip)]
Expand Down
219 changes: 219 additions & 0 deletions client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,47 @@ pub mod config;
pub mod error;
use std::collections::HashMap;

use futures::{SinkExt, StreamExt};
use serde::{Deserialize, Serialize};
use tokio_tungstenite::tungstenite::{client::IntoClientRequest, Message::Text};
use tracing::debug;
use web_prover_core::{
frame::{
Action, InitialInput, PromptResponse,
View::{self, InitialView, PromptView},
},
manifest::Manifest,
proof::{SignedVerificationReply, TeeProof},
};

use crate::error::WebProverClientError;

const EXAMPLE_DEVELOPER_SCRIPT: &str = r#"
await page.goto("https://pseudo-bank.pluto.dev");

const username = page.getByRole("textbox", { name: "Username" });
const password = page.getByRole("textbox", { name: "Password" });

let input = await prompt([
{ title: "Username", types: "text" },
{ title: "Password", types: "password" },
]);

await username.fill(input.inputs[0]);
await password.fill(input.inputs[1]);

const loginBtn = page.getByRole("button", { name: "Login" });
await loginBtn.click();

await page.waitForSelector("text=Your Accounts", { timeout: 5000 });

const balanceLocator = page.locator("\#balance-2");
await balanceLocator.waitFor({ state: "visible", timeout: 5000 });
const balanceText = (await balanceLocator.textContent()) || "";
const balance = parseFloat(balanceText.replace(/[$,]/g, ""));

await prove("bank_balance", balance);
"#;
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ProxyConfig {
pub target_method: String,
Expand Down Expand Up @@ -82,3 +114,190 @@ pub async fn verify<T: Serialize>(

Ok(verify_response)
}

pub async fn frame() {
let config = std::fs::read("./fixture/client.proxy.json").unwrap();
let mut config: config::Config = serde_json::from_slice(&config).unwrap();
config.set_session_id();

let url = format!(
"wss://{}:{}/v1/frame?session_id={}",
config.notary_host.clone(),
config.notary_port.clone(),
config.session_id
);
debug!("url={}", url);

// Set up TLS connector that accepts your server certificate
let mut connector_builder = native_tls::TlsConnector::builder();

// For testing only: disable certificate verification
// WARNING: Only use this for testing, never in production
connector_builder.danger_accept_invalid_certs(true);

let connector = connector_builder.build().unwrap();
let connector = native_tls::TlsConnector::from(connector);

// Connect with TLS
let request = url.into_client_request().unwrap();
let (mut ws_stream, response) = tokio_tungstenite::connect_async_tls_with_config(
request,
None,
false,
Some(tokio_tungstenite::Connector::NativeTls(connector)),
)
.await
.unwrap();

// assert!(response.status().is_success(), "WebSocket connection failed");
debug!("response={:?}", response);

let ws_spawn = tokio::spawn(async move {
while let Some(message) = ws_stream.next().await {
let message = message.unwrap();
debug!("message={:?}", message);

match message {
Text(text) => {
let view: View = serde_json::from_str(&text).unwrap();
match view {
InitialView => {
debug!("Received InitialView");
let action = Action {
kind: "initial_input".to_owned(),
payload: serde_json::to_value(InitialInput {
script: EXAMPLE_DEVELOPER_SCRIPT.to_owned(),
})
.unwrap(),
};
ws_stream.send(Text(serde_json::to_string(&action).unwrap().into())).await.unwrap();
},
PromptView { prompts } => {
debug!("Received PromptView with prompts: {:?}", prompts);
let prompt_response = PromptResponse {
inputs: prompts.iter().map(|prompt| prompt.title.clone()).collect(),
};
let action = Action {
kind: "prompt_response".to_owned(),
payload: serde_json::to_value(prompt_response).unwrap(),
};
ws_stream.send(Text(serde_json::to_string(&action).unwrap().into())).await.unwrap();
},
View::ProveView { proof } => {
debug!("Received ProveView with proof: {:?}", proof);

ws_stream.close(None).await.unwrap();
},
}
},
_ => panic!("unexpected message"),
};
}
});

match ws_spawn.await {
Ok(_) => debug!("WebSocket task completed"),
Err(e) => debug!("WebSocket task failed: {:?}", e),
}
}

#[cfg(test)]
mod tests {
use futures::{SinkExt, StreamExt};
use tokio_tungstenite::tungstenite::{client::IntoClientRequest, Message::Text};
use web_prover_core::frame::{
Action, InitialInput, PromptResponse,
View::{self, InitialView, PromptView},
};

use super::*;

#[tokio::test]
#[tracing::instrument]
async fn test_frame() {
let config = std::fs::read("../fixture/client.proxy.json").unwrap();
let mut config: config::Config = serde_json::from_slice(&config).unwrap();
config.set_session_id();

let url = format!(
"wss://{}:{}/v1/frame?session_id={}",
config.notary_host.clone(),
config.notary_port.clone(),
config.session_id
);
println!("url={}", url);

// Set up TLS connector that accepts your server certificate
let mut connector_builder = native_tls::TlsConnector::builder();

// For testing only: disable certificate verification
// WARNING: Only use this for testing, never in production
connector_builder.danger_accept_invalid_certs(true);

let connector = connector_builder.build().unwrap();
let connector = native_tls::TlsConnector::from(connector);

// Connect with TLS
let request = url.into_client_request().unwrap();
let (mut ws_stream, response) = tokio_tungstenite::connect_async_tls_with_config(
request,
None,
false,
Some(tokio_tungstenite::Connector::NativeTls(connector)),
)
.await
.unwrap();

// assert!(response.status().is_success(), "WebSocket connection failed");
println!("response={:?}", response);

let ws_spawn = tokio::spawn(async move {
while let Some(message) = ws_stream.next().await {
let message = message.unwrap();
println!("message={:?}", message);

match message {
Text(text) => {
let view: View = serde_json::from_str(&text).unwrap();
match view {
InitialView => {
println!("InitialView");
let action = Action {
kind: "initial_input".to_owned(),
payload: serde_json::to_value(InitialInput {
script: EXAMPLE_DEVELOPER_SCRIPT.to_owned(),
})
.unwrap(),
};
ws_stream.send(Text(serde_json::to_string(&action).unwrap().into())).await.unwrap();
},
PromptView { prompts } => {
println!("Received PromptView with prompts: {:?}", prompts);
let prompt_response = PromptResponse {
inputs: prompts.iter().map(|prompt| prompt.title.clone()).collect(),
};
let action = Action {
kind: "prompt_response".to_owned(),
payload: serde_json::to_value(prompt_response).unwrap(),
};
ws_stream.send(Text(serde_json::to_string(&action).unwrap().into())).await.unwrap();
println!("Sent prompt response: {:?}", action);
},
View::ProveView { proof } => {
println!("Received ProveView with proof: {:?}", proof);

ws_stream.close(None).await.unwrap();
},
}
},
_ => panic!("unexpected message"),
};
}
});

match ws_spawn.await {
Ok(_) => println!("WebSocket task completed"),
Err(e) => println!("WebSocket task failed: {:?}", e),
}
}
}
7 changes: 4 additions & 3 deletions client/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ async fn main() -> Result<(), WebProverClientError> {
let mut config: Config = serde_json::from_str(&config_json)?;
config.set_session_id();

let proof = web_prover_client::proxy(config).await?;
let proof_json = serde_json::to_string_pretty(&proof)?;
println!("Proving Successful: proof_len={:?}", proof_json.len());
// let proof = web_prover_client::proxy(config).await?;
// let proof_json = serde_json::to_string_pretty(&proof)?;
// println!("Proving Successful: proof_len={:?}", proof_json.len());
web_prover_client::frame().await;
Ok(())
}
Loading
Loading