Skip to content

Commit

Permalink
polish attestation example
Browse files Browse the repository at this point in the history
  • Loading branch information
sinui0 committed Oct 3, 2024
1 parent 281d15a commit 90f3683
Show file tree
Hide file tree
Showing 9 changed files with 317 additions and 336 deletions.
26 changes: 26 additions & 0 deletions crates/core/src/transcript/proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,32 @@ impl<'a> TranscriptProofBuilder<'a> {
self.reveal_with_kind(ranges, direction, self.default_kind)
}

/// Reveals the given ranges in the sent transcript using the default kind
/// of commitment.
///
/// # Arguments
///
/// * `ranges` - The ranges to reveal.
pub fn reveal_sent(
&mut self,
ranges: &dyn ToRangeSet<usize>,
) -> Result<&mut Self, TranscriptProofBuilderError> {
self.reveal(ranges, Direction::Sent)
}

/// Reveals the given ranges in the received transcript using the default
/// kind of commitment.
///
/// # Arguments
///
/// * `ranges` - The ranges to reveal.
pub fn reveal_recv(
&mut self,
ranges: &dyn ToRangeSet<usize>,
) -> Result<&mut Self, TranscriptProofBuilderError> {
self.reveal(ranges, Direction::Received)
}

/// Builds the transcript proof.
pub fn build(self) -> Result<TranscriptProof, TranscriptProofBuilderError> {
let encoding_proof = if !self.encoding_proof_idxs.is_empty() {
Expand Down
2 changes: 2 additions & 0 deletions crates/examples/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Ignore files from examples.
*.tlsn
15 changes: 11 additions & 4 deletions crates/examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@ tlsn-core = { workspace = true }
tlsn-prover = { workspace = true }
tlsn-utils = { workspace = true }
tlsn-verifier = { workspace = true }
tlsn-formats = { workspace = true }

bincode = { workspace = true }
chrono = { workspace = true }
dotenv = { version = "0.15.0" }
elliptic-curve = { workspace = true, features = ["pkcs8"] }
futures = { workspace = true }
http-body-util = { workspace = true }
hex = { workspace = true }
hyper = { workspace = true, features = ["client", "http1"] }
hyper-util = { workspace = true, features = ["full"] }
k256 = { workspace = true, features = ["ecdsa"] }
Expand All @@ -36,12 +39,16 @@ tracing = { workspace = true }
tracing-subscriber = { workspace = true }

[[example]]
name = "attestation_prover"
path = "attestation/prover.rs"
name = "attestation_prove"
path = "attestation/prove.rs"

[[example]]
name = "attestation_verifier"
path = "attestation/verifier.rs"
name = "attestation_present"
path = "attestation/present.rs"

[[example]]
name = "attestation_verify"
path = "attestation/verify.rs"

[[example]]
name = "interactive"
Expand Down
77 changes: 31 additions & 46 deletions crates/examples/attestation/README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
## Simple Example: Notarize Public Data from example.com (Rust) <a name="rust-simple"></a>
## Simple Attestation Example: Notarize Public Data from example.com (Rust) <a name="rust-simple"></a>

This example demonstrates the simplest possible use case for TLSNotary:
1. Notarize: Fetch <https://example.com/> and create a proof of its content.
2. Verify the proof.

Next, we will redact the content and verify it again:
1. Redact the `USER_AGENT` and titles.
2. Verify the redacted proof.
1. Fetch <https://example.com/> and acquire an attestation of its content.
2. Create a verifiable presentation using the attestation, while redacting the value of a header.
3. Verify the presentation.

### 1. Notarize <https://example.com/>

Run a simple prover:
Run the `prove` binary.

```shell
cargo run --release --example simple_prover
cargo run --release --example attestation_prove
```

If the notarization was successful, you should see this output in the console:
Expand All @@ -22,66 +19,54 @@ If the notarization was successful, you should see this output in the console:
Starting an MPC TLS connection with the server
Got a response from the server
Notarization completed successfully!
The proof has been written to `simple_proof.json`
The attestation has been written to `example.attestation.tlsn` and the corresponding secrets to `example.secrets.tlsn`.
```

⚠️ In this simple example the `Notary` server is automatically started in the background. Note that this is for demonstration purposes only. In a real work example, the notary should be run by a neutral party or the verifier of the proofs. Consult the [Notary Server Docs](https://docs.tlsnotary.org/developers/notary_server.html) for more details on how to run a notary server.
⚠️ In this simple example the `Notary` server is automatically started in the background. Note that this is for demonstration purposes only. In a real world example, the notary should be run by a trusted party. Consult the [Notary Server Docs](https://docs.tlsnotary.org/developers/notary_server.html) for more details on how to run a notary server.

### 2. Build a verifiable presentation

### 2. Verify the Proof
This will build a verifiable presentation with the `User-Agent` header redacted from the request. This presentation can be shared with any verifier you wish to present the data to.

When you open `simple_proof.json` in an editor, you will see a JSON file with lots of non-human-readable byte arrays. You can decode this file by running:
Run the `present` binary.

```shell
cargo run --release --example simple_verifier
cargo run --release --example attestation_present
```

This will output the TLS-transaction in clear text:
If successful, you should see this output in the console:

```log
Successfully verified that the bytes below came from a session with Dns("example.com") at 2023-11-03 08:48:20 UTC.
Note that the bytes which the Prover chose not to disclose are shown as X.
Bytes sent:
...
```

### 3. Redact Information

Open `simple_prover.rs` and locate the line with:

```rust
let redact = false;
Presentation built successfully!
The presentation has been written to `example.presentation.tlsn`.
```

and change it to:
### 3. Verify the presentation

```rust
let redact = true;
```
This will read the presentation from the previous step, verify it, and print the disclosed data to console.

Next, if you run the `simple_prover` and `simple_verifier` again, you'll notice redacted `X`'s in the output:
Run the `verify` binary.

```shell
cargo run --release --example simple_prover
cargo run --release --example simple_verifier
cargo run --release --example attestation_verify
```

If successful, you should see this output in the console:

```log
<!doctype html>
<html>
<head>
<title>XXXXXXXXXXXXXX</title>
...
```
Verifying presentation with {key algorithm} key: { hex encoded key }
You can also use <https://explorer.tlsnotary.org/> to inspect your proofs. Simply drag and drop `simple_proof.json` from your file explorer into the drop zone. Redacted bytes are marked with X characters. [Notary public key](../../notary/server/fixture/notary/notary.pub)
**Ask yourself, do you trust this key?**
### (Optional) Extra Experiments
-------------------------------------------------------------------
Successfully verified that the data below came from a session with example.com at 2024-10-03 03:01:40 UTC.
Note that the data which the Prover chose not to disclose are shown as X.
Feel free to try these extra challenges:
Data sent:
...
```

- [ ] Modify the `server_name` (or any other data) in `simple_proof.json` and verify that the proof is no longer valid.
- [ ] Modify the `build_proof_with_redactions` function in `simple_prover.rs` to redact more or different data.
⚠️ Notice that the presentation comes with a "verifying key". This is the key the Notary used when issuing the attestation that the presentation was built from. If you trust the Notary, or more specifically the verifying key, then you can trust that the presented data is authentic.

### Next steps

Expand Down
61 changes: 61 additions & 0 deletions crates/examples/attestation/present.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// This example demonstrates how to build a verifiable presentation from an
// attestation and the corresponding connection secrets. See the `prove.rs`
// example to learn how to acquire an attestation from a Notary.

use tlsn_core::{attestation::Attestation, presentation::Presentation, CryptoProvider, Secrets};
use tlsn_formats::http::HttpTranscript;

fn main() -> Result<(), Box<dyn std::error::Error>> {
// Read attestation from disk.
let attestation: Attestation =
bincode::deserialize(&std::fs::read("example.attestation.tlsn")?)?;

// Read secrets from disk.
let secrets: Secrets = bincode::deserialize(&std::fs::read("example.secrets.tlsn")?)?;

// Parse the HTTP transcript.
let transcript = HttpTranscript::parse(secrets.transcript())?;

// Build a transcript proof.
let mut builder = secrets.transcript_proof_builder();

let request = &transcript.requests[0];
// Reveal the structure of the request without the headers or body.
builder.reveal_sent(&request.without_data())?;
// Reveal the request target.
builder.reveal_sent(&request.request.target)?;
// Reveal all headers except the value of the User-Agent header.
for header in &request.headers {
if !header.name.as_str().eq_ignore_ascii_case("User-Agent") {
builder.reveal_sent(header)?;
} else {
builder.reveal_sent(&header.without_value())?;
}
}
// Reveal the entire response.
builder.reveal_recv(&transcript.responses[0])?;

let transcript_proof = builder.build()?;

// Use default crypto provider to build the presentation.
let provider = CryptoProvider::default();

let mut builder = attestation.presentation_builder(&provider);

builder
.identity_proof(secrets.identity_proof())
.transcript_proof(transcript_proof);

let presentation: Presentation = builder.build()?;

// Write the presentation to disk.
std::fs::write(
"example.presentation.tlsn",
bincode::serialize(&presentation)?,
)?;

println!("Presentation built successfully!");
println!("The presentation has been written to `example.presentation.tlsn`.");

Ok(())
}
126 changes: 126 additions & 0 deletions crates/examples/attestation/prove.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// This example demonstrates how to use the Prover to acquire an attestation for
// an HTTP request sent to example.com. The attestation and secrets are saved to
// disk.

use http_body_util::Empty;
use hyper::{body::Bytes, Request, StatusCode};
use hyper_util::rt::TokioIo;
use tokio::io::AsyncWriteExt as _;
use tokio_util::compat::{FuturesAsyncReadCompatExt, TokioAsyncReadCompatExt};

use tlsn_common::config::ProtocolConfig;
use tlsn_core::{request::RequestConfig, transcript::TranscriptCommitConfig};
use tlsn_examples::run_notary;
use tlsn_formats::http::{DefaultHttpCommitter, HttpCommit, HttpTranscript};
use tlsn_prover::{Prover, ProverConfig};

// Setting of the application server
const SERVER_DOMAIN: &str = "example.com";
const USER_AGENT: &str = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36";

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
tracing_subscriber::fmt::init();

let (prover_socket, notary_socket) = tokio::io::duplex(1 << 16);

// Start a local simple notary service
tokio::spawn(run_notary(notary_socket.compat()));

// Prover configuration.
let config = ProverConfig::builder()
.server_name(SERVER_DOMAIN)
.protocol_config(
ProtocolConfig::builder()
// We must configure the amount of data we expect to exchange beforehand, which will
// be preprocessed prior to the connection. Reducing these limits will improve
// performance.
.max_sent_data(1024)
.max_recv_data(4096)
.build()?,
)
.build()?;

// Create a new prover and perform necessary setup.
let prover = Prover::new(config).setup(prover_socket.compat()).await?;

// Open a TCP connection to the server.
let client_socket = tokio::net::TcpStream::connect((SERVER_DOMAIN, 443)).await?;

// Bind the prover to the server connection.
// The returned `mpc_tls_connection` is an MPC TLS connection to the server: all
// data written to/read from it will be encrypted/decrypted using MPC with
// the notary.
let (mpc_tls_connection, prover_fut) = prover.connect(client_socket.compat()).await?;
let mpc_tls_connection = TokioIo::new(mpc_tls_connection.compat());

// Spawn the prover task to be run concurrently in the background.
let prover_task = tokio::spawn(prover_fut);

// Attach the hyper HTTP client to the connection.
let (mut request_sender, connection) =
hyper::client::conn::http1::handshake(mpc_tls_connection).await?;

// Spawn the HTTP task to be run concurrently in the background.
tokio::spawn(connection);

// Build a simple HTTP request with common headers
let request = Request::builder()
.uri("/")
.header("Host", SERVER_DOMAIN)
.header("Accept", "*/*")
// Using "identity" instructs the Server not to use compression for its HTTP response.
// TLSNotary tooling does not support compression.
.header("Accept-Encoding", "identity")
.header("Connection", "close")
.header("User-Agent", USER_AGENT)
.body(Empty::<Bytes>::new())?;

println!("Starting an MPC TLS connection with the server");

// Send the request to the server and wait for the response.
let response = request_sender.send_request(request).await?;

println!("Got a response from the server");

assert!(response.status() == StatusCode::OK);

// The prover task should be done now, so we can await it.
let prover = prover_task.await??;

// Prepare for notarization.
let mut prover = prover.start_notarize();

// Parse the HTTP transcript.
let transcript = HttpTranscript::parse(prover.transcript())?;

// Commit to the transcript.
let mut builder = TranscriptCommitConfig::builder(prover.transcript());

DefaultHttpCommitter::default().commit_transcript(&mut builder, &transcript)?;

prover.transcript_commit(builder.build()?);

// Request an attestation.
let config = RequestConfig::default();

let (attestation, secrets) = prover.finalize(&config).await?;

// Write the attestation to disk.
let mut file = tokio::fs::File::create("example.attestation.tlsn").await?;
file.write_all(&bincode::serialize(&attestation)?).await?;

// Write the secrets to disk.
let mut file = tokio::fs::File::create("example.secrets.tlsn")
.await
.unwrap();
file.write_all(&bincode::serialize(&secrets)?).await?;

println!("Notarization completed successfully!");
println!(
"The attestation has been written to `example.attestation.tlsn` and the \
corresponding secrets to `example.secrets.tlsn`."
);

Ok(())
}
Loading

0 comments on commit 90f3683

Please sign in to comment.