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. Sticky Authentication
    with Fingerprints
    droidcon Vienna, 2018

    View full-size slide

  2. segunfamisa
    segunfamisa.com

    View full-size slide

  3. Some background story

    View full-size slide

  4. #Throwback
    First introduced to Android in M

    View full-size slide

  5. Why should I care?

    View full-size slide

  6. ● Easy and quick
    Why should I care?

    View full-size slide

  7. Why should I care?
    ● Easy and quick
    ● Safe from peeking while they type in their password

    View full-size slide

  8. Why should I care?
    https://findbiometrics.com/acuity-biometric-smartphones-are-officially-mainstream-302125/

    View full-size slide

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

    View full-size slide

  10. With the new feature came an API
    FingerprintManager
    Allows you to listen for fingerprint
    authentication events

    View full-size slide

  11. With FingerprintManager came design
    guidelines

    View full-size slide

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

    View full-size slide

  13. The new FingerprintDialog API

    View full-size slide

  14. The new FingerprintDialog API

    View full-size slide

  15. The new FingerprintDialog API
    BiometricPrompt

    View full-size slide

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

    View full-size slide

  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

    View full-size slide

  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

    View full-size slide

  19. Before going further, let’s
    understand some terms

    View full-size slide

  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

    View full-size slide

  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.

    View full-size slide

  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

    View full-size slide

  23. KeyGenerator
    Generates symmetric (secret) key based on
    different algorithms.
    E.g AES, HMAC-SHA

    View full-size slide

  24. Authenticating with
    BiometricPrompt API

    View full-size slide

  25. ...


    Previously, requesting permission looked like:
    0. Request permission

    View full-size slide

  26. 0. Request permission
    ...


    Previously, requesting permission looked like:
    ...


    New permission:

    View full-size slide

  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
    }

    View full-size slide

  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) {...}

    View full-size slide

  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()) {...}

    View full-size slide

  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()) {...}

    View full-size slide

  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

    View full-size slide

  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

    View full-size slide

  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

    View full-size slide

  34. 2. Create & initialize a cipher

    View full-size slide

  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

    View full-size slide

  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

    View full-size slide

  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
    }

    View full-size slide

  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)

    View full-size slide

  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
    }

    View full-size slide

  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
    }
    }

    View full-size slide

  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
    }
    }
    }

    View full-size slide

  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

    View full-size slide

  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

    View full-size slide

  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

    View full-size slide

  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

    View full-size slide

  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)

    View full-size slide

  47. BiometricPrompt in practice
    Negative button
    Description text
    Subtitle
    Title

    View full-size slide

  48. Handling results

    View full-size slide

  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
    }
    }

    View full-size slide

  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
    }
    }

    View full-size slide

  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

    View full-size slide

  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
    }
    }

    View full-size slide

  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
    }
    }

    View full-size slide

  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
    }
    }

    View full-size slide

  55. Handling results
    builder.authenticate()
    onAuthenticationHelp() onAuthenticationFailed() Recoverable
    events

    View full-size slide

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

    View full-size slide

  57. Last words...
    ● Provide alternative ways to authenticate (password, OTP,
    and other ways)

    View full-size slide

  58. 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

    View full-size slide

  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
    ● Provide as much info as possible to your users to help
    them

    View full-size slide

  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
    ● Ensure good API practices with signed requests

    View full-size slide

  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
    ● No Compat APIs yet

    View full-size slide

  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
    ● Build UIs for pre-P devices to look like the new system
    UI

    View full-size slide

  63. Thank you!
    segunfamisa

    View full-size slide