Skip to content

Commit 0d4e4dc

Browse files
committed
Better CLI; password is now hidden when typing.
1 parent 57f0eae commit 0d4e4dc

File tree

4 files changed

+110
-53
lines changed

4 files changed

+110
-53
lines changed

Cargo.lock

Lines changed: 36 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
[package]
22
name = "lesspass"
33
description = "An efficient implementation of the LessPass password generator."
4-
version = "0.1.0"
4+
version = "0.2.0"
55
authors = ["Grégoire Geis <[email protected]>"]
66

7-
homepage = "https://github.com/6A/lesspass.rs"
8-
repository = "https://github.com/6A/lesspass.rs"
7+
homepage = "https://github.com/71/lesspass.rs"
8+
repository = "https://github.com/71/lesspass.rs"
99
readme = "README.md"
1010
license = "GPL-3.0-only"
1111

@@ -20,13 +20,15 @@ required-features = [ "default" ]
2020

2121
[dependencies]
2222
bitflags = "1.0"
23-
lazy_static = "1.1"
23+
lazy_static = "1.2"
2424
num-bigint = "0.2"
2525
num-integer = "0.1"
2626
num-traits = "0.2"
2727
ring = "0.13"
2828

29-
structopt = { version = "0.2", optional = true }
29+
atty = { version = "0.2", optional = true }
30+
rpassword = { version = "2.1", optional = true }
31+
structopt = { version = "0.2", optional = true }
3032

3133
[features]
32-
default = [ "structopt" ]
34+
default = [ "atty", "rpassword", "structopt" ]

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ pub fn render_password(entropy: &[u8], charset: CharacterSet, len: u8) -> String
185185
/// Return the SHA-256 fingerprint that corresponds to the given master password.
186186
pub fn get_fingerprint(password: &str) -> hmac::Signature {
187187
let key = hmac::SigningKey::new(&digest::SHA256, password.as_bytes());
188-
188+
189189
hmac::sign(&key, b"")
190190
}
191191

src/main.rs

Lines changed: 65 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
extern crate lesspass;
22
extern crate ring;
3+
extern crate rpassword;
34
extern crate structopt;
45

