aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorConrad Ludgate <conradludgate@gmail.com>2023-02-06 19:22:58 +0000
committerGitHub <noreply@github.com>2023-02-06 19:22:58 +0000
commit5611bc59f584a48ffa3a4adfb8837f470cfe8c22 (patch)
tree9681cfeb382b62237c39c225687815e0798105eb
parentTry to make clippy happy 🥺 (#686) (diff)
downloadatuin-5611bc59f584a48ffa3a4adfb8837f470cfe8c22.zip
display mnemonic key (#694)
-rw-r--r--Cargo.lock74
-rw-r--r--Cargo.toml1
-rw-r--r--atuin-client/src/encryption.rs1
-rw-r--r--src/command/client/sync.rs19
-rw-r--r--src/command/client/sync/login.rs54
5 files changed, 143 insertions, 6 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 88b2ac2f..eb9081dd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -96,6 +96,7 @@ dependencies = [
"serde",
"serde_json",
"termion",
+ "tiny-bip39",
"tokio",
"tracing-subscriber",
"tui",
@@ -1386,6 +1387,15 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1658,6 +1668,12 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2082,6 +2098,18 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2153,6 +2181,25 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2399,6 +2446,12 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -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 21ea881d..574dc55e 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 46cf1b2e..bb017b74 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<String> {
}
}
+pub type Key = secretbox::Key;
pub fn encode_key(key: secretbox::Key) -> Result<String> {
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 b97a2240..f71bcc99 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 038e822b..bd3a8029 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::<bip39::ErrorKind>() {
+ 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");
+ }
+}