From 14933b15a54cb8ccd739c6dae0b844dc303d6c22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20=C4=8Eurech?= Date: Thu, 19 Sep 2024 13:18:56 +0200 Subject: [PATCH 1/4] Android: Fix #620: Store biometry key for each PowerAuthSDK instance (1.7.x branch) --- ...rAuthKeychainConfigurationBuilderTest.java | 6 +- .../biometry/BiometricAuthentication.java | 4 +- .../BiometricAuthenticationRequest.java | 26 +++ .../biometry/IBiometricKeystore.java | 21 +- .../biometry/impl/BiometricKeystore.java | 73 +++---- .../DefaultBiometricKeyEncryptorProvider.java | 4 +- .../impl/dummy/DummyBiometricKeystore.java | 15 +- .../sdk/PowerAuthKeychainConfiguration.java | 82 ++++++-- .../security/powerauth/sdk/PowerAuthSDK.java | 64 +++--- .../sdk/impl/BiometricDataMapper.java | 186 ++++++++++++++++++ 10 files changed, 390 insertions(+), 91 deletions(-) create mode 100644 proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/impl/BiometricDataMapper.java diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/sdk/PowerAuthKeychainConfigurationBuilderTest.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/sdk/PowerAuthKeychainConfigurationBuilderTest.java index aadc7935..2554b6f2 100644 --- a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/sdk/PowerAuthKeychainConfigurationBuilderTest.java +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/sdk/PowerAuthKeychainConfigurationBuilderTest.java @@ -35,7 +35,7 @@ public void testDefaultParameters() throws Exception { assertEquals(PowerAuthKeychainConfiguration.KEYCHAIN_ID_BIOMETRY, configuration.getKeychainBiometryId()); assertEquals(PowerAuthKeychainConfiguration.KEYCHAIN_ID_STATUS, configuration.getKeychainStatusId()); assertEquals(PowerAuthKeychainConfiguration.KEYCHAIN_ID_TOKEN_STORE, configuration.getKeychainTokenStoreId()); - assertEquals(PowerAuthKeychainConfiguration.KEYCHAIN_KEY_BIOMETRY_DEFAULT, configuration.getKeychainBiometryDefaultKey()); + assertNull(configuration.getKeychainKeyBiometry()); assertEquals(KeychainProtection.NONE, configuration.getMinimalRequiredKeychainProtection()); assertFalse(configuration.isConfirmBiometricAuthentication()); assertTrue(configuration.isLinkBiometricItemsToCurrentSet()); @@ -50,14 +50,14 @@ public void testCustomParameters() throws Exception { .keychainBiometryId("keychain.biometry") .keychainStatusId("keychain.status") .keychainTokenStoreId("keychain.tokens") - .keychainBiometryDefaultKey("biometryKey") + .keychainKeyBiometry("biometryKey") .minimalRequiredKeychainProtection(KeychainProtection.HARDWARE) .authenticateOnBiometricKeySetup(false) .build(); assertEquals("keychain.biometry", configuration.getKeychainBiometryId()); assertEquals("keychain.status", configuration.getKeychainStatusId()); assertEquals("keychain.tokens", configuration.getKeychainTokenStoreId()); - assertEquals("biometryKey", configuration.getKeychainBiometryDefaultKey()); + assertEquals("biometryKey", configuration.getKeychainKeyBiometry()); assertEquals(KeychainProtection.HARDWARE, configuration.getMinimalRequiredKeychainProtection()); assertTrue(configuration.isConfirmBiometricAuthentication()); assertFalse(configuration.isLinkBiometricItemsToCurrentSet()); diff --git a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/BiometricAuthentication.java b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/BiometricAuthentication.java index b4059a92..9a9bb2e2 100644 --- a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/BiometricAuthentication.java +++ b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/BiometricAuthentication.java @@ -135,7 +135,7 @@ public void onCompletion() { @Override public void onBiometricKeyUnavailable() { // Remove the default key, because the biometric key is no longer available. - device.getBiometricKeystore().removeBiometricKeyEncryptor(); + device.getBiometricKeystore().removeBiometricKeyEncryptor(request.getKeystoreAlias()); } }); final IBiometricKeyEncryptorProvider biometricKeyEncryptorProvider = new DefaultBiometricKeyEncryptorProvider(request, getBiometricKeystore()); @@ -170,7 +170,7 @@ public void onBiometricKeyUnavailable() { } // Failed to use biometric authentication. At first, we should cleanup the possible stored // biometric key. - device.getBiometricKeystore().removeBiometricKeyEncryptor(); + device.getBiometricKeystore().removeBiometricKeyEncryptor(request.getKeystoreAlias()); // Now show the error dialog, and report the exception later. if (exception == null) { diff --git a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/BiometricAuthenticationRequest.java b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/BiometricAuthenticationRequest.java index 0c2d5b47..29fbc2e0 100644 --- a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/BiometricAuthenticationRequest.java +++ b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/BiometricAuthenticationRequest.java @@ -38,6 +38,7 @@ public class BiometricAuthenticationRequest { private final @NonNull CharSequence title; private final @Nullable CharSequence subtitle; private final @NonNull CharSequence description; + private final @NonNull String keystoreAlias; private final boolean forceGenerateNewKey; private final boolean invalidateByBiometricEnrollment; private final boolean userConfirmationRequired; @@ -52,6 +53,7 @@ private BiometricAuthenticationRequest( @NonNull CharSequence description, @Nullable Fragment fragment, @Nullable FragmentActivity fragmentActivity, + @NonNull String keystoreAlias, boolean forceGenerateNewKey, boolean invalidateByBiometricEnrollment, boolean userConfirmationRequired, @@ -64,6 +66,7 @@ private BiometricAuthenticationRequest( this.description = description; this.fragment = fragment; this.fragmentActivity = fragmentActivity; + this.keystoreAlias = keystoreAlias; this.forceGenerateNewKey = forceGenerateNewKey; this.invalidateByBiometricEnrollment = invalidateByBiometricEnrollment; this.userConfirmationRequired = userConfirmationRequired; @@ -108,6 +111,14 @@ private BiometricAuthenticationRequest( return fragmentActivity; } + /** + * @return Alias to Android Keystore for the existing, or the new created key. + */ + @NonNull + public String getKeystoreAlias() { + return keystoreAlias; + } + /** * @return true whether the new biometric key has to be generated as a part of the operation. */ @@ -173,6 +184,7 @@ public static class Builder { private Fragment fragment; private FragmentActivity fragmentActivity; + private String keystoreAlias; private boolean forceGenerateNewKey = false; private boolean invalidateByBiometricEnrollment = true; private boolean userConfirmationRequired = false; @@ -200,6 +212,9 @@ public BiometricAuthenticationRequest build() { if (TextUtils.isEmpty(title) || TextUtils.isEmpty(description)) { throw new IllegalArgumentException("Title and description is required."); } + if (keystoreAlias == null) { + throw new IllegalArgumentException("KeyStore alias is required."); + } if (rawKeyData == null) { throw new IllegalArgumentException("RawKeyData is required."); } @@ -218,6 +233,7 @@ public BiometricAuthenticationRequest build() { description, fragment, fragmentActivity, + keystoreAlias, forceGenerateNewKey, invalidateByBiometricEnrollment, userConfirmationRequired, @@ -317,6 +333,16 @@ public Builder setFragmentActivity(@NonNull FragmentActivity fragmentActivity) { return this; } + /** + * Required: Set alias for a new or existing key stored in the Android Keystore. + * @param keystoreAlias Alias to key to create or access. + * @return This value will never be {@code null}. + */ + public Builder setKeystoreAlias(@NonNull String keystoreAlias) { + this.keystoreAlias = keystoreAlias; + return this; + } + /** * @param forceGenerateNewKey If true then the new biometric key will be generated as a * part of the process. diff --git a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/IBiometricKeystore.java b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/IBiometricKeystore.java index 856f96a0..e869ea5f 100644 --- a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/IBiometricKeystore.java +++ b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/IBiometricKeystore.java @@ -16,6 +16,7 @@ package io.getlime.security.powerauth.biometry; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; /** @@ -34,11 +35,12 @@ public interface IBiometricKeystore { /** * Check if a key for biometric key encryptor is present in Keystore and {@link IBiometricKeyEncryptor} * can be acquired. + * @param keyId Key identifier. * * @return {@code true} in case a key for biometric key encryptor is present, false otherwise. * Method returns false in case Keystore is not properly initialized (call {@link #isKeystoreReady()}). */ - boolean containsBiometricKeyEncryptor(); + boolean containsBiometricKeyEncryptor(@NonNull String keyId); /** * Generate a new biometry related Keystore key and return object that provide KEK encryption and decryption. @@ -48,21 +50,32 @@ public interface IBiometricKeystore { * * @param invalidateByBiometricEnrollment Sets whether the new key should be invalidated on biometric enrollment. * @param useSymmetricKey Sets whether symmetric key should be created. + * @param keyId Key identifier. * * @return New generated {@link IBiometricKeyEncryptor} key or {@code null} in case of failure. */ @Nullable - IBiometricKeyEncryptor createBiometricKeyEncryptor(boolean invalidateByBiometricEnrollment, boolean useSymmetricKey); + IBiometricKeyEncryptor createBiometricKeyEncryptor(@NonNull String keyId, boolean invalidateByBiometricEnrollment, boolean useSymmetricKey); /** * Removes an encryption key from Keystore. + * @param keyId Key identifier. */ - void removeBiometricKeyEncryptor(); + void removeBiometricKeyEncryptor(@NonNull String keyId); /** + * Get implementation of {@link IBiometricKeyEncryptor} constructed with key stored in KeyStore. + * @param keyId Key identifier. * @return {@link IBiometricKeyEncryptor} constructed with key stored in KeyStore or {@code null} * if no such key is stored. */ @Nullable - IBiometricKeyEncryptor getBiometricKeyEncryptor(); + IBiometricKeyEncryptor getBiometricKeyEncryptor(@NonNull String keyId); + + /** + * Return identifier of legacy key shared between multiple PowerAuthSDK instances. + * @return Identifier of shared legacy key. + */ + @NonNull + String getLegacySharedKeyId(); } diff --git a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/impl/BiometricKeystore.java b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/impl/BiometricKeystore.java index 4e00d129..6048de41 100644 --- a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/impl/BiometricKeystore.java +++ b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/impl/BiometricKeystore.java @@ -17,10 +17,13 @@ package io.getlime.security.powerauth.biometry.impl; import android.os.Build; +import android.util.Base64; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.security.Key; import java.security.KeyStore; import java.security.KeyStoreException; @@ -33,6 +36,7 @@ import io.getlime.security.powerauth.biometry.IBiometricKeyEncryptor; import io.getlime.security.powerauth.biometry.IBiometricKeystore; +import io.getlime.security.powerauth.core.CryptoUtils; import io.getlime.security.powerauth.system.PowerAuthLog; /** @@ -41,7 +45,8 @@ @RequiresApi(api = Build.VERSION_CODES.M) public class BiometricKeystore implements IBiometricKeystore { - private static final String KEY_NAME = "io.getlime.PowerAuthKeychain.KeyStore.BiometryKeychain"; + private static final String KEY_NAME_PREFIX = "com.wultra.powerauth.biometricKey."; + private static final String LEGACY_KEY_NAME = "io.getlime.PowerAuthKeychain.KeyStore.BiometryKeychain"; private static final String PROVIDER_NAME = "AndroidKeyStore"; private KeyStore mKeyStore; @@ -56,61 +61,40 @@ public BiometricKeystore() { } } - /** - * Check if the Keystore is ready. - * @return True if Keystore is ready, false otherwise. - */ @Override public boolean isKeystoreReady() { return mKeyStore != null; } - /** - * Check if a default key is present in Keystore - * - * @return True in case a default key is present, false otherwise. Method returns false in case Keystore is not properly initialized (call {@link #isKeystoreReady()}). - */ @Override - public boolean containsBiometricKeyEncryptor() { + public boolean containsBiometricKeyEncryptor(@NonNull String keyId) { if (!isKeystoreReady()) { return false; } try { - return mKeyStore.containsAlias(KEY_NAME); + return mKeyStore.containsAlias(getKeystoreAlias(keyId)); } catch (KeyStoreException e) { PowerAuthLog.e("BiometricKeystore.containsBiometricKeyEncryptor failed: " + e.getMessage()); return false; } } - /** - * Generate a new biometry related Keystore key with default key name. - * - * The key that is created during this process is used to encrypt key stored in shared preferences, - * in order to derive key used for biometric authentication. - * @param invalidateByBiometricEnrollment If true, then internal key stored in KeyStore will be invalidated on next biometric enrollment. - * @param useSymmetricKey If true, then symmetric key will be created. - * @return New generated {@link SecretKey} key or {@code null} in case of failure. - */ @Override public @Nullable - IBiometricKeyEncryptor createBiometricKeyEncryptor(boolean invalidateByBiometricEnrollment, boolean useSymmetricKey) { - removeBiometricKeyEncryptor(); + IBiometricKeyEncryptor createBiometricKeyEncryptor(@NonNull String keyId, boolean invalidateByBiometricEnrollment, boolean useSymmetricKey) { + removeBiometricKeyEncryptor(keyId); if (useSymmetricKey) { - return BiometricKeyEncryptorAes.createAesEncryptor(PROVIDER_NAME, KEY_NAME, invalidateByBiometricEnrollment); + return BiometricKeyEncryptorAes.createAesEncryptor(PROVIDER_NAME, getKeystoreAlias(keyId), invalidateByBiometricEnrollment); } else { - return BiometricKeyEncryptorRsa.createRsaEncryptor(PROVIDER_NAME, KEY_NAME, invalidateByBiometricEnrollment); + return BiometricKeyEncryptorRsa.createRsaEncryptor(PROVIDER_NAME, getKeystoreAlias(keyId), invalidateByBiometricEnrollment); } } - /** - * Removes an encryption key from Keystore. - */ @Override - public void removeBiometricKeyEncryptor() { + public void removeBiometricKeyEncryptor(@NonNull String keyId) { try { - if (containsBiometricKeyEncryptor()) { - mKeyStore.deleteEntry(KEY_NAME); + if (containsBiometricKeyEncryptor(keyId)) { + mKeyStore.deleteEntry(getKeystoreAlias(keyId)); } } catch (KeyStoreException e) { PowerAuthLog.e("BiometricKeystore.removeBiometricKeyEncryptor failed: " + e.getMessage()); @@ -122,13 +106,13 @@ public void removeBiometricKeyEncryptor() { */ @Override @Nullable - public IBiometricKeyEncryptor getBiometricKeyEncryptor() { + public IBiometricKeyEncryptor getBiometricKeyEncryptor(@NonNull String keyId) { if (!isKeystoreReady()) { return null; } try { mKeyStore.load(null); - final Key key = mKeyStore.getKey(KEY_NAME, null); + final Key key = mKeyStore.getKey(getKeystoreAlias(keyId), null); if (key instanceof SecretKey) { // AES symmetric key return new BiometricKeyEncryptorAes((SecretKey)key); @@ -145,4 +129,27 @@ public IBiometricKeyEncryptor getBiometricKeyEncryptor() { } } + @Override + @NonNull + public String getLegacySharedKeyId() { + return LEGACY_KEY_NAME; + } + + /** + * Function return alias for key stored in KeyStore for given key identifier. If the key identifier is equal to + * legacy key name, then the alias is legacy key name. Otherwise, the key alias is calculated as + * {@code KEY_NAME_PREFIX + SHA256(keyId)}. + * @param keyId Key identifier. + * @return Key alias to key stored in KeyStore. + */ + @NonNull + private String getKeystoreAlias(@NonNull String keyId) { + if (LEGACY_KEY_NAME.equals(keyId)) { + return LEGACY_KEY_NAME; + } + final String keyIdHash = Base64.encodeToString( + CryptoUtils.hashSha256(keyId.getBytes(StandardCharsets.UTF_8)), + Base64.NO_WRAP | Base64.NO_PADDING | Base64.URL_SAFE); + return KEY_NAME_PREFIX + keyIdHash; + } } diff --git a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/impl/DefaultBiometricKeyEncryptorProvider.java b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/impl/DefaultBiometricKeyEncryptorProvider.java index 74b10f42..5f2ed976 100644 --- a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/impl/DefaultBiometricKeyEncryptorProvider.java +++ b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/impl/DefaultBiometricKeyEncryptorProvider.java @@ -54,12 +54,12 @@ public boolean isAuthenticationRequiredOnEncryption() { public IBiometricKeyEncryptor getBiometricKeyEncryptor() throws PowerAuthErrorException { if (encryptor == null) { if (request.isForceGenerateNewKey()) { - encryptor = keystore.createBiometricKeyEncryptor(request.isInvalidateByBiometricEnrollment(), request.isUseSymmetricCipher()); + encryptor = keystore.createBiometricKeyEncryptor(request.getKeystoreAlias(), request.isInvalidateByBiometricEnrollment(), request.isUseSymmetricCipher()); if (encryptor == null) { throw new PowerAuthErrorException(PowerAuthErrorCodes.BIOMETRY_NOT_SUPPORTED, "Keystore failed to generate a new biometric key."); } } else { - encryptor = keystore.getBiometricKeyEncryptor(); + encryptor = keystore.getBiometricKeyEncryptor(request.getKeystoreAlias()); if (encryptor == null) { throw new PowerAuthErrorException(PowerAuthErrorCodes.BIOMETRY_NOT_AVAILABLE, "Cannot get biometric key from the keystore."); } diff --git a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/impl/dummy/DummyBiometricKeystore.java b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/impl/dummy/DummyBiometricKeystore.java index eea239c4..6a10b014 100644 --- a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/impl/dummy/DummyBiometricKeystore.java +++ b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/biometry/impl/dummy/DummyBiometricKeystore.java @@ -16,6 +16,7 @@ package io.getlime.security.powerauth.biometry.impl.dummy; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import io.getlime.security.powerauth.biometry.IBiometricKeyEncryptor; @@ -33,23 +34,29 @@ public boolean isKeystoreReady() { } @Override - public boolean containsBiometricKeyEncryptor() { + public boolean containsBiometricKeyEncryptor(@NonNull String keyId) { return false; } @Nullable @Override - public IBiometricKeyEncryptor createBiometricKeyEncryptor(boolean invalidateByBiometricEnrollment, boolean useSymmetricKey) { + public IBiometricKeyEncryptor createBiometricKeyEncryptor(@NonNull String keyId, boolean invalidateByBiometricEnrollment, boolean useSymmetricKey) { return null; } @Override - public void removeBiometricKeyEncryptor() { + public void removeBiometricKeyEncryptor(@NonNull String keyId) { } @Nullable @Override - public IBiometricKeyEncryptor getBiometricKeyEncryptor() { + public IBiometricKeyEncryptor getBiometricKeyEncryptor(@NonNull String keyId) { return null; } + + @NonNull + @Override + public String getLegacySharedKeyId() { + return ""; + } } diff --git a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthKeychainConfiguration.java b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthKeychainConfiguration.java index 31d9272f..ffd5239d 100644 --- a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthKeychainConfiguration.java +++ b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthKeychainConfiguration.java @@ -18,6 +18,7 @@ import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import io.getlime.security.powerauth.keychain.KeychainProtection; /** @@ -28,19 +29,22 @@ public class PowerAuthKeychainConfiguration { public static final String KEYCHAIN_ID_STATUS = "io.getlime.PowerAuthKeychain.StatusKeychain"; public static final String KEYCHAIN_ID_BIOMETRY = "io.getlime.PowerAuthKeychain.BiometryKeychain"; public static final String KEYCHAIN_ID_TOKEN_STORE = "io.getlime.PowerAuthKeychain.TokenStoreKeychain"; - public static final String KEYCHAIN_KEY_BIOMETRY_DEFAULT = "io.getlime.PowerAuthKeychain.BiometryKeychain.DefaultKey"; + public static final String KEYCHAIN_KEY_BIOMETRY_DEFAULT = null; + public static final String KEYCHAIN_KEY_SHARED_BIOMETRY_KEY = "io.getlime.PowerAuthKeychain.BiometryKeychain.DefaultKey"; public static final boolean DEFAULT_LINK_BIOMETRY_ITEMS_TO_CURRENT_SET = true; public static final boolean DEFAULT_CONFIRM_BIOMETRIC_AUTHENTICATION = false; public static final boolean DEFAULT_AUTHENTICATE_ON_BIOMETRIC_KEY_SETUP = true; + public static final boolean DEFAULT_ENABLE_FALLBACK_TO_SHARED_BIOMETRY_KEY = true; public static final @KeychainProtection int DEFAULT_REQUIRED_KEYCHAIN_PROTECTION = KeychainProtection.NONE; private final @NonNull String keychainIdStatus; private final @NonNull String keychainIdBiometry; private final @NonNull String keychainIdTokenStore; - private final @NonNull String keychainKeyBiometryDefault; + private final @Nullable String keychainKeyBiometry; private final boolean linkBiometricItemsToCurrentSet; private final boolean confirmBiometricAuthentication; private final boolean authenticateOnBiometricKeySetup; + private final boolean enableFallbackToSharedBiometryKey; private final @KeychainProtection int minimalRequiredKeychainProtection; /** @@ -61,10 +65,21 @@ public class PowerAuthKeychainConfiguration { /** * Get name of the Keychain key used for storing the default biometry key information. - * @return Name of the biometry Keychain key. + * @return Name of the default biometry Keychain key. + * @deprecated Use {@link #getKeychainKeyBiometry()} method instead. */ + @Deprecated // 1.7.10 - remove in 1.10.0 public @NonNull String getKeychainBiometryDefaultKey() { - return keychainKeyBiometryDefault; + return keychainKeyBiometry == null ? KEYCHAIN_KEY_SHARED_BIOMETRY_KEY : keychainKeyBiometry; + } + + /** + * Get name of the Keychain key used for storing the biometry key information for the PowerAuthSDK instance. If null + * then PowerAuthSDK instance will use its instance identifier to store the biometry key information. + * @return Get name of the Keychain key used for storing the biometry key information for the PowerAuthSDK instance. + */ + public @Nullable String getKeychainKeyBiometry() { + return keychainKeyBiometry; } /** @@ -110,6 +125,15 @@ public boolean isAuthenticateOnBiometricKeySetup() { return authenticateOnBiometricKeySetup; } + /** + * Get whether fallback to shared, legacy biometry key is enabled. By default, this is enabled for the compatibility + * reasons. If + * @return {@code true} if fallback to shared, legacy biometry key is enabled. + */ + public boolean isFallbackToSharedBiometryKeyEnabled() { + return enableFallbackToSharedBiometryKey; + } + /** * Get minimal required keychain protection level that must be supported on the current device. * If the level of protection on the device is insufficient, then you cannot use PowerAuth @@ -128,7 +152,7 @@ public boolean isAuthenticateOnBiometricKeySetup() { * * @param keychainIdStatus Name of the Keychain file used for storing the status information. * @param keychainIdBiometry Name of the Keychain file used for storing the biometry key information. - * @param keychainKeyBiometryDefault Name of the Keychain key used to store the default biometry key. + * @param keychainKeyBiometry Name of the Keychain key used for storing the biometry key information for the PowerAuthSDK instance. * @param keychainIdTokenStore Name of the Keychain file used for storing the access tokens. * @param linkBiometricItemsToCurrentSet If set, then the item protected with the biometry is invalidated * if fingers are added or removed, or if the user re-enrolls for face. @@ -137,25 +161,29 @@ public boolean isAuthenticateOnBiometricKeySetup() { * and may be ignored. * @param authenticateOnBiometricKeySetup If set, then the biometric key setup always require biometric authentication. * If not set, then only usage of biometric key require biometric authentication. + * @param enableFallbackToSharedBiometryKey If set, then the PowerAuthSDK does one more additional lookup to use legacy + * key shared between multiple PowerAuthSDK instances. * @param minimalRequiredKeychainProtection {@link KeychainProtection} constant with minimal required keychain * protection level that must be supported on the current device. */ private PowerAuthKeychainConfiguration( @NonNull String keychainIdStatus, @NonNull String keychainIdBiometry, - @NonNull String keychainKeyBiometryDefault, + @Nullable String keychainKeyBiometry, @NonNull String keychainIdTokenStore, boolean linkBiometricItemsToCurrentSet, boolean confirmBiometricAuthentication, boolean authenticateOnBiometricKeySetup, + boolean enableFallbackToSharedBiometryKey, @KeychainProtection int minimalRequiredKeychainProtection) { this.keychainIdStatus = keychainIdStatus; this.keychainIdBiometry = keychainIdBiometry; - this.keychainKeyBiometryDefault = keychainKeyBiometryDefault; + this.keychainKeyBiometry = keychainKeyBiometry; this.keychainIdTokenStore = keychainIdTokenStore; this.linkBiometricItemsToCurrentSet = linkBiometricItemsToCurrentSet; this.confirmBiometricAuthentication = confirmBiometricAuthentication; this.authenticateOnBiometricKeySetup = authenticateOnBiometricKeySetup; + this.enableFallbackToSharedBiometryKey = enableFallbackToSharedBiometryKey; this.minimalRequiredKeychainProtection = minimalRequiredKeychainProtection; } @@ -167,10 +195,11 @@ public static class Builder { private @NonNull String keychainStatusId = KEYCHAIN_ID_STATUS; private @NonNull String keychainBiometryId = KEYCHAIN_ID_BIOMETRY; private @NonNull String keychainTokenStoreId = KEYCHAIN_ID_TOKEN_STORE; - private @NonNull String keychainBiometryDefaultKey = KEYCHAIN_KEY_BIOMETRY_DEFAULT; + private @Nullable String keychainKeyBiometry = KEYCHAIN_KEY_BIOMETRY_DEFAULT; private boolean linkBiometricItemsToCurrentSet = DEFAULT_LINK_BIOMETRY_ITEMS_TO_CURRENT_SET; private boolean confirmBiometricAuthentication = DEFAULT_CONFIRM_BIOMETRIC_AUTHENTICATION; private boolean authenticateOnBiometricKeySetup = DEFAULT_AUTHENTICATE_ON_BIOMETRIC_KEY_SETUP; + private boolean enableFallbackToSharedBiometryKey = DEFAULT_ENABLE_FALLBACK_TO_SHARED_BIOMETRY_KEY; private @KeychainProtection int minimalRequiredKeychainProtection = DEFAULT_REQUIRED_KEYCHAIN_PROTECTION; /** @@ -215,11 +244,23 @@ public Builder() { /** * Set name of the Keychain key used to store the default biometry key. * - * @param keychainBiometryDefaultKey Name of the Keychain key used to store the default biometry key. + * @param keychainKeyBiometry Name of the Keychain key used to store the default biometry key. + * @return {@link Builder} + * @deprecated Use {@link #keychainKeyBiometry(String)} as a replacement. + */ + @Deprecated // 1.7.10 - remove in 1.10.0 + public @NonNull Builder keychainBiometryDefaultKey(@NonNull String keychainKeyBiometry) { + this.keychainKeyBiometry = keychainKeyBiometry; + return this; + } + + /** + * Set the name of the key to the biometry Keychain to store biometry-factor protection key. + * @param keychainKeyBiometry name of the key to biometry keychain to store data containing biometry related encryption key. * @return {@link Builder} */ - public @NonNull Builder keychainBiometryDefaultKey(@NonNull String keychainBiometryDefaultKey) { - this.keychainBiometryDefaultKey = keychainBiometryDefaultKey; + public @NonNull Builder keychainKeyBiometry(@NonNull String keychainKeyBiometry) { + this.keychainKeyBiometry = keychainKeyBiometry; return this; } @@ -257,7 +298,7 @@ public Builder() { *

