/// <summary> /// Reads and decrypts a file THAT IS ENCRYPTED WITH <see cref="AesGcmEncrypt(FileInfo,FileInfo,byte[],int)"/> /// </summary> /// <param name="input">The input file to decrypt it</param> /// <param name="output">The output file to write the decrypted data into it</param> /// <param name="key">Encryption key</param> public static void AesGcmDecrypt(FileInfo input, FileInfo output, byte[] key) { using (Stream reader = input.OpenRead()) using (Stream writer = output.OpenWrite()) { int bufferSize; byte[] fileKey; // at first read the crypt buffer { byte[] buffer = new byte[4]; reader.Read(buffer, 0, buffer.Length); bufferSize = SharedStuff.BytesToInt(buffer); bufferSize += 12 + 16; } // now read the key that is used to encrypt this file { byte[] buffer = new byte[12 + 32 + 16]; reader.Read(buffer, 0, buffer.Length); fileKey = AesGcmDecrypt(buffer, key); } // now read the input file; "bufferSize" bytes at a time int readCount; byte[] readBytes = new byte[bufferSize]; while ((readCount = reader.Read(readBytes, 0, readBytes.Length)) > 0) { if (readBytes.Length > readCount) { Array.Resize(ref readBytes, readCount); // this is the last chunk of file; Do not decrypt all of the data in readBytes } byte[] decrypted = AesGcmDecrypt(readBytes, fileKey); writer.Write(decrypted, 0, decrypted.Length); } } }
/// <summary> /// Encrypts a file with AesGcm /// </summary> /// <param name="input">The input file to encrypt</param> /// <param name="output">Output file to write the encrypted file</param> /// <param name="key">The key to encrypt data with it</param> /// <param name="bufferSize">The buffer size that the input is read and encrypted</param> public static void AesGcmEncrypt(FileInfo input, FileInfo output, byte[] key, int bufferSize) { byte[] nonce = new byte[12]; byte[] tag = new byte[16]; using (Stream reader = input.OpenRead()) using (Stream writer = output.OpenWrite()) { // at first write the buffer size to first of file writer.Write(SharedStuff.IntToBytes(bufferSize), 0, 4); // int is 4 bytes // generate a key for encryption byte[] fileKey = new byte[32]; SharedStuff.SecureRandom.GetBytes(fileKey); SharedStuff.SecureRandom.GetBytes(nonce); { byte[] keyToWrite = AesGcmEncrypt(fileKey, key, nonce); writer.Write(keyToWrite, 0, keyToWrite.Length); Array.Clear(keyToWrite, 0, keyToWrite.Length); } // now read the input file; "bufferSize" bytes at a time int readCount; byte[] readBytes = new byte[bufferSize]; while ((readCount = reader.Read(readBytes, 0, readBytes.Length)) > 0) { if (readBytes.Length > readCount) { Array.Resize(ref readBytes, readCount); // this is the last chunk of file; Do not encrypt all of the data in readBytes } //byte[] crypted = AesGcmEncrypt(readBytes, key); -> Do not use this because Concat takes a lot of time SharedStuff.SecureRandom.GetBytes(nonce); byte[] encrypted = AESGCM.GcmEncrypt(readBytes, fileKey, nonce, tag); writer.Write(nonce, 0, 12); writer.Write(encrypted, 0, encrypted.Length); writer.Write(tag, 0, 16); } } }
private async void AddChatButton_OnClick(object sender, RoutedEventArgs e) { var dialog = new AddChatDialog(); string username = "", name = ""; byte delegateResult = 0; // 0 is running, 1 is done, 2 is canceled Exception error = null; await DialogHost.Show(dialog, "MainWindowDialogHost", async delegate(object sender1, DialogClosingEventArgs args) { if (!(bool)args.Parameter) { delegateResult = 2; // check if the dialog is canceled return; } username = dialog.IdTextBox.Text; // check if the ID exists in database var dbResult = await SharedStuff.Database.Table <DatabaseHelper.Users>().Where(user => user.Username == username) .ToArrayAsync(); if (dbResult.Length == 0) // get the user's key from server { NameValueCollection queryString = HttpUtility.ParseQueryString(string.Empty); queryString.Add("username", username); string url = SharedStuff.CreateUrlWithQuery( "https://" + Properties.Settings.Default.ServerAddress + "/users/getData", queryString); string result; try { using (WebClient wc = new WebClient()) // request other user's public key result = await wc.DownloadStringTaskAsync(url); } catch (Exception ex) { error = new Exception("Cannot connect to server", ex); delegateResult = 1; return; } // check if the username exists try { var data = JsonConvert.DeserializeObject <JsonTypes.UserDataStruct>(result); if (data.PublicKey == "") { throw new Exception("User is not logged in."); } string key = Convert.ToBase64String(SharedStuff.Curve.calculateAgreement( Convert.FromBase64String(_savedData.PrivateKey), Convert.FromBase64String(data.PublicKey))); await SharedStuff.Database.InsertAsync( new DatabaseHelper.Users // save the key in database { Username = data.Username, Name = data.Name, Key = key, }); name = data.Name; } catch (Exception) { var data = JsonConvert.DeserializeObject <JsonTypes.ServerStatus>(result); error = new Exception("Server returned an error", new Exception(data.Message)); } finally { delegateResult = 1; } } }); // wait until the results are gathered while (delegateResult == 0) { await Task.Delay(50); } if (delegateResult == 2) { return; } if (error != null) { var errDialog = new ErrorDialogSample { ErrorTitle = { Text = error.Message }, ErrorText = { Text = error.InnerException.Message } }; await DialogHost.Show(errDialog, "MainWindowDialogHost"); return; } // check if messages list does not contain this user if (MessagesList.All(x => x.Username != username)) { MessagesList.Insert(0, new MainMessagesNotify { FullDate = DateTime.Now, IsLastMessageForUser = false, Message = "", Name = name, Username = username }); } // open the chat page OpenWindowsList.Add(username, new ChatWindow(username)); OpenWindowsList[username].Show(); }
private async void WS_OnMessage(object sender, MessageEventArgs e) { // at first try to parse the message as Server status. try { var status = JsonConvert.DeserializeObject <JsonTypes.MessageStatus>(e.Data); if (status.Id != null) // a real message status { if (!status.Ok) { Console.WriteLine("Error on message " + status.Id + ": " + status.Message); } if (status.Message != "sent") // this is a file token update { SharedStuff.PendingMessages[status.Id].Token = status.Message; // now start upload SharedStuff.UploadFile(status.Id); } else { SharedStuff.PendingMessages[status.Id].Sent = status.Ok ? (byte)0 : (byte)2; // Do not remove files from here; They are removed at "finally" block of SharedStuff.UploadFile if (SharedStuff.PendingMessages[status.Id].Type != 1) { SharedStuff.PendingMessages.Remove(status.Id); } } return; } if (status.Message != null && !status.Ok) { // TODO: HANDLE THIS SHIT return; } } catch (Exception) { // ignored : the type might be something else } // try ClientUpdateTypeStruct await _mu.WaitAsync(); // get the data in order that server sends them string nameSafe = ""; try { var msg = JsonConvert.DeserializeObject <JsonTypes.Updates>(e.Data); var keyList = await SharedStuff.Database.Table <DatabaseHelper.Users>().Where(k => k.Username == msg.Payload.From) .ToArrayAsync(); // get the key of the user string key; if (keyList.Length == 0) // Key agreement { var data = await SharedStuff.GetUserData(msg.Payload.From); key = Convert.ToBase64String(SharedStuff.Curve.calculateAgreement( Convert.FromBase64String(_savedData.PrivateKey), Convert.FromBase64String(data.PublicKey))); await SharedStuff.Database.InsertAsync(new DatabaseHelper.Users // save the key in database { Username = data.Username, Name = data.Name, Key = key, }); nameSafe = data.Name; } else // get the key from database; There is only one row because Username is unique { key = keyList[0].Key; } string toShowOnMainMenu = ""; JsonTypes.FilePayload filePayload = new JsonTypes.FilePayload(); switch (msg.Type) { case 0: // Text message // decrypt the message or assign the file token try { string message = BouncyCastleHelper.AesGcmDecrypt(msg.Payload.Message, key); await SharedStuff.Database.InsertAsync(new DatabaseHelper.Messages { Username = msg.Payload.From, Type = msg.Type, Date = msg.Payload.Date, Payload = message }); toShowOnMainMenu = message; } catch (Exception) { // TODO: RE-REQUEST public key } break; case 1: // File message filePayload = JsonConvert.DeserializeObject <JsonTypes.FilePayload>(msg.Payload.Message); await SharedStuff.Database.RunInTransactionAsync(trans => { trans.Insert(new DatabaseHelper.Messages { Username = msg.Payload.From, Type = msg.Type, Date = msg.Payload.Date, Payload = filePayload.Token }); trans.Insert(new DatabaseHelper.Files { Token = filePayload.Token, Name = filePayload.FileName }); }); toShowOnMainMenu = filePayload.FileName; break; } // save the value bool inserted = false, open = OpenWindowsList.ContainsKey(msg.Payload.From); int index = -1; try { index = MessagesList.IndexOf(MessagesList.First(x => x.Username == msg.Payload.From)); // get index of the UI if (index == -1) { throw new Exception(); } } catch (Exception) { string name = nameSafe; if (name == "") { name = (await SharedStuff.GetUserData(msg.Payload.From)).Name; } Application.Current.Dispatcher.Invoke(delegate { MessagesList.Insert(0, new MainMessagesNotify { FullDate = msg.Payload.Date, Message = toShowOnMainMenu, IsLastMessageForUser = false, Name = name, NewMessages = 1, Username = msg.Payload.From }); }); inserted = true; } if (index == -1) { return; } if (!open) // increase unread messages { await SharedStuff.Database.ExecuteAsync("UPDATE Users SET UnreadMessages = ? WHERE Username = ?", MessagesList[index].NewMessages + 1, msg.Payload.From); } if (!inserted) { Application.Current.Dispatcher.Invoke(delegate { // Show the user the update MessagesList.Move(index, 0); MessagesList[0].FullDate = msg.Payload.Date; MessagesList[0].Message = toShowOnMainMenu; MessagesList[0].IsLastMessageForUser = false; if (!open) { MessagesList[0].NewMessages++; } }); } // update the open windows if (open) { if (msg.Type == 0) // add text messages { OpenWindowsList[msg.Payload.From].AddMessageText(toShowOnMainMenu, msg.Payload.Date); } else if (msg.Type == 1) // add files { OpenWindowsList[msg.Payload.From].AddMessageFile(filePayload.Token, filePayload.FileName, msg.Payload.Date); } } } catch (Exception ex) { // TODO: HANDLE THIS SHIT } finally { _mu.Release(); } }