xpx_chain_sdk/models/message/
message_secure.rs1use anyhow::Result;
8
9use crate::{
10 account::{Account, PublicAccount},
11 crypto::{Ed25519BlockCipher, PublicKey},
12 errors_const,
13 helpers::{hex_decode, hex_encode, is_hex},
14};
15
16use super::{Message, MessageType, PlainMessage};
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct SecureMessage {
31 #[serde(rename = "type")]
33 pub r#type: MessageType,
34 pub payload: String,
36}
37
38impl SecureMessage {
39 pub fn create(
45 sender_account: &Account,
46 recipient_public_account: &PublicAccount,
47 payload: &str,
48 ) -> Result<Self> {
49 ensure!(!payload.is_empty(), "message must not be empty.");
50
51 let mut block_cipher = Ed25519BlockCipher::new(&sender_account.key_pair.secret);
53
54 let payload_vec = if is_hex(&payload) {
55 hex_decode(payload)
56 } else {
57 payload.as_bytes().to_vec()
58 };
59
60 let encrypted_payload = block_cipher.encrypt(
61 &payload_vec,
62 &PublicKey::from_bytes(recipient_public_account.to_builder())
63 .map_err(|err| anyhow!(err))?,
64 );
65
66 Ok(SecureMessage {
67 r#type: MessageType::SecureMessageType,
68 payload: hex_encode(&encrypted_payload),
69 })
70 }
71
72 pub fn from_hex_payload(payload: &str) -> Result<Self> {
76 ensure!(is_hex(payload), errors_const::ERR_INVALID_PAYLOAD_HEX);
77
78 Ok(SecureMessage {
79 r#type: MessageType::SecureMessageType,
80 payload: payload.to_string(),
81 })
82 }
83
84 pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
87 ensure!(!bytes.is_empty(), "bytes must not be empty.");
88 let payload = hex::encode(bytes);
89 Self::from_hex_payload(&payload)
90 }
91
92 pub fn decrypt(
96 &self,
97 recipient_account: &Account,
98 sender_public_account: PublicAccount,
99 ) -> Result<PlainMessage> {
100 let mut block_cipher = Ed25519BlockCipher::new(&recipient_account.key_pair.secret);
101
102 let encrypted_payload = block_cipher
103 .decrypt(
104 &hex_decode(&self.payload),
105 &PublicKey::from_bytes(sender_public_account.to_builder())
106 .map_err(|err| anyhow!(err))?,
107 )
108 .map_err(|err| anyhow!(err))?;
109
110 let payload = match String::from_utf8(encrypted_payload.to_owned()) {
111 Ok(payload) => payload,
112 Err(_) => hex_encode(&encrypted_payload),
113 };
114
115 Ok(PlainMessage::create(&payload))
116 }
117}
118
119#[typetag::serde]
120impl Message for SecureMessage {
121 fn message_type(&self) -> MessageType {
122 self.r#type
123 }
124 fn payload(&self) -> String {
125 self.payload.to_owned()
126 }
127 fn box_clone(&self) -> Box<dyn Message + 'static> {
128 Box::new((*self).clone())
129 }
130}
131
132impl core::fmt::Display for SecureMessage {
133 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
134 write!(
135 f,
136 "{}",
137 serde_json::to_string_pretty(&self).unwrap_or_default()
138 )
139 }
140}
141
142#[cfg(test)]
143pub mod tests {
144 use lazy_static::lazy_static;
145
146 use crate::account::Account;
147 use crate::message::SecureMessage;
148 use crate::network::NetworkType;
149
150 const SENDER_PRIVATE_KEY: &str =
151 "2602F4236B199B3DF762B2AAB46FC3B77D8DDB214F0B62538D3827576C46C108";
152 const RECIPIENT_PRIVATE_KEY: &str =
153 "B72F2950498111BADF276D6D9D5E345F04E0D5C9B8342DA983C3395B4CF18F08";
154
155 lazy_static! {
156 static ref SENDER: Account =
157 Account::from_hex_private_key(SENDER_PRIVATE_KEY, NetworkType::PrivateTest).unwrap();
158 static ref RECIPIENT: Account =
159 Account::from_hex_private_key(RECIPIENT_PRIVATE_KEY, NetworkType::PrivateTest).unwrap();
160 }
161
162 #[test]
163 fn test_should_create_from_a_dto() {
164 let payload = "test transaction";
165 let encrypted_message = SecureMessage::from_hex_payload(&payload);
166
167 assert!(encrypted_message.is_err());
168 }
169
170 #[test]
171 fn test_should_return_encrypted_message_dto() {
172 let encrypted_message = SENDER
173 .encrypt_message(RECIPIENT.public_account, "test transaction")
174 .unwrap();
175 let plain_message = RECIPIENT
176 .decrypt_message(SENDER.public_account, encrypted_message)
177 .unwrap();
178
179 assert_eq!(plain_message.payload, "test transaction");
180 }
181
182 #[test]
183 fn test_should_decrypt_message_from_raw_encrypted_message_payload() {
184 let encrypted_message = SENDER
185 .encrypt_message(RECIPIENT.public_account, "Testing simple transfer")
186 .unwrap();
187 let encrypted_message_from_payload =
188 SecureMessage::from_hex_payload(&encrypted_message.payload).unwrap();
189 let plain_message = RECIPIENT
190 .decrypt_message(SENDER.public_account, encrypted_message_from_payload)
191 .unwrap();
192
193 assert_eq!(plain_message.payload, "Testing simple transfer");
194 }
195}