diff --git a/spectre-cli/src/main.rs b/spectre-cli/src/main.rs index 8059b65303152c1c4cae332574a25fbc557d12d9..b88aef01f17e23658b2ad5190adecff70fe535a5 100644 --- a/spectre-cli/src/main.rs +++ b/spectre-cli/src/main.rs @@ -137,16 +137,9 @@ if args.print { println!("{pw}"); return; } - match copy_to_clipboard(pw) { - Ok(_) => println!("Password copied to clipboard"), - Err(e) => println!("Failed to copy password: {e}") - } -} - -fn copy_to_clipboard(text: String) -> Result<(), Box> { - let mut clipboard = Clipboard::new()?; - clipboard.set_text(text)?; - Ok(()) + let mut clipboard = Clipboard::new().unwrap(); + clipboard.set_text(pw).unwrap(); + println!("Password copied to clipboard") } #[cfg(test)] diff --git a/spectre/src/lib.rs b/spectre/src/lib.rs index 90b74f304bd8fc6033ded69f6b7bd383b0e5b1cd..94ed900433b905ea83e3b8d4adbf7c2c1c5333e4 100644 --- a/spectre/src/lib.rs +++ b/spectre/src/lib.rs @@ -7,20 +7,6 @@ pub mod scope; pub mod template; -/// Spectre implements the Spectre/Masterpassword cipher -/// -/// # Examples -/// -/// ``` -/// use spectre::{scope, SiteOptions, Spectre}; -/// let s = Spectre::new( -/// "Robert Lee Mitchell", -/// "banana colored duckling", -/// scope::SimpleScoper::default(), -/// ); -/// let pw = s.site("masterpasswordapp.com", SiteOptions::default()); -/// assert_eq!(pw, "Jejr5[RepuSosp") -/// ``` #[derive(Debug)] pub struct Spectre { key: String, @@ -28,12 +14,11 @@ scoper: Box, } impl Spectre { - /// Create a new Spectre instance that can be used to generate site passwords. - pub fn new(name: impl Into, secret: impl Into, scoper: impl scope::Scoper + 'static) -> Self { - let key = user_key(name, secret, &scoper); + pub fn new(name: String, secret: String, scoper: impl scope::Scoper + 'static) -> Self { + let key = user_key(&name, &secret, &scoper); Self { scoper: Box::new(scoper), - key, + key: key, } } @@ -51,19 +36,16 @@ let result = hmac.finalize(); result.into_bytes().to_vec() } - /// Generate a site password for a given site_name - /// - /// Generally this is the domain, e.g. `example.com` - pub fn site(&self, site_name: impl Into, opts: SiteOptions) -> String { + pub fn site(&self, site_name: String, opts: SiteOptions) -> String { let scope = opts.scope.unwrap_or(scope::Scope::AUTHENTICATION); let template_type = opts.template.unwrap_or(scope.default_template()); let templates = template::templates(template_type); let counter = opts.counter.unwrap_or(1); - let site_key = self.site_key(site_name.into(), counter, scope); + let site_key = self.site_key(site_name, counter, scope); let site_byte = site_key[0] as u32; let templates_len = templates.len() as u32; let template_idx = site_byte % templates_len; - let template = templates[template_idx as usize]; + let template = templates[template_idx as usize].clone(); let mut out = String::new(); for (i, b) in template.as_bytes().iter().enumerate() { let chars = template::chars(b).to_string(); @@ -84,8 +66,7 @@ pub template: Option, pub counter: Option, } -fn user_key(name: impl Into, secret: impl Into, scoper: &impl scope::Scoper) -> String { - let name = name.into(); +fn user_key(name: &str, secret: &str, scoper: &impl scope::Scoper) -> String { let key = scoper.scope(scope::Scope::AUTHENTICATION); let out: &mut [u8] = &mut [0u8; 64]; let mut salt = Vec::new(); @@ -93,7 +74,7 @@ salt.extend_from_slice(key.as_bytes()); salt.extend_from_slice(&(name.len() as u32).to_be_bytes()); salt.extend_from_slice(name.as_bytes()); scrypt( - secret.into().as_bytes(), + secret.as_bytes(), &salt, &Params::new(15, 8, 2, 64).unwrap(), out, @@ -106,22 +87,31 @@ #[cfg(test)] mod spectre_tests { use super::*; use serde::Deserialize; - use crate::scope::{Scope, SimpleScoper}; - use crate::template::Template; + + #[test] + fn website_sanity() { + let s = Spectre::new( + "Robert Lee Mitchell".to_string(), + "banana colored duckling".to_string(), + scope::SimpleScoper::default(), + ); + let pw = s.site("masterpasswordapp.com".to_string(), SiteOptions::default()); + assert_eq!(pw, "Jejr5[RepuSosp") + } #[test] fn custom() { - let scoper = SimpleScoper::new("com.jojodev.jolheiser"); + let scoper = scope::SimpleScoper::new("com.jojodev.jolheiser".to_string()); let s = Spectre::new( - "Robert Lee Mitchell", - "banana colored duckling", + "Robert Lee Mitchell".to_string(), + "banana colored duckling".to_string(), scoper, ); let pw = s.site( - "jojodev.com", + "jojodev.com".to_string(), SiteOptions { - scope: Some(Scope::IDENTIFICATION), - template: Some(Template::Maximum), + scope: Some(scope::Scope::IDENTIFICATION), + template: Some(template::Template::Maximum), counter: Some(2), }, ); @@ -179,13 +169,13 @@ SiteOptions { scope: Some( case.key_purpose .unwrap_or("Authentication".to_string()) - .parse::() + .parse::() .unwrap(), ), template: Some( case.result_type .unwrap_or("Long".to_string()) - .parse::