[Fix] WASM
This commit is contained in:
parent
3332aa621b
commit
cfc9cebf6a
14 changed files with 461 additions and 204 deletions
|
|
@ -2,13 +2,29 @@ mod auth;
|
|||
mod messages;
|
||||
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
use mtp::client::ClientConfig;
|
||||
use mtp::crypto::{KemPublicKey, PublicKeyBundle, SignaturePqPublicKey, SignaturePublicKey};
|
||||
|
||||
fn dev_cert_path() -> String {
|
||||
std::env::var("MTP_DEV_CERT").unwrap_or_else(|_| {
|
||||
if Path::new("example-usage/dev-cert/cert.pem").exists() {
|
||||
"example-usage/dev-cert/cert.pem".to_string()
|
||||
} else {
|
||||
"dev-cert/cert.pem".to_string()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let cert_pem = fs::read("server.pem").expect("Missing server.pem: run server first");
|
||||
let cert_path = dev_cert_path();
|
||||
let cert_pem = fs::read(&cert_path).unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"Missing TLS certificate at {cert_path}: enter the Nix shell first or run the server to generate it: {e}"
|
||||
)
|
||||
});
|
||||
let host_public_key = PublicKeyBundle::new(
|
||||
KemPublicKey::new(
|
||||
fs::read("host_enc_kem_pk.bin")
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ fn derive_demo_key() -> [u8; 32] {
|
|||
|
||||
pub fn build_demo_message(client_id: u64, keyring: &Keyring) -> CommunicationValue {
|
||||
let cipher = ChaCha20Poly1305::new(derive_demo_key());
|
||||
let signer = Ed25519Signer::new(&keyring.sig_cl_secret_key)
|
||||
.expect("Ed25519 signer from keyring");
|
||||
let signer =
|
||||
Ed25519Signer::new(&keyring.sig_cl_secret_key).expect("Ed25519 signer from keyring");
|
||||
|
||||
let inner_enc = DataValue::Container(vec![
|
||||
(DataTypeId(1), DataValue::Str("secret inner data".into())),
|
||||
|
|
@ -31,7 +31,10 @@ pub fn build_demo_message(client_id: u64, keyring: &Keyring) -> CommunicationVal
|
|||
dv_sig.sign_container(SigAlgorithm::ED25519, &signer);
|
||||
|
||||
let inner_sec = DataValue::Container(vec![
|
||||
(DataTypeId(1), DataValue::Str("signed+encrypted payload".into())),
|
||||
(
|
||||
DataTypeId(1),
|
||||
DataValue::Str("signed+encrypted payload".into()),
|
||||
),
|
||||
(DataTypeId(2), DataValue::UnsignedNumber(7)),
|
||||
]);
|
||||
let mut dv_sec = inner_sec;
|
||||
|
|
@ -42,13 +45,22 @@ pub fn build_demo_message(client_id: u64, keyring: &Keyring) -> CommunicationVal
|
|||
.unwrap()
|
||||
.as_secs();
|
||||
|
||||
CommunicationValue::new(CommunicationType::Ping)
|
||||
.add_typed_default(DataType::Description, DataValue::Str("MTP Data Type Demo".into()))
|
||||
.add_typed_default(DataType::Timestamp, DataValue::UnsignedNumber(timestamp as u128))
|
||||
let msg = CommunicationValue::new(CommunicationType::Ping)
|
||||
.add_typed_default(
|
||||
DataType::Description,
|
||||
DataValue::Str("MTP Data Type Demo".into()),
|
||||
)
|
||||
.add_typed_default(
|
||||
DataType::Timestamp,
|
||||
DataValue::UnsignedNumber(timestamp as u128),
|
||||
)
|
||||
.add_typed_default(DataType::Data, DataValue::Str("Hello, MTP!".into()))
|
||||
.add_typed_default(DataType::Flags, DataValue::BoolTrue)
|
||||
.add_typed_default(DataType::Value, DataValue::Float(2, 12345))
|
||||
.add_typed_default(DataType::BinaryData, DataValue::Bytes(vec![0xDE, 0xAD, 0xBE, 0xEF, 0x42]))
|
||||
.add_typed_default(
|
||||
DataType::BinaryData,
|
||||
DataValue::Bytes(vec![0xDE, 0xAD, 0xBE, 0xEF, 0x42]),
|
||||
)
|
||||
.add_typed_default(
|
||||
DataType::Items,
|
||||
DataValue::Array(vec![
|
||||
|
|
@ -60,7 +72,8 @@ pub fn build_demo_message(client_id: u64, keyring: &Keyring) -> CommunicationVal
|
|||
.add_typed_default(DataType::EncryptedPayload, dv_enc)
|
||||
.add_typed_default(DataType::SignedPayload, dv_sig)
|
||||
.add_typed_default(DataType::SecurePayload, dv_sec)
|
||||
.with_sender(client_id)
|
||||
.with_sender(client_id);
|
||||
msg
|
||||
}
|
||||
|
||||
pub async fn send_and_receive(
|
||||
|
|
@ -72,7 +85,9 @@ pub async fn send_and_receive(
|
|||
conn.sender.send(&msg).await?;
|
||||
|
||||
match conn.receiver.receive().await {
|
||||
Ok(resp) => println!("Received: {resp}"),
|
||||
Ok(resp) => {
|
||||
println!("Received: {resp}");
|
||||
}
|
||||
Err(e) => eprintln!("Receive error: {e}"),
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,14 +8,18 @@ pub fn load_client_db(
|
|||
path: &str,
|
||||
) -> Result<(Arc<Mutex<HashMap<u64, PublicKeyBundle>>>, Arc<Mutex<u64>>), Box<dyn std::error::Error>>
|
||||
{
|
||||
let clients: Arc<Mutex<HashMap<u64, PublicKeyBundle>>> =
|
||||
Arc::new(Mutex::new(if let Ok(data) = fs::read_to_string(path) {
|
||||
serde_json::from_str(&data).unwrap_or_default()
|
||||
} else {
|
||||
HashMap::new()
|
||||
}));
|
||||
let next_id = Arc::new(Mutex::new(
|
||||
clients.lock().unwrap().keys().max().unwrap_or(&999) + 1,
|
||||
));
|
||||
let clients_map = match fs::read_to_string(path) {
|
||||
Ok(data) => match serde_json::from_str(&data) {
|
||||
Ok(clients) => clients,
|
||||
Err(e) => {
|
||||
eprintln!("Failed to parse {path}; starting with empty client database: {e}");
|
||||
HashMap::new()
|
||||
}
|
||||
},
|
||||
Err(_) => HashMap::new(),
|
||||
};
|
||||
let clients: Arc<Mutex<HashMap<u64, PublicKeyBundle>>> = Arc::new(Mutex::new(clients_map));
|
||||
let next_value = clients.lock().unwrap().keys().max().unwrap_or(&999) + 1;
|
||||
let next_id = Arc::new(Mutex::new(next_value));
|
||||
Ok((clients, next_id))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
use mtp::codec::{CommunicationType, CommunicationValue, DataType, DataTypeId, DataValue, TypeMap};
|
||||
use mtp::crypto::{ChaCha20Poly1305, CryptoError, SignatureScheme, SignaturePublicKey, verify_ed25519};
|
||||
use mtp::crypto::{
|
||||
ChaCha20Poly1305, CryptoError, SignaturePublicKey, SignatureScheme, verify_ed25519,
|
||||
};
|
||||
|
||||
struct Ed25519Verifier(SignaturePublicKey);
|
||||
|
||||
|
|
@ -71,6 +73,7 @@ pub fn process_and_respond(
|
|||
enc_status = format!("EncryptedPayload decrypted OK ({} entries)", entries.len());
|
||||
}
|
||||
} else {
|
||||
eprintln!(" EncryptedPayload decryption failed");
|
||||
enc_status = String::from("EncryptedPayload: decryption FAILED");
|
||||
}
|
||||
}
|
||||
|
|
@ -83,13 +86,14 @@ pub fn process_and_respond(
|
|||
if dv.verify_into_container(&verifier).is_some() {
|
||||
if let Some(entries) = dv.as_container() {
|
||||
println!(" Verified SignedPayload: {:?}", entries);
|
||||
sig_status =
|
||||
format!("SignedPayload verified OK ({} entries)", entries.len());
|
||||
sig_status = format!("SignedPayload verified OK ({} entries)", entries.len());
|
||||
}
|
||||
} else {
|
||||
eprintln!(" SignedPayload verification failed");
|
||||
sig_status = String::from("SignedPayload: verification FAILED");
|
||||
}
|
||||
} else {
|
||||
eprintln!(" SignedPayload cannot be verified; no client public key available");
|
||||
sig_status = String::from("SignedPayload: no client public key available");
|
||||
}
|
||||
}
|
||||
|
|
@ -99,7 +103,9 @@ pub fn process_and_respond(
|
|||
if let Some(pk_bundle) = client_pk {
|
||||
let verifier = Ed25519Verifier(pk_bundle.sig_cl_public_key.clone());
|
||||
let mut dv = secure.clone();
|
||||
if dv.decrypt_signed_encrypted_container(&cipher, b"demo-aad").is_some()
|
||||
if dv
|
||||
.decrypt_signed_encrypted_container(&cipher, b"demo-aad")
|
||||
.is_some()
|
||||
&& dv.verify_into_container(&verifier).is_some()
|
||||
{
|
||||
if let Some(entries) = dv.as_container() {
|
||||
|
|
@ -110,9 +116,11 @@ pub fn process_and_respond(
|
|||
);
|
||||
}
|
||||
} else {
|
||||
eprintln!(" SecurePayload decryption/verification failed");
|
||||
secure_status = String::from("SecurePayload: decryption/verification FAILED");
|
||||
}
|
||||
} else {
|
||||
eprintln!(" SecurePayload cannot be verified; no client public key available");
|
||||
secure_status = String::from("SecurePayload: no client public key available");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
use std::fs;
|
||||
|
||||
use mtp::crypto::{Ed25519Signer, Keyring, MlDsaSigner};
|
||||
use mtp::crypto::kem::HybridKem;
|
||||
use mtp::crypto::{Ed25519Signer, Keyring, MlDsaSigner};
|
||||
|
||||
pub fn load_or_generate_host_keys(path: &str) -> Result<(u64, Keyring), Box<dyn std::error::Error>> {
|
||||
pub fn load_or_generate_host_keys(
|
||||
path: &str,
|
||||
) -> Result<(u64, Keyring), Box<dyn std::error::Error>> {
|
||||
if let Ok(data) = fs::read_to_string(path) {
|
||||
let json: serde_json::Value = serde_json::from_str(&data)?;
|
||||
let hid = json["host_id"].as_u64().unwrap_or(1);
|
||||
|
|
@ -29,10 +31,7 @@ pub fn load_or_generate_host_keys(path: &str) -> Result<(u64, Keyring), Box<dyn
|
|||
pub fn export_host_public_keys(host_keyring: &Keyring) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let public_key_bundle_hex = hex::encode(host_keyring.public_key_bundle().as_bytes());
|
||||
|
||||
fs::write(
|
||||
"host_public_key_bundle.hex",
|
||||
&public_key_bundle_hex,
|
||||
)?;
|
||||
fs::write("host_public_key_bundle.hex", &public_key_bundle_hex)?;
|
||||
fs::create_dir_all("web-client/public")?;
|
||||
fs::write(
|
||||
"web-client/public/host_public_key_bundle.hex",
|
||||
|
|
|
|||
|
|
@ -40,7 +40,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
|
||||
let clients_for_get = clients.clone();
|
||||
let get_existing_user = Box::new(move |id: u64| -> Option<mtp::crypto::PublicKeyBundle> {
|
||||
clients_for_get.lock().unwrap().get(&id).cloned()
|
||||
let result = clients_for_get.lock().unwrap().get(&id).cloned();
|
||||
if result.is_some() {
|
||||
println!("Auth lookup: client ID {id} found");
|
||||
} else {
|
||||
eprintln!("Auth lookup: unknown client ID {id}");
|
||||
}
|
||||
result
|
||||
});
|
||||
|
||||
let clients_for_register = clients.clone();
|
||||
|
|
@ -52,7 +58,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
let id = *nid;
|
||||
*nid += 1;
|
||||
db.insert(id, bundle);
|
||||
std::fs::write(&clients_path, serde_json::to_string_pretty(&*db).unwrap()).ok();
|
||||
match serde_json::to_string_pretty(&*db) {
|
||||
Ok(json) => match std::fs::write(&clients_path, json) {
|
||||
Ok(()) => {}
|
||||
Err(e) => eprintln!("Failed to persist client database to {clients_path}: {e}"),
|
||||
},
|
||||
Err(e) => eprintln!("Failed to serialize client database after registering {id}: {e}"),
|
||||
}
|
||||
println!("Registered new client with ID: {}", id);
|
||||
id
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import init, {
|
|||
ed25519_generate,
|
||||
keyring_from_ed25519,
|
||||
build_demo_message,
|
||||
format_frame,
|
||||
} from "mtp-wasm";
|
||||
|
||||
const STATUS = document.getElementById("status")!;
|
||||
|
|
@ -57,7 +58,9 @@ function hexToBytes(value: string): Uint8Array {
|
|||
}
|
||||
|
||||
function saveKeys() {
|
||||
if (!keyringBytes) return;
|
||||
if (!keyringBytes) {
|
||||
return;
|
||||
}
|
||||
|
||||
let hostPublicKey: number[] | undefined;
|
||||
try {
|
||||
|
|
@ -106,7 +109,7 @@ async function loadHostPublicKey() {
|
|||
|
||||
HOST_PUBLIC_KEY.value = hostPublicKey;
|
||||
saveKeys();
|
||||
log("Loaded host public key bundle from public file.");
|
||||
log(`Loaded host public key bundle (${hostPublicKey.length / 2} bytes).`);
|
||||
} catch {
|
||||
// Manual paste still works when the server has not exported the file yet.
|
||||
}
|
||||
|
|
@ -138,11 +141,11 @@ function createClient(): WasmClient {
|
|||
(state: number) =>
|
||||
log(`[state] ${ConnectionState[state] ?? state}`, "state"),
|
||||
(data: Uint8Array) => {
|
||||
const decoder = new TextDecoder();
|
||||
log(
|
||||
`[message] ${data.length} bytes: ${decoder.decode(data)}`,
|
||||
"received",
|
||||
);
|
||||
try {
|
||||
log(`Received: ${format_frame(data)}`, "received");
|
||||
} catch (e) {
|
||||
log(`[message parse error] ${e}`, "error");
|
||||
}
|
||||
},
|
||||
(err: any) => log(`[error] ${err}`, "error"),
|
||||
);
|
||||
|
|
@ -181,7 +184,8 @@ async function connect() {
|
|||
await loadDevCertHash();
|
||||
|
||||
const client = createClient();
|
||||
const config = new ConnectionConfig(SERVER_URL.value.trim());
|
||||
const serverUrl = SERVER_URL.value.trim();
|
||||
const config = new ConnectionConfig(serverUrl);
|
||||
if (devCertHash) {
|
||||
log(`Pinning WebTransport certificate hash: sha-256:${devCertHash}`);
|
||||
config.server_certificate_hashes = [`sha-256:${devCertHash}`];
|
||||
|
|
@ -210,6 +214,7 @@ async function connect() {
|
|||
|
||||
log("\nSending demo message...");
|
||||
const frame = build_demo_message(activeClientId, keyringBytes);
|
||||
log(`Sending: ${format_frame(frame)}`, "state");
|
||||
await client.send(frame);
|
||||
log(`Sent ${frame.length} bytes`);
|
||||
|
||||
|
|
@ -236,6 +241,10 @@ GENERATE_KEYPAIR.addEventListener("click", () => {
|
|||
CONNECT.addEventListener("click", () => {
|
||||
connect().catch((e) => {
|
||||
log(`Fatal error: ${e}`, "error");
|
||||
log(
|
||||
`[fatal context] clientId=${clientId?.toString() ?? "unregistered"}, server=${SERVER_URL.value.trim()}, hostPkChars=${HOST_PUBLIC_KEY.value.replace(/[^0-9a-fA-F]/g, "").length}, keyringBytes=${keyringBytes?.length ?? 0}, certHash=${devCertHash || "none"}`,
|
||||
"error",
|
||||
);
|
||||
console.error(e);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue