using System; using System.Threading.Tasks; using Android.OS; using Android.Security.Keystore; using Bit.Core.Abstractions; using Bit.Core.Services; using Bit.Core.Utilities; 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 SetupBiometricAsync(string bioIntegrityKey = null) { // bioIntegrityKey used in iOS only if (Build.VERSION.SdkInt >= BuildVersionCodes.M) { CreateKey(); } return Task.FromResult(true); } public Task ValidateIntegrityAsync(string bioIntegrityKey = null) { if (Build.VERSION.SdkInt < BuildVersionCodes.M) { return Task.FromResult(true); } try { _keystore.Load(null); var key = _keystore.GetKey(KeyName, null); var cipher = Cipher.GetInstance(Transformation); if (key == null || cipher == null) { return Task.FromResult(true); } 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 LoggerHelper.LogEvenIfCantBeResolved(e); CreateKey(); } return Task.FromResult(true); } private void CreateKey() { try { var keyGen = KeyGenerator.GetInstance(KeyAlgorithm, KeyStoreName); var keyGenSpec = new KeyGenParameterSpec.Builder(KeyName, KeyStorePurpose.Encrypt | KeyStorePurpose.Decrypt) .SetBlockModes(BlockMode) .SetEncryptionPaddings(EncryptionPadding) .SetUserAuthenticationRequired(true) .Build(); keyGen.Init(keyGenSpec); keyGen.GenerateKey(); } catch (Exception e) { // Catch silently to allow biometrics to function on devices that are in a state where key generation // is not functioning LoggerHelper.LogEvenIfCantBeResolved(e); } } } }