/// <summary>
        /// Creates another intermediate code with the same passphrase but differing only by ownersaltB.
        /// This takes less time.
        /// </summary>
        public Bip38Intermediate(Bip38Intermediate prevchain)
        {
            if (prevchain.LotSequencePresent == false)
            {
                throw new ArgumentException("Can only create from an intermediate code that includes the hash step.");
            }
            _ownerentropy = Util.CloneByteArray(prevchain._ownerentropy);

            this._lotSequencePresent = true;

            // increment ownersaltB
            _ownerentropy[7]++;
            if (_ownerentropy[7] == 0)
            {
                _ownerentropy[6]++;
                if (_ownerentropy[6] == 0)
                {
                    _ownerentropy[5]++;
                    if (_ownerentropy[5] == 0)
                    {
                        _ownerentropy[4]++;
                    }
                }
            }

            derivedBytes = prevchain.derivedBytes;
            byte[] prefactorB = new byte[32 + _ownerentropy.Length];
            Array.Copy(derivedBytes, 0, prefactorB, 0, 32);
            Array.Copy(_ownerentropy, 0, prefactorB, 32, _ownerentropy.Length);
            _passfactor = Util.ComputeDoubleSha256(prefactorB);

            computeCode();
        }
        /// <summary>
        /// Decrypts a key pair given an intermediate generated from a passphrase.
        /// Using a pre-generated intermediate allows for fast decryption of batches of keys that were
        /// generated from the same intermediate.
        /// </summary>
        public bool DecryptWithIntermediate(Bip38Intermediate intermediate)
        {
            byte[] hex = Util.Base58CheckToByteArray(_encryptedKey);
            // verify that the intermediate has the same ownersalt
            byte[] ownersalt = intermediate.ownerentropy;
            for (int i = 0; i < 8; i++)
            {
                if (hex[i + 7] != ownersalt[i])
                {
                    throw new ArgumentException("Intermediate does not have same salt");
                }
            }

            KeyPair tempkey = decryptUsingIntermediate(intermediate, hex);

            if (verifyAddressHash(tempkey.AddressBase58, hex) == false)
            {
                return(false);
            }

            _privKey = tempkey.PrivateKeyBytes;
            _pubKey  = tempkey.PublicKeyBytes;
            _hash160 = tempkey.Hash160;
            return(true);
        }
        /// <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 KeyPair decryptUsingIntermediate(Bip38Intermediate intermediate, byte[] hex)
        {
            if (intermediate.passfactor == null) {
                throw new ArgumentException("This is an encryption-only intermediate code because it was not created from a passphrase.  An intermediate must have been created from passphrase to be used for decryption");
            }

            byte[] encryptedpart2 = new byte[16];
            Array.Copy(hex, 23, encryptedpart2, 0, 16);

            // get the first part of encryptedpart1 (the rest is encrypted within encryptedpart2)
            byte[] encryptedpart1 = new byte[16];
            Array.Copy(hex, 15, encryptedpart1, 0, 8);

            // derive decryption key
            byte[] addresshashplusownerentropy = new byte[12];
            Array.Copy(hex, 3, addresshashplusownerentropy, 0, 4);
            Array.Copy(intermediate.ownerentropy, 0, addresshashplusownerentropy, 4, 8);
            byte[] derived = new byte[64];
            SCrypt.ComputeKey(intermediate.passpoint, addresshashplusownerentropy, 1024, 1, 1, 1, derived);
            byte[] derivedhalf2 = new byte[32];
            Array.Copy(derived, 32, derivedhalf2, 0, 32);

            // decrypt encrypted payload
            var aes = Aes.Create();
            aes.KeySize = 256;
            aes.Mode = CipherMode.ECB;
            aes.Key = derivedhalf2;
            ICryptoTransform decryptor = aes.CreateDecryptor();

            byte[] unencryptedpart2 = new byte[16];
            decryptor.TransformBlock(encryptedpart2, 0, 16, unencryptedpart2, 0);
            decryptor.TransformBlock(encryptedpart2, 0, 16, unencryptedpart2, 0);
            for (int i = 0; i < 16; i++) {
                unencryptedpart2[i] ^= derived[i + 16];
            }

            // take the decrypted part and recover encrypted part 1
            Array.Copy(unencryptedpart2, 0, encryptedpart1, 8, 8);

            // decrypt part 1
            byte[] unencryptedpart1 = new byte[16];
            decryptor.TransformBlock(encryptedpart1, 0, 16, unencryptedpart1, 0);
            decryptor.TransformBlock(encryptedpart1, 0, 16, unencryptedpart1, 0);
            for (int i = 0; i < 16; i++) {
                unencryptedpart1[i] ^= derived[i];
            }

            // recover seedb
            byte[] seedb = new byte[24];
            Array.Copy(unencryptedpart1, 0, seedb, 0, 16);
            Array.Copy(unencryptedpart2, 8, seedb, 16, 8);

            // turn seedb into factorb
            Sha256Digest sha256 = new Sha256Digest();
            sha256.BlockUpdate(seedb, 0, 24);
            byte[] factorb = new byte[32];
            sha256.DoFinal(factorb, 0);
            sha256.BlockUpdate(factorb, 0, 32);
            sha256.DoFinal(factorb, 0);

            // get private key
            var ps = Org.BouncyCastle.Asn1.Sec.SecNamedCurves.GetByName("secp256k1");
            BigInteger privatekey = new BigInteger(1, intermediate.passfactor).Multiply(new BigInteger(1, factorb)).Mod(ps.N);

            // use private key
            return new KeyPair(privatekey, this.IsCompressedPoint, this._addressType);
        }
        public override bool DecryptWithPassphrase(string passphrase)
        {
            if (passphrase == null) {
                return false;
            }

            byte[] hex = Util.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);

                var aes = Aes.Create();
                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);
                bool includeHashStep = (hex[2] & 4) == 4;
                Bip38Intermediate intermediate = new Bip38Intermediate(passphrase, ownersalt, includeHashStep);
                this.LotNumber = intermediate.LotNumber;
                this.SequenceNumber = intermediate.SequenceNumber;

                tempkey = decryptUsingIntermediate(intermediate, hex);
                if (verifyAddressHash(tempkey.AddressBase58, hex) == false) return false;
            }
            _privKey = tempkey.PrivateKeyBytes;
            _pubKey = tempkey.PublicKeyBytes;
            _hash160 = tempkey.Hash160;
            return true;
        }
        /// <summary>
        /// Decrypts a key pair given an intermediate generated from a passphrase.
        /// Using a pre-generated intermediate allows for fast decryption of batches of keys that were
        /// generated from the same intermediate.
        /// </summary>
        public bool DecryptWithIntermediate(Bip38Intermediate intermediate)
        {
            byte[] hex = Util.Base58CheckToByteArray(_encryptedKey);
            // verify that the intermediate has the same ownersalt
            byte[] ownersalt = intermediate.ownerentropy;
            for (int i = 0; i < 8; i++) {
                if (hex[i + 7] != ownersalt[i]) {
                    throw new ArgumentException("Intermediate does not have same salt");
                }
            }

            KeyPair 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[] addresshashplusownerentropy = new byte[12];
            Array.Copy(addresshashfull, 0, addresshashplusownerentropy, 0, 4);
            Array.Copy(intermediate.ownerentropy, 0, addresshashplusownerentropy, 4, 8);

            // derive encryption key material
            derived = new byte[64];
            SCrypt.ComputeKey(intermediate.passpoint, addresshashplusownerentropy, 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
            var aes = Aes.Create();
            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;
            if (intermediate.LotSequencePresent) result[2] |= 0x04;

            Array.Copy(addresshashfull, 0, result, 3, 4);
            Array.Copy(intermediate.ownerentropy, 0, result, 7, 8);
            Array.Copy(encryptedpart1, 0, result, 15, 8);
            Array.Copy(encryptedpart2, 0, result, 23, 16);

            _encryptedKey = Util.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 ownerentropy all copy verbatim
            Array.Copy(result, 2, confirmationCodeInfo, 5, 1 + 4 + 8);
        }
Beispiel #8
0
        public Exception DecryptWithPassphrase(string passphrase)
        {
            // check for null entry
            if (passphrase == null || passphrase == "")
            {
                return(new ArgumentException("Passphrase is required"));
            }

            Bip38Intermediate intermediate = new Bip38Intermediate(passphrase, _ownerentropy, LotSequencePresent);

            // derive the 64 bytes we need
            // get ECPoint from passpoint
            PublicKey pk = new PublicKey(intermediate.passpoint);

            byte[] addresshashplusownerentropy = Util.ConcatenateByteArrays(_addressHash, intermediate.ownerentropy);

            // derive encryption key material
            byte[] derived = new byte[64];
            CryptSharp.Utility.SCrypt.ComputeKey(intermediate.passpoint, addresshashplusownerentropy, 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)(_encryptedpointb[0] ^ (derived[63] & 0x01));

            // decrypt
            var aes = Aes.Create();

            aes.KeySize = 256;
            aes.Mode    = CipherMode.ECB;
            aes.Key     = derivedhalf2;
            ICryptoTransform decryptor = aes.CreateDecryptor();

            decryptor.TransformBlock(_encryptedpointb, 1, 16, unencryptedpubkey, 1);
            decryptor.TransformBlock(_encryptedpointb, 1, 16, unencryptedpubkey, 1);
            decryptor.TransformBlock(_encryptedpointb, 1 + 16, 16, unencryptedpubkey, 17);
            decryptor.TransformBlock(_encryptedpointb, 1 + 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.
                if (IsCompressedPoint == false)
                {
                    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] != _addressHash[i])
                    {
                        return(new ArgumentException("This passphrase is wrong or does not belong to this confirmation code."));
                    }
                }

                this.PublicKey = generatedaddress;
            } catch {
                return(new ArgumentException("This passphrase is wrong or does not belong to this confirmation code."));
            }
            return(null);
        }
        public Exception DecryptWithPassphrase(string passphrase)
        {
            // check for null entry
            if (passphrase == null || passphrase == "") {
                return new ArgumentException("Passphrase is required");
            }

            Bip38Intermediate intermediate = new Bip38Intermediate(passphrase, _ownerentropy, LotSequencePresent);

            // derive the 64 bytes we need
            // get ECPoint from passpoint
            PublicKey pk = new PublicKey(intermediate.passpoint);

            byte[] addresshashplusownerentropy = Util.ConcatenateByteArrays(_addressHash, intermediate.ownerentropy);

            // derive encryption key material
            byte[] derived = new byte[64];
            SCrypt.ComputeKey(intermediate.passpoint, addresshashplusownerentropy, 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)(_encryptedpointb[0] ^ (derived[63] & 0x01));

            // decrypt
            var aes = Aes.Create();
            aes.KeySize = 256;
            aes.Mode = CipherMode.ECB;
            aes.Key = derivedhalf2;
            ICryptoTransform decryptor = aes.CreateDecryptor();

            decryptor.TransformBlock(_encryptedpointb, 1, 16, unencryptedpubkey, 1);
            decryptor.TransformBlock(_encryptedpointb, 1, 16, unencryptedpubkey, 1);
            decryptor.TransformBlock(_encryptedpointb, 1 + 16, 16, unencryptedpubkey, 17);
            decryptor.TransformBlock(_encryptedpointb, 1 + 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.
                if (IsCompressedPoint==false) {
                    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] != _addressHash[i]) {
                        return new ArgumentException("This passphrase is wrong or does not belong to this confirmation code.");
                    }
                }

                this.PublicKey = generatedaddress;
            } catch {
                return new ArgumentException("This passphrase is wrong or does not belong to this confirmation code.");
            }
            return null;
        }
        /// <summary>
        /// Creates another intermediate code with the same passphrase but differing only by ownersaltB.
        /// This takes less time.
        /// </summary>
        public Bip38Intermediate(Bip38Intermediate prevchain)
        {
            if (prevchain.LotSequencePresent == false) {
                throw new ArgumentException("Can only create from an intermediate code that includes the hash step.");
            }
            _ownerentropy = Util.CloneByteArray(prevchain._ownerentropy);

            this._lotSequencePresent = true;

            // increment ownersaltB
            _ownerentropy[7]++;
            if (_ownerentropy[7] == 0) {
                _ownerentropy[6]++;
                if (_ownerentropy[6] == 0) {
                    _ownerentropy[5]++;
                    if (_ownerentropy[5] == 0) {
                        _ownerentropy[4]++;
                    }
                }
            }

            derivedBytes = prevchain.derivedBytes;
            byte[] prefactorB = new byte[32 + _ownerentropy.Length];
            Array.Copy(derivedBytes, 0, prefactorB, 0, 32);
            Array.Copy(_ownerentropy, 0, prefactorB, 32, _ownerentropy.Length);
            _passfactor = Util.ComputeDoubleSha256(prefactorB);

            computeCode();
        }
        private void btnVccVerifyConfirmationCode_Click(object sender, EventArgs e)
        {
            lblResult.Visible = false;

            // check for null entry
            if (tbxVccPassphrase.Text == "") {
                MessageBox.Show("Passphrase is required.", "Passphrase required", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }

            if (tbxVccConfirmationCode.Text == "") {
                MessageBox.Show("Confirmation code is required.", "Confirmation code required", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }

            // Parse confirmation code.
            byte[] confbytes = Util.Base58CheckToByteArray(tbxVccConfirmationCode.Text.Trim());
            if (confbytes == null) {
                // is it even close?
                if (tbxVccConfirmationCode.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(tbxVccConfirmationCode.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(tbxVccPassphrase.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(tbxVccPassphrase.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];
            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 btnGicGenerateIntermediateCode_Click(object sender, EventArgs e)
 {
     if ((tbxGicPassphrase.Text ?? "") == "")
     {
         MessageBox.Show("Enter a passphrase first.");
         return;
     }
     try
     {
         Bip38Intermediate intermediate = new Bip38Intermediate(tbxGicPassphrase.Text, Bip38Intermediate.Interpretation.Passphrase);
         tbxGicIntermediateCode.Text = intermediate.Code;
     }
     catch (Exception ae)
     {
         MessageBox.Show(ae.Message);
     }
 }
        public override bool DecryptWithPassphrase(string passphrase)
        {
            if (passphrase == null)
            {
                return(false);
            }

            byte[]  hex     = Util.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];
                CryptSharp.Utility.SCrypt.ComputeKey(utf8.GetBytes(passphrase), addresshash, 16384, 8, 8, 8, derivedBytes);

                var aes = Aes.Create();
                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);
                bool includeHashStep           = (hex[2] & 4) == 4;
                Bip38Intermediate intermediate = new Bip38Intermediate(passphrase, ownersalt, includeHashStep);
                this.LotNumber      = intermediate.LotNumber;
                this.SequenceNumber = intermediate.SequenceNumber;

                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[] addresshashplusownerentropy = new byte[12];
            Array.Copy(addresshashfull, 0, addresshashplusownerentropy, 0, 4);
            Array.Copy(intermediate.ownerentropy, 0, addresshashplusownerentropy, 4, 8);

            // derive encryption key material
            derived = new byte[64];
            CryptSharp.Utility.SCrypt.ComputeKey(intermediate.passpoint, addresshashplusownerentropy, 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
            var aes = Aes.Create();

            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;
            if (intermediate.LotSequencePresent)
            {
                result[2] |= 0x04;
            }

            Array.Copy(addresshashfull, 0, result, 3, 4);
            Array.Copy(intermediate.ownerentropy, 0, result, 7, 8);
            Array.Copy(encryptedpart1, 0, result, 15, 8);
            Array.Copy(encryptedpart2, 0, result, 23, 16);

            _encryptedKey = Util.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 ownerentropy all copy verbatim
            Array.Copy(result, 2, confirmationCodeInfo, 5, 1 + 4 + 8);
        }
        private KeyPair decryptUsingIntermediate(Bip38Intermediate intermediate, byte[] hex)
        {
            if (intermediate.passfactor == null)
            {
                throw new ArgumentException("This is an encryption-only intermediate code because it was not created from a passphrase.  An intermediate must have been created from passphrase to be used for decryption");
            }

            byte[] encryptedpart2 = new byte[16];
            Array.Copy(hex, 23, encryptedpart2, 0, 16);

            // get the first part of encryptedpart1 (the rest is encrypted within encryptedpart2)
            byte[] encryptedpart1 = new byte[16];
            Array.Copy(hex, 15, encryptedpart1, 0, 8);

            // derive decryption key
            byte[] addresshashplusownerentropy = new byte[12];
            Array.Copy(hex, 3, addresshashplusownerentropy, 0, 4);
            Array.Copy(intermediate.ownerentropy, 0, addresshashplusownerentropy, 4, 8);
            byte[] derived = new byte[64];
            CryptSharp.Utility.SCrypt.ComputeKey(intermediate.passpoint, addresshashplusownerentropy, 1024, 1, 1, 1, derived);
            byte[] derivedhalf2 = new byte[32];
            Array.Copy(derived, 32, derivedhalf2, 0, 32);

            // decrypt encrypted payload
            var aes = Aes.Create();

            aes.KeySize = 256;
            aes.Mode    = CipherMode.ECB;
            aes.Key     = derivedhalf2;
            ICryptoTransform decryptor = aes.CreateDecryptor();

            byte[] unencryptedpart2 = new byte[16];
            decryptor.TransformBlock(encryptedpart2, 0, 16, unencryptedpart2, 0);
            decryptor.TransformBlock(encryptedpart2, 0, 16, unencryptedpart2, 0);
            for (int i = 0; i < 16; i++)
            {
                unencryptedpart2[i] ^= derived[i + 16];
            }

            // take the decrypted part and recover encrypted part 1
            Array.Copy(unencryptedpart2, 0, encryptedpart1, 8, 8);

            // decrypt part 1
            byte[] unencryptedpart1 = new byte[16];
            decryptor.TransformBlock(encryptedpart1, 0, 16, unencryptedpart1, 0);
            decryptor.TransformBlock(encryptedpart1, 0, 16, unencryptedpart1, 0);
            for (int i = 0; i < 16; i++)
            {
                unencryptedpart1[i] ^= derived[i];
            }

            // recover seedb
            byte[] seedb = new byte[24];
            Array.Copy(unencryptedpart1, 0, seedb, 0, 16);
            Array.Copy(unencryptedpart2, 8, seedb, 16, 8);

            // turn seedb into factorb
            Sha256Digest sha256 = new Sha256Digest();

            sha256.BlockUpdate(seedb, 0, 24);
            byte[] factorb = new byte[32];
            sha256.DoFinal(factorb, 0);
            sha256.BlockUpdate(factorb, 0, 32);
            sha256.DoFinal(factorb, 0);

            // get private key
            var        ps         = Org.BouncyCastle.Asn1.Sec.SecNamedCurves.GetByName("secp256k1");
            BigInteger privatekey = new BigInteger(1, intermediate.passfactor).Multiply(new BigInteger(1, factorb)).Mod(ps.N);

            // use private key
            return(new KeyPair(privatekey, this.IsCompressedPoint, this._addressType));
        }
        void ThreadProc()
        {
            try {
                Bip38Intermediate mostrecentcode = null;

                while (leftToDo-- > 0) {
                    if (userDidCancel) {

                        return;

                    }
                    if (mostrecentcode==null) {
                        mostrecentcode = new Bip38Intermediate (thePassphrase, Bip38Intermediate.Interpretation.Passphrase, 1);
                    } else {
                        mostrecentcode = new Bip38Intermediate (mostrecentcode);
                    }
                    generatedCodes.Add (mostrecentcode.Code);
                    InvokeOnMainThread (delegate {
                        if (IsViewLoaded) {
                            btnGenerate.SetTitle ("Cancel (done " + generatedCodes.Count + ")", UIControlState.Normal);
                        }
                    });

                }

                lotNumber = mostrecentcode.LotNumber;

                if (userDidCancel == false) {
                    InvokeOnMainThread (delegate {
                        if (IsViewLoaded) {
                            if (MFMailComposeViewController.CanSendMail) {

                                _mail = new MFMailComposeViewController ();

                                _mail.SetSubject("Codes");

                                string msgstart = "Below find the code you'll need for generating my passphrase-protected cryptocurrency materials.  " +
                                    "This code was derived from the passphrase I have selected.  I understand that I will need to remember my original passphrase to decrypt the key.\r\n\r\n" +
                                    "Lot number: " + mostrecentcode.LotNumber + "\r\n";

                                if (generatedCodes.Count != 1) {
                                    msgstart = "Below find the codes you'll need for generating my passphrase-protected cryptocurrency materials.\r\n\r\n" +
                                    "Each code was derived from the passphrase I have selected.  I understand that I will need to remember my original passphrase to decrypt my keys.\r\n\r\n" +
                                            "I am expecting my materials will show a lot number of " + mostrecentcode.LotNumber + " and sequence numbers 1 thru " + mostrecentcode.SequenceNumber + ".\r\n\r\n";
                                }

                                string msgBody = msgstart +

                                    String.Join ("\r\n", generatedCodes);

                                Console.WriteLine (msgBody);
                                _mail.SetMessageBody (msgBody,

                                                  false);

                                _mail.Finished += HandleMailFinished;

                                this.PresentModalViewController (_mail, true);

                            } else {

                                using (var alert = new UIAlertView()) {
                                    alert.Message = "The tool could not launch Mail";
                                    alert.Title = "Mail not available";
                                    alert.AddButton ("OK");
                                    alert.Show ();
                                }

                            }
                        }
                    });

                }
            } finally {
                InvokeOnMainThread (delegate {
                    if (IsViewLoaded) {
                        workerRunning = false;
                        userDidCancel = false;

                        btnGenerate.SetTitle("Generate and E-mail Codes", UIControlState.Normal);
                        actIndicator.StopAnimating();
                    }
                });
            }
        }