The Local Authentication Testing in Android Applications

First, we need to start with explaining what really is going on when the process of the local authentication is happening? It is such a process where the application authenticates the user against the credentials which are stored locally on the mobile device. Maybe you would understand it better if I say that the user unlocks the so-called ‘unlock’ apps or some other inner layer of the functionality. In that case, he/she provides a PIN code, fingerprint or a password which are verified by the referencing local data themselves. But, why is this process invoked at all at first place? There are numerous different reasons, but some of them are definitely providing the user convenience for the resuming an existing session with the remote server, or maybe even as a means of a step-up authentication. All in all, the whole purpose is to protect some of the critical functions. Let us find out here what we have in Authentication testing for Android application.

How to Test The Biometric Authentication?

Actually, we will talk about the fingerprinting a bit here. How is its access provided really? It is possible because of the FingerPrint Manager class. Besides that, the 6.0, Android’s Marshmellow has introduced the APIs with the purpose to authenticate the users through the fingerprinting. Let’s say you have installed the FingerPrint Manager class we mentioned, what happens then? The app itself requests the fingerprinting authentication. The object is here calling the authenticate( ) method. So, it registers the callback methods with a purpose to handle some of the possible outcomes of the whole authentication process. For your better understanding, we can tell you such an example-it can be shown like an error maybe, a failure or a success. It is also important for you to know that this method actually doesn’t constitute such a strong proof that the fingerprint authentication has been performed truly. How does that happen? It may be the some of the steps of the authentication process could be patched out by some attacker. It can also happen that the success callback (the example we talked about) could be called, and all that by using the instrumentation.

I know what you ask yourself. What could be the better security then? That is exactly what we intended to talk about now. It could be achieved by using the fingerprint API in the conjunction with the Android’s KeyGenerator class at first place. How does this method work? Here, the symmetric key is actually stored in the Keystore itself. Besides that, it is later ‘unlocked’ with the users’ fingerprints. Can be it explained on the examples? Of course, that is the best way someone can understand the theory, right? Let’s say that the AES key is created. It is enabling the user’s access to the remote service. Besides that, it also encrypts the user’s PIN or maybe the authentication token. So, when you call the setUserAuthenticationRequired (true) while you’re creating a key, you can be sure that any user will need to re-authenticate the access with the purpose to retrieve it. Also, those authentication credentials which are encrypted can be later easily saved to the regular storage on the device. Let’s say the example could be SharedPreferences.

All in all, this is a relatively safe way for ensuring the user who actually entered the authorization fingerprinting. But, what else you need to know here is that that this setup will require the application to actually hold the symmetric key in its memory. It happens during the cryptographic operations and it can potentially expose it to the attacker which manages the access to the app memory during its runtime.

Do we have to show you the even more secure option? Yes, we do. Using the asymmetric cryptography. What happens in this process? Here, the app creates the asymmetric key pair in the Keystore and later it enrollees on the public key which is placed on the server’s backend. What happens with the later transactions after that? They are actually signed in this process with the private key and verified by the server. They are verified by the public key here. So, the transactions themselves can be signed using the KeyStore APIs and all that without even extracting the private key from the KeyStore. The conclusion would be that the attacker has no possibilities to obtain the key later from the memory dumps through the instrumentation.

The Static Analysis

Here, you need to start with the FingerpringManager.authenticate ( ). You just need to search for such calls. What could be the first parameter here which should pass this method? Definitely the CryptoObject. It is supported by the FingerPrint manager we mentioned in the previous sentence. If you notice that your parameter is set to the null, it may be creating you later some security issue.

Why is used the creation of the key here? It initializes the cheaper wrapper actually. It can also be tracked back to the CryptoObject.

The KeyGenerator was also used while verifying the key. Here, you need to be sure that you have also verified the authentication logic. How can the authentication be successful? You need to be sure 100% that the remote endpoint requires the client to represent the secret which is retrieved from the Keystore. Besides that, he/she also need to represent the value which is derived from the secret and the value which was signed with the client’s private key.

So, you understood how the safe fingerprinting and the implement of its authentication require following some basic and simple principles. First, you start by checking if the type you want for the authentication is even possible. All in all, one thing is a must-the device needs to run on the Android 6.0 or even higher. It may be the API 23+. Also, the fingerprinting hardware must be available and it would show itself in the FingerprintManager. Protected lock screen too, which will also show itself in the same category. The app should also need to have the permission to ask the user for its fingerprint and at least one fingerprint needs to be verified.

So, if all of these processes fails, then the authentication should fail and in that case, you stay safe. Let’s take a look at the examples!

Fingerprinting hardware is definitely available:

FingerprintManager fingerprintManager = (FingerprintManager)

context.getSystemService(Context.FINGERPRINT_SERVICE) ;

fingerprintManager.isHardwareDetected();.

Here is how you can see if you have the protected lock screen (or any user) :

KeyguardManager keyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD.keyguardManager.isKeyguardSecure();.

If at least one fingerprint is registered, it will be shown like this:

fingerprintManager.hasEnrolledFingerprints();.

Did you know that the fingerprinting authentication can also be done by using the symmetric key? We will explain you how! First of all, it may be implemented by creating some few AES keys. They need to be new and you can use the KeyGenerator class. Then, you need to add setUserAuthenticationRequired(true) in KeyGenParameterSpec.Builder.

If you want to perform the decryption or encryption with that protected key, you will need to create the Cipher object which will be initialized it with the key alias.

What about the asymmetric key pair usage while fingerprinting authentication? It can be done, too. First of all, you will need to create a signing key. You will do it by using the KeyPairGenerator class too. In that case, you will easily enroll the public key with the server itself. After you have finished that, you can start with authenticating pieces of the data. You need to sign them on the client and later to verify them with the signature on the server. What to do when you want to use that key for signing? Of course, you will need to instantiate the CryptoObject. You will then authenticate it through the FingerManager.

What I would like you to keep in your mind is that sometimes it can happen that the transactions are signed, but a random nonce needs to be generated and added later to the data which is signed. It needs to be done because if not, the attacker could easily replay the transaction himself/herself.

Also, if you wish to implement the authentication by using the symmetric fingerprint authentication here, you will need to get the challenge-response protocol.

You also need to know something about the additional security features. Let’s say that the invalidateKey value has been set to be the default (true), what happens with the keys that are valid? Those are immediately and irreversibly invalidated. It happens as soon as the new fingerprint is enrolled. What does this mean for you? It prevents the attacker to retrieve the key. He/she is not able to do it even if they have the possibility to enroll the additional fingerprint.

We almost come to an end of this lesson, but we need to say some more words about the dynamic analysis. What can you use when you want to authenticate the device by fingerprinting using the dynamic analysis? It would be the best if we show you that on the example! Frida. Use it to call the onAuthenticaionSucceeded. This is the callback method which is direct. What are you doing here is patching the application and using the runtime instrumentation, and all that by the bypass fingerprint authentication of the client.

I hope you enjoyed this lesson. I put together the whole story which will help you to understand the process of the testing the local authentication in the Android’s apps better. Stay informed and keep learning!