diff --git a/BtcAddress.csproj b/BtcAddress.csproj index 853c0be..b129049 100644 --- a/BtcAddress.csproj +++ b/BtcAddress.csproj @@ -63,6 +63,7 @@ + @@ -250,7 +251,6 @@ - PreserveNewest diff --git a/Forms/AddressGen.Designer.cs b/Forms/AddressGen.Designer.cs index 84908d0..90ce34f 100644 --- a/Forms/AddressGen.Designer.cs +++ b/Forms/AddressGen.Designer.cs @@ -226,7 +226,6 @@ partial class AddressGen { this.lblCoinType.Size = new System.Drawing.Size(51, 13); this.lblCoinType.TabIndex = 15; this.lblCoinType.Text = "Coin type"; - this.lblCoinType.Visible = false; // // cboCoinType // @@ -241,7 +240,6 @@ partial class AddressGen { this.cboCoinType.Size = new System.Drawing.Size(90, 21); this.cboCoinType.TabIndex = 23; this.cboCoinType.Text = "Bitcoin"; - this.cboCoinType.Visible = false; // // AddressGen // diff --git a/Forms/AddressGen.cs b/Forms/AddressGen.cs index 12f63e6..0b1922d 100644 --- a/Forms/AddressGen.cs +++ b/Forms/AddressGen.cs @@ -26,7 +26,6 @@ using System.Threading; using System.Diagnostics; using Casascius.Bitcoin; -using BtcAddress.Model; namespace BtcAddress.Forms { public partial class AddressGen : Form { @@ -40,7 +39,7 @@ private enum GenChoices { private GenChoices GenChoice; - private byte ChosenAddressVersion = 0; + private byte ChosenAddressType = 0; private bool Generating = false; private bool GeneratingEnded = false; @@ -67,8 +66,8 @@ private enum GenChoices { txtTextInput.Text = ""; txtTextInput.Visible = (rdoDeterministicWallet.Checked || rdoEncrypted.Checked); lblTextInput.Visible = (rdoDeterministicWallet.Checked || rdoEncrypted.Checked || rdoTwoFactor.Checked); - cboCoinType.Visible = rdoRandomWallet.Checked; - lblCoinType.Visible = rdoRandomWallet.Checked; + cboCoinType.Visible = rdoMiniKeys.Checked || rdoRandomWallet.Checked; + lblCoinType.Visible = rdoMiniKeys.Checked || rdoRandomWallet.Checked; if (rdoDeterministicWallet.Checked) { lblTextInput.Text = "Seed for deterministic generation"; @@ -158,22 +157,8 @@ private enum GenChoices { GenChoice = GenChoices.TwoFactor; } - switch (cboCoinType.SelectedItem.ToString()) - { - case "Namecoin": - ChosenAddressVersion = AddressVersion.Namecoin; - break; - case "Testnet": - ChosenAddressVersion = AddressVersion.Testnet; - break; - case "Litecoin": - ChosenAddressVersion = AddressVersion.Litecoin; - break; - default: - ChosenAddressVersion = AddressVersion.Bitcoin; - break; - } - + ChosenAddressType = AddressType.ToAddressType(cboCoinType.SelectedItem.ToString()); + timer1.Interval = 250; timer1.Enabled = true; Generating = true; @@ -218,12 +203,12 @@ private enum GenChoices { KeyCollectionItem newitem = null; switch (GenChoice) { case GenChoices.Minikey: - MiniKeyPair mkp = MiniKeyPair.CreateRandom(ExtraEntropy.GetEntropy()); + MiniKeyPair mkp = MiniKeyPair.CreateRandom(ExtraEntropy.GetEntropy(), ChosenAddressType); 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(), false, ChosenAddressVersion); + KeyPair kp = KeyPair.Create(ExtraEntropy.GetEntropy(), false, ChosenAddressType); s = kp.AddressBase58; newitem = new KeyCollectionItem(kp); break; diff --git a/Forms/Form1.cs b/Forms/Form1.cs index 3b932c9..510a1e9 100644 --- a/Forms/Form1.cs +++ b/Forms/Form1.cs @@ -39,7 +39,6 @@ using System.Drawing.Printing; using Casascius.Bitcoin; -using BtcAddress.Model; namespace BtcAddress { public partial class Form1 : Form { @@ -53,12 +52,16 @@ public partial class Form1 : Form { /// Item may represent an AddressBase, PublicKey, KeyPair, MiniKey, or an EncryptedKeyPair /// public void DisplayKeyCollectionItem(KeyCollectionItem item) { + if (item != null && item.Address != null) { + cboCoinType.Text = AddressType.FromAddressType(item.Address.AddressType); + } + try { ChangeFlag++; if (item.EncryptedKeyPair != null) { SetText(txtPrivWIF, item.EncryptedKeyPair.EncryptedPrivateKey); // blank out any validation info of the minikey - UpdateMinikeyDescription(); + UpdateMinikeyDescription(item.Address.AddressType); SetText(txtPassphrase, ""); if (item.EncryptedKeyPair.IsUnencryptedPrivateKeyAvailable()) { SetText(txtPrivHex, item.EncryptedKeyPair.GetUnencryptedPrivateKey().PublicKeyHex); @@ -90,7 +93,7 @@ public partial class Form1 : Form { } // update the label to indicate whether this is a valid minikey (or blank it out if n/a) - UpdateMinikeyDescription(); + UpdateMinikeyDescription(item.Address.AddressType); if (item.Address != null) { @@ -123,8 +126,8 @@ public partial class Form1 : Form { } - private void UpdateMinikeyDescription() { - int isminikey = MiniKeyPair.IsValidMiniKey(txtMinikey.Text); + private void UpdateMinikeyDescription(byte addressType) { + int isminikey = MiniKeyPair.IsValidMiniKey(txtMinikey.Text, addressType); if (isminikey == 1) { lblWhyNot.Visible = false; lblNotSafe.Visible = true; @@ -152,7 +155,7 @@ public partial class Form1 : Form { ChangeFlag++; try { SetText(txtPrivHex, RemoveSpacesIf(Util.PassphraseToPrivHex(txtMinikey.Text))); - UpdateMinikeyDescription(); + UpdateMinikeyDescription(0); btnPrivHexToWIF_Click(null, null); btnPrivToPub_Click(null, null); @@ -304,7 +307,7 @@ public partial class Form1 : Form { lblWhyNot.Visible = false; SetText(txtMinikey, ""); - KeyPair kp = KeyPair.Create(ExtraEntropy.GetEntropy(), compressToolStripMenuItem.Checked); + KeyPair kp = KeyPair.Create(ExtraEntropy.GetEntropy(), compressToolStripMenuItem.Checked, AddressType.ToAddressType(cboCoinType.Text)); if (txtPassphrase.Text != "") { SetText(txtPrivWIF, new Bip38KeyPair(kp, txtPassphrase.Text).EncryptedPrivateKey); @@ -396,12 +399,12 @@ public partial class Form1 : Form { private byte AddressTypeByte { get { - string cointype = cboCoinType.SelectedText.ToLowerInvariant(); + string cointype = cboCoinType.Text.ToLowerInvariant(); switch (cointype) { - case "bitcoin": return AddressVersion.Bitcoin; - case "namecoin": return AddressVersion.Namecoin; - case "testnet": return AddressVersion.Testnet; - case "litecoin": return AddressVersion.Litecoin; + case "bitcoin": return AddressType.Bitcoin; + case "namecoin": return AddressType.Namecoin; + case "testnet": return AddressType.Testnet; + case "litecoin": return AddressType.Litecoin; } byte b = 0; if (Byte.TryParse(cointype, out b)) return b; diff --git a/Forms/KeyCollectionView.cs b/Forms/KeyCollectionView.cs index 7be94fa..6cdbcde 100644 --- a/Forms/KeyCollectionView.cs +++ b/Forms/KeyCollectionView.cs @@ -28,7 +28,6 @@ using System.Xml.Serialization; using System.Drawing.Printing; using Casascius.Bitcoin; -using BtcAddress.Model; namespace BtcAddress.Forms { @@ -41,23 +40,23 @@ public partial class KeyCollectionView : Form { } private void newBtcAddressToolStripMenuItem_Click(object sender, EventArgs e) { - AddKey(AddressVersion.Bitcoin); + AddKey(AddressType.Bitcoin); } private void newTestnetAddressToolStripMenuItem_Click(object sender, EventArgs e) { - AddKey(AddressVersion.Testnet); + AddKey(AddressType.Testnet); } private void newNmcAddressToolStripMenuItem_Click(object sender, EventArgs e) { - AddKey(AddressVersion.Namecoin); + AddKey(AddressType.Namecoin); } private void newLtcAddressToolStripMenuItem_Click(object sender, EventArgs e) { - AddKey(AddressVersion.Litecoin); + AddKey(AddressType.Litecoin); } - private void AddKey(byte addressVersion) { - KeyPair kp = KeyPair.Create(ExtraEntropy.GetEntropy(), false, addressVersion); + private void AddKey(byte addressType) { + KeyPair kp = KeyPair.Create(ExtraEntropy.GetEntropy(), false, addressType); KeyCollectionItem item = new KeyCollectionItem(kp); KeyCollection.AddItem(item); } diff --git a/Model/AddressVersion.cs b/Model/AddressType.cs similarity index 52% rename from Model/AddressVersion.cs rename to Model/AddressType.cs index ae2bf50..39fc7f3 100644 --- a/Model/AddressVersion.cs +++ b/Model/AddressType.cs @@ -19,15 +19,44 @@ using System.Linq; using System.Text; -namespace BtcAddress.Model +namespace Casascius.Bitcoin { /// /// Class containing constant version numbers of different address types. /// - public class AddressVersion { + public static class AddressType { public const byte Bitcoin = 0; public const byte Litecoin = 48; public const byte Namecoin = 52; public const byte Testnet = 111; + + public static byte ToAddressType(string type) { + switch (type) + { + case "Namecoin": + return AddressType.Namecoin; + case "Testnet": + return AddressType.Testnet; + case "Litecoin": + return AddressType.Litecoin; + default: + return AddressType.Bitcoin; + } + } + + public static string FromAddressType(byte addressType) + { + switch (addressType) + { + case AddressType.Namecoin: + return "Namecoin"; + case AddressType.Testnet: + return "Testnet"; + case AddressType.Litecoin: + return "Litecoin"; + default: + return "Bitcoin"; + } + } } } diff --git a/Model/Bitcoin.cs b/Model/Bitcoin.cs index 3f092cf..69af6ea 100644 --- a/Model/Bitcoin.cs +++ b/Model/Bitcoin.cs @@ -29,7 +29,6 @@ using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; using Org.BouncyCastle.Math.EC; -using BtcAddress.Model; namespace Casascius.Bitcoin { public class Util { @@ -321,27 +320,24 @@ public class Util { } - public static string PubHashToAddress(string PubHash, string AddressType) { + public static string PubHashToAddress(string PubHash, string addressType) { byte[] hex = ValidateAndGetHexPublicHash(PubHash); if (hex == null) throw new ApplicationException("Invalid public hex key"); byte[] hex2 = new byte[21]; Array.Copy(hex, 0, hex2, 1, 20); - int cointype = AddressVersion.Bitcoin; - if (Int32.TryParse(AddressType, out cointype) == false) cointype = AddressVersion.Bitcoin; + byte cointype = AddressType.Bitcoin; + if (byte.TryParse(addressType, out cointype) == false) cointype = AddressType.Bitcoin; - if (AddressType == "Testnet") cointype = AddressVersion.Testnet; - if (AddressType == "Namecoin") cointype = AddressVersion.Namecoin; - if (AddressType == "Litecoin") cointype = AddressVersion.Litecoin; + if (addressType == "Testnet") cointype = AddressType.Testnet; + if (addressType == "Namecoin") cointype = AddressType.Namecoin; + if (addressType == "Litecoin") cointype = AddressType.Litecoin; hex2[0] = (byte)(cointype & 0xff); return ByteArrayToBase58Check(hex2); - - } public static bool PassphraseTooSimple(string passphrase) { - int Lowercase = 0, Uppercase = 0, Numbers = 0, Symbols = 0, Spaces = 0; foreach (char c in passphrase.ToCharArray()) { if (c >= 'a' && c <= 'z') { @@ -358,14 +354,13 @@ public class Util { } // let mini private keys through - they won't contain words, they are nonsense characters, so their entropy is a bit better per character - if (MiniKeyPair.IsValidMiniKey(passphrase) != 1) return false; + if (MiniKeyPair.IsValidMiniKey(passphrase, 0) != 1) return false; if (passphrase.Length < 30 && (Lowercase < 10 || Uppercase < 3 || Numbers < 2 || Symbols < 2)) { return true; } return false; - } public static byte[] ComputeSha256(string ofwhat) { diff --git a/Model/KeyPair.cs b/Model/KeyPair.cs index c05fdc7..4cd794c 100644 --- a/Model/KeyPair.cs +++ b/Model/KeyPair.cs @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Bitcoin Address Utility. If not, see http://www.gnu.org/licenses/. - using System; using System.Collections.Generic; using System.Linq; @@ -112,7 +111,7 @@ public class KeyPair : PublicKey { /// Create a Bitcoin address from a key represented in a string. /// public KeyPair(string key, bool compressed=false, byte addressType=0) { - this._addressType = addressType; + _addressType = addressType; string result = constructWithKey(key, compressed); if (result != null) throw new ArgumentException(result); @@ -127,8 +126,8 @@ public class KeyPair : PublicKey { hex = Util.HexStringToBytes(key, true); if (hex == null) { // tolerate a minikey - if (MiniKeyPair.IsValidMiniKey(key) > 0) { - PrivateKeyBytes = new MiniKeyPair(key).PrivateKeyBytes; + if (MiniKeyPair.IsValidMiniKey(key, _addressType) > 0) { + PrivateKeyBytes = new MiniKeyPair(key, _addressType).PrivateKeyBytes; return null; } else { return "Invalid private key"; diff --git a/Model/MiniKeyPair.cs b/Model/MiniKeyPair.cs index c431de9..4617a4b 100644 --- a/Model/MiniKeyPair.cs +++ b/Model/MiniKeyPair.cs @@ -32,16 +32,18 @@ namespace Casascius.Bitcoin { public class MiniKeyPair : KeyPair { - - public static MiniKeyPair CreateDeterministic(string seed) { - - // flow: - // 1. take SHA256 of seed to yield 32 bytes - // 2. base58-encode those 32 bytes as though it were a regular private key. now we have 51 characters. - // 3. remove all instances of the digit 1. (likely source of typos) - // 4. take 29 characters starting with position 4 - // (this is to skip those first characters of a base58check-encoded private key with low entropy) - // 5. test to see if it matches the typo check. while it does not, increment and try again. + + /// + /// flow: + /// 1. take SHA256 of seed to yield 32 bytes + /// 2. base58-encode those 32 bytes as though it were a regular private key. now we have 51 characters. + /// 3. remove all instances of the digit 1. (likely source of typos) + /// 4. take 29 characters starting with position 4 + /// (this is to skip those first characters of a base58check-encoded private key with low entropy) + /// 5. test to see if it matches the typo check. while it does not, increment and try again. + /// + /// + public static MiniKeyPair CreateDeterministic(string seed, byte addressType = 0) { UTF8Encoding utf8 = new UTF8Encoding(false); byte[] sha256ofseed = Util.ComputeSha256(seed); @@ -51,7 +53,7 @@ public class MiniKeyPair : KeyPair { char[] chars = keytotry.ToCharArray(); char[] charstest = (keytotry + "?").ToCharArray(); - while (Util.ComputeSha256(utf8.GetBytes(charstest))[0] != 0) { + while (Util.ComputeSha256(utf8.GetBytes(charstest))[0] != addressType) { // As long as key doesn't pass typo check, increment it. for (int i = chars.Length - 1; i >= 0; i--) { char c = chars[i]; @@ -79,7 +81,7 @@ public class MiniKeyPair : KeyPair { } } } - return new MiniKeyPair(new String(chars)); + return new MiniKeyPair(new String(chars), addressType); } /// @@ -87,7 +89,7 @@ public class MiniKeyPair : KeyPair { /// Entropy is taken from .NET's SecureRandom, the system clock, /// and any optionally provided salt. /// - public static MiniKeyPair CreateRandom(string usersalt) { + public static MiniKeyPair CreateRandom(string usersalt, byte addressType = 0) { if (usersalt == null) usersalt = "ok, whatever"; usersalt += DateTime.UtcNow.Ticks.ToString(); SecureRandom sr = new SecureRandom(); @@ -95,11 +97,12 @@ public class MiniKeyPair : KeyPair { for (int i = 0; i < 64; i++) { chars[i] = (char)(32 + (sr.NextInt() % 64)); } - return CreateDeterministic(usersalt + new String(chars)); + return CreateDeterministic(usersalt + new String(chars), addressType); } - public MiniKeyPair(string key) { + public MiniKeyPair(string key, byte addressType = 0) { + _addressType = addressType; MiniKey = key; } @@ -121,7 +124,7 @@ public class MiniKeyPair : KeyPair { if (value == null) { PrivateKeyBytes = null; } else { - if (IsValidMiniKey(value) <= 0) { + if (IsValidMiniKey(value, _addressType) <= 0) { throw new ApplicationException("Not a valid minikey"); } _minikey = value; @@ -140,13 +143,13 @@ public class MiniKeyPair : KeyPair { /// Zero or negative indicates not a valid Mini Private Key. /// -1 means well formed but fails typo check. /// - public static int IsValidMiniKey(string candidate) { + public static int IsValidMiniKey(string candidate, byte addressType) { if (candidate.Length != 22 && candidate.Length != 26 && candidate.Length != 30) return 0; if (candidate.StartsWith("S") == false) return 0; System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex("^S[1-9A-HJ-NP-Za-km-z]{21,29}$"); if (reg.IsMatch(candidate) == false) return 0; byte[] ahash = Util.ComputeSha256(candidate + "?"); // first round - if (ahash[0] == 0) return 1; + if ((int)ahash[0] == addressType) return 1; // for (int ct = 0; ct < 716; ct++) ahash = sha256.ComputeHash(ahash); // second thru 717th // if (ahash[0] == 0) return 1; return -1; diff --git a/Model/StringInterpreter.cs b/Model/StringInterpreter.cs index ece5f0d..76ffcad 100644 --- a/Model/StringInterpreter.cs +++ b/Model/StringInterpreter.cs @@ -141,7 +141,7 @@ public class StringInterpreter { } catch { } } - if (MiniKeyPair.IsValidMiniKey(what) == 1) return new MiniKeyPair(what); + if (MiniKeyPair.IsValidMiniKey(what, addressType) == 1) return new MiniKeyPair(what, addressType); return null;