aboutsummaryrefslogtreecommitdiffstats
path: root/src
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 /src
parentTry to make clippy happy 🥺 (#686) (diff)
downloadatuin-5611bc59f584a48ffa3a4adfb8837f470cfe8c22.zip
display mnemonic key (#694)
Diffstat (limited to '')
-rw-r--r--src/command/client/sync.rs19
-rw-r--r--src/command/client/sync/login.rs54
2 files changed, 67 insertions, 6 deletions
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");
+ }
+}