Skip to content

Commit 2a00375

Browse files
Add documentation
1 parent 88c2ccb commit 2a00375

File tree

3 files changed

+101
-0
lines changed

3 files changed

+101
-0
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
<p>
8+
Biometric local authentication such as fingerprint recognistion can be used to protect sensitive data or actions within an application.
9+
However, if this authentication does not make use of a <code>KeyStore</code>-backed key, it is able to be bypassed by a privileged malicious application or an attacker with physical access.
10+
</p>
11+
</overview>
12+
13+
<recommendation>
14+
<p>
15+
Generate a secure key in the Android <code>KeyStore</code> and ensure that the <code>onAuthenticaionSuccess</code> callback for a biometric prompt uses it
16+
in a way that is required for the sensitive parts of the application to function, such as by using it to decrypt sensitive data or credentials.
17+
</p>
18+
</recommendation>
19+
20+
<example>
21+
<p>In the following (bad) case, no <code>CryptoObject</code> is required for the biometric prompt to grant access, so it can be bypassed.</p>
22+
<sample src="AndroidInsecureLocalAuthenticationBad.java" />
23+
<p>In he following (good) case, a secret key is generated in the Android <code>KeyStore</code> that is required for the application to grant access.</p>
24+
<sample src="AndroidInsecureLocalAuthenticationGood.java" />
25+
</example>
26+
27+
<references>
28+
<li>
29+
OWASP Mobile Application Security: <a href="https://mas.owasp.org/MASTG/Android/0x05f-Testing-Local-Authentication/">Android Local Authentication</a>
30+
</li>
31+
<li>
32+
OWASP Mobile Application Security: <a href="https://mas.owasp.org/MASTG/tests/android/MASVS-AUTH/MASTG-TEST-0018/">Testing Biometric Authentication</a>
33+
</li>
34+
<li>
35+
WithSecure: <a href="https://labs.withsecure.com/publications/how-secure-is-your-android-keystore-authentication">How Secure is your Android Keystore Authentication?</a>
36+
</li>
37+
<li>
38+
Android Developers: <a href="https://developer.android.com/training/sign-in/biometric-auth">Biometric Authentication</a>
39+
</li>
40+
41+
</references>
42+
</qhelp>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
biometricPrompt.authenticate(
2+
cancellationSignal,
3+
executor,
4+
new BiometricPrompt.AuthenticationCallback {
5+
@Override
6+
// BAD: This authentication callback does not make use of a `CryptoObject` from the `result`.
7+
public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
8+
grantAccess()
9+
}
10+
}
11+
)
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
private void generateSecretKey() {
2+
KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(
3+
"MySecretKey",
4+
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
5+
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
6+
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
7+
.setUserAuthenticationRequired(true)
8+
.setInvalidatedByBiometricEnrollment(true)
9+
.build();
10+
KeyGenerator keyGenerator = KeyGenerator.getInstance(
11+
KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
12+
keyGenerator.init(keyGenParameterSpec);
13+
keyGenerator.generateKey();
14+
}
15+
16+
17+
private SecretKey getSecretKey() {
18+
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
19+
keyStore.load(null);
20+
return ((SecretKey)keyStore.getKey("MySecretKey", null));
21+
}
22+
23+
private Cipher getCipher() {
24+
return Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
25+
+ KeyProperties.BLOCK_MODE_CBC + "/"
26+
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);
27+
}
28+
29+
public prompt() {
30+
Cipher cipher = getCipher();
31+
SecretKey secretKey = getSecretKey();
32+
cipher.init(Cipher.DECRYPT_MODE, secretKey);
33+
34+
biometricPrompt.authenticate(
35+
new BiometricPrompt.CryptoObject(cipher);
36+
cancellationSignal,
37+
executor,
38+
new BiometricPrompt.AuthenticationCallback {
39+
@Override
40+
// GOOD: This authentication callback uses the result to decrypt some data.
41+
public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
42+
Cipher cipher = result.getCryptoObject().getCipher();
43+
byte[] decryptedData = cipher.doFinal(encryptedData);
44+
grantAccessWithData(decryptedData);
45+
}
46+
}
47+
);
48+
}

0 commit comments

Comments
 (0)