/// <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); }
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; }
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(); } }); } }