Advertisement
Guest User

decrypt_mbc_wallet_cipher.py

a guest
Jun 4th, 2017
1,552
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 10.23 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(7)
  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 == b"mendoza" and wallet_size % 16 == 0:
  101.         print("Cipher file is Encrypted")
  102.         takes_long = not pylibscrypt._done  # if a binary library wasn't found, this'll take a while
  103.  
  104.         ciphertext = wallet_file.read()
  105.         assert len(ciphertext) % 16 == 0
  106.  
  107.         password = get_password_fn(takes_long)
  108.         if not password:
  109.             return None
  110.  
  111.         # Derive the encryption key
  112.         salt = ciphertext[8:16] #'\x35\x51\x03\x80\x75\xa3\xb0\xc5'
  113.         iv = ciphertext[16:32]
  114.         key  = pylibscrypt.scrypt(password.encode('utf_16_be'), salt, olen=32)
  115.  
  116.         # Decrypt the wallet ( v0.5.0+ )
  117.         plaintext = aes256_cbc_decrypt(ciphertext[32:], key, iv)
  118.         if plaintext[2:6] != b"org.":
  119.           raise ValueError('incorrect password')
  120.        
  121.     # Else it's not whole-file encrypted
  122.     else:
  123.         print("File NOT Encrypted")
  124.         password  = None
  125.         plaintext = wallet_file.read()
  126.  
  127.     # Parse the wallet protobuf
  128.     pb_wallet = wallet_pb2.Wallet()
  129.     try:
  130.         pb_wallet.ParseFromString(plaintext)
  131.     except Exception as e:
  132.         msg = 'not a wallet file: ' + str(e)
  133.         if password:
  134.             msg = "incorrect password (or " + msg + ")"
  135.         raise ValueError(msg)
  136.    
  137.     f = open('parsed_cipher.txt','w')
  138.     f.write(pb_wallet.__str__())
  139.     f.close()
  140.    
  141.     print("--------------------------------------------------------------------------------")
  142.        
  143.     if pb_wallet.encryption_type == 2:
  144.        
  145.         print("Keys are encrypted")
  146.        
  147.         takes_long = not pylibscrypt._done  # if a binary library wasn't found, this'll take a while
  148.         password = get_password_fn(takes_long)
  149.         if not password:
  150.             return None
  151.                
  152.         salt = pb_wallet.encryption_parameters.salt
  153.         dkey = pylibscrypt.scrypt(password.encode('utf_16_be'), salt, olen=32)
  154.        
  155.         for enckeys in pb_wallet.key:
  156.          
  157.           ciphertext = enckeys.encrypted_data.encrypted_private_key
  158.           iv = enckeys.encrypted_data.initialisation_vector
  159.          
  160.           privkey = aes256_cbc_decrypt(ciphertext, dkey, iv)
  161.        
  162.           print("--------------------------------------------------------------------------------")
  163.           pubby = bitcoin.pubtoaddr(enckeys.public_key)
  164.           print("Pubkey: " + pubby)
  165.           print("Raw Privkey: " + binascii.hexlify(privkey))
  166.           try:
  167.             print("Raw WIF: " + bitcoin.encode_privkey(privkey, 'wif_compressed'))
  168.           except Exception as e:
  169.             print("Could not encode Raw " + str(e))
  170.           #print("len: " + str(len(privkey)))
  171.           rawhex = binascii.hexlify(privkey)
  172.           lenrawhex = len(rawhex)
  173.           if lenrawhex > 64:
  174.             print("----")
  175.             for x in range(0,lenrawhex-63):
  176.               sliced_priv = binascii.hexlify(privkey)[x:x+64]
  177.               #print("slice" + str(x) + ":" + sliced_priv)
  178.               comp_pub = bitcoin.encode_pubkey(bitcoin.privtopub(sliced_priv), 'hex_compressed')
  179.               comp_addr = bitcoin.pubtoaddr(comp_pub)
  180.               if comp_addr == pubby:
  181.                 print("Sliced Priv"+str(x)+": "+ sliced_priv)
  182.                 print("chkAddr"+str(x)+": "+ comp_addr)
  183.                 try:
  184.                   print("Sliced WIF"+str(x)+": " + bitcoin.encode_privkey(sliced_priv, 'wif_compressed'))
  185.                 except Exception as e:
  186.                   print("Could not encode sliced1 " + str(e))
  187.                 break
  188.          
  189.           #sliced_priv = binascii.hexlify(privkey)[:64]
  190.           #print("Sliced Priv1: " + sliced_priv)
  191.           #try:
  192.           #  print("Sliced WIF1: " + bitcoin.encode_privkey(sliced_priv, 'wif_compressed'))
  193.           #except Exception as e:
  194.           #  print("Could not encode sliced1 " + str(e))
  195.            
  196.           #comp_pub = bitcoin.encode_pubkey(bitcoin.privtopub(sliced_priv), 'hex_compressed')
  197.           #print("chkAddr1: " + bitcoin.pubtoaddr(comp_pub))
  198.           #print("----")
  199.           #sliced_priv2 = binascii.hexlify(privkey)[-64:]
  200.           #print("Sliced Priv2: " + sliced_priv2)
  201.           #try:
  202.           #  print("Sliced WIF2: " + bitcoin.encode_privkey(sliced_priv2, 'wif_compressed'))
  203.           #except Exception as e:
  204.           #  print("Could not encode sliced2 " + str(e))
  205.           #  
  206.           #comp_pub2 = bitcoin.encode_pubkey(bitcoin.privtopub(sliced_priv2), 'hex_compressed')
  207.           #print("chkAddr2: " + bitcoin.pubtoaddr(comp_pub2))
  208.          
  209.    
  210.     elif pb_wallet.encryption_type == 1:
  211.    
  212.         print("Keys NOT encrypted")
  213.      
  214.         for enckeys in pb_wallet.key:
  215.           print("")
  216.           print("Pubkey: " + bitcoin.pubtoaddr(enckeys.public_key))
  217.           print("Privkey: " + bitcoin.encode_privkey(enckeys.secret_bytes, 'wif_compressed'))
  218.  
  219.     print("")
  220.     print("--------------------------------------------------------------------------------")
  221.    
  222.     return pb_wallet
  223.  
  224.  
  225. if __name__ == '__main__':
  226.  
  227.     if len(sys.argv) != 2 or sys.argv[1].startswith('-'):
  228.         sys.exit('usage: find_unspent_multibitHD_txes.py multibitHD-wallet-file')
  229.  
  230.     wallet_file = open(sys.argv[1], 'rb')
  231.  
  232.     def get_password_factory(prompt):
  233.         def get_password(takes_long_arg_ignored):  # must return unicode
  234.             encoding = sys.stdin.encoding or 'ASCII'
  235.             if 'utf' not in encoding.lower():
  236.                 print('terminal does not support UTF; passwords with non-ASCII chars might not work', file=sys.stderr)
  237.             password = getpass.getpass(prompt + ' ')
  238.             if isinstance(password, str):
  239.                 password = password.decode(encoding)  # convert from terminal's encoding to unicode
  240.             return password
  241.         return get_password
  242.  
  243.     # These functions differ between command-line and GUI runs
  244.     get_password  = get_password_factory('This wallet file is encrypted, please enter its password:')
  245.     get_pin       = get_password_factory("This wallet's seed is encrypted with a PIN or password, please enter it:")
  246.     display_error = lambda msg: print(msg, file=sys.stderr)
  247.    
  248.     # Load (and possibly decrypt) the wallet, retrying on bad passwords
  249.     while True:
  250.         try:
  251.             wallet = load_wallet(wallet_file, get_password)
  252.             if not wallet:  # if no password was entered
  253.                 sys.exit('canceled')
  254.             break
  255.         except ValueError as e:
  256.             display_error(str(e))
  257.             if not e.args[0].startswith('incorrect password'):
  258.                 raise
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement