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_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",
|
||||
]
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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")?;
|
||||
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue