Skip to content
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

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open

Conversation

MoDevby
Copy link

@MoDevby MoDevby commented Jan 22, 2019

Close #187

@brarcher
Copy link
Owner

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) {
Copy link
Owner

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?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@brarcher
Copy link
Owner

FYI, I was able to bypass the password protection in the following ways:

  1. When the password prompt is shown press "back". This leads to the main activity without requiring the password.
  2. Add an icon to the home screen. Long press the icon. This brings up a static menu to add a revenue or expense. Click on either opens the TransactionViewActivity without requiring a password.

}

private void startAuthorization() {
authorized = true;
Copy link
Owner

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.

Copy link
Author

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();
Copy link
Owner

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?

Copy link
Author

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) {
Copy link
Owner

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.

Copy link
Author

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++) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

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];
Copy link
Owner

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)

@brarcher
Copy link
Owner

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) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed.

@MoDevby
Copy link
Author

MoDevby commented Apr 2, 2019

FYI, I was able to bypass the password protection in the following ways:

1. When the password prompt is shown press "back". This leads to the main activity without requiring the password.

2. Add an icon to the home screen. Long press the icon. This brings up a static menu to add a revenue or expense. Click on either opens the TransactionViewActivity without requiring a password.
  1. I solved this by using startActivityForResult as suggested by you.
  2. I intentionally want this behaviour, as if you remember from the discussion in my request Feature Request: password or pattern protection #187, the whole goal is to prevent the peeking into the financial info but adding an expense or revenue shouldn't be a problem (specially that these are shortcuts it should be for fast actions so no password protection is needed here).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants