1use base64::Engine;
2use clap::{Parser, Subcommand};
3use openmls::prelude::{MlsMessageIn, TlsDeserializeTrait};
4use proteus_wasm::internal::message::SessionTag;
5use proteus_wasm::internal::util::fmt_hex;
6use proteus_wasm::keys::{PreKeyBundle, Signature};
7use proteus_wasm::message::{CipherMessage, Envelope, Message, PreKeyMessage};
8use std::ops::Deref;
9
10#[derive(Debug)]
11#[allow(dead_code)]
12struct ProteusPreKeyBundle {
13 pub version: u8,
14 pub prekey_id: u16,
15 pub public_key: String,
16 pub identity_key: String,
17 pub signature: Option<Signature>,
18}
19
20impl From<PreKeyBundle> for ProteusPreKeyBundle {
21 fn from(bundle: PreKeyBundle) -> Self {
22 Self {
23 version: bundle.version,
24 prekey_id: bundle.prekey_id.value(),
25 public_key: bundle.public_key.fingerprint(),
26 identity_key: bundle.identity_key.fingerprint(),
27 signature: bundle.signature,
28 }
29 }
30}
31
32#[derive(Debug)]
33#[allow(dead_code)]
34struct ProteusEnvelope {
35 version: u16,
36 mac: String,
37 message: ProteusMessage,
38}
39
40#[derive(Debug)]
41#[allow(dead_code)]
42enum ProteusMessage {
43 Plain(ProteusCipherMessage),
44 Keyed(ProteusPrekeyMessage),
45}
46
47#[derive(Debug)]
48#[allow(dead_code)]
49struct ProteusPrekeyMessage {
50 prekey_id: u16,
51 base_key: String,
52 identity_key: String,
53 message: ProteusCipherMessage,
54}
55
56#[derive(Debug)]
57#[allow(dead_code)]
58struct ProteusCipherMessage {
59 pub session_tag: SessionTag,
60 pub counter: u32,
61 pub prev_counter: u32,
62 pub ratchet_key: String,
63 pub cipher_text: String,
64}
65
66impl From<Envelope<'_>> for ProteusEnvelope {
67 fn from(envelope: Envelope) -> Self {
68 Self {
69 version: envelope.version(),
70 mac: fmt_hex(&envelope.mac().clone().into_bytes()),
71 message: envelope.message().into(),
72 }
73 }
74}
75
76impl From<&Message<'_>> for ProteusMessage {
77 fn from(message: &Message) -> Self {
78 match message {
79 Message::Plain(plain) => ProteusMessage::Plain(plain.deref().into()),
80 Message::Keyed(keyed) => ProteusMessage::Keyed(keyed.deref().into()),
81 }
82 }
83}
84
85impl From<&PreKeyMessage<'_>> for ProteusPrekeyMessage {
86 fn from(message: &PreKeyMessage) -> Self {
87 Self {
88 prekey_id: message.prekey_id.value(),
89 base_key: message.base_key.fingerprint(),
90 identity_key: message.identity_key.fingerprint(),
91 message: (&message.message).into(),
92 }
93 }
94}
95
96impl From<&CipherMessage<'_>> for ProteusCipherMessage {
97 fn from(message: &CipherMessage) -> Self {
98 Self {
99 session_tag: message.session_tag,
100 counter: message.counter.value(),
101 prev_counter: message.prev_counter.value(),
102 ratchet_key: message.ratchet_key.fingerprint(),
103 cipher_text: base64::prelude::BASE64_STANDARD.encode(message.cipher_text.clone()),
104 }
105 }
106}
107
108#[derive(Parser, Debug)]
110#[clap(name = "decode", version)]
111pub struct App {
112 #[clap(subcommand)]
113 command: Command,
114}
115
116#[derive(Debug, Subcommand)]
117enum Command {
118 PrekeyBundle { bundle: String },
120 ProteusMessage {
122 message: String,
124 },
125 MlsMessage { message: String },
127}
128
129fn main() -> Result<(), Box<dyn std::error::Error>> {
130 let app = App::parse();
131 match app.command {
132 Command::PrekeyBundle { bundle } => {
133 let bytes = base64::prelude::BASE64_STANDARD.decode(bundle)?;
134 let bundle = PreKeyBundle::deserialise(&bytes)?;
135 println!("{:#?}", ProteusPreKeyBundle::from(bundle));
136 Ok(())
137 }
138 Command::ProteusMessage { message } => {
139 let bytes = base64::prelude::BASE64_STANDARD.decode(message)?;
140 let message = Envelope::deserialise(&bytes)?;
141 println!("{:#?}", ProteusEnvelope::from(message));
142 Ok(())
143 }
144 Command::MlsMessage { message } => {
145 let bytes = base64::prelude::BASE64_STANDARD.decode(message)?;
146 let message = MlsMessageIn::tls_deserialize(&mut bytes.as_slice())?;
147 println!("{message:#?}");
148 Ok(())
149 }
150 }
151}