/// <summary> /// Code which is actually run on the generation thread. /// </summary> private void GenerationThreadProcess() { Bip38Intermediate intermediate = null; if (GenChoice == GenChoices.Encrypted) { intermediate = new Bip38Intermediate(UserText, Bip38Intermediate.Interpretation.Passphrase); } int detcount = 1; while (RemainingToGenerate > 0 && StopRequested == false) { KeyCollectionItem newitem = null; switch (GenChoice) { case GenChoices.Minikey: MiniKeyPair mkp = MiniKeyPair.CreateRandom(ExtraEntropy.GetEntropy()); string s = mkp.AddressBase58; // read the property to entice it to compute everything newitem = new KeyCollectionItem(mkp); break; case GenChoices.WIF: KeyPair kp = KeyPair.Create(ExtraEntropy.GetEntropy()); s = kp.AddressBase58; newitem = new KeyCollectionItem(kp); break; case GenChoices.Deterministic: kp = KeyPair.CreateFromString(UserText + detcount); detcount++; s = kp.AddressBase58; newitem = new KeyCollectionItem(kp); break; case GenChoices.Encrypted: Bip38KeyPair ekp = new Bip38KeyPair(intermediate); newitem = new KeyCollectionItem(ekp); break; case GenChoices.TwoFactor: ekp = new Bip38KeyPair(intermediatesForGeneration[intermediateIdx++]); if (intermediateIdx >= intermediatesForGeneration.Length) { intermediateIdx = 0; } newitem = new KeyCollectionItem(ekp); break; } lock (GeneratedItems) { GeneratedItems.Add(newitem); RemainingToGenerate--; } } GeneratingEnded = true; }
private void btnEncode_Click(object sender, EventArgs e) { if ((txtPassphrase.Text ?? "") == "") { MessageBox.Show("Enter a passphrase first."); return; } try { Bip38Intermediate intermediate = new Bip38Intermediate(txtPassphrase.Text, Bip38Intermediate.Interpretation.Passphrase); txtPassphraseCode.Text = intermediate.Code; } catch (Exception ae) { MessageBox.Show(ae.Message); } }
private void btnConfirm_Click(object sender, EventArgs e) { lblAddressHeader.Visible = false; lblAddressItself.Visible = false; lblResult.Visible = false; // check for null entry if (txtPassphrase.Text == "") { MessageBox.Show("Passphrase is required.", "Passphrase required", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } if (txtConfCode.Text == "") { MessageBox.Show("Confirmation code is required.", "Confirmation code required", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } // Parse confirmation code. byte[] confbytes = Util.Base58CheckToByteArray(txtConfCode.Text.Trim()); if (confbytes == null) { // is it even close? if (txtConfCode.Text.StartsWith("cfrm38")) { MessageBox.Show("This is not a valid confirmation code. It has the right prefix, but " + "doesn't contain valid confirmation data. Possible typo or incomplete?", "Invalid confirmation code", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } MessageBox.Show("This is not a valid confirmation code.", "Invalid confirmation code", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } if (confbytes.Length != 51 || confbytes[0] != 0x64 || confbytes[1] != 0x3B || confbytes[2] != 0xF6 || confbytes[3] != 0xA8 || confbytes[4] != 0x9A || confbytes[18] < 0x02 || confbytes[18] > 0x03) { // Unrecognized Base58 object. Do we know what this is? Tell the user. object result = StringInterpreter.Interpret(txtConfCode.Text.Trim()); if (result != null) { // did we actually get an encrypted private key? if so, just try to decrypt it. if (result is PassphraseKeyPair) { PassphraseKeyPair ppkp = result as PassphraseKeyPair; if (ppkp.DecryptWithPassphrase(txtPassphrase.Text)) { confirmIsValid(ppkp.GetAddress().AddressBase58); MessageBox.Show("What you provided contains a private key, not just a confirmation. " + "Confirmation is successful, and with this correct passphrase, " + "you are also able to spend the funds from the address.", "This is actually a private key", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } else { MessageBox.Show("This is not a valid confirmation code. It looks like an " + "encrypted private key. Decryption was attempted but the passphrase couldn't decrypt it", "Invalid confirmation code", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } } string objectKind = result.GetType().Name; if (objectKind == "AddressBase") { objectKind = "an Address"; } else { objectKind = "a " + objectKind; } MessageBox.Show("This is not a valid confirmation code. Instead, it looks like " + objectKind + ". Perhaps you entered the wrong thing? Confirmation codes " + "start with \"cfrm\".", "Invalid confirmation code", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } MessageBox.Show("This is not a valid confirmation code.", "Invalid confirmation code", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } // extract ownersalt and get an intermediate byte[] ownersalt = new byte[8]; Array.Copy(confbytes, 10, ownersalt, 0, 8); bool includeHashStep = (confbytes[5] & 0x04) == 0x04; Bip38Intermediate intermediate = new Bip38Intermediate(txtPassphrase.Text, ownersalt, includeHashStep); // derive the 64 bytes we need // get ECPoint from passpoint PublicKey pk = new PublicKey(intermediate.passpoint); byte[] addresshashplusownersalt = new byte[12]; Array.Copy(confbytes, 6, addresshashplusownersalt, 0, 4); Array.Copy(intermediate.ownerentropy, 0, addresshashplusownersalt, 4, 8); // derive encryption key material byte[] derived = new byte[64]; CryptSharp.Utility.SCrypt.ComputeKey(intermediate.passpoint, addresshashplusownersalt, 1024, 1, 1, 1, derived); byte[] derivedhalf2 = new byte[32]; Array.Copy(derived, 32, derivedhalf2, 0, 32); byte[] unencryptedpubkey = new byte[33]; // recover the 0x02 or 0x03 prefix unencryptedpubkey[0] = (byte)(confbytes[18] ^ (derived[63] & 0x01)); // decrypt var aes = Aes.Create(); aes.KeySize = 256; aes.Mode = CipherMode.ECB; aes.Key = derivedhalf2; ICryptoTransform decryptor = aes.CreateDecryptor(); decryptor.TransformBlock(confbytes, 19, 16, unencryptedpubkey, 1); decryptor.TransformBlock(confbytes, 19, 16, unencryptedpubkey, 1); decryptor.TransformBlock(confbytes, 19 + 16, 16, unencryptedpubkey, 17); decryptor.TransformBlock(confbytes, 19 + 16, 16, unencryptedpubkey, 17); // xor out the padding for (int i = 0; i < 32; i++) { unencryptedpubkey[i + 1] ^= derived[i]; } // reconstitute the ECPoint var ps = Org.BouncyCastle.Asn1.Sec.SecNamedCurves.GetByName("secp256k1"); ECPoint point; try { point = ps.Curve.DecodePoint(unencryptedpubkey); // multiply passfactor. Result is going to be compressed. ECPoint pubpoint = point.Multiply(new BigInteger(1, intermediate.passfactor)); // Do we want it uncompressed? then we will have to uncompress it. byte flagbyte = confbytes[5]; if ((flagbyte & 0x20) == 0x00) { pubpoint = ps.Curve.CreatePoint(pubpoint.X.ToBigInteger(), pubpoint.Y.ToBigInteger(), false); } // Convert to bitcoin address and check address hash. PublicKey generatedaddress = new PublicKey(pubpoint); // get addresshash UTF8Encoding utf8 = new UTF8Encoding(false); Sha256Digest sha256 = new Sha256Digest(); byte[] generatedaddressbytes = utf8.GetBytes(generatedaddress.AddressBase58); sha256.BlockUpdate(generatedaddressbytes, 0, generatedaddressbytes.Length); byte[] addresshashfull = new byte[32]; sha256.DoFinal(addresshashfull, 0); sha256.BlockUpdate(addresshashfull, 0, 32); sha256.DoFinal(addresshashfull, 0); for (int i = 0; i < 4; i++) { if (addresshashfull[i] != confbytes[i + 6]) { MessageBox.Show("This passphrase is wrong or does not belong to this confirmation code.", "Invalid passphrase", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } } confirmIsValid(generatedaddress.AddressBase58); } catch { // Might throw an exception - not every 256-bit integer is a valid X coordinate MessageBox.Show("This passphrase is wrong or does not belong to this confirmation code.", "Invalid passphrase", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } }
private void btnConfirm_Click(object sender, EventArgs e) { lblAddressHeader.Visible = false; lblAddressItself.Visible = false; lblResult.Visible = false; // check for null entry if (txtPassphrase.Text == "") { MessageBox.Show("Passphrase is required.", "Passphrase required", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } if (txtConfCode.Text == "") { MessageBox.Show("Confirmation code is required.", "Confirmation code required", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } // Parse confirmation code. byte[] confbytes = Bitcoin.Base58CheckToByteArray(txtConfCode.Text.Trim()); if (confbytes == null) { // is it even close? if (txtConfCode.Text.StartsWith("cfrm38")) { MessageBox.Show("This is not a valid confirmation code. It has the right prefix, but " + "doesn't contain valid confirmation data. Possible typo or incomplete?", "Invalid confirmation code", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } MessageBox.Show("This is not a valid confirmation code.", "Invalid confirmation code", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } if (confbytes.Length != 51 || confbytes[0] != 0x64 || confbytes[1] != 0x3B || confbytes[2] != 0xF6 || confbytes[3] != 0xA8 || confbytes[4] != 0x9A || confbytes[18] < 0x02 || confbytes[18] > 0x03) { // Unrecognized Base58 object. Do we know what this is? Tell the user. object result = StringInterpreter.Interpret(txtConfCode.Text.Trim()); if (result != null) { // did we actually get an encrypted private key? if so, just try to decrypt it. if (result is PassphraseKeyPair) { PassphraseKeyPair ppkp = result as PassphraseKeyPair; if (ppkp.DecryptWithPassphrase(txtPassphrase.Text)) { confirmIsValid(ppkp.GetAddress().AddressBase58); MessageBox.Show("What you provided contains a private key, not just a confirmation. " + "Confirmation is successful, and with this correct passphrase, " + "you are also able to spend the funds from the address.", "This is actually a private key", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } else { MessageBox.Show("This is not a valid confirmation code. It looks like an " + "encrypted private key. Decryption was attempted but the passphrase couldn't decrypt it", "Invalid confirmation code", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } } string objectKind = result.GetType().Name; if (objectKind == "AddressBase") { objectKind = "an Address"; } else { objectKind = "a " + objectKind; } MessageBox.Show("This is not a valid confirmation code. Instead, it looks like " + objectKind + ". Perhaps you entered the wrong thing? Confirmation codes " + "start with \"cfrm\".", "Invalid confirmation code", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } MessageBox.Show("This is not a valid confirmation code.", "Invalid confirmation code", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } // extract ownersalt and get an intermediate byte[] ownersalt = new byte[8]; Array.Copy(confbytes, 10, ownersalt, 0, 8); Bip38Intermediate intermediate = new Bip38Intermediate(txtPassphrase.Text, ownersalt); // derive the 64 bytes we need // get ECPoint from passpoint PublicKey pk = new PublicKey(intermediate.passpoint); byte[] addresshashplusownersalt = new byte[12]; Array.Copy(confbytes, 6, addresshashplusownersalt, 0, 4); Array.Copy(intermediate.ownersalt, 0, addresshashplusownersalt, 4, 8); // derive encryption key material byte[] derived = new byte[64]; SCrypt.ComputeKey(intermediate.passpoint, addresshashplusownersalt, 1024, 1, 1, 1, derived); byte[] derivedhalf2 = new byte[32]; Array.Copy(derived, 32, derivedhalf2, 0, 32); byte[] unencryptedpubkey = new byte[33]; // recover the 0x02 or 0x03 prefix unencryptedpubkey[0] = (byte)(confbytes[18] ^ (derived[63] & 0x01)); // decrypt AesCryptoServiceProvider aes = new AesCryptoServiceProvider(); aes.KeySize = 256; aes.Mode = CipherMode.ECB; aes.Key = derivedhalf2; ICryptoTransform decryptor = aes.CreateDecryptor(); decryptor.TransformBlock(confbytes, 19, 16, unencryptedpubkey, 1); decryptor.TransformBlock(confbytes, 19, 16, unencryptedpubkey, 1); decryptor.TransformBlock(confbytes, 19 + 16, 16, unencryptedpubkey, 17); decryptor.TransformBlock(confbytes, 19 + 16, 16, unencryptedpubkey, 17); // xor out the padding for (int i = 0; i < 32; i++) unencryptedpubkey[i + 1] ^= derived[i]; // reconstitute the ECPoint var ps = Org.BouncyCastle.Asn1.Sec.SecNamedCurves.GetByName("secp256k1"); ECPoint point; try { point = ps.Curve.DecodePoint(unencryptedpubkey); // multiply passfactor. Result is going to be compressed. ECPoint pubpoint = point.Multiply(new BigInteger(1, intermediate.passfactor)); // Do we want it uncompressed? then we will have to uncompress it. byte flagbyte = confbytes[5]; if ((flagbyte & 0x20) == 0x00) { pubpoint = ps.Curve.CreatePoint(pubpoint.X.ToBigInteger(), pubpoint.Y.ToBigInteger(), false); } // Convert to bitcoin address and check address hash. PublicKey generatedaddress = new PublicKey(pubpoint); // get addresshash UTF8Encoding utf8 = new UTF8Encoding(false); Sha256Digest sha256 = new Sha256Digest(); byte[] generatedaddressbytes = utf8.GetBytes(generatedaddress.AddressBase58); sha256.BlockUpdate(generatedaddressbytes, 0, generatedaddressbytes.Length); byte[] addresshashfull = new byte[32]; sha256.DoFinal(addresshashfull, 0); sha256.BlockUpdate(addresshashfull, 0, 32); sha256.DoFinal(addresshashfull, 0); for (int i = 0; i < 4; i++) { if (addresshashfull[i] != confbytes[i + 6]) { MessageBox.Show("This passphrase is wrong or does not belong to this confirmation code.", "Invalid passphrase", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } } confirmIsValid(generatedaddress.AddressBase58); } catch { // Might throw an exception - not every 256-bit integer is a valid X coordinate MessageBox.Show("This passphrase is wrong or does not belong to this confirmation code.", "Invalid passphrase", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } }
private void btnGenerateAddresses_Click(object sender, EventArgs e) { if (Generating) { StopRequested = true; btnGenerateAddresses.Text = "Stopping..."; return; } if (rdoEncrypted.Checked && txtTextInput.Text == "") { MessageBox.Show("An encryption passphrase is required. Choose a different option if you don't want encrypted keys.", "Passphrase missing", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } if (rdoDeterministicWallet.Checked && txtTextInput.Text == "") { MessageBox.Show("A deterministic seed is required. If you do not intend to create a deterministic " + "wallet or know what one is used for, it is recommended you choose one of the other options. An inappropriate seed can result " + "in the unexpected theft of funds.", "Seed missing", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } GenerationThread = new Thread(new ThreadStart(GenerationThreadProcess)); RemainingToGenerate = (int)numGenCount.Value; UserText = txtTextInput.Text; RetainPrivateKeys = chkRetainPrivKey.Checked; if (rdoDeterministicWallet.Checked) GenChoice = GenChoices.Deterministic; if (rdoEncrypted.Checked) { GenChoice = GenChoices.Encrypted; // intermediate codes start with "passphrasek" thru "passphrases" string ti = txtTextInput.Text.Trim(); if (txtTextInput.Text.Length > 40 && ti.CompareTo("passphrasek") > 0 && ti.CompareTo("passphraset") < 0) { Bip38Intermediate inter = null; // try using it as an intermediate try { inter = new Bip38Intermediate(txtTextInput.Text.Trim(), Bip38Intermediate.Interpretation.IntermediateCode); // if this is an actual intermediate code, ensure surrounding whitespace isn't preserved. txtTextInput.Text = txtTextInput.Text.Trim(); } catch { var r = MessageBox.Show("The passphrase resembles an Intermediate Code, but isn't one. " + "If this is supposed to be an intermediate code, it is invalid, malformed, or has an error. " + "If you're attempting to generate from an intermediate code, the resulting keys will not work as expected. " + "Do you want to continue?", "Invalid intermediate code", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning); if (r == System.Windows.Forms.DialogResult.Cancel) { return; } } if (inter != null) { MessageBox.Show("Intermediate Code accepted. " + "Intermediate Codes can be used for generating encrypted keys, but not for decrypting them. " + "You will only be able to decrypt generated keys with " + "the original passphrase that was used to create the Intermediate Code.", "Intermediate Code accepted", MessageBoxButtons.OK, MessageBoxIcon.Information); } } txtTextInput.UseSystemPasswordChar = true; } if (rdoMiniKeys.Checked) GenChoice = GenChoices.Minikey; if (rdoRandomWallet.Checked) GenChoice = GenChoices.WIF; timer1.Interval = 250; timer1.Enabled = true; Generating = true; GeneratingEnded = false; StopRequested = false; btnGenerateAddresses.Text = "Cancel"; SetControlsEnabled(false); toolStripProgressBar1.Visible = true; GenerationThread.Start(); }
/// <summary> /// Code which is actually run on the generation thread. /// </summary> private void GenerationThreadProcess() { Bip38Intermediate intermediate = null; if (GenChoice == GenChoices.Encrypted) { try { intermediate = new Bip38Intermediate(txtTextInput.Text, Bip38Intermediate.Interpretation.IntermediateCode); } catch { } // sink exceptions - just means it's not an intermediate code if (intermediate == null) { intermediate = new Bip38Intermediate(UserText, Bip38Intermediate.Interpretation.Passphrase); } } int detcount = 1; while (RemainingToGenerate > 0 && StopRequested == false) { KeyCollectionItem newitem = null; switch (GenChoice) { case GenChoices.Minikey: MiniKeyPair mkp = MiniKeyPair.CreateRandom(ExtraEntropy.GetEntropy()); string s = mkp.AddressBase58; // read the property to entice it to compute everything newitem = new KeyCollectionItem(mkp); break; case GenChoices.WIF: KeyPair kp = KeyPair.Create(ExtraEntropy.GetEntropy()); s = kp.AddressBase58; newitem = new KeyCollectionItem(kp); break; case GenChoices.Deterministic: kp = KeyPair.CreateFromString(UserText + detcount); detcount++; s = kp.AddressBase58; newitem = new KeyCollectionItem(kp); break; case GenChoices.Encrypted: Bip38KeyPair ekp = new Bip38KeyPair(intermediate); newitem = new KeyCollectionItem(ekp); break; } lock (GeneratedItems) { GeneratedItems.Add(newitem); RemainingToGenerate--; } } GeneratingEnded = true; }