#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); }
/// <summary> /// Uploads a new profile picture to the server. /// </summary> /// <param name="totp">2FA Token.</param> /// <param name="pic">The profile picture (base64-encoded). Can be <c>null</c>.</param> /// <returns>Whether the update was accepted or failed.</returns> public async Task <bool> UpdateUserProfilePicture(string totp, string pic) { try { if (user is null || pic.Length > 512 * 512 || user.Id.NullOrEmpty() || user.Token is null || user.Token.Item2.NullOrEmpty() || totp.NullOrEmpty()) { return(false); } var dto = new UserChangeProfilePictureRequestDto { Totp = totp, ProfilePicture = pic, PasswordSHA512 = user.PasswordSHA512 }; var requestBody = new EpistleRequestBody { UserId = user.Id, Auth = user.Token?.Item2, Body = JsonSerializer.Serialize(dto) }; var request = EpistleRequest(requestBody.Sign(rsa, user.PrivateKeyPem), "users/pic", Method.Put); RestResponse response = await restClient.ExecuteAsync(request).ConfigureAwait(false); return(response?.StatusCode == HttpStatusCode.OK); } catch { return(false); } }
private async void TryJoinConvos() { if (!appSettings["SaveConvoPasswords", true]) { return; } var userConvos = (await userService.GetConvos(user.Id, user.Token.Item2)) .Select(c => (Convo)c) .Distinct() .Where(convo => !convo.IsExpired()).ToArray(); foreach (var convo in userConvos) { string cachedPwSHA512 = convoPasswordProvider.GetPasswordSHA512(convo.Id); if (cachedPwSHA512.NullOrEmpty()) { cachedPwSHA512 = await SecureStorage.GetAsync($"convo:{convo.Id}_pw:SHA512"); } if (cachedPwSHA512.NotNullNotEmpty()) { var dto = new ConvoJoinRequestDto { ConvoId = convo.Id, ConvoPasswordSHA512 = cachedPwSHA512 }; var body = new EpistleRequestBody { UserId = user.Id, Auth = user.Token.Item2, Body = JsonSerializer.Serialize(dto) }; if (await convoService.JoinConvo(body.Sign(crypto, user.PrivateKeyPem))) { convoPasswordProvider.SetPasswordSHA512(convo.Id, cachedPwSHA512); ConvoMetadataDto metadata = await convoService.GetConvoMetadata(convo.Id, cachedPwSHA512, user.Id, user.Token.Item2); if (metadata != null) { var viewModel = viewModelFactory.Create <ActiveConvoViewModel>(); viewModel.ActiveConvo = convo; viewModel.OnAppearing(); activeConvos[convo.Id] = viewModel; } } } } eventAggregator.GetEvent <TriggerUpdateConvosListEvent>().Publish(); }
/// <summary> /// Submits a message to a <see cref="Convo"/>. /// </summary> /// <param name="convo">The <see cref="Convo"/> to post the message into.</param> /// <param name="messageType">The type of message (e.g. "TEXT=UTF8", "FILE=example.png", etc...).</param> /// <param name="message">The message body to encrypt and post.</param> /// <returns>Whether the message could be submitted successfully or not.</returns> private async Task <bool> PostMessageToConvo(Convo convo, string messageType, byte[] message) { if (message.NullOrEmpty()) { return(false); } Task <IDictionary <string, string> > publicKeys = userService.GetUserPublicKeys(user.Id, convo.GetParticipantIdsCommaSeparated(), user.Token.Item2); byte[] compressedMessage = await compressionUtility.Compress(message, CompressionSettings.Default).ConfigureAwait(false); using var encryptionResult = await aes.EncryptAsync(compressedMessage).ConfigureAwait(false); byte[] key = new byte[48]; for (int i = 0; i < 32; i++) { key[i] = encryptionResult.Key[i]; } for (int i = 0; i < 16; i++) { key[i + 32] = encryptionResult.IV[i]; } // Encrypt the message decryption key for every convo participant individually. var encryptedKeys = new ConcurrentBag <string>(); Parallel.ForEach(await publicKeys.ConfigureAwait(false), kvp => { (string userId, string userPublicKey) = kvp; if (userId.NotNullNotEmpty() && userPublicKey.NotNullNotEmpty()) { string decompressedPublicKey = keyExchange.DecompressPublicKey(userPublicKey); encryptedKeys.Add(userId + ':' + Convert.ToBase64String(rsa.Encrypt(key, decompressedPublicKey))); } }); try { var postParamsDto = new PostMessageParamsDto { Type = messageType, SenderName = userSettings.Username, ConvoId = convo.Id, ConvoPasswordSHA512 = convoPasswordProvider.GetPasswordSHA512(convo.Id), EncryptedKeys = encryptedKeys.ToCommaSeparatedString(), EncryptedBody = Convert.ToBase64String(encryptionResult.EncryptedData) }; var body = new EpistleRequestBody { UserId = user.Id, Auth = user.Token.Item2, Body = JsonSerializer.Serialize(postParamsDto) }; return(await convoService.PostMessage(body.Sign(rsa, user.PrivateKeyPem)).ConfigureAwait(false)); } catch { return(false); } }