v0.0.38: FC-P4 complete — session versioning, wire envelope, auto-backup
FC-P4-T1: Session State Versioning - RatchetState serialize/deserialize with [MAGIC:0xFC][VERSION:1][bincode] - Legacy (raw bincode) still loads — backward compatible - Client + WASM both use versioned format - 2 new tests: roundtrip + legacy compat FC-P4-T2: WireMessage Versioning Envelope - Format: [WZ magic][version:u8][length:u32 BE][bincode payload] - Server + client + WASM accept both envelope and legacy on receive - Client still sends raw bincode (server handles both) - Future version → "update required" error instead of crash - 3 new tests: roundtrip, legacy compat, future version rejection FC-P4-T3: Periodic Auto-Backup - Every 5 minutes, encrypts sessions+contacts+sender_keys to ~/.warzone/backups/ - HKDF-derived key from seed, ChaCha20-Poly1305 AEAD - Atomic writes (temp file + rename), rotates to keep last 3 - /backup command for manual trigger 127 tests passing (was 122) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
10
warzone/Cargo.lock
generated
10
warzone/Cargo.lock
generated
@@ -2956,7 +2956,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "warzone-client"
|
||||
version = "0.0.37"
|
||||
version = "0.0.38"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argon2",
|
||||
@@ -2989,7 +2989,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "warzone-mule"
|
||||
version = "0.0.37"
|
||||
version = "0.0.38"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -2998,7 +2998,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "warzone-protocol"
|
||||
version = "0.0.37"
|
||||
version = "0.0.38"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bincode",
|
||||
@@ -3023,7 +3023,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "warzone-server"
|
||||
version = "0.0.37"
|
||||
version = "0.0.38"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -3053,7 +3053,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "warzone-wasm"
|
||||
version = "0.0.37"
|
||||
version = "0.0.38"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bincode",
|
||||
|
||||
@@ -9,7 +9,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.0.37"
|
||||
version = "0.0.38"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
rust-version = "1.75"
|
||||
|
||||
@@ -54,7 +54,8 @@ impl LocalDb {
|
||||
/// Save a ratchet session for a peer.
|
||||
pub fn save_session(&self, peer: &Fingerprint, state: &RatchetState) -> Result<()> {
|
||||
let key = peer.to_hex();
|
||||
let data = bincode::serialize(state).context("failed to serialize session")?;
|
||||
let data = state.serialize_versioned()
|
||||
.map_err(|e| anyhow::anyhow!("{}", e))?;
|
||||
self.sessions.insert(key.as_bytes(), data)?;
|
||||
self.sessions.flush()?;
|
||||
Ok(())
|
||||
@@ -73,8 +74,8 @@ impl LocalDb {
|
||||
let key = peer.to_hex();
|
||||
match self.sessions.get(key.as_bytes())? {
|
||||
Some(data) => {
|
||||
let state = bincode::deserialize(&data)
|
||||
.context("failed to deserialize session")?;
|
||||
let state = RatchetState::deserialize_versioned(&data)
|
||||
.map_err(|e| anyhow::anyhow!("{}", e))?;
|
||||
Ok(Some(state))
|
||||
}
|
||||
None => Ok(None),
|
||||
@@ -272,6 +273,87 @@ impl LocalDb {
|
||||
}))
|
||||
}
|
||||
|
||||
/// Create an encrypted backup of all session data.
|
||||
/// Returns the backup file path.
|
||||
pub fn create_backup(&self, seed: &[u8; 32]) -> Result<std::path::PathBuf> {
|
||||
use std::io::Write;
|
||||
|
||||
let backup_dir = crate::keystore::data_dir().join("backups");
|
||||
std::fs::create_dir_all(&backup_dir)?;
|
||||
|
||||
// Collect all data
|
||||
let mut data = serde_json::Map::new();
|
||||
|
||||
// Sessions
|
||||
let mut sessions = serde_json::Map::new();
|
||||
for item in self.sessions.iter() {
|
||||
if let Ok((key, value)) = item {
|
||||
let k = String::from_utf8_lossy(&key).to_string();
|
||||
sessions.insert(k, serde_json::Value::String(base64::Engine::encode(
|
||||
&base64::engine::general_purpose::STANDARD, &value
|
||||
)));
|
||||
}
|
||||
}
|
||||
data.insert("sessions".into(), serde_json::Value::Object(sessions));
|
||||
|
||||
// Contacts
|
||||
let mut contacts = serde_json::Map::new();
|
||||
for item in self.contacts.iter() {
|
||||
if let Ok((key, value)) = item {
|
||||
let k = String::from_utf8_lossy(&key).to_string();
|
||||
if let Ok(v) = serde_json::from_slice::<serde_json::Value>(&value) {
|
||||
contacts.insert(k, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
data.insert("contacts".into(), serde_json::Value::Object(contacts));
|
||||
|
||||
// Sender keys
|
||||
let mut sender_keys = serde_json::Map::new();
|
||||
for item in self.sender_keys.iter() {
|
||||
if let Ok((key, value)) = item {
|
||||
let k = String::from_utf8_lossy(&key).to_string();
|
||||
sender_keys.insert(k, serde_json::Value::String(base64::Engine::encode(
|
||||
&base64::engine::general_purpose::STANDARD, &value
|
||||
)));
|
||||
}
|
||||
}
|
||||
data.insert("sender_keys".into(), serde_json::Value::Object(sender_keys));
|
||||
|
||||
// Serialize and encrypt
|
||||
let plaintext = serde_json::to_vec(&serde_json::Value::Object(data))?;
|
||||
let key_bytes = warzone_protocol::crypto::hkdf_derive(seed, b"", b"warzone-backup", 32);
|
||||
let mut key = [0u8; 32];
|
||||
key.copy_from_slice(&key_bytes);
|
||||
let encrypted = warzone_protocol::crypto::aead_encrypt(&key, &plaintext, b"warzone-backup-aad");
|
||||
|
||||
// Write to temp file then rename (atomic)
|
||||
let timestamp = chrono::Utc::now().format("%Y%m%d_%H%M%S").to_string();
|
||||
let filename = format!("backup_{}.wzbk", timestamp);
|
||||
let path = backup_dir.join(&filename);
|
||||
let tmp_path = backup_dir.join(format!(".{}.tmp", filename));
|
||||
|
||||
let mut file = std::fs::File::create(&tmp_path)?;
|
||||
file.write_all(&encrypted)?;
|
||||
file.sync_all()?;
|
||||
std::fs::rename(&tmp_path, &path)?;
|
||||
|
||||
// Rotate: keep last 3 backups
|
||||
let mut backups: Vec<_> = std::fs::read_dir(&backup_dir)?
|
||||
.filter_map(|e| e.ok())
|
||||
.filter(|e| e.file_name().to_string_lossy().ends_with(".wzbk"))
|
||||
.collect();
|
||||
backups.sort_by_key(|e| e.file_name());
|
||||
while backups.len() > 3 {
|
||||
if let Some(old) = backups.first() {
|
||||
let _ = std::fs::remove_file(old.path());
|
||||
backups.remove(0);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
/// Import data from JSON backup (merges, doesn't overwrite existing).
|
||||
pub fn import_all(&self, data: &serde_json::Value) -> Result<usize> {
|
||||
let mut count = 0;
|
||||
|
||||
@@ -47,6 +47,7 @@ impl App {
|
||||
" /info Show your fingerprint",
|
||||
" /eth Show Ethereum address",
|
||||
" /seed Show recovery mnemonic (24 words)",
|
||||
" /backup Create encrypted backup now",
|
||||
" /peer <fp>, /p Set DM peer by fingerprint",
|
||||
" /peer @alias Set DM peer by alias",
|
||||
" /reply, /r Reply to last DM sender",
|
||||
@@ -189,6 +190,19 @@ impl App {
|
||||
}
|
||||
return;
|
||||
}
|
||||
if text == "/backup" {
|
||||
if let Ok(seed) = crate::keystore::load_seed_raw() {
|
||||
match db.create_backup(&seed) {
|
||||
Ok(path) => {
|
||||
self.add_message(ChatLine { sender: "system".into(), text: format!("Backup saved: {}", path.display()), is_system: true, is_self: false, message_id: None, timestamp: Local::now() });
|
||||
}
|
||||
Err(e) => {
|
||||
self.add_message(ChatLine { sender: "system".into(), text: format!("Backup failed: {}", e), is_system: true, is_self: false, message_id: None, timestamp: Local::now() });
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if text == "/friend" || text == "/friends" {
|
||||
// Fetch encrypted friend list from server, decrypt locally
|
||||
let url = format!("{}/v1/friends", client.base_url);
|
||||
|
||||
@@ -48,6 +48,23 @@ pub async fn run_tui(
|
||||
network::poll_loop(poll_messages, poll_receipts, poll_pending_files, poll_fp, poll_identity, poll_db, poll_client, poll_last_dm, poll_connected).await;
|
||||
});
|
||||
|
||||
// Spawn periodic backup task (every 5 minutes)
|
||||
{
|
||||
let backup_db = db.clone();
|
||||
tokio::spawn(async move {
|
||||
let mut interval = tokio::time::interval(std::time::Duration::from_secs(300));
|
||||
loop {
|
||||
interval.tick().await;
|
||||
if let Ok(seed) = crate::keystore::load_seed_raw() {
|
||||
match backup_db.create_backup(&seed) {
|
||||
Ok(path) => tracing::debug!("Auto-backup created: {}", path.display()),
|
||||
Err(e) => tracing::warn!("Auto-backup failed: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Auto-join #ops if no peer set (create if needed)
|
||||
if app.peer_fp.is_none() {
|
||||
let fp_clean: String = our_fp.chars().filter(|c| c.is_ascii_hexdigit()).collect::<String>().to_lowercase();
|
||||
|
||||
@@ -93,7 +93,7 @@ pub fn process_incoming(
|
||||
eth_cache: &EthCache,
|
||||
last_dm_peer: &Arc<Mutex<Option<String>>>,
|
||||
) {
|
||||
match bincode::deserialize::<WireMessage>(raw) {
|
||||
match warzone_protocol::message::deserialize_envelope(raw) {
|
||||
Ok(wire) => process_wire_message(wire, identity, db, messages, receipts, pending_files, our_fp, client, last_dm_peer, eth_cache),
|
||||
Err(_) => {}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "warzone-protocol"
|
||||
version = "0.0.37"
|
||||
version = "0.0.38"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
description = "Core crypto & wire protocol for featherChat (Warzone messenger)"
|
||||
|
||||
@@ -43,7 +43,7 @@ pub enum ReceiptType {
|
||||
|
||||
/// Wire message format for transport between clients.
|
||||
/// Used by both CLI and WASM — MUST be identical for interop.
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum WireMessage {
|
||||
/// First message to a peer: X3DH key exchange + first ratchet message.
|
||||
KeyExchange {
|
||||
@@ -132,3 +132,104 @@ pub enum CallSignalType {
|
||||
/// Peer is busy.
|
||||
Busy,
|
||||
}
|
||||
|
||||
/// Current wire protocol version.
|
||||
pub const WIRE_VERSION: u8 = 1;
|
||||
/// Magic bytes to identify versioned envelope: "WZ"
|
||||
pub const WIRE_MAGIC: [u8; 2] = [0x57, 0x5A];
|
||||
|
||||
/// Serialize a WireMessage with version envelope.
|
||||
/// Format: [0x57][0x5A][version: u8][length: u32 BE][bincode payload]
|
||||
pub fn serialize_envelope(msg: &WireMessage) -> Result<Vec<u8>, String> {
|
||||
let payload =
|
||||
bincode::serialize(msg).map_err(|e| format!("serialize: {}", e))?;
|
||||
let len = payload.len() as u32;
|
||||
let mut out = Vec::with_capacity(7 + payload.len());
|
||||
out.extend_from_slice(&WIRE_MAGIC);
|
||||
out.push(WIRE_VERSION);
|
||||
out.extend_from_slice(&len.to_be_bytes());
|
||||
out.extend_from_slice(&payload);
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// Deserialize a WireMessage, handling both envelope and legacy formats.
|
||||
/// - Envelope: [0x57][0x5A][version][length][payload]
|
||||
/// - Legacy: raw bincode (no envelope)
|
||||
pub fn deserialize_envelope(data: &[u8]) -> Result<WireMessage, String> {
|
||||
if data.len() >= 7 && data[0] == WIRE_MAGIC[0] && data[1] == WIRE_MAGIC[1] {
|
||||
let version = data[2];
|
||||
let len =
|
||||
u32::from_be_bytes([data[3], data[4], data[5], data[6]]) as usize;
|
||||
if version > WIRE_VERSION {
|
||||
return Err(format!(
|
||||
"unsupported wire version {} (max {}). Please update your client.",
|
||||
version, WIRE_VERSION
|
||||
));
|
||||
}
|
||||
if data.len() < 7 + len {
|
||||
return Err("truncated envelope".to_string());
|
||||
}
|
||||
bincode::deserialize(&data[7..7 + len])
|
||||
.map_err(|e| format!("v{} deserialize: {}", version, e))
|
||||
} else {
|
||||
// Legacy: raw bincode
|
||||
bincode::deserialize(data)
|
||||
.map_err(|e| format!("legacy deserialize: {}", e))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod envelope_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn envelope_roundtrip() {
|
||||
let msg = WireMessage::Receipt {
|
||||
sender_fingerprint: "abc123".to_string(),
|
||||
message_id: "msg-001".to_string(),
|
||||
receipt_type: ReceiptType::Delivered,
|
||||
};
|
||||
let envelope = serialize_envelope(&msg).unwrap();
|
||||
assert_eq!(&envelope[..2], &WIRE_MAGIC);
|
||||
assert_eq!(envelope[2], WIRE_VERSION);
|
||||
|
||||
let decoded = deserialize_envelope(&envelope).unwrap();
|
||||
match decoded {
|
||||
WireMessage::Receipt { message_id, .. } => {
|
||||
assert_eq!(message_id, "msg-001")
|
||||
}
|
||||
_ => panic!("wrong variant"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_still_works() {
|
||||
let msg = WireMessage::Receipt {
|
||||
sender_fingerprint: "abc123".to_string(),
|
||||
message_id: "msg-002".to_string(),
|
||||
receipt_type: ReceiptType::Read,
|
||||
};
|
||||
let raw = bincode::serialize(&msg).unwrap();
|
||||
let decoded = deserialize_envelope(&raw).unwrap();
|
||||
match decoded {
|
||||
WireMessage::Receipt { message_id, .. } => {
|
||||
assert_eq!(message_id, "msg-002")
|
||||
}
|
||||
_ => panic!("wrong variant"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn future_version_rejected() {
|
||||
let mut envelope = serialize_envelope(&WireMessage::Receipt {
|
||||
sender_fingerprint: "x".into(),
|
||||
message_id: "y".into(),
|
||||
receipt_type: ReceiptType::Delivered,
|
||||
})
|
||||
.unwrap();
|
||||
envelope[2] = 99; // fake future version
|
||||
let result = deserialize_envelope(&envelope);
|
||||
assert!(result.is_err());
|
||||
assert!(result.unwrap_err().contains("unsupported wire version"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,15 +11,20 @@ use crate::errors::ProtocolError;
|
||||
|
||||
const MAX_SKIP: u32 = 1000;
|
||||
|
||||
/// Current serialization version for [`RatchetState`].
|
||||
const RATCHET_VERSION: u8 = 1;
|
||||
/// Magic byte to distinguish versioned from unversioned (legacy) data.
|
||||
const RATCHET_MAGIC: u8 = 0xFC;
|
||||
|
||||
/// A message produced by the ratchet.
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct RatchetMessage {
|
||||
pub header: RatchetHeader,
|
||||
pub ciphertext: Vec<u8>,
|
||||
}
|
||||
|
||||
/// Header included with each ratchet message.
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct RatchetHeader {
|
||||
/// Current DH ratchet public key.
|
||||
pub dh_public: [u8; 32],
|
||||
@@ -208,6 +213,37 @@ impl RatchetState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Serialize with version prefix: `[MAGIC][VERSION][bincode data]`.
|
||||
///
|
||||
/// Use [`deserialize_versioned`](Self::deserialize_versioned) to restore.
|
||||
pub fn serialize_versioned(&self) -> Result<Vec<u8>, String> {
|
||||
let data = bincode::serialize(self)
|
||||
.map_err(|e| format!("serialize: {}", e))?;
|
||||
let mut out = Vec::with_capacity(2 + data.len());
|
||||
out.push(RATCHET_MAGIC);
|
||||
out.push(RATCHET_VERSION);
|
||||
out.extend_from_slice(&data);
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// Deserialize with version awareness. Handles:
|
||||
/// - Versioned format: `[0xFC][version][bincode]`
|
||||
/// - Legacy format: raw bincode (no prefix)
|
||||
pub fn deserialize_versioned(data: &[u8]) -> Result<Self, String> {
|
||||
if data.len() >= 2 && data[0] == RATCHET_MAGIC {
|
||||
let version = data[1];
|
||||
match version {
|
||||
1 => bincode::deserialize(&data[2..])
|
||||
.map_err(|e| format!("v1 deserialize: {}", e)),
|
||||
_ => Err(format!("unknown ratchet version: {}", version)),
|
||||
}
|
||||
} else {
|
||||
// Legacy: try raw bincode (pre-versioning data)
|
||||
bincode::deserialize(data)
|
||||
.map_err(|e| format!("legacy deserialize: {}", e))
|
||||
}
|
||||
}
|
||||
|
||||
fn dh_ratchet_step(&mut self) -> Result<(), ProtocolError> {
|
||||
let their_pub = self
|
||||
.dh_remote
|
||||
@@ -312,6 +348,35 @@ mod tests {
|
||||
assert_eq!(bob.decrypt(&m2).unwrap(), b"two");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn versioned_serialize_roundtrip() {
|
||||
let (mut alice, mut bob) = make_pair();
|
||||
let msg = alice.encrypt(b"test versioning").unwrap();
|
||||
|
||||
// Save alice with versioned format
|
||||
let serialized = alice.serialize_versioned().unwrap();
|
||||
assert_eq!(serialized[0], 0xFC); // magic byte
|
||||
assert_eq!(serialized[1], 1); // version 1
|
||||
|
||||
// Restore and use
|
||||
let mut restored = RatchetState::deserialize_versioned(&serialized).unwrap();
|
||||
let msg2 = restored.encrypt(b"after restore").unwrap();
|
||||
let plain = bob.decrypt(&msg).unwrap();
|
||||
assert_eq!(plain, b"test versioning");
|
||||
let plain2 = bob.decrypt(&msg2).unwrap();
|
||||
assert_eq!(plain2, b"after restore");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_deserialize_works() {
|
||||
let (alice, _) = make_pair();
|
||||
// Serialize with raw bincode (legacy format)
|
||||
let legacy = bincode::serialize(&alice).unwrap();
|
||||
// Should still deserialize with versioned reader
|
||||
let restored = RatchetState::deserialize_versioned(&legacy).unwrap();
|
||||
assert_eq!(bincode::serialize(&restored).unwrap(), legacy);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn many_messages() {
|
||||
let (mut alice, mut bob) = make_pair();
|
||||
|
||||
@@ -9,9 +9,9 @@ use warzone_protocol::message::WireMessage;
|
||||
use crate::errors::AppResult;
|
||||
use crate::state::AppState;
|
||||
|
||||
/// Try to extract the message ID from raw bincode-serialized WireMessage bytes.
|
||||
/// Try to extract the message ID from raw WireMessage bytes (envelope or legacy).
|
||||
fn extract_message_id(data: &[u8]) -> Option<String> {
|
||||
if let Ok(wire) = bincode::deserialize::<WireMessage>(data) {
|
||||
if let Ok(wire) = warzone_protocol::message::deserialize_envelope(data) {
|
||||
match wire {
|
||||
WireMessage::KeyExchange { id, .. } => Some(id),
|
||||
WireMessage::Message { id, .. } => Some(id),
|
||||
|
||||
@@ -50,7 +50,7 @@ async fn pwa_manifest() -> impl IntoResponse {
|
||||
|
||||
async fn service_worker() -> impl IntoResponse {
|
||||
([(header::CONTENT_TYPE, "application/javascript")], r##"
|
||||
const CACHE = 'wz-v19';
|
||||
const CACHE = 'wz-v20';
|
||||
const SHELL = ['/', '/wasm/warzone_wasm.js', '/wasm/warzone_wasm_bg.wasm', '/icon.svg', '/manifest.json'];
|
||||
|
||||
self.addEventListener('install', e => {
|
||||
@@ -287,7 +287,7 @@ let pollTimer = null;
|
||||
let ws = null; // WebSocket connection
|
||||
let wasmReady = false;
|
||||
|
||||
const VERSION = '0.0.37';
|
||||
const VERSION = '0.0.38';
|
||||
let DEBUG = true; // toggle with /debug command
|
||||
|
||||
// ── Receipt tracking ──
|
||||
|
||||
@@ -21,9 +21,9 @@ use warzone_protocol::message::WireMessage;
|
||||
|
||||
use crate::state::AppState;
|
||||
|
||||
/// Try to extract the message ID from raw bincode-serialized WireMessage bytes.
|
||||
/// Try to extract the message ID from raw WireMessage bytes (envelope or legacy).
|
||||
fn extract_message_id(data: &[u8]) -> Option<String> {
|
||||
if let Ok(wire) = bincode::deserialize::<WireMessage>(data) {
|
||||
if let Ok(wire) = warzone_protocol::message::deserialize_envelope(data) {
|
||||
match wire {
|
||||
WireMessage::KeyExchange { id, .. } => Some(id),
|
||||
WireMessage::Message { id, .. } => Some(id),
|
||||
@@ -147,7 +147,7 @@ async fn handle_socket(socket: WebSocket, state: AppState, fingerprint: String)
|
||||
}
|
||||
|
||||
// Call signal side effects
|
||||
if let Ok(WireMessage::CallSignal { ref id, ref sender_fingerprint, ref signal_type, .. }) = bincode::deserialize::<WireMessage>(message) {
|
||||
if let Ok(WireMessage::CallSignal { ref id, ref sender_fingerprint, ref signal_type, .. }) = warzone_protocol::message::deserialize_envelope(message) {
|
||||
use warzone_protocol::message::CallSignalType;
|
||||
let now = chrono::Utc::now().timestamp();
|
||||
match signal_type {
|
||||
|
||||
@@ -212,15 +212,16 @@ impl WasmSession {
|
||||
}
|
||||
|
||||
pub fn save(&self) -> Result<String, JsValue> {
|
||||
let bytes = bincode::serialize(&self.ratchet).map_err(|e| JsValue::from_str(&e.to_string()))?;
|
||||
let bytes = self.ratchet.serialize_versioned()
|
||||
.map_err(|e| JsValue::from_str(&e))?;
|
||||
Ok(base64::Engine::encode(&base64::engine::general_purpose::STANDARD, &bytes))
|
||||
}
|
||||
|
||||
pub fn restore(data: &str) -> Result<WasmSession, JsValue> {
|
||||
let bytes = base64::Engine::decode(&base64::engine::general_purpose::STANDARD, data)
|
||||
.map_err(|e| JsValue::from_str(&e.to_string()))?;
|
||||
let ratchet: RatchetState = bincode::deserialize(&bytes)
|
||||
.map_err(|e| JsValue::from_str(&e.to_string()))?;
|
||||
let ratchet = RatchetState::deserialize_versioned(&bytes)
|
||||
.map_err(|e| JsValue::from_str(&e))?;
|
||||
Ok(WasmSession { ratchet, x3dh_ephemeral_public: None, x3dh_used_otpk_id: None })
|
||||
}
|
||||
}
|
||||
@@ -372,7 +373,7 @@ pub fn decrypt_wire_message(
|
||||
let seed = Seed::from_bytes(sb);
|
||||
let id = seed.derive_identity();
|
||||
|
||||
let wire: WireMessage = bincode::deserialize(message_bytes)
|
||||
let wire: WireMessage = warzone_protocol::message::deserialize_envelope(message_bytes)
|
||||
.map_err(|e| JsValue::from_str(&format!("deserialize wire: {}", e)))?;
|
||||
|
||||
match wire {
|
||||
@@ -403,7 +404,7 @@ pub fn decrypt_wire_message(
|
||||
|
||||
let session_b64 = base64::Engine::encode(
|
||||
&base64::engine::general_purpose::STANDARD,
|
||||
&bincode::serialize(&ratchet).unwrap_or_default(),
|
||||
&ratchet.serialize_versioned().unwrap_or_default(),
|
||||
);
|
||||
|
||||
Ok(serde_json::json!({
|
||||
@@ -424,15 +425,15 @@ pub fn decrypt_wire_message(
|
||||
let session_bytes = base64::Engine::decode(
|
||||
&base64::engine::general_purpose::STANDARD, &session_data,
|
||||
).map_err(|e| JsValue::from_str(&e.to_string()))?;
|
||||
let mut ratchet: RatchetState = bincode::deserialize(&session_bytes)
|
||||
.map_err(|e| JsValue::from_str(&e.to_string()))?;
|
||||
let mut ratchet = RatchetState::deserialize_versioned(&session_bytes)
|
||||
.map_err(|e| JsValue::from_str(&e))?;
|
||||
|
||||
let plain = ratchet.decrypt(&ratchet_message)
|
||||
.map_err(|e| JsValue::from_str(&format!("decrypt: {}", e)))?;
|
||||
|
||||
let session_b64 = base64::Engine::encode(
|
||||
&base64::engine::general_purpose::STANDARD,
|
||||
&bincode::serialize(&ratchet).unwrap_or_default(),
|
||||
&ratchet.serialize_versioned().unwrap_or_default(),
|
||||
);
|
||||
|
||||
Ok(serde_json::json!({
|
||||
|
||||
Reference in New Issue
Block a user