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..455651370 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 @@ -97,8 +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: 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 @@ -107,16 +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: 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: 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 c8a8c6a4c..5d4f83e52 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,54 @@ 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 `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) @@ -217,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() @@ -235,6 +297,19 @@ public class DPoPKeyStoreTest { assertThat(exception.cause, `is`(cause)) } + + @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")