diff --git a/src/components/mod.rs b/src/components/mod.rs index 005f52c..ab7b8e4 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -1,2 +1,3 @@ -pub mod redactedBytesComponent; -pub mod viewFile; +pub mod pem_input; +pub mod redacted_bytes_component; +pub mod view_file; diff --git a/src/components/pem_input.rs b/src/components/pem_input.rs new file mode 100644 index 0000000..6242761 --- /dev/null +++ b/src/components/pem_input.rs @@ -0,0 +1,70 @@ +use elliptic_curve::pkcs8::DecodePublicKey; +// use gloo::console::log; +use web_sys::HtmlInputElement; +use yew::prelude::*; + +#[derive(Properties, Clone, PartialEq)] +pub struct Props { + pub pem_callback: Callback, +} + +// from https://github.com/tlsnotary/notary-server/tree/main/src/fixture/notary/notary.key +// converted with `openssl ec -in notary.key -pubout -outform PEM` +pub const DEFAULT_PEM: &str = "-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBv36FI4ZFszJa0DQFJ3wWCXvVLFr +cRzMG5kaTeHGoSzDu6cFqx3uEWYpFGo6C0EOUgf+mEgbktLrXocv5yHzKg== +-----END PUBLIC KEY-----"; + +#[function_component(PemInputComponent)] +pub fn pem_input_component(Props { pem_callback }: &Props) -> Html { + let input_value = use_state(|| DEFAULT_PEM.to_string()); + let invalid_input = use_state(|| None); + + let oninput = { + let input_value = input_value.clone(); + let callback = pem_callback.clone(); + let invalid_input = invalid_input.clone(); + + Callback::from(move |e: InputEvent| { + let input: HtmlInputElement = e.target_unchecked_into(); + let value = input.value().trim().to_string(); + + let result = p256::PublicKey::from_public_key_pem(value.as_str()); + match result { + Ok(public_key) => { + input_value.set(value.clone()); + invalid_input.set(None); + callback.emit(public_key.clone()); + } + Err(err) => { + input_value.set(value.clone()); + invalid_input.set(Some(err.to_string())); + // do not emit a false pem here + } + } + }) + }; + + let style = if invalid_input.is_none() { + "text-sm text-white border-gray-600 focus:ring-blue-500 focus:border-blue-500" + } else { + "text-sm text-pink-600 border-pink-500 focus:border-pink-500 focus:ring-pink-500" + }; + + html! { +
+
+ {"Change Notary Public Key:" }{if invalid_input.as_ref().is_some() {" ❌"} else {""}} + + if let Some(x) = invalid_input.as_ref() { +

{x}

+ } +
+
+ } +} diff --git a/src/components/redactedBytesComponent.rs b/src/components/redacted_bytes_component.rs similarity index 100% rename from src/components/redactedBytesComponent.rs rename to src/components/redacted_bytes_component.rs diff --git a/src/components/viewFile.rs b/src/components/view_file.rs similarity index 84% rename from src/components/viewFile.rs rename to src/components/view_file.rs index 2832ba4..c075950 100644 --- a/src/components/viewFile.rs +++ b/src/components/view_file.rs @@ -1,5 +1,4 @@ extern crate base64; -use elliptic_curve::pkcs8::DecodePublicKey; use std::str; use web_time::Duration; @@ -7,41 +6,30 @@ use yew::{function_component, html, Html, Properties}; use tlsn_core::proof::{SessionProof, TlsProof}; -use crate::components::redactedBytesComponent::Direction; -use crate::components::redactedBytesComponent::RedactedBytesComponent; +use crate::components::redacted_bytes_component::Direction; +use crate::components::redacted_bytes_component::RedactedBytesComponent; #[derive(Properties, PartialEq)] pub struct Props { pub name: String, pub file_type: String, pub data: Vec, + pub pem: p256::PublicKey, } #[function_component] pub fn ViewFile(props: &Props) -> Html { - fn notary_pubkey() -> p256::PublicKey { - // from https://github.com/tlsnotary/notary-server/tree/main/src/fixture/notary/notary.key - // converted with `openssl ec -in notary.key -pubout -outform PEM` - - let pem = "-----BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBv36FI4ZFszJa0DQFJ3wWCXvVLFr -cRzMG5kaTeHGoSzDu6cFqx3uEWYpFGo6C0EOUgf+mEgbktLrXocv5yHzKg== ------END PUBLIC KEY-----"; - - p256::PublicKey::from_public_key_pem(pem).unwrap() - } - // Verify the session proof against the Notary's public key - fn verify_proof(session: &SessionProof) -> Result<(), String> { + fn verify_proof(session: &SessionProof, pem: p256::PublicKey) -> Result<(), String> { // This verifies the identity of the server using a default certificate verifier which trusts // the root certificates from the `webpki-roots` crate. session - .verify_with_default_cert_verifier(notary_pubkey()) + .verify_with_default_cert_verifier(pem) .map_err(|err| err.to_string()) } - fn parse_tls_proof(json_str: &str) -> Html { + fn parse_tls_proof(json_str: &str, pem: p256::PublicKey) -> Html { let tls_proof: Result = serde_json::from_str(json_str); // info!("Parsing"); @@ -57,7 +45,7 @@ cRzMG5kaTeHGoSzDu6cFqx3uEWYpFGo6C0EOUgf+mEgbktLrXocv5yHzKg== substrings, } = tls_proof; - let proof_verification = verify_proof(&session); + let proof_verification = verify_proof(&session, pem); if proof_verification.is_err() { return html! { @@ -137,7 +125,7 @@ cRzMG5kaTeHGoSzDu6cFqx3uEWYpFGo6C0EOUgf+mEgbktLrXocv5yHzKg==
if props.file_type.contains("application/json") { - {parse_tls_proof(json_str)} + {parse_tls_proof(json_str, props.pem)} }
diff --git a/src/main.rs b/src/main.rs index 43860fd..6874f0e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,12 +3,18 @@ use gloo::file::callbacks::FileReader; use gloo::file::File; use std::collections::HashMap; +#[allow(unused_imports)] +use gloo::console::log; + use web_sys::{DragEvent, Event, FileList, HtmlInputElement}; use yew::html::TargetCast; -use yew::{html, Callback, Component, Context, Html, Properties}; +use yew::prelude::*; mod components; -use crate::components::viewFile::ViewFile; +use crate::components::pem_input::PemInputComponent; +use crate::components::pem_input::DEFAULT_PEM; +use crate::components::view_file::ViewFile; +use elliptic_curve::pkcs8::DecodePublicKey; #[derive(Properties, PartialEq)] struct FileDetails { @@ -20,11 +26,13 @@ struct FileDetails { pub enum Msg { Loaded(String, String, Vec), Files(Vec), + Pem(p256::PublicKey), } pub struct App { readers: HashMap, files: Vec, + pem: p256::PublicKey, } impl Component for App { @@ -35,6 +43,7 @@ impl Component for App { Self { readers: HashMap::default(), files: Vec::default(), + pem: p256::PublicKey::from_public_key_pem(DEFAULT_PEM).unwrap(), } } @@ -49,6 +58,10 @@ impl Component for App { self.readers.remove(&file_name); true } + Msg::Pem(pem) => { + self.pem = pem; + true + } Msg::Files(files) => { for file in files.into_iter() { let file_name = file.name(); @@ -151,9 +164,11 @@ impl Component for App { })} /> + +
{for self.files.iter().rev().map(|file| html! { - + })}