56
use lesspass::*;
@@ -16,14 +17,14 @@ use std::io::Write;
1617
lesspass example.org [email protected] password
1718
1819
Generate the fingerprint of a master password:
19-
lesspass password
20+
lesspass password -F
2021
2122
Generate a 32-characters password using SHA-512:
2223
echo password | lesspass example.org [email protected] --sha512 -l 32
23-
24+
2425
Generate the entropy of a password, using 10,000 iterations:
2526
lesspass example.org [email protected] password -i 10000 -E > entropy.txt
26-
27+
2728
Generate an alphanumeric password using the previously saved entropy:
2829
cat entropy.txt | lesspass -S
2930
@@ -41,6 +42,7 @@ pub struct Args {
4142
login: Option<String>,
4243

4344
/// Master password used for fingerprint and password generation.
45+
/// If not given, it will be read from stdin.
4446
#[structopt(name = "password")]
4547
master_password: Option<String>,
4648

@@ -86,7 +88,11 @@ pub struct Args {
8688

8789
/// Return the entropy instead of generating a password.
8890
#[structopt(short = "E", long = "return-entropy")]
89-
return_entropy: bool
91+
return_entropy: bool,
92+
93+
/// Print the fingerprint.
94+
#[structopt(short = "F", long = "print-fingerprint")]
95+
print_fingerprint: bool
9096
}
9197

9298
fn main() {
@@ -96,7 +102,7 @@ fn main() {
96102
let res = if !err.is_empty() {
97103
out.write_all(err.as_bytes()).map_err(|_| ())
98104
} else {
99-
Args::clap().write_help(&mut out).map_err(|_| ())
105+
Args::clap().write_long_help(&mut out).map_err(|_| ())
100106
};
101107

102108
std::process::exit(if res.is_ok() { 1 } else { 2 })
@@ -109,7 +115,7 @@ fn run() -> Result<(), &'static str> {
109115
iterations, length, counter,
110116
sha256, sha384, sha512,
111117
exclude_lower, exclude_upper, exclude_numbers, exclude_symbols,
112-
return_entropy
118+
return_entropy, print_fingerprint
113119
} = Args::from_args();
114120

115121
let mut out = std::io::stdout();
@@ -150,40 +156,57 @@ fn run() -> Result<(), &'static str> {
150156
// Compute entropy.
151157
let entropy = match (website, login, master_password) {
152158
(pass, None, None) => {
153-
let master_password = match pass {
154-
Some(pass) => pass,
155-
None => read_stdin()? // Get password from standart input.
156-
};
159+
if print_fingerprint {
160+
// Only the password was given, so we return its fingerprint.
161+
let master_password = match pass {
162+
Some(pass) => pass,
163+
None => read_password()? // Get password from standard input.
164+
};
157165

158-
// If the password matches the format of the entropy, then we use it. Otherwise
159-
// we only return the fingerprint.
160-
match parse_entropy(&master_password) {
161-
None => {
162-
// Only the password was given, so we return its fingerprint.
163-
let fingerprint = get_fingerprint(&master_password);
166+
print_buffer_hex(get_fingerprint(&master_password).as_ref(), &mut out)?;
167+
168+
return Ok(())
169+
}
164170

165-
for byte in fingerprint.as_ref() {
166-
write!(out, "{:02x}", byte).map_err(|_| "Unable to write to standard output.")?;
171+
let entropy = match pass {
172+
Some(pass) => pass,
173+
None => {
174+
// Get entropy from standard input.
175+
if atty::is(atty::Stream::Stdin) {
176+
// Stdin is a terminal, and no one in their right mind would copy
177+
// the entropy by hand, so we cancel early.
178+
return Err("")
167179
}
168180

169-
out.write(b"\n").map_err(|_| "Unable to write to standard output.")?;
181+
read_password()?
182+
}
183+
};
170184

171-
return Ok(())
172-
},
185+
// If the password matches the format of the entropy, then we use it. Otherwise
186+
// we only return the fingerprint.
187+
match parse_entropy(&entropy) {
173188
Some(entropy) => {
174-
// The entropy was given to us, so we use it instead.
189+
// The entropy was given to us, so we use it.
175190
entropy
191+
},
192+
None => {
193+
return Err("Invalid entropy format.")
176194
}
177195
}
178196
},
179197
(Some(website), Some(login), pass) => {
180198
// Everything needed to compute the entropy was given, so we get to it.
181199
let master_password = match pass {
182200
Some(pass) => pass,
183-
None => read_stdin()? // Get password from standart input.
201+
None => read_password()? // Get password from standard input.
184202
};
203+
185204
let salt = generate_salt(&website, &login, counter);
186205

206+
if print_fingerprint {
207+
print_buffer_hex(get_fingerprint(&master_password).as_ref(), &mut out)?;
208+
}
209+
187210
generate_entropy(&master_password, &salt, algorithm, iterations)
188211
},
189212
_ => {
@@ -214,29 +237,35 @@ fn print_buffer_hex(buf: &[u8], out: &mut Write) -> Result<(), &'static str> {
214237
Ok(())
215238
}
216239

217-
fn read_stdin() -> Result<String, &'static str> {
218-
let stdin = std::io::stdin();
219-
let mut input = String::new();
240+
fn read_password() -> Result<String, &'static str> {
241+
// If the input is passed from Stdin, it fails on my machine,
242+
// so we handle this here
243+
if atty::is(atty::Stream::Stdin) {
244+
rpassword::read_password().map_err(|_| "Unable to read password or entropy.")
245+
} else {
246+
let stdin = std::io::stdin();
247+
let mut input = String::new();
220248

221-
if stdin.read_line(&mut input).is_err() {
222-
return Err("Unable to read value from standard input.")
223-
}
249+
if stdin.read_line(&mut input).is_err() {
250+
return Err("Unable to read password or entropy from standard input.")
251+
}
224252

225-
// Trim string if needed.
226-
if input.ends_with('\n') {
227-
let new_len = input.len() - (if input.ends_with("\r\n") { 2 } else { 1 });
253+
// Trim string if needed.
254+
if input.ends_with('\n') {
255+
let new_len = input.len() - (if input.ends_with("\r\n") { 2 } else { 1 });
228256

229-
input.truncate(new_len);
230-
}
257+
input.truncate(new_len);
258+
}
231259

232-
Ok(input)
260+
Ok(input)
261+
}
233262
}
234263

235264
fn parse_entropy(entropy: &str) -> Option<Vec<u8>> {
236265
if entropy.len() != 64 {
237266
return None
238267
}
239-
268+
240269
let mut vec = Vec::with_capacity(32);
241270

242271
for i in 0..32 {

0 commit comments

Comments
 (0)