added fingerprint unlock settings

implemented full fingerprint unlock
This commit is contained in:
Philipp Crocoll 2016-01-02 12:31:30 +01:00
parent 9990ceeb8d
commit 756f6971f1
12 changed files with 257 additions and 18 deletions

View File

@ -35,6 +35,8 @@ namespace FingerprintTest
RequestPermissions(new[] { Manifest.Permission.UseFingerprint }, FINGERPRINT_PERMISSION_REQUEST_CODE);
}
string prefKey = "enc_pref_key";
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="FingerprintTest.FingerprintTest" android:versionCode="1" android:versionName="1.0">
<uses-sdk android:targetSdkVersion="15" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<application android:label="FingerprintTest" android:icon="@drawable/Icon"></application>
</manifest>

View File

@ -19,12 +19,15 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Security.Cryptography;
using System.Text;
using Android.Content;
using Java.Lang;
using KeePassLib;
using KeePassLib.Keys;
using KeePassLib.Serialization;
using keepass2android.Io;
using KeePassLib.Utility;
using Exception = System.Exception;
using String = System.String;
@ -148,6 +151,29 @@ namespace keepass2android
set { _databaseFormat = value; }
}
public static string GetFingerprintPrefKey(IOConnectionInfo ioc)
{
SHA256Managed sha256 = new SHA256Managed();
string iocAsHexString = MemUtil.ByteArrayToHexString(sha256.ComputeHash(Encoding.Unicode.GetBytes(ioc.Path.ToCharArray())));
return "kp2a_ioc_" + iocAsHexString;
}
public static string GetFingerprintModePrefKey(IOConnectionInfo ioc)
{
return GetFingerprintPrefKey(ioc) + "_mode";
}
public string CurrentFingerprintPrefKey
{
get { return GetFingerprintPrefKey(Ioc); }
}
public string CurrentFingerprintModePrefKey
{
get { return GetFingerprintModePrefKey(Ioc); }
}
protected virtual void PopulateDatabaseFromStream(PwDatabase pwDatabase, Stream s, IOConnectionInfo iocInfo, CompositeKey compositeKey, ProgressDialogStatusLogger status, IDatabaseFormat databaseFormat)
{
IFileStorage fileStorage = _app.GetFileStorage(iocInfo);

View File

@ -35,9 +35,12 @@ using Android.Preferences;
using Android.Text;
using Android.Content.PM;
using Android.Graphics;
using Android.Hardware.Fingerprints;
using Android.Provider;
using Android.Support.Design.Widget;
using Android.Support.V4.Widget;
using Android.Support.V7.App;
using Java.Lang;
using keepass2android;
using KeePassLib.Keys;
using KeePassLib.Serialization;
@ -55,6 +58,9 @@ using Process = Android.OS.Process;
using KeeChallenge;
using AlertDialog = Android.App.AlertDialog;
using Enum = System.Enum;
using Exception = System.Exception;
using String = System.String;
using Toolbar = Android.Support.V7.Widget.Toolbar;
namespace keepass2android
@ -64,7 +70,8 @@ namespace keepass2android
LaunchMode = LaunchMode.SingleInstance,
WindowSoftInputMode = SoftInput.AdjustResize,
Theme = "@style/MyTheme_Blue")] /*caution: also contained in AndroidManifest.xml*/
public class PasswordActivity : LockingActivity {
public class PasswordActivity : LockingActivity, IFingerprintAuthCallback
{
enum KeyProviders
{
@ -689,7 +696,9 @@ namespace keepass2android
private string mDrawerTitle;
private MeasuringRelativeLayout.MeasureArgs _measureArgs;
private ActivityDesign _activityDesign;
private FingerprintDecryption _fingerprintDec;
private bool _fingerprintPermissionGranted;
internal class MyActionBarDrawerToggle : ActionBarDrawerToggle
{
@ -899,9 +908,71 @@ namespace keepass2android
this._measureArgs = args;
};
RequestPermissions(new[] { Android.Manifest.Permission.UseFingerprint }, FingerprintPermissionRequestCode);
}
const int FingerprintPermissionRequestCode = 0;
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
if (requestCode == FingerprintPermissionRequestCode && grantResults[0] == Permission.Granted)
{
var btn = FindViewById<ImageButton>(Resource.Id.fingerprintbtn);
btn.Click += (sender, args) =>
{
AlertDialog.Builder b = new AlertDialog.Builder(this);
b.SetTitle(Resource.String.fingerprint_prefs);
b.SetMessage(btn.Tag.ToString());
b.SetPositiveButton(Android.Resource.String.Ok, (o, eventArgs) => ((Dialog)o).Dismiss());
b.Show();
};
_fingerprintPermissionGranted = true;
}
}
private void InitializeNavDrawerButtons()
private void ClearFingerprintUnlockData()
{
ISharedPreferencesEditor edit = PreferenceManager.GetDefaultSharedPreferences(this).Edit();
edit.PutString(Database.GetFingerprintPrefKey(_ioConnection), "");
edit.PutString(Database.GetFingerprintModePrefKey(_ioConnection), FingerprintUnlockMode.Disabled.ToString());
edit.Commit();
}
public void OnFingerprintError(string message)
{
var btn = FindViewById<ImageButton>(Resource.Id.fingerprintbtn);
btn.SetImageResource(Resource.Drawable.ic_fingerprint_error);
btn.PostDelayed(() =>
{
btn.SetImageResource(Resource.Drawable.ic_fp_40px);
btn.Tag = GetString(Resource.String.fingerprint_unlock_hint);
}, 1300);
Toast.MakeText(this, message, ToastLength.Long).Show();
}
public void OnFingerprintAuthSucceeded()
{
var btn = FindViewById<ImageButton>(Resource.Id.fingerprintbtn);
btn.SetImageResource(Resource.Drawable.ic_fingerprint_success);
var masterPassword = _fingerprintDec.DecryptStored(Database.GetFingerprintPrefKey(_ioConnection));
_password = FindViewById<EditText>(Resource.Id.password_edit).Text = masterPassword;
btn.PostDelayed(() =>
{
//re-init fingerprint unlock in case something goes wrong with opening the database
InitFingerprintUnlock();
//fire
OnOk();
}, 1000);
}
private void InitializeNavDrawerButtons()
{
FindViewById(Resource.Id.btn_nav_change_db).Click += (sender, args) =>
{
@ -1402,7 +1473,16 @@ namespace keepass2android
}
private void SetPasswordTypeface(TextView textView)
protected override void OnPause()
{
base.OnPause();
if (_fingerprintDec != null)
{
_fingerprintDec.StopListening();
}
}
private void SetPasswordTypeface(TextView textView)
{
if (_passwordFont == null)
{
@ -1682,8 +1762,65 @@ namespace keepass2android
}
}
}
if (_fingerprintPermissionGranted)
{
InitFingerprintUnlock();
}
else
{
FindViewById<ImageButton>(Resource.Id.fingerprintbtn).Visibility = ViewStates.Gone;
}
}
private void InitFingerprintUnlock()
{
var btn = FindViewById<ImageButton>(Resource.Id.fingerprintbtn);
try
{
FingerprintUnlockMode um;
Enum.TryParse(_prefs.GetString(Database.GetFingerprintModePrefKey(_ioConnection), ""), out um);
btn.Visibility = (um == FingerprintUnlockMode.FullUnlock) ? ViewStates.Visible : ViewStates.Gone;
if (um != FingerprintUnlockMode.FullUnlock)
{
return;
}
FingerprintModule fpModule = new FingerprintModule(this);
_fingerprintDec = new FingerprintDecryption(fpModule, Database.GetFingerprintPrefKey(_ioConnection), this,
Database.GetFingerprintPrefKey(_ioConnection));
btn.Tag = GetString(Resource.String.fingerprint_unlock_hint);
if (_fingerprintDec.InitCipher())
{
btn.SetImageResource(Resource.Drawable.ic_fp_40px);
_fingerprintDec.StartListening(new FingerprintAuthCallbackAdapter(this, this));
}
else
{
//key invalidated permanently
btn.SetImageResource(Resource.Drawable.ic_fingerprint_error);
btn.Tag = GetString(Resource.String.fingerprint_unlock_failed);
_fingerprintDec = null;
ClearFingerprintUnlockData();
}
}
catch (Exception e)
{
btn.SetImageResource(Resource.Drawable.ic_fingerprint_error);
btn.Tag = "Error initializing Fingerprint Unlock: " + e;
_fingerprintDec = null;
}
}
private void InitializeOptionCheckboxes() {
CheckBox cbQuickUnlock = (CheckBox)FindViewById(Resource.Id.enable_quickunlock);
cbQuickUnlock.Checked = _prefs.GetBoolean(GetString(Resource.String.QuickUnlockDefaultEnabled_key), true);
@ -1788,15 +1925,16 @@ namespace keepass2android
public override void Run()
{
if ( Success )
if (Success)
{
_act.ClearEnteredPassword();
_act.BroadcastOpenDatabase();
GC.Collect(); // Ensure temporary memory used while loading is collected
}
}
if ((Message != null) && (Message.Length > 150)) //show long messages as dialog
{
new AlertDialog.Builder(_act).SetMessage(Message)
@ -1886,5 +2024,11 @@ namespace keepass2android
}
}
public interface IFingerprintAuthCallback
{
void OnFingerprintAuthSucceeded();
void OnFingerprintError(string toString);
}
}

