diff --git a/src/e2ee.rs b/src/e2ee.rs index 09915a5b3..cd8ae794e 100644 --- a/src/e2ee.rs +++ b/src/e2ee.rs @@ -11,7 +11,7 @@ use crate::context::Context; use crate::error::*; use crate::headerdef::HeaderDef; use crate::headerdef::HeaderDefMap; -use crate::key::{DcKey, SignedPublicKey, SignedSecretKey}; +use crate::key::{DcKey, Fingerprint, SignedPublicKey, SignedSecretKey}; use crate::keyring::*; use crate::peerstate::*; use crate::pgp; @@ -119,7 +119,7 @@ pub async fn try_decrypt( context: &Context, mail: &ParsedMail<'_>, message_time: i64, -) -> Result<(Option>, HashSet)> { +) -> Result<(Option>, HashSet)> { let from = mail .headers .get_header(HeaderDef::From_) @@ -212,7 +212,7 @@ async fn decrypt_if_autocrypt_message<'a>( mail: &ParsedMail<'a>, private_keyring: Keyring, public_keyring_for_validate: Keyring, - ret_valid_signatures: &mut HashSet, + ret_valid_signatures: &mut HashSet, ) -> Result>> { // The returned bool is true if we detected an Autocrypt-encrypted // message and successfully decrypted it. Decryption then modifies the @@ -244,7 +244,7 @@ async fn decrypt_part( mail: &ParsedMail<'_>, private_keyring: Keyring, public_keyring_for_validate: Keyring, - ret_valid_signatures: &mut HashSet, + ret_valid_signatures: &mut HashSet, ) -> Result>> { let data = mail.get_body_raw()?; diff --git a/src/key.rs b/src/key.rs index 8f746bb1f..9ed951225 100644 --- a/src/key.rs +++ b/src/key.rs @@ -9,6 +9,7 @@ use num_traits::FromPrimitive; use pgp::composed::Deserializable; use pgp::ser::Serialize; use pgp::types::{KeyTrait, SecretKeyTrait}; +use thiserror::Error; use crate::config::Config; use crate::constants::*; @@ -106,7 +107,7 @@ pub trait DcKey: Serialize + Deserializable + KeyTrait + Clone { /// The fingerprint for the key. fn fingerprint(&self) -> Fingerprint { - Fingerprint::new(KeyTrait::fingerprint(self)) + Fingerprint::new(KeyTrait::fingerprint(self)).expect("Invalid fingerprint from rpgp") } } @@ -354,12 +355,15 @@ pub async fn store_self_keypair( } /// A key fingerprint -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct Fingerprint(Vec); impl Fingerprint { - pub fn new(v: Vec) -> Fingerprint { - Fingerprint(v) + pub fn new(v: Vec) -> std::result::Result { + match v.len() { + 20 => Ok(Fingerprint(v)), + _ => Err(FingerprintError::WrongLength), + } } /// Make a hex string from the fingerprint. @@ -389,42 +393,26 @@ impl fmt::Display for Fingerprint { /// Parse a human-readable or otherwise formatted fingerprint. impl std::str::FromStr for Fingerprint { - type Err = hex::FromHexError; + type Err = FingerprintError; fn from_str(input: &str) -> std::result::Result { let hex_repr: String = input + .to_uppercase() .chars() .filter(|&c| c >= '0' && c <= '9' || c >= 'A' && c <= 'F') .collect(); let v: Vec = hex::decode(hex_repr)?; - Ok(Fingerprint(v)) + let fp = Fingerprint::new(v)?; + Ok(fp) } } -/// Bring a human-readable or otherwise formatted fingerprint back to the 40-characters-uppercase-hex format. -pub fn dc_normalize_fingerprint(fp: &str) -> String { - fp.to_uppercase() - .chars() - .filter(|&c| c >= '0' && c <= '9' || c >= 'A' && c <= 'F') - .collect() -} - -/// Make a fingerprint human-readable, in hex format. -pub fn dc_format_fingerprint(fingerprint: &str) -> String { - // split key into chunks of 4 with space, and 20 newline - let mut res = String::new(); - - for (i, c) in fingerprint.chars().enumerate() { - if i > 0 && i % 20 == 0 { - res += "\n"; - } else if i > 0 && i % 4 == 0 { - res += " "; - } - - res += &c.to_string(); - } - - res +#[derive(Debug, Error)] +pub enum FingerprintError { + #[error("Invalid hex characters")] + NotHex(#[from] hex::FromHexError), + #[error("Incorrect fingerprint lengths")] + WrongLength, } #[cfg(test)] @@ -432,6 +420,8 @@ mod tests { use super::*; use crate::test_utils::*; + use std::error::Error; + use async_std::sync::Arc; use lazy_static::lazy_static; @@ -439,23 +429,6 @@ mod tests { static ref KEYPAIR: KeyPair = alice_keypair(); } - #[test] - fn test_normalize_fingerprint() { - let fingerprint = dc_normalize_fingerprint(" 1234 567890 \n AbcD abcdef ABCDEF "); - - assert_eq!(fingerprint, "1234567890ABCDABCDEFABCDEF"); - } - - #[test] - fn test_format_fingerprint() { - let fingerprint = dc_format_fingerprint("1234567890ABCDABCDEFABCDEF1234567890ABCD"); - - assert_eq!( - fingerprint, - "1234 5678 90AB CDAB CDEF\nABCD EF12 3456 7890 ABCD" - ); - } - #[test] fn test_from_armored_string() { let (private_key, _) = SignedSecretKey::from_asc( @@ -586,7 +559,6 @@ i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD } #[async_std::test] - #[ignore] // generating keys is expensive async fn test_load_self_generate_public() { let t = dummy_context().await; t.ctx @@ -598,7 +570,6 @@ i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD } #[async_std::test] - #[ignore] // generating keys is expensive async fn test_load_self_generate_secret() { let t = dummy_context().await; t.ctx @@ -610,7 +581,6 @@ i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD } #[async_std::test] - #[ignore] // generating keys is expensive async fn test_load_self_generate_concurrent() { use std::thread; @@ -685,32 +655,46 @@ i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD #[test] fn test_fingerprint_from_str() { - let res = Fingerprint::new(vec![1, 2, 4, 8, 16, 32, 64, 128, 255]); + let res = Fingerprint::new(vec![ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + ]) + .unwrap(); - let fp: Fingerprint = "0102040810204080FF".parse().unwrap(); + let fp: Fingerprint = "0102030405060708090A0B0c0d0e0F1011121314".parse().unwrap(); assert_eq!(fp, res); - let fp: Fingerprint = "zzzz 0102 0408\n1020 4080 FF zzz".parse().unwrap(); + let fp: Fingerprint = "zzzz 0102 0304 0506\n0708090a0b0c0D0E0F1011121314 yyy" + .parse() + .unwrap(); assert_eq!(fp, res); let err = "1".parse::().err().unwrap(); - assert_eq!(err, hex::FromHexError::OddLength); + match err { + FingerprintError::NotHex(_) => (), + _ => panic!("Wrong error"), + } + let src_err = err.source().unwrap().downcast_ref::(); + assert_eq!(src_err, Some(&hex::FromHexError::OddLength)); } #[test] fn test_fingerprint_hex() { - let fp = Fingerprint::new(vec![1, 2, 4, 8, 16, 32, 64, 128, 255]); - assert_eq!(fp.hex(), "0102040810204080FF"); + let fp = Fingerprint::new(vec![ + 1, 2, 4, 8, 16, 32, 64, 128, 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + ]) + .unwrap(); + assert_eq!(fp.hex(), "0102040810204080FF0A0B0C0D0E0F1011121314"); } #[test] fn test_fingerprint_to_string() { let fp = Fingerprint::new(vec![ - 1, 2, 4, 8, 16, 32, 64, 128, 255, 1, 2, 4, 8, 16, 32, 64, 128, 255, - ]); + 1, 2, 4, 8, 16, 32, 64, 128, 255, 1, 2, 4, 8, 16, 32, 64, 128, 255, 19, 20, + ]) + .unwrap(); assert_eq!( fp.to_string(), - "0102 0408 1020 4080 FF01\n0204 0810 2040 80FF" + "0102 0408 1020 4080 FF01\n0204 0810 2040 80FF 1314" ); } } diff --git a/src/lot.rs b/src/lot.rs index 480562aa4..1fa426bac 100644 --- a/src/lot.rs +++ b/src/lot.rs @@ -1,5 +1,7 @@ use deltachat_derive::{FromSql, ToSql}; +use crate::key::Fingerprint; + /// An object containing a set of values. /// The meaning of the values is defined by the function returning the object. /// Lot objects are created @@ -14,7 +16,7 @@ pub struct Lot { pub(crate) timestamp: i64, pub(crate) state: LotState, pub(crate) id: u32, - pub(crate) fingerprint: Option, + pub(crate) fingerprint: Option, pub(crate) invitenumber: Option, pub(crate) auth: Option, } diff --git a/src/mimeparser.rs b/src/mimeparser.rs index aa87b9143..4a5c09bb4 100644 --- a/src/mimeparser.rs +++ b/src/mimeparser.rs @@ -17,6 +17,7 @@ use crate::e2ee; use crate::error::{bail, Result}; use crate::events::Event; use crate::headerdef::{HeaderDef, HeaderDefMap}; +use crate::key::Fingerprint; use crate::location; use crate::message; use crate::param::*; @@ -44,7 +45,7 @@ pub struct MimeMessage { pub from: Vec, pub chat_disposition_notification_to: Option, pub decrypting_failed: bool, - pub signatures: HashSet, + pub signatures: HashSet, pub gossipped_addr: HashSet, pub is_forwarded: bool, pub is_system_message: SystemMessage, diff --git a/src/peerstate.rs b/src/peerstate.rs index f6c460e6a..ab4ac2953 100644 --- a/src/peerstate.rs +++ b/src/peerstate.rs @@ -7,7 +7,7 @@ use num_traits::FromPrimitive; use crate::aheader::*; use crate::context::Context; -use crate::key::{DcKey, SignedPublicKey}; +use crate::key::{DcKey, Fingerprint, SignedPublicKey}; use crate::sql::Sql; #[derive(Debug)] @@ -32,12 +32,12 @@ pub struct Peerstate<'a> { pub last_seen_autocrypt: i64, pub prefer_encrypt: EncryptPreference, pub public_key: Option, - pub public_key_fingerprint: Option, + pub public_key_fingerprint: Option, pub gossip_key: Option, pub gossip_timestamp: i64, - pub gossip_key_fingerprint: Option, + pub gossip_key_fingerprint: Option, pub verified_key: Option, - pub verified_key_fingerprint: Option, + pub verified_key_fingerprint: Option, pub to_save: Option, pub degrade_event: Option, } @@ -151,7 +151,7 @@ impl<'a> Peerstate<'a> { pub async fn from_fingerprint( context: &'a Context, _sql: &Sql, - fingerprint: &str, + fingerprint: &Fingerprint, ) -> Option> { let query = "SELECT addr, last_seen, last_seen_autocrypt, prefer_encrypted, public_key, \ gossip_timestamp, gossip_key, public_key_fingerprint, gossip_key_fingerprint, \ @@ -160,13 +160,8 @@ impl<'a> Peerstate<'a> { WHERE public_key_fingerprint=? COLLATE NOCASE \ OR gossip_key_fingerprint=? COLLATE NOCASE \ ORDER BY public_key_fingerprint=? DESC;"; - - Self::from_stmt( - context, - query, - paramsv![fingerprint, fingerprint, fingerprint], - ) - .await + let fp = fingerprint.hex(); + Self::from_stmt(context, query, paramsv![fp, fp, fp]).await } async fn from_stmt( @@ -189,33 +184,18 @@ impl<'a> Peerstate<'a> { res.prefer_encrypt = EncryptPreference::from_i32(row.get(3)?).unwrap_or_default(); res.gossip_timestamp = row.get(5)?; - res.public_key_fingerprint = row.get(7)?; - if res - .public_key_fingerprint - .as_ref() - .map(|s| s.is_empty()) - .unwrap_or_default() - { - res.public_key_fingerprint = None; - } - res.gossip_key_fingerprint = row.get(8)?; - if res - .gossip_key_fingerprint - .as_ref() - .map(|s| s.is_empty()) - .unwrap_or_default() - { - res.gossip_key_fingerprint = None; - } - res.verified_key_fingerprint = row.get(10)?; - if res - .verified_key_fingerprint - .as_ref() - .map(|s| s.is_empty()) - .unwrap_or_default() - { - res.verified_key_fingerprint = None; - } + res.public_key_fingerprint = row + .get::<_, Option>(7)? + .map(|s| s.parse::()) + .transpose()?; + res.gossip_key_fingerprint = row + .get::<_, Option>(8)? + .map(|s| s.parse::()) + .transpose()?; + res.verified_key_fingerprint = row + .get::<_, Option>(10)? + .map(|s| s.parse::()) + .transpose()?; res.public_key = row .get(4) .ok() @@ -238,7 +218,7 @@ impl<'a> Peerstate<'a> { pub fn recalc_fingerprint(&mut self) { if let Some(ref public_key) = self.public_key { let old_public_fingerprint = self.public_key_fingerprint.take(); - self.public_key_fingerprint = Some(public_key.fingerprint().hex()); + self.public_key_fingerprint = Some(public_key.fingerprint()); if old_public_fingerprint.is_none() || self.public_key_fingerprint.is_none() @@ -253,7 +233,7 @@ impl<'a> Peerstate<'a> { if let Some(ref gossip_key) = self.gossip_key { let old_gossip_fingerprint = self.gossip_key_fingerprint.take(); - self.gossip_key_fingerprint = Some(gossip_key.fingerprint().hex()); + self.gossip_key_fingerprint = Some(gossip_key.fingerprint()); if old_gossip_fingerprint.is_none() || self.gossip_key_fingerprint.is_none() @@ -387,7 +367,7 @@ impl<'a> Peerstate<'a> { pub fn set_verified( &mut self, which_key: PeerstateKeyType, - fingerprint: &str, + fingerprint: &Fingerprint, verified: PeerstateVerifiedStatus, ) -> bool { if verified == PeerstateVerifiedStatus::BidirectVerified { @@ -445,10 +425,10 @@ impl<'a> Peerstate<'a> { self.public_key.as_ref().map(|k| k.to_bytes()), self.gossip_timestamp, self.gossip_key.as_ref().map(|k| k.to_bytes()), - self.public_key_fingerprint, - self.gossip_key_fingerprint, + self.public_key_fingerprint.as_ref().map(|fp| fp.hex()), + self.gossip_key_fingerprint.as_ref().map(|fp| fp.hex()), self.verified_key.as_ref().map(|k| k.to_bytes()), - self.verified_key_fingerprint, + self.verified_key_fingerprint.as_ref().map(|fp| fp.hex()), self.addr, ], ).await?; @@ -469,7 +449,7 @@ impl<'a> Peerstate<'a> { Ok(()) } - pub fn has_verified_key(&self, fingerprints: &HashSet) -> bool { + pub fn has_verified_key(&self, fingerprints: &HashSet) -> bool { if self.verified_key.is_some() && self.verified_key_fingerprint.is_some() { let vkc = self.verified_key_fingerprint.as_ref().unwrap(); if fingerprints.contains(vkc) { @@ -481,6 +461,12 @@ impl<'a> Peerstate<'a> { } } +impl From for rusqlite::Error { + fn from(_source: crate::key::FingerprintError) -> Self { + Self::InvalidColumnType(0, "Invalid fingerprint".into(), rusqlite::types::Type::Text) + } +} + #[cfg(test)] mod tests { use super::*; @@ -502,12 +488,12 @@ mod tests { last_seen_autocrypt: 11, prefer_encrypt: EncryptPreference::Mutual, public_key: Some(pub_key.clone()), - public_key_fingerprint: Some(pub_key.fingerprint().hex()), + public_key_fingerprint: Some(pub_key.fingerprint()), gossip_key: Some(pub_key.clone()), gossip_timestamp: 12, - gossip_key_fingerprint: Some(pub_key.fingerprint().hex()), + gossip_key_fingerprint: Some(pub_key.fingerprint()), verified_key: Some(pub_key.clone()), - verified_key_fingerprint: Some(pub_key.fingerprint().hex()), + verified_key_fingerprint: Some(pub_key.fingerprint()), to_save: Some(ToSave::All), degrade_event: None, }; @@ -525,7 +511,7 @@ mod tests { peerstate.to_save = None; assert_eq!(peerstate, peerstate_new); let peerstate_new2 = - Peerstate::from_fingerprint(&ctx.ctx, &ctx.ctx.sql, &pub_key.fingerprint().hex()) + Peerstate::from_fingerprint(&ctx.ctx, &ctx.ctx.sql, &pub_key.fingerprint()) .await .expect("failed to load peerstate from db"); assert_eq!(peerstate, peerstate_new2); @@ -544,7 +530,7 @@ mod tests { last_seen_autocrypt: 11, prefer_encrypt: EncryptPreference::Mutual, public_key: Some(pub_key.clone()), - public_key_fingerprint: Some(pub_key.fingerprint().hex()), + public_key_fingerprint: Some(pub_key.fingerprint()), gossip_key: None, gossip_timestamp: 12, gossip_key_fingerprint: None, @@ -578,7 +564,7 @@ mod tests { last_seen_autocrypt: 11, prefer_encrypt: EncryptPreference::Mutual, public_key: Some(pub_key.clone()), - public_key_fingerprint: Some(pub_key.fingerprint().hex()), + public_key_fingerprint: Some(pub_key.fingerprint()), gossip_key: None, gossip_timestamp: 12, gossip_key_fingerprint: None, diff --git a/src/pgp.rs b/src/pgp.rs index 0c4a54c08..ee356b296 100644 --- a/src/pgp.rs +++ b/src/pgp.rs @@ -18,7 +18,7 @@ use rand::{thread_rng, CryptoRng, Rng}; use crate::constants::KeyGenType; use crate::dc_tools::EmailAddress; use crate::error::{bail, ensure, format_err, Result}; -use crate::key::DcKey; +use crate::key::{DcKey, Fingerprint}; use crate::keyring::Keyring; pub const HEADER_AUTOCRYPT: &str = "autocrypt-prefer-encrypt"; @@ -277,7 +277,7 @@ pub async fn pk_decrypt( ctext: Vec, private_keys_for_decryption: Keyring, public_keys_for_validation: Keyring, - ret_signature_fingerprints: Option<&mut HashSet>, + ret_signature_fingerprints: Option<&mut HashSet>, ) -> Result> { let msgs = async_std::task::spawn_blocking(move || { let cursor = Cursor::new(ctext); @@ -304,10 +304,10 @@ pub async fn pk_decrypt( let pkeys = public_keys_for_validation.keys(); - let mut fingerprints = Vec::new(); + let mut fingerprints: Vec = Vec::new(); for pkey in pkeys { if dec_msg.verify(&pkey.primary_key).is_ok() { - let fp = DcKey::fingerprint(pkey).hex(); + let fp = DcKey::fingerprint(pkey); fingerprints.push(fp); } } @@ -474,7 +474,7 @@ mod tests { decrypt_keyring.add(KEYS.alice_secret.clone()); let mut sig_check_keyring: Keyring = Keyring::new(); sig_check_keyring.add(KEYS.alice_public.clone()); - let mut valid_signatures: HashSet = Default::default(); + let mut valid_signatures: HashSet = Default::default(); let plain = pk_decrypt( CTEXT_SIGNED.as_bytes().to_vec(), decrypt_keyring, @@ -492,7 +492,7 @@ mod tests { decrypt_keyring.add(KEYS.bob_secret.clone()); let mut sig_check_keyring = Keyring::new(); sig_check_keyring.add(KEYS.alice_public.clone()); - let mut valid_signatures: HashSet = Default::default(); + let mut valid_signatures: HashSet = Default::default(); let plain = pk_decrypt( CTEXT_SIGNED.as_bytes().to_vec(), decrypt_keyring, @@ -511,7 +511,7 @@ mod tests { let mut keyring = Keyring::new(); keyring.add(KEYS.alice_secret.clone()); let empty_keyring = Keyring::new(); - let mut valid_signatures: HashSet = Default::default(); + let mut valid_signatures: HashSet = Default::default(); let plain = pk_decrypt( CTEXT_SIGNED.as_bytes().to_vec(), keyring, @@ -531,7 +531,7 @@ mod tests { decrypt_keyring.add(KEYS.bob_secret.clone()); let mut sig_check_keyring = Keyring::new(); sig_check_keyring.add(KEYS.bob_public.clone()); - let mut valid_signatures: HashSet = Default::default(); + let mut valid_signatures: HashSet = Default::default(); let plain = pk_decrypt( CTEXT_SIGNED.as_bytes().to_vec(), decrypt_keyring, @@ -549,7 +549,7 @@ mod tests { let mut decrypt_keyring = Keyring::new(); decrypt_keyring.add(KEYS.bob_secret.clone()); let sig_check_keyring = Keyring::new(); - let mut valid_signatures: HashSet = Default::default(); + let mut valid_signatures: HashSet = Default::default(); let plain = pk_decrypt( CTEXT_UNSIGNED.as_bytes().to_vec(), decrypt_keyring, diff --git a/src/qr.rs b/src/qr.rs index ff8487232..33c8c892f 100644 --- a/src/qr.rs +++ b/src/qr.rs @@ -10,8 +10,7 @@ use crate::constants::Blocked; use crate::contact::*; use crate::context::Context; use crate::error::{bail, ensure, format_err, Error}; -use crate::key::dc_format_fingerprint; -use crate::key::dc_normalize_fingerprint; +use crate::key::Fingerprint; use crate::lot::{Lot, LotState}; use crate::param::*; use crate::peerstate::*; @@ -80,6 +79,14 @@ async fn decode_openpgp(context: &Context, qr: &str) -> Lot { Some(pair) => pair, None => (payload, ""), }; + let fingerprint: Fingerprint = match fingerprint.parse() { + Ok(fp) => fp, + Err(err) => { + return Error::new(err) + .context("Failed to parse fingerprint in QR code") + .into() + } + }; // replace & with \n to match expected param format let fragment = fragment.replace('&', "\n"); @@ -128,13 +135,6 @@ async fn decode_openpgp(context: &Context, qr: &str) -> Lot { None }; - let fingerprint = dc_normalize_fingerprint(fingerprint); - - // ensure valid fingerprint - if fingerprint.len() != 40 { - return format_err!("Bad fingerprint length in QR code").into(); - } - let mut lot = Lot::new(); // retrieve known state for this fingerprint @@ -161,7 +161,7 @@ async fn decode_openpgp(context: &Context, qr: &str) -> Lot { chat::add_info_msg(context, id, format!("{} verified.", peerstate.addr)).await; } else { lot.state = LotState::QrFprWithoutAddr; - lot.text1 = Some(dc_format_fingerprint(&fingerprint)); + lot.text1 = Some(fingerprint.to_string()); } } else if let Some(addr) = addr { if grpid.is_some() && grpname.is_some() { diff --git a/src/securejoin.rs b/src/securejoin.rs index 163516bb5..5de6a35d9 100644 --- a/src/securejoin.rs +++ b/src/securejoin.rs @@ -14,7 +14,7 @@ use crate::e2ee::*; use crate::error::{bail, Error}; use crate::events::Event; use crate::headerdef::HeaderDef; -use crate::key::{dc_normalize_fingerprint, DcKey, SignedPublicKey}; +use crate::key::{DcKey, Fingerprint, SignedPublicKey}; use crate::lot::LotState; use crate::message::Message; use crate::mimeparser::*; @@ -73,8 +73,6 @@ pub async fn dc_get_securejoin_qr(context: &Context, group_chat_id: ChatId) -> O ==== Step 1 in "Setup verified contact" protocol ==== =======================================================*/ - let fingerprint: String; - ensure_secret_key_exists(context).await.ok(); // invitenumber will be used to allow starting the handshake, @@ -95,7 +93,7 @@ pub async fn dc_get_securejoin_qr(context: &Context, group_chat_id: ChatId) -> O .await .unwrap_or_default(); - fingerprint = match get_self_fingerprint(context).await { + let fingerprint: Fingerprint = match get_self_fingerprint(context).await { Some(fp) => fp, None => { return None; @@ -140,9 +138,9 @@ pub async fn dc_get_securejoin_qr(context: &Context, group_chat_id: ChatId) -> O qr } -async fn get_self_fingerprint(context: &Context) -> Option { +async fn get_self_fingerprint(context: &Context) -> Option { match SignedPublicKey::load_self(context).await { - Ok(key) => Some(key.fingerprint().hex()), + Ok(key) => Some(key.fingerprint()), Err(_) => { warn!(context, "get_self_fingerprint(): failed to load key"); None @@ -249,7 +247,7 @@ async fn securejoin(context: &Context, qr: &str) -> ChatId { chat_id_2_contact_id(context, contact_chat_id).await, 400 ); - let own_fingerprint = get_self_fingerprint(context).await.unwrap_or_default(); + let own_fingerprint = get_self_fingerprint(context).await; // Bob -> Alice if let Err(err) = send_handshake_msg( @@ -261,7 +259,7 @@ async fn securejoin(context: &Context, qr: &str) -> ChatId { "vc-request-with-auth" }, get_qr_attr!(context, auth).to_string(), - Some(own_fingerprint), + own_fingerprint, if join_vg { get_qr_attr!(context, text2).to_string() } else { @@ -311,7 +309,7 @@ async fn send_handshake_msg( contact_chat_id: ChatId, step: &str, param2: impl AsRef, - fingerprint: Option, + fingerprint: Option, grpid: impl AsRef, ) -> Result<(), HandshakeError> { let mut msg = Message::default(); @@ -328,7 +326,7 @@ async fn send_handshake_msg( msg.param.set(Param::Arg2, param2); } if let Some(fp) = fingerprint { - msg.param.set(Param::Arg3, fp); + msg.param.set(Param::Arg3, fp.hex()); } if !grpid.as_ref().is_empty() { msg.param.set(Param::Arg4, grpid.as_ref()); @@ -360,7 +358,7 @@ async fn chat_id_2_contact_id(context: &Context, contact_chat_id: ChatId) -> u32 async fn fingerprint_equals_sender( context: &Context, - fingerprint: impl AsRef, + fingerprint: &Fingerprint, contact_chat_id: ChatId, ) -> bool { let contacts = chat::get_chat_contacts(context, contact_chat_id).await; @@ -368,9 +366,8 @@ async fn fingerprint_equals_sender( if contacts.len() == 1 { if let Ok(contact) = Contact::load_from_db(context, contacts[0]).await { if let Some(peerstate) = Peerstate::from_addr(context, contact.get_addr()).await { - let fingerprint_normalized = dc_normalize_fingerprint(fingerprint.as_ref()); if peerstate.public_key_fingerprint.is_some() - && &fingerprint_normalized == peerstate.public_key_fingerprint.as_ref().unwrap() + && fingerprint == peerstate.public_key_fingerprint.as_ref().unwrap() { return true; } @@ -397,6 +394,8 @@ pub(crate) enum HandshakeError { NoSelfAddr, #[error("Failed to send message")] MsgSendFailed(#[source] Error), + #[error("Failed to parse fingerprint")] + BadFingerprint(#[from] crate::key::FingerprintError), } /// What to do with a Secure-Join handshake message after it was handled. @@ -516,10 +515,11 @@ pub(crate) async fn handle_securejoin_handshake( // no error, just aborted somehow or a mail from another handshake return Ok(HandshakeMessage::Ignore); } - let scanned_fingerprint_of_alice = get_qr_attr!(context, fingerprint).to_string(); + let scanned_fingerprint_of_alice: Fingerprint = + get_qr_attr!(context, fingerprint).clone(); let auth = get_qr_attr!(context, auth).to_string(); - if !encrypted_and_signed(context, mime_message, &scanned_fingerprint_of_alice) { + if !encrypted_and_signed(context, mime_message, Some(&scanned_fingerprint_of_alice)) { could_not_establish_secure_connection( context, contact_chat_id, @@ -576,8 +576,9 @@ pub(crate) async fn handle_securejoin_handshake( ==========================================================*/ // verify that Secure-Join-Fingerprint:-header matches the fingerprint of Bob - let fingerprint = match mime_message.get(HeaderDef::SecureJoinFingerprint) { - Some(fp) => fp, + let fingerprint: Fingerprint = match mime_message.get(HeaderDef::SecureJoinFingerprint) + { + Some(fp) => fp.parse()?, None => { could_not_establish_secure_connection( context, @@ -588,7 +589,7 @@ pub(crate) async fn handle_securejoin_handshake( return Ok(HandshakeMessage::Ignore); } }; - if !encrypted_and_signed(context, mime_message, &fingerprint) { + if !encrypted_and_signed(context, mime_message, Some(&fingerprint)) { could_not_establish_secure_connection( context, contact_chat_id, @@ -625,7 +626,7 @@ pub(crate) async fn handle_securejoin_handshake( .await; return Ok(HandshakeMessage::Ignore); } - if mark_peer_as_verified(context, fingerprint).await.is_err() { + if mark_peer_as_verified(context, &fingerprint).await.is_err() { could_not_establish_secure_connection( context, contact_chat_id, @@ -673,7 +674,7 @@ pub(crate) async fn handle_securejoin_handshake( contact_chat_id, "vc-contact-confirm", "", - Some(fingerprint.clone()), + Some(fingerprint), "", ) .await?; @@ -709,7 +710,8 @@ pub(crate) async fn handle_securejoin_handshake( ); return Ok(abort_retval); } - let scanned_fingerprint_of_alice = get_qr_attr!(context, fingerprint).to_string(); + let scanned_fingerprint_of_alice: Fingerprint = + get_qr_attr!(context, fingerprint).clone(); let vg_expect_encrypted = if join_vg { let group_id = get_qr_attr!(context, text2).to_string(); @@ -731,7 +733,7 @@ pub(crate) async fn handle_securejoin_handshake( true }; if vg_expect_encrypted - && !encrypted_and_signed(context, mime_message, &scanned_fingerprint_of_alice) + && !encrypted_and_signed(context, mime_message, Some(&scanned_fingerprint_of_alice)) { could_not_establish_secure_connection( context, @@ -888,7 +890,7 @@ pub(crate) async fn observe_securejoin_on_other_device( if !encrypted_and_signed( context, mime_message, - get_self_fingerprint(context).await.unwrap_or_default(), + get_self_fingerprint(context).await.as_ref(), ) { could_not_establish_secure_connection( context, @@ -898,8 +900,9 @@ pub(crate) async fn observe_securejoin_on_other_device( .await; return Ok(HandshakeMessage::Ignore); } - let fingerprint = match mime_message.get(HeaderDef::SecureJoinFingerprint) { - Some(fp) => fp, + let fingerprint: Fingerprint = match mime_message.get(HeaderDef::SecureJoinFingerprint) + { + Some(fp) => fp.parse()?, None => { could_not_establish_secure_connection( context, @@ -910,7 +913,7 @@ pub(crate) async fn observe_securejoin_on_other_device( return Ok(HandshakeMessage::Ignore); } }; - if mark_peer_as_verified(context, fingerprint).await.is_err() { + if mark_peer_as_verified(context, &fingerprint).await.is_err() { could_not_establish_secure_connection( context, contact_chat_id, @@ -967,16 +970,13 @@ async fn could_not_establish_secure_connection( error!(context, "{} ({})", &msg, details); } -async fn mark_peer_as_verified( - context: &Context, - fingerprint: impl AsRef, -) -> Result<(), Error> { +async fn mark_peer_as_verified(context: &Context, fingerprint: &Fingerprint) -> Result<(), Error> { if let Some(ref mut peerstate) = - Peerstate::from_fingerprint(context, &context.sql, fingerprint.as_ref()).await + Peerstate::from_fingerprint(context, &context.sql, fingerprint).await { if peerstate.set_verified( PeerstateKeyType::PublicKey, - fingerprint.as_ref(), + fingerprint, PeerstateVerifiedStatus::BidirectVerified, ) { peerstate.prefer_encrypt = EncryptPreference::Mutual; @@ -990,7 +990,7 @@ async fn mark_peer_as_verified( } bail!( "could not mark peer as verified for fingerprint {}", - fingerprint.as_ref() + fingerprint.hex() ); } @@ -1001,7 +1001,7 @@ async fn mark_peer_as_verified( fn encrypted_and_signed( context: &Context, mimeparser: &MimeMessage, - expected_fingerprint: impl AsRef, + expected_fingerprint: Option<&Fingerprint>, ) -> bool { if !mimeparser.was_encrypted() { warn!(context, "Message not encrypted.",); @@ -1009,17 +1009,17 @@ fn encrypted_and_signed( } else if mimeparser.signatures.is_empty() { warn!(context, "Message not signed.",); false - } else if expected_fingerprint.as_ref().is_empty() { - warn!(context, "Fingerprint for comparison missing.",); + } else if expected_fingerprint.is_none() { + warn!(context, "Fingerprint for comparison missing."); false } else if !mimeparser .signatures - .contains(expected_fingerprint.as_ref()) + .contains(expected_fingerprint.unwrap()) { warn!( context, "Message does not match expected fingerprint {}.", - expected_fingerprint.as_ref(), + expected_fingerprint.unwrap(), ); false } else {