Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Oluwasegun Famisa - Sticky_Authentication_with_Fingerprints

Oluwasegun Famisa - Sticky_Authentication_with_Fingerprints

droidcon Berlin

July 17, 2018
Tweet

More Decks by droidcon Berlin

Other Decks in Programming

Transcript

  1. Why should I care? • Easy and quick • Safe

    from peeking while they type in their password
  2. With the new feature came an API FingerprintManager Allows you

    to listen for fingerprint authentication events
  3. BiometricPrompt API • Known as FingerprintDialog API in early versions

    of P Preview (as at the time of submission of this talk)
  4. BiometricPrompt API • Known as FingerprintDialog API in early versions

    of P Preview (as at the time of submission of this talk) • Provide consistent and easy API for developers
  5. BiometricPrompt API • Known as FingerprintDialog API in early versions

    of P Preview (as at the time of submission of this talk) • Provide consistent and easy API for developers • Provide consistent system UI that is sensor-agnostic
  6. Symmetric vs Asymmetric Keystore Symmetric: aka “secret key” - same

    key for encryption and decryption Asymmetric: aka “public key” - different keys for encryption and decryption https://www.cs.utexas.edu/users/byoung/cs361/lecture44.pdf
  7. Android Keystore Hardware backed keystore that lets you store cryptographic

    keys in a container and makes it more difficult to extract from the device.
  8. Cipher Loosely speaking - it’s the algorithm used to encrypt/decrypt

    data Usually a set of operations or transformations to be performed on inputs
  9. 0. Request permission <manifest ... <uses-permission android:name="android.permission.USE_FINGERPRINT" /> </manifest> Previously,

    requesting permission looked like: <manifest ... <uses-permission android:name="android.permission.USE_BIOMETRIC" /> </manifest> New permission:
  10. 1. Check device requirements if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { //

    Proceed with fingerprint authentication } else { // Fallback to password or manual authentication }
  11. Check other criteria val fingerprintManager = getSystemService(FingerprintManager::class.java) val keyguardManager =

    getSystemService(KeyguardManager::class.java) // Show error - can't proceed without sensor if (!fingerprintManager.isHardwareDetected) {...}
  12. Check other criteria val fingerprintManager = getSystemService(FingerprintManager::class.java) val keyguardManager =

    getSystemService(KeyguardManager::class.java) // Show error - can't proceed without sensor if (!fingerprintManager.isHardwareDetected) {...} // Show error - can't proceed without fingerprint if (!fingerprintManager.hasEnrolledFingerprints()) {...}
  13. Check other criteria val fingerprintManager = getSystemService(FingerprintManager::class.java) val keyguardManager =

    getSystemService(KeyguardManager::class.java) // Show error - can't proceed without sensor if (!fingerprintManager.isHardwareDetected) {...} // Show error - can't proceed without fingerprint if (!fingerprintManager.hasEnrolledFingerprints()) {...}
  14. Check other criteria val fingerprintManager = getSystemService(FingerprintManager::class.java) val keyguardManager =

    getSystemService(KeyguardManager::class.java) // Show error - can't proceed without sensor if (!fingerprintManager.isHardwareDetected) {...} // Show error - can't proceed without fingerprint if (!fingerprintManager.hasEnrolledFingerprints()) {...} // Proceed with auth
  15. Check other criteria val fingerprintManager = getSystemService(FingerprintManager::class.java) val keyguardManager =

    getSystemService(KeyguardManager::class.java) // Show error - can't proceed without sensor if (!fingerprintManager.isHardwareDetected) {...} // Show error - can't proceed without fingerprint if (!fingerprintManager.hasEnrolledFingerprints()) {...} // Proceed with auth Deprecated
  16. Check other criteria val fingerprintManager = getSystemService(FingerprintManager::class.java) val keyguardManager =

    getSystemService(KeyguardManager::class.java) // Show error - can't proceed without sensor if (!fingerprintManager.isHardwareDetected) {...} // Show error - can't proceed without fingerprint if (!fingerprintManager.hasEnrolledFingerprints()) {...} // Proceed with auth New API offers callbacks to handle errors and unsatisfied criteria
  17. Create key store from the AndroidKeyStore // Create Keystore try

    { keyStore = KeyStore.getInstance("AndroidKeyStore") } catch (e: KeyStoreException) { throw RuntimeException("Failed to get an instance of KeyStore", e) } 2. Create & initialize a cipher
  18. Create KeyGenerator that will be used to create the symmetric

    key // Create Keystore try { keyStore = KeyStore.getInstance("AndroidKeyStore") } catch (e: KeyStoreException) { throw RuntimeException("Failed to get an instance of KeyStore", e) } // Create KeyGenerator (this will be used to create the key) try { keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM_AES, "AndroidKeyStore") } catch (e: Exception) { when (e) { is NoSuchAlgorithmException, is NoSuchProviderException -> throw RuntimeException(...) else -> throw e } 2. Create & initialize a cipher
  19. Use KeyGenerator to generate keys try { keyStore.load(null) val keyProperties

    = KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT val builder = KeyGenParameterSpec.Builder(“default_key”, keyProperties) .setBlockModes(BLOCK_MODE_CBC) .setUserAuthenticationRequired(true) .setEncryptionPaddings(ENCRYPTION_PADDING_PKCS7) .setInvalidatedByBiometricEnrollment(true) // generate keys keyGenerator.init(builder.build()) keyGenerator.generateKey() } catch (e: Exception) { // handle exceptions }
  20. Use KeyGenerator to generate keys try { keyStore.load(null) val keyProperties

    = KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT val builder = KeyGenParameterSpec.Builder(“default_key”, keyProperties) .setBlockModes(BLOCK_MODE_CBC) .setUserAuthenticationRequired(true) .setEncryptionPaddings(ENCRYPTION_PADDING_PKCS7) .setInvalidatedByBiometricEnrollment(true) // generate keys keyGenerator.init(builder.build()) keyGenerator.generateKey() } catch (e: Exception) { // handle exceptions } Key is authorized to be used only when user has been authenticated (e.g unlocked phone)
  21. Key is invalidated when a new fingerprint is added Use

    KeyGenerator to generate keys try { keyStore.load(null) val keyProperties = KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT val builder = KeyGenParameterSpec.Builder(“default_key”, keyProperties) .setBlockModes(BLOCK_MODE_CBC) .setUserAuthenticationRequired(true) .setEncryptionPaddings(ENCRYPTION_PADDING_PKCS7) .setInvalidatedByBiometricEnrollment(true) // generate keys keyGenerator.init(builder.build()) keyGenerator.generateKey() } catch (e: Exception) { // handle exceptions }
  22. Create a cipher // Create a cipher val cipher :

    Cipher try { val cipherString = "$KEY_ALGORITHM_AES/$BLOCK_MODE_CBC/$ENCRYPTION_PADDING_PKCS7" cipher = Cipher.getInstance(cipherString) } catch (e: Exception) { when (e) { is NoSuchAlgorithmException, is NoSuchPaddingException -> throw RuntimeException("Failed to get an instance of Cipher", e) else -> throw e } }
  23. Initialize the cipher fun initCipher(cipher: Cipher): Boolean { try {

    keyStore.load(null) val secretKey = keyStore.getKey("key_name", null) as SecretKey // init cipher.init(Cipher.ENCRYPT_MODE, secretKey) return true } catch (e: Exception) { when (e) { is KeyPermanentlyInvalidatedException -> return false ... else -> throw e } } }
  24. Initialize the cipher fun initCipher(cipher: Cipher): Boolean { try {

    keyStore.load(null) val secretKey = keyStore.getKey("default_key", null) as SecretKey // init cipher.init(Cipher.ENCRYPT_MODE, secretKey) return true } catch (e: Exception) { when (e) { is KeyPermanentlyInvalidatedException -> return false ... else -> throw e } } } Unrecoverably invalidated due to - change in security settings of the phone, or since we configured setInvalidatedByBiometricEnrollment to enrollment of new fingerprint
  25. val cancellationSignal = new CancellationSignal(); val cryptoObject = FingerprintManager.CryptoObject(cipher) //

    Warm up the sensor and start listening for authentication val manager = getSystemService(FingerprintManager::class.java) manager.authenticate(cryptoObject, cancellationSignal, 0, callback, null); 3. Listen for fingerprint auth events Authenticating with the old FingerprintManager
  26. val cancellationSignal = new CancellationSignal(); val cryptoObject = FingerprintManager.CryptoObject(cipher) //

    Warm up the sensor and start listening for authentication val manager = getSystemService(FingerprintManager::class.java) manager.authenticate(cryptoObject, cancellationSignal, 0, callback, null); 3. Listen for fingerprint auth events Authenticating with the old FingerprintManager Deprecated
  27. Authenticating with the new APIs // Create builder val builder

    = BiometricPrompt.Builder(this) .setTitle("Confirm purchase") .setSubtitle("Currywurst - €5,50") .setDescription("Touch the fingerprint sensor....") .setNegativeButton("Cancel", mainExecutor, object : DialogInterface.OnClickListener { override fun onClick(dialog: DialogInterface?, p1: Int) { // handle cancel action } }) .build() 3. Listen for fingerprint auth events
  28. Authenticating with the new APIs // Create builder val builder

    = BiometricPrompt.Builder(this) .setTitle("Confirm purchase") .setSubtitle("Currywurst - €5,50") .setDescription("Touch the fingerprint sensor....") .setNegativeButton("Cancel", mainExecutor, object : DialogInterface.OnClickListener { override fun onClick(dialog: DialogInterface?, p1: Int) { // handle cancel action } }) .build() // Authenticate and listen for events builder.authenticate(BiometricPrompt.CryptoObject(cipher), cancellationSignal, mainExecutor, callback)
  29. Handling results val callback = object : BiometricPrompt.AuthenticationCallback() { override

    fun onAuthenticationError(errorCode: Int, errString: CharSequence?) { // handle error } override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { // handle result } override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence?) { // handle help message if necessary } override fun onAuthenticationFailed() { // fingerprint recognized but doesn't match } }
  30. Handling results val callback = object : BiometricPrompt.AuthenticationCallback() { override

    fun onAuthenticationError(errorCode: Int, errString: CharSequence?) { // handle error } override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { // handle result } override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence?) { // handle help message if necessary } override fun onAuthenticationFailed() { // fingerprint recognized but doesn't match } }
  31. Handling results - error codes BIOMETRIC_ERROR_CANCELED BIOMETRIC_ERROR_HW_NOT_PRESENT BIOMETRIC_ERROR_HW_UNAVAILABLE BIOMETRIC_ERROR_LOCKOUT BIOMETRIC_ERROR_LOCKOUT_PERMANENT

    BIOMETRIC_ERROR_NO_BIOMETRICS BIOMETRIC_ERROR_NO_SPACE BIOMETRIC_ERROR_TIMEOUT BIOMETRIC_ERROR_UNABLE_TO_PROCESS BIOMETRIC_ERROR_USER_CANCELED BIOMETRIC_ERROR_VENDOR
  32. Handling results val callback = object : BiometricPrompt.AuthenticationCallback() { override

    fun onAuthenticationError(errorCode: Int, errString: CharSequence?) { // handle error } override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { // handle result } override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence?) { // handle help message if necessary } override fun onAuthenticationFailed() { // fingerprint recognized but doesn't match } }
  33. Handling results val callback = object : BiometricPrompt.AuthenticationCallback() { override

    fun onAuthenticationError(errorCode: Int, errString: CharSequence?) { // handle error } override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { // handle result } override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence?) { // handle help message if necessary } override fun onAuthenticationFailed() { // fingerprint recognized but doesn't match } }
  34. Handling results val callback = object : BiometricPrompt.AuthenticationCallback() { override

    fun onAuthenticationError(errorCode: Int, errString: CharSequence?) { // handle error } override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { // handle result } override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence?) { // handle help message if necessary } override fun onAuthenticationFailed() { // fingerprint recognized but doesn't match } }
  35. Last words... • Provide alternative ways to authenticate (password, OTP,

    and other ways) • Subtle enrollment - there should be a place in your settings where users can enroll to use fingerprint authentication in your app
  36. Last words... • Provide alternative ways to authenticate (password, OTP,

    and other ways) • Subtle enrollment - there should be a place in your settings where users can enroll to use fingerprint authentication in your app • Provide as much info as possible to your users to help them
  37. Last words... • Provide alternative ways to authenticate (password, OTP,

    and other ways) • Subtle enrollment - there should be a place in your settings where users can enroll to use fingerprint authentication in your app • Provide as much info as possible to your users to help them • Ensure good API practices with signed requests
  38. Last words... • Provide alternative ways to authenticate (password, OTP,

    and other ways) • Subtle enrollment - there should be a place in your settings where users can enroll to use fingerprint authentication in your app • Provide as much info as possible to your users to help them • Ensure good API practices with signed requests • The APIs may not be final yet
  39. Last words... • Provide alternative ways to authenticate (password, OTP,

    and other ways) • Subtle enrollment - there should be a place in your settings where users can enroll to use fingerprint authentication in your app • Provide as much info as possible to your users to help them • Ensure good API practices with signed requests • The APIs may not be final yet • No Compat APIs yet