@@ -78,6 +78,69 @@ pub enum WalletBoot {
78
78
Recovery ,
79
79
}
80
80
81
+ /// Get and confirm a passphrase from the user, with feedback
82
+ /// This is intended to be used for new or changed passphrases
83
+ ///
84
+ /// You must provide the initial and confirmation prompts to pass to the user
85
+ ///
86
+ /// We do several things:
87
+ /// - Prompt the user for a passphrase
88
+ /// - Have the user confirm the passphrase
89
+ /// - Score the passphrase
90
+ /// - If the passphrase is weak (or empty), give feedback and ask the user what to do:
91
+ /// - Proceed with the weak (or empty) passphrase
92
+ /// - Choose a better passphrase
93
+ /// - Cancel the operation
94
+ ///
95
+ /// If the passphrase and confirmation don't match, or if the user cancels, returns an error
96
+ /// Otherwise, returns the passphrase as a `SafePassword`
97
+ fn get_new_passphrase ( prompt : & str , confirm : & str ) -> Result < SafePassword , ExitError > {
98
+ // We may need to prompt for a passphrase multiple times
99
+ loop {
100
+ // Prompt the user for a passphrase and confirm it
101
+ let passphrase = prompt_password ( prompt) ?;
102
+ let confirmed = prompt_password ( confirm) ?;
103
+ if passphrase. reveal ( ) != confirmed. reveal ( ) {
104
+ return Err ( ExitError :: new ( ExitCode :: InputError , "Passphrases don't match!" ) ) ;
105
+ }
106
+
107
+ // Score the passphrase and provide feedback
108
+ let weak = display_password_feedback ( & passphrase) ;
109
+
110
+ // If the passphrase is weak, see if the user wishes to change it
111
+ if weak {
112
+ println ! ( "Would you like to choose a different passphrase?" ) ;
113
+ println ! ( " y/Y: Yes, choose a different passphrase" ) ;
114
+ println ! ( " n/N: No, use this passphrase" ) ;
115
+ println ! ( " Enter anything else if you changed your mind and want to cancel" ) ;
116
+
117
+ let mut input = "" . to_string ( ) ;
118
+ std:: io:: stdin ( ) . read_line ( & mut input) ;
119
+
120
+ match input. trim ( ) . to_lowercase ( ) . as_str ( ) {
121
+ // Choose a different passphrase
122
+ "y" => {
123
+ continue ;
124
+ } ,
125
+ // Use this passphrase
126
+ "n" => {
127
+ return Ok ( passphrase) ;
128
+ } ,
129
+ // By default, we cancel to be safe
130
+ _ => {
131
+ return Err ( ExitError :: new (
132
+ ExitCode :: InputError ,
133
+ "Canceling with unchanged passphrase!" ,
134
+ ) ) ;
135
+ } ,
136
+ }
137
+ } else {
138
+ // The passphrase is fine, so return it
139
+ return Ok ( passphrase) ;
140
+ }
141
+ }
142
+ }
143
+
81
144
/// Get feedback, if available, for a weak passphrase
82
145
fn get_password_feedback ( passphrase : & SafePassword ) -> Option < Vec < String > > {
83
146
std:: str:: from_utf8 ( passphrase. reveal ( ) )
@@ -88,27 +151,35 @@ fn get_password_feedback(passphrase: &SafePassword) -> Option<Vec<String>> {
88
151
. map ( |suggestion| suggestion. into_iter ( ) . map ( |item| item. to_string ( ) ) . collect ( ) )
89
152
}
90
153
91
- // Display password feedback to the user
92
- fn display_password_feedback ( passphrase : & SafePassword ) {
154
+ /// Display passphrase feedback to the user
155
+ ///
156
+ /// Returns `true` if and only if the passphrase is weak
157
+ fn display_password_feedback ( passphrase : & SafePassword ) -> bool {
93
158
if passphrase. reveal ( ) . is_empty ( ) {
94
159
// The passphrase is empty, which the scoring library doesn't handle
95
- println ! ( "However, an empty password puts your wallet at risk against an attacker with access to this device." ) ;
160
+ println ! ( ) ;
161
+ println ! ( "An empty password puts your wallet at risk against an attacker with access to this device." ) ;
96
162
println ! ( "Use this only if you are sure that your device is safe from prying eyes!" ) ;
97
163
println ! ( ) ;
164
+
165
+ true
98
166
} else if let Some ( feedback) = get_password_feedback ( passphrase) {
99
167
// The scoring library provided feedback
168
+ println ! ( ) ;
100
169
println ! (
101
- "However, the password you chose is weak; a determined attacker with access to your device may be able to \
102
- guess it."
170
+ "The password you chose is weak; a determined attacker with access to your device may be able to guess it."
103
171
) ;
104
172
println ! ( "You may want to consider changing it to a stronger one." ) ;
105
173
println ! ( "Here are some suggestions:" ) ;
106
174
for suggestion in feedback {
107
175
println ! ( "- {}" , suggestion) ;
108
176
}
109
177
println ! ( ) ;
178
+
179
+ true
110
180
} else {
111
- // There is no feedback to provide
181
+ // The Force is strong with this one
182
+ false
112
183
}
113
184
}
114
185
@@ -162,17 +233,8 @@ pub async fn change_password(
162
233
)
163
234
. await ?;
164
235
165
- let new = prompt_password ( "New wallet password: " ) ?;
166
- let confirmed = prompt_password ( "Confirm new password: " ) ?;
167
-
168
- if new. reveal ( ) != confirmed. reveal ( ) {
169
- return Err ( ExitError :: new ( ExitCode :: InputError , "Passwords don't match!" ) ) ;
170
- }
171
-
172
- println ! ( "Passwords match." ) ;
173
-
174
- // If the passphrase is weak, let the user know
175
- display_password_feedback ( & new) ;
236
+ // Get a new passphrase
237
+ let new = get_new_passphrase ( "New wallet passphrase: " , "Confirm new passphrase: " ) ?;
176
238
177
239
// Use the existing and new passphrases to attempt to change the wallet passphrase
178
240
wallet. db . change_passphrase ( & existing, & new) . map_err ( |e| match e {
@@ -655,24 +717,13 @@ pub(crate) fn boot_with_password(
655
717
656
718
let password = match boot_mode {
657
719
WalletBoot :: New => {
658
- debug ! ( target: LOG_TARGET , "Prompting for password." ) ;
659
- let password = prompt_password ( "Create wallet password: " ) ?;
660
- let confirmed = prompt_password ( "Confirm wallet password: " ) ?;
661
-
662
- if password. reveal ( ) != confirmed. reveal ( ) {
663
- return Err ( ExitError :: new ( ExitCode :: InputError , "Passwords don't match!" ) ) ;
664
- }
665
-
666
- println ! ( "Passwords match." ) ;
667
-
668
- // If the passphrase is weak, let the user know
669
- display_password_feedback ( & password) ;
670
-
671
- password
720
+ // Get a new passphrase
721
+ debug ! ( target: LOG_TARGET , "Prompting for passphrase." ) ;
722
+ get_new_passphrase ( "Create wallet passphrase: " , "Confirm wallet passphrase: " ) ?
672
723
} ,
673
724
WalletBoot :: Existing | WalletBoot :: Recovery => {
674
- debug ! ( target: LOG_TARGET , "Prompting for password ." ) ;
675
- prompt_password ( "Enter wallet password : " ) ?
725
+ debug ! ( target: LOG_TARGET , "Prompting for passphrase ." ) ;
726
+ prompt_password ( "Enter wallet passphrase : " ) ?
676
727
} ,
677
728
} ;
678
729
0 commit comments