diff --git a/cursive/src/main.rs b/cursive/src/main.rs index a626634..c4bd82a 100644 --- a/cursive/src/main.rs +++ b/cursive/src/main.rs @@ -33,7 +33,6 @@ use cursive::{ RadioGroup, ResizedView, ScrollView, SelectView, TextArea, TextView, }, }; -use hex::FromHex; use pass::Result; use ripasso::{ crypto::CryptoImpl, @@ -48,12 +47,12 @@ use unic_langid::LanguageIdentifier; mod helpers; mod wizard; -use lazy_static::lazy_static; -use zeroize::Zeroize; - use crate::helpers::{ get_value_from_input, is_checkbox_checked, is_radio_button_selected, recipients_widths, }; +use lazy_static::lazy_static; +use ripasso::crypto::Fingerprint; +use zeroize::Zeroize; /// The 'pointer' to the current PasswordStore is of this convoluted type. type PasswordStoreType = Arc>>>; @@ -1383,13 +1382,9 @@ fn get_stores(config: &config::Config, home: &Option) -> Result None, - Some(k) => match k.clone().into_string() { - Err(_) => None, - Ok(key) => <[u8; 20]>::from_hex(key).ok(), - }, - }; + let own_fingerprint = own_fingerprint + .map(|k| k.clone().into_string().map(|key| key.as_str().try_into())?) + .transpose()?; final_stores.push(PasswordStore::new( store_name, @@ -1507,7 +1502,7 @@ fn save_edit_config( false => CryptoImpl::GpgMe, }; - let own_fingerprint = <[u8; 20]>::from_hex(own_fingerprint).ok(); + let own_fingerprint: Fingerprint = own_fingerprint.as_str().try_into()?; let new_store = PasswordStore::new( e_n, @@ -1516,7 +1511,7 @@ fn save_edit_config( home, &None, &pgp_impl, - &own_fingerprint, + &Some(own_fingerprint), ); if let Err(err) = new_store { helpers::errorbox(ui, &err); @@ -1689,7 +1684,10 @@ fn edit_store_in_config( fingerprint_fields.add_child( EditView::new() .content(hex::encode_upper( - store.get_crypto().own_fingerprint().unwrap_or([0; 20]), + store + .get_crypto() + .own_fingerprint() + .unwrap_or(Fingerprint::V4([0; 20])), )) .with_name("edit_own_fingerprint_input") .fixed_size((50_usize, 1_usize)), diff --git a/cursive/src/tests/helpers.rs b/cursive/src/tests/helpers.rs index 03dc1b2..9921ca0 100644 --- a/cursive/src/tests/helpers.rs +++ b/cursive/src/tests/helpers.rs @@ -8,7 +8,7 @@ use cursive::{ views::{Checkbox, EditView, LinearLayout, RadioButton, RadioGroup}, }; use hex::FromHex; -use ripasso::crypto::CryptoImpl; +use ripasso::crypto::{CryptoImpl, Fingerprint}; use ripasso::pass::{Comment, KeyRingStatus, OwnerTrustLevel, Recipient}; #[test] @@ -100,9 +100,9 @@ pub fn recipient_alex() -> Recipient { post_comment: None, }, key_id: "1D108E6C07CBC406".to_owned(), - fingerprint: Some( + fingerprint: Some(Fingerprint::V4( <[u8; 20]>::from_hex("7E068070D5EF794B00C8A9D91D108E6C07CBC406").unwrap(), - ), + )), key_ring_status: KeyRingStatus::InKeyRing, trust_level: OwnerTrustLevel::Ultimate, not_usable: false, diff --git a/cursive/src/tests/main.rs b/cursive/src/tests/main.rs index 8c10f5b..cfd2d4d 100644 --- a/cursive/src/tests/main.rs +++ b/cursive/src/tests/main.rs @@ -1,6 +1,7 @@ use std::{fs::File, io::Write}; use chrono::Local; +use hex::FromHex; use ripasso::pass::{PasswordEntry, RepositoryStatus}; use tempfile::tempdir; @@ -168,9 +169,9 @@ fn render_recipient_label_ultimate() { post_comment: None, }, key_id: "1D108E6C07CBC406".to_owned(), - fingerprint: Some( + fingerprint: Some(Fingerprint::V4( <[u8; 20]>::from_hex("7E068070D5EF794B00C8A9D91D108E6C07CBC406").unwrap(), - ), + )), key_ring_status: pass::KeyRingStatus::InKeyRing, trust_level: OwnerTrustLevel::Ultimate, not_usable: false, diff --git a/gtk/src/window/mod.rs b/gtk/src/window/mod.rs index b4d13d7..bcb068a 100644 --- a/gtk/src/window/mod.rs +++ b/gtk/src/window/mod.rs @@ -6,17 +6,15 @@ use std::{ sync::{Arc, Mutex}, }; +use crate::{collection_object::CollectionObject, password_object::PasswordObject}; use adw::{ActionRow, NavigationDirection, prelude::*, subclass::prelude::*}; use glib::{Object, clone}; use gtk::{ AboutDialog, CustomFilter, Dialog, DialogFlags, Entry, FilterListModel, Label, ListBox, ListBoxRow, NoSelection, ResponseType, SelectionMode, gio, glib, glib::BindingFlags, pango, }; -use hex::FromHex; use ripasso::{crypto::CryptoImpl, pass::PasswordStore}; -use crate::{collection_object::CollectionObject, password_object::PasswordObject}; - glib::wrapper! { pub struct Window(ObjectSubclass) @extends adw::ApplicationWindow, gtk::ApplicationWindow, gtk::Window, gtk::Widget, @@ -580,13 +578,10 @@ fn get_stores( }?; let own_fingerprint = store.get("own_fingerprint"); - let own_fingerprint = match own_fingerprint { - None => None, - Some(k) => match k.clone().into_string() { - Err(_) => None, - Ok(key) => <[u8; 20]>::from_hex(key).ok(), - }, - }; + let own_fingerprint = own_fingerprint + .map(|k| k.clone().into_string().map(|fp| fp.as_str().try_into())) + .transpose()? + .transpose()?; final_stores.push(PasswordStore::new( store_name, diff --git a/src/crypto.rs b/src/crypto.rs index 5b48c61..215aba8 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -10,7 +10,7 @@ use std::{ use hex::FromHex; use sequoia_openpgp::{ - Cert, Fingerprint, KeyHandle, KeyID, + Cert, KeyHandle, KeyID, crypto::SessionKey, parse::{ Parse, @@ -114,13 +114,79 @@ pub enum FindSigningFingerprintStrategy { GPG, } +/// Contains a full pgp fingerprint of a certificate. +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)] +pub enum Fingerprint { + /// A RFC4880 style fingerprint. + V4([u8; 20]), + /// A RFC9580 style fingerprint. + V6([u8; 32]), +} + +impl From<[u8; 32]> for Fingerprint { + fn from(value: [u8; 32]) -> Self { + Fingerprint::V6(value) + } +} + +impl From<[u8; 20]> for Fingerprint { + fn from(value: [u8; 20]) -> Self { + Fingerprint::V4(value) + } +} + +/// Intended for usage with slices containing a v4 or v6 fingerprint. +impl TryFrom<&[u8]> for Fingerprint { + type Error = Error; + + fn try_from(b: &[u8]) -> std::result::Result { + match b.len() { + 20 => Ok(Fingerprint::V4( + b.try_into().expect("slice with incorrect length"), + )), + 32 => Ok(Fingerprint::V6( + b.try_into().expect("slice with incorrect length"), + )), + _ => Err(Error::Generic("slice isn't 20 or 32 bytes")), + } + } +} + +/// Intended for usage with string containing a v4 or v6 fingerprint in hex. +impl TryFrom<&str> for Fingerprint { + type Error = Error; + + fn try_from(key: &str) -> std::result::Result { + if key.len() == 40 { + Ok(Fingerprint::from(<[u8; 20]>::from_hex(key)?)) + } else if key.len() == 42 { + Ok(Fingerprint::from(<[u8; 20]>::from_hex(&key[2..])?)) + } else if key.len() == 64 { + Ok(Fingerprint::from(<[u8; 32]>::from_hex(key)?)) + } else if key.len() == 66 { + Ok(Fingerprint::from(<[u8; 32]>::from_hex(&key[2..])?)) + } else { + Err(Error::Generic("unable to parse fingerprint")) + } + } +} + +impl AsRef<[u8]> for Fingerprint { + fn as_ref(&self) -> &[u8] { + match self { + Fingerprint::V4(v4) => v4.as_slice(), + Fingerprint::V6(v6) => v6.as_slice(), + } + } +} + /// Models the interactions that can be done on a pgp key pub trait Key { /// returns a list of names associated with the key fn user_id_names(&self) -> Vec; /// returns the keys fingerprint - fn fingerprint(&self) -> Result<[u8; 20]>; + fn fingerprint(&self) -> Result; /// returns if the key isn't usable fn is_not_usable(&self) -> bool; @@ -140,10 +206,10 @@ impl Key for GpgMeKey { .collect() } - fn fingerprint(&self) -> Result<[u8; 20]> { + fn fingerprint(&self) -> Result { let fp = self.key.fingerprint()?; - Ok(<[u8; 20]>::from_hex(fp)?) + Ok(<[u8; 20]>::from_hex(fp)?.into()) } fn is_not_usable(&self) -> bool { @@ -175,7 +241,7 @@ pub trait Crypto { fn sign_string( &self, to_sign: &str, - valid_gpg_signing_keys: &[[u8; 20]], + valid_gpg_signing_keys: &[Fingerprint], strategy: &FindSigningFingerprintStrategy, ) -> Result; @@ -186,7 +252,7 @@ pub trait Crypto { &self, data: &[u8], sig: &[u8], - valid_signing_keys: &[[u8; 20]], + valid_signing_keys: &[Fingerprint], ) -> std::result::Result; /// Returns true if a recipient is in the user's keyring. @@ -210,13 +276,13 @@ pub trait Crypto { /// Returns a map from key fingerprints to OwnerTrustLevel's /// # Errors /// Will return `Err` on failure to obtain trust levels. - fn get_all_trust_items(&self) -> Result>; + fn get_all_trust_items(&self) -> Result>; /// Returns the type of this `CryptoImpl`, useful for serializing the store config fn implementation(&self) -> CryptoImpl; /// Returns the fingerprint of the user using ripasso - fn own_fingerprint(&self) -> Option<[u8; 20]>; + fn own_fingerprint(&self) -> Option; } /// Used when the user configures gpgme to be used as a pgp backend. @@ -258,7 +324,7 @@ impl Crypto for GpgMe { fn sign_string( &self, to_sign: &str, - valid_gpg_signing_keys: &[[u8; 20]], + valid_gpg_signing_keys: &[Fingerprint], strategy: &FindSigningFingerprintStrategy, ) -> Result { let mut ctx = gpgme::Context::from_protocol(gpgme::Protocol::OpenPgp)?; @@ -304,7 +370,7 @@ impl Crypto for GpgMe { &self, data: &[u8], sig: &[u8], - valid_signing_keys: &[[u8; 20]], + valid_signing_keys: &[Fingerprint], ) -> std::result::Result { let mut ctx = gpgme::Context::from_protocol(gpgme::Protocol::OpenPgp) .map_err(|e| InfrastructureError(format!("{e:?}")))?; @@ -323,7 +389,7 @@ impl Crypto for GpgMe { let fpr = <[u8; 20]>::from_hex(fpr).map_err(|e| InfrastructureError(format!("{e:?}")))?; - if !valid_signing_keys.contains(&fpr) { + if !valid_signing_keys.contains(&Fingerprint::V4(fpr)) { return Err(VerificationError::SignatureFromWrongRecipient); } if i == 0 { @@ -348,7 +414,7 @@ impl Crypto for GpgMe { fn is_key_in_keyring(&self, recipient: &Recipient) -> Result { let mut ctx = gpgme::Context::from_protocol(gpgme::Protocol::OpenPgp)?; - if let Some(fingerprint) = recipient.fingerprint { + if let Some(fingerprint) = &recipient.fingerprint { Ok(ctx.get_key(hex::encode(fingerprint)).is_ok()) } else { Ok(false) @@ -392,7 +458,7 @@ impl Crypto for GpgMe { })) } - fn get_all_trust_items(&self) -> Result> { + fn get_all_trust_items(&self) -> Result> { let mut ctx = gpgme::Context::from_protocol(gpgme::Protocol::OpenPgp)?; ctx.set_key_list_mode(gpgme::KeyListMode::SIGS)?; @@ -402,7 +468,7 @@ impl Crypto for GpgMe { for key_res in keys { let key = key_res?; trusts.insert( - <[u8; 20]>::from_hex(key.fingerprint()?)?, + Fingerprint::V4(<[u8; 20]>::from_hex(key.fingerprint()?)?), crate::signature::OwnerTrustLevel::from(&key.owner_trust()), ); } @@ -414,7 +480,7 @@ impl Crypto for GpgMe { CryptoImpl::GpgMe } - fn own_fingerprint(&self) -> Option<[u8; 20]> { + fn own_fingerprint(&self) -> Option { None } } @@ -445,7 +511,7 @@ struct Helper<'a> { /// the users cert secret: Option>, /// all certs - key_ring: &'a HashMap<[u8; 20], Arc>, + key_ring: &'a HashMap>, /// This is all the certificates that are allowed to sign something public_keys: Vec>, /// context if talking to gpg_agent for example @@ -488,7 +554,7 @@ impl VerificationHelper for Helper<'_> { } fn find( - key_ring: &HashMap<[u8; 20], Arc>, + key_ring: &HashMap>, recipient: &Option, ) -> Result> { let recipient = recipient.as_ref().ok_or(Error::Generic("No recipient"))?; @@ -496,17 +562,17 @@ fn find( match recipient { KeyHandle::Fingerprint(fpr) => { match fpr { - Fingerprint::V6(_v6) => { - return Err(Error::Generic("v6 keys not supported yet")); + sequoia_openpgp::Fingerprint::V6(v6) => { + if let Some(key_handle) = key_ring.get(&Fingerprint::V6(*v6)) { + return Ok(key_handle.clone()); + } } - Fingerprint::V4(v4) => { - for (key, value) in key_ring { - if key == v4 { - return Ok(value.clone()); - } + sequoia_openpgp::Fingerprint::V4(v4) => { + if let Some(key_handle) = key_ring.get(&Fingerprint::V4(*v4)) { + return Ok(key_handle.clone()); } } - Fingerprint::Unknown { .. } => { + sequoia_openpgp::Fingerprint::Unknown { .. } => { return Err(Error::Generic("unknown fingerprint version")); } _ => {} @@ -515,7 +581,7 @@ fn find( KeyHandle::KeyID(key_id) => match key_id { KeyID::Long(bytes) => { for (key, value) in key_ring { - if key[0..8] == *bytes { + if key.as_ref()[0..8] == *bytes { return Ok(value.clone()); } } @@ -601,18 +667,6 @@ impl DecryptionHelper for Helper<'_> { } } -/// Intended for usage with slices containing a v4 fingerprint. -pub fn slice_to_20_bytes(b: &[u8]) -> Result<[u8; 20]> { - if b.len() != 20 { - return Err(Error::Generic("slice isn't 20 bytes")); - } - - let mut f: [u8; 20] = [0; 20]; - f.copy_from_slice(&b[0..20]); - - Ok(f) -} - /// A pgp key produced with sequoia. pub struct SequoiaKey { /// The pgp key @@ -627,8 +681,8 @@ impl Key for SequoiaKey { .collect() } - fn fingerprint(&self) -> Result<[u8; 20]> { - slice_to_20_bytes(self.cert.fingerprint().as_bytes()) + fn fingerprint(&self) -> Result { + self.cert.fingerprint().as_bytes().try_into() } fn is_not_usable(&self) -> bool { @@ -647,9 +701,9 @@ impl Key for SequoiaKey { /// If the user selects to use sequoia as their pgp implementation. pub struct Sequoia { /// key id of the user. - user_key_id: [u8; 20], + user_key_id: Fingerprint, /// All certs in the keys directory - key_ring: HashMap<[u8; 20], Arc>, + key_ring: HashMap>, /// The home directory of the user, for gnupg context user_home: std::path::PathBuf, } @@ -658,8 +712,8 @@ impl Sequoia { /// creates the sequoia object /// # Errors /// If there is any problems reading the keys directory - pub fn new(config_path: &Path, own_fingerprint: [u8; 20], user_home: &Path) -> Result { - let mut key_ring: HashMap<[u8; 20], Arc> = HashMap::new(); + pub fn new(config_path: &Path, own_fingerprint: Fingerprint, user_home: &Path) -> Result { + let mut key_ring: HashMap> = HashMap::new(); let dir = config_path.join("share").join("ripasso").join("keys"); if dir.exists() { @@ -670,7 +724,7 @@ impl Sequoia { let data = fs::read(path)?; let cert = Cert::from_bytes(&data)?; - let fingerprint = slice_to_20_bytes(cert.fingerprint().as_bytes())?; + let fingerprint = cert.fingerprint().as_bytes().try_into()?; key_ring.insert(fingerprint, Arc::new(cert)); } } @@ -684,8 +738,8 @@ impl Sequoia { } pub fn from_values( - user_key_id: [u8; 20], - key_ring: HashMap<[u8; 20], Arc>, + user_key_id: Fingerprint, + key_ring: HashMap>, user_home: &Path, ) -> Self { Self { @@ -702,8 +756,8 @@ impl Sequoia { let mut result = vec![]; for recipient in input { - match recipient.fingerprint { - Some(fp) => match self.key_ring.get(&fp) { + match &recipient.fingerprint { + Some(fp) => match self.key_ring.get(fp) { Some(cert) => result.push(cert.clone()), None => { return Err(Error::GenericDyn(format!( @@ -742,7 +796,7 @@ impl Sequoia { fn write_cert(&mut self, cert_str: &str, keys_dir: &Path) -> Result { let cert = Cert::from_bytes(cert_str.as_bytes())?; - let fingerprint = slice_to_20_bytes(cert.fingerprint().as_bytes())?; + let fingerprint = cert.fingerprint().as_bytes().try_into()?; let mut file = File::create(keys_dir.join(hex::encode(fingerprint)))?; @@ -855,7 +909,7 @@ impl Crypto for Sequoia { fn sign_string( &self, to_sign: &str, - _valid_gpg_signing_keys: &[[u8; 20]], + _valid_gpg_signing_keys: &[Fingerprint], _strategy: &FindSigningFingerprintStrategy, ) -> Result { let p = sequoia_openpgp::policy::StandardPolicy::new(); @@ -905,7 +959,7 @@ impl Crypto for Sequoia { &self, data: &[u8], sig: &[u8], - valid_signing_keys: &[[u8; 20]], + valid_signing_keys: &[Fingerprint], ) -> std::result::Result { let p = sequoia_openpgp::policy::StandardPolicy::new(); @@ -990,8 +1044,8 @@ impl Crypto for Sequoia { Err(Error::GenericDyn(format!("no key found for {key_id}"))) } - fn get_all_trust_items(&self) -> Result> { - let mut res: HashMap<[u8; 20], OwnerTrustLevel> = HashMap::new(); + fn get_all_trust_items(&self) -> Result> { + let mut res: HashMap = HashMap::new(); for k in self.key_ring.keys() { res.insert(*k, OwnerTrustLevel::Ultimate); @@ -1004,7 +1058,7 @@ impl Crypto for Sequoia { CryptoImpl::Sequoia } - fn own_fingerprint(&self) -> Option<[u8; 20]> { + fn own_fingerprint(&self) -> Option { Some(self.user_key_id) } } diff --git a/src/pass.rs b/src/pass.rs index d04a412..3f586fc 100644 --- a/src/pass.rs +++ b/src/pass.rs @@ -29,6 +29,7 @@ use config::Config; use totp_rs::TOTP; use zeroize::Zeroize; +use crate::crypto::Fingerprint; use crate::{ crypto::{Crypto, CryptoImpl, GpgMe, Sequoia, VerificationError}, git::{ @@ -52,7 +53,7 @@ pub struct PasswordStore { root: PathBuf, /// A list of fingerprints of keys that are allowed to sign the .gpg-id file, obtained from the environmental /// variable `PASSWORD_STORE_SIGNING_KEY` or from the configuration file - valid_gpg_signing_keys: Vec<[u8; 20]>, + valid_gpg_signing_keys: Vec, /// a list of password files with metadata pub passwords: Vec, /// A file that describes the style of the store @@ -89,7 +90,7 @@ impl PasswordStore { home: &Option, style_file: &Option, crypto_impl: &CryptoImpl, - own_fingerprint: &Option<[u8; 20]>, + own_fingerprint: &Option, ) -> Result { let pass_home = password_dir_raw(password_store_dir, home); if !pass_home.exists() { @@ -224,7 +225,7 @@ impl PasswordStore { } /// Returns a vec with the keys that are allowed to sign the .gpg-id file - pub fn get_valid_gpg_signing_keys(&self) -> &Vec<[u8; 20]> { + pub fn get_valid_gpg_signing_keys(&self) -> &Vec { &self.valid_gpg_signing_keys } diff --git a/src/signature.rs b/src/signature.rs index 6ac7018..28fccf2 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -6,9 +6,7 @@ use std::{ path::{Path, PathBuf}, }; -use hex::FromHex; - -use crate::crypto::FindSigningFingerprintStrategy; +use crate::crypto::{FindSigningFingerprintStrategy, Fingerprint}; pub use crate::error::{Error, Result}; /// A git commit for a password might be signed by a gpg key, and this signature's verification @@ -42,7 +40,7 @@ impl From for SignatureStatus { pub fn parse_signing_keys( password_store_signing_key: &Option, crypto: &(dyn crate::crypto::Crypto + Send), -) -> Result> { +) -> Result> { if password_store_signing_key.is_none() { return Ok(vec![]); } @@ -50,10 +48,12 @@ pub fn parse_signing_keys( let mut signing_keys = vec![]; for key in password_store_signing_key.as_ref().unwrap().split(',') { let trimmed = key.trim().to_owned(); + let len = trimmed.len(); + let have_0x = trimmed.starts_with("0x"); - if trimmed.len() != 40 && (trimmed.len() != 42 && trimmed.starts_with("0x")) { + if !(len == 40 || len == 64 || len == 42 && have_0x || len == 66 && have_0x) { return Err(Error::Generic( - "signing key isn't in full 40 character id format", + "signing key isn't in full 40/64 hex character fingerprint format", )); } @@ -64,11 +64,7 @@ pub fn parse_signing_keys( ))); } - if trimmed.len() == 40 { - signing_keys.push(<[u8; 20]>::from_hex(trimmed)?); - } else { - signing_keys.push(<[u8; 20]>::from_hex(&trimmed[2..])?); - } + signing_keys.push(trimmed.as_str().try_into()?); } Ok(signing_keys) } @@ -173,9 +169,9 @@ pub struct Recipient { /// Machine-readable identity taken from the .gpg-id file, in the form of a gpg key id /// (16 hex chars) or a fingerprint (40 hex chars). pub key_id: String, - /// The fingerprint of the pgp key, as 20 bytes, + /// The fingerprint of the pgp key, as 20 bytes or 32 bytes, /// if the fingerprint of the key is not known, this will be None. - pub fingerprint: Option<[u8; 20]>, + pub fingerprint: Option, /// The status of the key in GPG's keyring pub key_ring_status: KeyRingStatus, /// The trust level the owner of the key ring has placed in this person @@ -191,7 +187,7 @@ impl Recipient { name: String, comment: Comment, key_id: String, - fingerprint: Option<[u8; 20]>, + fingerprint: Option, key_ring_status: KeyRingStatus, trust_level: OwnerTrustLevel, not_usable: bool, @@ -247,7 +243,7 @@ impl Recipient { _ => names.pop().unwrap(), }; - let trusts: HashMap<[u8; 20], OwnerTrustLevel> = crypto.get_all_trust_items()?; + let trusts: HashMap = crypto.get_all_trust_items()?; let fingerprint = real_key.fingerprint()?; @@ -339,7 +335,7 @@ impl Recipient { pub fn write_recipients_file( recipients: &[Self], recipients_file: &Path, - valid_gpg_signing_keys: &[[u8; 20]], + valid_gpg_signing_keys: &[Fingerprint], crypto: &(dyn crate::crypto::Crypto + Send), ) -> Result<()> { let mut file = fs::OpenOptions::new() @@ -411,7 +407,7 @@ impl Recipient { s: &Self, recipients_file: &Path, store_root_path: &Path, - valid_gpg_signing_keys: &[[u8; 20]], + valid_gpg_signing_keys: &[Fingerprint], crypto: &(dyn crate::crypto::Crypto + Send), ) -> Result<()> { let mut recipients: Vec = Self::all_recipients(recipients_file, crypto)?; @@ -446,7 +442,7 @@ impl Recipient { pub fn add_recipient_to_file( recipient: &Self, recipients_file: &Path, - valid_gpg_signing_keys: &[[u8; 20]], + valid_gpg_signing_keys: &[Fingerprint], crypto: &(dyn crate::crypto::Crypto + Send), ) -> Result<()> { let mut recipients: Vec = Self::all_recipients(recipients_file, crypto)?; diff --git a/src/tests/crypto.rs b/src/tests/crypto.rs index 5487be0..99615bd 100644 --- a/src/tests/crypto.rs +++ b/src/tests/crypto.rs @@ -4,8 +4,9 @@ use hex::FromHex; use sequoia_openpgp::{Cert, cert::CertBuilder, parse::Parse, serialize::Serialize}; use tempfile::tempdir; +use crate::crypto::Fingerprint; use crate::{ - crypto::{Crypto, CryptoImpl, Sequoia, slice_to_20_bytes}, + crypto::{Crypto, CryptoImpl, Sequoia}, signature::Recipient, }; @@ -31,20 +32,20 @@ pub fn crypto_impl_display() { #[test] pub fn slice_to_20_bytes_failure() { - let input = [3; 16]; + let input: [u8; 16] = [3; 16]; - let result = slice_to_20_bytes(&input); + let result = TryInto::::try_into(input.as_slice()); assert!(result.is_err()); } #[test] pub fn slice_to_20_bytes_success() { - let input = [3; 20]; + let input: &[u8] = &[3; 20]; - let result = slice_to_20_bytes(&input).unwrap(); + let result = input.try_into().unwrap(); - assert_eq!(input, result); + assert_eq!(Fingerprint::V4([3; 20]), result); } #[test] @@ -58,7 +59,7 @@ pub fn new_one_cert() { .generate() .unwrap(); - let f = slice_to_20_bytes(cert.fingerprint().as_bytes()).unwrap(); + let f = cert.fingerprint().as_bytes().try_into().unwrap(); let p = dir.path().join("share").join("ripasso").join("keys"); std::fs::create_dir_all(&p).unwrap(); @@ -141,11 +142,11 @@ HRCObAfLxAb07QD9FxvNNG1SDh3jzbvQZdL59p1ehgEniMmzGSALeBYbdtQBAILa ) } -fn fingerprint() -> [u8; 20] { - [ +fn fingerprint() -> Fingerprint { + Fingerprint::V4([ 0x7E, 0x06, 0x80, 0x70, 0xD5, 0xEF, 0x79, 0x4B, 0x00, 0xC8, 0xA9, 0xD9, 0x1D, 0x10, 0x8E, 0x6C, 0x07, 0xCB, 0xC4, 0x06, - ] + ]) } #[test] @@ -174,7 +175,9 @@ pub fn verify_sign_sequoia_git_commit() { let result = c.verify_sign( &data, &sig, - &[<[u8; 20]>::from_hex("7E068070D5EF794B00C8A9D91D108E6C07CBC406").unwrap()], + &[Fingerprint::V4( + <[u8; 20]>::from_hex("7E068070D5EF794B00C8A9D91D108E6C07CBC406").unwrap(), + )], ); assert!(result.is_ok()); @@ -206,7 +209,9 @@ pub fn verify_sign_sequoia_git_commit_invalid_signing_key() { let result = c.verify_sign( &data, &sig, - &[<[u8; 20]>::from_hex("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").unwrap()], + &[Fingerprint::V4( + <[u8; 20]>::from_hex("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").unwrap(), + )], ); assert!(result.is_err()); @@ -222,7 +227,7 @@ pub fn sign_string_sequoia() { .generate() .unwrap(); - let f = slice_to_20_bytes(cert.fingerprint().as_bytes()).unwrap(); + let f = cert.fingerprint().as_bytes().try_into().unwrap(); let mut c = Sequoia { user_key_id: f, @@ -253,7 +258,42 @@ pub fn sign_then_verify_sequoia_with_signing_keys() { .generate() .unwrap(); - let f = slice_to_20_bytes(cert.fingerprint().as_bytes()).unwrap(); + let f = cert.fingerprint().as_bytes().try_into().unwrap(); + + let mut c = Sequoia { + user_key_id: f, + key_ring: HashMap::new(), + user_home: user_home.path().to_path_buf(), + }; + + c.key_ring.insert(f, Arc::new(cert)); + + let sig = c + .sign_string( + "test", + &[], + &crate::crypto::FindSigningFingerprintStrategy::GPG, + ) + .unwrap(); + + let result = c.verify_sign("test".as_bytes(), sig.as_bytes(), &[f]); + + assert!(result.is_ok()); +} + +#[test] +pub fn sign_then_verify_sequoia_with_v6_signing_keys() { + let user_home = tempdir().unwrap(); + + let (cert, _) = CertBuilder::new() + .set_profile(sequoia_openpgp::Profile::RFC9580) + .unwrap() + .add_userid("someone@example.org") + .add_signing_subkey() + .generate() + .unwrap(); + + let f = cert.fingerprint().as_bytes().try_into().unwrap(); let mut c = Sequoia { user_key_id: f, @@ -286,7 +326,7 @@ pub fn sign_then_verify_sequoia_without_signing_keys() { .generate() .unwrap(); - let f = slice_to_20_bytes(cert.fingerprint().as_bytes()).unwrap(); + let f = cert.fingerprint().as_bytes().try_into().unwrap(); let mut c = Sequoia { user_key_id: f, @@ -319,7 +359,7 @@ pub fn encrypt_then_decrypt_sequoia() { .generate() .unwrap(); - let f = slice_to_20_bytes(cert.fingerprint().as_bytes()).unwrap(); + let f = cert.fingerprint().as_bytes().try_into().unwrap(); let mut c = Sequoia { user_key_id: f, @@ -337,3 +377,52 @@ pub fn encrypt_then_decrypt_sequoia() { assert_eq!("test", result); } + +fn cert_v6() -> Arc { + let (cert, _rev) = CertBuilder::new() + .set_profile(sequoia_openpgp::Profile::RFC9580) + .unwrap() + .add_userid("someone@example.org") + .add_signing_subkey() + .add_transport_encryption_subkey() + .add_storage_encryption_subkey() + .generate() + .unwrap(); + + Arc::new(cert) +} + +fn fingerprint_from_cert(cert: Arc) -> [u8; 32] { + <[u8; 32]>::try_from(cert.fingerprint().as_bytes()).unwrap() +} + +#[test] +fn test_fingerprint_from_cert() { + let cert = cert_v6(); + assert_eq!(32, fingerprint_from_cert(cert).iter().len()); +} + +#[test] +pub fn encrypt_then_decrypt_sequoia_v6() { + let user_home = tempdir().unwrap(); + + let cert = cert_v6(); + + let f = cert.fingerprint().as_bytes().try_into().unwrap(); + + let mut c = Sequoia { + user_key_id: f, + key_ring: HashMap::new(), + user_home: user_home.path().to_path_buf(), + }; + + c.key_ring.insert(f, cert); + + let r = Recipient::from(&hex::encode(f), &[], None, &c).unwrap(); + + let result = c.encrypt_string("test", &[r]).unwrap(); + + let result = c.decrypt_string(&result).unwrap(); + + assert_eq!("test", result); +} diff --git a/src/tests/pass.rs b/src/tests/pass.rs index e8fca62..d520105 100644 --- a/src/tests/pass.rs +++ b/src/tests/pass.rs @@ -12,12 +12,9 @@ use std::{env, fs::File, path::PathBuf}; use tempfile::tempdir; use super::*; -use crate::{ - crypto::slice_to_20_bytes, - test_helpers::{ - MockCrypto, UnpackedDir, count_recipients, generate_sequoia_cert, - generate_sequoia_cert_without_private_key, - }, +use crate::test_helpers::{ + MockCrypto, UnpackedDir, count_recipients, generate_sequoia_cert, + generate_sequoia_cert_without_private_key, }; impl PartialEq for Error { @@ -40,7 +37,7 @@ pub fn setup_store( ]; let mut key_ring = HashMap::new(); for u in &users { - key_ring.insert(slice_to_20_bytes(u.fingerprint().as_bytes())?, u.clone()); + key_ring.insert(u.fingerprint().as_bytes().try_into()?, u.clone()); } let store = PasswordStore { @@ -50,7 +47,7 @@ pub fn setup_store( passwords: [].to_vec(), style_file: None, crypto: Box::new(Sequoia::from_values( - slice_to_20_bytes(users[0].fingerprint().as_bytes())?, + users[0].fingerprint().as_bytes().try_into()?, key_ring, user_home, )), @@ -829,7 +826,7 @@ fn save_config_one_store() { &Some(home.path().to_path_buf()), &Some(style_file.path().to_path_buf()), &CryptoImpl::Sequoia, - &Some([0; 20]), + &Some(Fingerprint::V4([0; 20])), ) .unwrap(); @@ -890,7 +887,9 @@ fn save_config_one_store_with_fingerprint() { &Some(dir.path().to_path_buf()), &None, &CryptoImpl::Sequoia, - &Some(<[u8; 20]>::from_hex("7E068070D5EF794B00C8A9D91D108E6C07CBC406").unwrap()), + &Some(Fingerprint::V4( + <[u8; 20]>::from_hex("7E068070D5EF794B00C8A9D91D108E6C07CBC406").unwrap(), + )), ) .unwrap(); @@ -1100,8 +1099,7 @@ fn decrypt_secret() -> Result<()> { RepositoryStatus::NoRepo, ); - let crypto = - MockCrypto::new().with_decrypt_string_return("decrypt_secret unit test".to_owned()); + let crypto = MockCrypto::new().with_decrypt_string_return("decrypt_secret unit test"); let store = PasswordStore { name: "store_name".to_owned(), @@ -1179,8 +1177,7 @@ fn decrypt_password_multiline() -> Result<()> { RepositoryStatus::NoRepo, ); - let crypto = - MockCrypto::new().with_decrypt_string_return("row one\nrow two\nrow three".to_owned()); + let crypto = MockCrypto::new().with_decrypt_string_return("row one\nrow two\nrow three"); let store = PasswordStore { name: "store_name".to_owned(), @@ -1220,7 +1217,7 @@ fn mfa_setup(payload: String) -> Result<(tempfile::TempDir, PasswordEntry, Passw RepositoryStatus::NoRepo, ); - let crypto = MockCrypto::new().with_decrypt_string_return(payload); + let crypto = MockCrypto::new().with_decrypt_string_return(&payload); let store = PasswordStore { name: "store_name".to_owned(), @@ -1659,9 +1656,9 @@ fn test_verify_git_signature() -> Result<()> { let store = PasswordStore { name: "store_name".to_owned(), root: dir.dir(), - valid_gpg_signing_keys: vec![<[u8; 20]>::from_hex( + valid_gpg_signing_keys: vec![Fingerprint::V4(<[u8; 20]>::from_hex( "7E068070D5EF794B00C8A9D91D108E6C07CBC406", - )?], + )?)], passwords: [].to_vec(), style_file: None, crypto: Box::new(MockCrypto::new()), @@ -1715,9 +1712,9 @@ fn test_remove_and_commit() -> Result<()> { let store = PasswordStore { name: "store_name".to_owned(), root: dir.dir(), - valid_gpg_signing_keys: vec![<[u8; 20]>::from_hex( + valid_gpg_signing_keys: vec![Fingerprint::V4(<[u8; 20]>::from_hex( "7E068070D5EF794B00C8A9D91D108E6C07CBC406", - )?], + )?)], passwords: [].to_vec(), style_file: None, crypto: Box::new(MockCrypto::new()), @@ -1757,9 +1754,9 @@ fn test_verify_gpg_id_files_missing_sig_file() -> Result<()> { let store = PasswordStore { name: "store_name".to_owned(), root: td.path().to_path_buf(), - valid_gpg_signing_keys: vec![<[u8; 20]>::from_hex( + valid_gpg_signing_keys: vec![Fingerprint::V4(<[u8; 20]>::from_hex( "7E068070D5EF794B00C8A9D91D108E6C07CBC406", - )?], + )?)], passwords: [].to_vec(), style_file: None, crypto: Box::new(MockCrypto::new()), @@ -1790,9 +1787,9 @@ fn test_verify_gpg_id_files() -> Result<()> { let store = PasswordStore { name: "store_name".to_owned(), root: td.path().to_path_buf(), - valid_gpg_signing_keys: vec![<[u8; 20]>::from_hex( + valid_gpg_signing_keys: vec![Fingerprint::V4(<[u8; 20]>::from_hex( "7E068070D5EF794B00C8A9D91D108E6C07CBC406", - )?], + )?)], passwords: [].to_vec(), style_file: None, crypto: Box::new(MockCrypto::new()), @@ -1875,7 +1872,7 @@ fn test_verify_gpg_id_files_untrusted_key_in_keyring() { .add_signing_subkey() .generate() .unwrap(); - let sofp = slice_to_20_bytes(store_owner.fingerprint().as_bytes()).unwrap(); + let sofp = store_owner.fingerprint().as_bytes().try_into().unwrap(); let (unrelated_user, _) = CertBuilder::new() .add_userid("unrelated_user@example.org") .add_signing_subkey() @@ -2011,7 +2008,7 @@ fn test_new_password_file_encryption_failure() -> Result<()> { valid_gpg_signing_keys: vec![], passwords: [].to_vec(), style_file: None, - crypto: Box::new(MockCrypto::new().with_encrypt_error("unit test error".to_owned())), + crypto: Box::new(MockCrypto::new().with_encrypt_error("unit test error")), user_home: None, }; diff --git a/src/tests/signature.rs b/src/tests/signature.rs index 3585fbc..483d8c3 100644 --- a/src/tests/signature.rs +++ b/src/tests/signature.rs @@ -1,5 +1,6 @@ use hex::FromHex; +use crate::crypto::Fingerprint; use crate::{ pass::{KeyRingStatus, OwnerTrustLevel, Recipient}, signature::{Comment, parse_signing_keys}, @@ -9,14 +10,8 @@ use crate::{ #[test] fn test_parse_signing_keys_two_keys() { let crypto = MockCrypto::new() - .with_get_key_result( - "7E068070D5EF794B00C8A9D91D108E6C07CBC406".to_owned(), - MockKey::new(), - ) - .with_get_key_result( - "E6A7D758338EC2EF2A8A9F4EE7E3DB4B3217482F".to_owned(), - MockKey::new(), - ); + .with_get_key_result("7E068070D5EF794B00C8A9D91D108E6C07CBC406", MockKey::new()) + .with_get_key_result("E6A7D758338EC2EF2A8A9F4EE7E3DB4B3217482F", MockKey::new()); let file_content = "7E068070D5EF794B00C8A9D91D108E6C07CBC406,E6A7D758338EC2EF2A8A9F4EE7E3DB4B3217482F" @@ -25,44 +20,75 @@ fn test_parse_signing_keys_two_keys() { let result = parse_signing_keys(&Some(file_content), &crypto).unwrap(); assert_eq!(2, result.len()); - assert!( - result.contains(&<[u8; 20]>::from_hex("7E068070D5EF794B00C8A9D91D108E6C07CBC406").unwrap()) - ); - assert!( - result.contains(&<[u8; 20]>::from_hex("E6A7D758338EC2EF2A8A9F4EE7E3DB4B3217482F").unwrap()) - ); + assert!(result.contains(&Fingerprint::V4( + <[u8; 20]>::from_hex("7E068070D5EF794B00C8A9D91D108E6C07CBC406").unwrap() + ))); + assert!(result.contains(&Fingerprint::V4( + <[u8; 20]>::from_hex("E6A7D758338EC2EF2A8A9F4EE7E3DB4B3217482F").unwrap() + ))); } #[test] fn test_parse_signing_keys_two_keys_with_0x() { + let crypto = MockCrypto::new() + .with_get_key_result("0x7E068070D5EF794B00C8A9D91D108E6C07CBC406", MockKey::new()) + .with_get_key_result("0xE6A7D758338EC2EF2A8A9F4EE7E3DB4B3217482F", MockKey::new()); + + let file_content = + "0x7E068070D5EF794B00C8A9D91D108E6C07CBC406,0xE6A7D758338EC2EF2A8A9F4EE7E3DB4B3217482F" + .to_owned(); + + let result = parse_signing_keys(&Some(file_content), &crypto).unwrap(); + + assert_eq!(2, result.len()); + assert!(result.contains(&Fingerprint::V4( + <[u8; 20]>::from_hex("7E068070D5EF794B00C8A9D91D108E6C07CBC406").unwrap() + ))); + assert!(result.contains(&Fingerprint::V4( + <[u8; 20]>::from_hex("E6A7D758338EC2EF2A8A9F4EE7E3DB4B3217482F").unwrap() + ))); +} + +#[test] +fn test_parse_signing_keys_two_v6_keys_with_0x() { let crypto = MockCrypto::new() .with_get_key_result( - "0x7E068070D5EF794B00C8A9D91D108E6C07CBC406".to_owned(), + "0x211d3c737301f0540dbdfee64485573555beedac5bd5fe1d53293f6dcb0150fb", MockKey::new(), ) .with_get_key_result( - "0xE6A7D758338EC2EF2A8A9F4EE7E3DB4B3217482F".to_owned(), + "0x338b0a9792bd6c858bfecf527080123e69c3a4b787cb2b401bb5565465a9cbfb", MockKey::new(), ); let file_content = - "0x7E068070D5EF794B00C8A9D91D108E6C07CBC406,0xE6A7D758338EC2EF2A8A9F4EE7E3DB4B3217482F" + "0x211d3c737301f0540dbdfee64485573555beedac5bd5fe1d53293f6dcb0150fb,0x338b0a9792bd6c858bfecf527080123e69c3a4b787cb2b401bb5565465a9cbfb" .to_owned(); let result = parse_signing_keys(&Some(file_content), &crypto).unwrap(); assert_eq!(2, result.len()); assert!( - result.contains(&<[u8; 20]>::from_hex("7E068070D5EF794B00C8A9D91D108E6C07CBC406").unwrap()) + result.contains(&Fingerprint::V6( + <[u8; 32]>::from_hex( + "211d3c737301f0540dbdfee64485573555beedac5bd5fe1d53293f6dcb0150fb" + ) + .unwrap() + )) ); assert!( - result.contains(&<[u8; 20]>::from_hex("E6A7D758338EC2EF2A8A9F4EE7E3DB4B3217482F").unwrap()) + result.contains(&Fingerprint::V6( + <[u8; 32]>::from_hex( + "338b0a9792bd6c858bfecf527080123e69c3a4b787cb2b401bb5565465a9cbfb" + ) + .unwrap() + )) ); } #[test] fn parse_signing_keys_key_error() { - let crypto = MockCrypto::new().with_get_key_error("unit test error".to_owned()); + let crypto = MockCrypto::new().with_get_key_error("unit test error"); let file_content = "0x7E068070D5EF794B00C8A9D91D108E6C07CBC406,0xE6A7D758338EC2EF2A8A9F4EE7E3DB4B3217482F" @@ -93,7 +119,7 @@ fn parse_signing_keys_short() { #[test] fn recipient_from_key_error() { - let crypto = MockCrypto::new().with_get_key_error("unit test error".to_owned()); + let crypto = MockCrypto::new().with_get_key_error("unit test error"); let result = Recipient::from("0x1D108E6C07CBC406", &[], None, &crypto); @@ -105,9 +131,11 @@ fn recipient_from_key_error() { #[test] fn all_recipients() { let crypto = MockCrypto::new().with_get_key_result( - "0x1D108E6C07CBC406".to_owned(), + "0x1D108E6C07CBC406", MockKey::from_args( - <[u8; 20]>::from_hex("7E068070D5EF794B00C8A9D91D108E6C07CBC406").unwrap(), + Fingerprint::V4( + <[u8; 20]>::from_hex("7E068070D5EF794B00C8A9D91D108E6C07CBC406").unwrap(), + ), vec!["Alexander Kjäll ".to_owned()], ), ); @@ -132,9 +160,11 @@ fn all_recipients() { #[test] fn all_recipients_with_one_comment_line() { let crypto = MockCrypto::new().with_get_key_result( - "0x1D108E6C07CBC406".to_owned(), + "0x1D108E6C07CBC406", MockKey::from_args( - <[u8; 20]>::from_hex("7E068070D5EF794B00C8A9D91D108E6C07CBC406").unwrap(), + Fingerprint::V4( + <[u8; 20]>::from_hex("7E068070D5EF794B00C8A9D91D108E6C07CBC406").unwrap(), + ), vec!["Alexander Kjäll ".to_owned()], ), ); @@ -164,9 +194,11 @@ fn all_recipients_with_one_comment_line() { #[test] fn all_recipients_with_multiple_comment_lines() { let crypto = MockCrypto::new().with_get_key_result( - "0x1D108E6C07CBC406".to_owned(), + "0x1D108E6C07CBC406", MockKey::from_args( - <[u8; 20]>::from_hex("7E068070D5EF794B00C8A9D91D108E6C07CBC406").unwrap(), + Fingerprint::V4( + <[u8; 20]>::from_hex("7E068070D5EF794B00C8A9D91D108E6C07CBC406").unwrap(), + ), vec!["Alexander Kjäll ".to_owned()], ), ); @@ -200,9 +232,11 @@ fn all_recipients_with_multiple_comment_lines() { #[test] fn all_recipients_with_comment_lines_pre_and_post() { let crypto = MockCrypto::new().with_get_key_result( - "0x1D108E6C07CBC406".to_owned(), + "0x1D108E6C07CBC406", MockKey::from_args( - <[u8; 20]>::from_hex("7E068070D5EF794B00C8A9D91D108E6C07CBC406").unwrap(), + Fingerprint::V4( + <[u8; 20]>::from_hex("7E068070D5EF794B00C8A9D91D108E6C07CBC406").unwrap(), + ), vec!["Alexander Kjäll ".to_owned()], ), ); @@ -234,7 +268,7 @@ fn all_recipients_with_comment_lines_pre_and_post() { #[test] fn all_recipients_error() { - let crypto = MockCrypto::new().with_get_key_error("unit test error".to_owned()); + let crypto = MockCrypto::new().with_get_key_error("unit test error"); let dir = tempfile::tempdir().unwrap(); let file = dir.path().join(".gpg-id"); @@ -461,10 +495,11 @@ fn write_recipients_file_one_and_signed() { let recipients_file = dir.path().join(".gpg-id"); let signature_file = dir.path().join(".gpg-id.sig"); - let valid_gpg_signing_keys = - vec![<[u8; 20]>::from_hex("7E068070D5EF794B00C8A9D91D108E6C07CBC406").unwrap()]; + let valid_gpg_signing_keys = vec![Fingerprint::V4( + <[u8; 20]>::from_hex("7E068070D5EF794B00C8A9D91D108E6C07CBC406").unwrap(), + )]; - let crypto = MockCrypto::new().with_sign_string_return("unit test sign string".to_owned()); + let crypto = MockCrypto::new().with_sign_string_return("unit test sign string"); assert!(!recipients_file.exists()); assert!(!signature_file.exists()); @@ -545,11 +580,11 @@ fn remove_recipient_from_file_two() { let crypto = MockCrypto::new() .with_get_key_result( - r.key_id.clone(), + &r.key_id, MockKey::from_args(r.fingerprint.unwrap(), vec![r.name.clone()]), ) .with_get_key_result( - r2.key_id.clone(), + &r2.key_id, MockKey::from_args(r2.fingerprint.unwrap(), vec![r2.name.clone()]), ); @@ -594,9 +629,9 @@ fn remove_recipient_from_file_same_key_id_different_fingerprint() { post_comment: None, }, key_id: "DF0C3D316B7312D5".to_owned(), - fingerprint: Some( + fingerprint: Some(Fingerprint::V4( <[u8; 20]>::from_hex("DB07DAC5B3882EAB659E1D2FDF0C3D316B7312D5").unwrap(), - ), + )), key_ring_status: KeyRingStatus::InKeyRing, trust_level: OwnerTrustLevel::Ultimate, not_usable: false, @@ -608,9 +643,9 @@ fn remove_recipient_from_file_same_key_id_different_fingerprint() { post_comment: None, }, key_id: "DF0C3D316B7312D5".to_owned(), - fingerprint: Some( + fingerprint: Some(Fingerprint::V4( <[u8; 20]>::from_hex("88283D2EF664DD5F6AEBB51CDF0C3D316B7312D5").unwrap(), - ), + )), key_ring_status: KeyRingStatus::InKeyRing, trust_level: OwnerTrustLevel::Ultimate, not_usable: false, @@ -626,16 +661,20 @@ fn remove_recipient_from_file_same_key_id_different_fingerprint() { let crypto = MockCrypto::new() .with_get_key_result( - "0xDB07DAC5B3882EAB659E1D2FDF0C3D316B7312D5".to_owned(), + "0xDB07DAC5B3882EAB659E1D2FDF0C3D316B7312D5", MockKey::from_args( - <[u8; 20]>::from_hex("DB07DAC5B3882EAB659E1D2FDF0C3D316B7312D5").unwrap(), + Fingerprint::V4( + <[u8; 20]>::from_hex("DB07DAC5B3882EAB659E1D2FDF0C3D316B7312D5").unwrap(), + ), vec!["Alexander Kjäll ".to_owned()], ), ) .with_get_key_result( - "0x88283D2EF664DD5F6AEBB51CDF0C3D316B7312D5".to_owned(), + "0x88283D2EF664DD5F6AEBB51CDF0C3D316B7312D5", MockKey::from_args( - <[u8; 20]>::from_hex("88283D2EF664DD5F6AEBB51CDF0C3D316B7312D5").unwrap(), + Fingerprint::V4( + <[u8; 20]>::from_hex("88283D2EF664DD5F6AEBB51CDF0C3D316B7312D5").unwrap(), + ), vec!["Alexander Kjäll ".to_owned()], ), ); @@ -685,11 +724,11 @@ fn add_recipient_from_file_one_plus_one() { let crypto = MockCrypto::new() .with_get_key_result( - r.key_id.clone(), + &r.key_id, MockKey::from_args(r.fingerprint.unwrap(), vec![r.name.clone()]), ) .with_get_key_result( - r2.key_id.clone(), + &r2.key_id, MockKey::from_args(r2.fingerprint.unwrap(), vec![r2.name.clone()]), ); @@ -770,9 +809,9 @@ fn recipient_one_none() { post_comment: None, }, key_id: "DF0C3D316B7312D5".to_owned(), - fingerprint: Some( + fingerprint: Some(Fingerprint::V4( <[u8; 20]>::from_hex("DB07DAC5B3882EAB659E1D2FDF0C3D316B7312D5").unwrap(), - ), + )), key_ring_status: KeyRingStatus::InKeyRing, trust_level: OwnerTrustLevel::Ultimate, not_usable: false, @@ -803,9 +842,9 @@ fn recipient_same_fingerprint_different_key_id() { post_comment: None, }, key_id: "DF0C3D316B7312D5".to_owned(), - fingerprint: Some( + fingerprint: Some(Fingerprint::V4( <[u8; 20]>::from_hex("DB07DAC5B3882EAB659E1D2FDF0C3D316B7312D5").unwrap(), - ), + )), key_ring_status: KeyRingStatus::InKeyRing, trust_level: OwnerTrustLevel::Ultimate, not_usable: false, @@ -817,9 +856,9 @@ fn recipient_same_fingerprint_different_key_id() { post_comment: None, }, key_id: "DB07DAC5B3882EAB659E1D2FDF0C3D316B7312D5".to_owned(), - fingerprint: Some( + fingerprint: Some(Fingerprint::V4( <[u8; 20]>::from_hex("DB07DAC5B3882EAB659E1D2FDF0C3D316B7312D5").unwrap(), - ), + )), key_ring_status: KeyRingStatus::InKeyRing, trust_level: OwnerTrustLevel::Ultimate, not_usable: false, diff --git a/src/tests/test_helpers.rs b/src/tests/test_helpers.rs index d33a300..7270187 100644 --- a/src/tests/test_helpers.rs +++ b/src/tests/test_helpers.rs @@ -21,6 +21,7 @@ use sequoia_openpgp::{ }; use tar::Archive; +use crate::crypto::Fingerprint; use crate::{ crypto::{Crypto, CryptoImpl, FindSigningFingerprintStrategy, Key, VerificationError}, error::{Error, Result}, @@ -76,7 +77,7 @@ fn get_testres_path() -> PathBuf { #[derive(Clone)] pub struct MockKey { - fingerprint: [u8; 20], + fingerprint: Fingerprint, user_id_names: Vec, } @@ -85,7 +86,7 @@ impl Key for MockKey { self.user_id_names.clone() } - fn fingerprint(&self) -> Result<[u8; 20]> { + fn fingerprint(&self) -> Result { Ok(self.fingerprint) } @@ -103,12 +104,14 @@ impl Default for MockKey { impl MockKey { pub fn new() -> MockKey { MockKey { - fingerprint: <[u8; 20]>::from_hex("7E068070D5EF794B00C8A9D91D108E6C07CBC406").unwrap(), + fingerprint: Fingerprint::V4( + <[u8; 20]>::from_hex("7E068070D5EF794B00C8A9D91D108E6C07CBC406").unwrap(), + ), user_id_names: vec!["Alexander Kjäll ".to_owned()], } } - pub fn from_args(fingerprint: [u8; 20], user_id_names: Vec) -> MockKey { + pub fn from_args(fingerprint: Fingerprint, user_id_names: Vec) -> MockKey { MockKey { user_id_names, fingerprint, @@ -158,32 +161,32 @@ impl MockCrypto { self } - pub fn with_decrypt_string_return(mut self, data: String) -> MockCrypto { - self.decrypt_string_return = Some(data); + pub fn with_decrypt_string_return(mut self, data: &str) -> MockCrypto { + self.decrypt_string_return = Some(data.to_string()); self } - pub fn with_encrypt_error(mut self, err_str: String) -> MockCrypto { - self.encrypt_string_error = Some(err_str); + pub fn with_encrypt_error(mut self, err_str: &str) -> MockCrypto { + self.encrypt_string_error = Some(err_str.to_string()); self } - pub fn with_get_key_error(mut self, err_str: String) -> MockCrypto { - self.get_key_string_error = Some(err_str); + pub fn with_get_key_error(mut self, err_str: &str) -> MockCrypto { + self.get_key_string_error = Some(err_str.to_string()); self } - pub fn with_get_key_result(mut self, key_id: String, key: MockKey) -> MockCrypto { - self.get_key_answers.insert(key_id, key); + pub fn with_get_key_result(mut self, key_id: &str, key: MockKey) -> MockCrypto { + self.get_key_answers.insert(key_id.to_string(), key); self } - pub fn with_sign_string_return(mut self, sign_str: String) -> MockCrypto { - self.sign_string_return = Some(sign_str); + pub fn with_sign_string_return(mut self, sign_str: &str) -> MockCrypto { + self.sign_string_return = Some(sign_str.to_string()); self } @@ -213,7 +216,7 @@ impl Crypto for MockCrypto { fn sign_string( &self, _: &str, - _: &[[u8; 20]], + _: &[Fingerprint], _: &FindSigningFingerprintStrategy, ) -> Result { self.sign_called.replace(true); @@ -227,7 +230,7 @@ impl Crypto for MockCrypto { &self, _: &[u8], _: &[u8], - _: &[[u8; 20]], + _: &[Fingerprint], ) -> std::result::Result { self.verify_called.replace(true); Err(VerificationError::SignatureFromWrongRecipient) @@ -257,7 +260,7 @@ impl Crypto for MockCrypto { } } - fn get_all_trust_items(&self) -> Result> { + fn get_all_trust_items(&self) -> Result> { Ok(HashMap::new()) } @@ -265,7 +268,7 @@ impl Crypto for MockCrypto { CryptoImpl::GpgMe } - fn own_fingerprint(&self) -> Option<[u8; 20]> { + fn own_fingerprint(&self) -> Option { None } } @@ -278,9 +281,9 @@ pub fn recipient_alex() -> Recipient { post_comment: None, }, key_id: "1D108E6C07CBC406".to_owned(), - fingerprint: Some( + fingerprint: Some(Fingerprint::V4( <[u8; 20]>::from_hex("7E068070D5EF794B00C8A9D91D108E6C07CBC406").unwrap(), - ), + )), key_ring_status: KeyRingStatus::InKeyRing, trust_level: OwnerTrustLevel::Ultimate, not_usable: false, @@ -294,9 +297,9 @@ pub fn recipient_alex_old() -> Recipient { post_comment: None, }, key_id: "DF0C3D316B7312D5".to_owned(), - fingerprint: Some( + fingerprint: Some(Fingerprint::V4( <[u8; 20]>::from_hex("DB07DAC5B3882EAB659E1D2FDF0C3D316B7312D5").unwrap(), - ), + )), key_ring_status: KeyRingStatus::InKeyRing, trust_level: OwnerTrustLevel::Ultimate, not_usable: false, @@ -310,7 +313,9 @@ pub fn recipient_from_cert(cert: &Cert) -> Recipient { post_comment: None, }, key_id: cert.fingerprint().to_hex(), - fingerprint: Some(<[u8; 20]>::from_hex(cert.fingerprint().to_hex()).unwrap()), + fingerprint: Some(Fingerprint::V4( + <[u8; 20]>::from_hex(cert.fingerprint().to_hex()).unwrap(), + )), key_ring_status: KeyRingStatus::InKeyRing, trust_level: OwnerTrustLevel::Ultimate, not_usable: false,