diff --git a/FireWallet/FireWallet.csproj b/FireWallet/FireWallet.csproj index 066fdc3..7e97c3b 100644 --- a/FireWallet/FireWallet.csproj +++ b/FireWallet/FireWallet.csproj @@ -30,6 +30,7 @@ + diff --git a/FireWallet/MainForm.Designer.cs b/FireWallet/MainForm.Designer.cs index 2e25f73..1a5dc20 100644 --- a/FireWallet/MainForm.Designer.cs +++ b/FireWallet/MainForm.Designer.cs @@ -95,6 +95,7 @@ namespace FireWallet textBoxReceiveAddress = new TextBox(); labelReceive1 = new Label(); panelDomains = new Panel(); + labelDomainSort = new Label(); comboBoxDomainSort = new ComboBox(); buttonExportDomains = new Button(); groupBoxDomains = new GroupBox(); @@ -103,6 +104,7 @@ namespace FireWallet textBoxDomainSearch = new TextBox(); panelSettings = new Panel(); groupBoxSettingsWallet = new GroupBox(); + buttonSettingsYubikey = new Button(); buttonSettingsRescan = new Button(); buttonSeed = new Button(); groupBoxSettingsMisc = new GroupBox(); @@ -123,7 +125,6 @@ namespace FireWallet textBoxExAddr = new TextBox(); labelSettings4 = new Label(); textBoxExTX = new TextBox(); - labelDomainSort = new Label(); statusStripmain.SuspendLayout(); panelaccount.SuspendLayout(); groupBoxaccount.SuspendLayout(); @@ -241,7 +242,7 @@ namespace FireWallet // panelaccount.BackColor = Color.Transparent; panelaccount.Controls.Add(groupBoxaccount); - panelaccount.Location = new Point(1082, 211); + panelaccount.Location = new Point(132, 30); panelaccount.Name = "panelaccount"; panelaccount.Size = new Size(1074, 642); panelaccount.TabIndex = 1; @@ -574,7 +575,7 @@ namespace FireWallet panelSend.Controls.Add(labelSendingTo); panelSend.Controls.Add(labelSendPrompt); panelSend.Controls.Add(labelHIPArrow); - panelSend.Location = new Point(138, 33); + panelSend.Location = new Point(880, 441); panelSend.Name = "panelSend"; panelSend.Size = new Size(974, 521); panelSend.TabIndex = 2; @@ -792,12 +793,22 @@ namespace FireWallet panelDomains.Controls.Add(groupBoxDomains); panelDomains.Controls.Add(labelDomainSearch); panelDomains.Controls.Add(textBoxDomainSearch); - panelDomains.Location = new Point(120, 48); + panelDomains.Location = new Point(861, 364); panelDomains.Name = "panelDomains"; panelDomains.Size = new Size(920, 536); panelDomains.TabIndex = 18; panelDomains.Visible = false; // + // labelDomainSort + // + labelDomainSort.AutoSize = true; + labelDomainSort.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point); + labelDomainSort.Location = new Point(638, 15); + labelDomainSort.Name = "labelDomainSort"; + labelDomainSort.Size = new Size(42, 21); + labelDomainSort.TabIndex = 12; + labelDomainSort.Text = "Sort:"; + // // comboBoxDomainSort // comboBoxDomainSort.DropDownStyle = ComboBoxStyle.DropDownList; @@ -871,7 +882,7 @@ namespace FireWallet panelSettings.Controls.Add(buttonSettingsSave); panelSettings.Controls.Add(groupBoxSettingsExplorer); panelSettings.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point); - panelSettings.Location = new Point(1065, 51); + panelSettings.Location = new Point(848, 306); panelSettings.Name = "panelSettings"; panelSettings.Size = new Size(930, 550); panelSettings.TabIndex = 19; @@ -879,6 +890,7 @@ namespace FireWallet // // groupBoxSettingsWallet // + groupBoxSettingsWallet.Controls.Add(buttonSettingsYubikey); groupBoxSettingsWallet.Controls.Add(buttonSettingsRescan); groupBoxSettingsWallet.Controls.Add(buttonSeed); groupBoxSettingsWallet.Location = new Point(507, 16); @@ -888,6 +900,17 @@ namespace FireWallet groupBoxSettingsWallet.TabStop = false; groupBoxSettingsWallet.Text = "Wallet Controls"; // + // buttonSettingsYubikey + // + buttonSettingsYubikey.FlatStyle = FlatStyle.Flat; + buttonSettingsYubikey.Location = new Point(6, 133); + buttonSettingsYubikey.Name = "buttonSettingsYubikey"; + buttonSettingsYubikey.Size = new Size(98, 50); + buttonSettingsYubikey.TabIndex = 9; + buttonSettingsYubikey.Text = "YubiKey"; + buttonSettingsYubikey.UseVisualStyleBackColor = true; + buttonSettingsYubikey.Click += buttonSettingsYubikey_Click; + // // buttonSettingsRescan // buttonSettingsRescan.FlatStyle = FlatStyle.Flat; @@ -1081,25 +1104,15 @@ namespace FireWallet textBoxExTX.Size = new Size(307, 29); textBoxExTX.TabIndex = 1; // - // labelDomainSort - // - labelDomainSort.AutoSize = true; - labelDomainSort.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point); - labelDomainSort.Location = new Point(638, 15); - labelDomainSort.Name = "labelDomainSort"; - labelDomainSort.Size = new Size(42, 21); - labelDomainSort.TabIndex = 12; - labelDomainSort.Text = "Sort:"; - // // MainForm // AutoScaleDimensions = new SizeF(7F, 15F); AutoScaleMode = AutoScaleMode.Font; ClientSize = new Size(1152, 575); + Controls.Add(panelaccount); + Controls.Add(panelSettings); Controls.Add(panelDomains); Controls.Add(panelSend); - Controls.Add(panelSettings); - Controls.Add(panelaccount); Controls.Add(panelPortfolio); Controls.Add(panelRecieve); Controls.Add(panelNav); @@ -1234,5 +1247,6 @@ namespace FireWallet private Label labelSendingHIPAddress; private ComboBox comboBoxDomainSort; private Label labelDomainSort; + private Button buttonSettingsYubikey; } } \ No newline at end of file diff --git a/FireWallet/MainForm.cs b/FireWallet/MainForm.cs index 67188fa..0bef43a 100644 --- a/FireWallet/MainForm.cs +++ b/FireWallet/MainForm.cs @@ -7,13 +7,14 @@ using QRCoder; using System.Text.RegularExpressions; using System.Security.Cryptography; using System.Text; -using System.Security.Policy; -using System.Windows.Forms; using System.Net; using DnsClient; using DnsClient.Protocol; using System.Security.Cryptography.X509Certificates; using System.Net.Security; +// Used to use Yubikey to login +using Yubico.YubiKey; +using Yubico.YubiKey.Piv; namespace FireWallet { @@ -700,7 +701,27 @@ namespace FireWallet } account = comboBoxaccount.Text; - password = textBoxaccountpassword.Text; + + if (textBoxaccountpassword.Text == "") + { + if (File.Exists(dir + account + ".yubikey")) + { + // Check if yubikey is plugged in + var devices = YubiKeyDevice.FindAll(); + if (devices.Count() > 0) + { + // Get key from yubikey + password = YubiUnlock(); + } + + } + + } else password = textBoxaccountpassword.Text; + + + + + bool loggedin = await Login(); if (loggedin) { @@ -2246,5 +2267,155 @@ namespace FireWallet { UpdateDomains(); } + #region yubikey + static bool PinSubmitter(KeyEntryData pin) + { + string s = "123456"; + var s_b = Encoding.UTF8.GetBytes(s); + pin.SubmitValue(s_b); + return true; + } + private void buttonSettingsYubikey_Click(object sender, EventArgs e) + { + if (password.Length < 0) + { + return; + } + + NotifyForm notifyForm = new NotifyForm("Insert Yubikey\nThis will use your yubikey to encrypt your account password."); + notifyForm.ShowDialog(); + notifyForm.Dispose(); + + NotifyForm yubiLoadingForm = new NotifyForm("Encrypting. . .", false); + yubiLoadingForm.Show(); + // Wait for the form to load + Application.DoEvents(); + + try + { + //Assumes there is exactly one yubikey connected and it has a RSA2048 certificate in slot 9d + //PIV PIN is assumed to be 123456 + var devices = YubiKeyDevice.FindAll(); + var ykDevice = devices.First(); + PivSession piv = new(ykDevice); + + piv.KeyCollector += PinSubmitter; + piv.VerifyPin(); + + var slot = PivSlot.KeyManagement; + + X509Certificate2 cert = piv.GetCertificate(slot); + if (cert.SignatureAlgorithm.FriendlyName != "sha256RSA") + throw new CryptographicException("Certificate must be RSA with SHA256"); + + var publicKey = cert.GetRSAPublicKey() ?? throw new CryptographicException("Couldn't get public key from certificate."); + + Aes aesFirst = Aes.Create(); + + var encryptedKey = publicKey.Encrypt(aesFirst.Key, RSAEncryptionPadding.Pkcs1); + var decryptedKey = piv.Decrypt(slot, encryptedKey); + + //MessageBox.Show($"aesFirst.Key.Length: {aesFirst.Key.Length}"); + //MessageBox.Show($"encryptedKey.Length: {encryptedKey.Length}"); + //MessageBox.Show($"decryptedKey.Length: {decryptedKey.Length}"); + + // split the message into blocks of 128 bytes + + string message = password; + int blockSize = 128; + int blockCount = (int)Math.Ceiling((double)message.Length / blockSize); + + string[] strings = new string[blockCount]; + FileStream sw = new FileStream(dir + account + ".yubikey", FileMode.Create, FileAccess.Write); + + for (int i = 0; i < blockCount; i++) + { + int size = Math.Min(blockSize, message.Length - i * blockSize); + strings[i] = message.Substring(i * blockSize, size); + + byte[] bytes = Encoding.ASCII.GetBytes(strings[i]); + var encryptedBytes = publicKey.Encrypt(bytes, RSAEncryptionPadding.Pkcs1); + sw.Write(encryptedBytes, 0, encryptedBytes.Length); + } + sw.Close(); + sw.Dispose(); + + } + catch (Exception ex) + { + AddLog(ex.Message); + } + + yubiLoadingForm.CloseNotification(); + } + private string YubiUnlock() + { + NotifyForm notifyForm = new NotifyForm("Insert Yubikey to unlock"); + notifyForm.ShowDialog(); + notifyForm.Dispose(); + NotifyForm yubiLoadingForm = new NotifyForm("Decrypting. . .", false); + yubiLoadingForm.Show(); + // Wait for the form to load + Application.DoEvents(); + try + { + + //Assumes there is exactly one yubikey connected and it has a RSA2048 certificate in slot 9d + //PIV PIN is assumed to be 123456 + var devices = YubiKeyDevice.FindAll(); + var ykDevice = devices.First(); + PivSession piv = new(ykDevice); + + piv.KeyCollector += PinSubmitter; + piv.VerifyPin(); + + var slot = PivSlot.KeyManagement; + + X509Certificate2 cert = piv.GetCertificate(slot); + if (cert.SignatureAlgorithm.FriendlyName != "sha256RSA") + throw new CryptographicException("Certificate must be RSA with SHA256"); + + var publicKey = cert.GetRSAPublicKey() ?? throw new CryptographicException("Couldn't get public key from certificate."); + + Aes aesFirst = Aes.Create(); + + var encryptedKey = publicKey.Encrypt(aesFirst.Key, RSAEncryptionPadding.Pkcs1); + var decryptedKey = piv.Decrypt(slot, encryptedKey); + + byte[] input = File.ReadAllBytes(dir + account + ".yubikey"); + + // decrypt the input + + int blockSize = 256; + int blockCount = (int)Math.Ceiling((double)input.Length / blockSize); + + byte[][] blocks = new byte[blockCount][]; + byte[] decripted = new byte[blockCount * blockSize]; + string output = ""; + for (int i = 0; i < blockCount; i++) + { + int size = Math.Min(blockSize, input.Length - i * blockSize); + blocks[i] = new byte[size]; + Array.Copy(input, i * blockSize, blocks[i], 0, size); + var paddedDecryptedBytes = piv.Decrypt(slot, blocks[i]); + byte[] decryptedBytes; + bool couldParse = Yubico.YubiKey.Cryptography.RsaFormat.TryParsePkcs1Decrypt(paddedDecryptedBytes, out decryptedBytes); + Array.Copy(decryptedBytes, 0, decripted, i * blockSize, decryptedBytes.Length); + + output += Encoding.ASCII.GetString(decryptedBytes); + } + yubiLoadingForm.CloseNotification(); + return output; + } + catch (Exception ex) + { + AddLog(ex.Message); + yubiLoadingForm.CloseNotification(); + return ""; + } + + } + + #endregion } } \ No newline at end of file