Advertisement
Guest User

decrypt_multibit_classic_keys2.py

a guest
May 31st, 2017
2,001
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 8.44 KB | None | 0 0
  1. #!/usr/bin/python
  2.  
  3. # decrypt_multibit_classic_walletkeys.py
  4. # Extract Private Keys from a MultiBit Classic wallet
  5. # Copyright (C) 2017, HCP
  6. # All rights reserved.
  7. #
  8. # Based on decrypt_bitcoinj_seed.pyw
  9. # Copyright (C) 2014, 2016 Christopher Gurnee
  10. #
  11. # Redistribution and use in source and binary forms, with or without
  12. # modification, are permitted provided that the following conditions
  13. # are met:
  14. #
  15. # 1. Redistributions of source code must retain the above copyright
  16. # notice, this list of conditions and the following disclaimer.
  17. #
  18. # 2. Redistributions in binary form must reproduce the above copyright
  19. # notice, this list of conditions and the following disclaimer in the
  20. # documentation and/or other materials provided with the distribution.
  21. #
  22. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  23. # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  24. # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  25. # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  26. # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  27. # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  28. # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  29. # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  30. # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  31. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  32. #  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  33.  
  34. # If you find this program helpful, please consider a small
  35. # donation to the developer at the following Bitcoin address:
  36. #
  37. #           1NyVDDmhZPcyKhyrkiUFZbqPPuiYxwTujb
  38. #
  39. #                      Thank You!
  40.  
  41. from __future__ import print_function
  42.  
  43. __version__ =  '0.4.0'
  44.  
  45. import hashlib, sys, os, getpass
  46. import aespython.key_expander, aespython.aes_cipher, aespython.cbc_mode
  47. import wallet_pb2, binascii, bitcoin
  48. import pylibscrypt
  49.        
  50. sha256 = hashlib.sha256
  51. md5    = hashlib.md5
  52.  
  53.  
  54. key_expander = aespython.key_expander.KeyExpander(256)
  55.  
  56. def aes256_cbc_decrypt(ciphertext, key, iv):
  57.     """decrypts the ciphertext using AES256 in CBC mode
  58.  
  59.    :param ciphertext: the encrypted ciphertext
  60.    :type ciphertext: str
  61.    :param key: the 256-bit key
  62.    :type key: str
  63.    :param iv: the 128-bit initialization vector
  64.    :type iv: str
  65.    :return: the decrypted ciphertext, or raises a ValueError if the key was wrong
  66.    :rtype: str
  67.    """
  68.     block_cipher  = aespython.aes_cipher.AESCipher( key_expander.expand(map(ord, key)) )
  69.     stream_cipher = aespython.cbc_mode.CBCMode(block_cipher, 16)
  70.     stream_cipher.set_iv(bytearray(iv))
  71.     plaintext = bytearray()
  72.     for i in xrange(0, len(ciphertext), 16):
  73.         plaintext.extend( stream_cipher.decrypt_block(map(ord, ciphertext[i:i+16])) )
  74.     padding_len = plaintext[-1]
  75.     # check for PKCS7 padding
  76.     if not (1 <= padding_len <= 16 and plaintext.endswith(chr(padding_len) * padding_len)):
  77.         raise ValueError('incorrect password')
  78.     return str(plaintext[:-padding_len])
  79.  
  80.  
  81. multibit_hd_password = None
  82. def load_wallet(wallet_file, get_password_fn):
  83.     """load and if necessary decrypt a bitcoinj wallet file
  84.  
  85.    :param wallet_file: an open bitcoinj wallet file
  86.    :type wallet_file: file
  87.    :param get_password_fn: a callback returning a password that's called iff one is required
  88.    :type get_password_fn: function
  89.    :return: the Wallet protobuf message or None if no password was entered when required
  90.    :rtype: wallet_pb2.Wallet
  91.    """
  92.  
  93.     wallet_file.seek(0)
  94.     magic_bytes = wallet_file.read(12)
  95.    
  96.     wallet_file.seek(0, os.SEEK_END)
  97.     wallet_size = wallet_file.tell()
  98.     wallet_file.seek(0)
  99.  
  100.     if magic_bytes[2:6] != b"org." and wallet_size % 16 == 0:
  101.         takes_long = not pylibscrypt._done  # if a binary library wasn't found, this'll take a while
  102.  
  103.         ciphertext = wallet_file.read()
  104.         assert len(ciphertext) % 16 == 0
  105.  
  106.         password = get_password_fn(takes_long)
  107.         if not password:
  108.             return None
  109.  
  110.         # Derive the encryption key
  111.         salt = '\x35\x51\x03\x80\x75\xa3\xb0\xc5'
  112.         key  = pylibscrypt.scrypt(password.encode('utf_16_be'), salt, olen=32)
  113.  
  114.         # Decrypt the wallet ( v0.5.0+ )
  115.         try:
  116.             plaintext = aes256_cbc_decrypt(ciphertext[16:], key, ciphertext[:16])
  117.             if plaintext[2:6] != b"org.":
  118.                 raise ValueError('incorrect password')
  119.         except ValueError as e:
  120.             if e.args[0] == 'incorrect password':
  121.  
  122.                 # Decrypt the wallet ( < v0.5.0 )
  123.                 iv = '\xa3\x44\x39\x1f\x53\x83\x11\xb3\x29\x54\x86\x16\xc4\x89\x72\x3e'
  124.                 plaintext = aes256_cbc_decrypt(ciphertext, key, iv)
  125.  
  126.         global multibit_hd_password
  127.         multibit_hd_password = password
  128.  
  129.     # Else it's not whole-file encrypted
  130.     else:
  131.         print("File NOT Encrypted")
  132.         password  = None
  133.         plaintext = wallet_file.read()
  134.  
  135.     # Parse the wallet protobuf
  136.     pb_wallet = wallet_pb2.Wallet()
  137.     try:
  138.         pb_wallet.ParseFromString(plaintext)
  139.     except Exception as e:
  140.         msg = 'not a wallet file: ' + str(e)
  141.         if password:
  142.             msg = "incorrect password (or " + msg + ")"
  143.         raise ValueError(msg)
  144.    
  145.     f = open('parsed_wallet.txt','w')
  146.     f.write(pb_wallet.__str__())
  147.     f.close()
  148.    
  149.     print("--------------------------------------------------------------------------------")
  150.        
  151.     if pb_wallet.encryption_type == 2:
  152.        
  153.         print("Keys are encrypted")
  154.        
  155.         takes_long = not pylibscrypt._done  # if a binary library wasn't found, this'll take a while
  156.         password = get_password_fn(takes_long)
  157.         if not password:
  158.             return None
  159.                
  160.         salt = pb_wallet.encryption_parameters.salt
  161.         dkey = pylibscrypt.scrypt(password.encode('utf_16_be'), salt, olen=32)
  162.        
  163.         for enckeys in pb_wallet.key:
  164.          
  165.           ciphertext = enckeys.encrypted_data.encrypted_private_key
  166.           iv = enckeys.encrypted_data.initialisation_vector
  167.          
  168.           privkey = aes256_cbc_decrypt(ciphertext, dkey, iv)
  169.        
  170.           print("")
  171.           print("Pubkey: " + bitcoin.pubtoaddr(enckeys.public_key))
  172.           print("Privkey: " + binascii.hexlify(privkey))
  173.           print("Privkey: " + bitcoin.encode_privkey(privkey, 'wif_compressed'))
  174.    
  175.     elif pb_wallet.encryption_type == 1:
  176.    
  177.         print("Keys NOT encrypted")
  178.      
  179.         for enckeys in pb_wallet.key:
  180.           print("")
  181.           print("Pubkey: " + bitcoin.pubtoaddr(enckeys.public_key))
  182.           print("Privkey: " + bitcoin.encode_privkey(enckeys.secret_bytes, 'wif_compressed'))
  183.  
  184.     print("")
  185.     print("--------------------------------------------------------------------------------")
  186.    
  187.     return pb_wallet
  188.  
  189.  
  190. if __name__ == '__main__':
  191.  
  192.     if len(sys.argv) != 2 or sys.argv[1].startswith('-'):
  193.         sys.exit('usage: find_unspent_multibitHD_txes.py multibitHD-wallet-file')
  194.  
  195.     wallet_file = open(sys.argv[1], 'rb')
  196.  
  197.     def get_password_factory(prompt):
  198.         def get_password(takes_long_arg_ignored):  # must return unicode
  199.             encoding = sys.stdin.encoding or 'ASCII'
  200.             if 'utf' not in encoding.lower():
  201.                 print('terminal does not support UTF; passwords with non-ASCII chars might not work', file=sys.stderr)
  202.             password = getpass.getpass(prompt + ' ')
  203.             if isinstance(password, str):
  204.                 password = password.decode(encoding)  # convert from terminal's encoding to unicode
  205.             return password
  206.         return get_password
  207.  
  208.     # These functions differ between command-line and GUI runs
  209.     get_password  = get_password_factory('This wallet file is encrypted, please enter its password:')
  210.     get_pin       = get_password_factory("This wallet's seed is encrypted with a PIN or password, please enter it:")
  211.     display_error = lambda msg: print(msg, file=sys.stderr)
  212.    
  213.     # Load (and possibly decrypt) the wallet, retrying on bad passwords
  214.     while True:
  215.         try:
  216.             wallet = load_wallet(wallet_file, get_password)
  217.             if not wallet:  # if no password was entered
  218.                 sys.exit('canceled')
  219.             break
  220.         except ValueError as e:
  221.             display_error(str(e))
  222.             if not e.args[0].startswith('incorrect password'):
  223.                 raise
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement