From 8d7cbd4b3305b6294a85fa092557de6ce3d51cea Mon Sep 17 00:00:00 2001 From: Fina Wilke Date: Sun, 25 Aug 2024 22:03:36 +0200 Subject: [PATCH] wordlist: Return a properly parsed password --- src/core.rs | 32 ++++++++++++++++++++++++--- src/core/wordlist.rs | 51 ++++++++++++++++++++++---------------------- 2 files changed, 54 insertions(+), 29 deletions(-) diff --git a/src/core.rs b/src/core.rs index 2914867f..f66c173c 100644 --- a/src/core.rs +++ b/src/core.rs @@ -136,9 +136,9 @@ impl MailboxConnection { /// # Ok(()) })} /// ``` pub async fn create(config: AppConfig, code_length: usize) -> Result { - Self::create_with_password( + Self::create_with_validated_password( config, - &wordlist::default_wordlist(code_length).choose_words(), + wordlist::default_wordlist(code_length).choose_words(), ) .await } @@ -159,14 +159,40 @@ impl MailboxConnection { /// let mailbox_connection = MailboxConnection::create_with_password(config, "secret").await?; /// # Ok(()) })} /// ``` + /// + /// TODO: Replace this with create_with_validated_password pub async fn create_with_password( config: AppConfig, password: &str, + ) -> Result { + let password = password.parse().map_err(ParseCodeError::from)?; + Self::create_with_validated_password(config, password).await + } + + /// Create a connection to a mailbox which is configured with a `Code` containing the nameplate and the given password. + /// + /// # Arguments + /// + /// * `config`: Application configuration + /// * `password`: Free text password which will be appended to the nameplate number to form the `Code` + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> eyre::Result<()> { async_std::task::block_on(async { + /// use magic_wormhole::{transfer::APP_CONFIG, MailboxConnection}; + /// let config = APP_CONFIG; + /// let password: Password = "secret".parse()?; + /// let mailbox_connection = MailboxConnection::create_with_password(config, password).await?; + /// # Ok(()) })} + /// ``` + async fn create_with_validated_password( + config: AppConfig, + password: Password, ) -> Result { let (mut server, welcome) = RendezvousServer::connect(&config.id, &config.rendezvous_url).await?; let (nameplate, mailbox) = server.allocate_claim_open().await?; - let password = password.parse().map_err(ParseCodeError::from)?; let code = Code::from_components(nameplate, password); Ok(MailboxConnection { diff --git a/src/core/wordlist.rs b/src/core/wordlist.rs index d20e6acf..9ba082ce 100644 --- a/src/core/wordlist.rs +++ b/src/core/wordlist.rs @@ -2,6 +2,8 @@ use rand::{rngs::OsRng, seq::SliceRandom}; use serde_json::{self, Value}; use std::fmt; +use super::Password; + #[derive(PartialEq)] pub struct Wordlist { pub num_words: usize, @@ -55,7 +57,7 @@ impl Wordlist { completions } - pub fn choose_words(&self) -> String { + pub fn choose_words(&self) -> Password { let mut rng = OsRng; let components: Vec = self .words @@ -64,7 +66,10 @@ impl Wordlist { .take(self.num_words) .map(|words| words.choose(&mut rng).unwrap().to_string()) .collect(); - components.join("-") + #[allow(unsafe_code)] + unsafe { + Password::new_unchecked(components.join("-")) + } } #[cfg(feature = "entropy")] @@ -132,23 +137,21 @@ mod test { assert_eq!(d.words[1][255], "zulu"); } - fn vecstrings(all: &str) -> Vec { + fn vec_strs(all: &str) -> Vec<&str> { all.split_whitespace() - .map(|s| { - if s == "." { - String::from("") - } else { - s.to_string() - } - }) + .map(|s| if s == "." { "" } else { s }) .collect() } + fn vec_strings(all: &str) -> Vec { + vec_strs(all).iter().map(|s| (*s).to_owned()).collect() + } + #[test] fn test_completion() { let words: Vec> = vec![ - vecstrings("purple green yellow"), - vecstrings("sausages seltzer snobol"), + vec_strings("purple green yellow"), + vec_strings("sausages seltzer snobol"), ]; let w = Wordlist::new(2, words); @@ -160,40 +163,36 @@ mod test { #[test] fn test_choose_words() { - let few_words: Vec> = vec![vecstrings("purple"), vecstrings("sausages")]; + let few_words: Vec> = vec![vec_strings("purple"), vec_strings("sausages")]; let w = Wordlist::new(2, few_words.clone()); - assert_eq!(w.choose_words(), "purple-sausages"); + assert_eq!(w.choose_words().as_ref(), "purple-sausages"); let w = Wordlist::new(3, few_words.clone()); - assert_eq!(w.choose_words(), "purple-sausages-purple"); + assert_eq!(w.choose_words().as_ref(), "purple-sausages-purple"); let w = Wordlist::new(4, few_words); - assert_eq!(w.choose_words(), "purple-sausages-purple-sausages"); + assert_eq!(w.choose_words().as_ref(), "purple-sausages-purple-sausages"); } #[test] fn test_choose_more_words() { - let more_words: Vec> = - vec![vecstrings("purple yellow"), vecstrings("sausages")]; + let more_words = vec![vec_strings("purple yellow"), vec_strings("sausages")]; - let expected2 = vecstrings("purple-sausages yellow-sausages"); - let expected3: Vec = vec![ + let expected2 = vec_strs("purple-sausages yellow-sausages"); + let expected3 = vec![ "purple-sausages-purple", "yellow-sausages-purple", "purple-sausages-yellow", "yellow-sausages-yellow", - ] - .iter() - .map(|s| s.to_string()) - .collect(); + ]; let w = Wordlist::new(2, more_words.clone()); for _ in 0..20 { - assert!(expected2.contains(&w.choose_words())); + assert!(expected2.contains(&w.choose_words().as_ref())); } let w = Wordlist::new(3, more_words); for _ in 0..20 { - assert!(expected3.contains(&w.choose_words())); + assert!(expected3.contains(&w.choose_words().as_ref())); } }