BIP38 - Encrypting Feathercoin Private Keys
-
There is another company in addition to Netnerd who is producing collectable Feathercoin Wallets: http://finitebydesign.net/
One of my favourite features in Bitcoin is BIP38 which allows you to encrypt your private keys. This could be really good for collectable companies as they can get customers to send in their own private keys without worrying about trust.
What’s it going to take to get this feature in to Feathercoin?
-
Interesting : Bip 38
-
[quote name=“chrisj” post=“58458” timestamp=“1392120022”]
There is another company in addition to Netnerd who is producing collectable Feathercoin Wallets: http://finitebydesign.net/One of my favourite features in Bitcoin is BIP38 which allows you to encrypt your private keys. This could be really good for collectable companies as they can get customers to send in their own private keys without worrying about trust.
What’s it going to take to get this feature in to Feathercoin?
[/quote]This would be pretty great. Where can we see the dev process in action? Is there a mailing list? Not sure, but it looks like GH is used as a dumping ground and not really a place to view or participate in works-in-progress.
-
This sounds interesting.
-
I am sure the concepts can be easily transferred - if not, it seems to just be AES encryption turned into QR code. We can’t have a FIP38? 8)
-
-
Cheers Estrabd, interesting read.
-
This is something that I would like to see implemented into feathercoin as this is something that could be used for my NFC version of the Wallet Physical Coin.
-
BIP38 is a ridiculously tough encryption, it is also memory intensive!
I tried bruteforcing one a few days back with a C# implementation and it takes 0.25 seconds to decrypt one on a fourth generation quad core i5 processor. Memory usage = 300MB. Despite my best effort to optimize the code… (100% CPU utilization).
Having a password of at least 6 character will ensure that it’ll take 8000 years on a single machine. (72 ^ 6 tries)
Great! I support that since its safe.
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; } } #region Bruteforce /* An array containing the characters which will be used to create the brute force keys, * if less characters are used (e.g. only lower case chars) the faster the password is matched */ private static char[] charactersToTest = { '1','2','3','4','5','6','7','8','9','0', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z','A','B','C','D','E', 'F','G','H','I','J','K','L','M','N','O','P','Q','R', 'S','T','U','V','W','X','Y','Z','!','$','#','@','-' }; /* The length of the charactersToTest Array is stored in a * additional variable to increase performance */ private static int charactersToTestLength = charactersToTest.Length; private static long computedKeys = 0; private bool passFound = false; private string pass = null; private string currentComputingPass = ""; private System.Windows.Forms.Timer updateTimer; private void button_bruteforcePrivKey_Click(object sender, EventArgs e) { // Disable button button_bruteforcePrivKey.Enabled = false; // Referencing string privateKey = txtPrivWIF.Text; // 6PfR1Z9cEHVsHaQTzggoaZmEeQVQAqPbR6A5gd3Eop3JBTZ9oQLUi7hShs passFound = false; pass = null; currentComputingPass = ""; int threads = 10; int.TryParse(textBox_threads.Text, out threads); if (updateTimer != null) { updateTimer.Stop(); updateTimer = null; } updateTimer = new System.Windows.Forms.Timer(); updateTimer.Interval = 100; updateTimer.Tick += updateTimer_Tick; updateTimer.Start(); try { object interpretation = StringInterpreter.Interpret(privateKey, compressed: compressToolStripMenuItem.Checked, addressType: this.AddressTypeByte); if (interpretation is PassphraseKeyPair) { string passPhrase = txtPassphrase.Text; PassphraseKeyPair ppkp = (PassphraseKeyPair)interpretation; Task.Run(() => { startBruteForce(6, ppkp); }); } } catch (Exception ae) { MessageBox.Show(ae.Message); } finally { } } void updateTimer_Tick(object sender, EventArgs e) { label_tries.Text = computedKeys.ToString(); label_bruteforcetxt.Text = currentComputingPass; if (pass != null) { txtPassphrase.Text = pass; } } /// /// Starts the recursive method which will create the keys via brute force /// /// The length of the key private void startBruteForce(int keyLength, PassphraseKeyPair ppkp) { var keyChars = createCharArray(keyLength, charactersToTest[0]); // The index of the last character will be stored for slight perfomance improvement var indexOfLastChar = keyLength - 1; createNewKey(0, keyChars, keyLength, indexOfLastChar, ppkp); } /// /// Creates a new char array of a specific length filled with the defaultChar /// /// The length of the array /// The char with whom the array will be filled /// private char[] createCharArray(int length, char defaultChar) { return (from c in new char[length] select defaultChar).ToArray(); } /// /// This is the main workhorse, it creates new keys and compares them to the password until the password /// is matched or all keys of the current key length have been checked /// /// The position of the char which is replaced by new characters currently /// The current key represented as char array /// The length of the key /// The index of the last character of the key private void createNewKey(int currentCharPosition, char[] keyChars, int keyLength, int indexOfLastChar, PassphraseKeyPair ppkp) { int tryingNumber = 0; var nextCharPosition = currentCharPosition + 1; // We are looping trough the full length of our charactersToTest array for (int i = 0; i < charactersToTestLength; i++) { /* The character at the currentCharPosition will be replaced by a * new character from the charactersToTest array => a new key combination will be created */ keyChars[currentCharPosition] = charactersToTest[i]; // The method calls itself recursively until all positions of the key char array have been replaced if (currentCharPosition < indexOfLastChar) { createNewKey(nextCharPosition, keyChars, keyLength, indexOfLastChar, ppkp); } else { // A new key has been created, remove this counter to improve performance computedKeys++; string txt = new String(keyChars); while (tryingNumber > 4) { Thread.Sleep(10); } tryingNumber++; Task.Run(() => { /* The char array will be converted to a string and compared to the password. If the password * is matched the loop breaks and the password is stored as result. */ currentComputingPass = txt; System.Diagnostics.Debug.WriteLine(txt); if (ppkp.DecryptWithPassphrase(txt)) { if (!passFound) { passFound = true; pass = txt; } } tryingNumber--; }); if (passFound) return; } } } #endregion
-
I concur - this would open up a ton of wonderful, flexible uses for Feathercoin wallets.