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

To Protect and Confirm: Droidcon NYC 2018

To Protect and Confirm: Droidcon NYC 2018

Android P introduces new APIs for biometric authentication and securely authorizing actions on behalf of the user.

Let's take a look at what's changed on the biometric front since Droidcon NYC 2016's "Fingerprint Authentication In Action," and check out the new Protected Confirmation API for authorizing sensitive actions.

Ben Oberkfell

August 27, 2018
Tweet

More Decks by Ben Oberkfell

Other Decks in Technology

Transcript

  1. New (And New-Ish) Features In Android 9.0 • New Biometrics

    • BiometricPrompt • Protected Confirmation • Key Attestation
  2. New Biometrics • Android 9 de-emphasizes fingerprint, and generalizes the

    concept of biometric authentication • CDD for Android 9 allows for the inclusion of other biometric methods • Applies equivalent strength metrics to fingerprint
  3. False Acceptance Rate • The chance of accepting an outright

    incorrect input. • The CDD requires that biometric implementations have no worse than 0.002% FAR. • Restated, that’s 2 in 100,000
  4. Spoof Acceptance Rate • The likelihood of accepting a previous

    known good sample (similar to a “replay attack”). • Fingerprint mold • High-res facial photo, face mask • Replaying a voice recording • Iris image on a contact lens
  5. Impostor Acceptance Rate • The likelihood of accepting an attempt

    to mimic a known good input sample • Trying to look like the device owner • Trying to fake tone of voice or accent to sound like the device owner
  6. Strong vs Weak Biometrics • Strong biometrics are mechanisms that

    meet or exceed the 7% SAR/IAR thresholds. • Weak biometrics may unlock a device, but they may not be used to unlock authentication-required keys in the KeyStore. • Weak biometrics are not exposed through the new BiometricPrompt API.
  7. Now

  8. How to Use It val prompt = BiometricPrompt.Builder(this) .setTitle("Place Your

    Order") .setSubtitle("Confirm your delivery order") .setDescription("Deliver to 732 Evergreen Terrace? Total: $36.99") .setNegativeButton("D'oh! Cancel", mainExecutor, cancelOnClickListener) .build() // Wrap a signature and ask to unlock the key val signature = Signature.getInstance("SHA256withRSA") signature.initSign(getPrivateKey()) val cryptoObject = BiometricPrompt.CryptoObject(signature) prompt.authenticate(cryptoObject, cancellationSignal, mainExecutor, callback)
  9. How to Use It val prompt = BiometricPrompt.Builder(this) .setTitle("Place Your

    Order") .setSubtitle("Confirm your delivery order") .setDescription("Deliver to 732 Evergreen Terrace? Total: $36.99") .setNegativeButton("D'oh! Cancel", mainExecutor, cancelOnClickListener) .build() // Wrap a signature and ask to unlock the key val signature = Signature.getInstance("SHA256withRSA") signature.initSign(getPrivateKey()) val cryptoObject = BiometricPrompt.CryptoObject(signature) prompt.authenticate(cryptoObject, cancellationSignal, mainExecutor, callback)
  10. How to Use It val prompt = BiometricPrompt.Builder(this) .setTitle("Place Your

    Order") .setSubtitle("Confirm your delivery order") .setDescription("Deliver to 732 Evergreen Terrace? Total: $36.99") .setNegativeButton("D'oh! Cancel", mainExecutor, cancelOnClickListener) .build() // Wrap a signature and ask to unlock the key val signature = Signature.getInstance("SHA256withRSA") signature.initSign(getPrivateKey()) val cryptoObject = BiometricPrompt.CryptoObject(signature) prompt.authenticate(cryptoObject, cancellationSignal, mainExecutor, callback)
  11. How to Use It val prompt = BiometricPrompt.Builder(this) .setTitle("Place Your

    Order") .setSubtitle("Confirm your delivery order") .setDescription("Deliver to 732 Evergreen Terrace? Total: $36.99") .setNegativeButton("D'oh! Cancel", mainExecutor, cancelOnClickListener) .build() // Wrap a signature and ask to unlock the key val signature = Signature.getInstance("SHA256withRSA") signature.initSign(getPrivateKey()) val cryptoObject = BiometricPrompt.CryptoObject(signature) prompt.authenticate(cryptoObject, cancellationSignal, mainExecutor, callback)
  12. How to Use It val prompt = BiometricPrompt.Builder(this) .setTitle("Place Your

    Order") .setSubtitle("Confirm your delivery order") .setDescription("Deliver to 732 Evergreen Terrace? Total: $36.99") .setNegativeButton("D'oh! Cancel", mainExecutor, cancelOnClickListener) .build() // Wrap a signature and ask to unlock the key val signature = Signature.getInstance("SHA256withRSA") signature.initSign(getPrivateKey()) val cryptoObject = BiometricPrompt.CryptoObject(signature) prompt.authenticate(cryptoObject, cancellationSignal, mainExecutor, callback) The executor allows you to control what thread the listener & callback lands on (the getMainExecutor() shortcut is new in API 28)
  13. How to Use It val prompt = BiometricPrompt.Builder(this) .setTitle("Place Your

    Order") .setSubtitle("Confirm your delivery order") .setDescription("Deliver to 732 Evergreen Terrace? Total: $36.99") .setNegativeButton("D'oh! Cancel", mainExecutor, cancelOnClickListener) .build() // Wrap a signature and ask to unlock the key val signature = Signature.getInstance("SHA256withRSA") signature.initSign(getPrivateKey()) val cryptoObject = BiometricPrompt.CryptoObject(signature) prompt.authenticate(cryptoObject, cancellationSignal, mainExecutor, callback)
  14. Authentication Callback val callback = object : BiometricPrompt.AuthenticationCallback() { override

    fun onAuthenticationFailed() { //The biometric capture was good, but wrong } override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { //Hard error (terminate the flow) - e.g. too many tries } override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence) { //Help message, like "your finger moved too fast" } override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { //Success! Grab the CryptoObject from the result and use it to sign/encrypt/decrypt } }
  15. Authentication Callback val callback = object : BiometricPrompt.AuthenticationCallback() { override

    fun onAuthenticationFailed() { //The biometric capture was good, but wrong } override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { //Hard error (terminate the flow) - e.g. too many tries } override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence) { //Help message, like "your finger moved too fast" } override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { //Success! Grab the CryptoObject from the result and use it to sign/encrypt/decrypt } }
  16. Authentication Callback val callback = object : BiometricPrompt.AuthenticationCallback() { override

    fun onAuthenticationFailed() { //The biometric capture was good, but wrong } override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { //Hard error (terminate the flow) - e.g. too many tries } override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence) { //Help message, like "your finger moved too fast" } override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { //Success! Grab the CryptoObject from the result and use it to sign/encrypt/decrypt } }
  17. Authentication Callback val callback = object : BiometricPrompt.AuthenticationCallback() { override

    fun onAuthenticationFailed() { //The biometric capture was good, but wrong } override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { //Hard error (terminate the flow) - e.g. too many tries } override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence) { //Help message, like "your finger moved too fast" } override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { //Success! Grab the CryptoObject from the result and use it to sign/encrypt/decrypt } }
  18. Authentication Callback val callback = object : BiometricPrompt.AuthenticationCallback() { override

    fun onAuthenticationFailed() { //The biometric capture was good, but wrong } override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { //Hard error (terminate the flow) - e.g. too many tries } override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence) { //Help message, like "your finger moved too fast" } override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { //Success! Grab the CryptoObject from the result and use it to sign/encrypt/decrypt } }
  19. Support Version • There’s a support library implementation, but it

    isn’t published yet • androidx.biometric has its own BiometricPrompt compat lib • Presents either the BiometricPrompt system dialog or its own standardized Fingerprint dialog on older devices
  20. User’s Device Backend Username and Password Session Token Generate an

    authentication- bound keypair Public Key & Device Identifier
  21. User’s Device Backend Username and Password Session Token Generate an

    authentication- bound keypair Public Key & Device Identifier Store key with the user’s data
  22. User’s Device Backend Username and Password Session Token Generate an

    authentication- bound keypair Public Key & Device Identifier Store key with the user’s data Device Token
  23. User’s Device Backend Unlock key with biometric to
 sign device

    token & nonce Signed login request Verify signature & nonce
  24. User’s Device Backend Unlock key with biometric to
 sign device

    token & nonce Signed login request Verify signature & nonce Session Token
  25. Improving Trust • These authentication methods rely upon trust that

    the private side of the user’s key is securely stored in the trusted world and gated by the biometric reader • Given rules in the Android CDD, know that biometric-backed keys are handled via trusted hardware • However…
  26. Improving Trust Is the key securely generated & stored, on

    a device with a trusted execution environment? Is the authentication required flag set? Or is it a rando, using curl to post a manually- generated key?
  27. Key Attestation • We want proof of characteristics of the

    enrolled key: • that the key was created and lives in the trusted world • that the key has authentication required • the package name and signing fingerprint of apps authorized to use the key
  28. Key Attestation • What if we also want some secondary

    characteristics of the device? • Verified boot state • OS version • Patch level
  29. Key Attestation • The certificate associated with a key includes

    an extension block that describes key generation parameters and characteristics about where the key lives • This had been done earlier in software, but software implementations can be affected on rooted/compromised devices • For devices that first ship with Android 8.0+, this must be able to happen in trusted hardware
  30. Key Attestation • Google has a root certificate for key

    attestation • OEM vendors burn an intermediate key into the device at the factory, whose certificate is signed by Google & traces back to the key attestation cert • Certificates for keys generated on your device are signed by the intermediate key
  31. Key Attestation • The certificates contain a revocation list URL

    that should be checked when validating • If a device’s intermediate key is revoked, this permanently impacts key attestation for that device and others that share that key • “Devices containing revoked certificates are still at least as trustworthy as those only supporting software attestation.”
  32. Key Attestation • Create a key with authentication required, and

    get its certificate chain • Validate the chain of signatures and confirm the root cert • Validate that the certificate indicates the key has authentication required set and was created in the trusted world Certificate[] certs = getKeyStore().getCertificateChain(KEY_NAME); List<String> encodedCerts = new ArrayList<>(); for (Certificate cert : certs) { encodedCerts.add(Base64.encodeToString(cert.getEncoded(), Base64.NO_WRAP)); } //send these to your backend
  33. Challenge • You can prove the key meets your specifications,

    but you also want to prove the key was created for your purpose • So, you use a challenge string when creating the key • your backend creates the challenge string & delivers to client • client creates its key with this challenge • backend validates challenge along with attestation result
  34. User’s Device Backend Username and Password Session Token Device Identifier

    Store identifier, generate challenge Challenge Generate keypair with challenge
  35. User’s Device Backend Username and Password Session Token Device Identifier

    Store identifier, generate challenge Challenge Generate keypair with challenge Public Key & Certificate Chain
  36. User’s Device Backend Username and Password Session Token Device Identifier

    Store identifier, generate challenge Challenge Generate keypair with challenge Public Key & Certificate Chain Validate key & certificates
  37. User’s Device Backend Username and Password Session Token Device Identifier

    Store identifier, generate challenge Challenge Generate keypair with challenge Public Key & Certificate Chain Validate key & certificates Device Token
  38. Certificate Validation • Use an ASN.1 parser library to parse

    the extension data from the certificate • Java: BouncyCastle • JS: asn1js • Python: asn1crypto • Go: encoding/asn1
  39. Certificate Validation • Google has an example of the Android-specific

    implementation details, using BouncyCastle • https://github.com/googlesamples/android-key- attestation • The Google-provided schema specifies how the data in the cert is represented • https://developer.android.com/training/articles/security- key-attestation
  40. Protected Confirmation • Provides an API for displaying user prompts

    for confirmation • Offers high level of confidence that the user confirmed the details on the prompt, even if a device was rooted or compromised • Useful for high-stakes situations like financial transactions, or controlling medical devices • To be available on supported devices that ship with Android 9.0
  41. How It Works • Trusted Execution Environment takes control and

    presents a prompt to validate the transaction • The information on the prompt is signed and sent back to your server for validation https://www.youtube.com/watch?v=r54roADX2MI
  42. Issuing the Prompt // nonce should come from your backend

    as a replay-protection // token val nonce = 8676309; val dialog = ConfirmationPrompt.Builder(this) .setPromptText("Send $100 to Ben") .setExtraData(("Send $100 to Ben:$nonce").toByteArray()) .build() dialog.presentPrompt(executor, callback)
  43. Handling the Result val callback = object : ConfirmationCallback() {

    override fun onConfirmed(dataThatWasConfirmed: ByteArray?) { super.onConfirmed(dataThatWasConfirmed) signDataWithConfirmationKeyAndSendIt(dataThatWasConfirmed) } } The key we created with the confirmation flag can only be used to sign the data passed into this callback.
  44. Handling the Result • The data returned to the callback

    is a CBOR (http://cbor.io) data structure that has several key/value pairs: • promptText: the data that was displayed to the user • extraData: the data that was passed as “extra” in the prompt (a good place to encode the prompted data AND the nonce together) • You sign and send to your service • Service should validate that both pieces of data are as expected.
  45. Handling the Result • If the data validates, as does

    the signature, you know: • The data was signed with a key having the confirmation tag • The confirmation tag implies the data was confirmed, or else it wouldn’t have been accepted for signing • The nonce validation implies the data is not a replay
  46. Wrapping Up • BiometricPrompt • https://developer.android.com/reference/android/hardware/biometrics/ BiometricPrompt • androidx.biometric (soon,

    or look in the AndroidX source repo today) • Key Attestation • https://developer.android.com/training/articles/security-key-attestation • Protected Confirmation • https://developer.android.com/training/articles/security-android-protected- confirmation
  47. Other Resources • Android Fingerprint Authentication In Action (Droidcon NYC

    2016) • https://github.com/benoberkfell/android-fingerprint-demo • Advanced Fingerprint (Øredev 2017) • https://vimeo.com/243345710 • https://github.com/benoberkfell/CryptoDiary • Fragmented Podcast #74 • http://fragmentedpodcast.com/2017/02/
  48. Image Attributions • CC licensed • Fingerprint https://www.flickr.com/photos/penmachine/3470933991 • Gummy

    bear https://www.flickr.com/photos/dianasch/4395029937 • Groucho glasses https://www.flickr.com/photos/tereneta/ 393487865/ • Pixel 2 https://unsplash.com/photos/NHSnBQyE7RE • Cat at computer https://www.flickr.com/photos/ threecheersformcr_xo/5340309206 • Chain https://unsplash.com/photos/K8iHtzoIKQ4