def test_imported_privkey(setup_wallets): for n in ["mainnet", "testnet"]: privkey = "7d998b45c219a1e38e99e7cbd312ef67f77a455a9b50c730c27f02c6f730dfb401" jm_single().config.set("BLOCKCHAIN", "network", n) password = "******" password_key = bitcoin.bin_dbl_sha256(password) wifprivkey = bitcoin.wif_compressed_privkey(privkey, get_p2pk_vbyte()) #mainnet is "L1RrrnXkcKut5DEMwtDthjwRcTTwED36thyL1DebVrKuwvohjMNi" #to verify use from_wif_privkey and privkey_to_address if n == "mainnet": iaddr = "1LDsjB43N2NAQ1Vbc2xyHca4iBBciN8iwC" else: iaddr = "mzjq2E92B3oRB7yDKbwM7XnPaAnKfRERw2" privkey_bin = bitcoin.from_wif_privkey( wifprivkey, vbyte=get_p2pk_vbyte()).decode('hex')[:-1] encrypted_privkey = encryptData(password_key, privkey_bin) encrypted_privkey_bad = encryptData(password_key, privkey_bin[:6]) walletdir = "wallets" testwalletname = "test" + n pathtowallet = os.path.join(walletdir, testwalletname) seed = bitcoin.sha256("\xaa" * 64)[:32] encrypted_seed = encryptData(password_key, seed.decode('hex')) timestamp = datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S") for ep in [encrypted_privkey, encrypted_privkey_bad]: walletfilejson = { 'creator': 'joinmarket project', 'creation_time': timestamp, 'encrypted_seed': encrypted_seed.encode('hex'), 'network': n, 'index_cache': [[0, 0]] * 5, 'imported_keys': [{ 'encrypted_privkey': ep.encode('hex'), 'mixdepth': 0 }] } walletfile = json.dumps(walletfilejson) if not os.path.exists(walletdir): os.makedirs(walletdir) with open(pathtowallet, "wb") as f: f.write(walletfile) if ep == encrypted_privkey_bad: with pytest.raises(Exception) as e_info: Wallet(testwalletname, password, 5, 6, False, False) continue newwallet = Wallet(testwalletname, password, 5, 6, False, False) assert newwallet.seed == seed #test accessing the key from the addr assert newwallet.get_key_from_addr( iaddr) == bitcoin.from_wif_privkey(wifprivkey, vbyte=get_p2pk_vbyte()) if n == "testnet": jm_single().bc_interface.sync_wallet(newwallet) load_program_config()
def wallet_generate_recover_bip39( method, walletspath, default_wallet_name, callbacks=(cli_display_user_words, cli_user_mnemonic_entry, cli_get_wallet_passphrase_check, cli_get_wallet_file_name, cli_get_mnemonic_extension)): """Optionally provide callbacks: 0 - display seed 1 - enter seed (for recovery) 2 - enter wallet password 3 - enter wallet file name 4 - enter mnemonic extension The defaults are for terminal entry. """ #using 128 bit entropy, 12 words, mnemonic module m = Mnemonic("english") if method == "generate": mnemonic_extension = callbacks[4]() words = m.generate() callbacks[0](words, mnemonic_extension) elif method == 'recover': words, mnemonic_extension = callbacks[1]() if not words: return False entropy = str(m.to_entropy(words)) password = callbacks[2]() if not password: return False password_key = btc.bin_dbl_sha256(password) encrypted_entropy = encryptData(password_key, entropy) encrypted_mnemonic_extension = None if mnemonic_extension: mnemonic_extension = mnemonic_extension.strip() #check all ascii printable if not all([a > '\x19' and a < '\x7f' for a in mnemonic_extension]): return False #padding to stop an adversary easily telling how long the mn extension is #padding at the start because of how aes blocks are combined #checksum in order to tell whether the decryption was successful cleartext_length = 79 padding_length = cleartext_length - 10 - len(mnemonic_extension) if padding_length > 0: padding = os.urandom(padding_length).replace('\xff', '\xfe') else: padding = '' cleartext = (padding + '\xff' + mnemonic_extension + '\xff' + btc.dbl_sha256(mnemonic_extension)[:8]) encrypted_mnemonic_extension = encryptData(password_key, cleartext) return persist_walletfile(walletspath, default_wallet_name, encrypted_entropy, encrypted_mnemonic_extension, callbacks=(callbacks[3], ))
def wallet_importprivkey(wallet, mixdepth): print('WARNING: This imported key will not be recoverable with your 12 ' + 'word mnemonic phrase. Make sure you have backups.') print('WARNING: Handling of raw ECDSA bitcoin private keys can lead to ' 'non-intuitive behaviour and loss of funds.\n Recommended instead ' 'is to use the \'sweep\' feature of sendpayment.py ') privkeys = raw_input('Enter private key(s) to import: ') privkeys = privkeys.split(',') if ',' in privkeys else privkeys.split() # TODO read also one key for each line for privkey in privkeys: # TODO is there any point in only accepting wif format? check what # other wallets do privkey_bin = btc.from_wif_privkey(privkey, vbyte=get_p2pk_vbyte()).decode('hex')[:-1] encrypted_privkey = encryptData(wallet.password_key, privkey_bin) if 'imported_keys' not in wallet.walletdata: wallet.walletdata['imported_keys'] = [] wallet.walletdata['imported_keys'].append( {'encrypted_privkey': encrypted_privkey.encode('hex'), 'mixdepth': mixdepth}) if wallet.walletdata['imported_keys']: fd = open(wallet.path, 'w') fd.write(json.dumps(wallet.walletdata)) fd.close() print('Private key(s) successfully imported')
def wallet_generate_recover(method, walletspath, default_wallet_name='wallet.json'): if jm_single().config.get("POLICY", "segwit") == "true": #Here using default callbacks for scripts (not used in Qt) return wallet_generate_recover_bip39(method, walletspath, default_wallet_name) if method == 'generate': seed = btc.sha256(os.urandom(64))[:32] words = mn_encode(seed) print('Write down this wallet recovery seed\n\n' + ' '.join(words) + '\n') elif method == 'recover': words = raw_input('Input 12 word recovery seed: ') words = words.split() # default for split is 1 or more whitespace chars if len(words) != 12: print('ERROR: Recovery seed phrase must be exactly 12 words.') return False seed = mn_decode(words) print(seed) password = cli_get_wallet_passphrase_check() if not password: return False password_key = btc.bin_dbl_sha256(password) encrypted_seed = encryptData(password_key, seed.decode('hex')) return persist_walletfile(walletspath, default_wallet_name, encrypted_seed)
def wallet_generate_recover_bip39( method, walletspath, default_wallet_name, callbacks=(cli_user_words, cli_user_words_entry, cli_password_check, cli_get_walletname)): """Optionally provide callbacks: 0 - display seed 1 - enter seed (for recovery) 2 - enter password 3 - enter wallet name The defaults are for terminal entry. """ #using 128 bit entropy, 12 words, mnemonic module m = Mnemonic("english") if method == "generate": words = m.generate() callbacks[0](words) elif method == 'recover': words = callbacks[1]() entropy = str(m.to_entropy(words)) password, password_key = callbacks[2]() if not password: return False encrypted_entropy = encryptData(password_key, entropy) return persist_walletfile(walletspath, default_wallet_name, encrypted_entropy, callbacks=(callbacks[3], ))
def create_wallet_for_sync(wallet_file, password, wallet_structure, a): #Prepare a testnet wallet file for this wallet password_key = bitcoin.bin_dbl_sha256(password) #We need a distinct seed for each run so as not to step over each other; #make it through a deterministic hash seedh = bitcoin.sha256("".join([str(x) for x in a]))[:32] encrypted_seed = encryptData(password_key, seedh.decode('hex')) timestamp = datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S") walletfilejson = { 'creator': 'joinmarket project', 'creation_time': timestamp, 'encrypted_seed': encrypted_seed.encode('hex'), 'network': get_network() } walletfile = json.dumps(walletfilejson) if not os.path.exists('wallets'): os.makedirs('wallets') with open(os.path.join('wallets', wallet_file), "wb") as f: f.write(walletfile) #The call to Wallet() in make_wallets should now find the file #and read from it: return make_wallets(1, [wallet_structure], fixed_seeds=[wallet_file], test_wallet=True, passwords=[password])[0]['wallet']
def test_wallet_create(setup_wallets, includecache, wrongnet, storepwd, extendmd, pwdnumtries): walletdir, pathtowallet, testwalletname, wallet = create_default_testnet_wallet( ) assert wallet.get_key( 4, 1, 17) == "1289ca322f96673acef83f396a9735840e3ab69f0459cf9bfa8d9985a876534401" assert wallet.get_addr(2, 0, 5) == "myWPu9QJWHGE79XAmuKkwKgNk8vsr5evpk" jm_single().bc_interface.wallet_synced = True assert wallet.get_new_addr(1, 0) == "mi88ZgDGPmarzcsU6S437h9CY9BLmgH5M6" assert wallet.get_external_addr(3) == "mvChQuChnXVhqvH67wfMxrodPQ7xccdVJU" addr3internal = wallet.get_internal_addr(3) assert addr3internal == "mv26o79Bauf2miJMoxoSu1vXmfXnk85YPQ" assert wallet.get_key_from_addr( addr3internal) == "2a283c9a2168a25509e2fb944939637228c50c8b4fecd9024650316c4584246501" dummyaddr = "mvw1NazKDRbeNufFANqpYNAANafsMC2zVU" assert not wallet.get_key_from_addr(dummyaddr) #Make a new Wallet(), and prepare a testnet wallet file for this wallet password = "******" password_key = bitcoin.bin_dbl_sha256(password) seed = bitcoin.sha256("\xaa" * 64)[:32] encrypted_seed = encryptData(password_key, seed.decode('hex')) timestamp = datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S") net = get_network() if not wrongnet else 'mainnnet' walletfilejson = {'creator': 'joinmarket project', 'creation_time': timestamp, 'encrypted_seed': encrypted_seed.encode('hex'), 'network': net} if includecache: mmd = wallet.max_mix_depth if not extendmd else wallet.max_mix_depth + 5 print("using mmd: " + str(mmd)) walletfilejson.update({'index_cache': [[0, 0]] * mmd}) walletfile = json.dumps(walletfilejson) if not os.path.exists(walletdir): os.makedirs(walletdir) with open(pathtowallet, "wb") as f: f.write(walletfile) if wrongnet: with pytest.raises(ValueError) as e_info: Wallet(testwalletname, password, 5, 6, extend_mixdepth=extendmd, storepassword=storepwd) return from string import ascii_letters for i in range( pwdnumtries): #multiple tries to ensure pkcs7 error is triggered with pytest.raises(WalletError) as e_info: wrongpwd = "".join([random.choice(ascii_letters) for _ in range(20) ]) Wallet(testwalletname, wrongpwd, 5, 6, extend_mixdepth=extendmd, storepassword=storepwd) with pytest.raises(WalletError) as e_info: Wallet(testwalletname, None, 5, 6, extend_mixdepth=extendmd, storepassword=storepwd) newwallet = Wallet(testwalletname, password, 5, 6, extend_mixdepth=extendmd, storepassword=storepwd) assert newwallet.seed == seed #now we have a functional wallet + file, update the cache; first try #with failed paths oldpath = newwallet.path newwallet.path = None newwallet.update_cache_index() newwallet.path = "fake-path-definitely-doesnt-exist" newwallet.update_cache_index() #with real path newwallet.path = oldpath newwallet.index = [[1, 1]] * 5 newwallet.update_cache_index() #ensure we cannot find a mainnet wallet from seed seed = "goodbye" jm_single().config.set("BLOCKCHAIN", "network", "mainnet") with pytest.raises(IOError) as e_info: Wallet(seed, 5, 6, False, False) load_program_config()
def test_wallet_create(setup_wallets, includecache, wrongnet, storepwd, extendmd, pwdnumtries): walletdir, pathtowallet, testwalletname, wallet = create_default_testnet_wallet( ) assert wallet.get_key( 4, 1, 17) == "96095d7542e4e832c476b9df7e49ca9e5be61ad3bb8c8a3bdd8e141e2f4caf9101" assert wallet.get_addr(2, 0, 5) == "2NBUxbEQrGPKrYCV6d4o7Y4AtJ34Uy6gZZg" jm_single().bc_interface.wallet_synced = True assert wallet.get_new_addr(1, 0) == "2Mz817RE6zqywgkG2h9cATUoiXwnFSxufk2" assert wallet.get_external_addr(3) == "2N3gn65WXEzbLnjk5FLDZPc1pL6ebvZAmoA" addr3internal = wallet.get_internal_addr(3) assert addr3internal == "2N5NMTYogAyrGhDtWBnVQUp1kgwwFzcf7UM" assert wallet.get_key_from_addr( addr3internal) == "089a7173314d29f99e02a37e36da517ce41537a317c83284db1f33dda0af0cc201" dummyaddr = "mvw1NazKDRbeNufFANqpYNAANafsMC2zVU" assert not wallet.get_key_from_addr(dummyaddr) #Make a new Wallet(), and prepare a testnet wallet file for this wallet password = "******" password_key = bitcoin.bin_dbl_sha256(password) seed = bitcoin.sha256("\xaa" * 64)[:32] encrypted_seed = encryptData(password_key, seed.decode('hex')) timestamp = datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S") net = get_network() if not wrongnet else 'mainnnet' walletfilejson = {'creator': 'joinmarket project', 'creation_time': timestamp, 'encrypted_seed': encrypted_seed.encode('hex'), 'network': net} if includecache: mmd = wallet.max_mix_depth if not extendmd else wallet.max_mix_depth + 5 print("using mmd: " + str(mmd)) walletfilejson.update({'index_cache': [[0, 0]] * mmd}) walletfile = json.dumps(walletfilejson) if not os.path.exists(walletdir): os.makedirs(walletdir) with open(pathtowallet, "wb") as f: f.write(walletfile) if wrongnet: with pytest.raises(ValueError) as e_info: SegwitWallet(testwalletname, password, 5, 6, extend_mixdepth=extendmd, storepassword=storepwd) return from string import ascii_letters for i in range( pwdnumtries): #multiple tries to ensure pkcs7 error is triggered with pytest.raises(WalletError) as e_info: wrongpwd = "".join([random.choice(ascii_letters) for _ in range(20) ]) SegwitWallet(testwalletname, wrongpwd, 5, 6, extend_mixdepth=extendmd, storepassword=storepwd) with pytest.raises(WalletError) as e_info: SegwitWallet(testwalletname, None, 5, 6, extend_mixdepth=extendmd, storepassword=storepwd) newwallet = SegwitWallet(testwalletname, password, 5, 6, extend_mixdepth=extendmd, storepassword=storepwd) assert newwallet.seed == wallet.wallet_data_to_seed(seed) #now we have a functional wallet + file, update the cache; first try #with failed paths oldpath = newwallet.path newwallet.path = None newwallet.update_cache_index() newwallet.path = "fake-path-definitely-doesnt-exist" newwallet.update_cache_index() #with real path newwallet.path = oldpath newwallet.index = [[1, 1]] * 5 newwallet.update_cache_index() #ensure we cannot find a mainnet wallet from seed seed = "goodbye" jm_single().config.set("BLOCKCHAIN", "network", "mainnet") with pytest.raises(IOError) as e_info: Wallet(seed, 5, 6, False, False) load_program_config()
'\n') elif method == 'recover': words = raw_input('Input 12 word recovery seed: ') words = words.split() # default for split is 1 or more whitespace chars if len(words) != 12: print('ERROR: Recovery seed phrase must be exactly 12 words.') sys.exit(0) seed = mn_decode(words) print(seed) password = getpass.getpass('Enter wallet encryption passphrase: ') password2 = getpass.getpass('Reenter wallet encryption passphrase: ') if password != password2: print('ERROR. Passwords did not match') sys.exit(0) password_key = btc.bin_dbl_sha256(password) encrypted_seed = encryptData(password_key, seed.decode('hex')) timestamp = datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S") walletfile = json.dumps({'creator': 'joinmarket project', 'creation_time': timestamp, 'encrypted_seed': encrypted_seed.encode('hex'), 'network': get_network()}) walletname = raw_input('Input wallet file name (default: wallet.json): ') if len(walletname) == 0: walletname = 'wallet.json' walletpath = os.path.join('wallets', walletname) # Does a wallet with the same name exist? if os.path.isfile(walletpath): print('ERROR: ' + walletpath + ' already exists. Aborting.') sys.exit(0) else: fd = open(walletpath, 'w')