1use aes_gcm::aead::{Aead, KeyInit, OsRng};
6use aes_gcm::{Key, Nonce};
7use alloy::hex;
8use alloy::primitives::Bytes;
9use base64ct::{Base64, Base64UrlUnpadded, Encoding};
10use chacha20poly1305::ChaCha20Poly1305;
11use log::debug;
12use rand::RngCore;
13use serde::de::DeserializeOwned;
14use serde::{Deserialize, Serialize};
15use serde_json::Value;
16
17use crate::error::Result;
18use crate::types::Id;
19
20#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
21pub struct MessageError {
22 pub message: Option<String>,
23 pub code: Option<i64>,
24 pub data: Option<Bytes>,
25}
26
27#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
28pub struct Message<M = String, T = Value> {
29 pub jsonrpc: String,
30 #[serde(skip_serializing_if = "Option::is_none")]
31 pub method: Option<M>,
32 #[serde(skip_serializing_if = "Option::is_none")]
33 pub params: Option<T>,
34 #[serde(skip_serializing_if = "Option::is_none")]
35 pub result: Option<T>,
36 #[serde(skip_serializing_if = "Option::is_none")]
37 pub error: Option<MessageError>,
38 pub id: Id,
39}
40
41pub const IV_LENGTH: usize = 12;
42pub const KEY_LENGTH: usize = 32;
43pub const TYPE_LENGTH: usize = 1;
44pub const TYPE_0: u8 = 0;
45pub const TYPE_1: u8 = 1;
46pub const TYPE_2: u8 = 2;
47
48impl<T: Serialize + DeserializeOwned> Message<String, T> {
49 pub fn result(result: T, id: Id) -> Message<String, T> {
50 Message {
51 jsonrpc: "2.0".to_string(),
52 method: None,
53 params: None,
54 result: Some(result),
55 error: None,
56 id,
57 }
58 }
59}
60
61impl<
62 M: PartialEq + Serialize + DeserializeOwned,
63 T: Serialize + DeserializeOwned,
64> Message<M, T>
65{
66 pub fn encrypt(
68 &self,
69 sym_key: [u8; 32],
70 type_byte: Option<u8>,
71 sender_public_key: Option<String>,
72 encoding: Option<EncodingType>,
73 ) -> Result<String> {
74 let type_byte = type_byte.unwrap_or(TYPE_0);
75 if type_byte == TYPE_1 && sender_public_key.is_none() {
76 return Err("Missing sender public key for type 1 envelope".into());
77 }
78
79 let mut iv = vec![0u8; IV_LENGTH];
80 OsRng.fill_bytes(&mut iv);
81
82 let key = Key::<ChaCha20Poly1305>::from_slice(&sym_key);
83 let cipher = ChaCha20Poly1305::new(key);
84 let nonce = Nonce::from_slice(&iv);
85
86 let message = serde_json::to_string(self)?;
87 debug!("encrypting message -> {message}");
88 let sealed = cipher
89 .encrypt(nonce, message.as_bytes())
90 .expect("encryption failed");
91
92 Ok(EncryptedEnvelope {
93 type_byte,
94 sealed,
95 iv,
96 sender_public_key: sender_public_key.as_ref().map(|hex| {
97 hex::decode(hex).expect("invalid sender_public_key")
98 }),
99 }
100 .serialize(encoding.clone().unwrap_or(EncodingType::Base64)))
101 }
102}
103
104impl<M, P> Message<M, P>
105where
106 M: PartialEq,
107{
108 pub fn is(&self, method: M) -> bool {
109 self.method == Some(method)
110 }
111
112 pub fn create_success_response<R>(
113 &self,
114 result_data: R,
115 ) -> Message<String, R> {
116 Message {
117 jsonrpc: self.jsonrpc.clone(),
118 method: None,
119 params: None,
120 result: Some(result_data),
121 error: None,
122 id: self.id.clone(),
123 }
124 }
125
126 pub fn create_error_response(
127 &self,
128 message: String,
129 code: i64,
130 data: Option<Bytes>,
131 ) -> Message<M, Value> {
132 Message {
133 jsonrpc: self.jsonrpc.clone(),
134 method: None,
135 params: None,
136 result: None,
137 error: Some(MessageError {
138 message: Some(message),
139 code: Some(code),
140 data,
141 }),
142 id: self.id.clone(),
143 }
144 }
145}
146
147impl Message<String, Value> {
148 pub fn decrypt(
150 cipher_text: &str,
151 sym_key: [u8; 32],
152 encoding: Option<EncodingType>,
153 ) -> Result<Self> {
154 let key = Key::<ChaCha20Poly1305>::from_slice(&sym_key);
155
156 let encoding_params = EncryptedEnvelope::deserialize(
157 cipher_text,
158 encoding.clone().unwrap_or(EncodingType::Base64),
159 );
160
161 let cipher = ChaCha20Poly1305::new(key);
162 let nonce = Nonce::from_slice(&encoding_params.iv);
163 let decrypted =
164 cipher.decrypt(nonce, encoding_params.sealed.as_ref())?;
165 let str = String::from_utf8(decrypted)?;
166 debug!("decrypted message -> {str}");
167 Ok(serde_json::from_str::<Self>(&str).map_err(|e| {
168 format!("Failed to deserialize JSON-RPC request: {e}\n{str}")
169 })?)
170 }
171
172 pub fn try_decode<M, P>(&self) -> Result<Message<M, P>>
173 where
174 M: DeserializeOwned + Clone,
175 P: DeserializeOwned,
176 {
177 let method = self
178 .method
179 .as_ref()
180 .map(|m| serde_plain::from_str::<M>(m.as_str()))
181 .transpose()?;
182 let params = self
183 .params
184 .as_ref()
185 .map(|p| serde_json::from_value::<P>(p.clone()))
186 .transpose()?;
187 let result = self
188 .result
189 .as_ref()
190 .map(|r| serde_json::from_value::<P>(r.clone()))
191 .transpose()?;
192
193 Ok(Message {
194 jsonrpc: self.jsonrpc.clone(),
195 method,
196 params,
197 result,
198 error: self.error.clone(),
199 id: self.id.clone(),
200 })
201 }
202
203 pub fn decode_result<R>(&self) -> Result<Message<String, R>>
204 where
205 R: DeserializeOwned,
206 {
207 let result = self
208 .result
209 .as_ref()
210 .map(|r| serde_json::from_value::<R>(r.clone()))
211 .transpose()?;
212 Ok(Message {
213 jsonrpc: self.jsonrpc.clone(),
214 method: None,
215 params: None,
216 result,
217 error: None,
218 id: self.id.clone(),
219 })
220 }
221
222 pub fn into_value(self) -> Result<Value> {
223 Ok(serde_json::to_value(self)?)
224 }
225}
226
227#[derive(Debug)]
228pub struct EncryptedEnvelope {
229 pub type_byte: u8,
230 pub sealed: Vec<u8>,
231 pub iv: Vec<u8>,
232 pub sender_public_key: Option<Vec<u8>>,
234}
235
236#[derive(Debug, Clone)]
237pub enum EncodingType {
238 Base64,
239 Base64Url,
240}
241
242impl EncryptedEnvelope {
243 pub fn serialize(&self, encoding: EncodingType) -> String {
245 let mut bytes = vec![self.type_byte];
246
247 match self.type_byte {
248 TYPE_2 => {
249 bytes.extend_from_slice(&self.sealed);
250 }
251 TYPE_1 => {
252 let sender = self
253 .sender_public_key
254 .as_ref()
255 .expect("Missing sender public key for type 1 envelope");
256 bytes.extend_from_slice(sender);
257 bytes.extend_from_slice(&self.iv);
258 bytes.extend_from_slice(&self.sealed);
259 }
260 _ => {
261 bytes.extend_from_slice(&self.iv);
263 bytes.extend_from_slice(&self.sealed);
264 }
265 }
266
267 match encoding {
268 EncodingType::Base64 => Base64::encode_string(&bytes),
269 EncodingType::Base64Url => Base64UrlUnpadded::encode_string(&bytes),
270 }
271 }
272
273 pub fn deserialize(encoded: &str, encoding: EncodingType) -> Self {
275 let bytes = match encoding {
276 EncodingType::Base64 => {
277 Base64::decode_vec(encoded).expect("invalid base64")
278 }
279 EncodingType::Base64Url => Base64UrlUnpadded::decode_vec(encoded)
280 .expect("invalid base64url"),
281 };
282
283 let type_byte = bytes[0];
284 let slice1 = TYPE_LENGTH;
285
286 match type_byte {
287 TYPE_1 => {
288 let slice2 = slice1 + KEY_LENGTH;
289 let slice3 = slice2 + IV_LENGTH;
290 let sender_public_key = bytes[slice1..slice2].to_vec();
291 let iv = bytes[slice2..slice3].to_vec();
292 let sealed = bytes[slice3..].to_vec();
293 EncryptedEnvelope {
294 type_byte,
295 sealed,
296 iv,
297 sender_public_key: Some(sender_public_key),
298 }
299 }
300 TYPE_2 => {
301 let sealed = bytes[slice1..].to_vec();
302 let mut iv = vec![0u8; IV_LENGTH];
303 OsRng.fill_bytes(&mut iv);
304 EncryptedEnvelope {
305 type_byte,
306 sealed,
307 iv,
308 sender_public_key: None,
309 }
310 }
311 _ => {
312 let slice2 = slice1 + IV_LENGTH;
314 let iv = bytes[slice1..slice2].to_vec();
315 let sealed = bytes[slice2..].to_vec();
316 EncryptedEnvelope {
317 type_byte,
318 sealed,
319 iv,
320 sender_public_key: None,
321 }
322 }
323 }
324 }
325}
326
327#[cfg(test)]
328mod tests {
329 use serde_json::{Number, json};
330
331 use super::*;
332 use crate::message::TYPE_1;
333 use crate::{relay_auth::Keypair, types::Id, utils::derive_sym_key};
334
335 #[test]
336 fn test_encrypt_decrypt() {
337 let key = Keypair::generate();
338 let sym_key = derive_sym_key(key.seed, key.public_key);
339
340 let message = Message::result(
341 json!({
342 "cacao": {
343 "header": {
344 "h": "caip122"
345 },
346 "payload": {
347 "iss": {
348 "account_address": hex::encode(key.public_key),
349 "chain_id": "eip155"
350 },
351 "domain": "https://example.com",
352 "uri": "wc:1234@2?relay-protocol=irn",
353 "version": "1.0.0",
354 "statement": "Please sign this message to authenticate",
355 "nonce": hex::encode(key.seed),
356 },
357 "signature": {
358 "t": "eip191",
359 }
360 }
361 }),
362 Id::Number(Number::from(12345)),
363 );
364
365 let encrypted_message = message
366 .encrypt(
367 sym_key,
368 Some(TYPE_1),
369 Some(hex::encode(key.public_key)),
370 None,
371 )
372 .unwrap();
373
374 let decrypted_message =
375 Message::decrypt(&encrypted_message, sym_key, None).unwrap();
376
377 assert_eq!(message.jsonrpc, decrypted_message.jsonrpc);
378 }
379}