From a04cb7165e7f077ecba6ee68707d04723e4598be Mon Sep 17 00:00:00 2001 From: Prince Mathew Date: Wed, 25 Mar 2026 13:30:37 +0530 Subject: [PATCH 1/2] refactor : Handling more exceptions sceanrio in DPoP key generation --- .../com/auth0/android/dpop/DPoPKeyStore.kt | 26 ++++++++++++- .../auth0/android/dpop/DPoPKeyStoreTest.kt | 39 +++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/auth0/src/main/java/com/auth0/android/dpop/DPoPKeyStore.kt b/auth0/src/main/java/com/auth0/android/dpop/DPoPKeyStore.kt index 742e5beaf..e34e4244c 100644 --- a/auth0/src/main/java/com/auth0/android/dpop/DPoPKeyStore.kt +++ b/auth0/src/main/java/com/auth0/android/dpop/DPoPKeyStore.kt @@ -6,19 +6,21 @@ import android.os.Build import android.security.keystore.KeyGenParameterSpec import android.security.keystore.KeyProperties import android.util.Log +import java.io.IOException import java.security.InvalidAlgorithmParameterException import java.security.KeyPairGenerator import java.security.KeyStore import java.security.KeyStoreException import java.security.NoSuchAlgorithmException import java.security.NoSuchProviderException +import java.security.UnrecoverableKeyException import java.security.PrivateKey import java.security.ProviderException import java.security.PublicKey +import java.security.cert.CertificateException import java.security.spec.ECGenParameterSpec import java.util.Calendar import javax.security.auth.x500.X500Principal -import javax.security.cert.CertificateException /** * Class to handle all DPoP related keystore operations @@ -99,6 +101,16 @@ internal open class DPoPKeyStore { } } catch (e: KeyStoreException) { throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) + } catch (e: NoSuchAlgorithmException) { + throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) + } catch (e: UnrecoverableKeyException) { + throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) + } catch (e: ClassCastException) { + throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) + } catch (e: IOException) { + throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) + } catch (e: CertificateException) { + throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) } Log.d(TAG, "Returning null key pair ") return null @@ -109,6 +121,12 @@ internal open class DPoPKeyStore { return keyStore.containsAlias(KEY_ALIAS) } catch (e: KeyStoreException) { throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) + } catch (e: NoSuchAlgorithmException) { + throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) + } catch (e: IOException) { + throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) + } catch (e: CertificateException) { + throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) } } @@ -117,6 +135,12 @@ internal open class DPoPKeyStore { keyStore.deleteEntry(KEY_ALIAS) } catch (e: KeyStoreException) { throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) + } catch (e: NoSuchAlgorithmException) { + throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) + } catch (e: IOException) { + throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) + } catch (e: CertificateException) { + throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) } } diff --git a/auth0/src/test/java/com/auth0/android/dpop/DPoPKeyStoreTest.kt b/auth0/src/test/java/com/auth0/android/dpop/DPoPKeyStoreTest.kt index c8a8c6a4c..79f2ff834 100644 --- a/auth0/src/test/java/com/auth0/android/dpop/DPoPKeyStoreTest.kt +++ b/auth0/src/test/java/com/auth0/android/dpop/DPoPKeyStoreTest.kt @@ -32,9 +32,11 @@ import java.security.InvalidAlgorithmParameterException import java.security.KeyPairGenerator import java.security.KeyStore import java.security.KeyStoreException +import java.security.NoSuchAlgorithmException import java.security.PrivateKey import java.security.ProviderException import java.security.PublicKey +import java.security.UnrecoverableKeyException import java.security.cert.Certificate import javax.security.auth.x500.X500Principal @@ -191,6 +193,42 @@ public class DPoPKeyStoreTest { assertThat(exception.cause, `is`(cause)) } + @Test + public fun `getKeyPair should throw KEY_STORE_ERROR on NoSuchAlgorithmException`() { + val cause = NoSuchAlgorithmException("Test Exception") + whenever(mockKeyStore.getKey(any(), anyOrNull())).thenThrow(cause) + + val exception = assertThrows(DPoPException::class.java) { + dpopKeyStore.getKeyPair() + } + assertEquals(exception.message, DPoPException.KEY_STORE_ERROR.message) + assertThat(exception.cause, `is`(cause)) + } + + @Test + public fun `getKeyPair should throw KEY_STORE_ERROR on UnrecoverableKeyException`() { + val cause = UnrecoverableKeyException("Test Exception") + whenever(mockKeyStore.getKey(any(), anyOrNull())).thenThrow(cause) + + val exception = assertThrows(DPoPException::class.java) { + dpopKeyStore.getKeyPair() + } + assertEquals(exception.message, DPoPException.KEY_STORE_ERROR.message) + assertThat(exception.cause, `is`(cause)) + } + + @Test + public fun `getKeyPair should throw KEY_STORE_ERROR on ClassCastException`() { + val cause = ClassCastException("Test Exception") + whenever(mockKeyStore.getKey(any(), anyOrNull())).thenThrow(cause) + + val exception = assertThrows(DPoPException::class.java) { + dpopKeyStore.getKeyPair() + } + assertEquals(exception.message, DPoPException.KEY_STORE_ERROR.message) + assertThat(exception.cause, `is`(cause)) + } + @Test public fun `hasKeyPair should return true when alias exists`() { whenever(mockKeyStore.containsAlias(any())).thenReturn(true) @@ -235,6 +273,7 @@ public class DPoPKeyStoreTest { assertThat(exception.cause, `is`(cause)) } + @Test public fun `generateKeyPair should retry without StrongBox when ProviderException occurs with StrongBox enabled`() { val providerException = ProviderException("StrongBox attestation failed") From 7f587294dccc2f9afa606a7472b106f7e5aaaf50 Mon Sep 17 00:00:00 2001 From: Prince Mathew Date: Wed, 25 Mar 2026 15:28:44 +0530 Subject: [PATCH 2/2] Consolidated exceptions using when clause --- .../com/auth0/android/dpop/DPoPKeyStore.kt | 54 +++++++++---------- .../auth0/android/dpop/DPoPKeyStoreTest.kt | 36 +++++++++++++ 2 files changed, 62 insertions(+), 28 deletions(-) diff --git a/auth0/src/main/java/com/auth0/android/dpop/DPoPKeyStore.kt b/auth0/src/main/java/com/auth0/android/dpop/DPoPKeyStore.kt index e34e4244c..455651370 100644 --- a/auth0/src/main/java/com/auth0/android/dpop/DPoPKeyStore.kt +++ b/auth0/src/main/java/com/auth0/android/dpop/DPoPKeyStore.kt @@ -99,18 +99,16 @@ internal open class DPoPKeyStore { if (publicKey != null) { return Pair(privateKey, publicKey) } - } catch (e: KeyStoreException) { - throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) - } catch (e: NoSuchAlgorithmException) { - throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) - } catch (e: UnrecoverableKeyException) { - throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) - } catch (e: ClassCastException) { - throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) - } catch (e: IOException) { - throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) - } catch (e: CertificateException) { - throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) + } catch (e: Exception) { + when (e) { + is KeyStoreException, + is NoSuchAlgorithmException, + is UnrecoverableKeyException, + is ClassCastException, + is IOException, + is CertificateException -> throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) + else -> throw DPoPException(DPoPException.Code.UNKNOWN_ERROR, e) + } } Log.d(TAG, "Returning null key pair ") return null @@ -119,28 +117,28 @@ internal open class DPoPKeyStore { fun hasKeyPair(): Boolean { try { return keyStore.containsAlias(KEY_ALIAS) - } catch (e: KeyStoreException) { - throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) - } catch (e: NoSuchAlgorithmException) { - throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) - } catch (e: IOException) { - throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) - } catch (e: CertificateException) { - throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) + } catch (e: Exception) { + when (e) { + is KeyStoreException, + is NoSuchAlgorithmException, + is IOException, + is CertificateException -> throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) + else -> throw DPoPException(DPoPException.Code.UNKNOWN_ERROR, e) + } } } fun deleteKeyPair() { try { keyStore.deleteEntry(KEY_ALIAS) - } catch (e: KeyStoreException) { - throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) - } catch (e: NoSuchAlgorithmException) { - throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) - } catch (e: IOException) { - throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) - } catch (e: CertificateException) { - throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) + } catch (e: Exception) { + when (e) { + is KeyStoreException, + is NoSuchAlgorithmException, + is IOException, + is CertificateException -> throw DPoPException(DPoPException.Code.KEY_STORE_ERROR, e) + else -> throw DPoPException(DPoPException.Code.UNKNOWN_ERROR, e) + } } } diff --git a/auth0/src/test/java/com/auth0/android/dpop/DPoPKeyStoreTest.kt b/auth0/src/test/java/com/auth0/android/dpop/DPoPKeyStoreTest.kt index 79f2ff834..5d4f83e52 100644 --- a/auth0/src/test/java/com/auth0/android/dpop/DPoPKeyStoreTest.kt +++ b/auth0/src/test/java/com/auth0/android/dpop/DPoPKeyStoreTest.kt @@ -229,6 +229,18 @@ public class DPoPKeyStoreTest { assertThat(exception.cause, `is`(cause)) } + @Test + public fun `getKeyPair should throw UNKNOWN_ERROR on unhandled exception`() { + val cause = RuntimeException("Unexpected error") + whenever(mockKeyStore.getKey(any(), anyOrNull())).thenThrow(cause) + + val exception = assertThrows(DPoPException::class.java) { + dpopKeyStore.getKeyPair() + } + assertEquals(exception.message, DPoPException.UNKNOWN_ERROR.message) + assertThat(exception.cause, `is`(cause)) + } + @Test public fun `hasKeyPair should return true when alias exists`() { whenever(mockKeyStore.containsAlias(any())).thenReturn(true) @@ -255,6 +267,18 @@ public class DPoPKeyStoreTest { assertThat(exception.cause, `is`(cause)) } + @Test + public fun `hasKeyPair should throw UNKNOWN_ERROR on unhandled exception`() { + val cause = RuntimeException("Unexpected error") + whenever(mockKeyStore.containsAlias(any())).thenThrow(cause) + + val exception = assertThrows(DPoPException::class.java) { + dpopKeyStore.hasKeyPair() + } + assertEquals(exception.message, DPoPException.UNKNOWN_ERROR.message) + assertThat(exception.cause, `is`(cause)) + } + @Test public fun `deleteKeyPair should call deleteEntry`() { dpopKeyStore.deleteKeyPair() @@ -274,6 +298,18 @@ public class DPoPKeyStoreTest { } + @Test + public fun `deleteKeyPair should throw UNKNOWN_ERROR on unhandled exception`() { + val cause = RuntimeException("Unexpected error") + whenever(mockKeyStore.deleteEntry(any())).thenThrow(cause) + + val exception = assertThrows(DPoPException::class.java) { + dpopKeyStore.deleteKeyPair() + } + assertEquals(exception.message, DPoPException.UNKNOWN_ERROR.message) + assertThat(exception.cause, `is`(cause)) + } + @Test public fun `generateKeyPair should retry without StrongBox when ProviderException occurs with StrongBox enabled`() { val providerException = ProviderException("StrongBox attestation failed")