diff --git a/build.gradle.kts b/build.gradle.kts index c4c6eb7..dbb7537 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -51,7 +51,6 @@ kotlin { val commonMain by getting { dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.3") -// api("net.sergeych:unikrypto:1.2.2-SNAPSHOT") api("net.sergeych:parsec3:0.4.6") api("net.sergeych:unikrypto:1.2.5") } diff --git a/src/commonMain/kotlin/net.sergeych.superlogin/client/SuperloginClient.kt b/src/commonMain/kotlin/net.sergeych.superlogin/client/SuperloginClient.kt index 61161c2..d4840a4 100644 --- a/src/commonMain/kotlin/net.sergeych.superlogin/client/SuperloginClient.kt +++ b/src/commonMain/kotlin/net.sergeych.superlogin/client/SuperloginClient.kt @@ -15,8 +15,7 @@ import net.sergeych.mp_tools.globalLaunch import net.sergeych.parsec3.* import net.sergeych.superlogin.* import net.sergeych.superlogin.server.SuperloginRestoreAccessPayload -import net.sergeych.unikrypto.SignedRecord -import net.sergeych.unikrypto.SymmetricKey +import net.sergeych.unikrypto.* import kotlin.reflect.KType import kotlin.reflect.typeOf @@ -394,7 +393,7 @@ class SuperloginClient( aco.payload.dataStorageKey ) // new ACO with a new password key and payload (but the same secret!) - var newAco = aco.updatePasswordKey(keys.loginAccessKey).updatePayload(newSlp) + val newAco = aco.updatePasswordKey(keys.loginAccessKey).updatePayload(newSlp) // trying to update val result = invoke( serverApi.slChangePasswordAndLogin, ChangePasswordArgs( @@ -422,14 +421,18 @@ class SuperloginClient( } /** - * Change password for a logged-in user using its known password. It is a long operation + * Change password for a logged-in user using its known password. It is a long operation. + * * @param oldPassword existing password (re-request it from a user!) - * @param newPassword new password. we do not chek it but it should be strong - check it on your end + * @param newPassword new password. we do not check it, but it should be strong - check it on your end * for example with [net.sergeych.unikrypto.Passwords] tools - * @param passwordDerivationParams at this point derivation parameters are alwaus updated so it is possible + * @param passwordDerivationParams at this point derivation parameters are always updated so it is possible * to set it to desired - * @param loginKeyStrength login key is regenerateed so its strength could be updated here - * @return true if the password has been successfully changed + * @param loginKeyStrength login key is regenerated so its strength could be updated here + * + * @return true if the password has been successfully changed, false if the server didn't allow it. + * + * @throws InvalidPasswordError if the oldPassword is wrong */ suspend fun changePassword( oldPassword: String, newPassword: String, @@ -441,10 +444,24 @@ class SuperloginClient( val dp = invoke(serverApi.slRequestDerivationParams, loginName) val keys = DerivedKeys.derive(oldPassword, dp) val data = invoke(serverApi.slRequestACOByLoginName, RequestACOByLoginNameArgs(loginName, keys.loginId)) - return AccessControlObject.unpackWithKey(data.packedACO, keys.loginAccessKey) - ?.let { - changePasswordWithACO(it, newPassword, passwordDerivationParams, loginKeyStrength) - } ?: false + try { + return AccessControlObject.unpackWithKey( + data.packedACO, + keys.loginAccessKey + ) + ?.let { + changePasswordWithACO(it, newPassword, passwordDerivationParams, loginKeyStrength) + } ?: false + } catch (e: Exception) { + when (e) { + is Container.StructureError, + is Container.DecryptionError, + is EncryptedBinaryStorage.DecryptionFailed -> + throw InvalidPasswordError() + + else -> throw e + } + } } diff --git a/src/jvmTest/kotlin/net/sergeych/WsServerKtTest.kt b/src/jvmTest/kotlin/net/sergeych/WsServerKtTest.kt index 05cf5e4..4a36815 100644 --- a/src/jvmTest/kotlin/net/sergeych/WsServerKtTest.kt +++ b/src/jvmTest/kotlin/net/sergeych/WsServerKtTest.kt @@ -218,14 +218,15 @@ internal class WsServerKtTest { val api = TestApiServer() val slc = SuperloginClient(client) assertEquals(LoginState.LoggedOut, slc.state.value) - var rt = slc.register("foo", "passwd", TestData("bar!")) + var rt = slc.register("foo", "passwd", TestData("bar!"), 2048, 140) val dk1 = slc.dataKey!! assertIs(rt) val secret = rt.secret var token = rt.loginToken assertFalse(slc.changePassword("wrong", "new")) - assertTrue(slc.changePassword("passwd", "newpass1")) + assertTrue(slc.changePassword("passwd", "newpass1", + PasswordDerivationParams(300), 2048)) assertTrue { slc.isLoggedIn } assertEquals("foo", slc.call(api.loginName))