View File

@ -98,4 +98,5 @@
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="keepass2android.keepass2android_debug.permission.KP2aInternalFileBrowsing" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
</manifest>

View File

@ -101,4 +101,5 @@
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="keepass2android.keepass2android.permission.KP2aInternalFileBrowsing" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
</manifest>

View File

@ -79,4 +79,5 @@
</application>
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
</manifest>

View File

@ -160,13 +160,24 @@
android:paddingBottom="12dp"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:orientation="vertical">
android:orientation="horizontal">
<ImageButton
android:id="@+id/toggle_password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:src="@drawable/ic_menu_view"
android:background="?android:selectableItemBackground" />
<ImageButton
android:id="@+id/fingerprintbtn"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_margin="4dp"
android:layout_alignParentBottom="true"
android:src="@drawable/ic_fp_40px"
android:scaleType="fitXY"
android:background="?android:selectableItemBackground" />
</LinearLayout>
</RelativeLayout>
</RelativeLayout>

View File

@ -38,5 +38,9 @@
<color name="emphasis">#31b6e7</color>
<color name="emphasis2">#4f7a8a</color>
<color name="warning_color">#f4511e</color>
<color name="hint_color">#42000000</color>
<color name="success_color">#009688</color>
</resources>

View File

@ -49,6 +49,7 @@
<string name="FileHandling_prefs">File handling</string>
<string name="keyboard_prefs">Keyboard</string>
<string name="export_prefs">Export database...</string>
<string name="fingerprint_prefs">Fingerprint unlock</string>
<string name="import_db_prefs">Import database to internal folder</string>
<string name="import_keyfile_prefs">Import key file to internal folder</string>
<string name="keyboardswitch_prefs">Keyboard switching</string>
@ -79,6 +80,26 @@
<string name="disclaimer_formal">Keepass2Android comes with ABSOLUTELY NO WARRANTY; This is free software, and you are welcome to redistribute it under the conditions of the GPL version 2 or later.</string>
<string name="ellipsis">\u2026</string>
<string name="copy_to_clipboard">Copy to clipboard</string>
<string name="fingerprint_hint">Touch sensor</string>
<string name="fingerprint_description">Confirm fingerprint to continue</string>
<string name="fingerprint_fatal">Cannot setup Fingerprint Unlock:</string>
<string name="fingerprint_not_recognized">Fingerprint not recognized. Try again</string>
<string name="fingerprint_success">Fingerprint recognized</string>
<string name="fingerprint_os_error">Fingerprint Unlock requires Android 6.0 or later.</string>
<string name="fingerprint_hardware_error">No fingerprint hardware detected.</string>
<string name="fingerprint_no_enrolled">You have no enrolled fingerprints on this device. Please go to system settings first.</string>
<string name="disable_fingerprint_unlock">Disable Fingerprint Unlock</string>
<string name="enable_fingerprint_unlock">Enable full Fingerprint Unlock</string>
<string name="enable_fingerprint_quickunlock">Enable Fingerprint Unlock for QuickUnlock</string>
<string name="fingerprint_unlock_hint">Touch sensor to unlock database</string>
<string name="fingerprint_unlock_failed">Fingerprint Unlock failed. Decryption key was invalidated by Android OS. This usually happens if a new fingerprint was enrolled or security settings were changed. Please unlock with your password and then re-enabled Fingerprint Unlock in the database settings.</string>
<string name="enable_fingerprint_unlock_Info">
This will store your master password on this device,
encrypted with the Android Keystore and protected
using fingerprint authentication. Allows to unlock your database only with your fingerprint.
</string>
<string name="enable_fingerprint_quickunlock_Info">Allows to use fingerprint authentication instead of the QuickUnlock code. Does not require to store any information related to your master password.</string>
<string name="enter_filename">Enter database filename</string>
<string name="entry_accessed">Accessed</string>

