1use crate::{KeyPair, Signature, VerifyError, VerifyingKey};
2use blake3::Hasher;
3use serde::{Deserialize, Serialize};
4
5use crate::{
6 crypto::{ChaCha20Poly1305Content, PqxdhEncryptedContent},
7 Ed25519SelfEncryptedContent, EphemeralEcdhContent, KeyId as VerifyingKeyId, MessageId,
8 MlDsaSelfEncryptedContent,
9};
10use forward_compatible_enum::ForwardCompatibleEnum;
11
12mod store_key;
13pub use store_key::{PqxdhInboxProtocol, StoreKey};
14
15#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
16pub enum Tag {
17 Protected, Event {
19 id: MessageId,
21 #[serde(default)]
22 relays: Vec<String>,
23 },
24 User {
25 id: VerifyingKeyId,
27 #[serde(default)]
28 relays: Vec<String>,
29 },
30 Channel {
31 id: Vec<u8>,
33 #[serde(default)]
34 relays: Vec<String>,
35 },
36}
37
38impl From<&MessageFull> for Tag {
39 fn from(message: &MessageFull) -> Self {
40 Tag::Event {
41 id: *message.id(),
42 relays: vec![],
43 }
44 }
45}
46
47impl std::fmt::Debug for Tag {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 match self {
50 Tag::Protected => write!(f, "Protected"),
51 Tag::Event { id, .. } => write!(f, "Event(#{})", hex::encode(id.as_bytes())),
52 Tag::User { id, .. } => write!(f, "User(#{})", hex::encode(id)),
53 Tag::Channel { id, .. } => write!(f, "Channel(#{})", hex::encode(id)),
54 }
55 }
56}
57
58#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
59pub enum Kind {
60 Regular,
62 Ephemeral(u32),
67 Store(StoreKey),
69 ClearStore(StoreKey), }
72
73#[derive(Clone, PartialEq, ForwardCompatibleEnum)]
143pub enum Content {
144 #[discriminant(0)]
152 Raw(Vec<u8>),
153
154 #[discriminant(20)]
164 ChaCha20Poly1305(ChaCha20Poly1305Content),
165
166 #[discriminant(40)]
175 Ed25519SelfEncrypted(Ed25519SelfEncryptedContent),
176
177 #[discriminant(41)]
187 MlDsaSelfEncrypted(MlDsaSelfEncryptedContent),
188
189 #[discriminant(42)]
200 PqxdhEncrypted(PqxdhEncryptedContent),
201
202 #[discriminant(80)]
211 EphemeralEcdh(EphemeralEcdhContent),
212
213 Unknown { discriminant: u32, data: Vec<u8> },
218}
219
220impl std::fmt::Debug for Content {
221 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
222 match self {
223 Content::Raw(data) => write!(f, "Raw([u8; {}])", data.len()),
224 Content::ChaCha20Poly1305(..) => write!(f, "ChaCha20Poly1305(#redacted#)"),
225 Content::Ed25519SelfEncrypted(..) => write!(f, "Ed25519SelfEncrypted(#redacted#)"),
226 Content::MlDsaSelfEncrypted(..) => write!(f, "MlDsaSelfEncrypted(#redacted#)"),
227 Content::PqxdhEncrypted(..) => write!(f, "PqxdhEncrypted(#redacted#)"),
228 Content::EphemeralEcdh(..) => write!(f, "EphemeralEcdh(#redacted#)"),
229 Content::Unknown { discriminant, data } => {
230 write!(f, "Unknown({:?}, {:?})", discriminant, data.len())
231 }
232 }
233 }
234}
235
236impl Content {
237 pub fn raw(data: Vec<u8>) -> Self {
239 Content::Raw(data)
240 }
241
242 pub fn raw_typed<T: Serialize>(data: &T) -> Result<Self, postcard::Error> {
244 Ok(Content::Raw(postcard::to_stdvec(data)?))
245 }
246
247 pub fn encrypted(content: ChaCha20Poly1305Content) -> Self {
249 Content::ChaCha20Poly1305(content)
250 }
251
252 pub fn ed25519_self_encrypted(content: Ed25519SelfEncryptedContent) -> Self {
254 Content::Ed25519SelfEncrypted(content)
255 }
256
257 pub fn ml_dsa_self_encrypted(content: MlDsaSelfEncryptedContent) -> Self {
259 Content::MlDsaSelfEncrypted(content)
260 }
261
262 pub fn ephemeral_ecdh(content: EphemeralEcdhContent) -> Self {
264 Content::EphemeralEcdh(content)
265 }
266
267 pub fn pqxdh_encrypted(content: PqxdhEncryptedContent) -> Self {
269 Content::PqxdhEncrypted(content)
270 }
271
272 pub fn pqxdh_initial(message: crate::inbox::pqxdh::PqxdhInitialMessage) -> Self {
274 Content::PqxdhEncrypted(PqxdhEncryptedContent::Initial(message))
275 }
276
277 pub fn pqxdh_session(message: crate::inbox::pqxdh::PqxdhSessionMessage) -> Self {
279 Content::PqxdhEncrypted(PqxdhEncryptedContent::Session(message))
280 }
281
282 pub fn as_raw(&self) -> Option<&Vec<u8>> {
284 let Content::Raw(ref content) = self else {
285 return None;
286 };
287 Some(content)
288 }
289
290 pub fn as_encrypted(&self) -> Option<&ChaCha20Poly1305Content> {
292 let Content::ChaCha20Poly1305(ref content) = self else {
293 return None;
294 };
295 Some(content)
296 }
297
298 pub fn as_ed25519_self_encrypted(&self) -> Option<&Ed25519SelfEncryptedContent> {
300 let Content::Ed25519SelfEncrypted(ref content) = self else {
301 return None;
302 };
303 Some(content)
304 }
305
306 pub fn as_ml_dsa_self_encrypted(&self) -> Option<&MlDsaSelfEncryptedContent> {
308 let Content::MlDsaSelfEncrypted(ref content) = self else {
309 return None;
310 };
311 Some(content)
312 }
313
314 pub fn as_ephemeral_ecdh(&self) -> Option<&EphemeralEcdhContent> {
316 let Content::EphemeralEcdh(ref content) = self else {
317 return None;
318 };
319 Some(content)
320 }
321
322 pub fn as_pqxdh_encrypted(&self) -> Option<&PqxdhEncryptedContent> {
324 let Content::PqxdhEncrypted(ref content) = self else {
325 return None;
326 };
327 Some(content)
328 }
329
330 pub fn is_encrypted(&self) -> bool {
332 matches!(
333 self,
334 Content::ChaCha20Poly1305(_)
335 | Content::Ed25519SelfEncrypted(_)
336 | Content::MlDsaSelfEncrypted(_)
337 | Content::EphemeralEcdh(_)
338 | Content::PqxdhEncrypted(_)
339 )
340 }
341
342 pub fn is_raw(&self) -> bool {
344 matches!(self, Content::Raw(_))
345 }
346}
347
348#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
426pub enum Message {
427 MessageV0(MessageV0),
438}
439
440impl Message {
441 pub fn verify_sender_signature(
442 &self,
443 message_bytes: &[u8],
444 signature: &Signature,
445 ) -> Result<(), VerifyError> {
446 match self {
447 Message::MessageV0(ref inner) => inner.header.sender.verify(message_bytes, signature),
448 }
449 }
450
451 pub fn new_v0(
452 content: Content,
453 sender: VerifyingKey,
454 when: u64,
455 kind: Kind,
456 tags: Vec<Tag>,
457 ) -> Self {
458 Message::MessageV0(MessageV0 {
459 header: MessageV0Header {
460 sender,
461 when,
462 kind,
463 tags,
464 },
465 content,
466 })
467 }
468
469 pub fn new_v0_raw(
470 content: Vec<u8>,
471 sender: VerifyingKey,
472 when: u64,
473 kind: Kind,
474 tags: Vec<Tag>,
475 ) -> Self {
476 Message::MessageV0(MessageV0 {
477 header: MessageV0Header {
478 sender,
479 when,
480 kind,
481 tags,
482 },
483 content: Content::Raw(content),
484 })
485 }
486
487 pub fn new_v0_encrypted(
488 content: ChaCha20Poly1305Content,
489 sender: VerifyingKey,
490 when: u64,
491 kind: Kind,
492 tags: Vec<Tag>,
493 ) -> Self {
494 Message::MessageV0(MessageV0 {
495 header: MessageV0Header {
496 sender,
497 when,
498 kind,
499 tags,
500 },
501 content: Content::ChaCha20Poly1305(content),
502 })
503 }
504}
505
506#[derive(Serialize, Deserialize, Debug, Clone)]
527pub struct MessageV0Header {
528 pub sender: VerifyingKey,
534
535 pub when: u64,
542
543 pub kind: Kind,
551
552 #[serde(default)]
562 pub tags: Vec<Tag>,
563}
564
565#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
624pub struct MessageV0 {
625 pub header: MessageV0Header,
630
631 pub content: Content,
638}
639
640impl std::ops::Deref for MessageV0 {
641 type Target = MessageV0Header;
642
643 fn deref(&self) -> &Self::Target {
644 &self.header
645 }
646}
647
648#[derive(Debug, Clone, Serialize, Deserialize)]
740#[serde(try_from = "MessageFullWire")]
741pub struct MessageFull {
742 #[serde(skip_serializing)]
760 id: MessageId, signature: Signature,
770
771 message: Box<Message>,
776}
777
778#[derive(Debug, Clone, Serialize, Deserialize)]
781pub struct MessageFullWire {
782 signature: Signature,
783 message: Box<Message>,
784}
785
786impl From<MessageFull> for MessageFullWire {
787 fn from(val: MessageFull) -> Self {
788 MessageFullWire {
789 signature: val.signature,
790 message: val.message,
791 }
792 }
793}
794
795#[derive(Debug, thiserror::Error)]
796pub enum MessageFullError {
797 #[error("Signature does not match sender message")]
798 VerificationFailed(#[from] VerifyError),
799 #[error("Postcard Serialization error")]
800 SerializationError(#[from] postcard::Error),
801 #[error("Invalid content")]
802 InvalidContent,
803}
804
805impl TryFrom<MessageFullWire> for MessageFull {
806 type Error = MessageFullError;
807 fn try_from(value: MessageFullWire) -> Result<Self, Self::Error> {
808 let message_bytes = postcard::to_stdvec(&value.message)?;
809 Self::with_signature(value.signature, value.message, &message_bytes)
810 }
811}
812
813impl MessageFull {
814 pub fn new(message: Message, signer: &KeyPair) -> Result<Self, MessageFullError> {
816 let message_bytes = postcard::to_stdvec(&message)?;
817 let signature = signer.sign(&message_bytes);
818 Self::with_signature(signature, Box::new(message), &message_bytes)
819 }
820
821 fn with_signature(
822 signature: Signature,
823 message: Box<Message>,
824 message_bytes: &[u8],
825 ) -> Result<Self, MessageFullError> {
826 message.verify_sender_signature(message_bytes, &signature)?;
827 let mut hasher = Hasher::new();
828 hasher.update(message_bytes);
829 Ok(Self {
830 id: MessageId::new(hasher.finalize()),
831 message,
832 signature,
833 })
834 }
835
836 pub fn id(&self) -> &MessageId {
837 &self.id
838 }
839
840 pub fn message(&self) -> &Message {
841 &self.message
842 }
843
844 pub fn signature(&self) -> &Signature {
845 &self.signature
846 }
847
848 pub fn ref_tag(&self) -> Tag {
849 Tag::Event {
850 id: *self.id(),
851 relays: vec![],
852 }
853 }
854
855 pub fn storage_value(&self) -> Result<Vec<u8>, MessageFullError> {
857 Ok(postcard::to_stdvec(&self)?)
858 }
859
860 pub fn storage_timeout(&self) -> Option<u64> {
862 match &*self.message {
863 Message::MessageV0(msg) => match msg.header.kind {
864 Kind::Ephemeral(timeout) if timeout > 0 => Some(timeout as u64),
865 _ => None,
866 },
867 }
868 }
869
870 pub fn store_key(&self) -> Option<StoreKey> {
871 match &*self.message {
872 Message::MessageV0(msg) => match &msg.header.kind {
873 Kind::Store(key) => Some(key.clone()),
874 _ => None,
875 },
876 }
877 }
878
879 pub fn clear_key(&self) -> Option<StoreKey> {
881 None
882 }
883
884 pub fn from_storage_value(value: &[u8]) -> Result<Self, MessageFullError> {
886 let message: Self = postcard::from_bytes(value)?;
887 Ok(message)
888 }
889
890 pub fn author(&self) -> &VerifyingKey {
891 match &*self.message {
892 Message::MessageV0(message) => &message.header.sender,
893 }
894 }
895
896 pub fn when(&self) -> &u64 {
897 match &*self.message {
898 Message::MessageV0(message) => &message.header.when,
899 }
900 }
901
902 pub fn kind(&self) -> &Kind {
903 match &*self.message {
904 Message::MessageV0(message) => &message.header.kind,
905 }
906 }
907
908 pub fn tags(&self) -> &Vec<Tag> {
909 match &*self.message {
910 Message::MessageV0(message) => &message.header.tags,
911 }
912 }
913
914 pub fn content(&self) -> &Content {
915 match &*self.message {
916 Message::MessageV0(message) => &message.content,
917 }
918 }
919
920 pub fn raw_content(&self) -> Option<&Vec<u8>> {
922 self.content().as_raw()
923 }
924
925 pub fn encrypted_content(&self) -> Option<&ChaCha20Poly1305Content> {
927 self.content().as_encrypted()
928 }
929
930 pub fn is_expired(&self, current_time: u64) -> bool {
932 if let Some(timeout) = self.storage_timeout() {
933 let expiration_time = self.when().saturating_add(timeout);
934 return expiration_time < current_time;
935 }
936 false
937 }
938}
939
940impl MessageFull {
941 pub fn try_deserialize_content<C>(&self) -> Result<C, MessageFullError>
943 where
944 C: for<'a> Deserialize<'a>,
945 {
946 let Message::MessageV0(MessageV0 {
947 content: Content::Raw(bytes),
948 ..
949 }) = &*self.message
950 else {
951 return Err(MessageFullError::InvalidContent);
952 };
953 Ok(postcard::from_bytes(bytes)?)
954 }
955}
956
957impl PartialEq for MessageV0Header {
959 fn eq(&self, other: &Self) -> bool {
960 let self_encoded = self.sender.encode();
962 let other_encoded = other.sender.encode();
963
964 self_encoded == other_encoded
965 && self.when == other.when
966 && self.kind == other.kind
967 && self.tags == other.tags
968 }
969}
970
971impl Eq for MessageV0Header {}
973
974impl PartialEq for MessageFull {
976 fn eq(&self, other: &Self) -> bool {
977 let self_sig_encoded = self.signature.encode();
979 let other_sig_encoded = other.signature.encode();
980
981 self.id == other.id
982 && self.message == other.message
983 && self_sig_encoded == other_sig_encoded
984 }
985}
986
987impl Eq for MessageFull {}
989
990#[cfg(test)]
991mod tests {
992 use super::*;
993 use crate::{
994 keys::{KeyPair, VerifyingKey},
995 Hash, KeyId,
996 };
997 use rand::rngs::OsRng;
998 fn make_hash() -> Hash {
1001 let mut hasher = Hasher::new();
1002 hasher.update(b"1234567890abcdef");
1003 hasher.finalize()
1004 }
1005
1006 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1007 struct DummyContent {
1008 value: u32,
1009 }
1010
1011 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1012 struct ComplexContent {
1013 text: String,
1014 numbers: Vec<i32>,
1015 flag: bool,
1016 }
1017
1018 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1019 struct TestContent {
1020 text: String,
1021 timestamp: u64,
1022 value: u32,
1023 }
1024
1025 fn make_keys() -> (KeyPair, VerifyingKey) {
1026 let mut csprng = OsRng;
1027 let keypair = KeyPair::generate(&mut csprng);
1028 let public_key = keypair.public_key();
1029 (keypair, public_key)
1030 }
1031
1032 #[test]
1033 fn test_message_sign_and_verify() {
1034 let (sk, pk) = make_keys();
1035 let content = DummyContent { value: 42 };
1036 let core = Message::new_v0(
1037 Content::Raw(postcard::to_stdvec(&content).unwrap()),
1038 pk,
1039 1714857600,
1040 Kind::Regular,
1041 vec![Tag::Protected],
1042 );
1043 let msg_full = MessageFull::new(core.clone(), &sk).unwrap();
1044 let mut tampered: MessageFullWire = msg_full.clone().into();
1045 match &mut *tampered.message {
1047 Message::MessageV0(ref mut msg_v0) => {
1048 msg_v0.header.when += 1; }
1050 }
1051 assert!(MessageFull::try_from(tampered).is_err());
1052 }
1053
1054 #[test]
1055 fn test_signature_fails_with_wrong_key() {
1056 let (sk1, pk1) = make_keys();
1057 let (sk2, _pk2) = make_keys();
1058 let content = DummyContent { value: 7 };
1059 let core = Message::new_v0(
1060 Content::Raw(postcard::to_stdvec(&content).unwrap()),
1061 pk1,
1062 1714857600,
1063 Kind::Regular,
1064 vec![Tag::Protected],
1065 );
1066 let msg_full = MessageFull::new(core, &sk1).unwrap();
1067 let mut tampered: MessageFullWire = msg_full.clone().into();
1068 let message_bytes = postcard::to_stdvec(&tampered.message).unwrap();
1070 let fake_sig = sk2.sign(&message_bytes);
1071 tampered.signature = fake_sig;
1072 assert!(MessageFull::try_from(tampered).is_err());
1073 }
1074
1075 #[test]
1076 fn test_empty_content() {
1077 let (sk, pk) = make_keys();
1078 let core = Message::new_v0(
1079 Content::Raw(postcard::to_stdvec(&DummyContent { value: 0 }).unwrap()),
1080 pk,
1081 1714857600,
1082 Kind::Regular,
1083 vec![Tag::Protected],
1084 );
1085 let _msg_full = MessageFull::new(core, &sk).unwrap();
1086 }
1088
1089 #[test]
1090 fn test_multiple_content_items() {
1091 let (sk, pk) = make_keys();
1092 let contents = [
1093 DummyContent { value: 1 },
1094 DummyContent { value: 2 },
1095 DummyContent { value: 3 },
1096 ];
1097 let core = Message::new_v0(
1098 Content::Raw(postcard::to_stdvec(&contents[0]).unwrap()),
1099 pk,
1100 1714857600,
1101 Kind::Regular,
1102 vec![
1103 Tag::Protected,
1104 Tag::Event {
1105 id: MessageId::new(make_hash()),
1106 relays: vec!["relay1".to_string()],
1107 },
1108 ],
1109 );
1110
1111 let _msg_full = MessageFull::new(core, &sk).unwrap();
1112 }
1114
1115 #[test]
1116 fn test_complex_content_serialization() {
1117 let (sk, pk) = make_keys();
1118 let complex_content = ComplexContent {
1119 text: "Hello, World!".to_string(),
1120 numbers: vec![1, 2, 3, 4, 5],
1121 flag: true,
1122 };
1123 let core = Message::new_v0(
1124 Content::Raw(postcard::to_stdvec(&complex_content).unwrap()),
1125 pk,
1126 1714857600,
1127 Kind::Regular,
1128 vec![Tag::User {
1129 id: KeyId::from_bytes([1u8; 32]),
1130 relays: vec!["relay1".to_string()],
1131 }],
1132 );
1133 let msg_full = MessageFull::new(core, &sk).unwrap();
1134 let serialized = msg_full.storage_value().unwrap();
1137 let deserialized = MessageFull::from_storage_value(&serialized).unwrap();
1138
1139 let de_content: ComplexContent = deserialized.try_deserialize_content().unwrap();
1140
1141 assert_eq!(msg_full, deserialized);
1142 assert_eq!(complex_content, de_content);
1143 }
1145
1146 #[test]
1147 #[ignore = "this won't work, and that's the proof"]
1148 fn test_complex_content_serialization_is_not_vec_u8() {
1149 let (sk, pk) = make_keys();
1150 let complex_content = ComplexContent {
1151 text: "Hello, World!".to_string(),
1152 numbers: vec![1, 2, 3, 4, 5],
1153 flag: true,
1154 };
1155 let core = Message::new_v0(
1156 Content::Raw(postcard::to_stdvec(&complex_content).unwrap()),
1157 pk,
1158 1714857600,
1159 Kind::Regular,
1160 vec![Tag::User {
1161 id: KeyId::from_bytes([1u8; 32]),
1162 relays: vec!["relay1".to_string()],
1163 }],
1164 );
1165
1166 let msg_full = MessageFull::new(core, &sk).unwrap();
1167 let serialized = msg_full.storage_value().unwrap();
1169 let deserialized = MessageFull::from_storage_value(&serialized).unwrap();
1170 let deserialize_u8 = MessageFull::from_storage_value(&serialized).unwrap();
1173 let de_content: ComplexContent = deserialize_u8.try_deserialize_content().unwrap();
1174
1175 assert_eq!(msg_full, deserialized);
1176 assert_eq!(complex_content, de_content);
1177 }
1178
1179 #[test]
1180 fn test_complex_content_no_tags_serialization() {
1181 let (sk, pk) = make_keys();
1182 let complex_content = ComplexContent {
1183 text: "Hello, World!".to_string(),
1184 numbers: vec![1, 2, 3, 4, 5],
1185 flag: true,
1186 };
1187 let core = Message::new_v0(
1188 Content::Raw(postcard::to_stdvec(&complex_content).unwrap()),
1189 pk,
1190 1714857600,
1191 Kind::Regular,
1192 vec![],
1193 );
1194
1195 let msg_full = MessageFull::new(core, &sk).unwrap();
1196 let serialized = msg_full.storage_value().unwrap();
1199 let deserialized = MessageFull::from_storage_value(&serialized).unwrap();
1200
1201 let de_content: ComplexContent = deserialized.try_deserialize_content().unwrap();
1202
1203 assert_eq!(msg_full, deserialized);
1204 assert_eq!(complex_content, de_content);
1205 }
1207
1208 #[test]
1209 fn test_all_tag_types_serialization() {
1210 let (sk, pk) = make_keys();
1211 let content = DummyContent { value: 100 };
1212
1213 let tags = [
1214 Tag::Protected,
1215 Tag::Event {
1216 id: MessageId::new(make_hash()),
1217 relays: vec!["relay1".to_string()],
1218 },
1219 Tag::User {
1220 id: KeyId::from_bytes([2u8; 32]),
1221 relays: vec!["relay2".to_string()],
1222 },
1223 Tag::Channel {
1224 id: vec![3],
1225 relays: vec!["relay3".to_string()],
1226 },
1227 ];
1228
1229 for tag in tags {
1230 let core = Message::new_v0(
1231 Content::Raw(postcard::to_stdvec(&content).unwrap()),
1232 pk.clone(),
1233 1714857600,
1234 Kind::Regular,
1235 vec![tag.clone()],
1236 );
1237
1238 let msg_full = MessageFull::new(core, &sk).unwrap();
1239 let serialized = msg_full.storage_value().unwrap();
1242 let deserialized = MessageFull::from_storage_value(&serialized).unwrap();
1243 let de_content: DummyContent = deserialized.try_deserialize_content().unwrap();
1244
1245 assert_eq!(msg_full, deserialized);
1246 assert_eq!(content, de_content);
1247 }
1249 }
1250 #[test]
1251 fn test_complex_content_empheral_kind_serialization() {
1252 let (sk, pk) = make_keys();
1253 let complex_content = ComplexContent {
1254 text: "Hello, World!".to_string(),
1255 numbers: vec![1, 2, 3, 4, 5],
1256 flag: true,
1257 };
1258 let core = Message::new_v0(
1259 Content::Raw(postcard::to_stdvec(&complex_content).unwrap()),
1260 pk,
1261 1714857600,
1262 Kind::Ephemeral(10),
1263 vec![],
1264 );
1265
1266 let msg_full = MessageFull::new(core, &sk).unwrap();
1267 let serialized = msg_full.storage_value().unwrap();
1270 let deserialized = MessageFull::from_storage_value(&serialized).unwrap();
1271 let de_content: ComplexContent = deserialized.try_deserialize_content().unwrap();
1272
1273 assert_eq!(msg_full, deserialized);
1274 assert_eq!(complex_content, de_content);
1275
1276 assert_eq!(msg_full, deserialized);
1277 }
1279
1280 #[test]
1281 fn test_complex_content_clear_store_kind_serialization() {
1282 let (signing_key, verifying_key) = make_keys();
1284
1285 for i in 0..10 {
1286 let content = TestContent {
1287 text: format!("Test message {}", i + 1),
1288 timestamp: std::time::SystemTime::now()
1289 .duration_since(std::time::UNIX_EPOCH)
1290 .unwrap()
1291 .as_secs(),
1292 value: i as u32,
1293 };
1294
1295 let mut tags = Vec::new();
1296 let mut event_id_bytes = [0u8; 32];
1298 event_id_bytes[0] = i as u8;
1299 let event_id = blake3::Hash::from(event_id_bytes);
1300 tags.push(Tag::Event {
1301 id: MessageId::new(event_id),
1302 relays: Vec::new(),
1303 });
1304
1305 let message = Message::new_v0(
1306 Content::Raw(postcard::to_stdvec(&content).unwrap()),
1307 verifying_key.clone(),
1308 std::time::SystemTime::now()
1309 .duration_since(std::time::UNIX_EPOCH)
1310 .unwrap()
1311 .as_secs(),
1312 Kind::Regular,
1313 tags,
1314 );
1315
1316 let msg_full = MessageFull::new(message, &signing_key).unwrap();
1317 let serialized = msg_full.storage_value().unwrap();
1320 let deserialized = MessageFull::from_storage_value(&serialized).unwrap();
1321 let de_content: TestContent = deserialized.try_deserialize_content().unwrap();
1322
1323 assert_eq!(msg_full, deserialized);
1324 assert_eq!(content, de_content);
1325 }
1327 }
1328
1329 #[test]
1330 fn test_complex_content_store_kind_serialization() {
1331 let (sk, pk) = make_keys();
1332 let complex_content = ComplexContent {
1333 text: "Hello, World!".to_string(),
1334 numbers: vec![1, 2, 3, 4, 5],
1335 flag: true,
1336 };
1337 let core = Message::new_v0(
1338 Content::Raw(postcard::to_stdvec(&complex_content).unwrap()),
1339 pk,
1340 1714857600,
1341 Kind::Store(StoreKey::CustomKey(10)),
1342 vec![],
1343 );
1344
1345 let msg_full = MessageFull::new(core, &sk).unwrap();
1346 let serialized = msg_full.storage_value().unwrap();
1349 let deserialized = MessageFull::from_storage_value(&serialized).unwrap();
1350 let de_content: ComplexContent = deserialized.try_deserialize_content().unwrap();
1351
1352 assert_eq!(msg_full, deserialized);
1353 assert_eq!(complex_content, de_content);
1354 }
1356
1357 #[test]
1358 fn test_wire_format_roundtrip() {
1359 let (sk, pk) = make_keys();
1360 let content = DummyContent { value: 42 };
1361 let core = Message::new_v0(
1362 Content::Raw(postcard::to_stdvec(&content).unwrap()),
1363 pk,
1364 1714857600,
1365 Kind::Regular,
1366 vec![Tag::Protected],
1367 );
1368 let msg_full = MessageFull::new(core, &sk).unwrap();
1369 let original_id = *msg_full.id();
1370
1371 let wire: MessageFullWire = msg_full.clone().into();
1373 let reconstructed = MessageFull::try_from(wire).unwrap();
1374
1375 assert_eq!(msg_full, reconstructed);
1377 assert_eq!(original_id, *reconstructed.id());
1378 }
1379
1380 #[test]
1381 fn test_invalid_signature_wire() {
1382 let (sk, pk) = make_keys();
1383 let (wrong_sk, _) = make_keys();
1384 let content = DummyContent { value: 42 };
1385 let core = Message::new_v0(
1386 Content::Raw(postcard::to_stdvec(&content).unwrap()),
1387 pk,
1388 1714857600,
1389 Kind::Regular,
1390 vec![Tag::Protected],
1391 );
1392 let msg_full = MessageFull::new(core.clone(), &sk).unwrap();
1393 let mut tampered_wire: MessageFullWire = msg_full.into();
1394
1395 let message_bytes = postcard::to_stdvec(&tampered_wire.message).unwrap();
1397 let wrong_sig = wrong_sk.sign(&message_bytes);
1398 tampered_wire.signature = wrong_sig;
1399
1400 assert!(MessageFull::try_from(tampered_wire).is_err());
1402 }
1403
1404 #[test]
1405 fn test_message_tampering_wire() {
1406 let (sk, pk) = make_keys();
1407 let content = DummyContent { value: 42 };
1408 let core = Message::new_v0(
1409 Content::Raw(postcard::to_stdvec(&content).unwrap()),
1410 pk,
1411 1714857600,
1412 Kind::Regular,
1413 vec![Tag::Protected],
1414 );
1415 let msg_full = MessageFull::new(core.clone(), &sk).unwrap();
1416 let mut tampered_wire: MessageFullWire = msg_full.into();
1417
1418 let (_, different_pk) = make_keys();
1420 match &mut *tampered_wire.message {
1421 Message::MessageV0(ref mut msg_v0) => {
1422 msg_v0.header.sender = different_pk;
1423 }
1424 }
1425
1426 assert!(MessageFull::try_from(tampered_wire).is_err());
1428 }
1429
1430 #[test]
1431 fn test_signature_tampering_wire() {
1432 let (sk, pk) = make_keys();
1433 let content = DummyContent { value: 42 };
1434 let core = Message::new_v0(
1435 Content::Raw(postcard::to_stdvec(&content).unwrap()),
1436 pk,
1437 1714857600,
1438 Kind::Regular,
1439 vec![Tag::Protected],
1440 );
1441 let msg_full = MessageFull::new(core.clone(), &sk).unwrap();
1442 let mut tampered_wire: MessageFullWire = msg_full.into();
1443
1444 let (wrong_sk, _) = make_keys();
1446 let message_bytes = postcard::to_stdvec(&tampered_wire.message).unwrap();
1447 let wrong_signature = wrong_sk.sign(&message_bytes);
1448 tampered_wire.signature = wrong_signature;
1449
1450 assert!(MessageFull::try_from(tampered_wire).is_err());
1452 }
1453
1454 #[test]
1455 fn test_serialization_roundtrip() {
1456 let (sk, pk) = make_keys();
1457 let content = ComplexContent {
1458 text: "Hello, World!".to_string(),
1459 numbers: vec![1, 2, 3, 4, 5],
1460 flag: true,
1461 };
1462 let core = Message::new_v0(
1463 Content::Raw(postcard::to_stdvec(&content).unwrap()),
1464 pk,
1465 1714857600,
1466 Kind::Regular,
1467 vec![
1468 Tag::Protected,
1469 Tag::Event {
1470 id: MessageId::new(make_hash()),
1471 relays: vec!["relay1".to_string()],
1472 },
1473 ],
1474 );
1475
1476 let msg_full = MessageFull::new(core, &sk).unwrap();
1477
1478 let serialized = msg_full.storage_value().unwrap();
1480 let deserialized = MessageFull::from_storage_value(&serialized).unwrap();
1481 let de_content: ComplexContent = deserialized.try_deserialize_content().unwrap();
1482
1483 assert_eq!(msg_full, deserialized);
1484 assert_eq!(content, de_content);
1485 }
1487
1488 #[test]
1489 fn test_multiple_tags() {
1490 let (sk, pk) = make_keys();
1491 let content = DummyContent { value: 42 };
1492 let core = Message::new_v0(
1493 Content::Raw(postcard::to_stdvec(&content).unwrap()),
1494 pk,
1495 1714857600,
1496 Kind::Regular,
1497 vec![
1498 Tag::Protected,
1499 Tag::Event {
1500 id: MessageId::new(make_hash()),
1501 relays: vec!["relay1".to_string()],
1502 },
1503 Tag::User {
1504 id: KeyId::from_bytes([2u8; 32]),
1505 relays: vec!["relay2".to_string()],
1506 },
1507 ],
1508 );
1509 let _msg_full = MessageFull::new(core, &sk).unwrap();
1510 }
1512
1513 #[test]
1514 fn test_large_content() {
1515 let (sk, pk) = make_keys();
1516 let large_content = ComplexContent {
1517 text: "A".repeat(1000), numbers: (0..1000).collect(), flag: false,
1520 };
1521 let core = Message::new_v0(
1522 Content::Raw(postcard::to_stdvec(&large_content).unwrap()),
1523 pk,
1524 1714857600,
1525 Kind::Regular,
1526 vec![Tag::Channel {
1527 id: vec![1],
1528 relays: vec!["relay1".to_string()],
1529 }],
1530 );
1531 let _msg_full = MessageFull::new(core, &sk).unwrap();
1532 }
1534
1535 #[test]
1536 fn test_id_uniqueness() {
1537 let (sk, pk) = make_keys();
1538 let content1 = DummyContent { value: 1 };
1539 let content2 = DummyContent { value: 2 };
1540
1541 let core1 = Message::new_v0(
1542 Content::Raw(postcard::to_stdvec(&content1).unwrap()),
1543 pk.clone(),
1544 1714857600,
1545 Kind::Regular,
1546 vec![Tag::Protected],
1547 );
1548 let core2 = Message::new_v0(
1549 Content::Raw(postcard::to_stdvec(&content2).unwrap()),
1550 pk.clone(),
1551 1714857600,
1552 Kind::Regular,
1553 vec![Tag::Protected],
1554 );
1555
1556 let msg_full1 = MessageFull::new(core1, &sk).unwrap();
1557 let msg_full2 = MessageFull::new(core2, &sk).unwrap();
1558
1559 assert_ne!(msg_full1.id, msg_full2.id);
1561 }
1562
1563 #[test]
1564 fn test_same_content_different_signatures_but_same_id() {
1565 let (sk, pk) = make_keys();
1566 let content = DummyContent { value: 42 };
1567
1568 let core1 = Message::new_v0(
1569 Content::Raw(postcard::to_stdvec(&content).unwrap()),
1570 pk.clone(),
1571 1714857600,
1572 Kind::Regular,
1573 vec![Tag::Protected],
1574 );
1575 let core2 = Message::new_v0(
1576 Content::Raw(postcard::to_stdvec(&content).unwrap()),
1577 pk.clone(),
1578 1714857600,
1579 Kind::Regular,
1580 vec![Tag::Protected],
1581 );
1582
1583 let msg_full1 = MessageFull::new(core1, &sk).unwrap();
1584 let msg_full2 = MessageFull::new(core2, &sk).unwrap();
1585
1586 assert_ne!(msg_full1.signature, msg_full2.signature);
1588
1589 assert_eq!(msg_full1.id, msg_full2.id);
1591 assert_eq!(msg_full1.message, msg_full2.message);
1592 }
1593
1594 #[test]
1595 fn test_pqxdh_encrypted_content() {
1596 use crate::{
1597 inbox::pqxdh::{PqxdhInitialMessage, PqxdhSessionMessage},
1598 PqxdhEncryptedContent,
1599 };
1600
1601 let pqxdh_initial = PqxdhInitialMessage {
1603 initiator_identity: VerifyingKey::Ed25519(Box::new(
1604 ed25519_dalek::VerifyingKey::from_bytes(&[0u8; 32]).unwrap(),
1605 )),
1606 ephemeral_key: x25519_dalek::PublicKey::from([1u8; 32]),
1607 kem_ciphertext: vec![2u8; 100],
1608 signed_prekey_id: "spk_001".to_string(),
1609 one_time_prekey_id: Some("otk_001".to_string()),
1610 pq_signed_prekey_id: "pqspk_001".to_string(),
1611 pq_one_time_key_id: Some("pqotk_001".to_string()),
1612 encrypted_payload: vec![3u8; 50],
1613 };
1614
1615 let pqxdh_session = PqxdhSessionMessage {
1617 sequence_number: 42,
1618 encrypted_payload: vec![5u8; 30],
1619 auth_tag: [6u8; 16],
1620 };
1621
1622 let initial_content = Content::pqxdh_initial(pqxdh_initial.clone());
1624
1625 let session_content = Content::pqxdh_session(pqxdh_session.clone());
1627
1628 let retrieved_initial = initial_content.as_pqxdh_encrypted().unwrap();
1630 if let PqxdhEncryptedContent::Initial(msg) = retrieved_initial {
1631 assert_eq!(*msg, pqxdh_initial);
1632 } else {
1633 panic!("Expected Initial variant");
1634 }
1635
1636 let retrieved_session = session_content.as_pqxdh_encrypted().unwrap();
1637 if let PqxdhEncryptedContent::Session(msg) = retrieved_session {
1638 assert_eq!(*msg, pqxdh_session);
1639 } else {
1640 panic!("Expected Session variant");
1641 }
1642
1643 assert!(initial_content.is_encrypted());
1645 assert!(session_content.is_encrypted());
1646 assert!(!initial_content.is_raw());
1647 assert!(!session_content.is_raw());
1648
1649 let initial_serialized = postcard::to_stdvec(&initial_content).unwrap();
1651 let initial_deserialized: Content = postcard::from_bytes(&initial_serialized).unwrap();
1652 assert_eq!(initial_content, initial_deserialized);
1653
1654 let session_serialized = postcard::to_stdvec(&session_content).unwrap();
1655 let session_deserialized: Content = postcard::from_bytes(&session_serialized).unwrap();
1656 assert_eq!(session_content, session_deserialized);
1657 }
1658
1659 #[test]
1660 fn test_content_type_discriminants() {
1661 let raw_content = Content::raw(vec![1, 2, 3]);
1663 let pqxdh_content = Content::pqxdh_initial(crate::inbox::pqxdh::PqxdhInitialMessage {
1664 initiator_identity: VerifyingKey::Ed25519(Box::new(
1665 ed25519_dalek::VerifyingKey::from_bytes(&[0u8; 32]).unwrap(),
1666 )),
1667 ephemeral_key: x25519_dalek::PublicKey::from([0u8; 32]),
1668 kem_ciphertext: vec![],
1669 signed_prekey_id: "test".to_string(),
1670 one_time_prekey_id: None,
1671 pq_signed_prekey_id: "test".to_string(),
1672 pq_one_time_key_id: None,
1673 encrypted_payload: vec![],
1674 });
1675
1676 let raw_serialized = postcard::to_stdvec(&raw_content).unwrap();
1678 let pqxdh_serialized = postcard::to_stdvec(&pqxdh_content).unwrap();
1679
1680 assert_ne!(raw_serialized[0], pqxdh_serialized[0]);
1682 assert_eq!(raw_serialized[0], 0); assert_eq!(pqxdh_serialized[0], 42); }
1685}
1686
1687#[cfg(test)]
1688mod size_tests {
1689 use super::*;
1690 use crate::keys::KeyPair;
1691 use rand::rngs::OsRng;
1692 fn create_keypairs_by_type() -> Vec<(String, KeyPair)> {
1694 let mut rng = OsRng;
1695 vec![
1696 ("Ed25519".to_string(), KeyPair::generate_ed25519(&mut rng)),
1697 ("MlDsa44".to_string(), KeyPair::generate_ml_dsa44(&mut rng)),
1698 ("MlDsa65".to_string(), KeyPair::generate_ml_dsa65(&mut rng)),
1699 ("MlDsa87".to_string(), KeyPair::generate_ml_dsa87(&mut rng)),
1700 ]
1701 }
1702
1703 fn create_minimal_message(keypair: &KeyPair) -> MessageFull {
1705 let public_key = keypair.public_key();
1706
1707 let message = MessageV0 {
1709 header: MessageV0Header {
1710 sender: public_key,
1711 when: 1640995200, kind: Kind::Regular,
1713 tags: vec![], },
1715 content: Content::Raw(vec![]), };
1717
1718 MessageFull::new(Message::MessageV0(message), keypair).unwrap()
1719 }
1720
1721 #[test]
1722 fn test_minimum_message_sizes_by_key_variant() {
1723 let keypairs = create_keypairs_by_type();
1724
1725 for (key_type, keypair) in keypairs {
1726 let msg_full = create_minimal_message(&keypair);
1727 let serialized = postcard::to_stdvec(&MessageFullWire::from(msg_full)).unwrap();
1728 let size = serialized.len();
1729
1730 println!("Minimum serialized size for {key_type}: {size} bytes");
1731
1732 match key_type.as_str() {
1734 "Ed25519" => {
1735 assert!(
1737 (100..=150).contains(&size),
1738 "Ed25519 message size {size} should be ~120 bytes (100-150 range)"
1739 );
1740 }
1741 "MlDsa44" => {
1742 assert!(
1744 (3700..=4000).contains(&size),
1745 "ML-DSA-44 message size {size} should be ~3,832 bytes (3700-4000 range)"
1746 );
1747 }
1748 "MlDsa65" => {
1749 assert!(
1751 (5200..=5600).contains(&size),
1752 "ML-DSA-65 message size {size} should be ~5,362 bytes (5200-5600 range)"
1753 );
1754 }
1755 "MlDsa87" => {
1756 assert!(
1758 (7100..=7600).contains(&size),
1759 "ML-DSA-87 message size {size} should be ~7,322 bytes (7100-7600 range)"
1760 );
1761 }
1762 _ => panic!("Unknown key type: {key_type}"),
1763 }
1764 }
1765 }
1766
1767 #[test]
1768 fn test_ed25519_minimum_size_precise() {
1769 let mut rng = OsRng;
1770 let keypair = KeyPair::generate_ed25519(&mut rng);
1771 let msg_full = create_minimal_message(&keypair);
1772 let serialized = postcard::to_stdvec(&MessageFullWire::from(msg_full)).unwrap();
1773 let size = serialized.len();
1774
1775 assert!(
1777 (100..=150).contains(&size),
1778 "Ed25519 minimum message size {size} should be approximately 120 bytes"
1779 );
1780
1781 println!("Ed25519 precise minimum size: {size} bytes");
1782 }
1783
1784 #[test]
1785 fn test_mldsa44_minimum_size_precise() {
1786 let mut rng = OsRng;
1787 let keypair = KeyPair::generate_ml_dsa44(&mut rng);
1788 let msg_full = create_minimal_message(&keypair);
1789 let serialized = postcard::to_stdvec(&MessageFullWire::from(msg_full)).unwrap();
1790 let size = serialized.len();
1791
1792 assert!(
1794 (3700..=4000).contains(&size),
1795 "ML-DSA-44 minimum message size {size} should be approximately 3,832 bytes"
1796 );
1797
1798 println!("ML-DSA-44 precise minimum size: {size} bytes");
1799 }
1800
1801 #[test]
1802 fn test_mldsa65_minimum_size_precise() {
1803 let mut rng = OsRng;
1804 let keypair = KeyPair::generate_ml_dsa65(&mut rng);
1805 let msg_full = create_minimal_message(&keypair);
1806 let serialized = postcard::to_stdvec(&MessageFullWire::from(msg_full)).unwrap();
1807 let size = serialized.len();
1808
1809 assert!(
1811 (5200..=5600).contains(&size),
1812 "ML-DSA-65 minimum message size {size} should be approximately 5,362 bytes"
1813 );
1814
1815 println!("ML-DSA-65 precise minimum size: {size} bytes");
1816 }
1817
1818 #[test]
1819 fn test_mldsa87_minimum_size_precise() {
1820 let mut rng = OsRng;
1821 let keypair = KeyPair::generate_ml_dsa87(&mut rng);
1822 let msg_full = create_minimal_message(&keypair);
1823 let serialized = postcard::to_stdvec(&MessageFullWire::from(msg_full)).unwrap();
1824 let size = serialized.len();
1825
1826 assert!(
1828 (7100..=7600).contains(&size),
1829 "ML-DSA-87 minimum message size {size} should be approximately 7,322 bytes"
1830 );
1831
1832 println!("ML-DSA-87 precise minimum size: {size} bytes");
1833 }
1834
1835 #[test]
1836 fn test_size_comparison_ratios() {
1837 let keypairs = create_keypairs_by_type();
1838 let mut sizes = std::collections::BTreeMap::new();
1839
1840 for (key_type, keypair) in keypairs {
1841 let msg_full = create_minimal_message(&keypair);
1842 let serialized = postcard::to_stdvec(&MessageFullWire::from(msg_full)).unwrap();
1843 sizes.insert(key_type, serialized.len());
1844 }
1845
1846 let ed25519_size = sizes["Ed25519"] as f64;
1847
1848 let mldsa44_ratio = sizes["MlDsa44"] as f64 / ed25519_size;
1850 let mldsa65_ratio = sizes["MlDsa65"] as f64 / ed25519_size;
1851 let mldsa87_ratio = sizes["MlDsa87"] as f64 / ed25519_size;
1852
1853 println!("Size ratios relative to Ed25519:");
1854 println!(" Ed25519: {} bytes (1.0x)", sizes["Ed25519"]);
1855 println!(
1856 " ML-DSA-44: {} bytes ({:.1}x)",
1857 sizes["MlDsa44"], mldsa44_ratio
1858 );
1859 println!(
1860 " ML-DSA-65: {} bytes ({:.1}x)",
1861 sizes["MlDsa65"], mldsa65_ratio
1862 );
1863 println!(
1864 " ML-DSA-87: {} bytes ({:.1}x)",
1865 sizes["MlDsa87"], mldsa87_ratio
1866 );
1867
1868 assert!(
1870 (25.0..=40.0).contains(&mldsa44_ratio),
1871 "ML-DSA-44 should be 25-40x larger than Ed25519, got {mldsa44_ratio:.1}x"
1872 );
1873 assert!(
1874 (35.0..=55.0).contains(&mldsa65_ratio),
1875 "ML-DSA-65 should be 35-55x larger than Ed25519, got {mldsa65_ratio:.1}x"
1876 );
1877 assert!(
1878 (50.0..=75.0).contains(&mldsa87_ratio),
1879 "ML-DSA-87 should be 50-75x larger than Ed25519, got {mldsa87_ratio:.1}x"
1880 );
1881 }
1882
1883 #[test]
1884 fn test_component_sizes_breakdown() {
1885 let mut rng = OsRng;
1886
1887 let test_cases = vec![
1889 ("Ed25519", KeyPair::generate_ed25519(&mut rng)),
1890 ("MlDsa44", KeyPair::generate_ml_dsa44(&mut rng)),
1891 ("MlDsa65", KeyPair::generate_ml_dsa65(&mut rng)),
1892 ("MlDsa87", KeyPair::generate_ml_dsa87(&mut rng)),
1893 ];
1894
1895 for (key_type, keypair) in test_cases {
1896 let public_key = keypair.public_key();
1897
1898 let verifying_key_size = postcard::to_stdvec(&public_key).unwrap().len();
1900
1901 let test_data = b"test message";
1903 let signature = keypair.sign(test_data);
1904 let signature_size = postcard::to_stdvec(&signature).unwrap().len();
1905
1906 let msg_full = create_minimal_message(&keypair);
1908 let total_size = postcard::to_stdvec(&MessageFullWire::from(msg_full))
1909 .unwrap()
1910 .len();
1911
1912 println!("{key_type} component breakdown:");
1913 println!(" VerifyingKey: {verifying_key_size} bytes");
1914 println!(" Signature: {signature_size} bytes");
1915 println!(" Total message: {total_size} bytes");
1916 println!(
1917 " Overhead: {} bytes",
1918 total_size - verifying_key_size - signature_size
1919 );
1920 println!();
1921
1922 let key_and_sig_size = verifying_key_size + signature_size;
1924 let overhead = total_size - key_and_sig_size;
1925
1926 assert!(
1928 overhead < 100,
1929 "{key_type} overhead {overhead} bytes should be less than 100 bytes"
1930 );
1931
1932 let key_sig_ratio = key_and_sig_size as f64 / total_size as f64;
1934 assert!(
1935 key_sig_ratio >= 0.85,
1936 "{} key+signature should be at least 85% of total size, got {:.1}%",
1937 key_type,
1938 key_sig_ratio * 100.0
1939 );
1940 }
1941 }
1942}