display mnemonic key (#694)

This commit is contained in:
Conrad Ludgate 2023-02-06 19:22:58 +00:00 committed by GitHub
parent 3abc96fafe
commit 5611bc59f5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 143 additions and 6 deletions

74
Cargo.lock generated
View file

@ -96,6 +96,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"termion", "termion",
"tiny-bip39",
"tokio", "tokio",
"tracing-subscriber", "tracing-subscriber",
"tui", "tui",
@ -1385,6 +1386,15 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
[[package]]
name = "pbkdf2"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917"
dependencies = [
"digest",
]
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "2.2.0" version = "2.2.0"
@ -1657,6 +1667,12 @@ dependencies = [
"tinyvec", "tinyvec",
] ]
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.36.5" version = "0.36.5"
@ -2081,6 +2097,18 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8" 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]] [[package]]
name = "termcolor" name = "termcolor"
version = "1.1.3" version = "1.1.3"
@ -2152,6 +2180,25 @@ dependencies = [
"winapi", "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]] [[package]]
name = "tinyvec" name = "tinyvec"
version = "1.6.0" version = "1.6.0"
@ -2398,6 +2445,12 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "unicode-xid"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]] [[package]]
name = "unicode_categories" name = "unicode_categories"
version = "0.1.1" version = "0.1.1"
@ -2726,3 +2779,24 @@ checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
dependencies = [ dependencies = [
"winapi", "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",
]

View file

@ -73,6 +73,7 @@ whoami = "1.1.2"
rpassword = "7.0" rpassword = "7.0"
semver = "1.0.14" semver = "1.0.14"
runtime-format = "0.1.2" runtime-format = "0.1.2"
tiny-bip39 = "1"
[dependencies.tracing-subscriber] [dependencies.tracing-subscriber]
version = "0.3" version = "0.3"

View file

@ -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> { 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 = rmp_serde::to_vec(&key).wrap_err("could not encode key to message pack")?;
let buf = base64::encode(buf); let buf = base64::encode(buf);

View file

@ -27,7 +27,11 @@ pub enum Cmd {
Register(register::Cmd), Register(register::Cmd),
/// Print the encryption key for transfer to another machine /// Print the encryption key for transfer to another machine
Key, Key {
/// Switch to base64 output of the key
#[arg(long)]
base64: bool,
},
} }
impl Cmd { impl Cmd {
@ -37,11 +41,18 @@ impl Cmd {
Self::Login(l) => l.run(&settings).await, Self::Login(l) => l.run(&settings).await,
Self::Logout => logout::run(), Self::Logout => logout::run(),
Self::Register(r) => r.run(&settings).await, Self::Register(r) => r.run(&settings).await,
Self::Key => { Self::Key { base64 } => {
use atuin_client::encryption::{encode_key, load_key}; use atuin_client::encryption::{encode_key, load_key};
let key = load_key(&settings).wrap_err("could not load encryption key")?; let key = load_key(&settings).wrap_err("could not load encryption key")?;
if base64 {
let encode = encode_key(key).wrap_err("could not encode encryption key")?; let encode = encode_key(key).wrap_err("could not encode encryption key")?;
println!("{encode}"); println!("{encode}");
} else {
let mnemonic = bip39::Mnemonic::from_entropy(&key.0, bip39::Language::English)
.map_err(|_| eyre::eyre!("invalid key"))?;
println!("{mnemonic}");
}
Ok(()) Ok(())
} }
} }

View file

@ -1,10 +1,14 @@
use std::io; use std::io;
use clap::Parser; use clap::Parser;
use eyre::Result; use eyre::{bail, ContextCompat, Result};
use tokio::{fs::File, io::AsyncWriteExt}; 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 atuin_common::api::LoginRequest;
use rpassword::prompt_password; use rpassword::prompt_password;
@ -54,6 +58,31 @@ impl Cmd {
let key_path = settings.key_path.as_str(); let key_path = settings.key_path.as_str();
let mut file = File::create(key_path).await?; 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?; file.write_all(key.as_bytes()).await?;
println!("Logged in!"); println!("Logged in!");
@ -75,3 +104,24 @@ fn read_user_input(name: &'static str) -> String {
eprint!("Please enter {name}: "); eprint!("Please enter {name}: ");
get_input().expect("Failed to read from input") 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");
}
}