View File

@ -51,7 +51,14 @@
<Preference android:key="@string/master_pwd_key"
android:title="@string/menu_change_key"
/>
<PreferenceScreen
android:key="fingerprint_prefs"
android:title="@string/fingerprint_prefs"
>
<intent android:action="kp2a.action.FingerprintSetupActivity"/>
</PreferenceScreen>
<PreferenceScreen
android:key="export_prefs"
android:title="@string/export_prefs"
@ -70,6 +77,7 @@
/>
</PreferenceScreen>
<PreferenceScreen

View File

@ -15,9 +15,10 @@
<AssemblyName>keepass2android</AssemblyName>
<newfilesearch>OnLoad</newfilesearch>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
<TargetFrameworkVersion>v6.0</TargetFrameworkVersion>
<MandroidI18n />
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
<AndroidUseLatestPlatformSdk>False</AndroidUseLatestPlatformSdk>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>True</DebugSymbols>
@ -134,6 +135,8 @@
<Compile Include="EntryActivityClasses\StandardStringView.cs" />
<Compile Include="EntryActivityClasses\ToggleVisibilityPopupMenuItem.cs" />
<Compile Include="EntryActivityClasses\WriteBinaryToFilePopupItem.cs" />
<Compile Include="FingerprintModule.cs" />
<Compile Include="FingerprintSetupActivity.cs" />
<Compile Include="ExportDatabaseActivity.cs" />
<Compile Include="fileselect\FileChooserFileProvider.cs" />
<Compile Include="fileselect\FileStorageSetupActivity.cs" />
@ -142,6 +145,7 @@
<Compile Include="DonateReminder.cs" />
<Compile Include="app\ApplicationBroadcastReceiver.cs" />
<Compile Include="ChangeLog.cs" />
<Compile Include="FingerprintUnlockMode.cs" />
<Compile Include="icons\DrawableFactory.cs" />
<Compile Include="KeeChallenge.cs" />
<Compile Include="FixedDrawerLayout.cs" />
@ -269,6 +273,9 @@
<AndroidResource Include="Resources\layout\toolbar.axml">
<SubType>AndroidResource</SubType>
</AndroidResource>
<AndroidResource Include="Resources\layout\fingerprint_setup.xml">
<SubType>Designer</SubType>
</AndroidResource>
<None Include="settings\RoundsPreference %28Kopie%29.cs">
<Visible>False</Visible>
</None>
@ -722,6 +729,7 @@
<Folder Include="Resources\drawable-hdpi\" />
<Folder Include="Resources\drawable-ldpi\" />
<Folder Include="Resources\drawable-xxhdpi\" />
<Folder Include="Resources\values-iw\" />
<Folder Include="SupportLib\" />
</ItemGroup>
<ItemGroup>
@ -839,12 +847,6 @@
<SubType>Designer</SubType>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<XamarinComponentReference Include="xamandroidsupportdesign">
<Version>22.2.0.0</Version>
<Visible>False</Visible>
</XamarinComponentReference>
</ItemGroup>
<ItemGroup>
<AndroidAsset Include="Assets\LICENSE_SourceCodePro.txt" />
</ItemGroup>
@ -1641,5 +1643,23 @@
<ItemGroup>
<AndroidResource Include="Resources\layout\image_list_preference_row.xml" />
</ItemGroup>
<ItemGroup>
<XamarinComponentReference Include="xamandroidsupportdesign">
<Visible>False</Visible>
<Version>23.0.1.3</Version>
</XamarinComponentReference>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\ic_fingerprint_error.xml" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\ic_fingerprint_success.xml" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-xhdpi\ic_fp_40px.png" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable-mdpi\ic_fp_40px.png" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
</Project>