/// <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();
            }
        }