diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..d47b889f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "SamsungPass"] + path = SamsungPass + url = https://github.com/sraiteri/Xamarin-Samsung-Pass.git +[submodule "src/SamsungPass"] + path = src/SamsungPass + url = https://github.com/sraiteri/Xamarin-Samsung-Pass.git diff --git a/src/KeePass.sln b/src/KeePass.sln index e0ad92d3..bc7325c6 100644 --- a/src/KeePass.sln +++ b/src/KeePass.sln @@ -47,6 +47,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AncientIconSet", "AncientIc EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FingerprintTest", "FingerprintTest\FingerprintTest.csproj", "{52C0A0E7-D625-44BE-948E-D98BC6C82F0F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SamsungPass", "SamsungPass\Xamarin.SamsungPass\SamsungPass\SamsungPass.csproj", "{3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -524,6 +526,24 @@ Global {52C0A0E7-D625-44BE-948E-D98BC6C82F0F}.ReleaseNoNet|Mixed Platforms.Deploy.0 = Release|Any CPU {52C0A0E7-D625-44BE-948E-D98BC6C82F0F}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU {52C0A0E7-D625-44BE-948E-D98BC6C82F0F}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU + {3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Debug|Win32.ActiveCfg = Debug|Any CPU + {3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Debug|x64.ActiveCfg = Debug|Any CPU + {3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Release|Any CPU.Build.0 = Release|Any CPU + {3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Release|Win32.ActiveCfg = Release|Any CPU + {3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.Release|x64.ActiveCfg = Release|Any CPU + {3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU + {3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU + {3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU + {3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU + {3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU + {3A4B8E88-FA9B-4663-BCDA-21C12E3AF98A}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/SamsungPass b/src/SamsungPass new file mode 160000 index 00000000..1495b1ad --- /dev/null +++ b/src/SamsungPass @@ -0,0 +1 @@ +Subproject commit 1495b1ad7914fa2be69e111b90039770d101aa3d diff --git a/src/keepass2android/FingerprintModule.cs b/src/keepass2android/FingerprintModule.cs index 6abac0c9..3afcaeb1 100644 --- a/src/keepass2android/FingerprintModule.cs +++ b/src/keepass2android/FingerprintModule.cs @@ -17,6 +17,12 @@ using Javax.Crypto.Spec; namespace keepass2android { + public interface IFingerprintAuthCallback + { + void OnFingerprintAuthSucceeded(); + void OnFingerprintError(string toString); + } + public class FingerprintModule { public Context Context { get; set; } @@ -106,7 +112,7 @@ namespace keepass2android } } - public abstract class FingerprintCrypt: FingerprintManager.AuthenticationCallback + public abstract class FingerprintCrypt: FingerprintManager.AuthenticationCallback, IFingerprintIdentifier { protected const string FailedToInitCipher = "Failed to init Cipher"; public override void OnAuthenticationError(FingerprintState errorCode, ICharSequence errString) @@ -157,7 +163,9 @@ namespace keepass2android } - public abstract bool InitCipher(); + public abstract bool Init(); + + protected static string GetAlias(string keyId) { return "keepass2android." + keyId; @@ -175,6 +183,11 @@ namespace keepass2android } } + public void StartListening(Context ctx, IFingerprintAuthCallback callback) + { + StartListening(new FingerprintAuthCallbackAdapter(callback, ctx)); + } + public void StartListening(FingerprintManager.AuthenticationCallback callback) { if (!IsFingerprintAuthAvailable) @@ -227,6 +240,13 @@ namespace keepass2android } } + public interface IFingerprintIdentifier + { + bool Init(); + void StartListening(Context ctx, IFingerprintAuthCallback callback); + void StopListening(); + } + public class FingerprintDecryption : FingerprintCrypt { private readonly Context _context; @@ -245,9 +265,9 @@ namespace keepass2android _iv = Base64.Decode(PreferenceManager.GetDefaultSharedPreferences(context).GetString(GetIvPrefKey(prefKey), null), 0); } - public override bool InitCipher() + public override bool Init() { - Kp2aLog.Log("FP: InitCipher for Dec"); + Kp2aLog.Log("FP: Init for Dec"); try { _keystore.Load(null); @@ -355,9 +375,9 @@ namespace keepass2android } } - public override bool InitCipher() + public override bool Init() { - Kp2aLog.Log("FP: InitCipher for Enc "); + Kp2aLog.Log("FP: Init for Enc "); try { _keystore.Load(null); diff --git a/src/keepass2android/FingerprintSamsungIdentifier.cs b/src/keepass2android/FingerprintSamsungIdentifier.cs new file mode 100644 index 00000000..55773fd4 --- /dev/null +++ b/src/keepass2android/FingerprintSamsungIdentifier.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Android.App; +using Android.Content; +using Android.OS; +using Android.Runtime; +using Android.Util; +using Android.Views; +using Android.Widget; +using Com.Samsung.Android.Sdk; +using Com.Samsung.Android.Sdk.Pass; +using Java.Lang; + +namespace keepass2android +{ + class FingerprintSamsungIdentifier: IFingerprintIdentifier + { + SpassFingerprint _spassFingerprint; + Spass _spass; + public FingerprintSamsungIdentifier(Context context) + { + _spass = new Spass(); + + try + { + _spass.Initialize(context); + } + catch (SecurityException) + { + //"Did you add the permission to the AndroidManifest.xml?"); + throw; + } + + if (_spass.IsFeatureEnabled(Spass.DeviceFingerprint)) + { + _spassFingerprint = new SpassFingerprint(context); + } + else + { + throw new RuntimeException("Fingerprint Featue not available."); + } + + + } + + public bool Init() + { + try + { + return _spassFingerprint.HasRegisteredFinger; + } + catch (UnsupportedOperationException) + { + return false; + } + + + } + class IdentifyListener : Java.Lang.Object, IIdentifyListener + { + private readonly IFingerprintAuthCallback _callback; + private readonly Context _context; + private readonly FingerprintSamsungIdentifier _id; + + + public IdentifyListener(IFingerprintAuthCallback callback, Context context, FingerprintSamsungIdentifier id) + { + _callback = callback; + _context = context; + _id = id; + } + + + + public void OnFinished (int responseCode) + { + _id.Listening = false; + if (responseCode == SpassFingerprint.StatusAuthentificationSuccess) + { + _callback.OnFingerprintAuthSucceeded(); + } + else if (responseCode == SpassFingerprint.StatusAuthentificationPasswordSuccess) + { + _callback.OnFingerprintAuthSucceeded(); + } + + } + + public void OnReady () + { + + } + + public void OnStarted () + { + + } + } + + internal bool Listening + { + get; set; + } + + + public void StartListening(Context ctx, IFingerprintAuthCallback callback) + { + if (Listening) return; + + try + { + _spassFingerprint.StartIdentifyWithDialog(ctx, new IdentifyListener(callback, ctx, this), false); + Listening = true; + } + catch (SpassInvalidStateException m) + { + callback.OnFingerprintError(m.Message); + } + catch (IllegalStateException ex) + { + callback.OnFingerprintError(ex.Message); + } + } + + public void StopListening() + { + try + { + _spassFingerprint.CancelIdentify(); + Listening = false; + } + catch (IllegalStateException ise) + { + Kp2aLog.Log(ise.Message); + } + } + } +} \ No newline at end of file diff --git a/src/keepass2android/FingerprintSetupActivity.cs b/src/keepass2android/FingerprintSetupActivity.cs index aaef9ed7..bf933fce 100644 --- a/src/keepass2android/FingerprintSetupActivity.cs +++ b/src/keepass2android/FingerprintSetupActivity.cs @@ -24,7 +24,7 @@ namespace keepass2android ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden, Theme = "@style/MyTheme_ActionBar", MainLauncher = false)] [IntentFilter(new[] { "kp2a.action.FingerprintSetupActivity" }, Categories = new[] { Intent.CategoryDefault })] - public class FingerprintSetupActivity : LockCloseActivity + public class FingerprintSetupActivity : LockCloseActivity, IFingerprintAuthCallback { private readonly ActivityDesign _activityDesign; @@ -119,8 +119,29 @@ namespace keepass2android FindViewById(Resource.Id.radio_buttons).Visibility = ViewStates.Gone; FindViewById(Resource.Id.fingerprint_auth_container).Visibility = ViewStates.Gone; - if ((int)Build.VERSION.SdkInt >= 23) - RequestPermissions(new[] { Manifest.Permission.UseFingerprint }, FingerprintPermissionRequestCode); + if ((int) Build.VERSION.SdkInt >= 23) + RequestPermissions(new[] {Manifest.Permission.UseFingerprint}, FingerprintPermissionRequestCode); + else + { + try + { + //try to create a Samsung ID object + _samsungFingerprint = new FingerprintSamsungIdentifier(this); + if (!_samsungFingerprint.Init()) + { + SetError(Resource.String.fingerprint_no_enrolled); + } + ShowRadioButtons(); + } + catch (Exception) + { + _samsungFingerprint = null; + } + + } + FindViewById(Resource.Id.container_fingerprint_unlock).Visibility = _samsungFingerprint == null + ? ViewStates.Visible + : ViewStates.Gone; } string CurrentPreferenceKey @@ -181,18 +202,32 @@ namespace keepass2android SetError(Resource.String.fingerprint_no_enrolled); return; } - FindViewById(Resource.Id.tvFatalError).Visibility = ViewStates.Gone; - FindViewById(Resource.Id.radio_buttons).Visibility = ViewStates.Visible; - FindViewById(Resource.Id.fingerprint_auth_container).Visibility = ViewStates.Gone; + ShowRadioButtons(); } } - + + private void ShowRadioButtons() + { + FindViewById(Resource.Id.tvFatalError).Visibility = ViewStates.Gone; + FindViewById(Resource.Id.radio_buttons).Visibility = ViewStates.Visible; + FindViewById(Resource.Id.fingerprint_auth_container).Visibility = ViewStates.Gone; + } + private void ChangeUnlockMode(FingerprintUnlockMode oldMode, FingerprintUnlockMode newMode) { if (oldMode == newMode) return; + if (_samsungFingerprint != null) + { + _unlockMode = newMode; + ISharedPreferencesEditor edit = PreferenceManager.GetDefaultSharedPreferences(this).Edit(); + edit.PutString(App.Kp2a.GetDb().CurrentFingerprintModePrefKey, _unlockMode.ToString()); + edit.Commit(); + return; + } + if (newMode == FingerprintUnlockMode.Disabled) { _unlockMode = newMode; @@ -207,10 +242,10 @@ namespace keepass2android _enc = new FingerprintEncryption(new FingerprintModule(this), CurrentPreferenceKey); try { - if (!_enc.InitCipher()) + if (!_enc.Init()) throw new Exception("Failed to initialize cipher"); ResetErrorTextRunnable(); - _enc.StartListening(new SetupCallback(this)); + _enc.StartListening(new FingerprintAuthCallbackAdapter(this, this)); } catch (Exception e) { @@ -227,7 +262,10 @@ namespace keepass2android static readonly long SUCCESS_DELAY_MILLIS = 1300; private ImageView _fpIcon; private TextView _fpTextView; - public void OnAuthSucceeded() + + private FingerprintSamsungIdentifier _samsungFingerprint; + + public void OnFingerprintAuthSucceeded() { _unlockMode = _desiredUnlockMode; @@ -248,6 +286,7 @@ namespace keepass2android } + public void OnFingerprintError(string error) { _fpIcon.SetImageResource(Resource.Drawable.ic_fingerprint_error); @@ -270,7 +309,7 @@ namespace keepass2android { base.OnResume(); if (_enc != null) - _enc.StartListening(new SetupCallback(this)); + _enc.StartListening(new FingerprintAuthCallbackAdapter(this, this)); } protected override void OnPause() @@ -281,33 +320,4 @@ namespace keepass2android } } - internal class SetupCallback : FingerprintManager.AuthenticationCallback - { - private readonly FingerprintSetupActivity _fingerprintSetupActivity; - - public SetupCallback(FingerprintSetupActivity fingerprintSetupActivity) - { - _fingerprintSetupActivity = fingerprintSetupActivity; - } - - public override void OnAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) - { - _fingerprintSetupActivity.OnAuthSucceeded(); - } - - public override void OnAuthenticationError(FingerprintState errorCode, ICharSequence errString) - { - _fingerprintSetupActivity.OnFingerprintError(errString.ToString()); - } - - public override void OnAuthenticationHelp(FingerprintState helpCode, ICharSequence helpString) - { - _fingerprintSetupActivity.OnFingerprintError(helpString.ToString()); - } - - public override void OnAuthenticationFailed() - { - _fingerprintSetupActivity.OnFingerprintError(_fingerprintSetupActivity.Resources.GetString(Resource.String.fingerprint_not_recognized)); - } - } } \ No newline at end of file diff --git a/src/keepass2android/KeeChallenge.cs b/src/keepass2android/KeeChallenge.cs index 78b1d278..880103e7 100644 --- a/src/keepass2android/KeeChallenge.cs +++ b/src/keepass2android/KeeChallenge.cs @@ -64,8 +64,7 @@ namespace KeeChallenge private byte[] GenerateChallenge() { - CryptoRandom rand = CryptoRandom.Instance; - byte[] chal = CryptoRandom.Instance.GetRandomBytes(challengeLenBytes); + byte[] chal = CryptoRandom.Instance.GetRandomBytes(challengeLenBytes); if (LT64) { chal[challengeLenBytes - 2] = (byte)~chal[challengeLenBytes - 1]; diff --git a/src/keepass2android/PasswordActivity.cs b/src/keepass2android/PasswordActivity.cs index c935c759..3bbfa812 100644 --- a/src/keepass2android/PasswordActivity.cs +++ b/src/keepass2android/PasswordActivity.cs @@ -700,6 +700,7 @@ namespace keepass2android private ActivityDesign _activityDesign; private FingerprintDecryption _fingerprintDec; private bool _fingerprintPermissionGranted; + private PasswordActivityBroadcastReceiver _intentReceiver; internal class MyActionBarDrawerToggle : ActionBarDrawerToggle @@ -756,6 +757,11 @@ namespace keepass2android { _activityDesign.ApplyTheme(); base.OnCreate(savedInstanceState); + + _intentReceiver = new PasswordActivityBroadcastReceiver(this); + IntentFilter filter = new IntentFilter(); + filter.AddAction(Intent.ActionScreenOff); + RegisterReceiver(_intentReceiver, filter); //use FlagSecure to make sure the last (revealed) character of the master password is not visible in recent apps @@ -1800,7 +1806,7 @@ namespace keepass2android btn.Tag = GetString(Resource.String.fingerprint_unlock_hint); - if (_fingerprintDec.InitCipher()) + if (_fingerprintDec.Init()) { btn.SetImageResource(Resource.Drawable.ic_fp_40px); _fingerprintDec.StartListening(new FingerprintAuthCallbackAdapter(this, this)); @@ -2044,13 +2050,36 @@ namespace keepass2android } } - + private class PasswordActivityBroadcastReceiver : BroadcastReceiver + { + readonly PasswordActivity _activity; + public PasswordActivityBroadcastReceiver(PasswordActivity activity) + { + _activity = activity; + } + + public override void OnReceive(Context context, Intent intent) + { + switch (intent.Action) + { + case Intent.ActionScreenOff: + Kp2aLog.Log("bla"); + _activity.OnScreenLocked(); + break; + } + } + } + + private void OnScreenLocked() + { + if (_fingerprintDec != null) + _fingerprintDec.StopListening(); + + } } - public interface IFingerprintAuthCallback - { - void OnFingerprintAuthSucceeded(); - void OnFingerprintError(string toString); - } + + + } diff --git a/src/keepass2android/Properties/AndroidManifest_debug.xml b/src/keepass2android/Properties/AndroidManifest_debug.xml index 860c126f..c923fadf 100644 --- a/src/keepass2android/Properties/AndroidManifest_debug.xml +++ b/src/keepass2android/Properties/AndroidManifest_debug.xml @@ -99,4 +99,6 @@ + + \ No newline at end of file diff --git a/src/keepass2android/Properties/AndroidManifest_net.xml b/src/keepass2android/Properties/AndroidManifest_net.xml index ab164aae..e2b36094 100644 --- a/src/keepass2android/Properties/AndroidManifest_net.xml +++ b/src/keepass2android/Properties/AndroidManifest_net.xml @@ -102,4 +102,6 @@ + + \ No newline at end of file diff --git a/src/keepass2android/Properties/AndroidManifest_nonet.xml b/src/keepass2android/Properties/AndroidManifest_nonet.xml index 951fd68e..99e57912 100644 --- a/src/keepass2android/Properties/AndroidManifest_nonet.xml +++ b/src/keepass2android/Properties/AndroidManifest_nonet.xml @@ -80,4 +80,6 @@ + + \ No newline at end of file diff --git a/src/keepass2android/QuickUnlock.cs b/src/keepass2android/QuickUnlock.cs index 088c46ba..e02fae8a 100644 --- a/src/keepass2android/QuickUnlock.cs +++ b/src/keepass2android/QuickUnlock.cs @@ -43,7 +43,7 @@ namespace keepass2android private QuickUnlockBroadcastReceiver _intentReceiver; private ActivityDesign _design; private bool _fingerprintPermissionGranted; - private FingerprintDecryption _fingerprintDec; + private IFingerprintIdentifier _fingerprintIdentifier; private int _quickUnlockLength; private const int FingerprintPermissionRequestCode = 0; @@ -139,8 +139,12 @@ namespace keepass2android filter.AddAction(Intents.DatabaseLocked); RegisterReceiver(_intentReceiver, filter); - if ((int)Build.VERSION.SdkInt >= 23) - RequestPermissions(new[] { Manifest.Permission.UseFingerprint }, FingerprintPermissionRequestCode); + if ((int) Build.VERSION.SdkInt >= 23) + RequestPermissions(new[] {Manifest.Permission.UseFingerprint}, FingerprintPermissionRequestCode); + else + { + + } } @@ -176,7 +180,7 @@ namespace keepass2android public void OnFingerprintAuthSucceeded() { - _fingerprintDec.StopListening(); + _fingerprintIdentifier.StopListening(); var btn = FindViewById(Resource.Id.fingerprintbtn); btn.SetImageResource(Resource.Drawable.ic_fingerprint_success); @@ -207,23 +211,42 @@ namespace keepass2android return; } - FingerprintModule fpModule = new FingerprintModule(this); - _fingerprintDec = new FingerprintDecryption(fpModule, App.Kp2a.GetDb().CurrentFingerprintPrefKey, this, - App.Kp2a.GetDb().CurrentFingerprintPrefKey); - + if (_fingerprintPermissionGranted) + { + FingerprintModule fpModule = new FingerprintModule(this); + _fingerprintIdentifier = new FingerprintDecryption(fpModule, App.Kp2a.GetDb().CurrentFingerprintPrefKey, this, + App.Kp2a.GetDb().CurrentFingerprintPrefKey); + } + else + { + try + { + _fingerprintIdentifier = new FingerprintSamsungIdentifier(this); + btn.Click += (sender, args) => + { + if (_fingerprintIdentifier.Init()) + _fingerprintIdentifier.StartListening(this, this); + }; + } + catch (Exception) + { + FindViewById(Resource.Id.fingerprintbtn).Visibility = ViewStates.Gone; + return; + } + } btn.Tag = GetString(Resource.String.fingerprint_unlock_hint); - if (_fingerprintDec.InitCipher()) + if (_fingerprintIdentifier.Init()) { btn.SetImageResource(Resource.Drawable.ic_fp_40px); - _fingerprintDec.StartListening(new FingerprintAuthCallbackAdapter(this, this)); + _fingerprintIdentifier.StartListening(this, this); } else { //key invalidated permanently btn.SetImageResource(Resource.Drawable.ic_fingerprint_error); btn.Tag = GetString(Resource.String.fingerprint_unlock_failed); - _fingerprintDec = null; + _fingerprintIdentifier = null; ClearFingerprintUnlockData(); } @@ -233,7 +256,7 @@ namespace keepass2android btn.SetImageResource(Resource.Drawable.ic_fingerprint_error); btn.Tag = "Error initializing Fingerprint Unlock: " + e; - _fingerprintDec = null; + _fingerprintIdentifier = null; } @@ -294,23 +317,17 @@ namespace keepass2android }, 50); - if (_fingerprintPermissionGranted) - { - InitFingerprintUnlock(); - } - else - { - FindViewById(Resource.Id.fingerprintbtn).Visibility = ViewStates.Gone; - - } + + InitFingerprintUnlock(); + } protected override void OnPause() { base.OnPause(); - if (_fingerprintDec != null) + if (_fingerprintIdentifier != null) { - _fingerprintDec.StopListening(); + _fingerprintIdentifier.StopListening(); } } diff --git a/src/keepass2android/Resources/layout/fingerprint_setup.xml b/src/keepass2android/Resources/layout/fingerprint_setup.xml index 40b8441c..92a3ba40 100644 --- a/src/keepass2android/Resources/layout/fingerprint_setup.xml +++ b/src/keepass2android/Resources/layout/fingerprint_setup.xml @@ -50,6 +50,7 @@ + @@ -756,6 +757,10 @@ {3DA3911E-36DE-465E-8F15-F1991B6437E5} PluginSdkBinding + + {3a4b8e88-fa9b-4663-bcda-21c12e3af98a} + SamsungPass + {5CF675A5-9BEE-4720-BED9-D5BF14A2EBF9} TwofishCipher