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

Sticky Authentication with Fingerprints (droidcon Vienna)

9ab0b3b080e75e0c03a0c643333f8b93?s=47 Segun Famisa
September 22, 2018

Sticky Authentication with Fingerprints (droidcon Vienna)

A quick talk exploring the new Fingerprint authentication API introduced in Android P.

Presented at droidcon Vienna 2018 (droidcon.at)

9ab0b3b080e75e0c03a0c643333f8b93?s=128

Segun Famisa

September 22, 2018
Tweet

Transcript

  1. Sticky Authentication with Fingerprints droidcon Vienna, 2018

  2. segunfamisa segunfamisa.com

  3. Some background story

  4. #Throwback First introduced to Android in M

  5. Why should I care?

  6. • Easy and quick Why should I care?

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

    from peeking while they type in their password
  8. Why should I care? https://findbiometrics.com/acuity-biometric-smartphones-are-officially-mainstream-302125/

  9. Why should I care? https://www.statista.com/statistics/522058/global-smartphone-fingerprint-penetration/

  10. With the new feature came an API FingerprintManager Allows you

    to listen for fingerprint authentication events
  11. With FingerprintManager came design guidelines

  12. ...but still,inconsistent UI across apps

  13. The new FingerprintDialog API

  14. The new FingerprintDialog API

  15. The new FingerprintDialog API BiometricPrompt

  16. BiometricPrompt API • Known as FingerprintDialog API in early versions

    of P Preview (as at the time of submission of this talk)
  17. 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
  18. 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
  19. Before going further, let’s understand some terms

  20. 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
  21. Android Keystore Hardware backed keystore that lets you store cryptographic

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

    data Usually a set of operations or transformations to be performed on inputs
  23. KeyGenerator Generates symmetric (secret) key based on different algorithms. E.g

    AES, HMAC-SHA
  24. Authenticating with BiometricPrompt API

  25. <manifest ... <uses-permission android:name="android.permission.USE_FINGERPRINT" /> </manifest> Previously, requesting permission looked

    like: 0. Request permission
  26. 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:
  27. 1. Check device requirements if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { //

    Proceed with fingerprint authentication } else { // Fallback to password or manual authentication }
  28. 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) {...}
  29. 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()) {...}
  30. 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()) {...}
  31. 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
  32. 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
  33. 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
  34. 2. Create & initialize a cipher

  35. 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
  36. 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
  37. 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 }
  38. 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)
  39. 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 }
  40. 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 } }
  41. 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 } } }
  42. 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
  43. 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
  44. 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
  45. Authenticating with the new APIs // Create builder val builder

    = BiometricPrompt.Builder(this) .setTitle("Confirm purchase") .setSubtitle("Schnitzel - €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
  46. Authenticating with the new APIs // Create builder val builder

    = BiometricPrompt.Builder(this) .setTitle("Confirm purchase") .setSubtitle("Schnitzel - €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)
  47. BiometricPrompt in practice Negative button Description text Subtitle Title

  48. Handling results

  49. 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 } }
  50. 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 } }
  51. 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
  52. 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 } }
  53. 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 } }
  54. 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 } }
  55. Handling results builder.authenticate() onAuthenticationHelp() onAuthenticationFailed() Recoverable events

  56. onAuthenticationError() onAuthenticationSucceeded() Handling results builder.authenticate() onAuthenticationHelp() onAuthenticationFailed() Non-recoverable (terminal) events

    Recoverable events
  57. Last words

  58. Last words... • Provide alternative ways to authenticate (password, OTP,

    and other ways)
  59. 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
  60. 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
  61. 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
  62. 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 • No Compat APIs yet
  63. 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 • No Compat APIs yet • Build UIs for pre-P devices to look like the new system UI
  64. Thank you! segunfamisa