reaching_link/
intermediary.rs

1// SPDX-FileCopyrightText: 2025 eaon <eaon@posteo.net>
2// SPDX-License-Identifier: EUPL-1.2
3
4use std::collections::{HashMap, HashSet, VecDeque};
5
6use wasm_bindgen::prelude::*;
7use zeroize::Zeroizing;
8
9use reach_aliases::ReachRng;
10use reach_core::{
11    communication::Communicable,
12    memory::{
13        HintedEnvelopeId, Key, Message, MessageChunkMetadata, MessageOrder, OpenedEnvelope,
14        ReachingKeys,
15    },
16    wire::{
17        AttestantVerifyingKeys, CredentialVault, Envelope, EnvelopeId, MessageVaultId,
18        ReachableSignedKeys, RemoveEnvelopeIdHint, Salts,
19    },
20};
21
22use crate::memory::{ReachingSecrets, UnsignedReachingPublicKeys};
23
24#[wasm_bindgen]
25pub struct State {
26    pub(super) csprng: ReachRng,
27    pub(super) language: String,
28    pub(super) salts: Option<Salts>,
29    pub(super) attestant_verifying_keys: Option<AttestantVerifyingKeys>,
30    pub(super) passphrase: Option<Zeroizing<String>>,
31    pub(super) keys: Option<(ReachingSecrets, ReachingKeys, UnsignedReachingPublicKeys)>,
32    pub(super) reachable_signed_keys: VecDeque<Vec<ReachableSignedKeys>>,
33    pub(super) envelope_ids: HashSet<EnvelopeId>,
34    pub(super) envelopes: HashMap<MessageVaultId, (Envelope, OpenedEnvelope)>,
35    pub(super) envelope_seed_parts: Option<(Vec<ReachableSignedKeys>, Key, Vec<CredentialVault>)>,
36    pub(super) messages: Vec<Message>,
37}
38
39#[wasm_bindgen]
40impl State {
41    #[wasm_bindgen(constructor)]
42    pub fn new() -> Self {
43        Self::default()
44    }
45}
46
47impl Default for State {
48    fn default() -> Self {
49        let csprng = reach_core::memory::rng::<2048>().expect("Rng should initialize");
50
51        Self {
52            csprng,
53            language: "en".to_string(),
54            salts: None,
55            attestant_verifying_keys: None,
56            passphrase: None,
57            keys: None,
58            reachable_signed_keys: VecDeque::new(),
59            envelope_ids: HashSet::new(),
60            envelopes: HashMap::new(),
61            envelope_seed_parts: None,
62            messages: vec![],
63        }
64    }
65}
66
67#[wasm_bindgen]
68pub struct LinkHintedEnvelopeId {
69    envelope_id: Vec<u8>,
70    remove_envelope_id_hint: Vec<u8>,
71}
72
73impl From<&HintedEnvelopeId> for LinkHintedEnvelopeId {
74    fn from(value: &HintedEnvelopeId) -> Self {
75        LinkHintedEnvelopeId {
76            envelope_id: value.envelope_id.to_communication().encode(),
77            remove_envelope_id_hint: RemoveEnvelopeIdHint::from(value)
78                .to_communication()
79                .encode(),
80        }
81    }
82}
83
84#[wasm_bindgen]
85impl LinkHintedEnvelopeId {
86    pub fn envelope_id(&self) -> Vec<u8> {
87        self.envelope_id.clone()
88    }
89
90    pub fn remove_envelope_id_hint(&self) -> Vec<u8> {
91        self.remove_envelope_id_hint.clone()
92    }
93}
94
95#[wasm_bindgen]
96pub struct LinkMessage {
97    content: String,
98    language: String,
99    order: u64,
100    attachments: Vec<u8>,
101}
102
103#[wasm_bindgen]
104impl LinkMessage {
105    pub fn content(&self) -> String {
106        self.content.clone()
107    }
108
109    pub fn language(&self) -> String {
110        self.language.clone()
111    }
112
113    pub fn order(&self) -> u64 {
114        self.order
115    }
116
117    pub fn attachments(&self) -> Vec<u8> {
118        self.attachments.clone()
119    }
120}
121
122impl TryFrom<&Message> for LinkMessage {
123    type Error = LinkError;
124
125    fn try_from(message: &Message) -> Result<Self, Self::Error> {
126        let order = match message.order() {
127            MessageOrder::Relative(order) => Ok(*order),
128            _ => Err("Messages with absolute dates are not currently supported."),
129        }?;
130
131        let language = match &message.chunk.metadata {
132            MessageChunkMetadata::Language(language) => Ok(language.clone()),
133            _ => Err("Invalid message formatting"),
134        }?;
135
136        Ok(LinkMessage {
137            content: String::from_utf8(message.chunk.content.clone())?,
138            language,
139            order,
140            attachments: vec![],
141        })
142    }
143}
144
145#[wasm_bindgen]
146#[derive(Debug)]
147pub struct LinkError {
148    error: String,
149}
150
151#[wasm_bindgen]
152impl LinkError {
153    pub fn error(&self) -> String {
154        self.error.clone()
155    }
156}
157
158impl<T> From<T> for LinkError
159where
160    T: std::fmt::Display,
161{
162    fn from(value: T) -> Self {
163        Self {
164            error: format!("{}", value),
165        }
166    }
167}