* If set to {@code false}, then RSA cipher is used and only the usage of biometric key * require the biometric authentication. This is due to fact, that RSA cipher can encrypt - * data with using it's public key available immediate after the key-pair is created in + * data with using its public key available immediate after the key-pair is created in * Android KeyStore. *

* The default value is {@code true}. @@ -271,6 +312,20 @@ public Builder() { return this; } + /** + * (Optional) Set, whether PowerAuthSDK instance should also do additional lookup for a legacy biometric key, + * previously shared between multiple PowerAuthSDK object instances. + *

+ * The default value is {@code true} and the fallback is enabled. + * + * @param enable If {@code true} then fallback to legacy key is enabled. + * @return {@link Builder} + */ + public @NonNull Builder enableFallbackToSharedBiometryKey(boolean enable) { + this.enableFallbackToSharedBiometryKey = enable; + return this; + } + /** * Set minimal required keychain protection level that must be supported on the current device. Note that * if you enforce protection higher that {@link KeychainProtection#NONE}, then your application must target @@ -294,11 +349,12 @@ public Builder() { return new PowerAuthKeychainConfiguration( keychainStatusId, keychainBiometryId, - keychainBiometryDefaultKey, + keychainKeyBiometry, keychainTokenStoreId, linkBiometricItemsToCurrentSet, confirmBiometricAuthentication, authenticateOnBiometricKeySetup, + enableFallbackToSharedBiometryKey, minimalRequiredKeychainProtection); } } diff --git a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthSDK.java b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthSDK.java index c2813306..1afa9638 100644 --- a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthSDK.java +++ b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthSDK.java @@ -99,20 +99,7 @@ import io.getlime.security.powerauth.networking.response.IUserInfoListener; import io.getlime.security.powerauth.networking.response.IValidatePasswordListener; import io.getlime.security.powerauth.networking.response.UserInfo; -import io.getlime.security.powerauth.sdk.impl.CompositeCancelableTask; -import io.getlime.security.powerauth.sdk.impl.DefaultExecutorProvider; -import io.getlime.security.powerauth.sdk.impl.DefaultPossessionFactorEncryptionKeyProvider; -import io.getlime.security.powerauth.sdk.impl.DefaultSavePowerAuthStateListener; -import io.getlime.security.powerauth.sdk.impl.DummyCancelable; -import io.getlime.security.powerauth.sdk.impl.FragmentHelper; -import io.getlime.security.powerauth.sdk.impl.GetActivationStatusTask; -import io.getlime.security.powerauth.sdk.impl.ICallbackDispatcher; -import io.getlime.security.powerauth.sdk.impl.IPossessionFactorEncryptionKeyProvider; -import io.getlime.security.powerauth.sdk.impl.IPrivateCryptoHelper; -import io.getlime.security.powerauth.sdk.impl.ISavePowerAuthStateListener; -import io.getlime.security.powerauth.sdk.impl.ITaskCompletion; -import io.getlime.security.powerauth.sdk.impl.MainThreadExecutor; -import io.getlime.security.powerauth.sdk.impl.VaultUnlockReason; +import io.getlime.security.powerauth.sdk.impl.*; import io.getlime.security.powerauth.system.PowerAuthLog; import io.getlime.security.powerauth.system.PowerAuthSystem; @@ -134,6 +121,7 @@ public class PowerAuthSDK { private final @NonNull Keychain mBiometryKeychain; private final @NonNull ICallbackDispatcher mCallbackDispatcher; private final @NonNull PowerAuthTokenStore mTokenStore; + private final @NonNull BiometricDataMapper mBiometricDataMapper; /** * A builder that collects configurations and arguments for {@link PowerAuthSDK}. @@ -268,8 +256,13 @@ public PowerAuthSDK build(@NonNull Context context) throws PowerAuthErrorExcepti ); final Session session = new Session(sessionSetup); + // Biometric data mapper + final ReentrantLock sharedLock = new ReentrantLock(); + final BiometricDataMapper biometricDataMapper = new BiometricDataMapper(sharedLock, session, mConfiguration, mKeychainConfiguration, biometryKeychain); + // Create a final PowerAuthSDK instance final PowerAuthSDK instance = new PowerAuthSDK( + sharedLock, session, mConfiguration, mKeychainConfiguration, @@ -279,6 +272,7 @@ public PowerAuthSDK build(@NonNull Context context) throws PowerAuthErrorExcepti possessionEncryptionKeyProvider, biometryKeychain, tokenStoreKeychain, + biometricDataMapper, mCallbackDispatcher); // Restore state of this SDK instance. @@ -290,6 +284,7 @@ public PowerAuthSDK build(@NonNull Context context) throws PowerAuthErrorExcepti /** * Private class constructor. Use {@link Builder} to create an instance of this class. * + * @param sharedLock Reentrant lock shared between multiple SDK objects. * @param session Low-level {@link Session} instance. * @param configuration Main {@link PowerAuthConfiguration}. * @param keychainConfiguration Keychain configuration. @@ -299,9 +294,11 @@ public PowerAuthSDK build(@NonNull Context context) throws PowerAuthErrorExcepti * @param possessionKeyProvider Possession factor encryption key provider. * @param biometryKeychain Keychain that store biometry-related key. * @param tokenStoreKeychain Keychain that store tokens. + * @param biometricDataMapper Object that helps to get biometric-related encryption key from keychain and keystore. * @param callbackDispatcher Dispatcher that handle callbacks back to application. */ private PowerAuthSDK( + @NonNull ReentrantLock sharedLock, @NonNull Session session, @NonNull PowerAuthConfiguration configuration, @NonNull PowerAuthKeychainConfiguration keychainConfiguration, @@ -311,8 +308,9 @@ private PowerAuthSDK( @NonNull IPossessionFactorEncryptionKeyProvider possessionKeyProvider, @NonNull Keychain biometryKeychain, @NonNull Keychain tokenStoreKeychain, + @NonNull BiometricDataMapper biometricDataMapper, @NonNull ICallbackDispatcher callbackDispatcher) { - this.mLock = new ReentrantLock(); + this.mLock = sharedLock; this.mSession = session; this.mConfiguration = configuration; this.mKeychainConfiguration = keychainConfiguration; @@ -321,6 +319,7 @@ private PowerAuthSDK( this.mStateListener = stateListener; this.mPossessionFactorEncryptionKeyProvider = possessionKeyProvider; this.mBiometryKeychain = biometryKeychain; + this.mBiometricDataMapper = biometricDataMapper; this.mCallbackDispatcher = callbackDispatcher; this.mTokenStore = new PowerAuthTokenStore(this, tokenStoreKeychain, client); } @@ -1430,7 +1429,7 @@ public void onCancel() { * user has to remove the activation by using another channel (typically internet banking, or similar web management console) *

* WARNING: Note that if you have multiple activated SDK instances used in your application at the same time, then you should keep - * shared biometry key intact if it's still used in another SDK instance. For this kind of situations, it's recommended to use + * shared biometry key intact if it's still used in another SDK instance. For this kind of situation, it's recommended to use * another form of this method, where you can decide whether the key should be removed. * * @param context Context @@ -1447,23 +1446,23 @@ public void removeActivationLocal(@NonNull Context context) { * key remains intact. Unlike the `removeActivationWithAuthentication`, this method doesn't inform server about activation removal. * In this case user has to remove the activation by using another channel (typically internet banking, or similar web management console) *

- * NOTE: This method is useful for situations, where the application has multiple SDK instances activated at the same time and - * you need to manage a lifetime of shared biometry key. + * NOTE:The removeSharedBiometryKey parameter is now ignored, because PowerAuthSDK no longer use the shared key for a newly created + * biometry factors. * * @param context Android context. - * @param removeSharedBiometryKey If set to true, then also shared biometry key will be removed. + * @param removeSharedBiometryKey This parameter is ignored. * @throws PowerAuthMissingConfigException thrown in case configuration is not present. */ public void removeActivationLocal(@NonNull Context context, boolean removeSharedBiometryKey) { checkForValidSetup(); - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { - if (removeSharedBiometryKey && mSession.hasBiometryFactor()) { - mBiometryKeychain.remove(mKeychainConfiguration.getKeychainBiometryDefaultKey()); - } - BiometricAuthentication.getBiometricKeystore().removeBiometricKeyEncryptor(); + final BiometricDataMapper.Mapping biometricDataMapping = mBiometricDataMapper.getMapping(null, context, BiometricDataMapper.BIO_MAPPING_REMOVE_KEY); + if (mSession.hasBiometryFactor()) { + mBiometryKeychain.remove(biometricDataMapping.keychainKey); } + BiometricAuthentication.getBiometricKeystore().removeBiometricKeyEncryptor(biometricDataMapping.keystoreId); + // Remove all tokens from token store getTokenStore().cancelAllRequests(); getTokenStore().removeAllLocalTokens(context); @@ -1774,10 +1773,11 @@ public boolean hasBiometryFactor(@NonNull Context context) { // Initialize keystore final IBiometricKeystore keyStore = BiometricAuthentication.getBiometricKeystore(); + final BiometricDataMapper.Mapping biometricDataMapping = mBiometricDataMapper.getMapping(keyStore, context, BiometricDataMapper.BIO_MAPPING_NOOP); // Check if there is biometry factor in session, key in PA2Keychain and key in keystore. - return mSession.hasBiometryFactor() && keyStore.containsBiometricKeyEncryptor() && - mBiometryKeychain.contains(mKeychainConfiguration.getKeychainBiometryDefaultKey()); + return mSession.hasBiometryFactor() && keyStore.containsBiometricKeyEncryptor(biometricDataMapping.keystoreId) && + mBiometryKeychain.contains(biometricDataMapping.keychainKey); } /** @@ -2044,9 +2044,11 @@ public boolean removeBiometryFactor(@NonNull Context context) { final int result = mSession.removeBiometryFactor(); if (result == ErrorCode.OK) { // Update state after each successful calculations + final IBiometricKeystore keystore = BiometricAuthentication.getBiometricKeystore(); + final BiometricDataMapper.Mapping biometricDataMapping = mBiometricDataMapper.getMapping(keystore, context, BiometricDataMapper.BIO_MAPPING_REMOVE_KEY); saveSerializedState(); - mBiometryKeychain.remove(mKeychainConfiguration.getKeychainBiometryDefaultKey()); - BiometricAuthentication.getBiometricKeystore().removeBiometricKeyEncryptor(); + mBiometryKeychain.remove(biometricDataMapping.keychainKey); + keystore.removeBiometricKeyEncryptor(biometricDataMapping.keystoreId); } return result == ErrorCode.OK; } @@ -2226,13 +2228,14 @@ private ICancelable authenticateUsingBiometry( final boolean forceGenerateNewKey, final @NonNull IBiometricAuthenticationCallback callback) { + final BiometricDataMapper.Mapping biometricDataMapping = mBiometricDataMapper.getMapping(null, context, forceGenerateNewKey ? BiometricDataMapper.BIO_MAPPING_CREATE_KEY : BiometricDataMapper.BIO_MAPPING_NOOP); final byte[] rawKeyData; if (forceGenerateNewKey) { // new key has to be generated rawKeyData = mSession.generateSignatureUnlockKey(); } else { // old key should be used, if present - rawKeyData = mBiometryKeychain.getData(mKeychainConfiguration.getKeychainBiometryDefaultKey()); + rawKeyData = mBiometryKeychain.getData(biometricDataMapping.keychainKey); } if (rawKeyData == null) { @@ -2251,6 +2254,7 @@ public void run() { .setTitle(title) .setDescription(description) .setRawKeyData(rawKeyData) + .setKeystoreAlias(biometricDataMapping.keystoreId) .setForceGenerateNewKey(forceGenerateNewKey, mKeychainConfiguration.isLinkBiometricItemsToCurrentSet(), mKeychainConfiguration.isAuthenticateOnBiometricKeySetup()) .setUserConfirmationRequired(mKeychainConfiguration.isConfirmBiometricAuthentication()) .setBackgroundTaskExecutor(mExecutorProvider.getConcurrentExecutor()); @@ -2271,7 +2275,7 @@ public void onBiometricDialogCancelled(boolean userCancel) { public void onBiometricDialogSuccess(@NonNull BiometricKeyData biometricKeyData) { // Store the new key, if a new key was generated if (biometricKeyData.isNewKey()) { - mBiometryKeychain.putData(biometricKeyData.getDataToSave(), mKeychainConfiguration.getKeychainBiometryDefaultKey()); + mBiometryKeychain.putData(biometricKeyData.getDataToSave(), biometricDataMapping.keychainKey); } byte[] normalizedEncryptionKey = mSession.normalizeSignatureUnlockKeyFromData(biometricKeyData.getDerivedData()); callback.onBiometricDialogSuccess(new BiometricKeyData(biometricKeyData.getDataToSave(), normalizedEncryptionKey, biometricKeyData.isNewKey())); diff --git a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/impl/BiometricDataMapper.java b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/impl/BiometricDataMapper.java new file mode 100644 index 00000000..5891d009 --- /dev/null +++ b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/impl/BiometricDataMapper.java @@ -0,0 +1,186 @@ +/* + * Copyright 2024 Wultra s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getlime.security.powerauth.sdk.impl; + +import android.content.Context; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.getlime.security.powerauth.biometry.BiometricAuthentication; +import io.getlime.security.powerauth.biometry.IBiometricKeystore; +import io.getlime.security.powerauth.core.Session; +import io.getlime.security.powerauth.keychain.Keychain; +import io.getlime.security.powerauth.sdk.PowerAuthConfiguration; +import io.getlime.security.powerauth.sdk.PowerAuthKeychainConfiguration; + +import java.util.concurrent.locks.ReentrantLock; + +/** + * The {@code BiometricDataMapping} is a helper class that provides mapping for biometry-related encryption keys stored + * in the {@link Keychain} and the {@code Android KeyStore}. Previous versions of the SDK used a shared key for all + * {@code PowerAuthSDK} instances. This behavior was changed in the following SDK versions to use unique keys generated + * for each instance: + *

+ * This class provides a compatibility layer that allows migration from the shared key to per-instance keys. If the + * application has already created an activation and is using the shared key, the provided mapping will point to the + * shared key until the biometric factor is removed by the application. + *

+ * Related issue: Biometrics not working on multiple + * instances. + */ +public class BiometricDataMapper { + + /** + * The {@code Mapping} class contains information where the biometry-related encryption key is stored. + */ + public static class Mapping { + /** + * If {@code true}, then this mapping points to the shared key. + */ + public final boolean isSharedKey; + /** + * Key identifier (or alias) to the Android KeyStore. + */ + public final @NonNull String keystoreId; + /** + * Storage key to the PowerAuth {@link Keychain}. + */ + public final @NonNull String keychainKey; + + /** + * Construct mapping with required parameters. + * @param isSharedKey Information whether the key is shared. + * @param keystoreId Key identifier (or alias) to the Android KeyStore. + * @param keychainKey Storage key to the PowerAuth {@link Keychain}. + */ + Mapping(boolean isSharedKey, @NonNull String keystoreId, @NonNull String keychainKey) { + this.isSharedKey = isSharedKey; + this.keystoreId = keystoreId; + this.keychainKey = keychainKey; + } + } + + /** + * No additional operation is required when the mapping is created. + */ + public static final int BIO_MAPPING_NOOP = 0; + /** + * The mapping is required when the biometry-related encryption key is being newly crated. In this case, the mapper + * will always return a mapping to the per-instance data. + */ + public static final int BIO_MAPPING_CREATE_KEY = 2; + /** + * The mapping is required when the biometry-related encryption key is being removed. If the current mapping contains + * the mapping to the shared, legacy key, then the mapper will return this legacy mapping. The next call to + * {@link #getMapping(IBiometricKeystore, Context, int)} will provide a mapping to the per-instance data. + */ + public static final int BIO_MAPPING_REMOVE_KEY = 2; + + private final ReentrantLock lock; + private final Session session; + private final String instanceId; + private final String keychainStorageKey; + private final boolean isFallbackToSharedBiometryKeyEnabled; + private final Keychain biometricKeychain; + + /** + * The current mapping. + */ + private Mapping mapping; + + /** + * Create a helper object with all required parameters. + * @param sharedLock Instance of lock shared between multiple internal SDK objects. + * @param session Session instance. + * @param configuration PowerAuth SDK instance configuration. + * @param keychainConfiguration PowerAuth SDK keychain configuration. + * @param biometricKeychain A Keychain for storing biometry-related encryption keys. + */ + public BiometricDataMapper( + @NonNull ReentrantLock sharedLock, + @NonNull Session session, + @NonNull PowerAuthConfiguration configuration, + @NonNull PowerAuthKeychainConfiguration keychainConfiguration, + @NonNull Keychain biometricKeychain) { + this.lock = sharedLock; + this.session = session; + this.instanceId = configuration.getInstanceId(); + this.keychainStorageKey = keychainConfiguration.getKeychainKeyBiometry(); + this.isFallbackToSharedBiometryKeyEnabled = keychainConfiguration.isFallbackToSharedBiometryKeyEnabled(); + this.biometricKeychain = biometricKeychain; + } + + /** + * Get the mapping for stored biometry-related encryption keys. + * @param keyStore Instance of {@link IBiometricKeystore} object. If not provided, then function gets default, shared keystore. + * @param context Android context object. + * @param purpose Specify situation in which the mapping is acquired. Use {@code BIO_MAPPING_*} constants from this class. + * @return Mapping for stored biometry-related encryption keys. + */ + @NonNull + public Mapping getMapping(@Nullable IBiometricKeystore keyStore, @NonNull Context context, int purpose) { + try { + lock.lock(); + + if (keyStore == null) { + keyStore = BiometricAuthentication.getBiometricKeystore(); + } + + // New per-instance identifiers + final String instanceKeystoreId = instanceId; + final String instanceKeychainKey = keychainStorageKey != null ? keychainStorageKey : instanceId; + + if (mapping == null) { + if ((purpose != BIO_MAPPING_CREATE_KEY) && isFallbackToSharedBiometryKeyEnabled) { + // Legacy identifiers. + final String legacyKeystoreId = keyStore.getLegacySharedKeyId(); + final String legacyKeychainKey = keychainStorageKey != null ? keychainStorageKey : PowerAuthKeychainConfiguration.KEYCHAIN_KEY_SHARED_BIOMETRY_KEY; + if (session.hasBiometryFactor()) { + // Looks like session has a biometry factor configured. + // if per-instance keys are set, then don't use the shared encryptor and keychain data. We already have a new + // setup applied on per-instance basis. + if (!biometricKeychain.contains(instanceKeychainKey) && !keyStore.containsBiometricKeyEncryptor(instanceKeystoreId)) { + if (biometricKeychain.contains(legacyKeychainKey) && keyStore.containsBiometricKeyEncryptor(legacyKeystoreId)) { + // Looks like keychain and keystore contains data for a shared key, so try to use such key instead. + mapping = new Mapping(true, legacyKeystoreId, legacyKeychainKey); + } + } + } + } + if (mapping == null) { + // Legacy config was not created, so create a new one, to use per-instance identifiers. + mapping = new Mapping(false, instanceKeystoreId, instanceKeychainKey); + } + } + if (purpose == BIO_MAPPING_REMOVE_KEY) { + // We're going to remove key. If the current config is still legacy, then we should return this legacy + // mapping and simultaneously setup a new one. + if (mapping.isSharedKey) { + final Mapping legacyMapping = mapping; + mapping = new Mapping(false, instanceKeystoreId, instanceKeychainKey); + return legacyMapping; + } + } + return mapping; + } finally { + lock.unlock(); + } + } +} From 1b344070df68ef75a342b955cc17c74ab6bcfc4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20=C4=8Eurech?= Date: Thu, 19 Sep 2024 13:34:48 +0200 Subject: [PATCH 2/4] Android: tests: Integration test compatibility with PAS 1.9+ --- .../support/PowerAuthServerApi.java | 3 +- .../support/PowerAuthTestHelper.java | 3 +- .../client/PowerAuthClientFactory.java | 7 +- .../support/model/ProtocolVersion.java | 3 +- .../support/model/ServerVersion.java | 3 +- .../v10/PowerAuthClientV3_ServerV10.java | 5 +- .../v13/PowerAuthClientV3_ServerV13.java | 5 +- .../v15/PowerAuthClientV3_ServerV15.java | 5 +- .../v19/PowerAuthClientV3_ServerV19.java | 383 ++++++++++++++++++ .../endpoints/BlockActivationEndpoint.java | 96 +++++ .../endpoints/CommitActivationEndpoint.java | 95 +++++ .../endpoints/CreateApplicationEndpoint.java | 59 +++ .../CreateApplicationVersionEndpoint.java | 68 ++++ ...alizedOfflineSignaturePayloadEndpoint.java | 68 ++++ ...alizedOfflineSignaturePayloadEndpoint.java | 67 +++ .../v19/endpoints/EmptyRequestObject.java | 21 + .../GetActivationStatusEndpoint.java | 68 ++++ .../GetApplicationDetailEndpoint.java | 67 +++ .../endpoints/GetApplicationListEndpoint.java | 57 +++ .../endpoints/GetRecoveryConfigEndpoint.java | 59 +++ .../endpoints/GetSystemStatusEndpoint.java | 100 +++++ .../v19/endpoints/InitActivationEndpoint.java | 96 +++++ .../endpoints/RemoveActivationEndpoint.java | 95 +++++ ...etApplicationVersionSupportedEndpoint.java | 83 ++++ .../endpoints/UnblockActivationEndpoint.java | 87 ++++ .../UpdateActivationOtpEndpoint.java | 95 +++++ .../UpdateRecoveryConfigEndpoint.java | 71 ++++ .../v19/endpoints/ValidateTokenEndpoint.java | 95 +++++ .../VerifyEcdsaSignatureEndpoint.java | 91 +++++ .../VerifyOfflineSignatureEndpoint.java | 89 ++++ .../VerifyOnlineSignatureEndpoint.java | 120 ++++++ .../integration/tests/EcdsaSignatureTest.java | 2 +- .../tests/SymmetricSignatureTest.java | 2 +- .../integration/tests/TokenStoreTest.java | 2 +- .../default-instrumentation-tests.properties | 2 +- 35 files changed, 2158 insertions(+), 14 deletions(-) create mode 100644 proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/PowerAuthClientV3_ServerV19.java create mode 100644 proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/BlockActivationEndpoint.java create mode 100644 proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/CommitActivationEndpoint.java create mode 100644 proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/CreateApplicationEndpoint.java create mode 100644 proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/CreateApplicationVersionEndpoint.java create mode 100644 proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/CreateNonPersonalizedOfflineSignaturePayloadEndpoint.java create mode 100644 proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/CreatePersonalizedOfflineSignaturePayloadEndpoint.java create mode 100644 proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/EmptyRequestObject.java create mode 100644 proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/GetActivationStatusEndpoint.java create mode 100644 proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/GetApplicationDetailEndpoint.java create mode 100644 proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/GetApplicationListEndpoint.java create mode 100644 proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/GetRecoveryConfigEndpoint.java create mode 100644 proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/GetSystemStatusEndpoint.java create mode 100644 proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/InitActivationEndpoint.java create mode 100644 proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/RemoveActivationEndpoint.java create mode 100644 proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/SetApplicationVersionSupportedEndpoint.java create mode 100644 proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/UnblockActivationEndpoint.java create mode 100644 proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/UpdateActivationOtpEndpoint.java create mode 100644 proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/UpdateRecoveryConfigEndpoint.java create mode 100644 proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/ValidateTokenEndpoint.java create mode 100644 proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/VerifyEcdsaSignatureEndpoint.java create mode 100644 proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/VerifyOfflineSignatureEndpoint.java create mode 100644 proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/VerifyOnlineSignatureEndpoint.java diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/PowerAuthServerApi.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/PowerAuthServerApi.java index 8ca342aa..5d1dfdbd 100644 --- a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/PowerAuthServerApi.java +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/PowerAuthServerApi.java @@ -301,10 +301,11 @@ public interface PowerAuthServerApi { * @param activationId Activation identifier. * @param data Signed data. * @param signature Signature for data. + * @param format Signature format. Use "DER" (default if not provided) or "JOSE". * @return {@code true} if signature is valid. * @throws Exception In case of failure. */ - boolean verifyEcdsaSignature(@NonNull String activationId, @NonNull String data, @NonNull String signature) throws Exception; + boolean verifyEcdsaSignature(@NonNull String activationId, @NonNull String data, @NonNull String signature, @Nullable String format) throws Exception; /** * Create a payload for offline QR code, signed with non-personalized private key. diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/PowerAuthTestHelper.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/PowerAuthTestHelper.java index ab4e8e36..e8b53665 100644 --- a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/PowerAuthTestHelper.java +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/PowerAuthTestHelper.java @@ -27,7 +27,6 @@ import io.getlime.security.powerauth.integration.support.model.Application; import io.getlime.security.powerauth.integration.support.model.ApplicationDetail; import io.getlime.security.powerauth.integration.support.model.ApplicationVersion; -import io.getlime.security.powerauth.integration.support.model.ProtocolVersion; import io.getlime.security.powerauth.networking.ssl.HttpClientSslNoValidationStrategy; import io.getlime.security.powerauth.sdk.PowerAuthAuthenticationHelper; import io.getlime.security.powerauth.sdk.PowerAuthClientConfiguration; @@ -484,6 +483,6 @@ private PowerAuthTestHelper( * @return Expected protocol version for HTTP headers. */ public @NonNull String getProtocolVersionForHeader() { - return ProtocolVersion.V3_1.versionForHeader; + return testConfig.getServerVersion().maxProtocolVersion.versionForHeader; } } diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/client/PowerAuthClientFactory.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/client/PowerAuthClientFactory.java index 9a3cc19d..9b921b69 100644 --- a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/client/PowerAuthClientFactory.java +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/client/PowerAuthClientFactory.java @@ -24,6 +24,7 @@ import io.getlime.security.powerauth.integration.support.v10.PowerAuthClientV3_ServerV10; import io.getlime.security.powerauth.integration.support.v13.PowerAuthClientV3_ServerV13; import io.getlime.security.powerauth.integration.support.v15.PowerAuthClientV3_ServerV15; +import io.getlime.security.powerauth.integration.support.v19.PowerAuthClientV3_ServerV19; /** * The {@code PowerAuthClientFactory} provides client that communicate with PowerAuth Server API, @@ -46,8 +47,10 @@ public PowerAuthServerApi createApiClient(@NonNull PowerAuthTestConfig testConfi api = new PowerAuthClientV3_ServerV10(testConfig.getServerApiUrl(), testConfig.getAuthorizationHeaderValue(), ServerVersion.V1_0_0, ServerVersion.V1_2_5); } else if (numVer >= ServerVersion.V1_3_0.numericVersion && numVer < ServerVersion.V1_5_0.numericVersion) { api = new PowerAuthClientV3_ServerV13(testConfig.getServerApiUrl(), testConfig.getAuthorizationHeaderValue(), ServerVersion.V1_3_0, ServerVersion.V1_4_0); - } else if (numVer >= ServerVersion.V1_5_0.numericVersion && numVer <= ServerVersion.LATEST.numericVersion) { - api = new PowerAuthClientV3_ServerV15(testConfig.getServerApiUrl(), testConfig.getAuthorizationHeaderValue(), ServerVersion.V1_5_0, null); + } else if (numVer >= ServerVersion.V1_5_0.numericVersion && numVer <= ServerVersion.V1_8_0.numericVersion) { + api = new PowerAuthClientV3_ServerV15(testConfig.getServerApiUrl(), testConfig.getAuthorizationHeaderValue(), ServerVersion.V1_5_0, ServerVersion.V1_8_0); + } else if (numVer >= ServerVersion.V1_9_0.numericVersion && numVer <= ServerVersion.LATEST.numericVersion) { + api = new PowerAuthClientV3_ServerV19(testConfig.getServerApiUrl(), testConfig.getAuthorizationHeaderValue(), ServerVersion.V1_9_0, null); } if (api == null) { throw new Exception("Missing implementation for server API, for server version " + testConfig.getServerVersion().version); diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/model/ProtocolVersion.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/model/ProtocolVersion.java index 532278d5..340495df 100644 --- a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/model/ProtocolVersion.java +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/model/ProtocolVersion.java @@ -23,7 +23,8 @@ public enum ProtocolVersion { V2_1(21, "2.1"), V3(30, "3.0"), V3_1(31, "3.1"), - V3_2(32, "3.2"); + V3_2(32, "3.2"), + V3_3(33, "3.3"); public final int version; public final String versionForHeader; diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/model/ServerVersion.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/model/ServerVersion.java index 66f92899..7de1b393 100644 --- a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/model/ServerVersion.java +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/model/ServerVersion.java @@ -36,13 +36,14 @@ public enum ServerVersion { V1_6_0("1.6", 1006000, ProtocolVersion.V3_2), V1_7_0("1.7", 1007000, ProtocolVersion.V3_2), V1_8_0("1.8", 1008000, ProtocolVersion.V3_2), + V1_9_0("1.9", 1009000, ProtocolVersion.V3_3), ; /** * Contains constant for the latest PowerAuth Server version. */ - public static final ServerVersion LATEST = V1_8_0; + public static final ServerVersion LATEST = V1_9_0; /** * Server version represented as string. diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v10/PowerAuthClientV3_ServerV10.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v10/PowerAuthClientV3_ServerV10.java index 7a6c4db6..3d95e9fe 100644 --- a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v10/PowerAuthClientV3_ServerV10.java +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v10/PowerAuthClientV3_ServerV10.java @@ -319,7 +319,10 @@ public SignatureInfo verifyOfflineSignature(@NonNull SignatureData signatureData } @Override - public boolean verifyEcdsaSignature(@NonNull String activationId, @NonNull String data, @NonNull String signature) throws Exception { + public boolean verifyEcdsaSignature(@NonNull String activationId, @NonNull String data, @NonNull String signature, @Nullable String format) throws Exception { + if (format != null && !"DER".equals(format)) { + throw new IllegalArgumentException("Unsupported format: " + format); + } final VerifyEcdsaSignatureEndpoint.Request request = new VerifyEcdsaSignatureEndpoint.Request(); request.setActivationId(activationId); request.setData(data); diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v13/PowerAuthClientV3_ServerV13.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v13/PowerAuthClientV3_ServerV13.java index b950c62b..1ae00013 100644 --- a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v13/PowerAuthClientV3_ServerV13.java +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v13/PowerAuthClientV3_ServerV13.java @@ -352,7 +352,10 @@ public SignatureInfo verifyOfflineSignature(@NonNull SignatureData signatureData } @Override - public boolean verifyEcdsaSignature(@NonNull String activationId, @NonNull String data, @NonNull String signature) throws Exception { + public boolean verifyEcdsaSignature(@NonNull String activationId, @NonNull String data, @NonNull String signature, @Nullable String format) throws Exception { + if (format != null && !"DER".equals(format)) { + throw new IllegalArgumentException("Unsupported format: " + format); + } final VerifyEcdsaSignatureEndpoint.Request request = new VerifyEcdsaSignatureEndpoint.Request(); request.setActivationId(activationId); request.setData(data); diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v15/PowerAuthClientV3_ServerV15.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v15/PowerAuthClientV3_ServerV15.java index 6a48e856..bb04c2f6 100644 --- a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v15/PowerAuthClientV3_ServerV15.java +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v15/PowerAuthClientV3_ServerV15.java @@ -353,7 +353,10 @@ public SignatureInfo verifyOfflineSignature(@NonNull SignatureData signatureData } @Override - public boolean verifyEcdsaSignature(@NonNull String activationId, @NonNull String data, @NonNull String signature) throws Exception { + public boolean verifyEcdsaSignature(@NonNull String activationId, @NonNull String data, @NonNull String signature, @Nullable String format) throws Exception { + if (format != null && !"DER".equals(format)) { + throw new IllegalArgumentException("Unsupported format: " + format); + } final VerifyEcdsaSignatureEndpoint.Request request = new VerifyEcdsaSignatureEndpoint.Request(); request.setActivationId(activationId); request.setData(data); diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/PowerAuthClientV3_ServerV19.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/PowerAuthClientV3_ServerV19.java new file mode 100644 index 00000000..3e4f88ee --- /dev/null +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/PowerAuthClientV3_ServerV19.java @@ -0,0 +1,383 @@ +/* + * Copyright 2020 Wultra s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getlime.security.powerauth.integration.support.v19; + +import java.util.Collections; +import java.util.List; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.getlime.security.powerauth.integration.support.PowerAuthServerApi; +import io.getlime.security.powerauth.integration.support.client.HttpRestClient; +import io.getlime.security.powerauth.integration.support.model.Activation; +import io.getlime.security.powerauth.integration.support.model.ActivationDetail; +import io.getlime.security.powerauth.integration.support.model.ActivationOtpValidation; +import io.getlime.security.powerauth.integration.support.model.ActivationStatus; +import io.getlime.security.powerauth.integration.support.model.Application; +import io.getlime.security.powerauth.integration.support.model.ApplicationDetail; +import io.getlime.security.powerauth.integration.support.model.ApplicationVersion; +import io.getlime.security.powerauth.integration.support.model.OfflineSignaturePayload; +import io.getlime.security.powerauth.integration.support.model.RecoveryConfig; +import io.getlime.security.powerauth.integration.support.model.ServerConstants; +import io.getlime.security.powerauth.integration.support.model.ServerVersion; +import io.getlime.security.powerauth.integration.support.model.SignatureData; +import io.getlime.security.powerauth.integration.support.model.SignatureInfo; +import io.getlime.security.powerauth.integration.support.model.TokenInfo; +import io.getlime.security.powerauth.integration.support.v19.endpoints.BlockActivationEndpoint; +import io.getlime.security.powerauth.integration.support.v19.endpoints.CommitActivationEndpoint; +import io.getlime.security.powerauth.integration.support.v19.endpoints.CreateApplicationEndpoint; +import io.getlime.security.powerauth.integration.support.v19.endpoints.CreateApplicationVersionEndpoint; +import io.getlime.security.powerauth.integration.support.v19.endpoints.CreateNonPersonalizedOfflineSignaturePayloadEndpoint; +import io.getlime.security.powerauth.integration.support.v19.endpoints.CreatePersonalizedOfflineSignaturePayloadEndpoint; +import io.getlime.security.powerauth.integration.support.v19.endpoints.GetActivationStatusEndpoint; +import io.getlime.security.powerauth.integration.support.v19.endpoints.GetApplicationDetailEndpoint; +import io.getlime.security.powerauth.integration.support.v19.endpoints.GetApplicationListEndpoint; +import io.getlime.security.powerauth.integration.support.v19.endpoints.GetRecoveryConfigEndpoint; +import io.getlime.security.powerauth.integration.support.v19.endpoints.GetSystemStatusEndpoint; +import io.getlime.security.powerauth.integration.support.v19.endpoints.InitActivationEndpoint; +import io.getlime.security.powerauth.integration.support.v19.endpoints.RemoveActivationEndpoint; +import io.getlime.security.powerauth.integration.support.v19.endpoints.SetApplicationVersionSupportedEndpoint; +import io.getlime.security.powerauth.integration.support.v19.endpoints.UnblockActivationEndpoint; +import io.getlime.security.powerauth.integration.support.v19.endpoints.UpdateActivationOtpEndpoint; +import io.getlime.security.powerauth.integration.support.v19.endpoints.UpdateRecoveryConfigEndpoint; +import io.getlime.security.powerauth.integration.support.v19.endpoints.ValidateTokenEndpoint; +import io.getlime.security.powerauth.integration.support.v19.endpoints.VerifyEcdsaSignatureEndpoint; +import io.getlime.security.powerauth.integration.support.v19.endpoints.VerifyOfflineSignatureEndpoint; +import io.getlime.security.powerauth.integration.support.v19.endpoints.VerifyOnlineSignatureEndpoint; + +public class PowerAuthClientV3_ServerV19 implements PowerAuthServerApi { + + private final @NonNull HttpRestClient restClient; + private final @NonNull ServerVersion minSupportedVersion; + private final @NonNull ServerVersion maxSupportedVersion; + private ServerVersion currentServerVersion; + + /** + * Create REST client that communicate with PowerAuth Server RESTful API. + * + * @param serverApiUrl URL to PowerAuth Server. + * @param authorization Optional authorization header value, if PowerAuth Server require authorization. + * @param minSupportedVersion Minimum supported server version. If {@code null} is provided, then {@link ServerVersion#LATEST} is used. + * @param maxSupportedVersion Maximum supported server version. If {@code null} is provided, then {@link ServerVersion#LATEST} is used. + */ + public PowerAuthClientV3_ServerV19(@NonNull String serverApiUrl, @Nullable String authorization, @Nullable ServerVersion minSupportedVersion, @Nullable ServerVersion maxSupportedVersion) throws Exception { + this.restClient = new HttpRestClient(serverApiUrl, authorization); + this.minSupportedVersion = minSupportedVersion == null ? ServerVersion.LATEST : minSupportedVersion; + this.maxSupportedVersion = maxSupportedVersion == null ? ServerVersion.LATEST : maxSupportedVersion; + if (this.minSupportedVersion.numericVersion > this.maxSupportedVersion.numericVersion) { + throw new Exception("Minimum supported server version is higher that maximum."); + } + } + + @Override + public void validateConnection() throws Exception { + final GetSystemStatusEndpoint.Response response = restClient.send(null, new GetSystemStatusEndpoint()); + String version = response.getVersion(); + if (version == null) { + throw new Exception("Missing version in system status response."); + } + currentServerVersion = ServerVersion.versionFromString(version, true); + if (currentServerVersion.numericVersion < minSupportedVersion.numericVersion || currentServerVersion.numericVersion > maxSupportedVersion.numericVersion) { + throw new Exception("Unsupported server version " + response.getVersion()); + } + } + + @NonNull + @Override + public ServerVersion getServerVersion() throws Exception { + if (currentServerVersion == null) { + validateConnection(); + if (currentServerVersion == null) { + throw new Exception("Cannot determine server version."); + } + } + return currentServerVersion; + } + + @Nullable + @Override + public Application findApplicationByName(@NonNull String applicationName) throws Exception { + final GetApplicationListEndpoint.Response response = restClient.send(null, new GetApplicationListEndpoint()); + if (response != null && response.getApplications() != null) { + for (Application app : response.getApplications()) { + // If V1.3 server has been migrated from older version, then contains previous application names in form of identifier. + // There's no such application name in the new model. + if (applicationName.equals(app.getApplicationId())) { + return app; + } + } + } + return null; + } + + @Nullable + @Override + public ApplicationVersion findApplicationVersionByName(@NonNull ApplicationDetail applicationDetail, @NonNull String applicationVersionName) throws Exception { + if (applicationDetail.getVersions() != null) { + for (ApplicationVersion version: applicationDetail.getVersions()) { + // If V1.3 server has been migrated from older version, then contains previous version names in form of identifier. + // There's no such application version name in the new model. + if (applicationVersionName.equals(version.getApplicationVersionId())) { + return version; + } + } + } + return null; + } + + @NonNull + @Override + public List getApplicationList() throws Exception { + final GetApplicationListEndpoint.Response response = restClient.send(null, new GetApplicationListEndpoint()); + return response.getApplications() != null ? response.getApplications() : Collections.emptyList(); + } + + @NonNull + @Override + public Application createApplication(@NonNull String applicationName) throws Exception { + final CreateApplicationEndpoint.Request request = new CreateApplicationEndpoint.Request(); + request.setApplicationId(applicationName); + return restClient.send(request, new CreateApplicationEndpoint()); + } + + @NonNull + @Override + public ApplicationDetail getApplicationDetailByName(@NonNull String applicationName) throws Exception { + final GetApplicationDetailEndpoint.Request request = new GetApplicationDetailEndpoint.Request(); + request.setApplicationName(applicationName); + return restClient.send(request, new GetApplicationDetailEndpoint()); + } + + @NonNull + @Override + public ApplicationDetail getApplicationDetailById(String applicationId) throws Exception { + final GetApplicationDetailEndpoint.Request request = new GetApplicationDetailEndpoint.Request(); + request.setApplicationId(applicationId); + return restClient.send(request, new GetApplicationDetailEndpoint()); + } + + @NonNull + @Override + public ApplicationVersion createApplicationVersion(String applicationId, @NonNull String versionName) throws Exception { + final CreateApplicationVersionEndpoint.Request request = new CreateApplicationVersionEndpoint.Request(); + request.setApplicationId(applicationId); + request.setApplicationVersionId(versionName); + return restClient.send(request, new CreateApplicationVersionEndpoint()); + } + + @Override + public void setApplicationVersionSupported(String applicationVersionId, boolean supported) throws Exception { + final SetApplicationVersionSupportedEndpoint.Request request = new SetApplicationVersionSupportedEndpoint.Request(); + request.setApplicationVersionId(applicationVersionId); + final SetApplicationVersionSupportedEndpoint.Response response = restClient.send(request, new SetApplicationVersionSupportedEndpoint(supported)); + if (response.isSupported() != supported) { + throw new Exception("Application version is still " + (supported ? "unsupported" : "supported") + " after successful response."); + } + } + + @NonNull + @Override + public RecoveryConfig getRecoveryConfig(String applicationId) throws Exception { + final GetRecoveryConfigEndpoint.Request request = new GetRecoveryConfigEndpoint.Request(); + request.setApplicationId(applicationId); + return restClient.send(request, new GetRecoveryConfigEndpoint()); + } + + @Override + public void updateRecoveryConfig(@NonNull RecoveryConfig recoveryConfig) throws Exception { + final UpdateRecoveryConfigEndpoint.Request request = new UpdateRecoveryConfigEndpoint.Request(recoveryConfig); + final UpdateRecoveryConfigEndpoint.Response response = restClient.send(request, new UpdateRecoveryConfigEndpoint()); + if (!response.isUpdated()) { + throw new Exception("Recovery config for application " + recoveryConfig.getApplicationId() + " is not updated after successful response."); + } + } + + @NonNull + @Override + public Activation activationInit(@NonNull Application application, @NonNull String userId, @Nullable String otp, @Nullable ActivationOtpValidation otpValidation, @Nullable Long maxFailureCount) throws Exception { + if ((otp != null && otpValidation == null) || (otp == null) && (otpValidation != null)) { + throw new Exception("Invalid combination of activation OTP and OTP validation."); + } + final InitActivationEndpoint.Request request = new InitActivationEndpoint.Request(); + request.setApplicationId(application.getApplicationId()); + request.setUserId(userId); + request.setActivationOtp(otp); + request.setActivationOtpValidation(otpValidation); + request.setMaxFailureCount(maxFailureCount != null ? maxFailureCount : ServerConstants.DEFAULT_MAX_FAILURE_ATTEMPTS); + return restClient.send(request, new InitActivationEndpoint()); + } + + @NonNull + @Override + public Activation activationInit(@NonNull Application application, @NonNull String userId) throws Exception { + return activationInit(application, userId, null, null, null); + } + + @Override + public void updateActivationOtp(@NonNull String activationId, @NonNull String otp, @Nullable String externalUserId) throws Exception { + final UpdateActivationOtpEndpoint.Request request = new UpdateActivationOtpEndpoint.Request(); + request.setActivationId(activationId); + request.setActivationOtp(otp); + request.setExternalUserId(externalUserId != null ? externalUserId : ServerConstants.DEFAULT_EXTERNAL_USER_ID); + final UpdateActivationOtpEndpoint.Response response = restClient.send(request, new UpdateActivationOtpEndpoint()); + if (!response.isUpdated()) { + throw new Exception("Ativation OTP for activation " + activationId + " is not updated after request success."); + } + } + + @Override + public void updateActivationOtp(@NonNull Activation activation, @NonNull String otp) throws Exception { + updateActivationOtp(activation.getActivationId(), otp, ServerConstants.DEFAULT_EXTERNAL_USER_ID); + } + + @Override + public void activationCommit(@NonNull String activationId, @Nullable String otp, @Nullable String externalUserId) throws Exception { + final CommitActivationEndpoint.Request request = new CommitActivationEndpoint.Request(); + request.setActivationId(activationId); + request.setActivationOtp(otp); + request.setExternalUserId(externalUserId != null ? externalUserId : ServerConstants.DEFAULT_EXTERNAL_USER_ID); + final CommitActivationEndpoint.Response response = restClient.send(request, new CommitActivationEndpoint()); + if (!response.isActivated()) { + throw new Exception("Activation " + activationId + " is not activated after commit after successful response."); + } + } + + @Override + public void activationCommit(@NonNull Activation activation) throws Exception { + activationCommit(activation.getActivationId(), null, ServerConstants.DEFAULT_EXTERNAL_USER_ID); + } + + @Override + public void activationRemove(@NonNull String activationId, @Nullable String externalUserId, boolean revokeRecoveryCodes) throws Exception { + final RemoveActivationEndpoint.Request request = new RemoveActivationEndpoint.Request(); + request.setActivationId(activationId); + request.setExternalUserId(externalUserId); + request.setRevokeRecoveryCodes(revokeRecoveryCodes); + final RemoveActivationEndpoint.Response response = restClient.send(request, new RemoveActivationEndpoint()); + if (!response.isRemoved()) { + throw new Exception("Activation " + activationId + " is not removed after request success."); + } + } + + @Override + public void activationRemove(@NonNull Activation activation) throws Exception { + activationRemove(activation.getActivationId(), ServerConstants.DEFAULT_EXTERNAL_USER_ID, true); + } + + @Override + public void activationBlock(@NonNull String activationId, @Nullable String reason, @Nullable String externalUserId) throws Exception { + final BlockActivationEndpoint.Request request = new BlockActivationEndpoint.Request(); + request.setActivationId(activationId); + request.setReason(reason != null ? reason : ServerConstants.BLOCKED_REASON_NOT_SPECIFIED); + request.setExternalUserId(externalUserId != null ? externalUserId : ServerConstants.DEFAULT_EXTERNAL_USER_ID); + final BlockActivationEndpoint.Response response = restClient.send(request, new BlockActivationEndpoint()); + if (response.getActivationStatus() != ActivationStatus.BLOCKED) { + throw new Exception("Activation " + activationId + " is not blocked after block request success."); + } + } + + @Override + public void activationBlock(@NonNull Activation activation) throws Exception { + activationBlock(activation.getActivationId(), ServerConstants.BLOCKED_REASON_NOT_SPECIFIED, ServerConstants.DEFAULT_EXTERNAL_USER_ID); + } + + @Override + public void activationUnblock(@NonNull String activationId, @Nullable String externalUserId) throws Exception { + final UnblockActivationEndpoint.Request request = new UnblockActivationEndpoint.Request(); + request.setActivationId(activationId); + request.setExternalUserId(externalUserId != null ? externalUserId : ServerConstants.DEFAULT_EXTERNAL_USER_ID); + final UnblockActivationEndpoint.Response response = restClient.send(request, new UnblockActivationEndpoint()); + if (response.getActivationStatus() != ActivationStatus.ACTIVE) { + throw new Exception("Activation " + activationId + " is not active after unblock request success."); + } + } + + @Override + public void activationUnblock(@NonNull Activation activation) throws Exception { + activationUnblock(activation.getActivationId(), ServerConstants.DEFAULT_EXTERNAL_USER_ID); + } + + @NonNull + @Override + public ActivationDetail getActivationDetail(@NonNull String activationId, @Nullable String challenge) throws Exception { + final GetActivationStatusEndpoint.Request request = new GetActivationStatusEndpoint.Request(); + request.setActivationId(activationId); + request.setChallenge(challenge); + return restClient.send(request, new GetActivationStatusEndpoint()); + } + + @NonNull + @Override + public ActivationDetail getActivationDetail(@NonNull Activation activation) throws Exception { + return getActivationDetail(activation.getActivationId(), null); + } + + @NonNull + @Override + public TokenInfo validateToken(@NonNull String tokenId, @NonNull String tokenDigest, @NonNull String nonce, long timestamp, @NonNull String protocolVersion) throws Exception { + final ValidateTokenEndpoint.Request request = new ValidateTokenEndpoint.Request(); + request.setTokenId(tokenId); + request.setTokenDigest(tokenDigest); + request.setNonce(nonce); + request.setTimestamp(timestamp); + request.setProtocolVersion(protocolVersion); + return restClient.send(request, new ValidateTokenEndpoint()); + } + + @NonNull + @Override + public SignatureInfo verifyOnlineSignature(@NonNull SignatureData signatureData) throws Exception { + final VerifyOnlineSignatureEndpoint.Request request = new VerifyOnlineSignatureEndpoint.Request(signatureData); + return restClient.send(request, new VerifyOnlineSignatureEndpoint()); + } + + @NonNull + @Override + public SignatureInfo verifyOfflineSignature(@NonNull SignatureData signatureData) throws Exception { + final VerifyOfflineSignatureEndpoint.Request request = new VerifyOfflineSignatureEndpoint.Request(signatureData); + return restClient.send(request, new VerifyOfflineSignatureEndpoint()); + } + + @Override + public boolean verifyEcdsaSignature(@NonNull String activationId, @NonNull String data, @NonNull String signature, @Nullable String format) throws Exception { + final VerifyEcdsaSignatureEndpoint.Request request = new VerifyEcdsaSignatureEndpoint.Request(); + request.setActivationId(activationId); + request.setData(data); + request.setSignature(signature); + request.setSignatureFormat(format); + final VerifyEcdsaSignatureEndpoint.Response response = restClient.send(request, new VerifyEcdsaSignatureEndpoint()); + return response.isSignatureValid(); + } + + @NonNull + @Override + public OfflineSignaturePayload createNonPersonalizedOfflineSignaturePayload(String applicationId, @NonNull String data) throws Exception { + final CreateNonPersonalizedOfflineSignaturePayloadEndpoint.Request request = new CreateNonPersonalizedOfflineSignaturePayloadEndpoint.Request(); + request.setApplicationId(applicationId); + request.setData(data); + return restClient.send(request, new CreateNonPersonalizedOfflineSignaturePayloadEndpoint()); + } + + @NonNull + @Override + public OfflineSignaturePayload createPersonalizedOfflineSignaturePayload(@NonNull String activationId, @NonNull String data) throws Exception { + final CreatePersonalizedOfflineSignaturePayloadEndpoint.Request request = new CreatePersonalizedOfflineSignaturePayloadEndpoint.Request(); + request.setActivationId(activationId); + request.setData(data); + return restClient.send(request, new CreatePersonalizedOfflineSignaturePayloadEndpoint()); + } +} diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/BlockActivationEndpoint.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/BlockActivationEndpoint.java new file mode 100644 index 00000000..a4234660 --- /dev/null +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/BlockActivationEndpoint.java @@ -0,0 +1,96 @@ +/* + * Copyright 2020 Wultra s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getlime.security.powerauth.integration.support.v19.endpoints; + +import com.google.gson.reflect.TypeToken; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.getlime.security.powerauth.integration.support.client.IServerApiEndpoint; +import io.getlime.security.powerauth.integration.support.model.ActivationStatus; + +public class BlockActivationEndpoint implements IServerApiEndpoint { + + @NonNull + @Override + public String getRelativePath() { + return "/rest/v3/activation/block"; + } + + @Nullable + @Override + public TypeToken getResponseType() { + return TypeToken.get(Response.class); + } + + // Request + + public static class Request { + + private String activationId; + private String externalUserId; + private String reason; + + public String getActivationId() { + return activationId; + } + + public void setActivationId(String activationId) { + this.activationId = activationId; + } + + public String getExternalUserId() { + return externalUserId; + } + + public void setExternalUserId(String externalUserId) { + this.externalUserId = externalUserId; + } + + public String getReason() { + return reason; + } + + public void setReason(String reason) { + this.reason = reason; + } + } + + // Response + + public static class Response { + + private String activationId; + private ActivationStatus activationStatus; + + public String getActivationId() { + return activationId; + } + + public void setActivationId(String activationId) { + this.activationId = activationId; + } + + public ActivationStatus getActivationStatus() { + return activationStatus; + } + + public void setActivationStatus(ActivationStatus activationStatus) { + this.activationStatus = activationStatus; + } + } +} diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/CommitActivationEndpoint.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/CommitActivationEndpoint.java new file mode 100644 index 00000000..b9c4804b --- /dev/null +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/CommitActivationEndpoint.java @@ -0,0 +1,95 @@ +/* + * Copyright 2020 Wultra s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getlime.security.powerauth.integration.support.v19.endpoints; + +import com.google.gson.reflect.TypeToken; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.getlime.security.powerauth.integration.support.client.IServerApiEndpoint; + +public class CommitActivationEndpoint implements IServerApiEndpoint { + + @NonNull + @Override + public String getRelativePath() { + return "/rest/v3/activation/commit"; + } + + @Nullable + @Override + public TypeToken getResponseType() { + return TypeToken.get(Response.class); + } + + // Request + + public static class Request { + + private String activationId; + private String activationOtp; + private String externalUserId; + + public String getActivationId() { + return activationId; + } + + public void setActivationId(String activationId) { + this.activationId = activationId; + } + + public String getActivationOtp() { + return activationOtp; + } + + public void setActivationOtp(String activationOtp) { + this.activationOtp = activationOtp; + } + + public String getExternalUserId() { + return externalUserId; + } + + public void setExternalUserId(String externalUserId) { + this.externalUserId = externalUserId; + } + } + + // Response + + public static class Response { + + private boolean activated; + private String activationId; + + public boolean isActivated() { + return activated; + } + + public void setActivated(boolean activated) { + this.activated = activated; + } + + public String getActivationId() { + return activationId; + } + + public void setActivationId(String activationId) { + this.activationId = activationId; + } + } +} diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/CreateApplicationEndpoint.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/CreateApplicationEndpoint.java new file mode 100644 index 00000000..d18bbbd6 --- /dev/null +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/CreateApplicationEndpoint.java @@ -0,0 +1,59 @@ +/* + * Copyright 2020 Wultra s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getlime.security.powerauth.integration.support.v19.endpoints; + +import com.google.gson.reflect.TypeToken; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.getlime.security.powerauth.integration.support.client.IServerApiEndpoint; +import io.getlime.security.powerauth.integration.support.model.Application; + +public class CreateApplicationEndpoint implements IServerApiEndpoint { + + @NonNull + @Override + public String getRelativePath() { + return "/rest/v3/application/create"; + } + + @Nullable + @Override + public TypeToken getResponseType() { + return TypeToken.get(Response.class); + } + + // Request + + public static class Request { + + private String applicationId; + + public String getApplicationId() { + return applicationId; + } + + public void setApplicationId(String applicationName) { + this.applicationId = applicationName; + } + } + + // Response + + public static class Response extends Application { + } +} diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/CreateApplicationVersionEndpoint.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/CreateApplicationVersionEndpoint.java new file mode 100644 index 00000000..488af324 --- /dev/null +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/CreateApplicationVersionEndpoint.java @@ -0,0 +1,68 @@ +/* + * Copyright 2020 Wultra s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getlime.security.powerauth.integration.support.v19.endpoints; + +import com.google.gson.reflect.TypeToken; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.getlime.security.powerauth.integration.support.client.IServerApiEndpoint; +import io.getlime.security.powerauth.integration.support.model.ApplicationVersion; + +public class CreateApplicationVersionEndpoint implements IServerApiEndpoint { + + @NonNull + @Override + public String getRelativePath() { + return "/rest/v3/application/version/create"; + } + + @Nullable + @Override + public TypeToken getResponseType() { + return TypeToken.get(Response.class); + } + + // Request + + public static class Request { + + private String applicationId; + private String applicationVersionId; + + public String getApplicationId() { + return applicationId; + } + + public void setApplicationId(String applicationId) { + this.applicationId = applicationId; + } + + public String getApplicationVersionId() { + return applicationVersionId; + } + + public void setApplicationVersionId(String applicationVersionName) { + this.applicationVersionId = applicationVersionName; + } + } + + // Response + + public static class Response extends ApplicationVersion { + } +} diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/CreateNonPersonalizedOfflineSignaturePayloadEndpoint.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/CreateNonPersonalizedOfflineSignaturePayloadEndpoint.java new file mode 100644 index 00000000..cf8c82f2 --- /dev/null +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/CreateNonPersonalizedOfflineSignaturePayloadEndpoint.java @@ -0,0 +1,68 @@ +/* + * Copyright 2020 Wultra s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getlime.security.powerauth.integration.support.v19.endpoints; + +import com.google.gson.reflect.TypeToken; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.getlime.security.powerauth.integration.support.client.IServerApiEndpoint; +import io.getlime.security.powerauth.integration.support.model.OfflineSignaturePayload; + +public class CreateNonPersonalizedOfflineSignaturePayloadEndpoint implements IServerApiEndpoint { + + @NonNull + @Override + public String getRelativePath() { + return "/rest/v3/signature/offline/non-personalized/create"; + } + + @Nullable + @Override + public TypeToken getResponseType() { + return TypeToken.get(Response.class); + } + + // Request + + public static class Request { + + private String applicationId; + private String data; + + public String getApplicationId() { + return applicationId; + } + + public void setApplicationId(String applicationId) { + this.applicationId = applicationId; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + } + + // Response + + public static class Response extends OfflineSignaturePayload { + } +} diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/CreatePersonalizedOfflineSignaturePayloadEndpoint.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/CreatePersonalizedOfflineSignaturePayloadEndpoint.java new file mode 100644 index 00000000..187d5487 --- /dev/null +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/CreatePersonalizedOfflineSignaturePayloadEndpoint.java @@ -0,0 +1,67 @@ +/* + * Copyright 2020 Wultra s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getlime.security.powerauth.integration.support.v19.endpoints; + +import com.google.gson.reflect.TypeToken; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.getlime.security.powerauth.integration.support.client.IServerApiEndpoint; +import io.getlime.security.powerauth.integration.support.model.OfflineSignaturePayload; + +public class CreatePersonalizedOfflineSignaturePayloadEndpoint implements IServerApiEndpoint { + + @NonNull + @Override + public String getRelativePath() { + return "/rest/v3/signature/offline/personalized/create"; + } + + @Nullable + @Override + public TypeToken getResponseType() { + return TypeToken.get(Response.class); + } + + // Request + + public static class Request { + private String activationId; + private String data; + + public String getActivationId() { + return activationId; + } + + public void setActivationId(String activationId) { + this.activationId = activationId; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + } + + // Response + + public static class Response extends OfflineSignaturePayload { + } +} diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/EmptyRequestObject.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/EmptyRequestObject.java new file mode 100644 index 00000000..7627a791 --- /dev/null +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/EmptyRequestObject.java @@ -0,0 +1,21 @@ +/* + * Copyright 2020 Wultra s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getlime.security.powerauth.integration.support.v19.endpoints; + +public class EmptyRequestObject { + // Empty request object +} diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/GetActivationStatusEndpoint.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/GetActivationStatusEndpoint.java new file mode 100644 index 00000000..68113b04 --- /dev/null +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/GetActivationStatusEndpoint.java @@ -0,0 +1,68 @@ +/* + * Copyright 2020 Wultra s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getlime.security.powerauth.integration.support.v19.endpoints; + +import com.google.gson.reflect.TypeToken; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.getlime.security.powerauth.integration.support.client.IServerApiEndpoint; +import io.getlime.security.powerauth.integration.support.model.ActivationDetail; + +public class GetActivationStatusEndpoint implements IServerApiEndpoint { + + @NonNull + @Override + public String getRelativePath() { + return "/rest/v3/activation/status"; + } + + @Nullable + @Override + public TypeToken getResponseType() { + return TypeToken.get(Response.class); + } + + // Request + + public static class Request { + + private String activationId; + private String challenge; + + public String getActivationId() { + return activationId; + } + + public void setActivationId(String activationId) { + this.activationId = activationId; + } + + public String getChallenge() { + return challenge; + } + + public void setChallenge(String challenge) { + this.challenge = challenge; + } + } + + // Response + + public static class Response extends ActivationDetail { + } +} diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/GetApplicationDetailEndpoint.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/GetApplicationDetailEndpoint.java new file mode 100644 index 00000000..6265801c --- /dev/null +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/GetApplicationDetailEndpoint.java @@ -0,0 +1,67 @@ +/* + * Copyright 2020 Wultra s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getlime.security.powerauth.integration.support.v19.endpoints; + +import com.google.gson.reflect.TypeToken; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.getlime.security.powerauth.integration.support.client.IServerApiEndpoint; +import io.getlime.security.powerauth.integration.support.model.ApplicationDetail; + +public class GetApplicationDetailEndpoint implements IServerApiEndpoint { + + @NonNull + @Override + public String getRelativePath() { + return "/rest/v3/application/detail"; + } + + @Nullable + @Override + public TypeToken getResponseType() { + return TypeToken.get(Response.class); + } + + // Request + + public static class Request { + private String applicationId; + private String applicationName; + + public String getApplicationId() { + return applicationId; + } + + public void setApplicationId(String applicationId) { + this.applicationId = applicationId; + } + + public String getApplicationName() { + return applicationName; + } + + public void setApplicationName(String applicationName) { + this.applicationName = applicationName; + } + } + + // Response + + public static class Response extends ApplicationDetail { + } +} diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/GetApplicationListEndpoint.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/GetApplicationListEndpoint.java new file mode 100644 index 00000000..95554b70 --- /dev/null +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/GetApplicationListEndpoint.java @@ -0,0 +1,57 @@ +/* + * Copyright 2020 Wultra s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getlime.security.powerauth.integration.support.v19.endpoints; + +import com.google.gson.reflect.TypeToken; + +import java.util.List; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.getlime.security.powerauth.integration.support.client.IServerApiEndpoint; +import io.getlime.security.powerauth.integration.support.model.Application; + +public class GetApplicationListEndpoint implements IServerApiEndpoint { + + @NonNull + @Override + public String getRelativePath() { + return "/rest/v3/application/list"; + } + + @Nullable + @Override + public TypeToken getResponseType() { + return TypeToken.get(Response.class); + } + + // Empty request + + // Response + + public static class Response { + private List applications; + + public List getApplications() { + return applications; + } + + public void setApplications(List applications) { + this.applications = applications; + } + } +} diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/GetRecoveryConfigEndpoint.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/GetRecoveryConfigEndpoint.java new file mode 100644 index 00000000..9055c3bb --- /dev/null +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/GetRecoveryConfigEndpoint.java @@ -0,0 +1,59 @@ +/* + * Copyright 2020 Wultra s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getlime.security.powerauth.integration.support.v19.endpoints; + +import com.google.gson.reflect.TypeToken; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.getlime.security.powerauth.integration.support.client.IServerApiEndpoint; +import io.getlime.security.powerauth.integration.support.model.RecoveryConfig; + +public class GetRecoveryConfigEndpoint implements IServerApiEndpoint { + + @NonNull + @Override + public String getRelativePath() { + return "/rest/v3/recovery/config/detail"; + } + + @Nullable + @Override + public TypeToken getResponseType() { + return TypeToken.get(Response.class); + } + + // Request + + public static class Request { + + private String applicationId; + + public String getApplicationId() { + return applicationId; + } + + public void setApplicationId(String applicationId) { + this.applicationId = applicationId; + } + } + + // Response + + public static class Response extends RecoveryConfig { + } +} diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/GetSystemStatusEndpoint.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/GetSystemStatusEndpoint.java new file mode 100644 index 00000000..8be1ab95 --- /dev/null +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/GetSystemStatusEndpoint.java @@ -0,0 +1,100 @@ +/* + * Copyright 2020 Wultra s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getlime.security.powerauth.integration.support.v19.endpoints; + +import com.google.gson.reflect.TypeToken; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.getlime.security.powerauth.integration.support.client.IServerApiEndpoint; + +public class GetSystemStatusEndpoint implements IServerApiEndpoint { + + @NonNull + @Override + public String getRelativePath() { + return "/rest/v3/status"; + } + + @Nullable + @Override + public TypeToken getResponseType() { + return TypeToken.get(Response.class); + } + + // Empty request + + // Response + + public static class Response { + + private String status; + private String applicationName; + private String applicationDisplayName; + private String applicationEnvironment; + private String timestamp; + private String version; + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getApplicationName() { + return applicationName; + } + + public void setApplicationName(String applicationName) { + this.applicationName = applicationName; + } + + public String getApplicationDisplayName() { + return applicationDisplayName; + } + + public void setApplicationDisplayName(String applicationDisplayName) { + this.applicationDisplayName = applicationDisplayName; + } + + public String getApplicationEnvironment() { + return applicationEnvironment; + } + + public void setApplicationEnvironment(String applicationEnvironment) { + this.applicationEnvironment = applicationEnvironment; + } + + public String getTimestamp() { + return timestamp; + } + + public void setTimestamp(String timestamp) { + this.timestamp = timestamp; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + } +} diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/InitActivationEndpoint.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/InitActivationEndpoint.java new file mode 100644 index 00000000..f9921a89 --- /dev/null +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/InitActivationEndpoint.java @@ -0,0 +1,96 @@ +/* + * Copyright 2020 Wultra s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getlime.security.powerauth.integration.support.v19.endpoints; + +import com.google.gson.reflect.TypeToken; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.getlime.security.powerauth.integration.support.client.IServerApiEndpoint; +import io.getlime.security.powerauth.integration.support.model.Activation; +import io.getlime.security.powerauth.integration.support.model.ActivationOtpValidation; + +public class InitActivationEndpoint implements IServerApiEndpoint { + + @NonNull + @Override + public String getRelativePath() { + return "/rest/v3/activation/init"; + } + + @Nullable + @Override + public TypeToken getResponseType() { + return TypeToken.get(Response.class); + } + + // Request + + public static class Request { + + private String activationOtp; + private ActivationOtpValidation activationOtpValidation; + private String applicationId; + private long maxFailureCount; + private String userId; + + public String getActivationOtp() { + return activationOtp; + } + + public void setActivationOtp(String activationOtp) { + this.activationOtp = activationOtp; + } + + public ActivationOtpValidation getActivationOtpValidation() { + return activationOtpValidation; + } + + public void setActivationOtpValidation(ActivationOtpValidation activationOtpValidation) { + this.activationOtpValidation = activationOtpValidation; + } + + public String getApplicationId() { + return applicationId; + } + + public void setApplicationId(String applicationId) { + this.applicationId = applicationId; + } + + public long getMaxFailureCount() { + return maxFailureCount; + } + + public void setMaxFailureCount(long maxFailureCount) { + this.maxFailureCount = maxFailureCount; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + } + + // Response + + public static class Response extends Activation { + } +} diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/RemoveActivationEndpoint.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/RemoveActivationEndpoint.java new file mode 100644 index 00000000..d9642a07 --- /dev/null +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/RemoveActivationEndpoint.java @@ -0,0 +1,95 @@ +/* + * Copyright 2020 Wultra s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getlime.security.powerauth.integration.support.v19.endpoints; + +import com.google.gson.reflect.TypeToken; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.getlime.security.powerauth.integration.support.client.IServerApiEndpoint; + +public class RemoveActivationEndpoint implements IServerApiEndpoint { + + @NonNull + @Override + public String getRelativePath() { + return "/rest/v3/activation/remove"; + } + + @Nullable + @Override + public TypeToken getResponseType() { + return TypeToken.get(Response.class); + } + + // Request + + public static class Request { + + private String activationId; + private String externalUserId; + private boolean revokeRecoveryCodes; + + public String getActivationId() { + return activationId; + } + + public void setActivationId(String activationId) { + this.activationId = activationId; + } + + public String getExternalUserId() { + return externalUserId; + } + + public void setExternalUserId(String externalUserId) { + this.externalUserId = externalUserId; + } + + public boolean isRevokeRecoveryCodes() { + return revokeRecoveryCodes; + } + + public void setRevokeRecoveryCodes(boolean revokeRecoveryCodes) { + this.revokeRecoveryCodes = revokeRecoveryCodes; + } + } + + // Response + + public static class Response { + + private String activationId; + private boolean removed; + + public String getActivationId() { + return activationId; + } + + public void setActivationId(String activationId) { + this.activationId = activationId; + } + + public boolean isRemoved() { + return removed; + } + + public void setRemoved(boolean removed) { + this.removed = removed; + } + } +} diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/SetApplicationVersionSupportedEndpoint.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/SetApplicationVersionSupportedEndpoint.java new file mode 100644 index 00000000..7095a148 --- /dev/null +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/SetApplicationVersionSupportedEndpoint.java @@ -0,0 +1,83 @@ +/* + * Copyright 2020 Wultra s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getlime.security.powerauth.integration.support.v19.endpoints; + +import com.google.gson.reflect.TypeToken; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.getlime.security.powerauth.integration.support.client.IServerApiEndpoint; + +public class SetApplicationVersionSupportedEndpoint implements IServerApiEndpoint { + + private final boolean supported; + + public SetApplicationVersionSupportedEndpoint(boolean supported) { + this.supported = supported; + } + + @NonNull + @Override + public String getRelativePath() { + return supported ? "/rest/v3/application/version/support" : "/rest/v3/application/version/unsupport"; + } + + @Nullable + @Override + public TypeToken getResponseType() { + return TypeToken.get(Response.class); + } + + // Request + + public static class Request { + + private String applicationVersionId; + + public String getApplicationVersionId() { + return applicationVersionId; + } + + public void setApplicationVersionId(String applicationVersionId) { + this.applicationVersionId = applicationVersionId; + } + } + + // Response + + public static class Response { + + private String applicationVersionId; + private boolean supported; + + public String getApplicationVersionId() { + return applicationVersionId; + } + + public void setApplicationVersionId(String applicationVersionId) { + this.applicationVersionId = applicationVersionId; + } + + public boolean isSupported() { + return supported; + } + + public void setSupported(boolean supported) { + this.supported = supported; + } + } +} diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/UnblockActivationEndpoint.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/UnblockActivationEndpoint.java new file mode 100644 index 00000000..b20d9dd8 --- /dev/null +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/UnblockActivationEndpoint.java @@ -0,0 +1,87 @@ +/* + * Copyright 2020 Wultra s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getlime.security.powerauth.integration.support.v19.endpoints; + +import com.google.gson.reflect.TypeToken; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.getlime.security.powerauth.integration.support.client.IServerApiEndpoint; +import io.getlime.security.powerauth.integration.support.model.ActivationStatus; + +public class UnblockActivationEndpoint implements IServerApiEndpoint { + + @NonNull + @Override + public String getRelativePath() { + return "/rest/v3/activation/unblock"; + } + + @Nullable + @Override + public TypeToken getResponseType() { + return TypeToken.get(UnblockActivationEndpoint.Response.class); + } + + // Request + + public static class Request { + + private String activationId; + private String externalUserId; + + public String getActivationId() { + return activationId; + } + + public void setActivationId(String activationId) { + this.activationId = activationId; + } + + public String getExternalUserId() { + return externalUserId; + } + + public void setExternalUserId(String externalUserId) { + this.externalUserId = externalUserId; + } + } + + // Response + + public static class Response { + + private String activationId; + private ActivationStatus activationStatus; + + public String getActivationId() { + return activationId; + } + + public void setActivationId(String activationId) { + this.activationId = activationId; + } + + public ActivationStatus getActivationStatus() { + return activationStatus; + } + + public void setActivationStatus(ActivationStatus activationStatus) { + this.activationStatus = activationStatus; + } + } +} diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/UpdateActivationOtpEndpoint.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/UpdateActivationOtpEndpoint.java new file mode 100644 index 00000000..5a9aef35 --- /dev/null +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/UpdateActivationOtpEndpoint.java @@ -0,0 +1,95 @@ +/* + * Copyright 2020 Wultra s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getlime.security.powerauth.integration.support.v19.endpoints; + +import com.google.gson.reflect.TypeToken; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.getlime.security.powerauth.integration.support.client.IServerApiEndpoint; + +public class UpdateActivationOtpEndpoint implements IServerApiEndpoint { + + @NonNull + @Override + public String getRelativePath() { + return "/rest/v3/activation/otp/update"; + } + + @Nullable + @Override + public TypeToken getResponseType() { + return TypeToken.get(Response.class); + } + + // Request + + public static class Request { + + private String activationId; + private String activationOtp; + private String externalUserId; + + public String getActivationId() { + return activationId; + } + + public void setActivationId(String activationId) { + this.activationId = activationId; + } + + public String getActivationOtp() { + return activationOtp; + } + + public void setActivationOtp(String activationOtp) { + this.activationOtp = activationOtp; + } + + public String getExternalUserId() { + return externalUserId; + } + + public void setExternalUserId(String externalUserId) { + this.externalUserId = externalUserId; + } + } + + // Response + + public static class Response { + + private String activationId; + private boolean updated; + + public String getActivationId() { + return activationId; + } + + public void setActivationId(String activationId) { + this.activationId = activationId; + } + + public boolean isUpdated() { + return updated; + } + + public void setUpdated(boolean updated) { + this.updated = updated; + } + } +} diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/UpdateRecoveryConfigEndpoint.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/UpdateRecoveryConfigEndpoint.java new file mode 100644 index 00000000..4a33cd8e --- /dev/null +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/UpdateRecoveryConfigEndpoint.java @@ -0,0 +1,71 @@ +/* + * Copyright 2020 Wultra s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getlime.security.powerauth.integration.support.v19.endpoints; + +import com.google.gson.reflect.TypeToken; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.getlime.security.powerauth.integration.support.client.IServerApiEndpoint; +import io.getlime.security.powerauth.integration.support.model.RecoveryConfig; + +public class UpdateRecoveryConfigEndpoint implements IServerApiEndpoint { + + @NonNull + @Override + public String getRelativePath() { + return "/rest/v3/recovery/config/update"; + } + + @Nullable + @Override + public TypeToken getResponseType() { + return TypeToken.get(Response.class); + } + + // Request + + public static class Request extends RecoveryConfig { + + /** + * Create request from given recovery config. + * @param config Recovery config. + */ + public Request(@NonNull RecoveryConfig config) { + setApplicationId(config.getApplicationId()); + setActivationRecoveryEnabled(config.isActivationRecoveryEnabled()); + setRecoveryPostcardEnabled(config.isRecoveryPostcardEnabled()); + setAllowMultipleRecoveryCodes(config.getAllowMultipleRecoveryCodes()); + setRemotePostcardPublicKey(config.getRemotePostcardPublicKey()); + } + } + + // Response + + public static class Response { + + private boolean updated; + + public boolean isUpdated() { + return updated; + } + + public void setUpdated(boolean updated) { + this.updated = updated; + } + } +} diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/ValidateTokenEndpoint.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/ValidateTokenEndpoint.java new file mode 100644 index 00000000..ce1e48e6 --- /dev/null +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/ValidateTokenEndpoint.java @@ -0,0 +1,95 @@ +/* + * Copyright 2020 Wultra s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getlime.security.powerauth.integration.support.v19.endpoints; + +import com.google.gson.reflect.TypeToken; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.getlime.security.powerauth.integration.support.client.IServerApiEndpoint; +import io.getlime.security.powerauth.integration.support.model.TokenInfo; + +public class ValidateTokenEndpoint implements IServerApiEndpoint { + + @NonNull + @Override + public String getRelativePath() { + return "/rest/v3/token/validate"; + } + + @Nullable + @Override + public TypeToken getResponseType() { + return TypeToken.get(Response.class); + } + + // Request + + public static class Request { + + private String tokenId; + private String tokenDigest; + private String nonce; + private long timestamp; + private String protocolVersion; + + public String getTokenId() { + return tokenId; + } + + public void setTokenId(String tokenId) { + this.tokenId = tokenId; + } + + public String getTokenDigest() { + return tokenDigest; + } + + public void setTokenDigest(String tokenDigest) { + this.tokenDigest = tokenDigest; + } + + public String getNonce() { + return nonce; + } + + public void setNonce(String nonce) { + this.nonce = nonce; + } + + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + public String getProtocolVersion() { + return protocolVersion; + } + + public void setProtocolVersion(String protocolVersion) { + this.protocolVersion = protocolVersion; + } + } + + // Response + + public static class Response extends TokenInfo { + } +} diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/VerifyEcdsaSignatureEndpoint.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/VerifyEcdsaSignatureEndpoint.java new file mode 100644 index 00000000..b64d72c0 --- /dev/null +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/VerifyEcdsaSignatureEndpoint.java @@ -0,0 +1,91 @@ +/* + * Copyright 2020 Wultra s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getlime.security.powerauth.integration.support.v19.endpoints; + +import com.google.gson.reflect.TypeToken; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.getlime.security.powerauth.integration.support.client.IServerApiEndpoint; + +public class VerifyEcdsaSignatureEndpoint implements IServerApiEndpoint { + + @NonNull + @Override + public String getRelativePath() { + return "/rest/v3/signature/ecdsa/verify"; + } + + @Nullable + @Override + public TypeToken getResponseType() { + return TypeToken.get(Response.class); + } + + public static class Request { + + private String activationId; + private String data; + private String signature; + private String signatureFormat; + + public String getActivationId() { + return activationId; + } + + public void setActivationId(String activationId) { + this.activationId = activationId; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getSignature() { + return signature; + } + + public void setSignature(String signature) { + this.signature = signature; + } + + public String getSignatureFormat() { + return signatureFormat; + } + + public void setSignatureFormat(String signatureFormat) { + this.signatureFormat = signatureFormat; + } + } + + public static class Response { + + private boolean signatureValid; + + public boolean isSignatureValid() { + return signatureValid; + } + + public void setSignatureValid(boolean signatureValid) { + this.signatureValid = signatureValid; + } + } +} diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/VerifyOfflineSignatureEndpoint.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/VerifyOfflineSignatureEndpoint.java new file mode 100644 index 00000000..eb4ac6c5 --- /dev/null +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/VerifyOfflineSignatureEndpoint.java @@ -0,0 +1,89 @@ +/* + * Copyright 2020 Wultra s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getlime.security.powerauth.integration.support.v19.endpoints; + +import com.google.gson.reflect.TypeToken; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.getlime.security.powerauth.integration.support.client.IServerApiEndpoint; +import io.getlime.security.powerauth.integration.support.model.SignatureData; +import io.getlime.security.powerauth.integration.support.model.SignatureInfo; + +public class VerifyOfflineSignatureEndpoint implements IServerApiEndpoint { + @NonNull + @Override + public String getRelativePath() { + return "/rest/v3/signature/offline/verify"; + } + + @Nullable + @Override + public TypeToken getResponseType() { + return TypeToken.get(Response.class); + } + + public static class Request { + + private String activationId; + private String data; + private String signature; + private boolean allowBiometry; + + public Request(@NonNull SignatureData sd) { + activationId = sd.getActivationId(); + data = sd.getData(); + signature = sd.getSignature(); + allowBiometry = sd.getAllowBiometry() != null ? sd.getAllowBiometry() : false; + } + + public String getActivationId() { + return activationId; + } + + public void setActivationId(String activationId) { + this.activationId = activationId; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getSignature() { + return signature; + } + + public void setSignature(String signature) { + this.signature = signature; + } + + public boolean isAllowBiometry() { + return allowBiometry; + } + + public void setAllowBiometry(boolean allowBiometry) { + this.allowBiometry = allowBiometry; + } + } + + public static class Response extends SignatureInfo { + } +} diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/VerifyOnlineSignatureEndpoint.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/VerifyOnlineSignatureEndpoint.java new file mode 100644 index 00000000..fc5f704c --- /dev/null +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/v19/endpoints/VerifyOnlineSignatureEndpoint.java @@ -0,0 +1,120 @@ +/* + * Copyright 2020 Wultra s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getlime.security.powerauth.integration.support.v19.endpoints; + +import com.google.gson.reflect.TypeToken; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.getlime.security.powerauth.integration.support.client.IServerApiEndpoint; +import io.getlime.security.powerauth.integration.support.model.SignatureData; +import io.getlime.security.powerauth.integration.support.model.SignatureInfo; +import io.getlime.security.powerauth.integration.support.model.SignatureType; + +public class VerifyOnlineSignatureEndpoint implements IServerApiEndpoint { + @NonNull + @Override + public String getRelativePath() { + return "/rest/v3/signature/verify"; + } + + @Nullable + @Override + public TypeToken getResponseType() { + return TypeToken.get(Response.class); + } + + public static class Request { + + private String activationId; + private String applicationKey; + private String data; + private String signature; + private SignatureType signatureType; + private String signatureVersion; + private Long forcedSignatureVersion; + + public Request(@NonNull SignatureData sd) { + activationId = sd.getActivationId(); + applicationKey = sd.getApplicationKey(); + data = sd.getData(); + signature = sd.getSignature(); + signatureType = sd.getSignatureType(); + signatureVersion = sd.getSignatureVersion(); + forcedSignatureVersion = sd.getForcedSignatureVersion(); + } + + public String getActivationId() { + return activationId; + } + + public void setActivationId(String activationId) { + this.activationId = activationId; + } + + public String getApplicationKey() { + return applicationKey; + } + + public void setApplicationKey(String applicationKey) { + this.applicationKey = applicationKey; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getSignature() { + return signature; + } + + public void setSignature(String signature) { + this.signature = signature; + } + + public SignatureType getSignatureType() { + return signatureType; + } + + public void setSignatureType(SignatureType signatureType) { + this.signatureType = signatureType; + } + + public String getSignatureVersion() { + return signatureVersion; + } + + public void setSignatureVersion(String signatureVersion) { + this.signatureVersion = signatureVersion; + } + + public Long getForcedSignatureVersion() { + return forcedSignatureVersion; + } + + public void setForcedSignatureVersion(Long forcedSignatureVersion) { + this.forcedSignatureVersion = forcedSignatureVersion; + } + } + + public static class Response extends SignatureInfo { + } +} diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/tests/EcdsaSignatureTest.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/tests/EcdsaSignatureTest.java index 7cbb3320..c5e0ff4e 100644 --- a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/tests/EcdsaSignatureTest.java +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/tests/EcdsaSignatureTest.java @@ -89,7 +89,7 @@ public void onDataSignedFailed(@NonNull Throwable t) { final String signatureForVerification = Base64.encodeToString(signatureForData, Base64.NO_WRAP); // Now validate that signature on the server. - boolean result = testHelper.getServerApi().verifyEcdsaSignature(activationHelper.getActivation().getActivationId(), dataForVerification, signatureForVerification); + boolean result = testHelper.getServerApi().verifyEcdsaSignature(activationHelper.getActivation().getActivationId(), dataForVerification, signatureForVerification, null); assertTrue(result); } diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/tests/SymmetricSignatureTest.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/tests/SymmetricSignatureTest.java index fb50a23c..9b162b40 100644 --- a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/tests/SymmetricSignatureTest.java +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/tests/SymmetricSignatureTest.java @@ -190,7 +190,7 @@ public void testOnlineSignatureCalculation() throws Exception { final String sigType = Objects.requireNonNull(sigComponents.get("pa_signature_type")).toUpperCase(); final String sigValue = sigComponents.get("pa_signature"); - assertEquals(testHelper.getProtocolVersionForHeader(), sigVersion); + assertEquals("3.1", sigVersion); assertNotNull(sigActivationId); assertNotNull(sigNonce); assertNotNull(sigAppKey); diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/tests/TokenStoreTest.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/tests/TokenStoreTest.java index 1141400e..b1287e61 100644 --- a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/tests/TokenStoreTest.java +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/tests/TokenStoreTest.java @@ -389,7 +389,7 @@ private boolean calculateAndValidateTokenDigest(@NonNull PowerAuthToken token, @ assertEquals("X-PowerAuth-Token", header.getKey()); Map headerComponents = signatureHelper.parseAuthorizationHeader(header); // Validate values - assertEquals(testHelper.getProtocolVersionForHeader(), headerComponents.get("version")); + assertEquals("3.1", headerComponents.get("version")); assertEquals(token.getTokenIdentifier(), headerComponents.get("token_id")); String tokenId = Objects.requireNonNull(headerComponents.get("token_id")); diff --git a/proj-android/configs/default-instrumentation-tests.properties b/proj-android/configs/default-instrumentation-tests.properties index 10c43bef..f58da028 100644 --- a/proj-android/configs/default-instrumentation-tests.properties +++ b/proj-android/configs/default-instrumentation-tests.properties @@ -11,7 +11,7 @@ test.powerauth.restApiUrl=http://localhost:8080/enrollment-server test.powerauth.serverApiUrl=http://localhost:8080/powerauth-java-server test.powerauth.serverAuthUser= test.powerauth.serverAuthPass= -test.powerauth.serverVersion=1.3 +test.powerauth.serverVersion=1.9 test.powerauth.serverAutoCommit= test.powerauth.appName=AutomaticTest-Android test.powerauth.appVersion=default From 423ed0a9d96105676758796f33948b0ad1c59804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20=C4=8Eurech?= Date: Fri, 20 Sep 2024 11:54:04 +0200 Subject: [PATCH 3/4] Android: #620: Deprecate removeActivationLocal(Context, boolean) (1.7.x) --- .../support/PowerAuthTestHelper.java | 4 +- .../integration/tests/ActivationHelper.java | 8 ++-- .../tests/StandardActivationTest.java | 2 +- .../security/powerauth/sdk/PowerAuthSDK.java | 39 ++++++++++--------- 4 files changed, 26 insertions(+), 27 deletions(-) diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/PowerAuthTestHelper.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/PowerAuthTestHelper.java index e8b53665..195958e9 100644 --- a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/PowerAuthTestHelper.java +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/support/PowerAuthTestHelper.java @@ -208,7 +208,7 @@ public Builder(@NonNull Context context, @NonNull PowerAuthTestConfig testConfig if (sdk.hasValidActivation()) { Logger.e("Shared PowerAuthSDK has a valid activation at test initialization."); } - sdk.removeActivationLocal(context, true); + sdk.removeActivationLocal(context); } else { if (!sdk.hasValidActivation()) { Logger.e("Shared PowerAuthSDK doesn't have a valid activation at test initialization."); @@ -451,7 +451,7 @@ private PowerAuthTestHelper( .keychainConfiguration(getSharedPowerAuthKeychainConfiguration()) .build(getContext()); if (resetActivation && sdk.hasValidActivation()) { - sdk.removeActivationLocal(getContext(), true); + sdk.removeActivationLocal(getContext()); } return sdk; } diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/tests/ActivationHelper.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/tests/ActivationHelper.java index f1cdae71..cae6e3ae 100644 --- a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/tests/ActivationHelper.java +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/tests/ActivationHelper.java @@ -189,17 +189,15 @@ public void removeActivation(boolean revokeRecoveryCodes) throws Exception { invalidAuthentication = null; createActivationResult = null; } - removeActivationLocal(true); + removeActivationLocal(); } /** * Remove activation locally. - * - * @param removeSharedBiometryKey If true, then also remove a shared biometry key. */ - public void removeActivationLocal(boolean removeSharedBiometryKey) { + public void removeActivationLocal() { if (powerAuthSDK.hasValidActivation()) { - powerAuthSDK.removeActivationLocal(testHelper.getContext(), removeSharedBiometryKey); + powerAuthSDK.removeActivationLocal(testHelper.getContext()); } } diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/tests/StandardActivationTest.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/tests/StandardActivationTest.java index f89efd06..fe64a4c7 100644 --- a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/tests/StandardActivationTest.java +++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/integration/tests/StandardActivationTest.java @@ -252,7 +252,7 @@ public void onActivationCreateFailed(@NonNull Throwable t) { public void testRemoveActivationLocal() throws Exception { activationHelper.createStandardActivation(true, null); // Remove activation - powerAuthSDK.removeActivationLocal(testHelper.getContext(), true); + powerAuthSDK.removeActivationLocal(testHelper.getContext()); // Back to Initial expectations assertFalse(powerAuthSDK.hasValidActivation()); assertFalse(powerAuthSDK.hasPendingActivation()); diff --git a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthSDK.java b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthSDK.java index 1afa9638..fba26e93 100644 --- a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthSDK.java +++ b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthSDK.java @@ -1436,25 +1436,6 @@ public void onCancel() { * @throws PowerAuthMissingConfigException thrown in case configuration is not present. */ public void removeActivationLocal(@NonNull Context context) { - removeActivationLocal(context, true); - } - - /** - * Removes existing activation from the device. - *

- * This method removes the activation session state and optionally also shared biometry factor key. Cached possession related - * key remains intact. Unlike the `removeActivationWithAuthentication`, this method doesn't inform server about activation removal. - * In this case user has to remove the activation by using another channel (typically internet banking, or similar web management console) - *

- * NOTE:The removeSharedBiometryKey parameter is now ignored, because PowerAuthSDK no longer use the shared key for a newly created - * biometry factors. - * - * @param context Android context. - * @param removeSharedBiometryKey This parameter is ignored. - * @throws PowerAuthMissingConfigException thrown in case configuration is not present. - */ - public void removeActivationLocal(@NonNull Context context, boolean removeSharedBiometryKey) { - checkForValidSetup(); final BiometricDataMapper.Mapping biometricDataMapping = mBiometricDataMapper.getMapping(null, context, BiometricDataMapper.BIO_MAPPING_REMOVE_KEY); @@ -1477,6 +1458,26 @@ public void removeActivationLocal(@NonNull Context context, boolean removeShared clearCachedData(); } + /** + * Removes existing activation from the device. + *

+ * This method removes the activation session state and optionally also shared biometry factor key. Cached possession related + * key remains intact. Unlike the `removeActivationWithAuthentication`, this method doesn't inform server about activation removal. + * In this case user has to remove the activation by using another channel (typically internet banking, or similar web management console) + *

+ * NOTE:The removeSharedBiometryKey parameter is now ignored, because PowerAuthSDK no longer use the shared key for a newly created + * biometry factors. + * + * @param context Android context. + * @param removeSharedBiometryKey This parameter is ignored. + * @throws PowerAuthMissingConfigException thrown in case configuration is not present. + * @deprecated Use {@link #removeActivationLocal(Context)} as a replacement. + */ + @Deprecated // 1.7.10 - remove in 1.10.0 + public void removeActivationLocal(@NonNull Context context, boolean removeSharedBiometryKey) { + removeActivationLocal(context); + } + /** * Clear in-memory cached data. */ From 0073cc6c0294e8f40d0f39ea0732411c671f5a5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juraj=20=C4=8Eurech?= Date: Fri, 20 Sep 2024 13:34:39 +0200 Subject: [PATCH 4/4] Android: #620: Updated documentation (1.7.x) --- docs/Migration-from-1.6-to-1.7.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/Migration-from-1.6-to-1.7.md b/docs/Migration-from-1.6-to-1.7.md index 7d08162e..29d68a1a 100644 --- a/docs/Migration-from-1.6-to-1.7.md +++ b/docs/Migration-from-1.6-to-1.7.md @@ -214,3 +214,12 @@ The behavior of `PowerAuthSDK.authenticateUsingBiometry()` has been slightly cha ### tvOS The `PowerAuthSDK.authenticateUsingBiometry()` function is no longer available on tvOS platform. + +## Changes in 1.7.10+ + +### Android + +- The shared biometry-related encryption key is no longer supported in `PowerAuthSDK`. If an activation is already using the shared key, then it's in use until the activation or the biometry factor is removed. As part of this change, the following methods are now deprecated: + - Method `PowerAuthSDK.removeActivationLocal(Context, boolean)` is now deprecated. Use `removeActivationLocal(Context)` as a replacement. + - Method `PowerAuthKeychainConfiguration.getKeychainBiometryDefaultKey()` is now deprecated. Use `getKeychainKeyBiometry()` as a replacement. + - Method `PowerAuthKeychainConfiguration.Builder.keychainBiometryDefaultKey(String)` is now deprecated. Use `keychainKeyBiometry(String)` as a replacement.