mirror of
https://github.com/tlsnotary/explorer.git
synced 2026-01-09 06:48:09 -05:00
Implementing alpha.7 (#30)
This commit is contained in:
@@ -9,7 +9,7 @@ COPY . .
|
||||
RUN curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh -s -- -y
|
||||
RUN npm i
|
||||
RUN npm i --prefix rs/verifier/
|
||||
RUN npm i --prefix rs/0.1.0-alpha.6/
|
||||
RUN npm i --prefix rs/0.1.0-alpha.7/
|
||||
RUN npm run build
|
||||
|
||||
# Create image for the app by copying build artifacts from builder
|
||||
|
||||
292
package-lock.json
generated
292
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -44,6 +44,7 @@
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
"multiformats": "^13.1.0",
|
||||
"node-forge": "^1.3.1",
|
||||
"node-loader": "^2.0.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-redux": "^8.1.2",
|
||||
@@ -54,7 +55,7 @@
|
||||
"redux-thunk": "^2.4.2",
|
||||
"stream": "^0.0.2",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"tlsn-js": "0.1.0-alpha.6.2",
|
||||
"tlsn-js": "0.1.0-alpha.7.1",
|
||||
"tlsn-js-v5": "npm:tlsn-js@0.1.0-alpha.5.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -92,7 +93,6 @@
|
||||
"html-loader": "^4.2.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"image-webpack-loader": "^8.1.0",
|
||||
"node-loader": "^2.0.0",
|
||||
"nodemon": "^3.0.3",
|
||||
"postcss-loader": "^7.3.3",
|
||||
"postcss-preset-env": "^9.1.1",
|
||||
|
||||
228
pnpm-lock.yaml
generated
228
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
1584
rs/0.1.0-alpha.7/Cargo.lock
generated
Normal file
1584
rs/0.1.0-alpha.7/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
22
rs/0.1.0-alpha.7/Cargo.toml
Normal file
22
rs/0.1.0-alpha.7/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "verifier"
|
||||
version = "0.1.0"
|
||||
license = "ISC"
|
||||
edition = "2021"
|
||||
exclude = ["index.node"]
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
chrono = "0.4"
|
||||
neon = "1"
|
||||
elliptic-curve = { version = "0.13.5", features = ["pkcs8"] }
|
||||
k256 = { version = "0.13", features = ["pem", "ecdsa"] }
|
||||
serde = { version = "1.0.147", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
hex = "0.4"
|
||||
bincode = { version = "1.3" }
|
||||
tlsn-core = { git = "https://github.com/tlsnotary/tlsn.git", tag = "v0.1.0-alpha.7", package = "tlsn-core" }
|
||||
119
rs/0.1.0-alpha.7/README.md
Normal file
119
rs/0.1.0-alpha.7/README.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# verifier
|
||||
|
||||
This project was bootstrapped by [create-neon](https://www.npmjs.com/package/create-neon).
|
||||
|
||||
## Installing verifier
|
||||
|
||||
Installing verifier requires a [supported version of Node and Rust](https://github.com/neon-bindings/neon#platform-support).
|
||||
|
||||
You can install the project with npm. In the project directory, run:
|
||||
|
||||
```sh
|
||||
$ npm install
|
||||
```
|
||||
|
||||
This fully installs the project, including installing any dependencies and running the build.
|
||||
|
||||
## Building verifier
|
||||
|
||||
If you have already installed the project and only want to run the build, run:
|
||||
|
||||
```sh
|
||||
$ npm run build
|
||||
```
|
||||
|
||||
This command uses the [cargo-cp-artifact](https://github.com/neon-bindings/cargo-cp-artifact) utility to run the Rust build and copy the built library into `./index.node`.
|
||||
|
||||
## Exploring verifier
|
||||
|
||||
After building verifier, you can explore its exports at the Node REPL:
|
||||
|
||||
```sh
|
||||
$ npm install
|
||||
$ node
|
||||
> require('.').hello()
|
||||
"hello node"
|
||||
```
|
||||
|
||||
## Available Scripts
|
||||
|
||||
In the project directory, you can run:
|
||||
|
||||
### `npm install`
|
||||
|
||||
Installs the project, including running `npm run build`.
|
||||
|
||||
### `npm build`
|
||||
|
||||
Builds the Node addon (`index.node`) from source.
|
||||
|
||||
Additional [`cargo build`](https://doc.rust-lang.org/cargo/commands/cargo-build.html) arguments may be passed to `npm build` and `npm build-*` commands. For example, to enable a [cargo feature](https://doc.rust-lang.org/cargo/reference/features.html):
|
||||
|
||||
```
|
||||
npm run build -- --feature=beetle
|
||||
```
|
||||
|
||||
#### `npm build-debug`
|
||||
|
||||
Alias for `npm build`.
|
||||
|
||||
#### `npm build-release`
|
||||
|
||||
Same as [`npm build`](#npm-build) but, builds the module with the [`release`](https://doc.rust-lang.org/cargo/reference/profiles.html#release) profile. Release builds will compile slower, but run faster.
|
||||
|
||||
### `npm test`
|
||||
|
||||
Runs the unit tests by calling `cargo test`. You can learn more about [adding tests to your Rust code](https://doc.rust-lang.org/book/ch11-01-writing-tests.html) from the [Rust book](https://doc.rust-lang.org/book/).
|
||||
|
||||
## Project Layout
|
||||
|
||||
The directory structure of this project is:
|
||||
|
||||
```
|
||||
verifier/
|
||||
├── Cargo.toml
|
||||
├── README.md
|
||||
├── index.node
|
||||
├── package.json
|
||||
├── src/
|
||||
| └── lib.rs
|
||||
└── target/
|
||||
```
|
||||
|
||||
### Cargo.toml
|
||||
|
||||
The Cargo [manifest file](https://doc.rust-lang.org/cargo/reference/manifest.html), which informs the `cargo` command.
|
||||
|
||||
### README.md
|
||||
|
||||
This file.
|
||||
|
||||
### index.node
|
||||
|
||||
The Node addon—i.e., a binary Node module—generated by building the project. This is the main module for this package, as dictated by the `"main"` key in `package.json`.
|
||||
|
||||
Under the hood, a [Node addon](https://nodejs.org/api/addons.html) is a [dynamically-linked shared object](https://en.wikipedia.org/wiki/Library_(computing)#Shared_libraries). The `"build"` script produces this file by copying it from within the `target/` directory, which is where the Rust build produces the shared object.
|
||||
|
||||
### package.json
|
||||
|
||||
The npm [manifest file](https://docs.npmjs.com/cli/v7/configuring-npm/package-json), which informs the `npm` command.
|
||||
|
||||
### src/
|
||||
|
||||
The directory tree containing the Rust source code for the project.
|
||||
|
||||
### src/lib.rs
|
||||
|
||||
The Rust library's main module.
|
||||
|
||||
### target/
|
||||
|
||||
Binary artifacts generated by the Rust build.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Neon, see the [Neon documentation](https://neon-bindings.com).
|
||||
|
||||
To learn more about Rust, see the [Rust documentation](https://www.rust-lang.org).
|
||||
|
||||
To learn more about Node, see the [Node documentation](https://nodejs.org).
|
||||
BIN
rs/0.1.0-alpha.7/index.node
Executable file
BIN
rs/0.1.0-alpha.7/index.node
Executable file
Binary file not shown.
34
rs/0.1.0-alpha.7/package-lock.json
generated
Normal file
34
rs/0.1.0-alpha.7/package-lock.json
generated
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "verifier",
|
||||
"version": "0.1.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "verifier",
|
||||
"version": "0.1.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"cargo-cp-artifact": "^0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/cargo-cp-artifact": {
|
||||
"version": "0.1.9",
|
||||
"resolved": "https://registry.npmjs.org/cargo-cp-artifact/-/cargo-cp-artifact-0.1.9.tgz",
|
||||
"integrity": "sha512-6F+UYzTaGB+awsTXg0uSJA1/b/B3DDJzpKVRu0UmyI7DmNeaAl2RFHuTGIN6fEgpadRxoXGb7gbC1xo4C3IdyA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"cargo-cp-artifact": "bin/cargo-cp-artifact.js"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"cargo-cp-artifact": {
|
||||
"version": "0.1.9",
|
||||
"resolved": "https://registry.npmjs.org/cargo-cp-artifact/-/cargo-cp-artifact-0.1.9.tgz",
|
||||
"integrity": "sha512-6F+UYzTaGB+awsTXg0uSJA1/b/B3DDJzpKVRu0UmyI7DmNeaAl2RFHuTGIN6fEgpadRxoXGb7gbC1xo4C3IdyA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
||||
18
rs/0.1.0-alpha.7/package.json
Normal file
18
rs/0.1.0-alpha.7/package.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "verifier",
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"main": "index.node",
|
||||
"scripts": {
|
||||
"build": "cargo-cp-artifact -nc index.node -- cargo build --message-format=json-render-diagnostics",
|
||||
"build-debug": "npm run build --",
|
||||
"build-release": "npm run build -- --release",
|
||||
"install": "npm run build-release",
|
||||
"test": "cargo test"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"cargo-cp-artifact": "^0.1"
|
||||
}
|
||||
}
|
||||
8
rs/0.1.0-alpha.7/src/example.json
Normal file
8
rs/0.1.0-alpha.7/src/example.json
Normal file
File diff suppressed because one or more lines are too long
133
rs/0.1.0-alpha.7/src/lib.rs
Normal file
133
rs/0.1.0-alpha.7/src/lib.rs
Normal file
@@ -0,0 +1,133 @@
|
||||
use k256::pkcs8::DecodePublicKey;
|
||||
use neon::prelude::*;
|
||||
use tlsn_core::{
|
||||
presentation::{Presentation, PresentationOutput},
|
||||
signing::VerifyingKey,
|
||||
CryptoProvider,
|
||||
};
|
||||
|
||||
fn verify(mut cx: FunctionContext) -> JsResult<JsObject> {
|
||||
let presentation_cx = cx.argument::<JsString>(0)?.value(&mut cx);
|
||||
let notary_key_cx = cx.argument::<JsString>(1)?.value(&mut cx);
|
||||
|
||||
let (sent, recv, time) =
|
||||
verify_presentation(presentation_cx, notary_key_cx).or_else(|e| cx.throw_error(e))?;
|
||||
|
||||
let obj: Handle<JsObject> = cx.empty_object();
|
||||
let sent_str = cx.string(sent);
|
||||
obj.set(&mut cx, "sent", sent_str)?;
|
||||
let recv_str = cx.string(recv);
|
||||
obj.set(&mut cx, "recv", recv_str)?;
|
||||
let session_time = cx.number(time as f64);
|
||||
obj.set(&mut cx, "time", session_time)?;
|
||||
|
||||
Ok(obj)
|
||||
}
|
||||
|
||||
fn verify_presentation(
|
||||
presentation_cx: String,
|
||||
notary_key_cx: String,
|
||||
) -> Result<(String, String, u64), String> {
|
||||
// Deserialize the presentation
|
||||
let bytes: Vec<u8> = hex::decode(presentation_cx.as_str()).map_err(|e| e.to_string())?;
|
||||
let presentation: Presentation = bincode::deserialize(&bytes).map_err(|e| e.to_string())?;
|
||||
|
||||
// Verify the session proof against the Notary's public key
|
||||
let VerifyingKey {
|
||||
alg: _,
|
||||
data: key_data,
|
||||
} = presentation.verifying_key();
|
||||
|
||||
let notary_key = k256::PublicKey::from_public_key_pem(notary_key_cx.as_str())
|
||||
.map_err(|x| format!("Invalid notary key: {}", x))?;
|
||||
let verifying_key = k256::PublicKey::from_sec1_bytes(key_data)
|
||||
.map_err(|x| format!("Invalid verifying key: {}", x))?;
|
||||
|
||||
if notary_key != verifying_key {
|
||||
Err("The verifying key does not match the notary key")?;
|
||||
}
|
||||
|
||||
// Verify the presentation.
|
||||
let provider = CryptoProvider::default();
|
||||
let PresentationOutput {
|
||||
connection_info,
|
||||
transcript,
|
||||
..
|
||||
} = presentation
|
||||
.verify(&provider)
|
||||
.map_err(|x| format!("Presentation verification failed: {}", x))?;
|
||||
|
||||
let (sent, recv) = transcript
|
||||
.map(|mut partial_transcript| {
|
||||
// Set the unauthenticated bytes so they are distinguishable.
|
||||
partial_transcript.set_unauthed(b'X');
|
||||
let sent = String::from_utf8_lossy(partial_transcript.sent_unsafe()).to_string();
|
||||
let recv = String::from_utf8_lossy(partial_transcript.received_unsafe()).to_string();
|
||||
(sent, recv)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
Ok((sent, recv, connection_info.time))
|
||||
}
|
||||
|
||||
#[neon::main]
|
||||
fn main(mut cx: ModuleContext) -> NeonResult<()> {
|
||||
cx.export_function("verify", verify)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct Presentation {
|
||||
version: String,
|
||||
data: String,
|
||||
}
|
||||
|
||||
/// get example presentation from example.com for testing
|
||||
fn example_presentation() -> String {
|
||||
let example = include_str!("example.json");
|
||||
let presentation: Presentation = serde_json::from_str(&example).unwrap();
|
||||
assert_eq!("0.1.0-alpha.7", presentation.version);
|
||||
presentation.data
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify() {
|
||||
let notary_key_cx = String::from(
|
||||
"-----BEGIN PUBLIC KEY-----
|
||||
MDYwEAYHKoZIzj0CAQYFK4EEAAoDIgADe0jxnBObaIj7Xjg6TXLCM1GG/VhY5650
|
||||
OrS/jgcbBuc=
|
||||
-----END PUBLIC KEY-----",
|
||||
);
|
||||
|
||||
let (sent, recv, time) =
|
||||
verify_presentation(example_presentation(), notary_key_cx).unwrap();
|
||||
|
||||
assert_eq!(1728416631, time);
|
||||
assert!(sent.contains("host: example.com"));
|
||||
assert!(sent.contains("XXXXXXXXXXXXXXXXXX"));
|
||||
assert!(recv.contains("<title>Example Domain</title>"));
|
||||
assert!(recv.contains("Date: Tue, 08 Oct 2024"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_wrong_key() {
|
||||
let notary_key_cx = String::from(
|
||||
"-----BEGIN PUBLIC KEY-----
|
||||
MDYwEAYHKoZIzj0CAQYFK4EEAAoDIgADZT9nJiwhGESLjwQNnZ2MsZ1xwjGzvmhF
|
||||
xFi8Vjzanlg=
|
||||
-----END PUBLIC KEY-----",
|
||||
);
|
||||
|
||||
let res = verify_presentation(example_presentation(), notary_key_cx);
|
||||
|
||||
assert_eq!(
|
||||
res,
|
||||
Err("The verifying key does not match the notary key".to_string())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,9 @@ import configureAppStore, { AppRootState } from '../web/store';
|
||||
// @ts-ignore
|
||||
import { verify } from '../rs/verifier/index.node';
|
||||
// @ts-ignore
|
||||
import { verify as verifyV6 } from '../rs/0.1.0-alpha.6/index.node';
|
||||
import { verify as verifyV7 } from '../rs/0.1.0-alpha.7/index.node';
|
||||
import { Attestation } from '../web/utils/types/types';
|
||||
import { convertNotaryWsToHttp } from '../web/utils';
|
||||
|
||||
const app = express();
|
||||
const port = 3000;
|
||||
@@ -97,19 +98,37 @@ app.get('/ipfs/:cid', async (req, res) => {
|
||||
* redirect to root if verification fails
|
||||
*/
|
||||
if (!jsonProof.version && jsonProof.notaryUrl) {
|
||||
const proof = await verify(
|
||||
file,
|
||||
await fetchPublicKeyFromNotary(jsonProof.notaryUrl),
|
||||
);
|
||||
const notaryPem = await fetchPublicKeyFromNotary(jsonProof.notaryUrl);
|
||||
const proof = await verify(file, notaryPem);
|
||||
proof.notaryUrl = jsonProof.notaryUrl;
|
||||
storeConfig.proofs.ipfs[req.params.cid].proof = proof;
|
||||
} else if (jsonProof.version === '1.0') {
|
||||
const proof = await verifyV6(
|
||||
jsonProof.data,
|
||||
await fetchPublicKeyFromNotary(jsonProof.meta.notaryUrl),
|
||||
);
|
||||
storeConfig.proofs.ipfs[req.params.cid].proof = {
|
||||
...proof,
|
||||
version: '0.1.0-alpha.5',
|
||||
notaryUrl: jsonProof.notaryUrl,
|
||||
notaryKey: notaryPem,
|
||||
};
|
||||
} else if (jsonProof.version === '0.1.0-alpha.7') {
|
||||
const notaryUrl = convertNotaryWsToHttp(jsonProof.meta.notaryUrl);
|
||||
const notaryPem = await fetchPublicKeyFromNotary(notaryUrl);
|
||||
const proof = await verifyV7(jsonProof.data, notaryPem);
|
||||
proof.notaryUrl = jsonProof.meta.notaryUrl;
|
||||
storeConfig.proofs.ipfs[req.params.cid].proof = proof;
|
||||
storeConfig.proofs.ipfs[req.params.cid].proof = {
|
||||
version: '0.1.0-alpha.7',
|
||||
time: proof.time,
|
||||
sent: proof.sent,
|
||||
recv: proof.recv,
|
||||
notaryUrl: notaryUrl,
|
||||
websocketProxyUrl: jsonProof.meta.websocketProxyUrl,
|
||||
notaryKey: Buffer.from(
|
||||
notaryPem
|
||||
.replace('-----BEGIN PUBLIC KEY-----', '')
|
||||
.replace('-----END PUBLIC KEY-----', '')
|
||||
.replace(/\n/g, ''),
|
||||
'base64',
|
||||
)
|
||||
.slice(23)
|
||||
.toString('hex'),
|
||||
};
|
||||
}
|
||||
|
||||
const store = configureAppStore(storeConfig);
|
||||
@@ -205,11 +224,8 @@ app.listen(port, () => {
|
||||
});
|
||||
|
||||
async function fetchPublicKeyFromNotary(notaryUrl: string) {
|
||||
const res = await fetch(
|
||||
notaryUrl.replace('localhost', '127.0.0.1') + '/info',
|
||||
);
|
||||
const res = await fetch(notaryUrl + '/info');
|
||||
const json: any = await res.json();
|
||||
|
||||
if (!json.publicKey) throw new Error('invalid response');
|
||||
return json.publicKey;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,10 @@ import React, {
|
||||
} from 'react';
|
||||
import c from 'classnames';
|
||||
import classNames from 'classnames';
|
||||
import { Attestation, Proof as VerifiedProof } from '../../utils/types/types';
|
||||
import {
|
||||
Attestation,
|
||||
AttestedData as VerifiedProof,
|
||||
} from '../../utils/types/types';
|
||||
import Modal, { ModalContent, ModalFooter, ModalHeader } from '../Modal';
|
||||
import Icon from '../Icon';
|
||||
import { useDispatch } from 'react-redux';
|
||||
@@ -55,15 +58,15 @@ export default function ProofViewer(props: {
|
||||
)}
|
||||
<div className="flex flex-col px-2">
|
||||
<div className="flex flex-row gap-2 items-center">
|
||||
<TabLabel onClick={() => setTab('info')} active={tab === 'info'}>
|
||||
Info
|
||||
</TabLabel>
|
||||
<TabLabel onClick={() => setTab('sent')} active={tab === 'sent'}>
|
||||
Sent
|
||||
</TabLabel>
|
||||
<TabLabel onClick={() => setTab('recv')} active={tab === 'recv'}>
|
||||
Recv
|
||||
</TabLabel>
|
||||
<TabLabel onClick={() => setTab('info')} active={tab === 'info'}>
|
||||
Metadata
|
||||
</TabLabel>
|
||||
<div className="flex flex-row flex-grow items-center justify-end">
|
||||
<button className="button" onClick={onClickShare}>
|
||||
Share
|
||||
@@ -73,15 +76,30 @@ export default function ProofViewer(props: {
|
||||
</div>
|
||||
<div className="flex flex-col flex-grow px-2">
|
||||
{tab === 'info' && (
|
||||
<div className="w-full bg-slate-100 text-slate-800 border p-2 text-xs break-all h-full outline-none font-mono">
|
||||
<div>
|
||||
<div>Notary URL:</div>
|
||||
<div>
|
||||
{props.proof.version === '1.0'
|
||||
? props.proof.meta.notaryUrl
|
||||
: props.proof.notaryUrl}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col w-full bg-slate-100 text-slate-800 border p-2 text-xs break-all h-full outline-none font-mono gap-4">
|
||||
<MetadataRow label="Version" value={props.verifiedProof.version} />
|
||||
<MetadataRow
|
||||
label="Notary URL"
|
||||
value={props.verifiedProof.notaryUrl}
|
||||
/>
|
||||
{props.verifiedProof.websocketProxyUrl && (
|
||||
<MetadataRow
|
||||
label="Websocket Proxy URL"
|
||||
value={props.verifiedProof.websocketProxyUrl}
|
||||
/>
|
||||
)}
|
||||
{props.verifiedProof.verifierKey && (
|
||||
<MetadataRow
|
||||
label="Verifying Key"
|
||||
value={props.verifiedProof.verifierKey}
|
||||
/>
|
||||
)}
|
||||
{props.verifiedProof.notaryKey && (
|
||||
<MetadataRow
|
||||
label="Notary Key"
|
||||
value={props.verifiedProof.notaryKey}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{tab === 'sent' && (
|
||||
@@ -103,6 +121,15 @@ export default function ProofViewer(props: {
|
||||
);
|
||||
}
|
||||
|
||||
function MetadataRow({ label, value }: { label: string; value: string }) {
|
||||
return (
|
||||
<div>
|
||||
<div>{label}:</div>
|
||||
<div className="text-sm font-semibold whitespace-pre-wrap">{value}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function TabLabel(props: {
|
||||
children: ReactNode;
|
||||
onClick: MouseEventHandler;
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useDispatch } from 'react-redux';
|
||||
import ProofViewer from '../ProofViewer';
|
||||
import { FileDropdown } from '../FileDropdown';
|
||||
import { PubkeyInput } from '../../pages/PubkeyInput';
|
||||
import { Proof } from '../../utils/types/types';
|
||||
import { AttestedData } from '../../utils/types/types';
|
||||
import { File } from '@web-std/file';
|
||||
import { verify } from '../../utils';
|
||||
|
||||
@@ -18,7 +18,7 @@ export default function SharedProof(): ReactElement {
|
||||
const file = new File([JSON.stringify(proofData?.raw)], `${cid}.json`, {
|
||||
type: 'text/plain',
|
||||
});
|
||||
const [verifiedProof, setVerifiedProof] = useState<Proof | null>(
|
||||
const [verifiedProof, setVerifiedProof] = useState<AttestedData | null>(
|
||||
proofData?.proof || null,
|
||||
);
|
||||
|
||||
|
||||
@@ -3,7 +3,10 @@ import { useDispatch } from 'react-redux';
|
||||
import { readFileAsync, safeParseJSON, verify } from '../../utils';
|
||||
import FileUploadInput from '../../components/FileUploadInput';
|
||||
import ProofViewer from '../../components/ProofViewer';
|
||||
import { Attestation, Proof as VerifiedProof } from '../../utils/types/types';
|
||||
import {
|
||||
Attestation,
|
||||
AttestedData as VerifiedProof,
|
||||
} from '../../utils/types/types';
|
||||
import { FileDropdown } from '../../components/FileDropdown';
|
||||
import { PubkeyInput } from '../PubkeyInput';
|
||||
|
||||
@@ -25,6 +28,7 @@ export default function FileDrop(): ReactElement {
|
||||
setVerifiedProof(resp);
|
||||
setStep('result');
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
if (e?.message !== 'Failed to fetch') {
|
||||
setError(
|
||||
typeof e === 'string'
|
||||
|
||||
@@ -70,7 +70,7 @@ export function PubkeyInput(props: {
|
||||
<div className="font-semibold text-sm cursor-default">
|
||||
Please enter the notary key for{' '}
|
||||
<span className="text-blue-500 italic font-normal">
|
||||
{proof.version === '1.0' ? proof.meta.notaryUrl : proof.notaryUrl}
|
||||
{proof.version === '0.1.0-alpha.7' ? proof.meta.notaryUrl : <></>}
|
||||
</span>
|
||||
:
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { Proof } from 'tlsn-js-v5/build/types';
|
||||
import { useSelector } from 'react-redux';
|
||||
import deepEqual from 'fast-deep-equal';
|
||||
import { EXPLORER_URL } from '../utils/constants';
|
||||
import { Attestation } from '../utils/types/types';
|
||||
import { Attestation, AttestedData } from '../utils/types/types';
|
||||
import { verify } from '../utils';
|
||||
|
||||
enum ActionType {
|
||||
@@ -18,19 +18,14 @@ export type Action<payload = any> = {
|
||||
meta?: any;
|
||||
};
|
||||
|
||||
type ProofData = {
|
||||
type AttestationData = {
|
||||
raw: Attestation;
|
||||
proof?: {
|
||||
time: number;
|
||||
sent: string;
|
||||
recv: string;
|
||||
notaryUrl: string;
|
||||
};
|
||||
proof?: AttestedData;
|
||||
};
|
||||
|
||||
type State = {
|
||||
ipfs: {
|
||||
[cid: string]: ProofData;
|
||||
[cid: string]: AttestationData;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -66,7 +61,7 @@ export const fetchProofFromIPFS =
|
||||
};
|
||||
|
||||
export const setIPFSProof = (
|
||||
payload: ProofData & {
|
||||
payload: AttestationData & {
|
||||
cid: string;
|
||||
},
|
||||
) => ({
|
||||
@@ -76,16 +71,11 @@ export const setIPFSProof = (
|
||||
|
||||
export default function proofs(
|
||||
state = initState,
|
||||
action: Action<{
|
||||
cid: string;
|
||||
raw: Proof;
|
||||
proof: {
|
||||
time: number;
|
||||
sent: string;
|
||||
recv: string;
|
||||
notaryUrl: string;
|
||||
};
|
||||
}>,
|
||||
action: Action<
|
||||
AttestationData & {
|
||||
cid: string;
|
||||
}
|
||||
>,
|
||||
): State {
|
||||
switch (action.type) {
|
||||
case ActionType.SetIPFSProof:
|
||||
@@ -104,7 +94,7 @@ export default function proofs(
|
||||
}
|
||||
}
|
||||
|
||||
export const useIPFSProof = (cid?: string): ProofData | null => {
|
||||
export const useIPFSProof = (cid?: string): AttestationData | null => {
|
||||
return useSelector((state: AppRootState) => {
|
||||
if (!cid) return null;
|
||||
return state.proofs.ipfs[cid] || null;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AppRootState } from '.';
|
||||
import type { Proof } from '../utils/types/types';
|
||||
import type { AttestedData } from '../utils/types/types';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
export enum ActionType {
|
||||
@@ -8,7 +8,7 @@ export enum ActionType {
|
||||
UploadFileSuccess = 'proofupload/uploadFileSuccess',
|
||||
}
|
||||
|
||||
export const uploadFile = (fileName: string, proof: Proof) => ({
|
||||
export const uploadFile = (fileName: string, proof: AttestedData) => ({
|
||||
type: ActionType.AddFile,
|
||||
payload: { fileName, proof },
|
||||
});
|
||||
@@ -31,8 +31,12 @@ export type Action<payload = any> = {
|
||||
};
|
||||
|
||||
type State = {
|
||||
proofs: { fileName: string; proof: Proof }[];
|
||||
selectedProof?: { fileName: string; proof: Proof; ipfsCID?: string } | null;
|
||||
proofs: { fileName: string; proof: AttestedData }[];
|
||||
selectedProof?: {
|
||||
fileName: string;
|
||||
proof: AttestedData;
|
||||
ipfsCID?: string;
|
||||
} | null;
|
||||
};
|
||||
|
||||
const initState: State = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { ReactElement, useRef } from 'react';
|
||||
import { Attestation, Proof } from './types/types';
|
||||
import { Attestation, AttestedData } from './types/types';
|
||||
|
||||
export const readFileAsync = (file: File): Promise<string> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -98,33 +98,50 @@ async function initTlsnJs() {
|
||||
export async function verify(
|
||||
attestation: Attestation,
|
||||
pubKey: string,
|
||||
): Promise<Proof> {
|
||||
): Promise<AttestedData> {
|
||||
let key = pubKey;
|
||||
const { NotaryServer } = await import('tlsn-js');
|
||||
await initTlsnJs();
|
||||
|
||||
switch (attestation.version) {
|
||||
case undefined: {
|
||||
const { verify } = await import('tlsn-js-v5');
|
||||
key = key || (await NotaryServer.from(attestation.notaryUrl).publicKey());
|
||||
return await verify(attestation, key);
|
||||
}
|
||||
case '1.0': {
|
||||
const { TlsProof } = await import('tlsn-js');
|
||||
console.log(Buffer.from(attestation.data, 'hex').toString('binary'));
|
||||
await initTlsnJs();
|
||||
key =
|
||||
key ||
|
||||
(await NotaryServer.from(attestation.meta.notaryUrl).publicKey());
|
||||
const tlsProof = new TlsProof(attestation.data);
|
||||
const data = await tlsProof.verify({
|
||||
typ: 'P256',
|
||||
key: key,
|
||||
});
|
||||
(await NotaryServer.from(attestation.notaryUrl).publicKey('pem'));
|
||||
const data = await verify(attestation, key);
|
||||
return {
|
||||
sent: data.sent,
|
||||
recv: data.recv,
|
||||
time: data.time,
|
||||
notaryUrl: attestation.meta.notaryUrl,
|
||||
...data,
|
||||
version: '0.1.0-alpha.5',
|
||||
notaryUrl: attestation.notaryUrl,
|
||||
notaryKey: key,
|
||||
};
|
||||
}
|
||||
case '0.1.0-alpha.7': {
|
||||
const { Presentation, Transcript } = await import('tlsn-js');
|
||||
const tlsProof = new Presentation(attestation.data);
|
||||
const data = await tlsProof.verify();
|
||||
const transcript = new Transcript({
|
||||
sent: data.transcript.sent,
|
||||
recv: data.transcript.recv,
|
||||
});
|
||||
const vk = await tlsProof.verifyingKey();
|
||||
const verifyingKey = Buffer.from(vk.data).toString('hex');
|
||||
const notaryUrl = convertNotaryWsToHttp(attestation.meta.notaryUrl);
|
||||
const publicKey = await new NotaryServer(notaryUrl).publicKey();
|
||||
|
||||
if (verifyingKey !== publicKey)
|
||||
throw new Error(`Notary key doesn't match verifying key`);
|
||||
|
||||
return {
|
||||
version: '0.1.0-alpha.7',
|
||||
sent: transcript.sent(),
|
||||
recv: transcript.recv(),
|
||||
time: data.connection_info.time,
|
||||
notaryUrl: notaryUrl,
|
||||
notaryKey: publicKey,
|
||||
websocketProxyUrl: attestation.meta.websocketProxyUrl,
|
||||
verifierKey: verifyingKey,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -132,6 +149,13 @@ export async function verify(
|
||||
throw new Error('Invalid Proof');
|
||||
}
|
||||
|
||||
export function convertNotaryWsToHttp(notaryWs: string) {
|
||||
const { protocol, pathname, hostname } = new URL(notaryWs);
|
||||
const p = protocol === 'wss:' ? 'https:' : 'http';
|
||||
const path = pathname === '/' ? '' : pathname.replace('/notarize', '');
|
||||
return p + '//' + hostname + path;
|
||||
}
|
||||
|
||||
function defer(): {
|
||||
promise: Promise<any>;
|
||||
resolve: (args?: any) => any;
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
export interface Proof {
|
||||
export interface AttestedData {
|
||||
version: '0.1.0-alpha.7' | '0.1.0-alpha.5';
|
||||
time: number;
|
||||
sent: string;
|
||||
recv: string;
|
||||
notaryUrl: string;
|
||||
notaryKey: string;
|
||||
websocketProxyUrl?: string;
|
||||
verifierKey?: string;
|
||||
}
|
||||
|
||||
export type AttestationV0 = {
|
||||
@@ -13,7 +17,7 @@ export type AttestationV0 = {
|
||||
};
|
||||
|
||||
export type AttestationV1 = {
|
||||
version: '1.0';
|
||||
version: '0.1.0-alpha.7';
|
||||
data: string;
|
||||
meta: {
|
||||
notaryUrl: string;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import * as Comlink from 'comlink';
|
||||
import init, { Prover, NotarizedSession, TlsProof } from 'tlsn-js';
|
||||
import init, { Prover, Attestation, Presentation } from 'tlsn-js';
|
||||
|
||||
Comlink.expose({
|
||||
init,
|
||||
Prover,
|
||||
NotarizedSession,
|
||||
TlsProof,
|
||||
Attestation,
|
||||
Presentation,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user