-
Notifications
You must be signed in to change notification settings - Fork 44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Password protection option #192
base: master
Are you sure you want to change the base?
Conversation
Store plain password in SharedPreferences.
I'll take a look at your change in a bit. The build failure is from the findbugs task, which runs the FindBugs Java static analysis tool. Could you run that tool locally to see if it is pointing out any bugs or issues that need to be addressed. To run it: ./gradlew findbugs |
public final int iconId; | ||
public final int menuTextId; | ||
public final int menuDescId; | ||
|
||
public MainMenuItem(int iconId, int menuTextId, int menuDescId) | ||
{ | ||
public MainMenuItem(int iconId, int menuTextId, int menuDescId) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you keep the style the same as before, namely where the { } are?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
FYI, I was able to bypass the password protection in the following ways:
|
} | ||
|
||
private void startAuthorization() { | ||
authorized = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How is the main activity receiving a response from the authentication activity that the authentication was successful? The expectation here is that the authentication activity will not "return" until a password is entered, however this is not the case. A better pattern may be to startActivityForResult(). See documentation here:
https://developer.android.com/training/basics/intents/result
That will allow the return result from the authentication activity to be captured, and a more informed decision made about authentication success or failure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are correct, i changed this and that allowed me to fix the issue related to bypassing the password when clicking back button.
// Generate Hash | ||
byte[] hash = generateHash(password, salt); | ||
// Write to SharedPreferences | ||
_prefs.edit().putString(PasswordKey, toHex(salt) + ":" + toHex(hash)).commit(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not store the salt and password hash as separate entries, so you do not need to parse it out when you retrieve it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that's necessary, usually saveing the hash and the salt together is a common practice.
byte[] hash = generateHash(password, salt); | ||
// Write to SharedPreferences | ||
_prefs.edit().putString(PasswordKey, toHex(salt) + ":" + toHex(hash)).commit(); | ||
} catch (Exception e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this a bit of a wide net to catch? It would be better to catch the specific exception(s), as well as log them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue is i don't know what kind of exception can be thrown here.
The main propose of this catch is to prevent the App from crashing in my opinion.
byte[] passwordHash = generateHash(password, salt); | ||
|
||
int diff = hash.length ^ passwordHash.length; | ||
for (int i = 0; i < hash.length && i < passwordHash.length; i++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not Arrays.equals()?
https://docs.oracle.com/javase/7/docs/api/java/util/Arrays.html#equals(byte[],%20byte[])
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct, changed.
return skf.generateSecret(spec).getEncoded(); | ||
} catch (Exception e) { | ||
e.printStackTrace(); | ||
return new byte[0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking if generating the key failed the error should be presented to the user, instead of attempting to save an empty password. I simulated this failing by editing the preferences file and setting an empty password hash. The result was when a password was entered to authorize when it was compared an out of bounds exception resulted:
01-21 23:43:40.888 6693 6693 E AndroidRuntime: FATAL EXCEPTION: main
01-21 23:43:40.888 6693 6693 E AndroidRuntime: Process: protect.budgetwatch, PID: 6693
01-21 23:43:40.888 6693 6693 E AndroidRuntime: java.lang.ArrayIndexOutOfBoundsException: length=1; index=1
01-21 23:43:40.888 6693 6693 E AndroidRuntime: at protect.budgetwatch.PasswordManager.isPasswordCorrect(PasswordManager.java:58)
01-21 23:43:40.888 6693 6693 E AndroidRuntime: at protect.budgetwatch.PasswordAuthenticationActivity.attemptLogin(PasswordAuthenticationActivity.java:65)
01-21 23:43:40.888 6693 6693 E AndroidRuntime: at protect.budgetwatch.PasswordAuthenticationActivity.access$000(PasswordAuthenticationActivity.java:14)
01-21 23:43:40.888 6693 6693 E AndroidRuntime: at protect.budgetwatch.PasswordAuthenticationActivity$2.onClick(PasswordAuthenticationActivity.java:44)
01-21 23:43:40.888 6693 6693 E AndroidRuntime: at android.view.View.performClick(View.java:6597)
01-21 23:43:40.888 6693 6693 E AndroidRuntime: at android.view.View.performClickInternal(View.java:6574)
01-21 23:43:40.888 6693 6693 E AndroidRuntime: at android.view.View.access$3100(View.java:778)
01-21 23:43:40.888 6693 6693 E AndroidRuntime: at android.view.View$PerformClick.run(View.java:25885)
01-21 23:43:40.888 6693 6693 E AndroidRuntime: at android.os.Handler.handleCallback(Handler.java:873)
01-21 23:43:40.888 6693 6693 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:99)
01-21 23:43:40.888 6693 6693 E AndroidRuntime: at android.os.Looper.loop(Looper.java:193)
01-21 23:43:40.888 6693 6693 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:6669)
01-21 23:43:40.888 6693 6693 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
01-21 23:43:40.888 6693 6693 E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
01-21 23:43:40.888 6693 6693 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Looking over most of the changes and playing with it on an emulator, I understand the change is focused on preventing a user from accessing the application rather than protecting the underlying data. I would support a change to encrypt the underlying data, which would require a password to unlock. Further, the key would need to be stored in Android's Keystore. If you are interested in pursuing this change I can help with advice and code reviews. I've not used the Java Keystore APIs before, so perhaps we can learn together. I appreciate your sending the changes you have. The nature of the proposed changes I'll not be able to accept, however, as they only attempt to hide the data rather than encrypting it. |
} | ||
} | ||
|
||
private String toHex(byte[] array) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would something like Guava's BaseEncoding be useful?
https://google.github.io/guava/releases/16.0/api/docs/com/google/common/io/BaseEncoding.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed.
|
Close #187