resultCatcher) throws Exception {
- powerAuthSDK.validatePasswordCorrect(testHelper.getContext(), password, new IValidatePasswordListener() {
- @Override
- public void onPasswordValid() {
- resultCatcher.completeWithResult(true);
+ public void onPasswordValid() {
+ resultCatcher.completeWithResult(true);
+ }
+
+ @Override
+ public void onPasswordValidationFailed(@NonNull Throwable t) {
+ if (t instanceof ErrorResponseApiException) {
+ final ErrorResponseApiException apiException = (ErrorResponseApiException)t;
+ if (apiException.getResponseCode() == 401) {
+ resultCatcher.completeWithResult(false);
+ return;
}
+ }
+ resultCatcher.completeWithError(t);
+ }
+ }));
+ }
- @Override
- public void onPasswordValidationFailed(@NonNull Throwable t) {
- if (t instanceof ErrorResponseApiException) {
- final ErrorResponseApiException apiException = (ErrorResponseApiException)t;
- if (apiException.getResponseCode() == 401) {
- resultCatcher.completeWithResult(false);
- return;
- }
- }
- resultCatcher.completeWithError(t);
+ /**
+ * Validate user password on server.
+ *
+ * @param password Password to validate.
+ * @return {@code true} if password is equal to password that was used during PowerAuthSDK activation creation.
+ * @throws Exception In case of other failure.
+ */
+ public boolean validateUserPassword(@NonNull final String password) throws Exception {
+ return AsyncHelper.await(resultCatcher -> powerAuthSDK.validatePassword(testHelper.getContext(), password, new IValidatePasswordListener() {
+ @Override
+ public void onPasswordValid() {
+ resultCatcher.completeWithResult(true);
+ }
+
+ @Override
+ public void onPasswordValidationFailed(@NonNull Throwable t) {
+ if (t instanceof ErrorResponseApiException) {
+ final ErrorResponseApiException apiException = (ErrorResponseApiException)t;
+ if (apiException.getResponseCode() == 401) {
+ resultCatcher.completeWithResult(false);
+ return;
}
- });
+ }
+ resultCatcher.completeWithError(t);
}
- });
+ }));
}
/**
@@ -467,7 +518,7 @@ public PowerAuthSDK getPowerAuthSDK() {
* @return Valid password.
* @throws Exception In case that such object is not created yet.
*/
- public @NonNull String getValidPassword() throws Exception {
+ public @NonNull Password getValidPassword() throws Exception {
if (validAuthentication == null || validAuthentication.getPassword() == null) {
throw new Exception("ActivationHelper has no activation yet.");
}
@@ -479,7 +530,7 @@ public PowerAuthSDK getPowerAuthSDK() {
* @return Invalid password.
* @throws Exception In case that such object is not created yet.
*/
- public @NonNull String getInvalidPassword() throws Exception {
+ public @NonNull Password getInvalidPassword() throws Exception {
if (invalidAuthentication == null || invalidAuthentication.getPassword() == null) {
throw new Exception("ActivationHelper has no activation yet.");
}
@@ -497,4 +548,14 @@ public PowerAuthSDK getPowerAuthSDK() {
}
return createActivationResult;
}
+
+ /**
+ * Extract plaintext password from Password object.
+ * @param password Password object.
+ * @return Extracted password or null if no password object was provided.
+ */
+ @NonNull
+ public static String extractPlaintextPassword(@NonNull Password password) {
+ return PowerAuthAuthenticationHelper.extractPlaintextPassword(password);
+ }
}
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 eaf2b9ea..d869fcfb 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
@@ -97,6 +97,26 @@ public void testCreateWithExtraAttributes() throws Exception {
assertEquals(PowerAuthSystem.getDeviceInfo(), activationDetail.getDeviceInfo());
}
+ @Test
+ public void testCreateAndCommitWithPassword() throws Exception {
+ activationHelper.createStandardActivation(ActivationHelper.TF_COMMIT_WITH_PASSWORD, null);
+ // Validate valid and invalid password
+ boolean passwordValid = activationHelper.validateUserPassword(ActivationHelper.extractPlaintextPassword(activationHelper.getValidPassword()));
+ assertTrue(passwordValid);
+ passwordValid = activationHelper.validateUserPassword(ActivationHelper.extractPlaintextPassword(activationHelper.getInvalidPassword()));
+ assertFalse(passwordValid);
+ }
+
+ @Test
+ public void testCreateAndCommitWithCorePassword() throws Exception {
+ activationHelper.createStandardActivation(ActivationHelper.TF_COMMIT_WITH_CORE_PASSWORD, null);
+ // Validate valid and invalid password
+ boolean passwordValid = activationHelper.validateUserPassword(activationHelper.getValidPassword());
+ assertTrue(passwordValid);
+ passwordValid = activationHelper.validateUserPassword(activationHelper.getInvalidPassword());
+ assertFalse(passwordValid);
+ }
+
// Using legacy method
@Test
diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/sdk/PowerAuthAuthenticationHelper.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/sdk/PowerAuthAuthenticationHelper.java
new file mode 100644
index 00000000..a4d4d78e
--- /dev/null
+++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/sdk/PowerAuthAuthenticationHelper.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2022 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;
+
+import java.nio.charset.Charset;
+
+import androidx.annotation.NonNull;
+import io.getlime.security.powerauth.core.Password;
+
+/**
+ * The {@code PowerAuthAuthenticationHelper} class reveal some methods visible internally only in
+ * ".sdk" package for testing purposes. The method also contains password helpers.
+ */
+public class PowerAuthAuthenticationHelper {
+ /**
+ * Enable or disable strict mode for PowerAuthAuthentication usage validation. See
+ * {@link PowerAuthAuthentication#setStrictValidateAuthenticationUsage(boolean)} for more details.
+ * @param strictMode Enable or disable strict mode.
+ */
+ public static void setStrictModeForUsageValidation(boolean strictMode) {
+ PowerAuthAuthentication.setStrictValidateAuthenticationUsage(strictMode);
+ }
+
+ /**
+ * Extract plaintext password from PowerAuthAuthentication's password.
+ * @param authentication Authentication that should contain password.
+ * @return Extracted password.
+ */
+ @NonNull
+ public static String extractPlaintextPassword(@NonNull PowerAuthAuthentication authentication) {
+ if (authentication.getPassword() == null) {
+ throw new IllegalArgumentException("Authentication object has no password set");
+ }
+ return extractPlaintextPassword(authentication.getPassword());
+ }
+
+ /**
+ * Extract plaintext password from Password object.
+ * @param password Password object.
+ * @return Extracted password.
+ */
+ @NonNull
+ public static String extractPlaintextPassword(@NonNull Password password) {
+ final String[] result = new String[1];
+ password.validatePasswordComplexity(passwordBytes -> {
+ result[0] = new String(passwordBytes, Charset.defaultCharset());
+ return 0;
+ });
+ return result[0];
+ }
+}
diff --git a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/sdk/PowerAuthAuthenticationTests.java b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/sdk/PowerAuthAuthenticationTests.java
index 33977e42..875133e0 100644
--- a/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/sdk/PowerAuthAuthenticationTests.java
+++ b/proj-android/PowerAuthLibrary/src/androidTest/java/io/getlime/security/powerauth/sdk/PowerAuthAuthenticationTests.java
@@ -16,11 +16,15 @@
package io.getlime.security.powerauth.sdk;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import io.getlime.security.powerauth.core.Password;
+import io.getlime.security.powerauth.integration.support.PowerAuthTestHelper;
import io.getlime.security.powerauth.integration.support.RandomGenerator;
+import io.getlime.security.powerauth.integration.tests.ActivationHelper;
import io.getlime.security.powerauth.system.PowerAuthLog;
import static org.junit.Assert.*;
@@ -31,17 +35,25 @@ public class PowerAuthAuthenticationTests {
final RandomGenerator randomGenerator;
final byte[] customPossessionKey;
final byte[] biometryKey;
- final String password;
+ final Password password;
+ final String stringPassword;
public PowerAuthAuthenticationTests() {
this.randomGenerator = new RandomGenerator();
this.customPossessionKey = randomGenerator.generateBytes(16);
this.biometryKey = randomGenerator.generateBytes(16);
- this.password = "1234";
+ this.stringPassword = "1234";
+ this.password = new Password(stringPassword);
PowerAuthLog.setEnabled(true);
}
+ @Before
+ public void setUp() throws Exception {
+ // disable strict validation mode, we would like to return failures instead of throwing an exception.
+ PowerAuthAuthenticationHelper.setStrictModeForUsageValidation(false);
+ }
+
@Test
public void testCommitWithPassword() throws Exception {
PowerAuthAuthentication authentication = PowerAuthAuthentication.commitWithPassword(password);
@@ -52,6 +64,15 @@ public void testCommitWithPassword() throws Exception {
assertTrue(authentication.validateAuthenticationUsage(true));
assertEquals(password, authentication.getPassword());
assertArrayEquals(customPossessionKey, authentication.getOverriddenPossessionKey());
+
+ authentication = PowerAuthAuthentication.commitWithPassword(stringPassword);
+ assertTrue(authentication.validateAuthenticationUsage(true));
+ assertEquals(password, authentication.getPassword());
+
+ authentication = PowerAuthAuthentication.commitWithPassword(stringPassword, customPossessionKey);
+ assertTrue(authentication.validateAuthenticationUsage(true));
+ assertEquals(password, authentication.getPassword());
+ assertArrayEquals(customPossessionKey, authentication.getOverriddenPossessionKey());
}
@Test
@@ -66,6 +87,17 @@ public void testCommitWithPasswordAndBiometry() throws Exception {
assertEquals(password, authentication.getPassword());
assertArrayEquals(biometryKey, authentication.getBiometryFactorRelatedKey());
assertArrayEquals(customPossessionKey, authentication.getOverriddenPossessionKey());
+
+ authentication = PowerAuthAuthentication.commitWithPasswordAndBiometry(stringPassword, biometryKey);
+ assertTrue(authentication.validateAuthenticationUsage(true));
+ assertEquals(password, authentication.getPassword());
+ assertArrayEquals(biometryKey, authentication.getBiometryFactorRelatedKey());
+
+ authentication = PowerAuthAuthentication.commitWithPasswordAndBiometry(stringPassword, biometryKey, customPossessionKey);
+ assertTrue(authentication.validateAuthenticationUsage(true));
+ assertEquals(password, authentication.getPassword());
+ assertArrayEquals(biometryKey, authentication.getBiometryFactorRelatedKey());
+ assertArrayEquals(customPossessionKey, authentication.getOverriddenPossessionKey());
}
@Test
@@ -92,6 +124,17 @@ public void testPossessionWithPassword() throws Exception {
assertEquals(password, authentication.getPassword());
assertArrayEquals(customPossessionKey, authentication.getOverriddenPossessionKey());
assertEquals(1 + 2, authentication.getSignatureFactorsMask());
+
+ authentication = PowerAuthAuthentication.possessionWithPassword(stringPassword);
+ assertTrue(authentication.validateAuthenticationUsage(false));
+ assertEquals(password, authentication.getPassword());
+ assertEquals(1 + 2, authentication.getSignatureFactorsMask());
+
+ authentication = PowerAuthAuthentication.possessionWithPassword(stringPassword, customPossessionKey);
+ assertTrue(authentication.validateAuthenticationUsage(false));
+ assertEquals(password, authentication.getPassword());
+ assertArrayEquals(customPossessionKey, authentication.getOverriddenPossessionKey());
+ assertEquals(1 + 2, authentication.getSignatureFactorsMask());
}
@Test
@@ -114,4 +157,16 @@ public void testLegacyObjectConstructor() throws Exception {
assertFalse(authentication.validateAuthenticationUsage(false));
assertFalse(authentication.validateAuthenticationUsage(true));
}
+
+ @Test
+ public void testLegacyObjectConstructorWithStrictMode() throws Exception {
+ PowerAuthAuthenticationHelper.setStrictModeForUsageValidation(true);
+ PowerAuthAuthentication authentication = new PowerAuthAuthentication();
+ try {
+ authentication.validateAuthenticationUsage(false);
+ fail();
+ } catch (IllegalArgumentException e) {
+ // Ignore exception
+ }
+ }
}
diff --git a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthAuthentication.java b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthAuthentication.java
index 533dcef8..8d480452 100644
--- a/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthAuthentication.java
+++ b/proj-android/PowerAuthLibrary/src/main/java/io/getlime/security/powerauth/sdk/PowerAuthAuthentication.java
@@ -16,8 +16,11 @@
package io.getlime.security.powerauth.sdk;
+import java.nio.charset.Charset;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import io.getlime.security.powerauth.core.Password;
import io.getlime.security.powerauth.system.PowerAuthLog;
/**
@@ -41,11 +44,9 @@ public class PowerAuthAuthentication {
@Deprecated // 1.7.0
public @Nullable byte[] useBiometry;
/**
- * Accessing field directly is now deprecated. Please use appropriate static method to construct
- * {@code PowerAuthAuthentication} object.
+ * Contains {@link Password} object in case that knowledge factor is used in authentication.
*/
- @Deprecated // 1.7.0
- public @Nullable String usePassword;
+ private @Nullable Password password;
/**
* Accessing field directly is now deprecated. Please use appropriate static method to construct
* {@code PowerAuthAuthentication} object.
@@ -53,7 +54,11 @@ public class PowerAuthAuthentication {
@Deprecated // 1.7.0
public @Nullable byte[] overriddenPossessionKey;
-
+ /**
+ * Contains {@code true} if authentication object should be used for activation commit, {@code false}
+ * if object is for signature calculation or {@code null} if this is a legacy object with no usage
+ * specified.
+ */
private final Boolean activationCommit;
/**
@@ -66,7 +71,7 @@ public class PowerAuthAuthentication {
public PowerAuthAuthentication() {
this.usePossession = false;
this.useBiometry = null;
- this.usePassword = null;
+ this.password = null;
this.overriddenPossessionKey = null;
this.activationCommit = null;
}
@@ -84,13 +89,13 @@ public PowerAuthAuthentication() {
* @param overriddenPossessionKey Custom possession factor related key.
*/
PowerAuthAuthentication(
- boolean activationCommit,
- @Nullable String password,
+ Boolean activationCommit,
+ @Nullable Password password,
@Nullable byte[] biometryFactorRelatedKey,
@Nullable byte[] overriddenPossessionKey) {
this.usePossession = true;
this.useBiometry = biometryFactorRelatedKey;
- this.usePassword = password;
+ this.password = password;
this.overriddenPossessionKey = overriddenPossessionKey;
this.activationCommit = activationCommit;
}
@@ -103,7 +108,7 @@ public PowerAuthAuthentication() {
* @return Authentication object constructed for commit activation with the password.
*/
public static PowerAuthAuthentication commitWithPassword(@NonNull String password) {
- return new PowerAuthAuthentication(true, password, null, null);
+ return new PowerAuthAuthentication(true, new Password(password), null, null);
}
/**
@@ -113,7 +118,7 @@ public static PowerAuthAuthentication commitWithPassword(@NonNull String passwor
* @return Authentication object constructed for commit activation and password, with using custom key for the possession factor.
*/
public static PowerAuthAuthentication commitWithPassword(@NonNull String password, @NonNull byte[] overriddenPossessionKey) {
- return new PowerAuthAuthentication(true, password, null, overriddenPossessionKey);
+ return new PowerAuthAuthentication(true, new Password(password), null, overriddenPossessionKey);
}
/**
@@ -123,7 +128,7 @@ public static PowerAuthAuthentication commitWithPassword(@NonNull String passwor
* @return Authentication object constructed for commit activation with password and biometry.
*/
public static PowerAuthAuthentication commitWithPasswordAndBiometry(@NonNull String password, @NonNull byte[] biometryFactorRelatedKey) {
- return new PowerAuthAuthentication(true, password, biometryFactorRelatedKey, null);
+ return new PowerAuthAuthentication(true, new Password(password), biometryFactorRelatedKey, null);
}
/**
@@ -134,6 +139,48 @@ public static PowerAuthAuthentication commitWithPasswordAndBiometry(@NonNull Str
* @return Authentication object constructed for commit activation with password, with using custom key for the possession factor.
*/
public static PowerAuthAuthentication commitWithPasswordAndBiometry(@NonNull String password, @NonNull byte[] biometryFactorRelatedKey, @NonNull byte[] overriddenPossessionKey) {
+ return new PowerAuthAuthentication(true, new Password(password), biometryFactorRelatedKey, overriddenPossessionKey);
+ }
+
+ // core/Password variants
+
+ /**
+ * Construct authentication object for activation commit with password.
+ * @param password Password to set for new activation.
+ * @return Authentication object constructed for commit activation with the password.
+ */
+ public static PowerAuthAuthentication commitWithPassword(@NonNull Password password) {
+ return new PowerAuthAuthentication(true, password, null, null);
+ }
+
+ /**
+ * Construct authentication object for activation commit with password and custom key for the possession factor.
+ * @param password Password to set for new activation.
+ * @param overriddenPossessionKey Custom possession key to set for new activation.
+ * @return Authentication object constructed for commit activation and password, with using custom key for the possession factor.
+ */
+ public static PowerAuthAuthentication commitWithPassword(@NonNull Password password, @NonNull byte[] overriddenPossessionKey) {
+ return new PowerAuthAuthentication(true, password, null, overriddenPossessionKey);
+ }
+
+ /**
+ * Construct authentication object for activation commit with password and with biometry.
+ * @param password Password to set for new activation.
+ * @param biometryFactorRelatedKey Biometry factor related key to set for new activation.
+ * @return Authentication object constructed for commit activation with password and biometry.
+ */
+ public static PowerAuthAuthentication commitWithPasswordAndBiometry(@NonNull Password password, @NonNull byte[] biometryFactorRelatedKey) {
+ return new PowerAuthAuthentication(true, password, biometryFactorRelatedKey, null);
+ }
+
+ /**
+ * Construct authentication object for activation commit with password, biometry and with custom key for the possession factor.
+ * @param password Password to set for new activation.
+ * @param biometryFactorRelatedKey Biometry factor related key to set for new activation.
+ * @param overriddenPossessionKey Custom possession key to set for new activation.
+ * @return Authentication object constructed for commit activation with password, with using custom key for the possession factor.
+ */
+ public static PowerAuthAuthentication commitWithPasswordAndBiometry(@NonNull Password password, @NonNull byte[] biometryFactorRelatedKey, @NonNull byte[] overriddenPossessionKey) {
return new PowerAuthAuthentication(true, password, biometryFactorRelatedKey, overriddenPossessionKey);
}
@@ -162,7 +209,7 @@ public static PowerAuthAuthentication possession(@NonNull byte[] overriddenPosse
* @return Authentication object constructed to calculate signature with possession and knowledge factors.
*/
public static PowerAuthAuthentication possessionWithPassword(@NonNull String password) {
- return new PowerAuthAuthentication(false, password, null, null);
+ return new PowerAuthAuthentication(false, new Password(password), null, null);
}
/**
@@ -172,7 +219,7 @@ public static PowerAuthAuthentication possessionWithPassword(@NonNull String pas
* @return Authentication object constructed to calculate signature with possession and knowledge factors, with using custom possession key.
*/
public static PowerAuthAuthentication possessionWithPassword(@NonNull String password, @NonNull byte[] overriddenPossessionKey) {
- return new PowerAuthAuthentication(false, password, null, overriddenPossessionKey);
+ return new PowerAuthAuthentication(false, new Password(password), null, overriddenPossessionKey);
}
/**
@@ -194,8 +241,29 @@ public static PowerAuthAuthentication possessionWithBiometry(@NonNull byte[] bio
return new PowerAuthAuthentication(false, null, biometryFactorRelatedKey, overriddenPossessionKey);
}
+ // core/Password variants
+
/**
- * Biometry key data, or nil if biometry factor is not used.
+ * Construct authentication object for signature calculation purposes. The signature is calculated with possession and knowledge factors.
+ * @param password Password to use for the signature calculation.
+ * @return Authentication object constructed to calculate signature with possession and knowledge factors.
+ */
+ public static PowerAuthAuthentication possessionWithPassword(@NonNull Password password) {
+ return new PowerAuthAuthentication(false, password, null, null);
+ }
+
+ /**
+ * Construct authentication object for signature calculation purposes. The signature is calculated with possession and knowledge factors, with using custom possession key.
+ * @param password Password to use for the signature calculation.
+ * @param overriddenPossessionKey Custom possession key to use for the signature calculation.
+ * @return Authentication object constructed to calculate signature with possession and knowledge factors, with using custom possession key.
+ */
+ public static PowerAuthAuthentication possessionWithPassword(@NonNull Password password, @NonNull byte[] overriddenPossessionKey) {
+ return new PowerAuthAuthentication(false, password, null, overriddenPossessionKey);
+ }
+
+ /**
+ * @return Biometry key data, or nil if biometry factor is not used.
*/
@Nullable
public byte[] getBiometryFactorRelatedKey() {
@@ -203,15 +271,54 @@ public byte[] getBiometryFactorRelatedKey() {
}
/**
- * Password to be used for knowledge factor, or nil if knowledge factor is not used.
+ * @return Password to be used for knowledge factor, or nil if knowledge factor is not used.
+ */
+ @Nullable
+ public Password getPassword() {
+ return password;
+ }
+
+ /**
+ * Direct access to {@code usePassword} property is no longer possible. Use static authentication
+ * object functions to construct appropriate authentication object, or {@link #getPassword()}
+ * function to test, whether the knowledge factor is used.
+ * @param password Password to set to authentication.
+ */
+ @Deprecated // 1.7.0
+ public void setUsePassword(@Nullable String password) {
+ if (password != null) {
+ this.password = new Password(password);
+ } else {
+ this.password = null;
+ }
+ }
+
+ /**
+ * Direct access to {@code usePassword} property is no longer possible. Use static authentication
+ * object functions to construct appropriate authentication object, or {@link #getPassword()}
+ * function to test, whether the knowledge factor is used.
+ *
+ * @return User password in plaintext form.
*/
+ @Deprecated // 1.7.2
@Nullable
- public String getPassword() {
- return usePassword;
+ public String getUsePassword() {
+ if (password != null) {
+ // The purpose of 'validatePasswordComplexity' is quite different, but there's no other API to extract
+ // plaintext passphrase from Password. We're keeping this only for a compatibility reasons,
+ // so the implementation will be removed in 1.8.x release.
+ final String[] result = new String[1];
+ password.validatePasswordComplexity(passwordBytes -> {
+ result[0] = new String(passwordBytes, Charset.defaultCharset());
+ return 0;
+ });
+ return result[0];
+ }
+ return null;
}
/**
- * If non-null, then custom key is specified for the possession factor.
+ * @return If non-null, then custom key is specified for the possession factor.
*/
@Nullable
public byte[] getOverriddenPossessionKey() {
@@ -229,7 +336,7 @@ int getSignatureFactorsMask() {
if (usePossession) {
factors |= 1;
}
- if (usePassword != null) {
+ if (password != null) {
factors |= 2;
}
if (useBiometry != null) {
@@ -245,6 +352,36 @@ int getSignatureFactorsMask() {
* @return false if object is created for the different purpose or is legacy constructed.
*/
boolean validateAuthenticationUsage(boolean forCommit) {
+ boolean result = validateAuthenticationUsageImpl(forCommit);
+ if (!result && strictAuthenticationUsageValidation) {
+ throw new IllegalArgumentException("Invalid PowerAuthAuthentication object provided");
+ }
+ return result;
+ }
+
+ /**
+ * If set to true, then validateAuthenticationUsage() throws IllegalArgumentException().
+ */
+ private static boolean strictAuthenticationUsageValidation = false;
+
+ /**
+ * Enable or disable strict mode for {@link #validateAuthenticationUsage(boolean)} method.
+ * If strict mode is enabled, then validation throws an error in case that validation fails.
+ * This is useful only for PowerAuth SDK unit and integration testing.
+ *
+ * @param strict Enable or disable strict mode.
+ */
+ static void setStrictValidateAuthenticationUsage(boolean strict) {
+ strictAuthenticationUsageValidation = strict;
+ }
+
+ /**
+ * Validate usage of PowerAuthAuthentication object. If something doesn't match, then function
+ * print warning to the debug console.
+ * @param forCommit If true, then activation commit is expected.
+ * @return false if object is created for the different purpose or is legacy constructed.
+ */
+ private boolean validateAuthenticationUsageImpl(boolean forCommit) {
if (activationCommit == null) {
PowerAuthLog.e("WARNING: Using PowerAuthAuthentication object created with legacy constructor.");
return false;
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 0e070c16..e253f563 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
@@ -413,7 +413,6 @@ private byte[] deviceRelatedKey(@NonNull Context context) {
// Generate signature key encryption keys
byte[] possessionKey;
byte[] biometryKey = null;
- Password knowledgeKey = null;
if (authentication.getOverriddenPossessionKey() != null) {
possessionKey = authentication.getOverriddenPossessionKey();
@@ -425,12 +424,8 @@ private byte[] deviceRelatedKey(@NonNull Context context) {
biometryKey = authentication.getBiometryFactorRelatedKey();
}
- if (authentication.getPassword() != null) {
- knowledgeKey = new Password(authentication.getPassword());
- }
-
// Prepare signature unlock keys structure
- return new SignatureUnlockKeys(possessionKey, biometryKey, knowledgeKey);
+ return new SignatureUnlockKeys(possessionKey, biometryKey, authentication.getPassword());
}
/**
@@ -917,7 +912,6 @@ public void run() {
}
}
-
/**
* Commit activation that was created and store related data using default authentication instance setup with provided password.
*
@@ -932,6 +926,20 @@ public int commitActivationWithPassword(@NonNull Context context, @NonNull Strin
return commitActivationWithAuthentication(context, PowerAuthAuthentication.possessionWithPassword(password));
}
+ /**
+ * Commit activation that was created and store related data using default authentication instance setup with provided password.
+ *
+ * @param context Context
+ * @param password Password to be used for the knowledge related authentication factor.
+ * @return int {@link PowerAuthErrorCodes} error code.
+ * @throws PowerAuthMissingConfigException thrown in case configuration is not present.
+ */
+ @CheckResult
+ @PowerAuthErrorCodes
+ public int commitActivationWithPassword(@NonNull Context context, @NonNull Password password) {
+ return commitActivationWithAuthentication(context, PowerAuthAuthentication.possessionWithPassword(password));
+ }
+
/**
* Commit activation that was created and store related data using default authentication instance setup with provided password and biometry key.
*
@@ -953,6 +961,30 @@ public ICancelable commitActivation(
@NonNull String description,
@NonNull final String password,
final @NonNull ICommitActivationWithBiometryListener callback) {
+ return commitActivationWithBiometryImpl(context, FragmentHelper.from(fragmentActivity), title, description, new Password(password), callback);
+ }
+
+ /**
+ * Commit activation that was created and store related data using default authentication instance setup with provided password and biometry key.
+ *
+ * @param context Context.
+ * @param fragmentActivity Activity of the application that will host the prompt.
+ * @param title Dialog title.
+ * @param description Dialog description.
+ * @param password Password used for activation commit.
+ * @param callback Callback with the authentication result.
+ * @return {@link ICancelable} object associated with the biometric prompt.
+ */
+ @UiThread
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ @NonNull
+ public ICancelable commitActivation(
+ final @NonNull Context context,
+ @NonNull FragmentActivity fragmentActivity,
+ @NonNull String title,
+ @NonNull String description,
+ @NonNull final Password password,
+ final @NonNull ICommitActivationWithBiometryListener callback) {
return commitActivationWithBiometryImpl(context, FragmentHelper.from(fragmentActivity), title, description, password, callback);
}
@@ -977,6 +1009,30 @@ public ICancelable commitActivation(
@NonNull String description,
@NonNull final String password,
final @NonNull ICommitActivationWithBiometryListener callback) {
+ return commitActivationWithBiometryImpl(context, FragmentHelper.from(fragment), title, description, new Password(password), callback);
+ }
+
+ /**
+ * Commit activation that was created and store related data using default authentication instance setup with provided password and biometry key.
+ *
+ * @param context Context.
+ * @param fragment Fragment of the application that will host the prompt.
+ * @param title Dialog title.
+ * @param description Dialog description.
+ * @param password Password used for activation commit.
+ * @param callback Callback with the authentication result.
+ * @return {@link ICancelable} object associated with the biometric prompt.
+ */
+ @UiThread
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ @NonNull
+ public ICancelable commitActivation(
+ final @NonNull Context context,
+ @NonNull Fragment fragment,
+ @NonNull String title,
+ @NonNull String description,
+ @NonNull final Password password,
+ final @NonNull ICommitActivationWithBiometryListener callback) {
return commitActivationWithBiometryImpl(context, FragmentHelper.from(fragment), title, description, password, callback);
}
@@ -999,7 +1055,7 @@ private ICancelable commitActivationWithBiometryImpl(
@NonNull FragmentHelper fragmentHelper,
@NonNull String title,
@NonNull String description,
- @NonNull final String password,
+ @NonNull final Password password,
final @NonNull ICommitActivationWithBiometryListener callback) {
return authenticateUsingBiometry(context, fragmentHelper, title, description, true, new IBiometricAuthenticationCallback() {
@Override
@@ -1030,7 +1086,7 @@ public void onBiometricDialogFailed(@NonNull PowerAuthErrorException error) {
/**
* Commit activation that was created and store related data using default authentication instance setup with provided password.
*
- * Calling this method is equivalent to commitActivationWithAuthentication with authentication object set to use all factors and provided password.
+ * Calling this method is equivalent to {@link #commitActivationWithAuthentication(Context, PowerAuthAuthentication)} with authentication object set to use all factors and provided password.
*
* @param context Context
* @param password Password to be used for the knowledge related authentication factor.
@@ -1042,6 +1098,24 @@ public void onBiometricDialogFailed(@NonNull PowerAuthErrorException error) {
@CheckResult
@PowerAuthErrorCodes
public int commitActivationWithPassword(@NonNull Context context, @NonNull String password, @Nullable byte[] encryptedBiometryKey) {
+ return commitActivationWithPassword(context, new Password(password), encryptedBiometryKey);
+ }
+
+ /**
+ * Commit activation that was created and store related data using default authentication instance setup with provided password.
+ *
+ * Calling this method is equivalent to {@link #commitActivationWithAuthentication(Context, PowerAuthAuthentication)} with authentication object set to use all factors and provided password.
+ *
+ * @param context Context
+ * @param password Password to be used for the knowledge related authentication factor.
+ * @param encryptedBiometryKey Optional biometry related factor key.
+ * @return int {@link PowerAuthErrorCodes} error code.
+ * @throws PowerAuthMissingConfigException thrown in case configuration is not present.
+ */
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ @CheckResult
+ @PowerAuthErrorCodes
+ public int commitActivationWithPassword(@NonNull Context context, @NonNull Password password, @Nullable byte[] encryptedBiometryKey) {
return commitActivationWithAuthentication(context, new PowerAuthAuthentication(true, password, encryptedBiometryKey, null));
}
@@ -1056,13 +1130,16 @@ public int commitActivationWithPassword(@NonNull Context context, @NonNull Strin
@CheckResult
@PowerAuthErrorCodes
public int commitActivationWithAuthentication(@NonNull Context context, @NonNull PowerAuthAuthentication authentication) {
-
// Input validations
checkForValidSetup();
// Check if there is a pending activation present and not an already existing valid activation
if (!mSession.hasPendingActivation()) {
return PowerAuthErrorCodes.INVALID_ACTIVATION_STATE;
}
+ if (authentication.getPassword() == null) {
+ PowerAuthLog.e("Password is required for activation commit");
+ return PowerAuthErrorCodes.WRONG_PARAMETER;
+ }
// Validate authentication usage for commit.
authentication.validateAuthenticationUsage(true);
@@ -1070,10 +1147,9 @@ public int commitActivationWithAuthentication(@NonNull Context context, @NonNull
// Prepare key encryption keys
final byte[] possessionKey = deviceRelatedKey(context);
final byte[] biometryKey = authentication.getBiometryFactorRelatedKey();
- final Password knowledgeKey = authentication.getPassword() != null ? new Password(authentication.getPassword()) : null;
// Prepare signature unlock keys structure
- final SignatureUnlockKeys keys = new SignatureUnlockKeys(possessionKey, biometryKey, knowledgeKey);
+ final SignatureUnlockKeys keys = new SignatureUnlockKeys(possessionKey, biometryKey, authentication.getPassword());
// Complete the activation
final int result = mSession.completeActivation(keys);
@@ -1422,6 +1498,7 @@ public void removeActivationLocal(@NonNull Context context, boolean removeShared
// Determine authentication factor type
@SignatureFactor final int signatureFactor = determineSignatureFactorForAuthentication(authentication);
+ // @Deprecated // 1.7.0 - the next test is no longer required once we migrate to immutable authentication object
if (signatureFactor == 0) {
throw new PowerAuthErrorException(PowerAuthErrorCodes.WRONG_PARAMETER, "Invalid combination of signature factors.");
}
@@ -1515,7 +1592,23 @@ public void onFetchEncryptedVaultUnlockKeyFailed(Throwable t) {
* @throws PowerAuthMissingConfigException thrown in case configuration is not present.
*/
public boolean changePasswordUnsafe(@NonNull final String oldPassword, @NonNull final String newPassword) {
- final int result = mSession.changeUserPassword(new Password(oldPassword), new Password(newPassword));
+ return changePasswordUnsafe(new Password(oldPassword), new Password(newPassword));
+ }
+
+ /**
+ * Change the password using local re-encryption, do not validate old password by calling any endpoint.
+ *
+ * You are responsible for validating the old password against some server endpoint yourself before using it in this method.
+ * If you do not validate the old password to make sure it is correct, calling this method will corrupt the local data, since
+ * existing data will be decrypted using invalid PIN code and re-encrypted with a new one.
+ *
+ * @param oldPassword Old password, currently set to store the data.
+ * @param newPassword New password to be set to store the data.
+ * @return Returns 'true' in case password was changed without error, 'false' otherwise.
+ * @throws PowerAuthMissingConfigException thrown in case configuration is not present.
+ */
+ public boolean changePasswordUnsafe(@NonNull final Password oldPassword, @NonNull final Password newPassword) {
+ final int result = mSession.changeUserPassword(oldPassword, newPassword);
if (result == ErrorCode.OK) {
saveSerializedState();
return true;
@@ -1535,12 +1628,27 @@ public boolean changePasswordUnsafe(@NonNull final String oldPassword, @NonNull
*/
public @Nullable
ICancelable changePassword(@NonNull Context context, @NonNull final String oldPassword, @NonNull final String newPassword, @NonNull final IChangePasswordListener listener) {
+ return changePassword(context, new Password(oldPassword), new Password(newPassword), listener);
+ }
+
+ /**
+ * Validate old password by calling a PowerAuth REST API and if it's correct, then change the password to new one.
+ *
+ * @param context Context.
+ * @param oldPassword Old password, currently set to store the data.
+ * @param newPassword New password, to be set in case authentication with old password passes.
+ * @param listener The callback method with the password change result.
+ * @return {@link ICancelable} object associated with the running HTTP request.
+ * @throws PowerAuthMissingConfigException thrown in case configuration is not present.
+ */
+ public @Nullable
+ ICancelable changePassword(@NonNull Context context, @NonNull final Password oldPassword, @NonNull final Password newPassword, @NonNull final IChangePasswordListener listener) {
// At first, validate the old password
- return validatePasswordCorrect(context, oldPassword, new IValidatePasswordListener() {
+ return validatePassword(context, oldPassword, new IValidatePasswordListener() {
@Override
public void onPasswordValid() {
// Old password is valid, so let's change it to new one
- final int result = mSession.changeUserPassword(new Password(oldPassword), new Password(newPassword));
+ final int result = mSession.changeUserPassword(oldPassword, newPassword);
if (result == ErrorCode.OK) {
// Update state
saveSerializedState();
@@ -1599,6 +1707,32 @@ public ICancelable addBiometryFactor(
final @NonNull String description,
@NonNull String password,
@NonNull final IAddBiometryFactorListener listener) {
+ return addBiometryFactorImpl(context, FragmentHelper.from(fragment), title, description, new Password(password), listener);
+ }
+
+ /**
+ * Regenerate a biometry related factor key.
+ *
+ * This method calls PowerAuth REST API endpoint to obtain the vault encryption key used for original private key encryption.
+ *
+ * @param context Context.
+ * @param fragment The fragment of the application that will host the prompt.
+ * @param title Title for the biometry alert
+ * @param description Description displayed in the biometry alert
+ * @param password Password used for authentication during vault unlocking call.
+ * @param listener The callback method with the encrypted key.
+ * @return {@link ICancelable} object associated with the running HTTP request and the biometric prompt.
+ */
+ @UiThread
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ @Nullable
+ public ICancelable addBiometryFactor(
+ @NonNull final Context context,
+ final @NonNull Fragment fragment,
+ final @NonNull String title,
+ final @NonNull String description,
+ @NonNull Password password,
+ @NonNull final IAddBiometryFactorListener listener) {
return addBiometryFactorImpl(context, FragmentHelper.from(fragment), title, description, password, listener);
}
@@ -1625,6 +1759,32 @@ public ICancelable addBiometryFactor(
final @NonNull String description,
@NonNull String password,
@NonNull final IAddBiometryFactorListener listener) {
+ return addBiometryFactorImpl(context, FragmentHelper.from(fragmentActivity), title, description, new Password(password), listener);
+ }
+
+ /**
+ * Regenerate a biometry related factor key.
+ *
+ * This method calls PowerAuth REST API endpoint to obtain the vault encryption key used for original private key encryption.
+ *
+ * @param context Context.
+ * @param fragmentActivity The activity of the client application that will host the prompt.
+ * @param title Title for the biometry alert
+ * @param description Description displayed in the biometry alert
+ * @param password Password used for authentication during vault unlocking call.
+ * @param listener The callback method with the encrypted key.
+ * @return {@link ICancelable} object associated with the running HTTP request and the biometric prompt.
+ */
+ @UiThread
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ @Nullable
+ public ICancelable addBiometryFactor(
+ @NonNull final Context context,
+ final @NonNull FragmentActivity fragmentActivity,
+ final @NonNull String title,
+ final @NonNull String description,
+ @NonNull Password password,
+ @NonNull final IAddBiometryFactorListener listener) {
return addBiometryFactorImpl(context, FragmentHelper.from(fragmentActivity), title, description, password, listener);
}
@@ -1649,7 +1809,7 @@ private ICancelable addBiometryFactorImpl(
final @NonNull FragmentHelper fragmentHelper,
final @NonNull String title,
final @NonNull String description,
- @NonNull String password,
+ @NonNull Password password,
@NonNull final IAddBiometryFactorListener listener) {
// Initial authentication object, used for vault unlock call on server
@@ -1720,7 +1880,31 @@ public void onFetchEncryptedVaultUnlockKeyFailed(Throwable t) {
* @return {@link ICancelable} object associated with the running HTTP request.
*/
public @Nullable
- ICancelable addBiometryFactor(@NonNull final Context context, @NonNull String password, final @NonNull byte[] encryptedBiometryKey, @NonNull final IAddBiometryFactorListener listener) {
+ ICancelable addBiometryFactor(
+ final @NonNull Context context,
+ @NonNull String password,
+ final @NonNull byte[] encryptedBiometryKey,
+ final @NonNull IAddBiometryFactorListener listener) {
+ return addBiometryFactor(context, new Password(password), encryptedBiometryKey, listener);
+ }
+
+ /**
+ * Regenerate a biometry related factor key.
+ *
+ * This method calls PowerAuth REST API endpoint to obtain the vault encryption key used for original private key encryption.
+ *
+ * @param context Context.
+ * @param password Password used for authentication during vault unlocking call.
+ * @param encryptedBiometryKey Encrypted biometry key used for storing biometry related factor key.
+ * @param listener The callback method with the encrypted key.
+ * @return {@link ICancelable} object associated with the running HTTP request.
+ */
+ public @Nullable
+ ICancelable addBiometryFactor(
+ final @NonNull Context context,
+ @NonNull Password password,
+ final @NonNull byte[] encryptedBiometryKey,
+ final @NonNull IAddBiometryFactorListener listener) {
final PowerAuthAuthentication authAuthentication = PowerAuthAuthentication.possessionWithPassword(password);
return fetchEncryptedVaultUnlockKey(context, authAuthentication, VaultUnlockReason.ADD_BIOMETRY, new IFetchEncryptedVaultUnlockKeyListener() {
@@ -1810,14 +1994,43 @@ public void onFetchEncryptedVaultUnlockKeyFailed(Throwable t) {
/**
* Validate a user password. This method calls PowerAuth REST API endpoint to validate the password on the server.
+ * This method is deprecated and you can use {@link #validatePassword(Context, String, IValidatePasswordListener)}
+ * as a replacement.
*
* @param context Context.
* @param password Password to be verified.
* @param listener The callback method with error associated with the password validation.
* @return {@link ICancelable} object associated with the running HTTP request.
*/
+ @Deprecated // 1.7.2
public @Nullable
ICancelable validatePasswordCorrect(@NonNull Context context, @NonNull String password, @NonNull final IValidatePasswordListener listener) {
+ return validatePassword(context, new Password(password), listener);
+ }
+
+ /**
+ * Validate a user password. This method calls PowerAuth REST API endpoint to validate the password on the server.
+ *
+ * @param context Context.
+ * @param password Password to be verified.
+ * @param listener The callback method with error associated with the password validation.
+ * @return {@link ICancelable} object associated with the running HTTP request.
+ */
+ public @Nullable
+ ICancelable validatePassword(@NonNull Context context, @NonNull String password, @NonNull final IValidatePasswordListener listener) {
+ return validatePassword(context, new Password(password), listener);
+ }
+
+ /**
+ * Validate a user password. This method calls PowerAuth REST API endpoint to validate the password on the server.
+ *
+ * @param context Context.
+ * @param password Password to be verified.
+ * @param listener The callback method with error associated with the password validation.
+ * @return {@link ICancelable} object associated with the running HTTP request.
+ */
+ public @Nullable
+ ICancelable validatePassword(@NonNull Context context, @NonNull Password password, @NonNull final IValidatePasswordListener listener) {
// Prepare authentication object
PowerAuthAuthentication authentication = PowerAuthAuthentication.possessionWithPassword(password);
diff --git a/proj-xcode/PowerAuth2.xcodeproj/project.pbxproj b/proj-xcode/PowerAuth2.xcodeproj/project.pbxproj
index 03ed4660..40ea022b 100644
--- a/proj-xcode/PowerAuth2.xcodeproj/project.pbxproj
+++ b/proj-xcode/PowerAuth2.xcodeproj/project.pbxproj
@@ -376,6 +376,10 @@
BF8CF50B2032EB41002A6B6E /* PowerAuthRestApiError.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B0A23911D6C487C00D24167 /* PowerAuthRestApiError.h */; settings = {ATTRIBUTES = (Public, ); }; };
BF8CF50D2032EB41002A6B6E /* PA2PrivateMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 1B7A3A531D8FCC0D002D1043 /* PA2PrivateMacros.h */; };
BF8D4BF721661AA400AD896C /* PA2RestApiObjects.h in Headers */ = {isa = PBXBuildFile; fileRef = BF8D4BF621661A5C00AD896C /* PA2RestApiObjects.h */; };
+ BF97A72A28A3E594002F3ACE /* PowerAuthCorePasswordHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = BF97A72828A3E594002F3ACE /* PowerAuthCorePasswordHelper.m */; };
+ BF97A72B28A3E594002F3ACE /* PowerAuthCorePasswordHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = BF97A72828A3E594002F3ACE /* PowerAuthCorePasswordHelper.m */; };
+ BF97A72C28A3E594002F3ACE /* PowerAuthCorePasswordHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = BF97A72828A3E594002F3ACE /* PowerAuthCorePasswordHelper.m */; };
+ BF97A72D28A3E594002F3ACE /* PowerAuthCorePasswordHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = BF97A72828A3E594002F3ACE /* PowerAuthCorePasswordHelper.m */; };
BF9C1F7522411BFA00CA5027 /* PA2ConfirmRecoveryCodeResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = BF9C1F7322411BFA00CA5027 /* PA2ConfirmRecoveryCodeResponse.h */; };
BF9C1F7622411BFA00CA5027 /* PA2ConfirmRecoveryCodeResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = BF9C1F7422411BFA00CA5027 /* PA2ConfirmRecoveryCodeResponse.m */; };
BF9D9C412174E3C7004FAE9C /* PA2UpgradeStartV3Response.h in Headers */ = {isa = PBXBuildFile; fileRef = BF9D9C3F2174E3C7004FAE9C /* PA2UpgradeStartV3Response.h */; };
@@ -725,6 +729,8 @@
BF8BFBEB215BBA7B001D6852 /* PowerAuthClientConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PowerAuthClientConfiguration.m; sourceTree = ""; };
BF8CF5122032EB41002A6B6E /* PowerAuth2.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PowerAuth2.framework; sourceTree = BUILT_PRODUCTS_DIR; };
BF8D4BF621661A5C00AD896C /* PA2RestApiObjects.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PA2RestApiObjects.h; sourceTree = ""; };
+ BF97A72828A3E594002F3ACE /* PowerAuthCorePasswordHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PowerAuthCorePasswordHelper.m; sourceTree = ""; };
+ BF97A72928A3E594002F3ACE /* PowerAuthCorePasswordHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PowerAuthCorePasswordHelper.h; sourceTree = ""; };
BF9C1F7322411BFA00CA5027 /* PA2ConfirmRecoveryCodeResponse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PA2ConfirmRecoveryCodeResponse.h; sourceTree = ""; };
BF9C1F7422411BFA00CA5027 /* PA2ConfirmRecoveryCodeResponse.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PA2ConfirmRecoveryCodeResponse.m; sourceTree = ""; };
BF9D9C3F2174E3C7004FAE9C /* PA2UpgradeStartV3Response.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PA2UpgradeStartV3Response.h; sourceTree = ""; };
@@ -935,6 +941,8 @@
children = (
BFD386611F2B528600F74FF9 /* TestConfig */,
BFAF730A1EAA8467005E7572 /* PowerAuthServerSOAP */,
+ BF97A72928A3E594002F3ACE /* PowerAuthCorePasswordHelper.h */,
+ BF97A72828A3E594002F3ACE /* PowerAuthCorePasswordHelper.m */,
BFE341C027F6F035005337B1 /* PowerAuthSdkTestHelper.h */,
BFE341C127F6F035005337B1 /* PowerAuthSdkTestHelper.m */,
BFE341C427F71AC0005337B1 /* PowerAuthSDKDefaultTests.h */,
@@ -1221,8 +1229,8 @@
BF667EC32835A0B2006E97F8 /* PowerAuthActivationTests.m */,
BF667EC52835A0B2006E97F8 /* PowerAuthCustomHeaderRequestInterceptorTests.m */,
BF667EC62835A0B2006E97F8 /* PowerAuthBasicHttpAuthenticationRequestInterceptorTests.m */,
- BF667EC72835A0B2006E97F8 /* Info.plist */,
BF215F01287D8C9300EC3F3E /* PowerAuthAuthenticationTests.m */,
+ BF667EC72835A0B2006E97F8 /* Info.plist */,
);
path = PowerAuth2Tests;
sourceTree = SOURCE_ROOT;
@@ -1927,6 +1935,7 @@
BF369FCA285370DD0004C454 /* PA2GroupedTaskTests.m in Sources */,
BF667ED02835A0B2006E97F8 /* PowerAuthBasicHttpAuthenticationRequestInterceptorTests.m in Sources */,
BF215F02287D8C9300EC3F3E /* PowerAuthAuthenticationTests.m in Sources */,
+ BF97A72A28A3E594002F3ACE /* PowerAuthCorePasswordHelper.m in Sources */,
BF667EC82835A0B2006E97F8 /* PA2WeakArrayTests.m in Sources */,
BF667ECA2835A0B2006E97F8 /* PowerAuthActivationTests.m in Sources */,
BF667ECC2835A0B2006E97F8 /* PA2AsyncOperationTests.m in Sources */,
@@ -2018,6 +2027,7 @@
BF369FCB285370DD0004C454 /* PA2GroupedTaskTests.m in Sources */,
BF667ED12835A0B2006E97F8 /* PowerAuthBasicHttpAuthenticationRequestInterceptorTests.m in Sources */,
BF215F03287D8C9300EC3F3E /* PowerAuthAuthenticationTests.m in Sources */,
+ BF97A72B28A3E594002F3ACE /* PowerAuthCorePasswordHelper.m in Sources */,
BF667EC92835A0B2006E97F8 /* PA2WeakArrayTests.m in Sources */,
BF667ECB2835A0B2006E97F8 /* PowerAuthActivationTests.m in Sources */,
BF667ECD2835A0B2006E97F8 /* PA2AsyncOperationTests.m in Sources */,
@@ -2034,6 +2044,7 @@
BF667E6B28359C7C006E97F8 /* SoapHelper.m in Sources */,
BF667E6C28359C7C006E97F8 /* PowerAuthTestServerConfig.m in Sources */,
BF667E6D28359C7C006E97F8 /* CXMLNode_XPathExtensions.m in Sources */,
+ BF97A72C28A3E594002F3ACE /* PowerAuthCorePasswordHelper.m in Sources */,
BF667E6E28359C7C006E97F8 /* CXMLUnsupportedNode.m in Sources */,
BF667E7028359C7C006E97F8 /* PowerAuthTestServerAPI.m in Sources */,
BF667E7128359C7C006E97F8 /* PowerAuthSdkTestHelper.m in Sources */,
@@ -2066,6 +2077,7 @@
BF667E9A28359C93006E97F8 /* SoapHelper.m in Sources */,
BF667E9B28359C93006E97F8 /* PowerAuthTestServerConfig.m in Sources */,
BF667E9C28359C93006E97F8 /* CXMLNode_XPathExtensions.m in Sources */,
+ BF97A72D28A3E594002F3ACE /* PowerAuthCorePasswordHelper.m in Sources */,
BF667E9D28359C93006E97F8 /* CXMLUnsupportedNode.m in Sources */,
BF667E9E28359C93006E97F8 /* PowerAuthTestServerModel.m in Sources */,
BF667E9F28359C93006E97F8 /* PowerAuthTestServerAPI.m in Sources */,
diff --git a/proj-xcode/PowerAuth2/PowerAuthAuthentication.h b/proj-xcode/PowerAuth2/PowerAuthAuthentication.h
index b8f245c2..eb4d9e9d 100644
--- a/proj-xcode/PowerAuth2/PowerAuthAuthentication.h
+++ b/proj-xcode/PowerAuth2/PowerAuthAuthentication.h
@@ -14,15 +14,14 @@
* limitations under the License.
*/
-// PA2_SHARED_SOURCE PowerAuth2ForWatch .
-// PA2_SHARED_SOURCE PowerAuth2ForExtensions .
-
#import
#if PA2_HAS_LACONTEXT == 1
#import
#endif
+@class PowerAuthCorePassword;
+
/** Class representing a multi-factor authentication object.
*/
@interface PowerAuthAuthentication : NSObject
@@ -39,8 +38,12 @@
/// Password to be used for knowledge factor, or nil of knowledge factor should not be used.
///
-/// Modifying content of usePassword property is deprecated. Please use appropriate static method to create PowerAuthAuthentication instance.
-@property (nonatomic, strong, nullable) NSString *usePassword;
+/// Modifying content of usePassword property is deprecated. Please use appropriate static method to create PowerAuthAuthentication instance
+/// or use new `password` property to test whether authentication has knowledge factor in use.
+@property (nonatomic, strong, nullable) NSString *usePassword PA2_DEPRECATED(1.7.2);
+
+/// Contains password safely stored in PowerAuthCorePassword object in case that knowledge factor is required in authentication.
+@property (nonatomic, readonly, strong, nullable) PowerAuthCorePassword * password;
/// Specifies the text displayed on Touch or Face ID prompt in case biometry is required to obtain data.
///
@@ -68,8 +71,6 @@
/// Modifying content of usePossession property is deprecated. Please use appropriate static method to create PowerAuthAuthentication instance.
- (void) setUsePossession:(BOOL)usePossession PA2_DEPRECATED(1.7.0);
-/// Modifying content of usePassword property is deprecated. Please use appropriate static method to create PowerAuthAuthentication instance.
-- (void) setUsePassword:(nullable NSString*)usePassword PA2_DEPRECATED(1.7.0);
/// Modifying content of useBiometry property is deprecated. Please use appropriate static method to create PowerAuthAuthentication instance.
- (void) setUseBiometry:(BOOL)useBiometry PA2_DEPRECATED(1.7.0);
/// Modifying content of biometryPrompt property is deprecated. Please use appropriate static method to create PowerAuthAuthentication instance.
@@ -83,8 +84,6 @@
@interface PowerAuthAuthentication (EasyAccessors)
-#if PA2_HAS_CORE_MODULE
-
// Commit, Possession + Knowledge
/// Create a new instance of authentication object configured for activation commit with password.
@@ -132,8 +131,6 @@
customPossessionKey:(nullable NSData*)customPossessionKey
NS_SWIFT_NAME(commitWithPasswordAndBiometry(password:customBiometryKey:customPossessionKey:));
-#endif // PA2_HAS_CORE_MODULE
-
// Signing, Possession only
/// Create a new instance of authentication object preconfigured for signing with a possession factor.
@@ -233,3 +230,70 @@
NS_SWIFT_NAME(possession(withPassword:))
PA2_DEPRECATED(1.7.0);
@end
+
+@interface PowerAuthAuthentication (CorePassword)
+
+// Commit, Possession + Knowledge
+
+/// Create a new instance of authentication object configured for activation commit with password.
+///
+/// Function is not available for App extensions and on watchOS.
+///
+/// @param password PowerAuthCorePassword used for the knowledge factor.
+/// @return Instance of authentication object configured for activation commit with password.
++ (nonnull PowerAuthAuthentication*) commitWithCorePassword:(nonnull PowerAuthCorePassword*)password
+ NS_SWIFT_NAME(commitWithPassword(password:));
+
+/// Create a new instance of authentication object configured for activation commit with password and custom possession key.
+///
+/// Function is not available for App extensions and on watchOS.
+///
+/// @param password PowerAuthCorePassword used for the knowledge factor.
+/// @param customPossessionKey Custom key used for possession factor.
+/// @return Instance of authentication object configured for activation commit with password and custom possession key.
++ (nonnull PowerAuthAuthentication*) commitWithCorePassword:(nonnull PowerAuthCorePassword*)password
+ customPossessionKey:(nonnull NSData*)customPossessionKey
+ NS_SWIFT_NAME(commitWithPassword(password:customPossessionKey:));
+
+// Commit, Possession + Knowledge + Biometry
+
+/// Create a new instance of authentication object configured for activation commit with password and with biometry.
+///
+/// Function is not available for App extensions and on watchOS.
+///
+/// @param password PowerAuthCorePassword used for the knowledge factor.
+/// @return Instance of authentication object configured for activation commit with password and biometry.
++ (nonnull PowerAuthAuthentication*) commitWithCorePasswordAndBiometry:(nonnull PowerAuthCorePassword*)password
+ NS_SWIFT_NAME(commitWithPasswordAndBiometry(password:));
+
+/// Create a new instance of authentication object configured for activation commit with password and with biometry.
+/// This variant of function allows you to use custom keys for biometry and possession factors.
+///
+/// Function is not available for App extensions and on watchOS.
+///
+/// @param password PowerAuthCorePassword used for the knowledge factor.
+/// @param customBiometryKey Custom key used for biometry factor.
+/// @param customPossessionKey Custom key used for possession factor.
+/// @return Instance of authentication object configured for activation commit with password and biometry, allowing to usecustom keys for possession and biometry factors.
++ (nonnull PowerAuthAuthentication*) commitWithCorePasswordAndBiometry:(nonnull PowerAuthCorePassword*)password
+ customBiometryKey:(nullable NSData*)customBiometryKey
+ customPossessionKey:(nullable NSData*)customPossessionKey
+ NS_SWIFT_NAME(commitWithPasswordAndBiometry(password:customBiometryKey:customPossessionKey:));
+
+// Signing, Possession + Knowledge
+
+/// Create a new instance of authentication object preconfigured for combination of possesion and knowledge factors.
+/// @param password PowerAuthCorePassword used for the knowledge factor.
+/// @return New instance of authentication object configured for signing with a possession and knowledge factors.
++ (nonnull PowerAuthAuthentication *) possessionWithCorePassword:(nonnull PowerAuthCorePassword*)password
+ NS_SWIFT_NAME(possessionWithPassword(password:));
+
+/// Create a new instance of authentication object preconfigured for combination of possesion and knowledge factors, with using custom possession key.
+/// @param password PowerAuthCorePassword used for the knowledge factor.
+/// @param customPossessionKey Custom key used for possession factor.
+/// @return New instnace of authentication object configured for signing with custom possession key and knowledge factor.
++ (nonnull PowerAuthAuthentication *) possessionWithCorePassword:(nonnull PowerAuthCorePassword*)password
+ customPossessionKey:(nonnull NSData*)customPossessionKey
+ NS_SWIFT_NAME(possessionWithPassword(password:customPossessionKey:));
+
+@end
diff --git a/proj-xcode/PowerAuth2/PowerAuthAuthentication.m b/proj-xcode/PowerAuth2/PowerAuthAuthentication.m
index ad3bcd7f..b0c0e611 100644
--- a/proj-xcode/PowerAuth2/PowerAuthAuthentication.m
+++ b/proj-xcode/PowerAuth2/PowerAuthAuthentication.m
@@ -14,14 +14,13 @@
* limitations under the License.
*/
-// PA2_SHARED_SOURCE PowerAuth2ForWatch .
-// PA2_SHARED_SOURCE PowerAuth2ForExtensions .
-
#import
#import
#import
#import "PowerAuthAuthentication+Private.h"
+@import PowerAuthCore;
+
@implementation PowerAuthAuthentication
{
NSInteger _objectUsage;
@@ -31,7 +30,7 @@ @implementation PowerAuthAuthentication
#define AUTH_FOR_SIGN 2
- (id) initWithObjectUsage:(NSInteger)objectUsage
- password:(NSString*)password
+ password:(PowerAuthCorePassword*)password
biometry:(BOOL)biometry
biometryPrompt:(NSString*)biometryPrompt
biometryContext:(id)biometryContext
@@ -42,7 +41,7 @@ - (id) initWithObjectUsage:(NSInteger)objectUsage
if (self) {
_objectUsage = objectUsage;
_usePossession = YES;
- _usePassword = password;
+ _password = password;
_useBiometry = biometry;
_biometryPrompt = biometryPrompt;
_biometryContext = biometryContext;
@@ -59,7 +58,7 @@ - (id) copyWithZone:(NSZone *)zone
copy->_objectUsage = _objectUsage;
copy->_usePossession = _usePossession;
copy->_useBiometry = _useBiometry;
- copy->_usePassword = _usePassword;
+ copy->_password = _password;
copy->_biometryPrompt = _biometryPrompt;
copy->_overridenPossessionKey = _overridenPossessionKey;
copy->_overridenBiometryKey = _overridenBiometryKey;
@@ -98,7 +97,7 @@ - (NSString*) description
if (_usePossession) {
[factors addObject:@"possession"];
}
- if (_usePassword) {
+ if (_password) {
[factors addObject:@"knowledge"];
}
if (_useBiometry) {
@@ -125,6 +124,28 @@ - (NSString*) description
}
#endif
+// PA2_DEPRECATED(1.7.2) // getter deprecated in 1.7.2
+- (NSString*) usePassword
+{
+ __block NSString * result = nil;
+ if (_password) {
+ // The purpose of 'validatePasswordComplexity' is quite different, but there's no other API to extract
+ // plaintext passphrase from PowerAuthCorePassword. We're keeping this only for a compatibility reasons,
+ // so the implementation will be removed in 1.8.x release.
+ [_password validatePasswordComplexity:^NSInteger(const char * passphrase, NSInteger length) {
+ result = [[NSString alloc] initWithBytes:passphrase length:length encoding:NSUTF8StringEncoding];
+ return 0;
+ }];
+ }
+ return result;
+}
+
+// PA2_DEPRECATED(1.7.0) // setter was already deprecated in 1.7.0
+- (void) setUsePassword:(NSString *)usePassword
+{
+ _password = [PowerAuthCorePassword passwordWithString:usePassword];
+}
+
@end
@@ -132,60 +153,34 @@ @implementation PowerAuthAuthentication (EasyAccessors)
// MARK: - Commit, Possession + Knowledge
-#if PA2_HAS_CORE_MODULE
-
+ (PowerAuthAuthentication*) commitWithPassword:(NSString*)password
{
- return [[PowerAuthAuthentication alloc] initWithObjectUsage:AUTH_FOR_COMMIT
- password:password
- biometry:NO
- biometryPrompt:nil
- biometryContext:nil
- customPossessionKey:nil
- customBiometryKey:nil];
+ return [self commitWithCorePassword:[PowerAuthCorePassword passwordWithString:password]];
}
+ (PowerAuthAuthentication*) commitWithPassword:(NSString*)password
customPossessionKey:(NSData*)customPossessionKey
{
- return [[PowerAuthAuthentication alloc] initWithObjectUsage:AUTH_FOR_COMMIT
- password:password
- biometry:NO
- biometryPrompt:nil
- biometryContext:nil
- customPossessionKey:customPossessionKey
- customBiometryKey:nil];
+ return [self commitWithCorePassword:[PowerAuthCorePassword passwordWithString:password]
+ customPossessionKey:customPossessionKey];
}
// MARK: Commit, Possession + Knowledge + Biometry
+ (PowerAuthAuthentication*) commitWithPasswordAndBiometry:(NSString*)password
{
- return [[PowerAuthAuthentication alloc] initWithObjectUsage:AUTH_FOR_COMMIT
- password:password
- biometry:YES
- biometryPrompt:nil
- biometryContext:nil
- customPossessionKey:nil
- customBiometryKey:nil];
+ return [self commitWithCorePasswordAndBiometry:[PowerAuthCorePassword passwordWithString:password]];
}
+ (PowerAuthAuthentication*) commitWithPasswordAndBiometry:(NSString*)password
customBiometryKey:(NSData*)customBiometryKey
customPossessionKey:(NSData*)customPossessionKey
{
- return [[PowerAuthAuthentication alloc] initWithObjectUsage:AUTH_FOR_COMMIT
- password:password
- biometry:YES
- biometryPrompt:nil
- biometryContext:nil
- customPossessionKey:customPossessionKey
- customBiometryKey:customBiometryKey];
+ return [self commitWithCorePasswordAndBiometry:[PowerAuthCorePassword passwordWithString:password]
+ customBiometryKey:customBiometryKey
+ customPossessionKey:customPossessionKey];
}
-#endif // PA2_HAS_CORE_MODULE
-
-
// MARK: - Signing, Possession only
+ (PowerAuthAuthentication *) possession
@@ -286,7 +281,38 @@ + (PowerAuthAuthentication *) possessionWithBiometryContext:(LAContext*)context
+ (PowerAuthAuthentication *) possessionWithPassword:(NSString *)password
{
- return [[PowerAuthAuthentication alloc] initWithObjectUsage:AUTH_FOR_SIGN
+ return [self possessionWithCorePassword:[PowerAuthCorePassword passwordWithString:password]];
+}
+
++ (PowerAuthAuthentication *) possessionWithPassword:(NSString*)password
+ customPossessionKey:(NSData*)customPossessionKey
+{
+ return [self possessionWithCorePassword:[PowerAuthCorePassword passwordWithString:password]
+ customPossessionKey:customPossessionKey];
+}
+
+#pragma mark - Deprecated
+
+// PA2_DEPRECATED(1.7.0)
++ (PowerAuthAuthentication *) possessionWithBiometryWithPrompt:(NSString *)biometryPrompt
+{
+ return [self possessionWithBiometryPrompt:biometryPrompt];
+}
+// PA2_DEPRECATED(1.7.0)
++ (PowerAuthAuthentication *) possessionWithPasswordDeprecated:(NSString*)password
+{
+ return [self possessionWithPassword:password];
+}
+
+@end
+
+#pragma mark - PowerAuthCorePassword
+
+@implementation PowerAuthAuthentication (CorePassword)
+
++ (PowerAuthAuthentication*) commitWithCorePassword:(PowerAuthCorePassword*)password
+{
+ return [[PowerAuthAuthentication alloc] initWithObjectUsage:AUTH_FOR_COMMIT
password:password
biometry:NO
biometryPrompt:nil
@@ -295,10 +321,10 @@ + (PowerAuthAuthentication *) possessionWithPassword:(NSString *)password
customBiometryKey:nil];
}
-+ (PowerAuthAuthentication *) possessionWithPassword:(NSString*)password
- customPossessionKey:(NSData*)customPossessionKey
++ (PowerAuthAuthentication*) commitWithCorePassword:(PowerAuthCorePassword*)password
+ customPossessionKey:(NSData*)customPossessionKey
{
- return [[PowerAuthAuthentication alloc] initWithObjectUsage:AUTH_FOR_SIGN
+ return [[PowerAuthAuthentication alloc] initWithObjectUsage:AUTH_FOR_COMMIT
password:password
biometry:NO
biometryPrompt:nil
@@ -307,28 +333,65 @@ + (PowerAuthAuthentication *) possessionWithPassword:(NSString*)password
customBiometryKey:nil];
}
-#pragma mark - Deprecated
++ (PowerAuthAuthentication*) commitWithCorePasswordAndBiometry:(PowerAuthCorePassword*)password
+{
+ return [[PowerAuthAuthentication alloc] initWithObjectUsage:AUTH_FOR_COMMIT
+ password:password
+ biometry:YES
+ biometryPrompt:nil
+ biometryContext:nil
+ customPossessionKey:nil
+ customBiometryKey:nil];
+}
-// PA2_DEPRECATED(1.7.0)
-+ (PowerAuthAuthentication *) possessionWithBiometryWithPrompt:(NSString *)biometryPrompt
++ (PowerAuthAuthentication*) commitWithCorePasswordAndBiometry:(PowerAuthCorePassword*)password
+ customBiometryKey:(NSData*)customBiometryKey
+ customPossessionKey:(NSData*)customPossessionKey
{
- return [self possessionWithBiometryPrompt:biometryPrompt];
+ return [[PowerAuthAuthentication alloc] initWithObjectUsage:AUTH_FOR_COMMIT
+ password:password
+ biometry:YES
+ biometryPrompt:nil
+ biometryContext:nil
+ customPossessionKey:customPossessionKey
+ customBiometryKey:customBiometryKey];
}
-// PA2_DEPRECATED(1.7.0)
-+ (PowerAuthAuthentication *) possessionWithPasswordDeprecated:(NSString*)password
+
++ (PowerAuthAuthentication *) possessionWithCorePassword:(PowerAuthCorePassword*)password
{
- return [self possessionWithPassword:password];
+ return [[PowerAuthAuthentication alloc] initWithObjectUsage:AUTH_FOR_SIGN
+ password:password
+ biometry:NO
+ biometryPrompt:nil
+ biometryContext:nil
+ customPossessionKey:nil
+ customBiometryKey:nil];
+}
+
++ (PowerAuthAuthentication *) possessionWithCorePassword:(PowerAuthCorePassword*)password
+ customPossessionKey:(NSData*)customPossessionKey
+{
+ return [[PowerAuthAuthentication alloc] initWithObjectUsage:AUTH_FOR_SIGN
+ password:password
+ biometry:NO
+ biometryPrompt:nil
+ biometryContext:nil
+ customPossessionKey:customPossessionKey
+ customBiometryKey:nil];
}
+
@end
+#pragma mark - Private
+
@implementation PowerAuthAuthentication (Private)
- (NSInteger) signatureFactorMask
{
NSUInteger result = 0;
if (_usePossession) result |= 1;
- if (_usePassword) result |= 2;
+ if (_password) result |= 2;
if (_useBiometry) result |= 4;
return result;
}
diff --git a/proj-xcode/PowerAuth2/PowerAuthSDK.h b/proj-xcode/PowerAuth2/PowerAuthSDK.h
index 85cab1cf..b3d677ac 100644
--- a/proj-xcode/PowerAuth2/PowerAuthSDK.h
+++ b/proj-xcode/PowerAuth2/PowerAuthSDK.h
@@ -33,7 +33,7 @@
#import
// Core classes
-@class PowerAuthCoreSession, PowerAuthCoreEciesEncryptor;
+@class PowerAuthCoreSession, PowerAuthCorePassword, PowerAuthCoreEciesEncryptor;
@interface PowerAuthSDK : NSObject
@@ -77,6 +77,11 @@
*/
@property (nonatomic, strong, nonnull, readonly) id tokenStore;
+/**
+ Constructor with no parameters is not available.
+ */
+- (instancetype _Null_unspecified) init NS_UNAVAILABLE;
+
/**
Creates an instance of SDK and initializes it with given configuration objects.
@@ -215,15 +220,27 @@
/** Commit activation that was created and store related data using default authentication instance setup with provided password.
- Calling this method is equivalent to commitActivationWithAuthentication:error: with authentication object set to use all factors and provided password.
+ Calling this method is equivalent to commitActivationWithAuthentication:error: with authentication object set to use possession and provided password.
@param password Password to be used for the knowledge related authentication factor.
@param error Error reference in case some error occurs.
@exception NSException thrown in case configuration is not present.
*/
- (BOOL) commitActivationWithPassword:(nonnull NSString*)password
- error:(NSError * _Nullable * _Nullable)error;
+ error:(NSError * _Nullable * _Nullable)error
+ NS_SWIFT_NAME(commitActivation(withPassword:));
+/** Commit activation that was created and store related data using default authentication instance setup with provided password.
+
+ Calling this method is equivalent to commitActivationWithAuthentication:error: with authentication object set to use possession and provided password.
+
+ @param password Password to be used for the knowledge related authentication factor.
+ @param error Error reference in case some error occurs.
+ @exception NSException thrown in case configuration is not present.
+ */
+- (BOOL) commitActivationWithCorePassword:(nonnull PowerAuthCorePassword*)password
+ error:(NSError * _Nullable * _Nullable)error
+ NS_SWIFT_NAME(commitActivation(withPassword:));
/**
Read only property contains fingerprint calculated from device's public key or nil if object has no valid activation.
*/
@@ -357,7 +374,23 @@
@exception NSException thrown in case configuration is not present.
*/
- (BOOL) unsafeChangePasswordFrom:(nonnull NSString*)oldPassword
- to:(nonnull NSString*)newPassword;
+ to:(nonnull NSString*)newPassword
+ NS_SWIFT_NAME(unsafeChangePassword(from:to:));
+
+/** Change the password using local re-encryption, do not validate old password by calling any endpoint.
+
+ You are responsible for validating the old password against some server endpoint yourself before using it in this method.
+ If you do not validate the old password to make sure it is correct, calling this method will corrupt the local data, since
+ existing data will be decrypted using invalid PIN code and re-encrypted with a new one.
+
+ @param oldPassword Old password, currently set to store the data.
+ @param newPassword New password, to be set in case authentication with old password passes.
+ @return Returns YES in case password was changed without error, NO otherwise.
+ @exception NSException thrown in case configuration is not present.
+ */
+- (BOOL) unsafeChangeCorePasswordFrom:(nonnull PowerAuthCorePassword*)oldPassword
+ to:(nonnull PowerAuthCorePassword*)newPassword
+ NS_SWIFT_NAME(unsafeChangePassword(from:to:));
/** Change the password, validate old password by calling a PowerAuth Standard RESTful API endpoint '/pa/signature/validate'.
@@ -369,18 +402,95 @@
*/
- (nullable id) changePasswordFrom:(nonnull NSString*)oldPassword
to:(nonnull NSString*)newPassword
- callback:(nonnull void(^)(NSError * _Nullable error))callback;
+ callback:(nonnull void(^)(NSError * _Nullable error))callback
+ NS_SWIFT_NAME(changePassword(from:to:callback:));
+
+/** Change the password, validate old password by calling a PowerAuth Standard RESTful API endpoint '/pa/signature/validate'.
+
+ @param oldPassword Old password, currently set to store the data.
+ @param newPassword New password, to be set in case authentication with old password passes.
+ @param callback The callback method with the password change result.
+ @return PowerAuthOperationTask associated with the running request.
+ @exception NSException thrown in case configuration is not present.
+ */
+- (nullable id) changeCorePasswordFrom:(nonnull PowerAuthCorePassword*)oldPassword
+ to:(nonnull PowerAuthCorePassword*)newPassword
+ callback:(nonnull void(^)(NSError * _Nullable error))callback
+ NS_SWIFT_NAME(changePassword(from:to:callback:));
+
+/** Validate a user password.
+
+ This method calls PowerAuth Standard RESTful API endpoint '/pa/signature/validate' to validate the signature value.
+
+ @param password Password to be verified.
+ @param callback The callback method with error associated with the password validation.
+ @return PowerAuthOperationTask associated with the running request.
+ */
+- (nullable id) validateCorePassword:(nonnull PowerAuthCorePassword*)password
+ callback:(nonnull void(^)(NSError * _Nullable error))callback
+ NS_SWIFT_NAME(validatePassword(password:callback:));
+
+/** Validate a user password.
+
+ This method calls PowerAuth Standard RESTful API endpoint '/pa/signature/validate' to validate the signature value.
+
+ @param password Password to be verified.
+ @param callback The callback method with error associated with the password validation.
+ @return PowerAuthOperationTask associated with the running request.
+ */
+- (nullable id) validatePassword:(nonnull NSString*)password
+ callback:(nonnull void(^)(NSError * _Nullable error))callback
+ NS_SWIFT_NAME(validatePassword(password:callback:));
+
+/** Validate a user password.
+
+ This method calls PowerAuth Standard RESTful API endpoint '/pa/signature/validate' to validate the signature value. The method
+ is deprecated in favor of `validatePassword(password:callback:)` variant.
+
+ @param password Password to be verified.
+ @param callback The callback method with error associated with the password validation.
+ @return PowerAuthOperationTask associated with the running request.
+ */
+- (nullable id) validatePasswordCorrect:(nonnull NSString*)password
+ callback:(nonnull void(^)(NSError * _Nullable error))callback PA2_DEPRECATED(1.7.2);
/** Regenerate a biometry related factor key.
This method calls PowerAuth Standard RESTful API endpoint '/pa/vault/unlock' to obtain the vault encryption key used for original private key decryption.
+ The method is deprecated in favor of `addBiometryFactor(password:callback:)` variant.
+
+ @param password Password used for authentication during vault unlocking call.
+ @param callback The callback method with the biometry key adding operation result.
+ @return PowerAuthOperationTask associated with the running request.
+ */
+- (nullable id) addBiometryFactorWithPassword:(nonnull NSString*)password
+ callback:(nonnull void(^)(NSError * _Nullable error))callback
+ NS_SWIFT_NAME(addBiometryFactor(password:callback:));
+
+/** Regenerate a biometry related factor key.
+
+ This method calls PowerAuth Standard RESTful API endpoint '/pa/vault/unlock' to obtain the vault encryption key used for original private key decryption.
+ The method is deprecated in favor of `addBiometryFactor(password:callback:)` variant.
+
+ @param password Password used for authentication during vault unlocking call.
+ @param callback The callback method with the biometry key adding operation result.
+ @return PowerAuthOperationTask associated with the running request.
+ */
+- (nullable id) addBiometryFactorWithCorePassword:(nonnull PowerAuthCorePassword*)password
+ callback:(nonnull void(^)(NSError * _Nullable error))callback
+ NS_SWIFT_NAME(addBiometryFactor(password:callback:));
+
+/** Regenerate a biometry related factor key.
+
+ This method calls PowerAuth Standard RESTful API endpoint '/pa/vault/unlock' to obtain the vault encryption key used for original private key decryption.
+ The method is deprecated in favor of `addBiometryFactor(password:callback:)` variant.
@param password Password used for authentication during vault unlocking call.
@param callback The callback method with the biometry key adding operation result.
@return PowerAuthOperationTask associated with the running request.
*/
- (nullable id) addBiometryFactor:(nonnull NSString*)password
- callback:(nonnull void(^)(NSError * _Nullable error))callback;
+ callback:(nonnull void(^)(NSError * _Nullable error))callback PA2_DEPRECATED(1.7.2);
/** Checks if a biometry related factor is present.
@@ -460,17 +570,6 @@
data:(nullable NSData*)data
callback:(nonnull void(^)(NSData * _Nullable signature, NSError * _Nullable error))callback;
-/** Validate a user password.
-
- This method calls PowerAuth Standard RESTful API endpoint '/pa/signature/validate' to validate the signature value.
-
- @param password Password to be verified.
- @param callback The callback method with error associated with the password validation.
- @return PowerAuthOperationTask associated with the running request.
- */
-- (nullable id) validatePasswordCorrect:(nonnull NSString*)password
- callback:(nonnull void(^)(NSError * _Nullable error))callback;
-
@end
diff --git a/proj-xcode/PowerAuth2/PowerAuthSDK.m b/proj-xcode/PowerAuth2/PowerAuthSDK.m
index 9bab3e8a..633be369 100644
--- a/proj-xcode/PowerAuth2/PowerAuthSDK.m
+++ b/proj-xcode/PowerAuth2/PowerAuthSDK.m
@@ -318,7 +318,6 @@ - (PowerAuthCoreSignatureUnlockKeys*) signatureKeysForAuthentication:(PowerAuthA
// Generate signature key encryption keys
NSData *possessionKey = nil;
NSData *biometryKey = nil;
- PowerAuthCorePassword *knowledgeKey = nil;
if (authentication.usePossession) {
if (authentication.overridenPossessionKey) {
possessionKey = authentication.overridenPossessionKey;
@@ -344,15 +343,12 @@ - (PowerAuthCoreSignatureUnlockKeys*) signatureKeysForAuthentication:(PowerAuthA
}
}
}
- if (authentication.usePassword) {
- knowledgeKey = [PowerAuthCorePassword passwordWithString:authentication.usePassword];
- }
// Prepare signature unlock keys structure
PowerAuthCoreSignatureUnlockKeys *keys = [[PowerAuthCoreSignatureUnlockKeys alloc] init];
keys.possessionUnlockKey = possessionKey;
keys.biometryUnlockKey = biometryKey;
- keys.userPassword = knowledgeKey;
+ keys.userPassword = authentication.password;
return keys;
}
@@ -362,7 +358,7 @@ - (PowerAuthCoreSignatureFactor) determineSignatureFactorForAuthentication:(Powe
if (authentication.usePossession) {
factor |= PowerAuthCoreSignatureFactor_Possession;
}
- if (authentication.usePassword != nil) {
+ if (authentication.password != nil) {
factor |= PowerAuthCoreSignatureFactor_Knowledge;
}
if (authentication.useBiometry) {
@@ -631,8 +627,15 @@ - (void) cancelAllPendingTasks
- (BOOL) commitActivationWithPassword:(NSString*)password
error:(NSError**)error
{
- PowerAuthAuthentication *authentication = [PowerAuthAuthentication commitWithPassword:password];
- return [self commitActivationWithAuthentication:authentication error:error];
+ return [self commitActivationWithAuthentication:[PowerAuthAuthentication commitWithPassword:password]
+ error:error];
+}
+
+- (BOOL) commitActivationWithCorePassword:(PowerAuthCorePassword *)password
+ error:(NSError **)error
+{
+ return [self commitActivationWithAuthentication:[PowerAuthAuthentication commitWithCorePassword:password]
+ error:error];
}
- (BOOL) commitActivationWithAuthentication:(PowerAuthAuthentication*)authentication
@@ -651,22 +654,18 @@ - (BOOL) commitActivationWithAuthentication:(PowerAuthAuthentication*)authentica
// Prepare key encryption keys
NSData *possessionKey = nil;
NSData *biometryKey = nil;
- PowerAuthCorePassword *knowledgeKey = nil;
if (authentication.usePossession) {
possessionKey = [self deviceRelatedKey];
}
if (authentication.useBiometry) {
biometryKey = [PowerAuthCoreSession generateSignatureUnlockKey];
}
- if (authentication.usePassword) {
- knowledgeKey = [PowerAuthCorePassword passwordWithString:authentication.usePassword];
- }
// Prepare signature unlock keys structure
PowerAuthCoreSignatureUnlockKeys *keys = [[PowerAuthCoreSignatureUnlockKeys alloc] init];
keys.possessionUnlockKey = possessionKey;
keys.biometryUnlockKey = biometryKey;
- keys.userPassword = knowledgeKey;
+ keys.userPassword = authentication.password;
// Complete the activation
BOOL result = [session completeActivation:keys];
@@ -1042,25 +1041,25 @@ - (BOOL) verifyServerSignedData:(nonnull NSData*)data
#pragma mark - Password
-- (BOOL) unsafeChangePasswordFrom:(NSString*)oldPassword
- to:(NSString*)newPassword
+// PowerAuthCorePassword versions
+
+- (BOOL) unsafeChangeCorePasswordFrom:(PowerAuthCorePassword*)oldPassword
+ to:(PowerAuthCorePassword*)newPassword
{
return [_sessionInterface writeBoolTaskWithSession:^BOOL(PowerAuthCoreSession * session) {
- return [session changeUserPassword:[PowerAuthCorePassword passwordWithString:oldPassword]
- newPassword:[PowerAuthCorePassword passwordWithString:newPassword]];
+ return [session changeUserPassword:oldPassword newPassword:newPassword];
}];
}
-- (id) changePasswordFrom:(NSString*)oldPassword
- to:(NSString*)newPassword
- callback:(void(^)(NSError *error))callback
+- (id) changeCorePasswordFrom:(PowerAuthCorePassword*)oldPassword
+ to:(PowerAuthCorePassword*)newPassword
+ callback:(void(^)(NSError *error))callback
{
- return [self validatePasswordCorrect:oldPassword callback:^(NSError * error) {
+ return [self validateCorePassword:oldPassword callback:^(NSError * error) {
if (!error) {
error = [_sessionInterface writeTaskWithSession:^NSError* (PowerAuthCoreSession * session) {
// Let's change the password
- BOOL result = [session changeUserPassword:[PowerAuthCorePassword passwordWithString:oldPassword]
- newPassword:[PowerAuthCorePassword passwordWithString:newPassword]];
+ BOOL result = [session changeUserPassword:oldPassword newPassword:newPassword];
return result ? nil : PA2MakeError(PowerAuthErrorCode_InvalidActivationState, nil);
}];
}
@@ -1069,28 +1068,57 @@ - (BOOL) unsafeChangePasswordFrom:(NSString*)oldPassword
}];
}
-- (id) validatePasswordCorrect:(NSString*)password callback:(void(^)(NSError * error))callback
+- (id) validateCorePassword:(PowerAuthCorePassword*)password callback:(void(^)(NSError * error))callback
{
[self checkForValidSetup];
return [_client postObject:[PA2ValidateSignatureRequest requestWithReason:@"VALIDATE_PASSWORD"]
to:[PA2RestApiEndpoint validateSignature]
- auth:[PowerAuthAuthentication possessionWithPassword:password]
+ auth:[PowerAuthAuthentication possessionWithCorePassword:password]
completion:^(PowerAuthRestApiResponseStatus status, id response, NSError *error) {
callback(error);
}];
}
+// NSString versions
+
+- (BOOL) unsafeChangePasswordFrom:(NSString*)oldPassword
+ to:(NSString*)newPassword
+{
+ return [self unsafeChangeCorePasswordFrom:[PowerAuthCorePassword passwordWithString:oldPassword]
+ to:[PowerAuthCorePassword passwordWithString:newPassword]];
+}
+
+- (id) changePasswordFrom:(NSString*)oldPassword
+ to:(NSString*)newPassword
+ callback:(void(^)(NSError *error))callback
+{
+ return [self changeCorePasswordFrom:[PowerAuthCorePassword passwordWithString:oldPassword]
+ to:[PowerAuthCorePassword passwordWithString:newPassword]
+ callback:callback];
+}
+
+- (id) validatePassword:(NSString*)password callback:(void (^)(NSError *))callback
+{
+ return [self validateCorePassword:[PowerAuthCorePassword passwordWithString:password] callback:callback];
+}
+
+// PA2_DEPRECATED(1.7.2)
+- (id) validatePasswordCorrect:(NSString *)password callback:(void (^)(NSError *))callback
+{
+ return [self validatePassword:password callback:callback];
+}
+
#pragma mark - Biometry
-- (id) addBiometryFactor:(NSString*)password
- callback:(void(^)(NSError *error))callback
+- (id) addBiometryFactorWithCorePassword:(PowerAuthCorePassword*)password
+ callback:(void(^)(NSError *error))callback
{
// Check if biometry can be used
if (![PowerAuthKeychain canUseBiometricAuthentication]) {
callback(PA2MakeError(PowerAuthErrorCode_BiometryNotAvailable, nil));
return nil;
}
- PowerAuthAuthentication * authentication = [PowerAuthAuthentication possessionWithPassword:password];
+ PowerAuthAuthentication * authentication = [PowerAuthAuthentication possessionWithCorePassword:password];
return [self fetchEncryptedVaultUnlockKey:authentication reason:PA2VaultUnlockReason_ADD_BIOMETRY callback:^(NSString *encryptedEncryptionKey, NSError *error) {
if (!error) {
// Let's add the biometry key
@@ -1114,6 +1142,17 @@ - (BOOL) unsafeChangePasswordFrom:(NSString*)oldPassword
}];
}
+- (id) addBiometryFactorWithPassword:(NSString *)password callback:(void (^)(NSError *))callback
+{
+ return [self addBiometryFactorWithCorePassword:[PowerAuthCorePassword passwordWithString:password] callback:callback];
+}
+
+// PA2_DEPRECATED(1.7.2)
+- (id) addBiometryFactor:(NSString *)password callback:(void (^)(NSError * _Nullable))callback
+{
+ return [self addBiometryFactorWithCorePassword:[PowerAuthCorePassword passwordWithString:password] callback:callback];
+}
+
- (BOOL) hasBiometryFactor
{
[self checkForValidSetup];
diff --git a/proj-xcode/PowerAuth2/private/PowerAuthAuthentication+Private.h b/proj-xcode/PowerAuth2/private/PowerAuthAuthentication+Private.h
index 80cf76a4..63bd2b50 100644
--- a/proj-xcode/PowerAuth2/private/PowerAuthAuthentication+Private.h
+++ b/proj-xcode/PowerAuth2/private/PowerAuthAuthentication+Private.h
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-// PA2_SHARED_SOURCE PowerAuth2ForWatch private
-// PA2_SHARED_SOURCE PowerAuth2ForExtensions private
-
#import
@interface PowerAuthAuthentication (Private)
diff --git a/proj-xcode/PowerAuth2ForExtensions/PowerAuthAuthentication.h b/proj-xcode/PowerAuth2ForExtensions/PowerAuthAuthentication.h
index eb65e56e..b5575556 100644
--- a/proj-xcode/PowerAuth2ForExtensions/PowerAuthAuthentication.h
+++ b/proj-xcode/PowerAuth2ForExtensions/PowerAuthAuthentication.h
@@ -15,7 +15,10 @@
*/
// PA2_SHARED_SOURCE PowerAuth2ForWatch .
-// PA2_SHARED_SOURCE PowerAuth2ForExtensions .
+
+// Do not edit this file in PowerAuth2ForWatch project. Use version in
+// PowerAuht2ForExtensions and the shared file will be copied in the next
+// copy-shared-sources.sh run.
#import
@@ -39,8 +42,12 @@
/// Password to be used for knowledge factor, or nil of knowledge factor should not be used.
///
-/// Modifying content of usePassword property is deprecated. Please use appropriate static method to create PowerAuthAuthentication instance.
-@property (nonatomic, strong, nullable) NSString *usePassword;
+/// Modifying content of usePassword property is deprecated. Please use appropriate static method to create PowerAuthAuthentication instance
+/// or use new `password` property to test whether authentication has knowledge factor in use.
+@property (nonatomic, strong, nullable) NSString *usePassword PA2_DEPRECATED(1.7.2);
+
+/// Contains password in case that knowledge factor is required in authentication.
+@property (nonatomic, strong, readonly, nullable) NSString * password;
/// Specifies the text displayed on Touch or Face ID prompt in case biometry is required to obtain data.
///
@@ -83,57 +90,6 @@
@interface PowerAuthAuthentication (EasyAccessors)
-#if PA2_HAS_CORE_MODULE
-
-// Commit, Possession + Knowledge
-
-/// Create a new instance of authentication object configured for activation commit with password.
-///
-/// Function is not available for App extensions and on watchOS.
-///
-/// @param password Password used for the knowledge factor.
-/// @return Instance of authentication object configured for activation commit with password.
-+ (nonnull PowerAuthAuthentication*) commitWithPassword:(nonnull NSString*)password
- NS_SWIFT_NAME(commitWithPassword(password:));
-
-/// Create a new instance of authentication object configured for activation commit with password and custom possession key.
-///
-/// Function is not available for App extensions and on watchOS.
-///
-/// @param password Password used for the knowledge factor.
-/// @param customPossessionKey Custom key used for possession factor.
-/// @return Instance of authentication object configured for activation commit with password and custom possession key.
-+ (nonnull PowerAuthAuthentication*) commitWithPassword:(nonnull NSString*)password
- customPossessionKey:(nonnull NSData*)customPossessionKey
- NS_SWIFT_NAME(commitWithPassword(password:customPossessionKey:));
-
-// Commit, Possession + Knowledge + Biometry
-
-/// Create a new instance of authentication object configured for activation commit with password and with biometry.
-///
-/// Function is not available for App extensions and on watchOS.
-///
-/// @param password Password used for the knowledge factor.
-/// @return Instance of authentication object configured for activation commit with password and biometry.
-+ (nonnull PowerAuthAuthentication*) commitWithPasswordAndBiometry:(nonnull NSString*)password
- NS_SWIFT_NAME(commitWithPasswordAndBiometry(password:));
-
-/// Create a new instance of authentication object configured for activation commit with password and with biometry.
-/// This variant of function allows you to use custom keys for biometry and possession factors.
-///
-/// Function is not available for App extensions and on watchOS.
-///
-/// @param password Password used for the knowledge factor.
-/// @param customBiometryKey Custom key used for biometry factor.
-/// @param customPossessionKey Custom key used for possession factor.
-/// @return Instance of authentication object configured for activation commit with password and biometry, allowing to use custom keys for possession and biometry factors.
-+ (nonnull PowerAuthAuthentication*) commitWithPasswordAndBiometry:(nonnull NSString*)password
- customBiometryKey:(nullable NSData*)customBiometryKey
- customPossessionKey:(nullable NSData*)customPossessionKey
- NS_SWIFT_NAME(commitWithPasswordAndBiometry(password:customBiometryKey:customPossessionKey:));
-
-#endif // PA2_HAS_CORE_MODULE
-
// Signing, Possession only
/// Create a new instance of authentication object preconfigured for signing with a possession factor.
diff --git a/proj-xcode/PowerAuth2ForExtensions/PowerAuthAuthentication.m b/proj-xcode/PowerAuth2ForExtensions/PowerAuthAuthentication.m
index d2810c05..ac8ee34e 100644
--- a/proj-xcode/PowerAuth2ForExtensions/PowerAuthAuthentication.m
+++ b/proj-xcode/PowerAuth2ForExtensions/PowerAuthAuthentication.m
@@ -15,7 +15,10 @@
*/
// PA2_SHARED_SOURCE PowerAuth2ForWatch .
-// PA2_SHARED_SOURCE PowerAuth2ForExtensions .
+
+// Do not edit this file in PowerAuth2ForWatch project. Use version in
+// PowerAuht2ForExtensions and the shared file will be copied in the next
+// copy-shared-sources.sh run.
#import
#import
@@ -42,7 +45,7 @@ - (id) initWithObjectUsage:(NSInteger)objectUsage
if (self) {
_objectUsage = objectUsage;
_usePossession = YES;
- _usePassword = password;
+ _password = password;
_useBiometry = biometry;
_biometryPrompt = biometryPrompt;
_biometryContext = biometryContext;
@@ -59,7 +62,7 @@ - (id) copyWithZone:(NSZone *)zone
copy->_objectUsage = _objectUsage;
copy->_usePossession = _usePossession;
copy->_useBiometry = _useBiometry;
- copy->_usePassword = _usePassword;
+ copy->_password = _password;
copy->_biometryPrompt = _biometryPrompt;
copy->_overridenPossessionKey = _overridenPossessionKey;
copy->_overridenBiometryKey = _overridenBiometryKey;
@@ -98,7 +101,7 @@ - (NSString*) description
if (_usePossession) {
[factors addObject:@"possession"];
}
- if (_usePassword) {
+ if (_password) {
[factors addObject:@"knowledge"];
}
if (_useBiometry) {
@@ -125,66 +128,22 @@ - (NSString*) description
}
#endif
-@end
-
-
-@implementation PowerAuthAuthentication (EasyAccessors)
-
-// MARK: - Commit, Possession + Knowledge
-
-#if PA2_HAS_CORE_MODULE
-
-+ (PowerAuthAuthentication*) commitWithPassword:(NSString*)password
+// PA2_DEPRECATED(1.7.2)
+- (void) setUsePassword:(NSString *)usePassword
{
- return [[PowerAuthAuthentication alloc] initWithObjectUsage:AUTH_FOR_COMMIT
- password:password
- biometry:NO
- biometryPrompt:nil
- biometryContext:nil
- customPossessionKey:nil
- customBiometryKey:nil];
+ _password = usePassword;
}
-+ (PowerAuthAuthentication*) commitWithPassword:(NSString*)password
- customPossessionKey:(NSData*)customPossessionKey
+// PA2_DEPRECATED(1.7.2)
+- (NSString*) usePassword
{
- return [[PowerAuthAuthentication alloc] initWithObjectUsage:AUTH_FOR_COMMIT
- password:password
- biometry:NO
- biometryPrompt:nil
- biometryContext:nil
- customPossessionKey:customPossessionKey
- customBiometryKey:nil];
+ return _password;
}
-// MARK: Commit, Possession + Knowledge + Biometry
-
-+ (PowerAuthAuthentication*) commitWithPasswordAndBiometry:(NSString*)password
-{
- return [[PowerAuthAuthentication alloc] initWithObjectUsage:AUTH_FOR_COMMIT
- password:password
- biometry:YES
- biometryPrompt:nil
- biometryContext:nil
- customPossessionKey:nil
- customBiometryKey:nil];
-}
-
-+ (PowerAuthAuthentication*) commitWithPasswordAndBiometry:(NSString*)password
- customBiometryKey:(NSData*)customBiometryKey
- customPossessionKey:(NSData*)customPossessionKey
-{
- return [[PowerAuthAuthentication alloc] initWithObjectUsage:AUTH_FOR_COMMIT
- password:password
- biometry:YES
- biometryPrompt:nil
- biometryContext:nil
- customPossessionKey:customPossessionKey
- customBiometryKey:customBiometryKey];
-}
+@end
-#endif // PA2_HAS_CORE_MODULE
+@implementation PowerAuthAuthentication (EasyAccessors)
// MARK: - Signing, Possession only
@@ -328,7 +287,7 @@ - (NSInteger) signatureFactorMask
{
NSUInteger result = 0;
if (_usePossession) result |= 1;
- if (_usePassword) result |= 2;
+ if (_password) result |= 2;
if (_useBiometry) result |= 4;
return result;
}
diff --git a/proj-xcode/PowerAuth2ForExtensions/private/PowerAuthAuthentication+Private.h b/proj-xcode/PowerAuth2ForExtensions/private/PowerAuthAuthentication+Private.h
index 049cfa63..b3f7269b 100644
--- a/proj-xcode/PowerAuth2ForExtensions/private/PowerAuthAuthentication+Private.h
+++ b/proj-xcode/PowerAuth2ForExtensions/private/PowerAuthAuthentication+Private.h
@@ -15,7 +15,10 @@
*/
// PA2_SHARED_SOURCE PowerAuth2ForWatch private
-// PA2_SHARED_SOURCE PowerAuth2ForExtensions private
+
+// Do not edit this file in PowerAuth2ForWatch project. Use version in
+// PowerAuht2ForExtensions and the shared file will be copied in the next
+// copy-shared-sources.sh run.
#import
diff --git a/proj-xcode/PowerAuth2ForExtensionsTests/PowerAuthAuthenticationTests.m b/proj-xcode/PowerAuth2ForExtensionsTests/PowerAuthAuthenticationTests.m
index fbf9b158..ef25e094 100644
--- a/proj-xcode/PowerAuth2ForExtensionsTests/PowerAuthAuthenticationTests.m
+++ b/proj-xcode/PowerAuth2ForExtensionsTests/PowerAuthAuthenticationTests.m
@@ -50,7 +50,7 @@ - (void) testSignPossessionOnly
PowerAuthAuthentication * auth = [PowerAuthAuthentication possession];
XCTAssertTrue(auth.usePossession);
XCTAssertFalse(auth.useBiometry);
- XCTAssertNil(auth.usePassword);
+ XCTAssertNil(auth.password);
XCTAssertNil(auth.biometryPrompt);
XCTAssertContextNil(auth.biometryContext);
XCTAssertNil(auth.overridenBiometryKey);
@@ -63,7 +63,7 @@ - (void) testSignPossessionWithPassword
PowerAuthAuthentication * auth = [PowerAuthAuthentication possessionWithPassword:@"1234"];
XCTAssertTrue(auth.usePossession);
XCTAssertFalse(auth.useBiometry);
- XCTAssertEqual(@"1234", auth.usePassword);
+ XCTAssertEqualObjects(@"1234", auth.password);
XCTAssertNil(auth.biometryPrompt);
XCTAssertContextNil(auth.biometryContext);
XCTAssertNil(auth.overridenBiometryKey);
@@ -73,11 +73,11 @@ - (void) testSignPossessionWithPassword
auth = [PowerAuthAuthentication possessionWithPassword:@"4321" customPossessionKey:_customPossessionKey];
XCTAssertTrue(auth.usePossession);
XCTAssertFalse(auth.useBiometry);
- XCTAssertEqual(@"4321", auth.usePassword);
+ XCTAssertEqualObjects(@"4321", auth.password);
XCTAssertNil(auth.biometryPrompt);
XCTAssertContextNil(auth.biometryContext);
XCTAssertNil(auth.overridenBiometryKey);
- XCTAssertEqual(self.customPossessionKey, auth.overridenPossessionKey);
+ XCTAssertEqualObjects(self.customPossessionKey, auth.overridenPossessionKey);
XCTAssertTrue([auth validateUsage:NO]);
}
@@ -86,7 +86,7 @@ - (void) testSignPossessionWithBiometry
PowerAuthAuthentication * auth = [PowerAuthAuthentication possessionWithBiometry];
XCTAssertTrue(auth.usePossession);
XCTAssertTrue(auth.useBiometry);
- XCTAssertNil(auth.usePassword);
+ XCTAssertNil(auth.password);
XCTAssertNil(auth.biometryPrompt);
XCTAssertContextNil(auth.biometryContext);
XCTAssertNil(auth.overridenBiometryKey);
@@ -96,18 +96,18 @@ - (void) testSignPossessionWithBiometry
auth = [PowerAuthAuthentication possessionWithBiometryWithCustomBiometryKey:_customBiometryKey customPossessionKey:_customPossessionKey];
XCTAssertTrue(auth.usePossession);
XCTAssertTrue(auth.useBiometry);
- XCTAssertNil(auth.usePassword);
+ XCTAssertNil(auth.password);
XCTAssertNil(auth.biometryPrompt);
XCTAssertContextNil(auth.biometryContext);
- XCTAssertEqual(self.customBiometryKey, auth.overridenBiometryKey);
- XCTAssertEqual(self.customPossessionKey, auth.overridenPossessionKey);
+ XCTAssertEqualObjects(self.customBiometryKey, auth.overridenBiometryKey);
+ XCTAssertEqualObjects(self.customPossessionKey, auth.overridenPossessionKey);
XCTAssertTrue([auth validateUsage:NO]);
auth = [PowerAuthAuthentication possessionWithBiometryPrompt:_biometryPrompt];
XCTAssertTrue(auth.usePossession);
XCTAssertTrue(auth.useBiometry);
- XCTAssertNil(auth.usePassword);
- XCTAssertEqual(_biometryPrompt, auth.biometryPrompt);
+ XCTAssertNil(auth.password);
+ XCTAssertEqualObjects(_biometryPrompt, auth.biometryPrompt);
XCTAssertContextNil(auth.biometryContext);
XCTAssertNil(auth.overridenBiometryKey);
XCTAssertNil(auth.overridenPossessionKey);
@@ -116,20 +116,20 @@ - (void) testSignPossessionWithBiometry
auth = [PowerAuthAuthentication possessionWithBiometryPrompt:_biometryPrompt customPossessionKey:_customPossessionKey];
XCTAssertTrue(auth.usePossession);
XCTAssertTrue(auth.useBiometry);
- XCTAssertNil(auth.usePassword);
- XCTAssertEqual(_biometryPrompt, auth.biometryPrompt);
+ XCTAssertNil(auth.password);
+ XCTAssertEqualObjects(_biometryPrompt, auth.biometryPrompt);
XCTAssertContextNil(auth.biometryContext);
XCTAssertNil(auth.overridenBiometryKey);
- XCTAssertEqual(self.customPossessionKey, auth.overridenPossessionKey);
+ XCTAssertEqualObjects(self.customPossessionKey, auth.overridenPossessionKey);
XCTAssertTrue([auth validateUsage:NO]);
#if PA2_HAS_LACONTEXT
auth = [PowerAuthAuthentication possessionWithBiometryContext:_biometryContext];
XCTAssertTrue(auth.usePossession);
XCTAssertTrue(auth.useBiometry);
- XCTAssertNil(auth.usePassword);
+ XCTAssertNil(auth.password);
XCTAssertNil(auth.biometryPrompt);
- XCTAssertEqual(self.biometryContext, auth.biometryContext);
+ XCTAssertEqualObjects(self.biometryContext, auth.biometryContext);
XCTAssertNil(auth.overridenBiometryKey);
XCTAssertNil(auth.overridenPossessionKey);
XCTAssertTrue([auth validateUsage:NO]);
@@ -137,11 +137,11 @@ - (void) testSignPossessionWithBiometry
auth = [PowerAuthAuthentication possessionWithBiometryContext:_biometryContext customPossessionKey:_customPossessionKey];
XCTAssertTrue(auth.usePossession);
XCTAssertTrue(auth.useBiometry);
- XCTAssertNil(auth.usePassword);
+ XCTAssertNil(auth.password);
XCTAssertNil(auth.biometryPrompt);
- XCTAssertEqual(self.biometryContext, auth.biometryContext);
+ XCTAssertEqualObjects(self.biometryContext, auth.biometryContext);
XCTAssertNil(auth.overridenBiometryKey);
- XCTAssertEqual(self.customPossessionKey, auth.overridenPossessionKey);
+ XCTAssertEqualObjects(self.customPossessionKey, auth.overridenPossessionKey);
XCTAssertTrue([auth validateUsage:NO]);
#endif // PA2_HAS_LACONTEXT
}
diff --git a/proj-xcode/PowerAuth2ForWatch/PowerAuthAuthentication.h b/proj-xcode/PowerAuth2ForWatch/PowerAuthAuthentication.h
index ada81f5e..693a2865 100644
--- a/proj-xcode/PowerAuth2ForWatch/PowerAuthAuthentication.h
+++ b/proj-xcode/PowerAuth2ForWatch/PowerAuthAuthentication.h
@@ -15,7 +15,10 @@
*/
// PA2_SHARED_SOURCE PowerAuth2ForWatch .
-// PA2_SHARED_SOURCE PowerAuth2ForExtensions .
+
+// Do not edit this file in PowerAuth2ForWatch project. Use version in
+// PowerAuht2ForExtensions and the shared file will be copied in the next
+// copy-shared-sources.sh run.
#import
@@ -39,8 +42,12 @@
/// Password to be used for knowledge factor, or nil of knowledge factor should not be used.
///
-/// Modifying content of usePassword property is deprecated. Please use appropriate static method to create PowerAuthAuthentication instance.
-@property (nonatomic, strong, nullable) NSString *usePassword;
+/// Modifying content of usePassword property is deprecated. Please use appropriate static method to create PowerAuthAuthentication instance
+/// or use new `password` property to test whether authentication has knowledge factor in use.
+@property (nonatomic, strong, nullable) NSString *usePassword PA2_DEPRECATED(1.7.2);
+
+/// Contains password in case that knowledge factor is required in authentication.
+@property (nonatomic, strong, readonly, nullable) NSString * password;
/// Specifies the text displayed on Touch or Face ID prompt in case biometry is required to obtain data.
///
@@ -83,57 +90,6 @@
@interface PowerAuthAuthentication (EasyAccessors)
-#if PA2_HAS_CORE_MODULE
-
-// Commit, Possession + Knowledge
-
-/// Create a new instance of authentication object configured for activation commit with password.
-///
-/// Function is not available for App extensions and on watchOS.
-///
-/// @param password Password used for the knowledge factor.
-/// @return Instance of authentication object configured for activation commit with password.
-+ (nonnull PowerAuthAuthentication*) commitWithPassword:(nonnull NSString*)password
- NS_SWIFT_NAME(commitWithPassword(password:));
-
-/// Create a new instance of authentication object configured for activation commit with password and custom possession key.
-///
-/// Function is not available for App extensions and on watchOS.
-///
-/// @param password Password used for the knowledge factor.
-/// @param customPossessionKey Custom key used for possession factor.
-/// @return Instance of authentication object configured for activation commit with password and custom possession key.
-+ (nonnull PowerAuthAuthentication*) commitWithPassword:(nonnull NSString*)password
- customPossessionKey:(nonnull NSData*)customPossessionKey
- NS_SWIFT_NAME(commitWithPassword(password:customPossessionKey:));
-
-// Commit, Possession + Knowledge + Biometry
-
-/// Create a new instance of authentication object configured for activation commit with password and with biometry.
-///
-/// Function is not available for App extensions and on watchOS.
-///
-/// @param password Password used for the knowledge factor.
-/// @return Instance of authentication object configured for activation commit with password and biometry.
-+ (nonnull PowerAuthAuthentication*) commitWithPasswordAndBiometry:(nonnull NSString*)password
- NS_SWIFT_NAME(commitWithPasswordAndBiometry(password:));
-
-/// Create a new instance of authentication object configured for activation commit with password and with biometry.
-/// This variant of function allows you to use custom keys for biometry and possession factors.
-///
-/// Function is not available for App extensions and on watchOS.
-///
-/// @param password Password used for the knowledge factor.
-/// @param customBiometryKey Custom key used for biometry factor.
-/// @param customPossessionKey Custom key used for possession factor.
-/// @return Instance of authentication object configured for activation commit with password and biometry, allowing to use custom keys for possession and biometry factors.
-+ (nonnull PowerAuthAuthentication*) commitWithPasswordAndBiometry:(nonnull NSString*)password
- customBiometryKey:(nullable NSData*)customBiometryKey
- customPossessionKey:(nullable NSData*)customPossessionKey
- NS_SWIFT_NAME(commitWithPasswordAndBiometry(password:customBiometryKey:customPossessionKey:));
-
-#endif // PA2_HAS_CORE_MODULE
-
// Signing, Possession only
/// Create a new instance of authentication object preconfigured for signing with a possession factor.
diff --git a/proj-xcode/PowerAuth2ForWatch/PowerAuthAuthentication.m b/proj-xcode/PowerAuth2ForWatch/PowerAuthAuthentication.m
index c1bb3ae4..f765e439 100644
--- a/proj-xcode/PowerAuth2ForWatch/PowerAuthAuthentication.m
+++ b/proj-xcode/PowerAuth2ForWatch/PowerAuthAuthentication.m
@@ -15,7 +15,10 @@
*/
// PA2_SHARED_SOURCE PowerAuth2ForWatch .
-// PA2_SHARED_SOURCE PowerAuth2ForExtensions .
+
+// Do not edit this file in PowerAuth2ForWatch project. Use version in
+// PowerAuht2ForExtensions and the shared file will be copied in the next
+// copy-shared-sources.sh run.
#import
#import
@@ -42,7 +45,7 @@ - (id) initWithObjectUsage:(NSInteger)objectUsage
if (self) {
_objectUsage = objectUsage;
_usePossession = YES;
- _usePassword = password;
+ _password = password;
_useBiometry = biometry;
_biometryPrompt = biometryPrompt;
_biometryContext = biometryContext;
@@ -59,7 +62,7 @@ - (id) copyWithZone:(NSZone *)zone
copy->_objectUsage = _objectUsage;
copy->_usePossession = _usePossession;
copy->_useBiometry = _useBiometry;
- copy->_usePassword = _usePassword;
+ copy->_password = _password;
copy->_biometryPrompt = _biometryPrompt;
copy->_overridenPossessionKey = _overridenPossessionKey;
copy->_overridenBiometryKey = _overridenBiometryKey;
@@ -98,7 +101,7 @@ - (NSString*) description
if (_usePossession) {
[factors addObject:@"possession"];
}
- if (_usePassword) {
+ if (_password) {
[factors addObject:@"knowledge"];
}
if (_useBiometry) {
@@ -125,66 +128,22 @@ - (NSString*) description
}
#endif
-@end
-
-
-@implementation PowerAuthAuthentication (EasyAccessors)
-
-// MARK: - Commit, Possession + Knowledge
-
-#if PA2_HAS_CORE_MODULE
-
-+ (PowerAuthAuthentication*) commitWithPassword:(NSString*)password
+// PA2_DEPRECATED(1.7.2)
+- (void) setUsePassword:(NSString *)usePassword
{
- return [[PowerAuthAuthentication alloc] initWithObjectUsage:AUTH_FOR_COMMIT
- password:password
- biometry:NO
- biometryPrompt:nil
- biometryContext:nil
- customPossessionKey:nil
- customBiometryKey:nil];
+ _password = usePassword;
}
-+ (PowerAuthAuthentication*) commitWithPassword:(NSString*)password
- customPossessionKey:(NSData*)customPossessionKey
+// PA2_DEPRECATED(1.7.2)
+- (NSString*) usePassword
{
- return [[PowerAuthAuthentication alloc] initWithObjectUsage:AUTH_FOR_COMMIT
- password:password
- biometry:NO
- biometryPrompt:nil
- biometryContext:nil
- customPossessionKey:customPossessionKey
- customBiometryKey:nil];
+ return _password;
}
-// MARK: Commit, Possession + Knowledge + Biometry
-
-+ (PowerAuthAuthentication*) commitWithPasswordAndBiometry:(NSString*)password
-{
- return [[PowerAuthAuthentication alloc] initWithObjectUsage:AUTH_FOR_COMMIT
- password:password
- biometry:YES
- biometryPrompt:nil
- biometryContext:nil
- customPossessionKey:nil
- customBiometryKey:nil];
-}
-
-+ (PowerAuthAuthentication*) commitWithPasswordAndBiometry:(NSString*)password
- customBiometryKey:(NSData*)customBiometryKey
- customPossessionKey:(NSData*)customPossessionKey
-{
- return [[PowerAuthAuthentication alloc] initWithObjectUsage:AUTH_FOR_COMMIT
- password:password
- biometry:YES
- biometryPrompt:nil
- biometryContext:nil
- customPossessionKey:customPossessionKey
- customBiometryKey:customBiometryKey];
-}
+@end
-#endif // PA2_HAS_CORE_MODULE
+@implementation PowerAuthAuthentication (EasyAccessors)
// MARK: - Signing, Possession only
@@ -328,7 +287,7 @@ - (NSInteger) signatureFactorMask
{
NSUInteger result = 0;
if (_usePossession) result |= 1;
- if (_usePassword) result |= 2;
+ if (_password) result |= 2;
if (_useBiometry) result |= 4;
return result;
}
diff --git a/proj-xcode/PowerAuth2ForWatch/private/PowerAuthAuthentication+Private.h b/proj-xcode/PowerAuth2ForWatch/private/PowerAuthAuthentication+Private.h
index 6d9b09b9..79e27756 100644
--- a/proj-xcode/PowerAuth2ForWatch/private/PowerAuthAuthentication+Private.h
+++ b/proj-xcode/PowerAuth2ForWatch/private/PowerAuthAuthentication+Private.h
@@ -15,7 +15,10 @@
*/
// PA2_SHARED_SOURCE PowerAuth2ForWatch private
-// PA2_SHARED_SOURCE PowerAuth2ForExtensions private
+
+// Do not edit this file in PowerAuth2ForWatch project. Use version in
+// PowerAuht2ForExtensions and the shared file will be copied in the next
+// copy-shared-sources.sh run.
#import
diff --git a/proj-xcode/PowerAuth2IntegrationTests/PowerAuthCorePasswordHelper.h b/proj-xcode/PowerAuth2IntegrationTests/PowerAuthCorePasswordHelper.h
new file mode 100644
index 00000000..e2f02fe0
--- /dev/null
+++ b/proj-xcode/PowerAuth2IntegrationTests/PowerAuthCorePasswordHelper.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#import
+
+@interface PowerAuthCorePassword (PowerAuthCorePasswordHelper)
+
+@property (nonatomic, strong, readonly, nullable) NSString * extractedPassword;
+
+@end
diff --git a/proj-xcode/PowerAuth2IntegrationTests/PowerAuthCorePasswordHelper.m b/proj-xcode/PowerAuth2IntegrationTests/PowerAuthCorePasswordHelper.m
new file mode 100644
index 00000000..4960ede2
--- /dev/null
+++ b/proj-xcode/PowerAuth2IntegrationTests/PowerAuthCorePasswordHelper.m
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2022 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.
+ */
+
+#import "PowerAuthCorePasswordHelper.h"
+@import PowerAuthCore;
+
+@implementation PowerAuthCorePassword (PowerAuthCorePasswordHelper)
+
+- (NSString*) extractedPassword
+{
+ __block NSString * password = nil;
+ [self validatePasswordComplexity:^NSInteger(const char * passphrase, NSInteger length) {
+ password = [[NSString alloc] initWithBytes:passphrase length:length encoding:NSUTF8StringEncoding];
+ return 0;
+ }];
+ return password;
+}
+
+@end
diff --git a/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSDKDefaultTests.m b/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSDKDefaultTests.m
index bee24510..03adff7e 100644
--- a/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSDKDefaultTests.m
+++ b/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSDKDefaultTests.m
@@ -64,6 +64,15 @@ - (void) prepareConfigs:(PowerAuthConfiguration*)configuration
return obj; \
}
+/**
+ Checks whether biometry is available for testing.
+ */
+#define CHECK_BIOMETRY() \
+ if (![PowerAuthKeychain canUseBiometricAuthentication]) { \
+ XCTFail(@"Biometric authentication is not available on this simulator. Please go to Device Simulator and make sure that `Features -> Face/Touch ID -> Enrolled` is ON"); \
+ return; \
+ }
+
#pragma mark - Integration tests
@@ -102,6 +111,25 @@ - (void) testCreateActivationWithOtpAndSignature
XCTAssertTrue(activation.success);
}
+- (void) testCreateActivationAndCommitWithPassword
+{
+ CHECK_TEST_CONFIG();
+
+ PowerAuthSdkActivation * activation = [_helper createActivationWithFlags:TestActivationFlags_RemoveAfter
+ activationOtp:nil];
+ XCTAssertTrue(activation.success);
+}
+
+- (void) testCreateActivationAndCommitWithCorePassword
+{
+ CHECK_TEST_CONFIG();
+
+ PowerAuthSdkActivation * activation = [_helper createActivationWithFlags:TestActivationFlags_CommitWithCorePassword | TestActivationFlags_RemoveAfter
+ activationOtp:nil];
+ XCTAssertTrue(activation.success);
+}
+
+
- (void) testRemoveActivation
{
CHECK_TEST_CONFIG();
@@ -139,7 +167,7 @@ - (void) testPasswordCorrect
XCTAssertFalse(result); // if YES then something is VERY wrong. The wrong password passed the test.
// 2) Now use a valid password
- result = [_helper checkForPassword:auth.usePassword];
+ result = [_helper checkForCorePassword:auth.password];
XCTAssertTrue(result); // if NO then a valid password did not pass the test.
}
@@ -155,11 +183,11 @@ - (void) testChangePassword
}
PowerAuthAuthentication * auth = activation.credentials;
- NSString * newPassword = @"nbusr321";
+ PowerAuthCorePassword * newPassword = [PowerAuthCorePassword passwordWithString:@"nbusr321"];
// 1) At first, change password
result = [[AsyncHelper synchronizeAsynchronousBlock:^(AsyncHelper *waiting) {
- id task = [_sdk changePasswordFrom:auth.usePassword to:newPassword callback:^(NSError * _Nullable error) {
+ id task = [_sdk changeCorePasswordFrom:auth.password to:newPassword callback:^(NSError * _Nullable error) {
[waiting reportCompletion:@(error == nil)];
}];
// Returned task should not be cancelled
@@ -168,7 +196,25 @@ - (void) testChangePassword
XCTAssertTrue(result);
// 2) Now validate that new password
- result = [_helper checkForPassword:newPassword];
+ result = [_helper checkForCorePassword:newPassword];
+ XCTAssertTrue(result);
+
+ // Now use string version instead of core password
+
+ NSString * oldStringPassword = newPassword.extractedPassword;
+ NSString * newStringPassword = auth.password.extractedPassword;
+ // 1) At first, change password
+ result = [[AsyncHelper synchronizeAsynchronousBlock:^(AsyncHelper *waiting) {
+ id task = [_sdk changePasswordFrom:oldStringPassword to:newStringPassword callback:^(NSError * _Nullable error) {
+ [waiting reportCompletion:@(error == nil)];
+ }];
+ // Returned task should not be cancelled
+ XCTAssertNotNil(task);
+ }] boolValue];
+ XCTAssertTrue(result);
+
+ // 2) Now validate that new password
+ result = [_helper checkForPassword:newStringPassword];
XCTAssertTrue(result);
}
@@ -222,7 +268,7 @@ - (void) testValidateSignature
// Do more valid signatures. Count is important, due to fact that we have 8-bit local counter sice V3.1
for (int i = 1; i < 264; i++) {
result = [[AsyncHelper synchronizeAsynchronousBlock:^(AsyncHelper *waiting) {
- id task = [_sdk validatePasswordCorrect:auth.usePassword callback:^(NSError * error) {
+ id task = [_sdk validateCorePassword:auth.password callback:^(NSError * error) {
[waiting reportCompletion:@(error == nil)];
}];
XCTAssertNotNil(task);
@@ -452,7 +498,7 @@ - (void) testActivationStatusFailCounters
PowerAuthAuthentication * just_possession = _helper.authPossession;
// Correct AUTH with knowledge
- result = [_helper checkForPassword:auth.usePassword];
+ result = [_helper checkForCorePassword:auth.password];
XCTAssertTrue(result);
PowerAuthActivationStatus * status_after_correct = [_helper fetchActivationStatus];
@@ -478,7 +524,7 @@ - (void) testActivationStatusFailCounters
// Now try valid password
// Fail attempt
- result = [_helper checkForPassword:auth.usePassword];
+ result = [_helper checkForCorePassword:auth.password];
XCTAssertTrue(result);
status_after_correct = [_helper fetchActivationStatus];
XCTAssertNotNil(status_after_correct);
@@ -501,7 +547,7 @@ - (void) testActivationStatusMaxFailAttempts
PowerAuthAuthentication * auth = activation.credentials;
// Correct AUTH with knowledge
- result = [_helper checkForPassword:auth.usePassword];
+ result = [_helper checkForCorePassword:auth.password];
XCTAssertTrue(result);
PowerAuthActivationStatus * status_after_correct = [_helper fetchActivationStatus];
PowerAuthActivationStatus * after = status_after_correct;
@@ -611,7 +657,7 @@ - (void) testCounterSync_ServerIsAhead
status = [_helper fetchActivationStatus];
XCTAssertNotNil(status);
XCTAssertEqual(status.state, PowerAuthActivationState_Active);
- BOOL password_result = [_helper checkForPassword:auth.usePassword];
+ BOOL password_result = [_helper checkForCorePassword:auth.password];
XCTAssertTrue(password_result);
// Negative
@@ -833,7 +879,7 @@ - (void) testPasswordCorrectWhenBlocked
// 2) At first, use invalid password
result = [[AsyncHelper synchronizeAsynchronousBlock:^(AsyncHelper *waiting) {
- id task = [_sdk validatePasswordCorrect:@"MustBeWrong" callback:^(NSError * error) {
+ id task = [_sdk validatePassword:@"MustBeWrong" callback:^(NSError * error) {
[waiting reportCompletion:@(error == nil)];
}];
XCTAssertNotNil(task);
@@ -842,7 +888,7 @@ - (void) testPasswordCorrectWhenBlocked
// 3) Now use a valid password
result = [[AsyncHelper synchronizeAsynchronousBlock:^(AsyncHelper *waiting) {
- id task = [_sdk validatePasswordCorrect:auth.usePassword callback:^(NSError * error) {
+ id task = [_sdk validateCorePassword:auth.password callback:^(NSError * error) {
[waiting reportCompletion:@(error == nil)];
}];
XCTAssertNotNil(task);
@@ -855,7 +901,7 @@ - (void) testPasswordCorrectWhenBlocked
// 5) Test password
result = [[AsyncHelper synchronizeAsynchronousBlock:^(AsyncHelper *waiting) {
- id task = [_sdk validatePasswordCorrect:auth.usePassword callback:^(NSError * error) {
+ id task = [_sdk validateCorePassword:auth.password callback:^(NSError * error) {
[waiting reportCompletion:@(error == nil)];
}];
XCTAssertNotNil(task);
@@ -889,6 +935,8 @@ - (void) testWrongAPIUsage
}
}
+// MARK: - EEK
+
- (void) testExternalEncryptionKey
{
CHECK_TEST_CONFIG();
@@ -903,7 +951,7 @@ - (void) testExternalEncryptionKey
}
XCTAssertFalse(_sdk.hasExternalEncryptionKey);
- XCTAssertTrue([_helper checkForPassword:activation.credentials.usePassword]);
+ XCTAssertTrue([_helper checkForCorePassword:activation.credentials.password]);
NSData * eek = [PowerAuthCoreSession generateSignatureUnlockKey];
@@ -913,14 +961,14 @@ - (void) testExternalEncryptionKey
XCTAssertNil(error);
XCTAssertTrue(_sdk.hasExternalEncryptionKey);
- XCTAssertTrue([_helper checkForPassword:activation.credentials.usePassword]);
+ XCTAssertTrue([_helper checkForCorePassword:activation.credentials.password]);
result = [_sdk removeExternalEncryptionKey:&error];
XCTAssertTrue(result);
XCTAssertNil(error);
XCTAssertFalse(_sdk.hasExternalEncryptionKey);
- XCTAssertTrue([_helper checkForPassword:activation.credentials.usePassword]);
+ XCTAssertTrue([_helper checkForCorePassword:activation.credentials.password]);
}
- (void) testEEKFromConfiguration
@@ -942,7 +990,7 @@ - (void) testEEKFromConfiguration
return;
}
- XCTAssertTrue([_helper checkForPassword:activation.credentials.usePassword]);
+ XCTAssertTrue([_helper checkForCorePassword:activation.credentials.password]);
NSError * error = nil;
BOOL result = [_sdk removeExternalEncryptionKey:&error];
@@ -950,7 +998,7 @@ - (void) testEEKFromConfiguration
XCTAssertNil(error);
XCTAssertFalse(_sdk.hasExternalEncryptionKey);
- XCTAssertTrue([_helper checkForPassword:activation.credentials.usePassword]);
+ XCTAssertTrue([_helper checkForCorePassword:activation.credentials.password]);
}
- (void) testSetEEKBeforeActivation
@@ -973,14 +1021,14 @@ - (void) testSetEEKBeforeActivation
return;
}
- XCTAssertTrue([_helper checkForPassword:activation.credentials.usePassword]);
+ XCTAssertTrue([_helper checkForCorePassword:activation.credentials.password]);
result = [_sdk removeExternalEncryptionKey:&error];
XCTAssertTrue(result);
XCTAssertNil(error);
XCTAssertFalse(_sdk.hasExternalEncryptionKey);
- XCTAssertTrue([_helper checkForPassword:activation.credentials.usePassword]);
+ XCTAssertTrue([_helper checkForCorePassword:activation.credentials.password]);
}
- (void) testSetEEKAfterActivation
@@ -997,7 +1045,7 @@ - (void) testSetEEKAfterActivation
}
XCTAssertFalse(_sdk.hasExternalEncryptionKey);
- XCTAssertTrue([_helper checkForPassword:activation.credentials.usePassword]);
+ XCTAssertTrue([_helper checkForCorePassword:activation.credentials.password]);
NSData * eek = [PowerAuthCoreSession generateSignatureUnlockKey];
@@ -1007,7 +1055,7 @@ - (void) testSetEEKAfterActivation
XCTAssertNil(error);
XCTAssertTrue(_sdk.hasExternalEncryptionKey);
- XCTAssertTrue([_helper checkForPassword:activation.credentials.usePassword]);
+ XCTAssertTrue([_helper checkForCorePassword:activation.credentials.password]);
// Now re-instantiate SDK and try to set EEK manually
PowerAuthConfiguration * newConfig = [_sdk.configuration copy];
@@ -1023,9 +1071,11 @@ - (void) testSetEEKAfterActivation
XCTAssertNil(error);
XCTAssertTrue(_sdk.hasExternalEncryptionKey);
- XCTAssertTrue([_helper checkForPassword:activation.credentials.usePassword]);
+ XCTAssertTrue([_helper checkForCorePassword:activation.credentials.password]);
}
+// MARK: - Request synchronization
+
- (void) testCancelEnqueuedHttpOperation
{
CHECK_TEST_CONFIG();
@@ -1040,15 +1090,78 @@ - (void) testCancelEnqueuedHttpOperation
return;
}
[AsyncHelper synchronizeAsynchronousBlock:^(AsyncHelper *waiting) {
- [_sdk validatePasswordCorrect:activation.credentials.usePassword callback:^(NSError * _Nullable error) {
+ [_sdk validateCorePassword:activation.credentials.password callback:^(NSError * _Nullable error) {
XCTAssertNil(error);
[waiting reportCompletion:nil];
}];
- id task = [_sdk validatePasswordCorrect:activation.credentials.usePassword callback:^(NSError * _Nullable error) {
+ id task = [_sdk validateCorePassword:activation.credentials.password callback:^(NSError * _Nullable error) {
XCTFail();
}];
[task cancel];
}];
}
+#if defined(PA2_BIOMETRY_SUPPORT)
+
+// MARK: - Biometry
+
+- (void) testCreateActivationWithBiometry
+{
+ CHECK_TEST_CONFIG();
+
+ //
+ // This test validates whether it's possible to create activation with biometry factor set.
+ //
+
+ CHECK_BIOMETRY();
+
+ PowerAuthSdkActivation * activation = [_helper createActivationWithFlags:TestActivationFlags_CommitWithBiometry activationOtp:nil];
+ if (!activation) {
+ return;
+ }
+ XCTAssertTrue([_sdk hasBiometryFactor]);
+}
+
+- (void) testAddingBiometryFactor
+{
+ CHECK_TEST_CONFIG();
+
+ //
+ // This test validates whether it's possible to add biometry factor later.
+ //
+
+ CHECK_BIOMETRY();
+
+ PowerAuthSdkActivation * activation = [_helper createActivation:YES];
+ if (!activation) {
+ return;
+ }
+
+ XCTAssertFalse([_sdk hasBiometryFactor]);
+
+ [AsyncHelper synchronizeAsynchronousBlock:^(AsyncHelper *waiting) {
+ [_sdk addBiometryFactorWithCorePassword:activation.credentials.password callback:^(NSError * _Nullable error) {
+ XCTAssertNil(error);
+ [waiting reportCompletion:nil];
+ }];
+ }];
+
+ XCTAssertTrue([_sdk hasBiometryFactor]);
+
+ XCTAssertTrue([_sdk removeBiometryFactor]);
+
+ XCTAssertFalse([_sdk hasBiometryFactor]);
+
+ [AsyncHelper synchronizeAsynchronousBlock:^(AsyncHelper *waiting) {
+ [_sdk addBiometryFactorWithPassword:activation.credentials.password.extractedPassword callback:^(NSError * _Nullable error) {
+ XCTAssertNil(error);
+ [waiting reportCompletion:nil];
+ }];
+ }];
+
+ XCTAssertTrue([_sdk hasBiometryFactor]);
+}
+
+#endif // PA2_BIOMETRY_SUPPORT
+
@end
diff --git a/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSDKProtocolUpgradeTests.m b/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSDKProtocolUpgradeTests.m
index 3b337d4e..32a7d995 100644
--- a/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSDKProtocolUpgradeTests.m
+++ b/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSDKProtocolUpgradeTests.m
@@ -132,7 +132,7 @@ - (void)setUp
- (NSError*) checkForPassword:(NSString*)password
{
NSError * result = [AsyncHelper synchronizeAsynchronousBlock:^(AsyncHelper *waiting) {
- PA2TestsOperationTask task = [_sdk validatePasswordCorrect:password callback:^(NSError * error) {
+ PA2TestsOperationTask task = [_sdk validatePassword:password callback:^(NSError * error) {
[waiting reportCompletion:error];
}];
XCTAssertNotNil(task);
@@ -176,7 +176,7 @@ - (void) createOldActivation
NSString * activationStateData = [[_helper sessionCoreSerializedState] base64EncodedStringWithOptions:0];
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
- [defaults setObject:auth.usePassword forKey:s_PossessionFactorKey];
+ [defaults setObject:auth.password.extractedPassword forKey:s_PossessionFactorKey];
[defaults setObject:activationData.activationId forKey:s_ActivationIdKey];
[defaults setObject:activationStateData forKey:s_StateDataKey];
[defaults synchronize];
@@ -186,7 +186,7 @@ - (void) createOldActivation
NSLog(@"=======================================================================");
NSLog(@"Upgrade params (for old SDK step):");
- NSLog(@" - password %@", auth.usePassword);
+ NSLog(@" - password %@", auth.password.extractedPassword);
NSLog(@" - act-id %@", activationData.activationId);
NSLog(@"=======================================================================");
}
diff --git a/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSDKSharedTests.m b/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSDKSharedTests.m
index c85b0afd..2a6b2a8b 100644
--- a/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSDKSharedTests.m
+++ b/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSDKSharedTests.m
@@ -147,14 +147,14 @@ - (void) testConcurrentSignatureCalculations
}];
} else {
completionCount += 2;
- [self.sdk validatePasswordCorrect:credentials.usePassword callback:^(NSError * error) {
+ [self.sdk validateCorePassword:credentials.password callback:^(NSError * error) {
XCTAssertNil(error);
[waiting extendWaitingTime];
if (!--completionCount) {
[waiting reportCompletion:nil];
}
}];
- id paTask = [self.sdk validatePasswordCorrect:credentials.usePassword callback:^(NSError * error) {
+ id paTask = [self.sdk validateCorePassword:credentials.password callback:^(NSError * error) {
XCTAssertNil(error);
[_waitForQueuesTask extendWaitingTime];
if (!--completionCount) {
@@ -188,14 +188,14 @@ - (void) testConcurrentSignatureCalculations
}];
} else {
completionCount += 2;
- [self.sdk validatePasswordCorrect:credentials.usePassword callback:^(NSError * error) {
+ [self.sdk validateCorePassword:credentials.password callback:^(NSError * error) {
XCTAssertNil(error);
[waiting extendWaitingTime];
if (!--completionCount) {
[waiting reportCompletion:nil];
}
}];
- [self.sdk validatePasswordCorrect:credentials.usePassword callback:^(NSError * error) {
+ [self.sdk validateCorePassword:credentials.password callback:^(NSError * error) {
XCTAssertNil(error);
[_waitForQueuesTask extendWaitingTime];
if (!--completionCount) {
@@ -264,7 +264,7 @@ - (void) testConcurrentActivation
XCTAssertEqual(PowerAuthExternalPendingOperationType_Activation, extOp2.externalOperationType);
XCTAssertTrue([_app1 isEqualToString:extOp2.externalApplicationId]);
- BOOL result = [self.sdk commitActivationWithPassword:credentials.usePassword error:nil];
+ BOOL result = [self.sdk commitActivationWithCorePassword:credentials.password error:nil];
XCTAssertTrue(result);
XCTAssertTrue([self.sdk hasValidActivation]);
diff --git a/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSdkTestHelper.h b/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSdkTestHelper.h
index 455d8a4e..31978f9c 100644
--- a/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSdkTestHelper.h
+++ b/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSdkTestHelper.h
@@ -17,11 +17,23 @@
#import
#import "PowerAuthTestServerAPI.h"
#import "PowerAuthTestServerConfig.h"
+#import "PowerAuthCorePasswordHelper.h"
#import "AsyncHelper.h"
@import PowerAuth2;
@import PowerAuthCore;
+#import // Expose SDK macros, to allow platform specific #if-defs
+
+typedef NS_OPTIONS(NSUInteger, TestActivationFlags) {
+ TestActivationFlags_None = 0,
+ TestActivationFlags_UseSignature = 1 << 0,
+ TestActivationFlags_CommitWithPlainPassword = 1 << 1,
+ TestActivationFlags_CommitWithCorePassword = 1 << 2,
+ TestActivationFlags_CommitWithBiometry = 1 << 3,
+ TestActivationFlags_RemoveAfter = 1 << 31,
+};
+
/**
Object containing activation data.
*/
@@ -113,6 +125,9 @@
- (PowerAuthSdkActivation*) createActivation:(BOOL)useSignature
activationOtp:(NSString*)activationOtp;
+- (PowerAuthSdkActivation*) createActivationWithFlags:(TestActivationFlags)flags
+ activationOtp:(NSString*)activationOtp;
+
/**
Prepare activation on server.
*/
@@ -173,6 +188,10 @@
*/
- (BOOL) checkForPassword:(NSString*)password;
+/**
+ Validates password on server. Returns YES if password is valid.
+ */
+- (BOOL) checkForCorePassword:(PowerAuthCorePassword*)password;
/**
Converts factors from auth object to string.
@@ -194,10 +213,16 @@
/**
- Creates a new PowerAuthAuthentication object with default configuration.
+ Creates a new PowerAuthAuthentication object for commit with default configuration.
*/
- (PowerAuthAuthentication*) createAuthentication;
+/**
+ Creates a new PowerAuthAuthentication object for commit with biometry.
+ */
+- (PowerAuthAuthentication*) createAuthenticationWithBiometry;
+
+
// Tokens
/**
diff --git a/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSdkTestHelper.m b/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSdkTestHelper.m
index 5dfd23df..ee08ac53 100644
--- a/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSdkTestHelper.m
+++ b/proj-xcode/PowerAuth2IntegrationTests/PowerAuthSdkTestHelper.m
@@ -224,15 +224,13 @@ - (PATSInitActivationResponse*) prepareActivation:(BOOL)useSignature
otp:activationOtp];
}
-/**
- Returns object with activation data, authentication object,
- and result of activation. You can configure whether the activation can use optional signature during the activation
- and whether the activation should be removed automatically after the creation.
- */
-- (PowerAuthSdkActivation*) createActivation:(BOOL)useSignature
- activationOtp:(NSString*)activationOtp
- removeAfter:(BOOL)removeAfter
+- (PowerAuthSdkActivation*) createActivationWithFlags:(TestActivationFlags)flags activationOtp:(NSString *)activationOtp
{
+ BOOL useSignature = (flags & TestActivationFlags_UseSignature) != 0;
+ BOOL removeAfter = (flags & TestActivationFlags_RemoveAfter) != 0;
+ BOOL commitWithPass = (flags & TestActivationFlags_CommitWithPlainPassword) != 0;
+ BOOL commitWithCorePass = (flags & TestActivationFlags_CommitWithCorePassword) != 0;
+ BOOL commitWithBio = (flags & TestActivationFlags_CommitWithBiometry) != 0;
_currentActivation = nil;
XCTAssertFalse([_sdk hasPendingActivation]);
@@ -293,8 +291,15 @@ - (PowerAuthSdkActivation*) createActivation:(BOOL)useSignature
XCTAssertFalse([_sdk hasValidActivation]);
// 3) CLIENT: Now it's time to commit activation locally
- PowerAuthAuthentication * auth = [self createAuthentication];
- result = [_sdk commitActivationWithAuthentication:auth error:&error];
+ PowerAuthAuthentication * auth = commitWithBio ? [self createAuthenticationWithBiometry] : [self createAuthentication];
+ if (commitWithPass) {
+ result = [_sdk commitActivationWithPassword:auth.password.extractedPassword error:&error];
+ } else if (commitWithCorePass) {
+ result = [_sdk commitActivationWithCorePassword:auth.password error:&error];
+ } else {
+ // By default, use authentication for commit
+ result = [_sdk commitActivationWithAuthentication:auth error:&error];
+ }
if (!result) {
return nil;
}
@@ -366,6 +371,15 @@ - (PowerAuthSdkActivation*) createActivation:(BOOL)useSignature
return _currentActivation;
}
+- (PowerAuthSdkActivation*) createActivation:(BOOL)useSignature
+ activationOtp:(NSString*)activationOtp
+ removeAfter:(BOOL)removeAfter
+{
+ TestActivationFlags flags = (useSignature ? TestActivationFlags_UseSignature : 0) |
+ (removeAfter ? TestActivationFlags_RemoveAfter : 0);
+ return [self createActivationWithFlags:flags activationOtp:activationOtp];
+}
+
- (PowerAuthSdkActivation*) createActivation:(BOOL)useSignature removeAfter:(BOOL)removeAfter
{
return [self createActivation:useSignature activationOtp:nil removeAfter:removeAfter];
@@ -473,23 +487,52 @@ - (PowerAuthActivationStatus*) fetchActivationStatus
#pragma mark - Utils
+- (NSArray*) veryStrongPasswords
+{
+ return @[ @"supersecure", @"nbusr123", @"8520", @"pa55w0rd" ];
+}
+
/**
Creates a new PowerAuthAuthentication object with default configuration.
*/
- (PowerAuthAuthentication*) createAuthentication
{
- NSArray * veryCleverPasswords = @[ @"supersecure", @"nbusr123", @"8520", @"pa55w0rd" ];
+ NSArray * veryCleverPasswords = [self veryStrongPasswords];
NSString * newPassword = veryCleverPasswords[arc4random_uniform((uint32_t)veryCleverPasswords.count)];
return [PowerAuthAuthentication commitWithPassword:newPassword];
}
+/**
+ Creates a new PowerAuthAuthentication object with default configuration.
+ */
+- (PowerAuthAuthentication*) createAuthenticationWithBiometry
+{
+ NSArray * veryCleverPasswords = [self veryStrongPasswords];
+ NSString * newPassword = veryCleverPasswords[arc4random_uniform((uint32_t)veryCleverPasswords.count)];
+ return [PowerAuthAuthentication commitWithPasswordAndBiometry:newPassword];
+}
+
/**
Validates password on server. Returns YES if password is valid.
*/
- (BOOL) checkForPassword:(NSString*)password
{
BOOL result = [[AsyncHelper synchronizeAsynchronousBlock:^(AsyncHelper *waiting) {
- id task = [_sdk validatePasswordCorrect:password callback:^(NSError * error) {
+ id task = [_sdk validatePassword:password callback:^(NSError * error) {
+ [waiting reportCompletion:@(error == nil)];
+ }];
+ XCTAssertNotNil(task);
+ }] boolValue];
+ return result;
+}
+
+/**
+ Validates password on server. Returns YES if password is valid.
+ */
+- (BOOL) checkForCorePassword:(PowerAuthCorePassword*)password
+{
+ BOOL result = [[AsyncHelper synchronizeAsynchronousBlock:^(AsyncHelper *waiting) {
+ id task = [_sdk validateCorePassword:password callback:^(NSError * error) {
[waiting reportCompletion:@(error == nil)];
}];
XCTAssertNotNil(task);
@@ -589,7 +632,7 @@ - (NSString*) authToString:(PowerAuthAuthentication*)auth
if (auth.usePossession) {
[components addObject:@"POSSESSION"];
}
- if (auth.usePassword) {
+ if (auth.password) {
[components addObject:@"KNOWLEDGE"];
}
if (auth.useBiometry) {
@@ -740,16 +783,16 @@ @implementation PowerAuthAuthentication (TestHelper)
- (PowerAuthAuthentication*) copyForSigning
{
- if (self.usePassword == nil) {
+ if (self.password == nil) {
@throw [NSException exceptionWithName:@"TestError" reason:@"Wrong PowerAuthAuthentication object" userInfo:nil];
}
- return [PowerAuthAuthentication possessionWithPassword:self.usePassword];
+ return [PowerAuthAuthentication possessionWithCorePassword:self.password];
}
- (PowerAuthAuthentication*) copyCrippledForSigning
{
// cripple auth object
- if (self.usePassword) {
+ if (self.password) {
return [PowerAuthAuthentication possession];
} else {
return [PowerAuthAuthentication possessionWithPassword:@"alwaysBadPassword"];
diff --git a/proj-xcode/PowerAuth2Tests/PowerAuthAuthenticationTests.m b/proj-xcode/PowerAuth2Tests/PowerAuthAuthenticationTests.m
index b8a2eb7a..1448f28e 100644
--- a/proj-xcode/PowerAuth2Tests/PowerAuthAuthenticationTests.m
+++ b/proj-xcode/PowerAuth2Tests/PowerAuthAuthenticationTests.m
@@ -19,6 +19,7 @@
#import "PowerAuthAuthentication+Private.h"
#import "PowerAuthMacros.h"
+#import "PowerAuthCorePasswordHelper.h"
@interface PowerAuthAuthenticationTests : XCTestCase
@property (nonatomic, strong) NSData * customBiometryKey;
@@ -50,7 +51,7 @@ - (void) testCommitWithPassword
PowerAuthAuthentication * auth = [PowerAuthAuthentication commitWithPassword:@"1234"];
XCTAssertTrue(auth.usePossession);
XCTAssertFalse(auth.useBiometry);
- XCTAssertEqual(@"1234", auth.usePassword);
+ XCTAssertEqualObjects(@"1234", auth.password.extractedPassword);
XCTAssertNil(auth.biometryPrompt);
XCTAssertContextNil(auth.biometryContext);
XCTAssertNil(auth.overridenBiometryKey);
@@ -60,11 +61,33 @@ - (void) testCommitWithPassword
auth = [PowerAuthAuthentication commitWithPassword:@"4321" customPossessionKey:_customPossessionKey];
XCTAssertTrue(auth.usePossession);
XCTAssertFalse(auth.useBiometry);
- XCTAssertEqual(@"4321", auth.usePassword);
+ XCTAssertEqualObjects(@"4321", auth.password.extractedPassword);
XCTAssertNil(auth.biometryPrompt);
XCTAssertContextNil(auth.biometryContext);
XCTAssertNil(auth.overridenBiometryKey);
- XCTAssertEqual(self.customPossessionKey, auth.overridenPossessionKey);
+ XCTAssertEqualObjects(self.customPossessionKey, auth.overridenPossessionKey);
+ XCTAssertTrue([auth validateUsage:YES]);
+
+ // core password variants
+
+ auth = [PowerAuthAuthentication commitWithCorePassword:[PowerAuthCorePassword passwordWithString:@"1234"]];
+ XCTAssertTrue(auth.usePossession);
+ XCTAssertFalse(auth.useBiometry);
+ XCTAssertEqualObjects(@"1234", auth.password.extractedPassword);
+ XCTAssertNil(auth.biometryPrompt);
+ XCTAssertContextNil(auth.biometryContext);
+ XCTAssertNil(auth.overridenBiometryKey);
+ XCTAssertNil(auth.overridenPossessionKey);
+ XCTAssertTrue([auth validateUsage:YES]);
+
+ auth = [PowerAuthAuthentication commitWithCorePassword:[PowerAuthCorePassword passwordWithString:@"4321"] customPossessionKey:_customPossessionKey];
+ XCTAssertTrue(auth.usePossession);
+ XCTAssertFalse(auth.useBiometry);
+ XCTAssertEqualObjects(@"4321", auth.password.extractedPassword);
+ XCTAssertNil(auth.biometryPrompt);
+ XCTAssertContextNil(auth.biometryContext);
+ XCTAssertNil(auth.overridenBiometryKey);
+ XCTAssertEqualObjects(self.customPossessionKey, auth.overridenPossessionKey);
XCTAssertTrue([auth validateUsage:YES]);
}
@@ -73,7 +96,7 @@ - (void) testCommitWithPasswordAndBiometry
PowerAuthAuthentication * auth = [PowerAuthAuthentication commitWithPasswordAndBiometry:@"1234"];
XCTAssertTrue(auth.usePossession);
XCTAssertTrue(auth.useBiometry);
- XCTAssertEqual(@"1234", auth.usePassword);
+ XCTAssertEqualObjects(@"1234", auth.password.extractedPassword);
XCTAssertNil(auth.biometryPrompt);
XCTAssertContextNil(auth.biometryContext);
XCTAssertNil(auth.overridenBiometryKey);
@@ -83,11 +106,33 @@ - (void) testCommitWithPasswordAndBiometry
auth = [PowerAuthAuthentication commitWithPasswordAndBiometry:@"4321" customBiometryKey:_customBiometryKey customPossessionKey:_customPossessionKey];
XCTAssertTrue(auth.usePossession);
XCTAssertTrue(auth.useBiometry);
- XCTAssertEqual(@"4321", auth.usePassword);
+ XCTAssertEqualObjects(@"4321", auth.password.extractedPassword);
+ XCTAssertNil(auth.biometryPrompt);
+ XCTAssertContextNil(auth.biometryContext);
+ XCTAssertEqualObjects(self.customBiometryKey, auth.overridenBiometryKey);
+ XCTAssertEqualObjects(self.customPossessionKey, auth.overridenPossessionKey);
+ XCTAssertTrue([auth validateUsage:YES]);
+
+ // core password variants
+
+ auth = [PowerAuthAuthentication commitWithCorePasswordAndBiometry:[PowerAuthCorePassword passwordWithString:@"1234"]];
+ XCTAssertTrue(auth.usePossession);
+ XCTAssertTrue(auth.useBiometry);
+ XCTAssertEqualObjects(@"1234", auth.password.extractedPassword);
+ XCTAssertNil(auth.biometryPrompt);
+ XCTAssertContextNil(auth.biometryContext);
+ XCTAssertNil(auth.overridenBiometryKey);
+ XCTAssertNil(auth.overridenPossessionKey);
+ XCTAssertTrue([auth validateUsage:YES]);
+
+ auth = [PowerAuthAuthentication commitWithCorePasswordAndBiometry:[PowerAuthCorePassword passwordWithString:@"4321"] customBiometryKey:_customBiometryKey customPossessionKey:_customPossessionKey];
+ XCTAssertTrue(auth.usePossession);
+ XCTAssertTrue(auth.useBiometry);
+ XCTAssertEqualObjects(@"4321", auth.password.extractedPassword);
XCTAssertNil(auth.biometryPrompt);
XCTAssertContextNil(auth.biometryContext);
- XCTAssertEqual(self.customBiometryKey, auth.overridenBiometryKey);
- XCTAssertEqual(self.customPossessionKey, auth.overridenPossessionKey);
+ XCTAssertEqualObjects(self.customBiometryKey, auth.overridenBiometryKey);
+ XCTAssertEqualObjects(self.customPossessionKey, auth.overridenPossessionKey);
XCTAssertTrue([auth validateUsage:YES]);
}
@@ -96,7 +141,7 @@ - (void) testSignPossessionOnly
PowerAuthAuthentication * auth = [PowerAuthAuthentication possession];
XCTAssertTrue(auth.usePossession);
XCTAssertFalse(auth.useBiometry);
- XCTAssertNil(auth.usePassword);
+ XCTAssertNil(auth.password);
XCTAssertNil(auth.biometryPrompt);
XCTAssertContextNil(auth.biometryContext);
XCTAssertNil(auth.overridenBiometryKey);
@@ -109,7 +154,7 @@ - (void) testSignPossessionWithPassword
PowerAuthAuthentication * auth = [PowerAuthAuthentication possessionWithPassword:@"1234"];
XCTAssertTrue(auth.usePossession);
XCTAssertFalse(auth.useBiometry);
- XCTAssertEqual(@"1234", auth.usePassword);
+ XCTAssertEqualObjects(@"1234", auth.password.extractedPassword);
XCTAssertNil(auth.biometryPrompt);
XCTAssertContextNil(auth.biometryContext);
XCTAssertNil(auth.overridenBiometryKey);
@@ -119,11 +164,33 @@ - (void) testSignPossessionWithPassword
auth = [PowerAuthAuthentication possessionWithPassword:@"4321" customPossessionKey:_customPossessionKey];
XCTAssertTrue(auth.usePossession);
XCTAssertFalse(auth.useBiometry);
- XCTAssertEqual(@"4321", auth.usePassword);
+ XCTAssertEqualObjects(@"4321", auth.password.extractedPassword);
+ XCTAssertNil(auth.biometryPrompt);
+ XCTAssertContextNil(auth.biometryContext);
+ XCTAssertNil(auth.overridenBiometryKey);
+ XCTAssertEqualObjects(self.customPossessionKey, auth.overridenPossessionKey);
+ XCTAssertTrue([auth validateUsage:NO]);
+
+ // core password variants
+
+ auth = [PowerAuthAuthentication possessionWithCorePassword:[PowerAuthCorePassword passwordWithString:@"1234"]];
+ XCTAssertTrue(auth.usePossession);
+ XCTAssertFalse(auth.useBiometry);
+ XCTAssertEqualObjects(@"1234", auth.password.extractedPassword);
+ XCTAssertNil(auth.biometryPrompt);
+ XCTAssertContextNil(auth.biometryContext);
+ XCTAssertNil(auth.overridenBiometryKey);
+ XCTAssertNil(auth.overridenPossessionKey);
+ XCTAssertTrue([auth validateUsage:NO]);
+
+ auth = [PowerAuthAuthentication possessionWithCorePassword:[PowerAuthCorePassword passwordWithString:@"4321"] customPossessionKey:_customPossessionKey];
+ XCTAssertTrue(auth.usePossession);
+ XCTAssertFalse(auth.useBiometry);
+ XCTAssertEqualObjects(@"4321", auth.password.extractedPassword);
XCTAssertNil(auth.biometryPrompt);
XCTAssertContextNil(auth.biometryContext);
XCTAssertNil(auth.overridenBiometryKey);
- XCTAssertEqual(self.customPossessionKey, auth.overridenPossessionKey);
+ XCTAssertEqualObjects(self.customPossessionKey, auth.overridenPossessionKey);
XCTAssertTrue([auth validateUsage:NO]);
}
@@ -132,7 +199,7 @@ - (void) testSignPossessionWithBiometry
PowerAuthAuthentication * auth = [PowerAuthAuthentication possessionWithBiometry];
XCTAssertTrue(auth.usePossession);
XCTAssertTrue(auth.useBiometry);
- XCTAssertNil(auth.usePassword);
+ XCTAssertNil(auth.password);
XCTAssertNil(auth.biometryPrompt);
XCTAssertContextNil(auth.biometryContext);
XCTAssertNil(auth.overridenBiometryKey);
@@ -142,18 +209,18 @@ - (void) testSignPossessionWithBiometry
auth = [PowerAuthAuthentication possessionWithBiometryWithCustomBiometryKey:_customBiometryKey customPossessionKey:_customPossessionKey];
XCTAssertTrue(auth.usePossession);
XCTAssertTrue(auth.useBiometry);
- XCTAssertNil(auth.usePassword);
+ XCTAssertNil(auth.password);
XCTAssertNil(auth.biometryPrompt);
XCTAssertContextNil(auth.biometryContext);
- XCTAssertEqual(self.customBiometryKey, auth.overridenBiometryKey);
- XCTAssertEqual(self.customPossessionKey, auth.overridenPossessionKey);
+ XCTAssertEqualObjects(self.customBiometryKey, auth.overridenBiometryKey);
+ XCTAssertEqualObjects(self.customPossessionKey, auth.overridenPossessionKey);
XCTAssertTrue([auth validateUsage:NO]);
auth = [PowerAuthAuthentication possessionWithBiometryPrompt:_biometryPrompt];
XCTAssertTrue(auth.usePossession);
XCTAssertTrue(auth.useBiometry);
- XCTAssertNil(auth.usePassword);
- XCTAssertEqual(_biometryPrompt, auth.biometryPrompt);
+ XCTAssertNil(auth.password);
+ XCTAssertEqualObjects(_biometryPrompt, auth.biometryPrompt);
XCTAssertContextNil(auth.biometryContext);
XCTAssertNil(auth.overridenBiometryKey);
XCTAssertNil(auth.overridenPossessionKey);
@@ -162,20 +229,20 @@ - (void) testSignPossessionWithBiometry
auth = [PowerAuthAuthentication possessionWithBiometryPrompt:_biometryPrompt customPossessionKey:_customPossessionKey];
XCTAssertTrue(auth.usePossession);
XCTAssertTrue(auth.useBiometry);
- XCTAssertNil(auth.usePassword);
- XCTAssertEqual(_biometryPrompt, auth.biometryPrompt);
+ XCTAssertNil(auth.password);
+ XCTAssertEqualObjects(_biometryPrompt, auth.biometryPrompt);
XCTAssertContextNil(auth.biometryContext);
XCTAssertNil(auth.overridenBiometryKey);
- XCTAssertEqual(self.customPossessionKey, auth.overridenPossessionKey);
+ XCTAssertEqualObjects(self.customPossessionKey, auth.overridenPossessionKey);
XCTAssertTrue([auth validateUsage:NO]);
#if PA2_HAS_LACONTEXT
auth = [PowerAuthAuthentication possessionWithBiometryContext:_biometryContext];
XCTAssertTrue(auth.usePossession);
XCTAssertTrue(auth.useBiometry);
- XCTAssertNil(auth.usePassword);
+ XCTAssertNil(auth.password);
XCTAssertNil(auth.biometryPrompt);
- XCTAssertEqual(self.biometryContext, auth.biometryContext);
+ XCTAssertEqualObjects(self.biometryContext, auth.biometryContext);
XCTAssertNil(auth.overridenBiometryKey);
XCTAssertNil(auth.overridenPossessionKey);
XCTAssertTrue([auth validateUsage:NO]);
@@ -183,11 +250,11 @@ - (void) testSignPossessionWithBiometry
auth = [PowerAuthAuthentication possessionWithBiometryContext:_biometryContext customPossessionKey:_customPossessionKey];
XCTAssertTrue(auth.usePossession);
XCTAssertTrue(auth.useBiometry);
- XCTAssertNil(auth.usePassword);
+ XCTAssertNil(auth.password);
XCTAssertNil(auth.biometryPrompt);
- XCTAssertEqual(self.biometryContext, auth.biometryContext);
+ XCTAssertEqualObjects(self.biometryContext, auth.biometryContext);
XCTAssertNil(auth.overridenBiometryKey);
- XCTAssertEqual(self.customPossessionKey, auth.overridenPossessionKey);
+ XCTAssertEqualObjects(self.customPossessionKey, auth.overridenPossessionKey);
XCTAssertTrue([auth validateUsage:NO]);
#endif // PA2_HAS_LACONTEXT
}
@@ -198,6 +265,8 @@ - (void) testWrongUsage
XCTAssertFalse([auth validateUsage:YES]);
auth = [PowerAuthAuthentication commitWithPassword:@"Hello"];
XCTAssertFalse([auth validateUsage:NO]);
+ auth = [PowerAuthAuthentication commitWithCorePassword:[PowerAuthCorePassword passwordWithString:@"Hello"]];
+ XCTAssertFalse([auth validateUsage:NO]);
}
- (void) testLegacyObject
diff --git a/proj-xcode/PowerAuth2TestsHostApp/ContentView.swift b/proj-xcode/PowerAuth2TestsHostApp/ContentView.swift
index b9d8dea2..17a65d56 100644
--- a/proj-xcode/PowerAuth2TestsHostApp/ContentView.swift
+++ b/proj-xcode/PowerAuth2TestsHostApp/ContentView.swift
@@ -16,6 +16,9 @@
import SwiftUI
+import PowerAuth2
+import PowerAuthCore
+
struct ContentView: View {
var body: some View {
Text("Executing PowerAuth integration tests.")
@@ -28,3 +31,15 @@ struct ContentView_Previews: PreviewProvider {
ContentView()
}
}
+
+/// This function is useful only for test whether our Objective-C API
+/// is properly exposed to Swift. You can prototype any code you want
+/// here to see, whether our API makes sense in Swift.
+///
+/// Please revert your changes in this file before you commit & push
+/// the rest of your work into the repository.
+fileprivate func dummyFunction() {
+ let config = PowerAuthConfiguration()
+ let sdk = PowerAuthSDK(configuration: config)!
+ _ = sdk.hasBiometryFactor()
+}
diff --git a/proj-xcode/PowerAuthCore/PowerAuthCorePassword.h b/proj-xcode/PowerAuthCore/PowerAuthCorePassword.h
index 84021aca..3900398e 100644
--- a/proj-xcode/PowerAuthCore/PowerAuthCorePassword.h
+++ b/proj-xcode/PowerAuthCore/PowerAuthCorePassword.h
@@ -70,19 +70,38 @@
*/
@interface PowerAuthCorePassword : NSObject
+/**
+ Constructor with no parameters is not available.
+ */
+- (nonnull instancetype) init NS_UNAVAILABLE;
+
+/**
+ Initialize PowerAuthCorePassword object with UTF8 data from the given string. The method is useful
+ for scenarios, when you have the full password already prepared and you want to pass it to the Session
+ as a parameter.
+ */
+- (nonnull instancetype) initWithString:(nonnull NSString*)string;
+
+/**
+ Initialize PowerAuthCorePassword object with the content copied from given data object.
+ The password object will contain an immutable passphrase, created exactly from the bytes,
+ provided by the data object.
+ */
+- (nonnull instancetype) initWithData:(nonnull NSData*)data;
+
/**
Returns a new instance of PowerAuthCorePassword object, initialized with UTF8 data
from the given string. The method is useful for scenarios, when you have
the full password already prepared and you want to pass it to the Session
as a parameter.
*/
-+ (nullable instancetype) passwordWithString:(nonnull NSString*)string;
++ (nonnull instancetype) passwordWithString:(nonnull NSString*)string;
/**
Creates a new instance of PowerAuthCorePassword object, initialized with the content
copied from given data object. The password object will contain an immutable
passphrase, created exactly from the bytes, provided by the data object.
*/
-+ (nullable instancetype) passwordWithData:(nonnull NSData*)data;
++ (nonnull instancetype) passwordWithData:(nonnull NSData*)data;
/**
Returns length of the password (in bytes).
@@ -96,15 +115,21 @@
- (BOOL) isEqualToPassword:(nullable PowerAuthCorePassword*)password;
/**
- The method validates stored passphrase with using provided validation block. The raw bytes of
- the passphrase are revealed to the block, which can decide whether the passphrase's complexity
- is sufficient or not. It's not recommended to copy the plaintext password to another memory
- location, to minimize traces of the password in the memory.
+ The method allows you to validate stored passphrase with using provided validation block.
+ The raw characters and the length of the passphrase are revealed to the block, and the validation
+ block can decide whether the passphrase's complexity is sufficient or not.
+
+ The provided pointer to the passhphrase is always null terminated, so it's safe to pass it
+ to functions that accept the null terminated string. On opposite to that, it's not recommended
+ to use this validation function on passwords created form an arbitrary data.
- Returns value provided by the validation block. The meaning of returned integer depends on
+ It's not recommended to copy the plaintext password to another memory location, to minimize traces
+ of the password in the memory.
+
+ @return Returns value provided by the validation block. The meaning of returned integer depends on
validation block's implementation.
*/
-- (NSInteger) validatePasswordComplexity:(NSInteger (NS_NOESCAPE ^_Nonnull)(const UInt8 * _Nonnull passphrase, NSUInteger length))validationBlock;
+- (NSInteger) validatePasswordComplexity:(NSInteger (NS_NOESCAPE ^_Nonnull)(const char * _Nonnull passphrase, NSInteger length))validationBlock;
@end
@@ -117,10 +142,25 @@
*/
@interface PowerAuthCoreMutablePassword : PowerAuthCorePassword
+/**
+ Initialize PowerAuthCoreMutablePassword object with empty passphrase.
+ */
+- (nonnull instancetype) init;
+
+/**
+ Mutable password cannot be created with predefined string.
+ */
+- (nonnull instancetype) initWithString:(nonnull NSString*)string NS_UNAVAILABLE;
+
+/**
+ Mutable password cannot be created with predefined data.
+ */
+- (nonnull instancetype) initWithData:(nonnull NSData*)data NS_UNAVAILABLE;
+
/**
Returns a new insntace of PowerAuthCoreMutablePassword object.
*/
-+ (nullable instancetype) mutablePassword;
++ (nonnull instancetype) mutablePassword;
/**
Clears current content of the password
diff --git a/proj-xcode/PowerAuthCore/PowerAuthCorePassword.mm b/proj-xcode/PowerAuthCore/PowerAuthCorePassword.mm
index 02433c80..28e0ed5d 100644
--- a/proj-xcode/PowerAuthCore/PowerAuthCorePassword.mm
+++ b/proj-xcode/PowerAuthCore/PowerAuthCorePassword.mm
@@ -26,22 +26,41 @@ @implementation PowerAuthCorePassword
io::getlime::powerAuth::Password _password;
}
-+ (instancetype) passwordWithString:(NSString *)string
+- (instancetype) initWithString:(NSString *)string
{
- PowerAuthCorePassword * pass = [[PowerAuthCorePassword alloc] init];
- if (pass) {
- pass->_password.initAsImmutable(cc7::MakeRange(string.UTF8String));
+ self = [super init];
+ if (self) {
+ _password.initAsImmutable(cc7::MakeRange(string.UTF8String));
}
- return pass;
+ return self;
}
-+ (instancetype) passwordWithData:(NSData *)data
+- (instancetype) initWithData:(NSData *)data
{
- PowerAuthCorePassword * pass = [[PowerAuthCorePassword alloc] init];
- if (pass) {
- pass->_password.initAsImmutable(cc7::ByteRange(data.bytes, data.length));
+ self = [super init];
+ if (self) {
+ _password.initAsImmutable(cc7::ByteRange(data.bytes, data.length));
}
- return pass;
+ return self;
+}
+
+- (instancetype) initMutable
+{
+ self = [super init];
+ if (self) {
+ _password.initAsMutable();
+ }
+ return self;
+}
+
++ (instancetype) passwordWithString:(NSString *)string
+{
+ return [[PowerAuthCorePassword alloc] initWithString:string];
+}
+
++ (instancetype) passwordWithData:(NSData *)data
+{
+ return [[PowerAuthCorePassword alloc] initWithData:data];
}
- (NSUInteger) length
@@ -70,10 +89,15 @@ - (BOOL) isEqual:(id)object
return NO;
}
-- (NSInteger) validatePasswordComplexity:(NSInteger (NS_NOESCAPE ^)(const UInt8* passphrase, NSUInteger length))validationBlock
+- (NSInteger) validatePasswordComplexity:(NSInteger (NS_NOESCAPE ^)(const char* passphrase, NSInteger length))validationBlock
{
auto plaintext = _password.passwordData();
- return validationBlock(plaintext.data(), plaintext.size());
+ auto size = plaintext.size();
+ // Append null terminator in case that consumer would like to use the pointer
+ // in functions that accept c-style strings. The validation block still gets
+ // the correct size.
+ plaintext.append(0);
+ return validationBlock((const char*)plaintext.data(), size);
}
@end
@@ -98,13 +122,9 @@ @implementation PowerAuthCorePassword (Private)
@implementation PowerAuthCoreMutablePassword
-- (id) init
+- (instancetype) init
{
- self = [super init];
- if (self) {
- _password.initAsMutable();
- }
- return self;
+ return [super initMutable];
}
+ (instancetype) mutablePassword
diff --git a/proj-xcode/PowerAuthCoreTests/PowerAuthCorePasswordTests.m b/proj-xcode/PowerAuthCoreTests/PowerAuthCorePasswordTests.m
index 62ce0280..e18cf29f 100644
--- a/proj-xcode/PowerAuthCoreTests/PowerAuthCorePasswordTests.m
+++ b/proj-xcode/PowerAuthCoreTests/PowerAuthCorePasswordTests.m
@@ -163,7 +163,7 @@ - (void) testPasswordNotEqual
- (NSString*) extractStringFromPassword:(PowerAuthCorePassword*)password
{
__block NSString * stringPassword = nil;
- [password validatePasswordComplexity:^NSInteger(const UInt8 * _Nonnull passphrase, NSUInteger length) {
+ [password validatePasswordComplexity:^NSInteger(const char * _Nonnull passphrase, NSInteger length) {
stringPassword = [[NSString alloc] initWithBytes:passphrase length:length encoding:NSUTF8StringEncoding];
return 0;
}];
@@ -173,7 +173,7 @@ - (NSString*) extractStringFromPassword:(PowerAuthCorePassword*)password
- (NSData*) extractBytesFromPassword:(PowerAuthCorePassword*)password
{
__block NSData * passwordData = nil;
- [password validatePasswordComplexity:^NSInteger(const UInt8 * _Nonnull passphrase, NSUInteger length) {
+ [password validatePasswordComplexity:^NSInteger(const char * _Nonnull passphrase, NSInteger length) {
passwordData = [NSData dataWithBytes:passphrase length:length];
return 0;
}];
diff --git a/src/PowerAuth/Password.cpp b/src/PowerAuth/Password.cpp
index 3956a0b5..febab8bd 100644
--- a/src/PowerAuth/Password.cpp
+++ b/src/PowerAuth/Password.cpp
@@ -119,7 +119,7 @@ namespace powerAuth
cc7::ByteArray Password::passwordData() const
{
- // Pre-allos result ByteArray with actual stored data size.
+ // Pre-allocate ByteArray with actual stored password size.
const size_t data_size = _pass.size() - randomKeySize;
cc7::ByteArray plaintext(data_size);
// Reveal plaintext bytes to result ByteArray
@@ -227,6 +227,15 @@ namespace powerAuth
return false;
}
+ void Password::secureClear()
+ {
+ if (isMutable()) {
+ initAsMutable();
+ } else {
+ initAsImmutable(cc7::ByteRange());
+ }
+ }
+
// MARK: - Private interface -
size_t Password::indexToPos(size_t index)