use crate::crypto::{PrivateKey, PublicKey, SharedSecret}; use bytes::Bytes; use log::warn; use std::{ collections::{HashMap, HashSet}, rc::Rc, str::FromStr, }; use serde::{Deserialize, de}; #[derive(PartialEq, Clone, Deserialize, Debug)] /// The Identity structure contains the information to decrypt /// incoming messages and sign and encrypt outgoing messages. /// /// It has the necessary cryptographic material needed to appear /// as this identity when creating messages. pub struct Identity { /// The display name if this user identity. pub name: Rc, /// The private key of this identity. There are currently /// no ways in this library to generate a new private key /// from scratch (while ensuing it's sound) so it must be /// provided by the user. #[serde(deserialize_with = "deserialize_private_key")] pub private_key: PrivateKey, /// The derived public key given this identity's /// private key. The hash prefix of this (typically 1-byte) /// is used as a first-pass to identity the recipient. #[serde(skip)] pub public_key: PublicKey, /// The secrets has is a collection of shared secrets /// indexed by the public key of the other side of the /// connection. The whole remote public key must be /// used because there are many many hash collisions /// when using just the 1-byte hash prefix. #[serde(skip)] pub secrets: HashSet<(Rc, SharedSecret)>, } #[derive(PartialEq, Clone, Deserialize, Debug)] /// The Contact structure contains the information needed /// to decrypt messages from a remote user intended for /// either an identity or a channel. /// /// It's cryptographic material is also needed to uniquely /// identify the origin of a message. Most source values /// are only one byte, so that information just limits the /// number of entries in the contact list that must be searched /// before the correct contact is found. pub struct Contact { /// The display name of the contact pub name: Rc, /// The provided public key of the remote contact #[serde(deserialize_with = "deserialize_public_key")] pub public_key: PublicKey, } #[derive(PartialEq, Clone, Deserialize, Debug)] /// A Group in MeshCore is a kind of contact, except that its /// secret is fixed and shared directly. It's not derived via /// a public and private key. Otherwise, it behaves more like a /// contact in the sense that it's full identity is discovered /// by whether its secret can correctly decrypt messages. pub struct Group { /// The display name of the group pub name: Rc, /// The group's shared secret #[serde(deserialize_with = "deserialize_secret")] pub secret: SharedSecret, } #[derive(PartialEq, Clone, Deserialize, Debug)] pub struct Keystore { pub identities: HashMap, Identity>, pub contacts: HashMap, Contact>, pub groups: HashMap, Group>, } impl Keystore { pub fn identity_by_name(&self, name: &str) -> Option { if let Some(identity) = self.identities.get(&Rc::new(name.to_owned())) { Some(identity.clone()) } else { None } } pub fn decrypt_and_id_p2p( &self, _source: u8, _dest: u8, mac: u16, data: &Bytes, ) -> Option<(Bytes, Rc, Rc)> { // Just brute-force all the secrets until one matches... for identity in self.identities.iter() { for secret in identity.1.secrets.iter() { let result = secret.1.mac_then_decrypt(mac, data); if let Some(result) = result { return Some((result, identity.0.clone(), secret.0.clone())); } } } None } pub fn decrypt_and_id_group( &self, _group_hash_prefix: u8, mac: u16, data: &Bytes, ) -> Option<(Bytes, u32)> { for group in self.groups.iter() { let result = group.1.secret.mac_then_decrypt(mac, data); if let Some(result) = result { return Some((result, 0)); } } None } pub fn decrypt_anon( &self, _dest: u8, pub_key: &PublicKey, mac: u16, data: &Bytes, ) -> Option<(Bytes, Rc)> { // For each identity, create a shared secret against the provided public key and check it. for identity in self.identities.iter() { let secret = identity.1.private_key.create_secret(pub_key); let result = secret.mac_then_decrypt(mac, data); if let Some(result) = result { return Some((result, identity.0.clone())); } } None } } #[derive(PartialEq, Debug, Clone, Deserialize)] pub struct KeystoreInput { pub identities: HashMap, pub contacts: HashMap, pub groups: HashMap, } impl KeystoreInput { pub fn compile(self) -> Keystore { let mut retval = Keystore { identities: HashMap::new(), contacts: HashMap::new(), groups: HashMap::new(), }; // Iterate through the input keystore file // for each one, make sure it's in the map correctly // and use the opportunity to compute the shared keys. // Unfortunately, for testing, we really need this to be // sorted so we can ensure that the order of values // is in the same order every time. let mut contact_names: Vec<&String> = self.contacts.keys().collect(); contact_names.sort(); let mut contacts: Vec = vec![]; for name in contact_names { if let Some(key_string) = self.contacts.get(name) { match PublicKey::from_str(&key_string) { Ok(pub_key) => { contacts.push(Contact { name: Rc::new(name.to_string()), public_key: pub_key, }); } Err(e) => { warn!( "Unable to add contact named \"{}\" because there was a problem with the public key: {}", name, e ); } } } } let mut identity_names: Vec<&String> = self.identities.keys().collect(); identity_names.sort(); let mut identities: Vec<(Rc, Identity)> = vec![]; for name in identity_names { if let Some(key_string) = self.identities.get(name) { match PrivateKey::from_str(&key_string) { Ok(private_key) => { let public_key = PublicKey::from(&private_key); let mut i = Identity { name: Rc::new(name.to_string()), private_key, public_key, secrets: HashSet::new(), }; i.secrets = contacts .iter() .map(|c| (c.name.clone(), i.private_key.create_secret(&c.public_key))) .collect(); identities.push((i.name.clone(), i)); } Err(e) => { warn!( "Unable to add identity named \"{}\" because there was a problem with the private key: {}", name, e ); } } } } retval.identities = HashMap::from_iter(identities); // Create a hash of contacts let contacts = contacts.into_iter().map(|c| (c.name.clone(), c)); retval.contacts = HashMap::from_iter(contacts); // Create a hash of groups let groups = self.groups.into_iter().filter_map(|(name, secret)| { let name = Rc::new(name); match SharedSecret::from_str(&secret) { Ok(secret) => { Some((name.clone(), Group { name: name.clone(), secret })) } Err(e) => { warn!("Unable to add group named \"{}\" because there was a problem with the secret: {}", name, e); None } } }); retval.groups = HashMap::from_iter(groups); retval } } fn deserialize_private_key<'de, D>(deserializer: D) -> Result where D: de::Deserializer<'de>, { use crate::crypto::PrivateKey; use std::str::FromStr; let s: String = de::Deserialize::deserialize(deserializer)?; match PrivateKey::from_str(&s) { Ok(key) => Ok(key), Err(e) => Err(de::Error::custom(format!("{}", e))), } } fn deserialize_public_key<'de, D>(deserializer: D) -> Result where D: de::Deserializer<'de>, { use crate::crypto::{MeshcoreCryptoError, PublicKey}; use hex::decode; let s: String = de::Deserialize::deserialize(deserializer)?; match decode(s) { Err(hex::FromHexError::InvalidHexCharacter { c, index }) => { Err(de::Error::custom(format!( "Invalid hex character {} at {} when decoding Public Key.", c, index ))) } Err(hex::FromHexError::OddLength) => Err(de::Error::custom(format!( "Odd number of characters when decoding Public Key." ))), Err(hex::FromHexError::InvalidStringLength) => Err(de::Error::custom(format!( "Invalid hex string length when decoding Public Key." ))), Ok(hex) => { let key = PublicKey::try_from(hex.as_slice()); match key { Ok(key) => Ok(key), Err(MeshcoreCryptoError::KeyCreationError) => Err(de::Error::custom(format!( "Unable to decompress public key points" ))), Err(_) => Err(de::Error::custom(format!( "Invalid hex string length when decoding Public Key." ))), } } } } fn deserialize_secret<'de, D>(deserializer: D) -> Result where D: de::Deserializer<'de>, { use crate::crypto::SharedSecret; use std::str::FromStr; let s: String = de::Deserialize::deserialize(deserializer)?; match SharedSecret::from_str(&s) { Ok(secret) => Ok(secret), Err(e) => Err(de::Error::custom(format!("{}", e))), } } // Tests for std operations #[cfg(test)] mod tests { use hex::decode; use log::LevelFilter; use serde::de::IntoDeserializer; use serde::de::value::{Error as ValueError, StrDeserializer}; use std::io; use std::str::FromStr; use std::sync::mpsc::{Sender, channel}; use std::vec; use super::*; use crate::crypto::*; use crate::std_identity::KeystoreInput; #[test] fn secret_deserializer() { let deserializer: StrDeserializer = "eb50a1bcb3e4e5d7bf69a57c9dada211".into_deserializer(); let result = deserialize_secret(deserializer); match result { Ok(result) => assert_eq!( result, SharedSecret::from_str("eb50a1bcb3e4e5d7bf69a57c9dada211").unwrap() ), Err(err) => assert!(false, "Shouldn't have had an error, but got one: {}", err), } // Test failure conditions let deserializer: StrDeserializer = "eb50a1bcb3e4e5d7bf69a57c9dada21".into_deserializer(); let result = deserialize_secret(deserializer); let error = result.unwrap_err(); assert_eq!( error.to_string(), "Try From Slice Error", "Unexpected error response: {}", error.to_string() ); } #[test] fn public_key_deserializer() { let deserializer: StrDeserializer = "34569df1f9661916901669666fb8025eccb9ddb0499cddad4c164fec219c8b8f".into_deserializer(); let result = deserialize_public_key(deserializer); match result { Ok(result) => assert_eq!( result, PublicKey::from_str( "34569df1f9661916901669666fb8025eccb9ddb0499cddad4c164fec219c8b8f" ) .unwrap() ), Err(err) => assert!(false, "Shouldn't have had an error, but got one: {}", err), } // Test failure conditions let deserializer: StrDeserializer = "34569df1f9661916901669666fb8025eccb9ddb0499cddad4c164fec219".into_deserializer(); let result = deserialize_public_key(deserializer); let error = result.unwrap_err(); assert_eq!( error.to_string(), "Odd number of characters when decoding Public Key.", "Unexpected error response: {}", error.to_string() ); let deserializer: StrDeserializer = "34569df1f9661916901669666fb8025eccb9ddb0499cddad4c164fec21".into_deserializer(); let result = deserialize_public_key(deserializer); let error = result.unwrap_err(); assert_eq!( error.to_string(), "Invalid hex string length when decoding Public Key.", "Unexpected error response: {}", error.to_string() ); let deserializer: StrDeserializer = "34569df1f9661916901669666fb8025eccb9ddb0499cddad4b164f4c219a8b8f".into_deserializer(); let result = deserialize_public_key(deserializer); let error = result.unwrap_err(); assert_eq!( error.to_string(), "Unable to decompress public key points", "Unexpected error response: {}", error.to_string() ); let deserializer: StrDeserializer = "34569df1f9661916901669666fb8z25eccb9ddb0499cddad4b164f4c219a8b8f".into_deserializer(); let result = deserialize_public_key(deserializer); let error = result.unwrap_err(); assert_eq!( error.to_string(), "Invalid hex character z at 28 when decoding Public Key.", "Unexpected error response: {}", error.to_string() ); } #[test] fn private_key_deserializer() { let deserializer: StrDeserializer = "4885CF25975EA09742EF76DA587D0957E74EE02AAA34A001458E207E63CF7E6C4940C8C42C335862C71CC2F139633057D1FEE5687B172B27E1E0302A1D480E08".into_deserializer(); let result = deserialize_private_key(deserializer); match result { Ok(result) => assert_eq!(result, PrivateKey::from_str("4885CF25975EA09742EF76DA587D0957E74EE02AAA34A001458E207E63CF7E6C4940C8C42C335862C71CC2F139633057D1FEE5687B172B27E1E0302A1D480E08").unwrap()), Err(err) => assert!(false, "Shouldn't have had an error, but got one: {}", err), } // Test failure conditions let deserializer: StrDeserializer = "4885CF25975EA09742EF76DA587D0957E74EE02AAA34A001458E207E63CF7E6C4940C8C42C335862C71CC2F139633057D1FEE5687B172B27E1E0302A1D480E0".into_deserializer(); let result = deserialize_private_key(deserializer); let error = result.unwrap_err(); assert_eq!( error.to_string(), "Hex Decode Error", "Unexpected error response: {}", error.to_string() ); let deserializer: StrDeserializer = "4885CF25975EA09742EF76DA587D0957E74EE02AAA34A001458E207E63CF7E6C4940C8C42C335862C71CC2F139633057D1FEE5687B172B27E1E0302A1D480E".into_deserializer(); let result = deserialize_private_key(deserializer); let error = result.unwrap_err(); assert_eq!( error.to_string(), "Try From Slice Error", "Unexpected error response: {}", error.to_string() ); } #[test] fn deserialize_toml() { let file_contents = include_str!("../test_identities_file.toml"); let keystore_in: KeystoreInput = toml::from_str(file_contents).unwrap(); let keystore = keystore_in.compile(); println!("{:#?}", keystore.identity_by_name("Sample 1 ID").unwrap()); let lhs = keystore.identity_by_name("Sample 1 ID").unwrap(); let rhs = Identity { name: Rc::new("Sample 1 ID".to_owned()), private_key: PrivateKey::from_str("4885CF25975EA09742EF76DA587D0957E74EE02AAA34A001458E207E63CF7E6C4940C8C42C335862C71CC2F139633057D1FEE5687B172B27E1E0302A1D480E08").unwrap(), public_key: PublicKey::from_str("34569df1f9661916901669666fb8025eccb9ddb0499cddad4c164fec219c8b8f").unwrap(), secrets: HashSet::from([ (Rc::new("Sample 1 CT".to_owned()), SharedSecret::try_from(Bytes::copy_from_slice(&decode("15f857c60a0672e999fa20f022ffc7d5f8c7a1a1808868273aa7d6caa772f818").unwrap())).unwrap()), (Rc::new("Sample 2 CT".to_owned()), SharedSecret::try_from(Bytes::copy_from_slice(&decode("eb7a365363bd8548ee2b54b9234247be5e42e96be9625adcdf3a55b6c1d04850").unwrap())).unwrap()), (Rc::new("Sample 3 CT".to_owned()), SharedSecret::try_from(Bytes::copy_from_slice(&decode("87c4bbfc210ef8c6eda4ab46ac40e27d4a9f7c649df63d932ccb94279e26a549").unwrap())).unwrap()), (Rc::new("Sample 4 CT".to_owned()), SharedSecret::try_from(Bytes::copy_from_slice(&decode("02237f72fcd9405ee890b3d706800d1fd4e44005552c00252c3f3a9726c46d13").unwrap())).unwrap()), (Rc::new("Sample 5 CT".to_owned()), SharedSecret::try_from(Bytes::copy_from_slice(&decode("d7c2916d671ee530ce7acba8b235414cce5b6e5b9079e714b77179359b2f5d4c").unwrap())).unwrap()), ]) }; assert_eq!(lhs, rhs); } // This struct is used as an adaptor, it implements io::Write and forwards the buffer to a mpsc::Sender struct WriteAdapter { sender: Sender, } impl io::Write for WriteAdapter { // On write we forward each u8 of the buffer to the sender and return the length of the buffer fn write(&mut self, buf: &[u8]) -> io::Result { for chr in buf { self.sender.send(*chr).unwrap(); } Ok(buf.len()) } fn flush(&mut self) -> io::Result<()> { Ok(()) } } #[test] fn deserialize_toml_errors() { let (rx, tx) = channel(); let expect: Vec<&str> = vec![ "Hex Decode Error", "Key Length Error", "Key Creation Error", "Hex Decode Error", "Hex Decode Error", "Try From Slice Error", "Try From Slice Error", ]; let _ = pretty_env_logger::env_logger::builder() .is_test(true) .filter_level(LevelFilter::max()) .target(pretty_env_logger::env_logger::Target::Pipe(Box::new( WriteAdapter { sender: rx }, ))) .try_init(); // Test failure conditions let contents = r#" [identities] "1 Hex Decode Error" = "4885CF25975EA09742EF76DA587D0957E74EE02AAA34A001458E207E63CF7E6C4940C8C42C335862C71CC2F139633057D1FEE5687B172B27E1E0302A1D480E0" "2 Slice Error" = "4885CF25975EA09742EF76DA587D0957E74EE02AAA34A001458E207E63CF7E6C4940C8C42C335862C71CC2F139633057D1FEE5687B172B27E1E0302A1D480E" [contacts] "1 Odd number of characters" = "34569df1f9661916901669666fb8025eccb9ddb0499cddad4c164fec219" "2 Invalid hex length" = "34569df1f9661916901669666fb8025eccb9ddb0499cddad4c164fec21" "3 Unable to decompress" = "34569df1f9661916901669666fb8025eccb9ddb0499cddad4b164f4c219a8b8f" "4 Invalid hex character" = "34569df1f9661916901669666fb8z25eccb9ddb0499cddad4b164f4c219a8b8f" [groups] "Slice error" = "eb50a1bcb3e4e5d7bf69a57c9dada21" "#; let keystore_in = toml::from_str::(contents).unwrap(); let _keystore = keystore_in.compile(); String::from_utf8(tx.try_iter().collect::>()) .unwrap() .split('\n') .into_iter() .zip(expect.into_iter()) .for_each(|(got, expect)| { println!("{}", &got); assert!( got.contains(expect), "Got log line: \"{}\" and didn't find \"{}\" within it as expected.", got, expect ); }); } }