1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-25 12:43:39 +00:00

Invalidate biometric on change (#1026)

* Initial working version for Android

* Add a fallback for when upgrading from older app version.

* Ensure biometric validity is re-checked on focus

* Only setup biometric integrity key if biometric is turned on.

* Fix styling according to comments

* Fallback for Android 5.

* Improve comment

* Add boilerplate for iOS

* Change BiometricService to public

* Untested iOS implementation.

* Convert IBiometricService to async. Fix code style for iOS.

* Base64 NSData.

* Review comments for Android BiometricService.

* Rename methods in BiometricService to append Async

* Ensure we wait for async SetupBiometricAsync.

* Update BiometricService.cs

Co-authored-by: Kyle Spearrin <kspearrin@users.noreply.github.com>
This commit is contained in:
Oscar Hinton
2020-08-09 03:33:49 +02:00
committed by GitHub
parent 39de2c1d25
commit ae28de4159
12 changed files with 218 additions and 20 deletions

View File

@@ -133,6 +133,7 @@
<Compile Include="MainActivity.cs" />
<Compile Include="Resources\Resource.designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services\BiometricService.cs" />
<Compile Include="Services\CryptoPrimitiveService.cs" />
<Compile Include="Services\DeviceActionService.cs" />
<Compile Include="Services\LocalizeService.cs" />

View File

@@ -98,6 +98,7 @@ namespace Bit.Droid
broadcasterService, () => ServiceContainer.Resolve<IEventService>("eventService"));
var platformUtilsService = new MobilePlatformUtilsService(deviceActionService, messagingService,
broadcasterService);
var biometricService = new BiometricService();
ServiceContainer.Register<IBroadcasterService>("broadcasterService", broadcasterService);
ServiceContainer.Register<IMessagingService>("messagingService", messagingService);
@@ -108,6 +109,7 @@ namespace Bit.Droid
ServiceContainer.Register<IStorageService>("secureStorageService", secureStorageService);
ServiceContainer.Register<IDeviceActionService>("deviceActionService", deviceActionService);
ServiceContainer.Register<IPlatformUtilsService>("platformUtilsService", platformUtilsService);
ServiceContainer.Register<IBiometricService>("biometricService", biometricService);
// Push
#if FDROID

View File

@@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Android.OS;
using Android.Security.Keystore;
using Bit.Core.Abstractions;
using Java.Security;
using Javax.Crypto;
namespace Bit.Droid.Services
{
public class BiometricService : IBiometricService
{
private const string KeyName = "com.8bit.bitwarden.biometric_integrity";
private const string KeyStoreName = "AndroidKeyStore";
private const string KeyAlgorithm = KeyProperties.KeyAlgorithmAes;
private const string BlockMode = KeyProperties.BlockModeCbc;
private const string EncryptionPadding = KeyProperties.EncryptionPaddingPkcs7;
private const string Transformation = KeyAlgorithm + "/" + BlockMode + "/" + EncryptionPadding;
private readonly KeyStore _keystore;
public BiometricService()
{
_keystore = KeyStore.GetInstance(KeyStoreName);
_keystore.Load(null);
}
public Task<bool> SetupBiometricAsync()
{
if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
{
CreateKey();
}
return Task.FromResult(true);
}
public Task<bool> ValidateIntegrityAsync()
{
if (Build.VERSION.SdkInt < BuildVersionCodes.M)
{
return Task.FromResult(true);
}
_keystore.Load(null);
IKey key = _keystore.GetKey(KeyName, null);
Cipher cipher = Cipher.GetInstance(Transformation);
try
{
cipher.Init(CipherMode.EncryptMode, key);
}
catch (KeyPermanentlyInvalidatedException e)
{
// Biometric has changed
return Task.FromResult(false);
}
catch (UnrecoverableKeyException e)
{
// Biometric was disabled and re-enabled
return Task.FromResult(false);
}
catch (InvalidKeyException e)
{
// Fallback for old bitwarden users without a key
CreateKey();
}
return Task.FromResult(false);
}
private void CreateKey()
{
KeyGenerator keyGen = KeyGenerator.GetInstance(KeyAlgorithm, KeyStoreName);
KeyGenParameterSpec keyGenSpec =
new KeyGenParameterSpec.Builder(KeyName, KeyStorePurpose.Encrypt | KeyStorePurpose.Decrypt)
.SetBlockModes(BlockMode)
.SetEncryptionPaddings(EncryptionPadding)
.SetUserAuthenticationRequired(true)
.Build();
keyGen.Init(keyGenSpec);
keyGen.GenerateKey();
}
}
}