From 4aa2011e0e25e06edf0db3d775f0460420b6955d Mon Sep 17 00:00:00 2001 From: Martin Junghanns Date: Sun, 5 Feb 2023 16:56:09 +0100 Subject: [PATCH 1/3] Fix CI build badge (#683) see https://github.com/badges/shields/issues/8671 also, nice talk at FOSDEM :) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ccc2aa49c2..7d6e83de1b 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@

- + From 3abc96fafe052f3c68ac7d1ae18e4fafee8e1f34 Mon Sep 17 00:00:00 2001 From: Ellie Huxtable Date: Mon, 6 Feb 2023 12:59:01 +0100 Subject: [PATCH 2/3] =?UTF-8?q?Try=20to=20make=20clippy=20happy=20?= =?UTF-8?q?=F0=9F=A5=BA=20(#686)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Try to make clippy happy 🥺 * Fmt * I missed one (can't run clippy locally on airport wifi...) --- atuin-client/src/api_client.rs | 8 ++++---- atuin-client/src/database.rs | 2 +- atuin-client/src/import/fish.rs | 2 +- atuin-client/src/settings.rs | 5 ++--- src/command/client/history.rs | 3 +-- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/atuin-client/src/api_client.rs b/atuin-client/src/api_client.rs index b20d9378fc..44375c06f6 100644 --- a/atuin-client/src/api_client.rs +++ b/atuin-client/src/api_client.rs @@ -42,14 +42,14 @@ pub async fn register( map.insert("email", email); map.insert("password", password); - let url = format!("{}/user/{}", address, username); + let url = format!("{address}/user/{username}"); let resp = reqwest::get(url).await?; if resp.status().is_success() { bail!("username already in use"); } - let url = format!("{}/register", address); + let url = format!("{address}/register"); let client = reqwest::Client::new(); let resp = client .post(url) @@ -68,7 +68,7 @@ pub async fn register( } pub async fn login(address: &str, req: LoginRequest) -> Result { - let url = format!("{}/login", address); + let url = format!("{address}/login"); let client = reqwest::Client::new(); let resp = client @@ -111,7 +111,7 @@ pub async fn latest_version() -> Result { impl<'a> Client<'a> { pub fn new(sync_addr: &'a str, session_token: &'a str, key: String) -> Result { let mut headers = HeaderMap::new(); - headers.insert(AUTHORIZATION, format!("Token {}", session_token).parse()?); + headers.insert(AUTHORIZATION, format!("Token {session_token}").parse()?); Ok(Client { sync_addr, diff --git a/atuin-client/src/database.rs b/atuin-client/src/database.rs index 874c440765..d0e696c45b 100644 --- a/atuin-client/src/database.rs +++ b/atuin-client/src/database.rs @@ -448,7 +448,7 @@ impl Database for Sqlite { } else if let Some(term) = query_part.strip_prefix('\'') { format!("{glob}{term}{glob}") } else if is_inverse { - format!("{glob}{term}{glob}", term = query_part) + format!("{glob}{query_part}{glob}") } else { query_part.split("").join(glob) }; diff --git a/atuin-client/src/import/fish.rs b/atuin-client/src/import/fish.rs index 84f4a2f14e..850814c7ab 100644 --- a/atuin-client/src/import/fish.rs +++ b/atuin-client/src/import/fish.rs @@ -31,7 +31,7 @@ fn default_histpath() -> Result { }; let mut histpath = data.join("fish"); - histpath.push(format!("{}_history", session)); + histpath.push(format!("{session}_history")); if histpath.exists() { Ok(histpath) diff --git a/atuin-client/src/settings.rs b/atuin-client/src/settings.rs index 379237c6c9..3cefe1cb26 100644 --- a/atuin-client/src/settings.rs +++ b/atuin-client/src/settings.rs @@ -262,9 +262,8 @@ impl Settings { let data_dir = atuin_common::utils::data_dir(); create_dir_all(&config_dir) - .wrap_err_with(|| format!("could not create dir {:?}", config_dir))?; - create_dir_all(&data_dir) - .wrap_err_with(|| format!("could not create dir {:?}", data_dir))?; + .wrap_err_with(|| format!("could not create dir {config_dir:?}"))?; + create_dir_all(&data_dir).wrap_err_with(|| format!("could not create dir {data_dir:?}"))?; let mut config_file = if let Ok(p) = std::env::var("ATUIN_CONFIG_DIR") { PathBuf::from(p) diff --git a/src/command/client/history.rs b/src/command/client/history.rs index 145fe63a8e..34b851d926 100644 --- a/src/command/client/history.rs +++ b/src/command/client/history.rs @@ -257,8 +257,7 @@ impl Cmd { } (Some(session), Some(cwd)) => { let query = format!( - "select * from history where cwd = '{}' and session = '{}';", - cwd, session + "select * from history where cwd = '{cwd}' and session = '{session}';", ); db.query_history(&query).await? } From 5611bc59f584a48ffa3a4adfb8837f470cfe8c22 Mon Sep 17 00:00:00 2001 From: Conrad Ludgate Date: Mon, 6 Feb 2023 19:22:58 +0000 Subject: [PATCH 3/3] display mnemonic key (#694) --- Cargo.lock | 74 ++++++++++++++++++++++++++++++++ Cargo.toml | 1 + atuin-client/src/encryption.rs | 1 + src/command/client/sync.rs | 19 ++++++-- src/command/client/sync/login.rs | 54 ++++++++++++++++++++++- 5 files changed, 143 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 88b2ac2fe9..eb9081dd64 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -96,6 +96,7 @@ dependencies = [ "serde", "serde_json", "termion", + "tiny-bip39", "tokio", "tracing-subscriber", "tui", @@ -1385,6 +1386,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest", +] + [[package]] name = "percent-encoding" version = "2.2.0" @@ -1657,6 +1667,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustix" version = "0.36.5" @@ -2081,6 +2097,18 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8" +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + [[package]] name = "termcolor" version = "1.1.3" @@ -2152,6 +2180,25 @@ dependencies = [ "winapi", ] +[[package]] +name = "tiny-bip39" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62cc94d358b5a1e84a5cb9109f559aa3c4d634d2b1b4de3d0fa4adc7c78e2861" +dependencies = [ + "anyhow", + "hmac", + "once_cell", + "pbkdf2", + "rand", + "rustc-hash", + "sha2", + "thiserror", + "unicode-normalization", + "wasm-bindgen", + "zeroize", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -2398,6 +2445,12 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + [[package]] name = "unicode_categories" version = "0.1.1" @@ -2726,3 +2779,24 @@ checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" dependencies = [ "winapi", ] + +[[package]] +name = "zeroize" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] diff --git a/Cargo.toml b/Cargo.toml index 21ea881d23..574dc55e0e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,6 +73,7 @@ whoami = "1.1.2" rpassword = "7.0" semver = "1.0.14" runtime-format = "0.1.2" +tiny-bip39 = "1" [dependencies.tracing-subscriber] version = "0.3" diff --git a/atuin-client/src/encryption.rs b/atuin-client/src/encryption.rs index 46cf1b2e42..bb017b749d 100644 --- a/atuin-client/src/encryption.rs +++ b/atuin-client/src/encryption.rs @@ -66,6 +66,7 @@ pub fn load_encoded_key(settings: &Settings) -> Result { } } +pub type Key = secretbox::Key; pub fn encode_key(key: secretbox::Key) -> Result { let buf = rmp_serde::to_vec(&key).wrap_err("could not encode key to message pack")?; let buf = base64::encode(buf); diff --git a/src/command/client/sync.rs b/src/command/client/sync.rs index b97a224068..f71bcc996f 100644 --- a/src/command/client/sync.rs +++ b/src/command/client/sync.rs @@ -27,7 +27,11 @@ pub enum Cmd { Register(register::Cmd), /// Print the encryption key for transfer to another machine - Key, + Key { + /// Switch to base64 output of the key + #[arg(long)] + base64: bool, + }, } impl Cmd { @@ -37,11 +41,18 @@ impl Cmd { Self::Login(l) => l.run(&settings).await, Self::Logout => logout::run(), Self::Register(r) => r.run(&settings).await, - Self::Key => { + Self::Key { base64 } => { use atuin_client::encryption::{encode_key, load_key}; let key = load_key(&settings).wrap_err("could not load encryption key")?; - let encode = encode_key(key).wrap_err("could not encode encryption key")?; - println!("{encode}"); + + if base64 { + let encode = encode_key(key).wrap_err("could not encode encryption key")?; + println!("{encode}"); + } else { + let mnemonic = bip39::Mnemonic::from_entropy(&key.0, bip39::Language::English) + .map_err(|_| eyre::eyre!("invalid key"))?; + println!("{mnemonic}"); + } Ok(()) } } diff --git a/src/command/client/sync/login.rs b/src/command/client/sync/login.rs index 038e822bc5..bd3a80297a 100644 --- a/src/command/client/sync/login.rs +++ b/src/command/client/sync/login.rs @@ -1,10 +1,14 @@ use std::io; use clap::Parser; -use eyre::Result; +use eyre::{bail, ContextCompat, Result}; use tokio::{fs::File, io::AsyncWriteExt}; -use atuin_client::{api_client, settings::Settings}; +use atuin_client::{ + api_client, + encryption::{encode_key, Key}, + settings::Settings, +}; use atuin_common::api::LoginRequest; use rpassword::prompt_password; @@ -54,6 +58,31 @@ impl Cmd { let key_path = settings.key_path.as_str(); let mut file = File::create(key_path).await?; + + // try parse the key as a mnemonic... + let key = match bip39::Mnemonic::from_phrase(&key, bip39::Language::English) { + Ok(mnemonic) => encode_key( + Key::from_slice(mnemonic.entropy()).context("key was not the correct length")?, + )?, + Err(err) => { + if let Some(err) = err.downcast_ref::() { + match err { + // assume they copied in the base64 key + bip39::ErrorKind::InvalidWord => key, + bip39::ErrorKind::InvalidChecksum => bail!("key mnemonic was not valid"), + bip39::ErrorKind::InvalidKeysize(_) + | bip39::ErrorKind::InvalidWordLength(_) + | bip39::ErrorKind::InvalidEntropyLength(_, _) => { + bail!("key was not the correct length") + } + } + } else { + // unknown error. assume they copied the base64 key + key + } + } + }; + file.write_all(key.as_bytes()).await?; println!("Logged in!"); @@ -75,3 +104,24 @@ fn read_user_input(name: &'static str) -> String { eprint!("Please enter {name}: "); get_input().expect("Failed to read from input") } + +#[cfg(test)] +mod tests { + use atuin_client::encryption::Key; + + #[test] + fn mnemonic_round_trip() { + let key = Key { + 0: [ + 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 3, 2, 3, 8, 4, 6, 2, 6, 4, 3, 3, 8, 3, + 2, 7, 9, 5, + ], + }; + let phrase = bip39::Mnemonic::from_entropy(&key.0, bip39::Language::English) + .unwrap() + .into_phrase(); + let mnemonic = bip39::Mnemonic::from_phrase(&phrase, bip39::Language::English).unwrap(); + assert_eq!(mnemonic.entropy(), &key.0); + assert_eq!(phrase, "adapt amused able anxiety mother adapt beef gaze amount else seat alcohol cage lottery avoid scare alcohol cactus school avoid coral adjust catch pink"); + } +}