display mnemonic key (#694)
This commit is contained in:
parent
3abc96fafe
commit
5611bc59f5
5 changed files with 143 additions and 6 deletions
74
Cargo.lock
generated
74
Cargo.lock
generated
|
@ -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",
|
||||||
|
]
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue