public static bool IsValidPublicKey(string hex) { byte[] pubKeyBytes = Bitcoin.HexStringToBytes(hex); PublicKey pk = new PublicKey(); string result = pk.constructFromBytes(pubKeyBytes); return (result == null); }
protected virtual bool calculateHash160() { if (IsPublicKeyAvailable()) { PublicKey pub = new PublicKey(_pubKey); _hash160 = pub.Hash160; return true; } return false; }
/// <summary> /// Constructor that takes a single Escrow Invitation Code and produces a Payment Invitation Code. /// </summary> public EscrowCodeSet(string escrowInvitationCode, bool doCompressed=false, byte networkByte = 0) { byte[] pubpart, privpart; int identifier30; string failreason = parseEscrowCode(escrowInvitationCode, out pubpart, out privpart, out identifier30); if (failreason != null) throw new ArgumentException(failreason); // produce a new factor byte[] z = new byte[32]; SecureRandom sr = new SecureRandom(); sr.NextBytes(z); // calculate Gxy then Gxyz PublicKey pk = new PublicKey(pubpart); ECPoint Gxyz = pk.GetECPoint().Multiply(new BigInteger(1, privpart)).Multiply(new BigInteger(1, z)); // Uncompress it Gxyz = PublicKey.GetUncompressed(Gxyz); // We can get the Bitcoin address now, so do so PublicKey pkxyz = new PublicKey(Gxyz); byte[] hash160 = pkxyz.Hash160; BitcoinAddress = new AddressBase(hash160, networkByte).AddressBase58; // make the payment invitation record byte[] invp = new byte[74]; long headP = headbaseP + (long)identifier30; for (int i = 7; i >= 0; i--) { invp[i] = (byte)(headP & 0xff); headP >>= 8; } invp[8] = networkByte; Array.Copy(z, 0, invp, 8 + 1 + 1, 32); // set flag to indicate if einvb was used to generate this, and make it available in the object if (escrowInvitationCode.StartsWith("einvb")) { invp[8 + 1 + 1 + 32 + 20] = 0x2; } // copy hash160 Array.Copy(hash160, 0, invp, 8 + 1 + 1 + 32, 20); PaymentInvitationCode = Bitcoin.ByteArrayToBase58Check(invp); setAddressConfirmationCode(identifier30, networkByte, invp[8 + 1 + 1 + 32 + 20], z, hash160); }
/// <summary> /// Constructor that calculates the address given one escrow invitation code and one matching payment invitation code. /// They can be provided in any order. /// </summary> public EscrowCodeSet(string code1, string code2) { if (code1 == null || code2 == null || code1 == "" || code2 == "") { throw new ArgumentException("Two codes are required to use this function."); } string escrowInvitationCode=null, paymentInvitationCode=null; if (code1.StartsWith("einva") || code1.StartsWith("einvb")) escrowInvitationCode = code1; if (code2.StartsWith("einva") || code2.StartsWith("einvb")) escrowInvitationCode = code2; if (code1.StartsWith("einvp")) paymentInvitationCode = code1; if (code2.StartsWith("einvp")) paymentInvitationCode = code2; if (escrowInvitationCode == null || paymentInvitationCode == null) { throw new ArgumentException("In order to use this function, one code MUST be an Escrow Invitation (starting " + "with \"einva\" or \"einvb\") and the other code MUST be a Payment Invitation (starting with \"einvp\")."); } byte[] pubpart, privpart; int identifier30; string failreason = parseEscrowCode(escrowInvitationCode, out pubpart, out privpart, out identifier30); if (failreason != null) throw new ArgumentException(failreason); 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 head; byte[] invbytes; string failReason = parseEitherCode(paymentInvitationCode, notvalid, notvalid2, out invbytes, out head); if (head < headbaseP) throw new ArgumentException(notvalid); long identifier30L = head - headbaseP; if (identifier30L < 0 || identifier30L > 0x3FFFFFFFL) throw new ArgumentException(notvalid); if ((long)identifier30 != identifier30L) { throw new ArgumentException(notvalid3); } byte[] privpartz = new byte[32]; Array.Copy(invbytes, 8 + 1 + 1, privpartz, 0, 32); byte networkByte = invbytes[8]; bool compressedFlag = (invbytes[8+1+1+32+20] & 0x1) == 1; // get bitcoin address PublicKey pk = new PublicKey(pubpart); ECPoint Gxyz = pk.GetECPoint().Multiply(new BigInteger(1, privpart)).Multiply(new BigInteger(1, privpartz)); // uncompress if compress is not indicated if (compressedFlag == false) Gxyz = PublicKey.GetUncompressed(Gxyz); // We can get the Bitcoin address now, so do so PublicKey pkxyz = new PublicKey(Gxyz); byte[] addrhash160 = pkxyz.Hash160; BitcoinAddress = new AddressBase(addrhash160, networkByte).AddressBase58; // Does the hash160 match? for (int i = 0; i < 20; i++) { if (addrhash160[i] != invbytes[8+1+1+32+i]) { throw new ArgumentException(notvalid3); } } this.PaymentInvitationCode = paymentInvitationCode; byte expectedabflag = (byte)(escrowInvitationCode.StartsWith("einva") ? 2 : 0); if ((invbytes[8+1+1+32+20] & 0x1) != expectedabflag) { SamePartyWarningApplies = true; } }
private void createFromCode(string code) { if (code == null || code == "") { throw new ArgumentException("Intermediate passphrase code is required"); } // get passphrase code byte[] ppcode = Bitcoin.Base58CheckToByteArray(code); if (ppcode == null) { throw new ArgumentException("Intermediate passphrase code is not valid."); } // check length if (ppcode.Length != 49) { throw new ArgumentException("This is not an intermediate passphrase code."); } // check magic for (int i = 0; i < 8; i++) { if (magic[i] != ppcode[i]) { throw new ArgumentException("This is not an intermediate passphrase code."); } } // get ownersalt and passpoint _ownersalt = new byte[8]; _passpoint = new byte[33]; Array.Copy(ppcode, 8, _ownersalt, 0, 8); Array.Copy(ppcode, 16, _passpoint, 0, 33); this.Code = code; // ensure that passpoint can be turned into a valid ECPoint PublicKey pk = new PublicKey(_passpoint); }
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"; } }
/// <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); }