1+ use argon2:: password_hash:: SaltString ;
12use clap:: { ArgGroup , Parser } ;
23use std:: io:: { self , BufRead , IsTerminal , Write } ;
3- use argon2:: password_hash:: SaltString ;
44
55// Usage: argon2 [-h] salt [-i|-d|-id] [-t iterations] [-m log2(memory in KiB) | -k memory in KiB] [-p parallelism] [-l hash length] [-e|-r] [-v (10|13)]
66#[ derive( Parser , Debug ) ]
7- #[ command( name = "argon2" , about = "(Rust implementation)" , disable_help_flag = false ) ]
7+ #[ command(
8+ name = "argon2" ,
9+ about = "(Rust implementation)" ,
10+ disable_help_flag = false
11+ ) ]
812#[ command( group( ArgGroup :: new( "variant" ) . args( & [ "i" , "d" , "id" ] ) ) ) ]
913#[ command( group( ArgGroup :: new( "memory" ) . args( & [ "m" , "k" ] ) ) ) ]
1014#[ command( group( ArgGroup :: new( "output_format" ) . args( & [ "e" , "r" ] ) ) ) ]
1115struct Args {
12- /// The salt to use (optional, a random salt is generated if omitted)
16+ /// The salt to use (optional, a random salt is generated if omitted; minimum 8 characters )
1317 salt : Option < String > ,
1418
1519 /// Use Argon2i (this is the default)
@@ -73,30 +77,42 @@ fn get_input() -> io::Result<String> {
7377 }
7478}
7579
80+ fn validate_salt ( salt : & str ) -> Result < ( ) , String > {
81+ if salt. len ( ) < 8 {
82+ return Err ( "Invalid salt: minimum length is 8 characters" . to_string ( ) ) ;
83+ }
84+
85+ Ok ( ( ) )
86+ }
87+
7688fn main ( ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
7789 // Handle the non-standard `-id` flag which conflicts with clap's short flag clustering
7890 let args_env = std:: env:: args ( ) ;
79- let new_args: Vec < String > = args_env. map ( |arg| {
80- if arg == "-id" {
81- "--id" . to_string ( )
82- } else {
83- arg
84- }
85- } ) . collect ( ) ;
91+ let new_args: Vec < String > = args_env
92+ . map ( |arg| {
93+ if arg == "-id" {
94+ "--id" . to_string ( )
95+ } else {
96+ arg
97+ }
98+ } )
99+ . collect ( ) ;
86100
87101 let args = Args :: parse_from ( new_args) ;
88102
89103 let salt_string = match & args. salt {
90- Some ( salt) => SaltString :: encode_b64 ( salt. as_bytes ( ) )
91- . map_err ( |e| format ! ( "Invalid salt: {}" , e) ) ?,
104+ Some ( salt) => {
105+ validate_salt ( salt) ?;
106+ SaltString :: encode_b64 ( salt. as_bytes ( ) ) . map_err ( |e| format ! ( "Invalid salt: {}" , e) ) ?
107+ }
92108 None => SaltString :: generate ( & mut rand_core:: OsRng ) ,
93109 } ;
94110
95111 let password = get_input ( ) . unwrap_or_else ( |e| {
96112 eprintln ! ( "Error reading input: {}" , e) ;
97113 std:: process:: exit ( 1 ) ;
98114 } ) ;
99-
115+
100116 // Select algorithm variant
101117 let algorithm = if args. d {
102118 argon2:: Algorithm :: Argon2d
@@ -107,59 +123,48 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
107123 } ;
108124
109125 // Calculate memory cost
110- let memory_kib = if let Some ( k) = args. k {
111- k
112- } else {
113- 1 << args. m
114- } ;
126+ let memory_kib = if let Some ( k) = args. k { k } else { 1 << args. m } ;
115127
116- let params = argon2:: Params :: new (
117- memory_kib,
118- args. t ,
119- args. p ,
120- Some ( args. l as usize ) ,
121- ) . map_err ( |e| format ! ( "Invalid parameters: {}" , e) ) ?;
128+ let params = argon2:: Params :: new ( memory_kib, args. t , args. p , Some ( args. l as usize ) )
129+ . map_err ( |e| format ! ( "Invalid parameters: {}" , e) ) ?;
122130
123131 let version = match args. v {
124132 10 => argon2:: Version :: V0x10 ,
125133 13 => argon2:: Version :: V0x13 ,
126134 _ => return Err ( format ! ( "Invalid version: {} (expected 10 or 13)" , args. v) . into ( ) ) ,
127135 } ;
128136
129- let argon2 = argon2:: Argon2 :: new (
130- algorithm,
131- version,
132- params,
133- ) ;
137+ let argon2 = argon2:: Argon2 :: new ( algorithm, version, params) ;
134138
135139 let start = std:: time:: Instant :: now ( ) ;
136-
140+
137141 let password_bytes = password. as_bytes ( ) ;
138-
142+
139143 use argon2:: PasswordHasher ;
140- let password_hash = argon2. hash_password ( password_bytes, salt_string. as_salt ( ) )
144+ let password_hash = argon2
145+ . hash_password ( password_bytes, salt_string. as_salt ( ) )
141146 . map_err ( |e| format ! ( "Hashing failed: {}" , e) ) ?;
142-
147+
143148 let duration = start. elapsed ( ) ;
144149
145150 // Generate output based on flags
146151 if args. e {
147152 println ! ( "{}" , password_hash) ;
148153 } else if args. r {
149154 if let Some ( hash) = password_hash. hash {
150- io:: stdout ( ) . write_all ( hash. as_bytes ( ) ) ?;
155+ io:: stdout ( ) . write_all ( hash. as_bytes ( ) ) ?;
151156 }
152157 } else {
153158 println ! ( "Type: {:?}" , algorithm) ;
154159 println ! ( "Iterations: {}" , args. t) ;
155160 println ! ( "Memory: {} KiB" , memory_kib) ;
156161 println ! ( "Parallelism: {}" , args. p) ;
157-
162+
158163 if let Some ( hash) = password_hash. hash {
159164 println ! ( "Hash: {}" , hex:: encode( hash. as_bytes( ) ) ) ;
160165 }
161166 println ! ( "Encoded: {}" , password_hash) ;
162-
167+
163168 println ! ( "{:.3} seconds" , duration. as_secs_f64( ) ) ;
164169 println ! ( "Verification ok" ) ;
165170 }
0 commit comments