From b5b5f92b9d3f37a2249ff6102fa06553007119f4 Mon Sep 17 00:00:00 2001 From: Silicon Rainbow Date: Sun, 21 Sep 2014 17:39:22 +0700 Subject: [PATCH 1/6] Add Python cache files to .gitignore. --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pyc From 9e61a0da928776b2887edf2141c76f757ad13c6c Mon Sep 17 00:00:00 2001 From: Silicon Rainbow Date: Sun, 21 Sep 2014 17:37:57 +0700 Subject: [PATCH 2/6] Fix minor errors with print statements. --- bip38.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bip38.py b/bip38.py index 736da87..239657c 100644 --- a/bip38.py +++ b/bip38.py @@ -108,7 +108,7 @@ def runtests(): decryptedpriv = bip38_decrypt(resultpriv,passphrase) if decryptedpriv == expectedwif: print('Decryption Success!') - print('-')*80 + print('-'*80) def compresstest(): for test in compresstests: @@ -131,4 +131,4 @@ def compresstest(): print('Decryption Failed!') print('Expected %s' %(expectedwif)) print('Returned %s' %(decryptedpriv)) - print('-')*80 + print('-'*80) From a93434bfb0d4deb61ce35a18fea29e229fb41922 Mon Sep 17 00:00:00 2001 From: Silicon Rainbow Date: Sun, 21 Sep 2014 17:36:04 +0700 Subject: [PATCH 3/6] Raise an exception instead of printing a warning if an address fails verification. --- bip38.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bip38.py b/bip38.py index 239657c..d65a7b1 100644 --- a/bip38.py +++ b/bip38.py @@ -14,6 +14,10 @@ # TODO: # verify encrypted privkey checksum before decrypting? +class AddressVerificationFailure(StandardError): + """This exception is raised if a decryption result fails address + verification.""" + tests = [{'passphrase':'TestingOneTwoThree', 'expectedpriv':"6PRVWUbkzzsbcVac2qwfssoUJAN1Xhrg6bNk8J7Nzm5H7kxEbn2Nh2ZoGg", @@ -62,7 +66,7 @@ def bip38_encrypt(privkey,passphrase): encrypted_privkey = base58.b58encode(encrypted_privkey) return encrypted_privkey -def bip38_decrypt(encrypted_privkey,passphrase): +def bip38_decrypt(encrypted_privkey,passphrase,verify_key=True): '''BIP0038 non-ec-multiply decryption. Returns WIF privkey.''' d = base58.b58decode(encrypted_privkey) d = d[2:] @@ -92,7 +96,7 @@ def bip38_decrypt(encrypted_privkey,passphrase): wif = encode_privkey(priv,'wif') addr = pubtoaddr(pub) if hashlib.sha256(hashlib.sha256(addr).digest()).digest()[0:4] != addresshash: - print('Addresshash verification failed! Password is likely incorrect.') + if verify_key: raise(AddressVerificationFailure) return wif def runtests(): From de80b011be9d7c8b45131822282359edd7d394cd Mon Sep 17 00:00:00 2001 From: Silicon Rainbow Date: Sun, 21 Sep 2014 17:38:38 +0700 Subject: [PATCH 4/6] Add a command line interface. --- bip38.py | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) mode change 100644 => 100755 bip38.py diff --git a/bip38.py b/bip38.py old mode 100644 new mode 100755 index d65a7b1..c09e1bc --- a/bip38.py +++ b/bip38.py @@ -136,3 +136,73 @@ def compresstest(): print('Expected %s' %(expectedwif)) print('Returned %s' %(decryptedpriv)) print('-'*80) + +if __name__ == '__main__': + import sys + import getpass + + if 1 < len(sys.argv) < 4 and sys.argv[1] == '-d': + if len(sys.argv) > 2: + enckey = sys.argv[2] + else: + if sys.stdin.isatty(): + enckey = raw_input('BIP38 Encrypted Privkey: ').strip() + else: + enckey = raw_input().strip() + + try: + base58.b58encode_check(enckey) + except AssertionError: + sys.stderr.write('Invalid BIP38 privkey.\n') + sys.exit(1) + + if sys.stdin.isatty(): + passwd = getpass.getpass('Password: ') + else: + passwd = raw_input() + + try: + privkey = bip38_decrypt(enckey, passwd) + except AddressVerificationFailure: + sys.stderr.write('Invalid password.\n') + sys.exit(2) + + if sys.stdout.isatty(): print('') + print(privkey) + + elif len(sys.argv) == 1 or sys.argv[1] != '--help': + if sys.stdin.isatty(): + privkey = raw_input('Private key: ').strip() + else: + privkey = raw_input().strip() + + try: + base58.b58encode_check(privkey) + except AssertionError: + sys.stderr.write('Invalid privkey.') + sys.exit(1) + + if sys.stdin.isatty(): + while True: + passwd = getpass.getpass('Password: ') + pwconf = getpass.getpass('Confirm Password: ') + + if passwd == pwconf: + break + else: + sys.stderr.write("Passwords don't match.\n") + else: + passwd = raw_input() + + try: + if sys.stdout.isatty(): print('') + print(bip38_encrypt(privkey, passwd)) + except AssertionError: + sys.stderr.write('Invalid privkey.\n') + sys.exit(1) + + else: + sys.stderr.write( +'''%s # Generate a BIP38 privkey from a normal privkey. +%s -d [bip38_encrypted_key] # Decrypt a BIP38 encrypted key. +'''.replace('%s', sys.argv[0])) From 035a458c63a6f0bccca437c39541261876914d55 Mon Sep 17 00:00:00 2001 From: Silicon Rainbow Date: Sun, 21 Sep 2014 18:05:29 +0700 Subject: [PATCH 5/6] Update README with CLI usage info. --- README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/README.md b/README.md index be8da76..9cf9f98 100644 --- a/README.md +++ b/README.md @@ -6,3 +6,27 @@ Encrypt function based on modified code from https://github.com/sowbug/cold-wall Thanks to /u/thatdontmakenosense for finishing the decrypt function +CLI Usage +--------- + + # Create a new BIP38 encrypted key. + $ ./bip38.py + Private key: + Password: + Confirm Password: + + + + # Decrypt a BIP38 encrypted key. + $ ./bip38.py -d + BIP38 Encrypted Privkey: + Password: + + + + # or + + $ ./bip38.py -d + Password: + + From bfd6e785ce73c7ab74b5da06bf59b39eb0f2b41b Mon Sep 17 00:00:00 2001 From: Silicon Rainbow Date: Sun, 21 Sep 2014 19:09:51 +0700 Subject: [PATCH 6/6] Add -m command line option. --- README.md | 9 +++++++++ bip38.py | 21 +++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/README.md b/README.md index 9cf9f98..af23095 100644 --- a/README.md +++ b/README.md @@ -30,3 +30,12 @@ CLI Usage Password: + + # Create many BIP38 keys with the same passphrase. + $ ./bip38.py -m + Password: + Private key: + Private key: + Private key: + : + : diff --git a/bip38.py b/bip38.py index c09e1bc..ac91936 100755 --- a/bip38.py +++ b/bip38.py @@ -170,6 +170,26 @@ def compresstest(): if sys.stdout.isatty(): print('') print(privkey) + elif len(sys.argv) == 2 and sys.argv[1] == '-m': + passwd = raw_input('Password: ') + keys = [] + while True: + key = raw_input('Private key: ') + if key == '': break + keys.append(key) + + exit_code = 0 + for privkey in keys: + try: + enckey = bip38_encrypt(privkey, passwd) + addr = privkey_to_address(privkey) + print(addr + ': ' + enckey) + except: + sys.stderr.write('Invalid privkey: %s.\n' % privkey) + exit_code = 1 + + sys.exit(exit_code) + elif len(sys.argv) == 1 or sys.argv[1] != '--help': if sys.stdin.isatty(): privkey = raw_input('Private key: ').strip() @@ -205,4 +225,5 @@ def compresstest(): sys.stderr.write( '''%s # Generate a BIP38 privkey from a normal privkey. %s -d [bip38_encrypted_key] # Decrypt a BIP38 encrypted key. +%s -m # Generate multiple BIP38 privkeys with a single passphrase. '''.replace('%s', sys.argv[0]))