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

Sticky Authentication with Fingerprints (droidcon Vienna)

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)

Segun Famisa

September 22, 2018
Tweet

More Decks by Segun Famisa

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("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
  28. 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)
  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 • No Compat APIs 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 • No Compat APIs yet • Build UIs for pre-P devices to look like the new system UI