#pragma warning restore 1591 /// <summary> /// Changes an Epistle <see cref="User"/>'s password. /// </summary> /// <param name="oldPw">The old <see cref="User"/> password (NOT its SHA512!).</param> /// <param name="newPw">The new, better and safer password (NOT its SHA512!).</param> /// <param name="totp">Request authentication token.</param> /// <returns>Whether the password change request was successful or not.</returns> /// <exception cref="ApplicationException">Thrown if the <see cref="User.PrivateKeyPem"/> is <c>null</c> or empty.</exception> public async Task <bool> ChangePassword(string oldPw, string newPw, string totp) { if (oldPw == newPw) { return(false); } if (user.PrivateKeyPem.NullOrEmpty()) { string msg = "The user's in-memory private key seems to be null or empty; can't change passwords without re-encrypting a new copy of the user key!"; logger?.LogError(msg); throw new ApplicationException(msg); } var dto = new UserChangePasswordRequestDto { Totp = totp, OldPwSHA512 = oldPw.SHA512(), NewPwSHA512 = newPw.SHA512(), NewPrivateKey = await keyExchange.EncryptAndCompressPrivateKeyAsync(user.PrivateKeyPem, newPw).ConfigureAwait(false) }; var requestBody = new EpistleRequestBody { UserId = user.Id, Auth = user.Token.Item2, Body = await compressionUtility.Compress(JsonSerializer.Serialize(dto)).ConfigureAwait(false) }; bool success = await userService.ChangeUserPassword(requestBody.Sign(crypto, user.PrivateKeyPem)).ConfigureAwait(false); return(success); }
public async Task KeyExchangeUtility_EncryptAndCompress_DecompressAndDecrypt_IdenticalAfterwards() { const string TEST_PW = "test.@#°§çUserPassword$$$__ bvgcgfc-jgvgch-67554-hjvghcv _69420 \r\n847KWdHfhoö\nüä!\t!]] [} \r\n\r\n $äö\""; string i = await keyExchange.EncryptAndCompressPrivateKeyAsync(privateKeyPem, TEST_PW); string o = await keyExchange.DecompressAndDecryptPrivateKeyAsync(i, TEST_PW); Assert.Equal(privateKeyPem, o); }
#pragma warning restore 1591 /// <summary> /// Submits a user registration request to the Epistle backend and returns the resulting status code.<para> </para> /// If the user creation succeeded, the created user's data is applied to the currently active session <see cref="User"/>. /// The meaning of the returned status codes is as follows:<para> </para> /// 0 = Success! The user was created and the related data was loaded into session <see cref="User"/>.<para> </para> /// 1 = Connection to the Epistle server could not be established.<para> </para> /// 2 = RSA Key generation failed/incomplete.<para> </para> /// 3 = User registration failed server-side.<para> </para> /// 4 = User registration failed client-side.<para> </para> /// </summary> /// <param name="password">The user's password (NOT the SHA512!)</param> /// <param name="userCreationSecret">The backend's user creation secret.</param> /// <returns>A tuple containing the resulting status code and (eventually) the <see cref="UserCreationResponseDto"/></returns> public async Task <ValueTuple <int, UserCreationResponseDto> > CreateUser(string password, string userCreationSecret) { if (!await connectionTest.TestConnection().ConfigureAwait(false)) { return(1, null); } (string, string)keyPair = await keyGenerationTask.ConfigureAwait(false); keyGenerationTask = Task.Run(() => keygen.GenerateKeyPair(RSA_KEY_SIZE)); if (keyPair.Item1.NullOrEmpty() || keyPair.Item2.NullOrEmpty()) { return(2, null); } string publicKeyPem = keyPair.Item1; string privateKeyPem = keyPair.Item2; try { var userCreationResponse = await userService.CreateUser(new UserCreationRequestDto { PasswordSHA512 = password.SHA512(), CreationSecret = userCreationSecret, PublicKey = await keyExchange.CompressPublicKeyAsync(publicKeyPem).ConfigureAwait(false), PrivateKey = await keyExchange.EncryptAndCompressPrivateKeyAsync(privateKeyPem, password).ConfigureAwait(false), }).ConfigureAwait(false); if (userCreationResponse is null) { logger?.LogError("The user creation process failed server-side. Reason unknown; please make an admin check out the server's log files!"); return(3, null); } appSettings.LastUserId = userCreationResponse.Id; // Handle this event back in the client UI, // since it's there where the backup codes + 2FA secret (QR) will be displayed. logger?.LogMessage($"Created user {userCreationResponse.Id}."); return(0, userCreationResponse); } catch (Exception e) { logger?.LogError($"The user creation process failed. Thrown exception: {e.Message}"); return(4, null); } }