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:
@@ -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" />
|
||||
|
||||
@@ -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
|
||||
|
||||
90
src/Android/Services/BiometricService.cs
Normal file
90
src/Android/Services/BiometricService.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user