16

I am using the new BiometricPrompt API in Android P (API 28) in my application. (I am actually using it inside a wrapper based on this project so that it functions on older devices too, but that is not relevant to the question.) This is working very well on all devices I have tested, except for the Samsung S9 with face unlock.

Even though the stock Android version of BiometricPrompt currently only implements fingerprint authentication, Samsung appears to have extended it to support Face Unlock as well. When I trigger biometric authentication in my app, the "bottom sheet" pops up with a face icon (instead of the fingerprint icon shown on all other devices) and at the top of the screen some text appears that says "no face detected". (Note that the icon shown here is provided by the operating system, not by me, so it is obviously of Samsung's design.)

According to the documentation, the BiometricPrompt is only supposed to close itself and call my onAuthenticationSucceeded method if the authentication has been successful. According to logcat, it looks like it has been successful:

I/IFaceDaemonCallback: BpFaceDaemonCallback onAcquired()
I/SS_3A:  INFO: AEC: TsAec_process_get_aec_info: 650: [Id=132] algo_out g=1.785 e_time=0.025 IsLLS=0x0 Ev=7.422 Bv=2.348 ProEv=7.348 Cvgd=1 lux=261, lls=0x0
E/CHI: [SS_ERR ]: [CHI_FACTORY ]: chxseccamerafactoryusecase.cpp: ExecuteCaptureRequest: 452: pMetaData is NULL
I/FaceHal: face_processFrontImage[614398]
I/FaceServiceWrapper: ss_face_processFrontImage(data_len = 614398, width = 480, height = 640, rotation = 270)
I/NativeFaceService: FaceService::processFrontImage - data_len (614398) width(480) height(640) rotation(270) format(2)
I/NativeFaceService: SEC_FR_SERVICE_AUTHENTICATE
I/sec_fr_engine_qsee: sec_fr_engine_on_authenticate_frame
D/sec_fr_engine_qsee: call QSEECom_send_cmd
I/SS_3A:  INFO: AEC: TsAec_process_get_aec_info: 650: [Id=133] algo_out g=1.785 e_time=0.025 IsLLS=0x0 Ev=7.422 Bv=2.352 ProEv=7.352 Cvgd=1 lux=261, lls=0x0
E/CHI: [SS_ERR ]: [CHI_FACTORY ]: chxseccamerafactoryusecase.cpp: ExecuteCaptureRequest: 452: pMetaData is NULL
I/SS_3A:  INFO: AEC: TsAec_process_get_aec_info: 650: [Id=134] algo_out g=1.864 e_time=0.025 IsLLS=0x0 Ev=7.359 Bv=2.332 ProEv=7.332 Cvgd=0 lux=262, lls=0x0
E/CHI: [SS_ERR ]: [CHI_FACTORY ]: chxseccamerafactoryusecase.cpp: ExecuteCaptureRequest: 452: pMetaData is NULL
I/SS_3A:  INFO: AEC: TsAec_process_get_aec_info: 650: [Id=135] algo_out g=1.910 e_time=0.025 IsLLS=0x0 Ev=7.324 Bv=2.324 ProEv=7.324 Cvgd=0 lux=262, lls=0x0
E/CHI: [SS_ERR ]: [CHI_FACTORY ]: chxseccamerafactoryusecase.cpp: ExecuteCaptureRequest: 452: pMetaData is NULL
I/SS_3A:  INFO: AEC: TsAec_process_get_aec_info: 650: [Id=136] algo_out g=1.920 e_time=0.025 IsLLS=0x0 Ev=7.316 Bv=2.316 ProEv=7.316 Cvgd=0 lux=262, lls=0x0
E/CHI: [SS_ERR ]: [CHI_FACTORY ]: chxseccamerafactoryusecase.cpp: ExecuteCaptureRequest: 452: pMetaData is NULL
I/sec_fr_engine_qsee: [Performance Log] QSEECom_send_cmd (129683) us in sec_fr_engine_on_authenticate_frame
D/sec_fr_engine_qsee: QSEECom_send_cmd Success
D/sec_fr_engine_qsee: return value from qsapp is 0
I/NativeFaceService: sec_fr_engine_on_authenticate_frame - status = [0], identified = [1], keepProcessing = [1]
I/NativeFaceService: identify succeeds
I/FaceServiceStorage: GetFileSize::Size of file: 196 bytes.
I/FaceServiceStorage: file size = 196
I/NativeFaceService: sid file length = 196
I/sec_fr_engine_qsee: sec_fr_engine_authenticated
D/sec_fr_engine_qsee: call QSEECom_send_cmd
I/SS_3A:  INFO: AEC: TsAec_process_get_aec_info: 650: [Id=137] algo_out g=1.936 e_time=0.025 IsLLS=0x0 Ev=7.305 Bv=2.301 ProEv=7.301 Cvgd=0 lux=263, lls=0x0
I/sec_fr_engine_qsee: [Performance Log] QSEECom_send_cmd (12414) us in sec_fr_engine_authenticated
D/sec_fr_engine_qsee: QSEECom_send_cmd Success
D/sec_fr_engine_qsee: return value from qsapp is 0
I/FaceServiceCallback: sendAuthenticated in
I/faced_Proxy: wrapped_object_length = 0
I/IFaceDaemonCallback: BpFaceDaemonCallback onAuthenticated() 
I/FaceServiceCallback: sendAuthenticated out
I/SemBioFaceServiceD: handleAuthenticated : 1
D/keystore: AddAuthenticationToken: timestamp = 168377203, time_received = 16675
I/SemBioFacePrompt: isSuccess = true

However, it then crashes with the following error:

E/keystore: getAuthToken failed: -3
W/System.err: javax.crypto.IllegalBlockSizeException
W/System.err:     at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:519)
W/System.err:     at javax.crypto.Cipher.doFinal(Cipher.java:2055)
W/System.err:     at com.mycompany.myapp.activities.LoginActivity.onAuthenticationSuccessful(LoginActivity.java:560)
W/System.err:     at com.mycompany.common.security.BiometricCallbackV28.onAuthenticationSucceeded(BiometricCallbackV28.kt:18)
W/System.err:     at com.samsung.android.bio.face.SemBioFaceManager.sendAuthenticatedSucceeded(SemBioFaceManager.java:1507)
W/System.err:     at com.samsung.android.bio.face.SemBioFaceManager.access$2400(SemBioFaceManager.java:73)
W/System.err:     at com.samsung.android.bio.face.SemBioFaceManager$3.lambda$onAuthenticationSucceeded$1(SemBioFaceManager.java:1673)
W/System.err:     at com.samsung.android.bio.face.-$$Lambda$SemBioFaceManager$3$GGUPv9osWllaLwJM7Wg6GJEWK8E.run(Unknown Source:6)
W/System.err:     at android.os.Handler.handleCallback(Handler.java:873)
W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:99)
W/System.err:     at android.os.Looper.loop(Looper.java:214)
W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:6981)
W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
W/System.err:     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1445)
W/System.err: Caused by: android.security.KeyStoreException: Key user not authenticated
W/System.err:     at android.security.KeyStore.getKeyStoreException(KeyStore.java:1168)
W/System.err:     at android.security.keystore.KeyStoreCryptoOperationChunkedStreamer.update(KeyStoreCryptoOperationChunkedStreamer.java:132)
W/System.err:     at android.security.keystore.KeyStoreCryptoOperationChunkedStreamer.doFinal(KeyStoreCryptoOperationChunkedStreamer.java:217)
W/System.err:     at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:506)
W/System.err:   ... 14 more

