1
1
extern crate lesspass;
2
2
extern crate ring;
3
+ extern crate rpassword;
3
4
extern crate structopt;
4
5
5
6
use lesspass:: * ;
@@ -16,14 +17,14 @@ use std::io::Write;
16
17
lesspass example.org [email protected] password
17
18
18
19
Generate the fingerprint of a master password:
19
- lesspass password
20
+ lesspass password -F
20
21
21
22
Generate a 32-characters password using SHA-512:
22
23
echo password | lesspass example.org [email protected] --sha512 -l 32
23
-
24
+
24
25
Generate the entropy of a password, using 10,000 iterations:
25
26
lesspass example.org [email protected] password -i 10000 -E > entropy.txt
26
-
27
+
27
28
Generate an alphanumeric password using the previously saved entropy:
28
29
cat entropy.txt | lesspass -S
29
30
@@ -41,6 +42,7 @@ pub struct Args {
41
42
login : Option < String > ,
42
43
43
44
/// Master password used for fingerprint and password generation.
45
+ /// If not given, it will be read from stdin.
44
46
#[ structopt( name = "password" ) ]
45
47
master_password : Option < String > ,
46
48
@@ -86,7 +88,11 @@ pub struct Args {
86
88
87
89
/// Return the entropy instead of generating a password.
88
90
#[ 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
90
96
}
91
97
92
98
fn main ( ) {
@@ -96,7 +102,7 @@ fn main() {
96
102
let res = if !err. is_empty ( ) {
97
103
out. write_all ( err. as_bytes ( ) ) . map_err ( |_| ( ) )
98
104
} else {
99
- Args :: clap ( ) . write_help ( & mut out) . map_err ( |_| ( ) )
105
+ Args :: clap ( ) . write_long_help ( & mut out) . map_err ( |_| ( ) )
100
106
} ;
101
107
102
108
std:: process:: exit ( if res. is_ok ( ) { 1 } else { 2 } )
@@ -109,7 +115,7 @@ fn run() -> Result<(), &'static str> {
109
115
iterations, length, counter,
110
116
sha256, sha384, sha512,
111
117
exclude_lower, exclude_upper, exclude_numbers, exclude_symbols,
112
- return_entropy
118
+ return_entropy, print_fingerprint
113
119
} = Args :: from_args ( ) ;
114
120
115
121
let mut out = std:: io:: stdout ( ) ;
@@ -150,40 +156,57 @@ fn run() -> Result<(), &'static str> {
150
156
// Compute entropy.
151
157
let entropy = match ( website, login, master_password) {
152
158
( 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
+ } ;
157
165
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
+ }
164
170
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 ( "" )
167
179
}
168
180
169
- out. write ( b"\n " ) . map_err ( |_| "Unable to write to standard output." ) ?;
181
+ read_password ( ) ?
182
+ }
183
+ } ;
170
184
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) {
173
188
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.
175
190
entropy
191
+ } ,
192
+ None => {
193
+ return Err ( "Invalid entropy format." )
176
194
}
177
195
}
178
196
} ,
179
197
( Some ( website) , Some ( login) , pass) => {
180
198
// Everything needed to compute the entropy was given, so we get to it.
181
199
let master_password = match pass {
182
200
Some ( pass) => pass,
183
- None => read_stdin ( ) ? // Get password from standart input.
201
+ None => read_password ( ) ? // Get password from standard input.
184
202
} ;
203
+
185
204
let salt = generate_salt ( & website, & login, counter) ;
186
205
206
+ if print_fingerprint {
207
+ print_buffer_hex ( get_fingerprint ( & master_password) . as_ref ( ) , & mut out) ?;
208
+ }
209
+
187
210
generate_entropy ( & master_password, & salt, algorithm, iterations)
188
211
} ,
189
212
_ => {
@@ -214,29 +237,35 @@ fn print_buffer_hex(buf: &[u8], out: &mut Write) -> Result<(), &'static str> {
214
237
Ok ( ( ) )
215
238
}
216
239
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 ( ) ;
220
248
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
+ }
224
252
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 } ) ;
228
256
229
- input. truncate ( new_len) ;
230
- }
257
+ input. truncate ( new_len) ;
258
+ }
231
259
232
- Ok ( input)
260
+ Ok ( input)
261
+ }
233
262
}
234
263
235
264
fn parse_entropy ( entropy : & str ) -> Option < Vec < u8 > > {
236
265
if entropy. len ( ) != 64 {
237
266
return None
238
267
}
239
-
268
+
240
269
let mut vec = Vec :: with_capacity ( 32 ) ;
241
270
242
271
for i in 0 ..32 {
0 commit comments