/// <summary> /// Default constructor. Creates a new matched pair of escrow invitation codes. /// </summary> public EscrowCodeSet() { SecureRandom sr = new SecureRandom(); byte[] x = new byte[32]; byte[] y = new byte[32]; sr.NextBytes(x); sr.NextBytes(y); KeyPair kx = new KeyPair(x, true); KeyPair ky = new KeyPair(y, true); BigInteger xi = new BigInteger(1, x); BigInteger yi = new BigInteger(1, y); ECPoint Gx = kx.GetECPoint(); byte[] bytesGx = Gx.GetEncoded(); ECPoint Gy = ky.GetECPoint(); byte[] bytesGy = Gy.GetEncoded(); ECPoint Gxy = Gx.Multiply(yi); byte[] bytesGxy = Gxy.GetEncoded(); Sha256Digest sha256 = new Sha256Digest(); byte[] hashGxy = new byte[32]; sha256.BlockUpdate(bytesGxy, 0, bytesGxy.Length); sha256.DoFinal(hashGxy, 0); sha256.BlockUpdate(hashGxy, 0, 32); sha256.DoFinal(hashGxy, 0); int identifier30 = ((hashGxy[0] & 0x3f) << 24) + (hashGxy[1] << 16) + (hashGxy[2] << 8) + hashGxy[3]; byte[] invitationA = new byte[74]; byte[] invitationB = new byte[74]; long headA = headbaseA + (long)identifier30; long headB = headbaseB + (long)identifier30; // turn headA and headB into bytes for (int i = 7; i >= 0; i--) { invitationA[i] = (byte)(headA & 0xFF); invitationB[i] = (byte)(headB & 0xFF); headA >>= 8; headB >>= 8; } Array.Copy(x, 0, invitationA, 8 + 1, 32); Array.Copy(y, 0, invitationB, 8 + 1, 32); Array.Copy(bytesGy, 0, invitationA, 8 + 1 + 32, 33); Array.Copy(bytesGx, 0, invitationB, 8 + 1 + 32, 33); EscrowInvitationCodeA = Bitcoin.ByteArrayToBase58Check(invitationA); EscrowInvitationCodeB = Bitcoin.ByteArrayToBase58Check(invitationB); }
/// <summary> /// Encryption constructor (non-EC-multiply) /// </summary> public Bip38KeyPair(KeyPair key, string passphrase) { if (passphrase == null && passphrase == "") { throw new ArgumentException("Passphrase is required"); } if (key == null) throw new ArgumentException("Passphrase is required"); this.IsCompressedPoint = key.IsCompressedPoint; this._addressType = key.AddressType; UTF8Encoding utf8 = new UTF8Encoding(false); SHA256CryptoServiceProvider sha256 = new SHA256CryptoServiceProvider(); byte[] addrhashfull = sha256.ComputeHash(sha256.ComputeHash(utf8.GetBytes(key.AddressBase58))); byte[] addresshash = new byte[] { addrhashfull[0], addrhashfull[1], addrhashfull[2], addrhashfull[3] }; byte[] derivedBytes = new byte[64]; SCrypt.ComputeKey(utf8.GetBytes(passphrase), addresshash, 16384, 8, 8, 8, derivedBytes); AesCryptoServiceProvider aes = new AesCryptoServiceProvider(); aes.KeySize = 256; aes.Mode = CipherMode.ECB; byte[] aeskey = new byte[32]; Array.Copy(derivedBytes, 32, aeskey, 0, 32); aes.Key = aeskey; ICryptoTransform encryptor = aes.CreateEncryptor(); byte[] unencrypted = new byte[32]; byte[] rv = new byte[39]; Array.Copy(key.PrivateKeyBytes, unencrypted, 32); for (int x = 0; x < 32; x++) unencrypted[x] ^= derivedBytes[x]; encryptor.TransformBlock(unencrypted, 0, 16, rv, 7); encryptor.TransformBlock(unencrypted, 0, 16, rv, 7); encryptor.TransformBlock(unencrypted, 16, 16, rv, 23); encryptor.TransformBlock(unencrypted, 16, 16, rv, 23); // put header rv[0] = 0x01; rv[1] = 0x42; rv[2] = IsCompressedPoint ? (byte)0xe0 : (byte)0xc0; byte[] checksum = sha256.ComputeHash(sha256.ComputeHash(utf8.GetBytes(key.AddressBase58))); rv[3] = checksum[0]; rv[4] = checksum[1]; rv[5] = checksum[2]; rv[6] = checksum[3]; _encryptedKey = Bitcoin.ByteArrayToBase58Check(rv); _pubKey = key.PublicKeyBytes; _hash160 = key.Hash160; }
/// <summary> /// Encrypt constructor. /// Creates a new encrypted key pair with a passphrase. /// The resulting key pair record retains the public key and bitcoin address /// but not the passphrase or the unencrypted private key. /// </summary> public ShaPassphraseKeyPair(KeyPair key, string passphrase) { if (passphrase == null || passphrase == "") { throw new ArgumentException("Passphrase is required"); } if (key == null) throw new ArgumentException("Key is required"); IsCompressedPoint = key.IsCompressedPoint; _addressType = key.AddressType; this._hash160 = key.Hash160; this._pubKey = key.PublicKeyBytes; AesCryptoServiceProvider aes = new AesCryptoServiceProvider(); aes.KeySize = 256; aes.Mode = CipherMode.ECB; SHA256CryptoServiceProvider sha256 = new SHA256CryptoServiceProvider(); UTF8Encoding utf8 = new UTF8Encoding(false); byte[] encryptionKey = sha256.ComputeHash(utf8.GetBytes(passphrase)); aes.Key = encryptionKey; ICryptoTransform encryptor = aes.CreateEncryptor(); byte[] rv = new byte[36]; encryptor.TransformBlock(_privKey, 0, 16, rv, 4); encryptor.TransformBlock(_privKey, 0, 16, rv, 4); byte[] interblock = new byte[16]; Array.Copy(rv, 4, interblock, 0, 16); for (int x = 0; x < 16; x++) interblock[x] ^= _privKey[16 + x]; encryptor.TransformBlock(interblock, 0, 16, rv, 20); encryptor.TransformBlock(interblock, 0, 16, rv, 20); // put header rv[0] = 0x02; rv[1] = 0x05; byte[] checksum = sha256.ComputeHash(utf8.GetBytes(passphrase + "?")); rv[2] = (byte)(checksum[0] & 0x7F); rv[3] = (byte)(checksum[1] & 0xFE); if (key.IsCompressedPoint) rv[3]++; this._encryptedKey = Bitcoin.ByteArrayToBase58Check(rv); }
private void setAddressConfirmationCode(int identifier30, byte networkbyte, byte flagbyte, byte[] z, byte[] hash160) { byte[] accbytes = new byte[74]; long head = headconfP + (int)identifier30; for (int i=7; i>=0; i--) { accbytes[i] = (byte)(head & 0x7F); head >>= 8; } accbytes[8]=networkbyte; byte[] Gzbytes = new KeyPair(z, true).GetECPoint().GetEncoded(); Array.Copy(Gzbytes, 0, accbytes, 8+1, 33); Array.Copy(hash160, 0, accbytes, 8+1+33, 20); accbytes[8+1+33+20] = flagbyte; this.AddressConfirmationCode = Bitcoin.ByteArrayToBase58Check(accbytes); }
/// <summary> /// Constructor which attempts to redeem a completed set of three codes, and calculate the private key. /// </summary> public EscrowCodeSet(string code1, string code2, string code3) { if (code1 == null || code2 == null || code3 == null || code1 == "" || code2 == "" || code3 == "") { throw new ArgumentException("Three codes are required to use this function."); } string codea = null, codeb=null, codep = null; if (code1.StartsWith("einva")) codea = code1; if (code2.StartsWith("einva")) codea = code2; if (code3.StartsWith("einva")) codea = code3; if (code1.StartsWith("einvb")) codeb = code1; if (code2.StartsWith("einvb")) codeb = code2; if (code3.StartsWith("einvb")) codeb = code3; if (code1.StartsWith("einvp")) codep = code1; if (code2.StartsWith("einvp")) codep = code2; if (code3.StartsWith("einvp")) codep = code3; if (codea==null || codeb == null || codep == null) { throw new ArgumentException("In order to use this function, one code MUST be an Escrow Invitation A (starting " + "with \"einva\"), one must be an Escrow Invitation B (starting with \"einvb\") and the last " + "code MUST be a Payment Invitation (starting with \"einvp\")."); } byte[] pubparta, privparta; int identifier30a; string failreason = parseEscrowCode(codea, out pubparta, out privparta, out identifier30a); if (failreason != null) throw new ArgumentException("Escrow Invitation Code A: " + failreason); byte[] pubpartb, privpartb; int identifier30b; failreason = parseEscrowCode(codeb, out pubpartb, out privpartb, out identifier30b); if (failreason != null) throw new ArgumentException("Escrow Invitation Code B: " + failreason); if (identifier30a != identifier30b) { throw new ArgumentException("The two Escrow Invitations are not mates and cannot unlock the private key."); } string notvalid = "Not a valid Payment Invitation Code"; string notvalid2 = "Code is not a valid Payment Invitation Code or may have a typo or other error."; string notvalid3 = "The Payment Invitation does not belong to the provided Escrow Invitation."; long headp; byte[] invbytesp; string failReason = parseEitherCode(codep, notvalid, notvalid2, out invbytesp, out headp); if (headp < headbaseP) throw new ArgumentException(notvalid); long identifier30L = headp - headbaseP; if (identifier30L < 0 || identifier30L > 0x3FFFFFFFL) throw new ArgumentException(notvalid); if (identifier30L != (long)identifier30a) { throw new ArgumentException("The Payment Invitation was not generated from either of the provided Escrow Invitation codes and cannot be unlocked by them."); } byte[] privpartz = new byte[32]; Array.Copy(invbytesp, 8 + 1 + 1, privpartz, 0, 32); byte networkByte = invbytesp[8]; bool compressedFlag = (invbytesp[8 + 1 + 1 + 32 + 20] & 0x1) == 1; // get private key BigInteger xyz = new BigInteger(1, privparta).Multiply(new BigInteger(1, privpartb)).Multiply(new BigInteger(1, privpartz)); var ps = Org.BouncyCastle.Asn1.Sec.SecNamedCurves.GetByName("secp256k1"); xyz = xyz.Mod(ps.N); KeyPair kp = new KeyPair(xyz, compressedFlag, networkByte); // provide everything this.EscrowInvitationCodeA = codea; this.EscrowInvitationCodeB = codeb; this.PaymentInvitationCode = codep; this.BitcoinAddress = kp.AddressBase58; this.PrivateKey = kp.PrivateKey; }
/// <summary> /// Initialize the intermediate from a passphrase /// </summary> private void createFromPassphrase(string passphrase, byte[] ownersalt) { if (passphrase == null || passphrase == "") { throw new ArgumentException("Passphrase is required"); } _ownersalt = ownersalt; UTF8Encoding utf8 = new UTF8Encoding(false); _passfactor = new byte[32]; SCrypt.ComputeKey(utf8.GetBytes(passphrase), _ownersalt, 16384, 8, 8, 8, _passfactor); // make a compressed key out of it just by using the existing bitcoin address classes KeyPair kp = new KeyPair(_passfactor, compressed: true); _passpoint = kp.PublicKeyBytes; byte[] result = new byte[49]; // 8 bytes are a constant, responsible for making the result start with the characters "passphrase" Array.Copy(magic, 0, result, 0, 8); Array.Copy(_ownersalt, 0, result, 8, 8); Array.Copy(_passpoint, 0, result, 16, 33); Code = Bitcoin.ByteArrayToBase58Check(result); }
private void btnCombine_Click(object sender, EventArgs e) { // What is input #1? string input1 = txtInput1.Text; string input2 = txtInput2.Text; PublicKey pub1 = null, pub2 = null; KeyPair kp1 = null, kp2 = null; if (KeyPair.IsValidPrivateKey(input1)) { pub1 = kp1 = new KeyPair(input1); } else if (PublicKey.IsValidPublicKey(input1)) { pub1 = new PublicKey(input1); } else { MessageBox.Show("Input key #1 is not a valid Public Key or Private Key Hex", "Can't combine", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (KeyPair.IsValidPrivateKey(input2)) { pub2 = kp2 = new KeyPair(input2); } else if (PublicKey.IsValidPublicKey(input2)) { pub2 = new PublicKey(input2); } else { MessageBox.Show("Input key #2 is not a valid Public Key or Private Key Hex", "Can't combine", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (kp1 == null && kp2 == null && rdoAdd.Checked == false) { MessageBox.Show("Can't multiply two public keys. At least one of the keys must be a private key.", "Can't combine", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (pub1.IsCompressedPoint != pub2.IsCompressedPoint) { MessageBox.Show("Can't combine a compressed key with an uncompressed key.", "Can't combine", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (pub1.AddressBase58 == pub2.AddressBase58) { if (MessageBox.Show("Both of the key inputs have the same public key hash. You can continue, but " + "the results are probably going to be wrong. You might have provided the wrong " + "information, such as two parts from the same side of the transaction, instead " + "of one part from each side. Continue anyway?", "Duplicate Key Warning", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) != DialogResult.OK) { return; } } var ps = Org.BouncyCastle.Asn1.Sec.SecNamedCurves.GetByName("secp256k1"); // Combining two private keys? if (kp1 != null && kp2 != null) { BigInteger e1 = new BigInteger(1, kp1.PrivateKeyBytes); BigInteger e2 = new BigInteger(1, kp2.PrivateKeyBytes); BigInteger ecombined = (rdoAdd.Checked ? e1.Add(e2) : e1.Multiply(e2)).Mod(ps.N); System.Diagnostics.Debug.WriteLine(kp1.PublicKeyHex); System.Diagnostics.Debug.WriteLine(kp2.PublicKeyHex); KeyPair kpcombined = new KeyPair(Bitcoin.Force32Bytes(ecombined.ToByteArrayUnsigned()), compressed: kp1.IsCompressedPoint); txtOutputAddress.Text = kpcombined.AddressBase58; txtOutputPubkey.Text = kpcombined.PublicKeyHex.Replace(" ", ""); txtOutputPriv.Text = kpcombined.PrivateKeyBase58; } else if (kp1 != null || kp2 != null) { // Combining one public and one private KeyPair priv = (kp1 == null) ? kp2 : kp1; PublicKey pub = (kp1 == null) ? pub1 : pub2; ECPoint point = pub.GetECPoint(); ECPoint combined = rdoAdd.Checked ? point.Add(priv.GetECPoint()) : point.Multiply(new BigInteger(1, priv.PrivateKeyBytes)); ECPoint combinedc = ps.Curve.CreatePoint(combined.X.ToBigInteger(), combined.Y.ToBigInteger(), priv.IsCompressedPoint); PublicKey pkcombined = new PublicKey(combinedc.GetEncoded()); txtOutputAddress.Text = pkcombined.AddressBase58; txtOutputPubkey.Text = pkcombined.PublicKeyHex.Replace(" ", ""); txtOutputPriv.Text = "Only available when combining two private keys"; } else { // Adding two public keys ECPoint combined = pub1.GetECPoint().Add(pub2.GetECPoint()); ECPoint combinedc = ps.Curve.CreatePoint(combined.X.ToBigInteger(), combined.Y.ToBigInteger(), pub1.IsCompressedPoint); PublicKey pkcombined = new PublicKey(combinedc.GetEncoded()); txtOutputAddress.Text = pkcombined.AddressBase58; txtOutputPubkey.Text = pkcombined.PublicKeyHex.Replace(" ", ""); txtOutputPriv.Text = "Only available when combining two private keys"; } }
public void Decode() { ChecksumMatched=false; Decoded=false; KeyPair = null; if (PartsAccepted < PartsNeeded) return; BigInteger[] pc = new BigInteger[8]; for (int i = 0; i < decodedKeyParts.Count; i++) { byte[] g = decodedKeyParts[i]; pc[i] = new BigInteger(1, g, 4, 35); // If there is an overflow, then add it in. if ((g[2] & 0x80) == 0x80) { pc[i] = pc[i].Add(new BigInteger(((int)((g[2] & 0x60) >> 5)).ToString()).ShiftLeft(280)); Debug.WriteLine("overflow added"); } } List<equation> equations = new List<equation>(); // create equations for all the parts we need. for (int i = 0; i < PartsNeeded; i++) { byte[] got = decodedKeyParts[i]; // extract out part number int partnumber0 = (byte)((got[2] & 0x0e) >> 1); equations.Add(new equation(i, PartsNeeded, partnumber0)); } List<List<equation>> steps = new List<List<equation>>(); steps.Add(equations); // goal: get our equation set down such that there's only one coefficient on the left side. while (equations.Count > 1) { equations = solvesome(equations); steps.Add(equations); Debug.WriteLine("-----"); foreach (equation eq in equations) { Debug.WriteLine(eq.ToString()); } } BigInteger[] v = new BigInteger[8]; while (steps.Count > 0) { // pop off the last step List<equation> laststeps = steps[steps.Count - 1]; steps.RemoveAt(steps.Count - 1); equation laststep = laststeps[0]; Debug.WriteLine("-----"); Debug.WriteLine(laststep.ToString()); // solve for v long divisor = laststep.leftside[0].multiplier; laststep.divisor = laststep.leftside[0].multiplier; laststep.leftside[0].multiplier = 1; //foreach (coefficient c in laststep.rightside) c.divisor = divisor; //laststep.subtractor = laststep.subtractor.Divide(new BigInteger(divisor.ToString())); long idx = laststep.leftside[0].vindex; v[idx] = laststep.SolveRight(pc); Debug.WriteLine(String.Format("v({0})={1}", laststep.leftside[0].vindex, v[idx].ToString())); // go through all other steps and see that our solved value is incorporated into the equation. foreach (List<equation> eqbl in steps) { foreach (equation eqb in eqbl) eqb.SolveLeft(v); } } // xor the ones we need BigInteger xoraccum = BigInteger.Zero; for (int i = 0; i < PartsNeeded; i++) { xoraccum = xoraccum.Xor(v[i]); } ChecksumMatched = false; Decoded = true; byte[] keybytes = xoraccum.ToByteArrayUnsigned(); if (keybytes.Length > 32) { // if more than 32 bytes, decoding probably went wrong! truncate to 32 bytes, but force a checksum failure byte[] newkey = new byte[32]; for (int jj = 0; jj < 32; jj++) newkey[jj] = keybytes[jj]; keybytes = newkey; return; } else if (keybytes.Length < 32) { byte[] array32 = new byte[32]; Array.Copy(keybytes, 0, array32, 32 - keybytes.Length, keybytes.Length); keybytes = array32; } KeyPair = new KeyPair(keybytes); // Get the bitcoin address SHA256CryptoServiceProvider sha256 = new SHA256CryptoServiceProvider(); ASCIIEncoding ae = new ASCIIEncoding(); byte[] checksum = sha256.ComputeHash(ae.GetBytes(BitcoinAddress)); int mychecksum = ((checksum[0] & 1) << 8) + checksum[1]; if (mychecksum == expectedChecksum) ChecksumMatched=true; }
public override bool DecryptWithPassphrase(string passphrase) { if (passphrase == null) { return false; } byte[] hex = Bitcoin.Base58CheckToByteArray(_encryptedKey); KeyPair tempkey = null; if (hex.Length == 39 && hex[0] == 1 && hex[1] == 0x42) { UTF8Encoding utf8 = new UTF8Encoding(false); byte[] addresshash = new byte[] { hex[3], hex[4], hex[5], hex[6] }; byte[] derivedBytes = new byte[64]; SCrypt.ComputeKey(utf8.GetBytes(passphrase), addresshash, 16384, 8, 8, 8, derivedBytes); AesCryptoServiceProvider aes = new AesCryptoServiceProvider(); aes.KeySize = 256; aes.Mode = CipherMode.ECB; byte[] aeskey = new byte[32]; Array.Copy(derivedBytes, 32, aeskey, 0, 32); aes.Key = aeskey; ICryptoTransform decryptor = aes.CreateDecryptor(); byte[] decrypted = new byte[32]; decryptor.TransformBlock(hex, 7, 16, decrypted, 0); decryptor.TransformBlock(hex, 7, 16, decrypted, 0); decryptor.TransformBlock(hex, 23, 16, decrypted, 16); decryptor.TransformBlock(hex, 23, 16, decrypted, 16); for (int x = 0; x < 32; x++) decrypted[x] ^= derivedBytes[x]; tempkey = new KeyPair(decrypted, compressed: IsCompressedPoint); Sha256Digest sha256 = new Sha256Digest(); byte[] addrhash = new byte[32]; byte[] addrtext = utf8.GetBytes(tempkey.AddressBase58); sha256.BlockUpdate(addrtext, 0, addrtext.Length); sha256.DoFinal(addrhash, 0); sha256.BlockUpdate(addrhash, 0, 32); sha256.DoFinal(addrhash, 0); if (addrhash[0] != hex[3] || addrhash[1] != hex[4] || addrhash[2] != hex[5] || addrhash[3] != hex[6]) { return false; } _privKey = tempkey.PrivateKeyBytes; _pubKey = tempkey.PublicKeyBytes; _hash160 = tempkey.Hash160; return true; } else if (hex.Length == 39 && hex[0] == 1 && hex[1] == 0x43) { // produce the intermediate from the passphrase // get ownersalt and encryptedpart2 since they are in the record byte[] ownersalt = new byte[8]; Array.Copy(hex, 7, ownersalt, 0, 8); Bip38Intermediate intermediate = new Bip38Intermediate(passphrase, ownersalt); tempkey = decryptUsingIntermediate(intermediate, hex); if (verifyAddressHash(tempkey.AddressBase58, hex) == false) return false; } _privKey = tempkey.PrivateKeyBytes; _pubKey = tempkey.PublicKeyBytes; _hash160 = tempkey.Hash160; return true; }
/// <summary> /// Encryption constructor to create a new random key from an intermediate /// </summary> public Bip38KeyPair(Bip38Intermediate intermediate, bool retainPrivateKeyWhenPossible=false) { // generate seedb byte[] seedb = new byte[24]; SecureRandom sr = new SecureRandom(); sr.NextBytes(seedb); // get factorb as sha256(sha256(seedb)) Sha256Digest sha256 = new Sha256Digest(); sha256.BlockUpdate(seedb, 0, 24); factorb = new byte[32]; sha256.DoFinal(factorb, 0); sha256.BlockUpdate(factorb, 0, 32); sha256.DoFinal(factorb, 0); // get ECPoint from passpoint PublicKey pk = new PublicKey(intermediate.passpoint); ECPoint generatedpoint = pk.GetECPoint().Multiply(new BigInteger(1, factorb)); byte[] generatedpointbytes = generatedpoint.GetEncoded(); PublicKey generatedaddress = new PublicKey(generatedpointbytes); // get addresshash UTF8Encoding utf8 = new UTF8Encoding(false); 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); byte[] addresshashplusownersalt = new byte[12]; Array.Copy(addresshashfull, 0, addresshashplusownersalt, 0, 4); Array.Copy(intermediate.ownersalt, 0, addresshashplusownersalt, 4, 8); // derive encryption key material 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[] unencryptedpart1 = new byte[16]; for (int i = 0; i < 16; i++) { unencryptedpart1[i] = (byte)(seedb[i] ^ derived[i]); } byte[] encryptedpart1 = new byte[16]; // encrypt it AesCryptoServiceProvider aes = new AesCryptoServiceProvider(); aes.KeySize = 256; aes.Mode = CipherMode.ECB; aes.Key = derivedhalf2; ICryptoTransform encryptor = aes.CreateEncryptor(); encryptor.TransformBlock(unencryptedpart1, 0, 16, encryptedpart1, 0); encryptor.TransformBlock(unencryptedpart1, 0, 16, encryptedpart1, 0); byte[] unencryptedpart2 = new byte[16]; for (int i = 0; i < 8; i++) { unencryptedpart2[i] = (byte)(encryptedpart1[i + 8] ^ derived[i + 16]); } for (int i = 0; i < 8; i++) { unencryptedpart2[i + 8] = (byte)(seedb[i + 16] ^ derived[i + 24]); } byte[] encryptedpart2 = new byte[16]; encryptor.TransformBlock(unencryptedpart2, 0, 16, encryptedpart2, 0); encryptor.TransformBlock(unencryptedpart2, 0, 16, encryptedpart2, 0); byte[] result = new byte[39]; result[0] = 0x01; result[1] = 0x43; result[2] = generatedaddress.IsCompressedPoint ? (byte)0x20 : (byte)0x00; Array.Copy(addresshashfull, 0, result, 3, 4); Array.Copy(intermediate.ownersalt, 0, result, 7, 8); Array.Copy(encryptedpart1, 0, result, 15, 8); Array.Copy(encryptedpart2, 0, result, 23, 16); _encryptedKey = Bitcoin.ByteArrayToBase58Check(result); _pubKey = generatedaddress.PublicKeyBytes; _hash160 = generatedaddress.Hash160; var ps = Org.BouncyCastle.Asn1.Sec.SecNamedCurves.GetByName("secp256k1"); if (retainPrivateKeyWhenPossible && intermediate.passfactor != null) { BigInteger privatekey = new BigInteger(1, intermediate.passfactor).Multiply(new BigInteger(1, factorb)).Mod(ps.N); _privKey = new KeyPair(privatekey).PrivateKeyBytes; } // create the confirmation code confirmationCodeInfo = new byte[51]; // constant provides for prefix "cfrm38" confirmationCodeInfo[0] = 0x64; confirmationCodeInfo[1] = 0x3B; confirmationCodeInfo[2] = 0xF6; confirmationCodeInfo[3] = 0xA8; confirmationCodeInfo[4] = 0x9A; // fields for flagbyte, addresshash, and ownersalt all copy verbatim Array.Copy(result, 2, confirmationCodeInfo, 5, 1 + 4 + 8); }
private void button1_Click(object sender, EventArgs e) { if (button1.Enabled) { int n = 0; if (Int32.TryParse(textBox2.Text, out n) == false) n = 0; if (n < 1 || n > 9999) { MessageBox.Show("Please enter a number of addresses between 1 and 9999", "Invalid entry"); return; } if (txtPassphrase.Text.Length < 20) { if (MessageBox.Show("Your passphrase is too short (< 20 characters). If you generate this wallet it may be easily compromised. Are you sure you'd like to use this passphrase?", "Passphrase too short", MessageBoxButtons.YesNo) == DialogResult.No) { return; } } if (Bitcoin.PassphraseTooSimple(txtPassphrase.Text)) { if (MessageBox.Show("Your passphrase is too simple. If you generate this wallet it may be easily compromised. Are you sure you'd like to use this passphrase?", "Passphrase too simple", MessageBoxButtons.YesNo) == DialogResult.No) { return; } } StringBuilder wallet = new StringBuilder(); bool CSVmode = cboOutputType.Text.Contains("CSV"); bool ScriptMode = cboOutputType.Text.Contains("Import script"); bool ShowHelpText = cboOutputType.Text.Contains("Normal"); if (ShowHelpText) { wallet.AppendLine("Paper Bitcoin Wallet. Keep private, do not lose, do not allow anyone to make a copy. Anyone with the passphrase or private keys can steal your funds.\r\n"); wallet.AppendLine("Passphrase was:"); wallet.AppendLine(txtPassphrase.Text); wallet.AppendLine("Freely give out the Bitcoin address. The private key after each address is the key needed to unlock funds sent to the Bitcoin address.\r\n"); } progressBar1.Maximum = n; progressBar1.Minimum = 0; progressBar1.Visible = true; label3.Text = "Progress:"; button1.Enabled = false; for (int i = 1; i <= n; i++) { Application.DoEvents(); string privatestring; switch (GenerationFormula) { case 1: privatestring = txtPassphrase.Text + i.ToString(); break; default: privatestring = i.ToString() + "/" + txtPassphrase.Text + "/" + i.ToString() + "/BITCOIN"; break; } SHA256CryptoServiceProvider sha256 = new SHA256CryptoServiceProvider(); UTF8Encoding encoding = new UTF8Encoding(false); byte[] privatekey = sha256.ComputeHash(encoding.GetBytes(privatestring)); KeyPair kp = new KeyPair(privatekey); string bytestring = kp.PrivateKeyHex; string PrivWIF = kp.PrivateKeyBase58; string PubHex = kp.PublicKeyHex; string Address = kp.AddressBase58; if (CSVmode) { wallet.AppendFormat("{0},\"{1}\",\"{2}\"\r\n", i, Address, PrivWIF); } else if (ScriptMode) { wallet.AppendFormat("# {0}: {1}\"\r\n./bitcoind importprivkey {2}\r\n", i, Address, PrivWIF); } else { wallet.AppendFormat("Bitcoin Address #{0}: {1}\r\n", i, Address); wallet.AppendFormat("Private Key: {0}\r\n\r\n", PrivWIF); } progressBar1.Value = i; } txtWallet.Text = wallet.ToString(); progressBar1.Value = 0; progressBar1.Visible = false; label3.Text = "Passphrase:"; button1.Enabled = true; } }
/// <summary> /// Returns true if a given string can be turned into a private key. /// </summary> public static bool IsValidPrivateKey(string key) { KeyPair kp = new KeyPair(); string result = kp.constructWithKey(key, false); return (result == null); }
/// <summary> /// Generate a set of M-of-N parts for a specific private key. /// If desiredPrivKey is null, then a random key will be selected. /// </summary> public void Generate(int PartsNeededToDecode, int PartsToGenerate, byte[] desiredPrivKey) { if (PartsNeededToDecode > PartsToGenerate) { throw new ApplicationException("Number of parts needed exceeds number of parts to generate."); } if (PartsNeededToDecode > 8 || PartsToGenerate > 8) { throw new ApplicationException("Maximum number of parts is 8"); } if (PartsNeededToDecode < 1 || PartsToGenerate < 1) { throw new ApplicationException("Minimum number of parts is 1"); } if (desiredPrivKey != null && desiredPrivKey.Length != 32) { throw new ApplicationException("Desired private key must be 32 bytes"); } KeyParts.Clear(); decodedKeyParts.Clear(); SecureRandom sr = new SecureRandom(); // Get 8 random big integers into v[i]. byte[][] vvv = new byte[8][]; BigInteger[] v = new BigInteger[8]; for (int i = 0; i < 8; i++) { byte[] b = new byte[32]; sr.NextBytes(b, 0, 32); // For larger values of i, chop off some most-significant-bits to prevent overflows as they are // multiplied with increasingly larger factors. if (i >= 7) { b[0] &= 0x7f; } v[i] = new BigInteger(1, b); Debug.WriteLine(String.Format("v({0})={1}", i, v[i].ToString())); } // if a certain private key is desired, then specify it. if (desiredPrivKey != null) { // replace v[0] with xor(v[1...7]) xor desiredPrivKey BigInteger newv0 = BigInteger.Zero; for (int i=1; i<PartsNeededToDecode; i++) { newv0 = newv0.Xor(v[i]); } v[0] = newv0.Xor(new BigInteger(1,desiredPrivKey)); } // Generate the expected private key from all the parts BigInteger privkey = new BigInteger("0"); for (int i = 0; i < PartsNeededToDecode; i++) { privkey = privkey.Xor(v[i]); } // Get the bitcoin address byte[] keybytes = privkey.ToByteArrayUnsigned(); // make sure we have 32 bytes, we'll need it if (keybytes.Length < 32) { byte[] array32 = new byte[32]; Array.Copy(keybytes, 0, array32, 32 - keybytes.Length, keybytes.Length); keybytes = array32; } KeyPair = new BtcAddress.KeyPair(keybytes); SHA256CryptoServiceProvider sha256 = new SHA256CryptoServiceProvider(); ASCIIEncoding ae = new ASCIIEncoding(); byte[] checksum = sha256.ComputeHash(ae.GetBytes(BitcoinAddress)); // Generate the parts for (int i = 0; i < PartsToGenerate; i++) { BigInteger total = new BigInteger("0"); for (int j = 0; j < PartsNeededToDecode; j++) { int factor = 1; for (int ii = 0; ii <= i; ii++) factor = factor * (j + 1); BigInteger bfactor = new BigInteger(factor.ToString()); total = total.Add(v[j].Multiply(bfactor)); } Debug.WriteLine(String.Format(" pc{0}={1}", i, total.ToString())); byte[] parts = new byte[39]; parts[0] = 0x4f; parts[1] = (byte)(0x93 + PartsNeededToDecode); int parts23 = (((checksum[0] << 8) + checksum[1]) & 0x1ff); Debug.WriteLine("checksum " + parts23.ToString()); parts23 += 0x6000; parts23 += (i << 9); byte[] btotal = total.ToByteArrayUnsigned(); for (int jj = 0; jj < btotal.Length; jj++) { parts[jj + 4 + (35 - btotal.Length)] = btotal[jj]; } parts[2] = (byte)((parts23 & 0xFF00) >> 8); parts[3] = (byte)(parts23 & 0xFF); KeyParts.Add(Bitcoin.ByteArrayToBase58Check(parts)); decodedKeyParts.Add(parts); } }
/// <summary> /// Calculates public key and returns true if calculating public key was possible. /// This might cause a delay of milliseconds if it must be computed from the public /// key, possibly more if it leads to decrypting a private key. /// </summary> protected virtual bool calculatePubKey() { if (IsUnencryptedPrivateKeyAvailable()) { KeyPair kp = new KeyPair(_privKey, compressed: IsCompressedPoint, addressType: _addressType); _pubKey = kp.PublicKeyBytes; return true; } return false; }
/// <summary> /// A string that allows someone to independently calculate the /// bitcoin address given their key material, without divulging the encrypted private key. /// This can be used to prove that the bitcoin address is encumbered by the passphrase. /// This is null if unavailable or not applicable. When available, calculates upon first /// read, which involves an EC multiply and takes a little bit of CPU time. /// </summary> public string GetConfirmationCode() { if (_confirmationCode != null) return _confirmationCode; if (confirmationCodeInfo == null || factorb == null) return null; // finish calculating the confirmation code // use the KeyPair class to do the EC multiply for us KeyPair kp = new KeyPair(new BigInteger(1, factorb), true); // the public key is 33 bytes, or rather, 32 bytes plus one bit // (the first byte is either 0x02 or 0x03). // xor it with one bit of derived so it's not readable from the confirmation code. byte[] kppubbytes = kp.PublicKeyBytes; confirmationCodeInfo[18] = (byte)(kppubbytes[0] ^ (derived[63] & 0x01)); // xor the pub bytes with derived[0...31] to get similar benefit like having an IV byte[] unencrypted = new byte[32]; for (int i = 0; i < 32; i++) unencrypted[i] = (byte)(kppubbytes[i + 1] ^ derived[i]); // prepare for AES encryption byte[] derivedhalf2 = new byte[32]; Array.Copy(derived, 32, derivedhalf2, 0, 32); AesCryptoServiceProvider aes = new AesCryptoServiceProvider(); aes.KeySize = 256; aes.Mode = CipherMode.ECB; aes.Key = derivedhalf2; ICryptoTransform encryptor = aes.CreateEncryptor(); // encrypt remaining two blocks of 16 bytes using same derived key encryptor.TransformBlock(unencrypted, 0, 16, confirmationCodeInfo, 19); encryptor.TransformBlock(unencrypted, 0, 16, confirmationCodeInfo, 19); encryptor.TransformBlock(unencrypted, 16, 16, confirmationCodeInfo, 19+16); encryptor.TransformBlock(unencrypted, 16, 16, confirmationCodeInfo, 19+16); _confirmationCode = Bitcoin.ByteArrayToBase58Check(confirmationCodeInfo); return _confirmationCode; }
private void btnGenerateSpecific_Click(object sender, EventArgs e) { KeyPair k = null; try { k = new KeyPair(txtPrivKey.Text); targetPrivKey = k.PrivateKeyBytes; } catch (Exception) { MessageBox.Show("Not a valid private key."); } btnGenerate_Click(sender, e); targetPrivKey = null; }
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. SHA256CryptoServiceProvider sha256 = new SHA256CryptoServiceProvider(); UTF8Encoding utf8 = new UTF8Encoding(false); byte[] sha256ofseed = sha256.ComputeHash(utf8.GetBytes(seed)); string asbase58 = new KeyPair(sha256ofseed).PrivateKeyBase58.Replace("1",""); string keytotry = "S" + asbase58.Substring(4, 29); char[] chars = keytotry.ToCharArray(); char[] charstest = (keytotry + "?").ToCharArray(); while (sha256.ComputeHash(utf8.GetBytes(charstest))[0] != 0) { // As long as key doesn't pass typo check, increment it. for (int i = chars.Length - 1; i >= 0; i--) { char c = chars[i]; if (c == '9') { charstest[i] = chars[i] = 'A'; break; } else if (c == 'H') { charstest[i] = chars[i] = 'J'; break; } else if (c == 'N') { charstest[i] = chars[i] = 'P'; break; } else if (c == 'Z') { charstest[i] = chars[i] = 'a'; break; } else if (c == 'k') { charstest[i] = chars[i] = 'm'; break; } else if (c == 'z') { charstest[i] = chars[i] = '2'; // No break - let loop increment prior character. } else { charstest[i] = chars[i] = ++c; break; } } } return new MiniKeyPair(new String(chars)); }