According to the documentation, the success of the biometric authentication should have unlocked the keystore, but that has clearly not happened as shown by the Key user not authenticated message in the exception.

How can I get this working?

Moshe Katz
  • 13,048
  • 7
  • 58
  • 99
  • 1
    Probably it's a Samsung bug. Does the same device work correctly if you switch to fingerprints instead of the face? IOW, is this a Face Unlock bug, or a general S9 biometrics bug? – CommonsWare Apr 11 '19 at 14:28
  • 1
    Possible duplicate of [Error after Fingerprint touched on Samsung phones: android.security.KeyStoreException: Key user not authenticated](https://stackoverflow.com/questions/36043912/error-after-fingerprint-touched-on-samsung-phones-android-security-keystoreexce) – Martin Zeitler Apr 11 '19 at 14:43
  • [keystore.h](https://android.googlesource.com/platform/system/security/+/refs/heads/master/keystore/include/keystore/keystore.h#33) says `3` means `STATE_UNINITIALIZED`. possibly related: [25591236](https://stackoverflow.com/questions/25591236/how-to-initialize-the-keystore). – Martin Zeitler Apr 11 '19 at 16:21
  • @MartinZeitler if that were the reason, I would expect this to happen on all devices, but it works perfectly on all other devices I've tried (including other Samsung devices). – Moshe Katz Apr 14 '19 at 14:03
  • @MosheKatz `KeyStoreException: Key user not authenticated` generally tells the same. [this](https://developer.android.com/reference/android/security/keystore/KeyGenParameterSpec.Builder#setUserAuthenticationValidityDurationSeconds(int)) seems to be a possible solution... because it might have been authenticated at first, but when trying to access it again, it is inaccessible already; timing issue. if that specific implementation uses `-1 if user authentication must take place for every use of the key`... this might not work out. – Martin Zeitler Apr 14 '19 at 16:25
  • It turns out that it *doesn't* actually crash for fingerprint - I can reliably get it to work with fingerprints but not with Face or Iris authentication. I'm going back to saying that it really seems like a Samsung bug for now. – Moshe Katz Apr 15 '19 at 15:54
  • 2
    I'm also getting this same problem on a S10 Plus. Fingerprint works correctly, face ID crashes with this exception – Orgmir Sep 11 '19 at 05:17
  • 1
    I was able to repro this with the [Biometrics Sample app](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/samples/BiometricDemos/) as well. The onAuthenticationSucceeded works correctly, but unlocking the key store for encryption always fails. Iris and Fingerprint work CORRECTLY though. – A. Nguyen Sep 30 '19 at 19:43
  • If you are going to use strong type authentication in your application. Authentication can be [checked for the strong type.](https://stackoverflow.com/questions/61698257/keygeneration-using-androidx-biometric-fails-if-only-face-as-biometric-is-instal/65060325#65060325) – Eniz Bilgin Nov 29 '20 at 13:11

1 Answers1

10

I had this same issue when attempting to use the BiometricPrompt APIs to authenticate users into my app. I reached out to Samsung technical support and they confirmed that Samsungs face recognition is not secure enough to unlock the Android Keystore.

This makes sense since the current Samsung Galaxy devices (S8, S9, S10) do not have the necessary hardware to do 3D imaging of a face (rumor has it that the Pixel 4 will). This reminds me of Samsungs first fingerprint implementation, on the S5 I believe, that did not meet Google's technical specifications and didn't work after upgrading to Marshmallow.

Below is the actual reply that I received from Samsung tech support:

For security reasons, Face Biometric can not update the keystore after authentication. So, SecurityException is shown when the application tries to make a keystore or sign by the keystore.

Currently, you can not use Face Biometric in your application to authenticate. Alternatively, you may guide the user to change Preferred Biometric to Fingerprint / Iris by showing a pop up (to open biometric preference setting) while getting these exceptions.

Thank you for your patience.

Curiously, I no longer see the crash on the Galaxy S10. The authentication just returns an error code. This may be even worse since it breaks their suggested fix. Ugh.

SBerg413
  • 14,046
  • 6
  • 55
  • 87
  • @nAndroid - Samsungs current face ID implementation on devices up to and including the S10 will never work to unlock the Android Keystore as the hardware isn't there to support the functionality. As I mentioned, I no longer see an exception thrown, but I do see a normal error response returned in the BiometricPrompt call. – SBerg413 Oct 11 '19 at 18:26
  • my apologies, I'm actually asking if biometric works with fingerprint on s10 for anyone – nAndroid Oct 14 '19 at 15:14