def new_masterkey(passphrase, rand=Random(), get_time=time.time, nb_iterations_test=25000): """Create a new masterkey""" crypter = Crypter() plain_master_key = rand.get_random_bytes(WALLET_CRYPTO_KEY_SIZE) rand.add_system_seeds() salt = rand.get_random_bytes(WALLET_CRYPTO_SALT_SIZE) deriv_method = MasterKey.DERIVMETHOD_EVP_SHA512 target_seconds = 0.1 #100ms # estimate number of derivations for 100ms per decrypt using 25000 iterations. start_time = get_time() derive_key_from_passphrase(passphrase, salt, nb_iterations_test, deriv_method) estimate1 = int((nb_iterations_test * 1.0 * target_seconds) / (get_time() - start_time)) # try it and take the mean of the estimate1 and estimate2 start_time = get_time() derive_key_from_passphrase(passphrase, salt, estimate1, deriv_method) estimate2 = int(estimate1 * target_seconds / (get_time() - start_time)) deriv_iterations = (estimate1 + estimate2) / 2 # use it key, init_vect = derive_key_from_passphrase(passphrase, salt, deriv_iterations, deriv_method) crypter.set_key(key, init_vect) crypted_key = crypter.encrypt(plain_master_key) return MasterKey(crypted_key, salt, deriv_method, deriv_iterations)
def __init__(self, wallet_database, runmode): super(Wallet, self).__init__() self.wallet_database = wallet_database self.runmode = runmode self.addresses = {} self.crypter = Crypter() self.plain_masterkeys = []
def decrypt_masterkey(master_key, passphrase): """Return plain master key bytestring from a MasterKey object and a passphrase""" key, init_vect = derive_key_from_passphrase(passphrase, master_key.salt, master_key.derive_iterations, master_key.derivation_method) crypter = Crypter() crypter.set_key(key, init_vect) plain_masterkey = crypter.decrypt(master_key.crypted_key) return plain_masterkey
def create(self, passphrase): self.wallet_database.begin_updates() crypter = Crypter() #first create masterkey master_key = new_masterkey(passphrase) plain_masterkey = decrypt_masterkey(master_key, passphrase) self.wallet_database.add_master_key(master_key) #create transaction pool for i in range(100): k = KEY() k.generate(True) public_key = k.get_pubkey() crypter.set_key(plain_masterkey, doublesha256(public_key)) crypted_secret = crypter.encrypt(k.get_secret()) self.wallet_database.add_crypted_key(public_key, crypted_secret) pool_key = WalletPoolKey(i, 60000, time.time(), public_key) self.wallet_database.add_poolkey(pool_key) self.wallet_database.commit_updates() self.load()
class Wallet(Observable): ''' Wallet implementes the basic satoshi wallet logic. Database logic is implemented in WalletDatabase Features requiring a blockchain are implemented in WalletAccount. ''' EVT_NEW_TRANSACTION = Observable.createevent() def __init__(self, wallet_database, runmode): super(Wallet, self).__init__() self.wallet_database = wallet_database self.runmode = runmode self.addresses = {} self.crypter = Crypter() self.plain_masterkeys = [] def open(self): self.wallet_database.open() self.load() def create(self, passphrase): self.wallet_database.begin_updates() crypter = Crypter() #first create masterkey master_key = new_masterkey(passphrase) plain_masterkey = decrypt_masterkey(master_key, passphrase) self.wallet_database.add_master_key(master_key) #create transaction pool for i in range(100): k = KEY() k.generate(True) public_key = k.get_pubkey() crypter.set_key(plain_masterkey, doublesha256(public_key)) crypted_secret = crypter.encrypt(k.get_secret()) self.wallet_database.add_crypted_key(public_key, crypted_secret) pool_key = WalletPoolKey(i, 60000, time.time(), public_key) self.wallet_database.add_poolkey(pool_key) self.wallet_database.commit_updates() self.load() def load(self): for public_key, keypair in self.wallet_database.get_keys().iteritems(): self.addresses[BitcoinAddress.from_publickey( public_key, self.runmode)] = (public_key, False) for public_key, secret in self.wallet_database.get_crypted_keys( ).iteritems(): self.addresses[BitcoinAddress.from_publickey( public_key, self.runmode)] = (public_key, True) def begin_updates(self): self.wallet_database.begin_updates() def commit_updates(self): self.wallet_database.commit_updates() def get_receive_key(self): return self.wallet_database.get_receive_key() def allocate_key(self, public_key, label=None, ischange=False): address = BitcoinAddress.from_publickey(public_key, self.runmode) return self.wallet_database.allocate_key(public_key, address, label) def add_transaction(self, hashtx, wallet_tx): self.wallet_database.set_transaction(hashtx, wallet_tx) def get_transaction(self, hashtx): return self.wallet_database.get_transaction(hashtx) def set_transaction(self, hashtx, wallet_tx): self.wallet_database.set_transaction(hashtx, wallet_tx) def del_transaction(self, hashtx): self.wallet_database.del_transaction(hashtx) """ yields ( public_key, is_crypted, address, description ) entries. """ def iterkeys(self): names = self.wallet_database.get_names() for address, (public_key, is_crypted) in self.addresses.iteritems(): description = self.get_address_description(public_key) yield (public_key, is_crypted, address, description) def get_address_description(self, public_key): address = BitcoinAddress.from_publickey(public_key, self.runmode) description = "" if public_key in self.wallet_database.poolkeys_by_public_key: poolkey = self.wallet_database.poolkeys_by_public_key[public_key] description = "Pool (id:%d, time:%s)" % ( poolkey.poolnum, time.strftime("%Y-%m-%d %H:%m:%S", time.gmtime(poolkey.time))) else: if address in self.wallet_database.get_names(): description = "Receive (\"%s\")" % self.wallet_database.get_names( )[address].name else: description = "Change" return description def iter_my_outputs(self): for hash, wallet_tx in self.wallet_database.get_wallet_txs().iteritems( ): for index, txout in enumerate(wallet_tx.merkle_tx.tx.out_list): if not wallet_tx.is_spent(index) and self.is_mine(txout): yield (wallet_tx.merkle_tx.tx, Outpoint(hash, index), txout) #yield ControlledOutput(hash, wallet_tx.merkle_tx.tx, index, txout, self.get_keypair_for_output(txout)) '''' A TxIn is "debit" if the previous output is in the wallet and is mine. If it is, get_debit_txin will return the value spent. Debit transaction are used only for transaction history. The balance is computed using unspent transactions. ''' def get_debit_txin(self, txin): if txin.previous_output.hash not in self.wallet_database.get_wallet_txs( ): return 0 txprev = self.wallet_database.get_wallet_txs()[ txin.previous_output.hash] txout = txprev.merkle_tx.tx.out_list[txin.previous_output.index] if not self.is_mine(txout): return 0 return txout.value def get_debit_tx(self, wallet_tx): return sum( self.get_debit_txin(txin) for txin in wallet_tx.merkle_tx.tx.in_list) def get_credit_txout(self, txout): if self.is_mine(txout): return txout.value return 0 def get_credit_tx(self, wallet_tx): return sum( self.get_credit_txout(txout) for txout in wallet_tx.merkle_tx.tx.out_list) def iter_transaction_history(self): # see wallet.cpp:448 GetAmounts for hash, wallet_tx in self.wallet_database.get_wallet_txs().iteritems( ): debit = self.get_debit_tx(wallet_tx) for txout in wallet_tx.merkle_tx.tx.out_list: address = extract_txout_address(txout, self.runmode) name = "" #print self.get_names() #print encode_base58check(chr(ADDRESSVERSION[self.runmode]) + address) if address and address in self.get_names(): name = self.get_names()[address].name if debit > 0 and self.is_change(txout): pass elif debit > 0: yield (wallet_tx, hash, wallet_tx.time_received, address, name, -txout.value) elif self.is_mine(txout): yield (wallet_tx, hash, wallet_tx.time_received, address, name, txout.value) def get_wallet_txs(self): return self.wallet_database.get_wallet_txs() def get_keypairs(self): return self.wallet_database.keypairs def get_poolkeys(self): return self.wallet_database.get_poolkeys() def get_names(self): return self.wallet_database.get_names() def have_key_for_addresss(self, address): return (address in self.addresses) def is_passphrase_required(self, txout): address = extract_txout_address(txout, self.runmode) _, is_crypted = self.addresses[address] return is_crypted def unlock(self, passphrases): for pphrase in passphrases: for mkey in self.get_master_keys().values(): self.plain_masterkeys.append(decrypt_masterkey(mkey, pphrase)) """ Return a private key for a txout as binary bignum. Requires unlock() if this key in encrypted """ def get_txout_private_key_secret(self, txout): address = extract_txout_address(txout, self.runmode) public_key, is_crypted = self.addresses[address] return self.get_private_key_secret(public_key) def get_private_key_secret(self, public_key): if public_key in self.wallet_database.keys: # private key is not crypted k = KEY() k.set_privkey(self.wallet_database.keys[public_key].private_key) return k.get_secret() crypted_secret = self.wallet_database.get_crypted_keys()[public_key] for key in self.plain_masterkeys: self.crypter.set_key(key, doublesha256(public_key)) secret = self.crypter.decrypt(crypted_secret) k = KEY() is_compressed = len(public_key) == 33 k.set_secret(secret, is_compressed) if k.get_pubkey() == public_key: return secret raise KeyDecryptException( "Can't decrypt private key, wallet not unlocked or incorrect masterkey" ) def lock(self): self.plain_masterkeys = [] def is_mine(self, txout): address = extract_txout_address(txout, self.runmode) if not address: # if unknown script type, return False return False return self.have_key_for_addresss(address) """ Return the wallet's besthash as Uint256() or None if not supported """ def get_besthash_reference(self): locator = self.wallet_database.get_block_locator() if locator: return self.wallet_database.get_block_locator().highest() def is_change(self, txout): # Fix to be implemented in main client, see wallet.cpp:390 # current bad assumption is that any payment to a TX_PUBKEYHASH that # is mine but isn't in the address book is change. address = extract_txout_address(txout, self.runmode) return self.is_mine(txout) and (address not in self.get_names()) def get_master_keys(self): return self.wallet_database.get_master_keys()
class Wallet(Observable): ''' Wallet implementes the basic satoshi wallet logic. Database logic is implemented in WalletDatabase Features requiring a blockchain are implemented in WalletAccount. ''' EVT_NEW_TRANSACTION = Observable.createevent() def __init__(self, wallet_database, runmode): super(Wallet, self).__init__() self.wallet_database = wallet_database self.runmode = runmode self.addresses = {} self.crypter = Crypter() self.plain_masterkeys = [] def open(self): self.wallet_database.open() self.load() def create(self, passphrase): self.wallet_database.begin_updates() crypter = Crypter() #first create masterkey master_key = new_masterkey(passphrase) plain_masterkey = decrypt_masterkey(master_key, passphrase) self.wallet_database.add_master_key(master_key) #create transaction pool for i in range(100): k = KEY() k.generate(True) public_key = k.get_pubkey() crypter.set_key(plain_masterkey, doublesha256(public_key)) crypted_secret = crypter.encrypt(k.get_secret()) self.wallet_database.add_crypted_key(public_key, crypted_secret) pool_key = WalletPoolKey(i, 60000, time.time(), public_key) self.wallet_database.add_poolkey(pool_key) self.wallet_database.commit_updates() self.load() def load(self): for public_key, keypair in self.wallet_database.get_keys().iteritems(): self.addresses[BitcoinAddress.from_publickey(public_key, self.runmode)] = (public_key, False) for public_key, secret in self.wallet_database.get_crypted_keys().iteritems(): self.addresses[BitcoinAddress.from_publickey(public_key, self.runmode)] = (public_key, True) def begin_updates(self): self.wallet_database.begin_updates() def commit_updates(self): self.wallet_database.commit_updates() def get_receive_key(self): return self.wallet_database.get_receive_key() def allocate_key(self, public_key, label=None, ischange=False): address = BitcoinAddress.from_publickey(public_key, self.runmode) return self.wallet_database.allocate_key(public_key, address, label) def add_transaction(self, hashtx, wallet_tx): self.wallet_database.set_transaction(hashtx, wallet_tx) def get_transaction(self, hashtx): return self.wallet_database.get_transaction(hashtx) def set_transaction(self, hashtx, wallet_tx): self.wallet_database.set_transaction(hashtx, wallet_tx) def del_transaction(self, hashtx): self.wallet_database.del_transaction(hashtx) """ yields ( public_key, is_crypted, address, description ) entries. """ def iterkeys(self): names = self.wallet_database.get_names() for address, (public_key, is_crypted) in self.addresses.iteritems(): description = self.get_address_description(public_key) yield (public_key, is_crypted, address, description) def get_address_description(self, public_key): address = BitcoinAddress.from_publickey(public_key, self.runmode) description = "" if public_key in self.wallet_database.poolkeys_by_public_key: poolkey = self.wallet_database.poolkeys_by_public_key[public_key] description = "Pool (id:%d, time:%s)" % (poolkey.poolnum, time.strftime("%Y-%m-%d %H:%m:%S", time.gmtime(poolkey.time))) else: if address in self.wallet_database.get_names(): description = "Receive (\"%s\")" % self.wallet_database.get_names()[address].name else: description = "Change" return description def iter_my_outputs(self): for hash, wallet_tx in self.wallet_database.get_wallet_txs().iteritems(): for index, txout in enumerate(wallet_tx.merkle_tx.tx.out_list): if not wallet_tx.is_spent(index) and self.is_mine(txout): yield (wallet_tx.merkle_tx.tx, Outpoint(hash, index), txout) #yield ControlledOutput(hash, wallet_tx.merkle_tx.tx, index, txout, self.get_keypair_for_output(txout)) '''' A TxIn is "debit" if the previous output is in the wallet and is mine. If it is, get_debit_txin will return the value spent. Debit transaction are used only for transaction history. The balance is computed using unspent transactions. ''' def get_debit_txin(self, txin): if txin.previous_output.hash not in self.wallet_database.get_wallet_txs(): return 0 txprev = self.wallet_database.get_wallet_txs()[txin.previous_output.hash] txout = txprev.merkle_tx.tx.out_list[txin.previous_output.index] if not self.is_mine(txout): return 0 return txout.value def get_debit_tx(self, wallet_tx): return sum(self.get_debit_txin(txin) for txin in wallet_tx.merkle_tx.tx.in_list) def get_credit_txout(self, txout): if self.is_mine(txout): return txout.value return 0 def get_credit_tx(self, wallet_tx): return sum(self.get_credit_txout(txout) for txout in wallet_tx.merkle_tx.tx.out_list) def iter_transaction_history(self): # see wallet.cpp:448 GetAmounts for hash, wallet_tx in self.wallet_database.get_wallet_txs().iteritems(): debit = self.get_debit_tx(wallet_tx) for txout in wallet_tx.merkle_tx.tx.out_list: address = extract_txout_address(txout, self.runmode) name = "" #print self.get_names() #print encode_base58check(chr(ADDRESSVERSION[self.runmode]) + address) if address and address in self.get_names(): name = self.get_names()[address].name if debit > 0 and self.is_change(txout): pass elif debit > 0: yield (wallet_tx, hash, wallet_tx.time_received, address, name, -txout.value) elif self.is_mine(txout): yield (wallet_tx, hash, wallet_tx.time_received, address, name, txout.value) def get_wallet_txs(self): return self.wallet_database.get_wallet_txs() def get_keypairs(self): return self.wallet_database.keypairs def get_poolkeys(self): return self.wallet_database.get_poolkeys() def get_names(self): return self.wallet_database.get_names() def have_key_for_addresss(self, address): return (address in self.addresses) def is_passphrase_required(self, txout): address = extract_txout_address(txout, self.runmode) _, is_crypted = self.addresses[address] return is_crypted def unlock(self, passphrases): for pphrase in passphrases: for mkey in self.get_master_keys().values(): self.plain_masterkeys.append(decrypt_masterkey(mkey, pphrase)) """ Return a private key for a txout as binary bignum. Requires unlock() if this key in encrypted """ def get_txout_private_key_secret(self, txout): address = extract_txout_address(txout, self.runmode) public_key, is_crypted = self.addresses[address] return self.get_private_key_secret(public_key) def get_private_key_secret(self, public_key): if public_key in self.wallet_database.keys: # private key is not crypted k = KEY() k.set_privkey(self.wallet_database.keys[public_key].private_key) return k.get_secret() crypted_secret = self.wallet_database.get_crypted_keys()[public_key] for key in self.plain_masterkeys: self.crypter.set_key(key, doublesha256(public_key)) secret = self.crypter.decrypt(crypted_secret) k = KEY() is_compressed = len(public_key) == 33 k.set_secret(secret, is_compressed) if k.get_pubkey() == public_key: return secret raise KeyDecryptException("Can't decrypt private key, wallet not unlocked or incorrect masterkey") def lock(self): self.plain_masterkeys = [] def is_mine(self, txout): address = extract_txout_address(txout, self.runmode) if not address: # if unknown script type, return False return False return self.have_key_for_addresss(address) """ Return the wallet's besthash as Uint256() or None if not supported """ def get_besthash_reference(self): locator = self.wallet_database.get_block_locator() if locator: return self.wallet_database.get_block_locator().highest() def is_change(self, txout): # Fix to be implemented in main client, see wallet.cpp:390 # current bad assumption is that any payment to a TX_PUBKEYHASH that # is mine but isn't in the address book is change. address = extract_txout_address(txout, self.runmode) return self.is_mine(txout) and (address not in self.get_names()) def get_master_keys(self): return self.wallet_database.get_master_keys()
"be4afa6923ad06790b0f8c3345131499cf2b149ca422bd11a7e67a76347c51a456a2d626f75da1ff809632fca7165d71" ), salt=decodehexstr("8cdcbd8a494b0eeb"), derivation_method=MasterKey.DERIVMETHOD_EVP_SHA512, derive_iterations=45193, other_derivation_parameters="") #Decrypt the master crypted_key using the passphrase plain_masterkey = decrypt_masterkey(master_key, "hello") #Decrypt a crypted_secret public_key = decodehexstr( "046a82d73af2cc093e3df7ae0185f045946970bcd5f0ef26f82d4f9a24e0d50f977c51e311e079e3183cfadd67d9b3f089fe7ba94a196c365fbd9e03b8c423787d" ) crypted_secret = decodehexstr( "ff914ab69f58af92ac56de85051441e729cc51e11608d563e2a266ce3b8c59f573ed6a1828ff98fadb345890b6ed2626" ) crypter2 = Crypter() crypter2.set_key(plain_masterkey, doublesha256(public_key)) secret = crypter2.decrypt(crypted_secret) print hexstr(secret) #Test the secret k = KEY() k.set_secret(secret) sig = signature1 = k.sign("sign something") k2 = KEY() k2.set_pubkey(public_key) print k2.verify("sign something", sig) #Create a new masterkey m = new_masterkey("hello hello") print m print hexstr(decrypt_masterkey(m, "hello hello"))
from coinpy.tools.hex import decodehexstr from coinpy.tools.hex import hexstr from coinpy.model.wallet.masterkey import MasterKey from coinpy.tools.bitcoin.sha256 import doublesha256 from coinpy.tools.crypto.ecdsa.ecdsa_ssl import KEY master_key = MasterKey(crypted_key=decodehexstr("be4afa6923ad06790b0f8c3345131499cf2b149ca422bd11a7e67a76347c51a456a2d626f75da1ff809632fca7165d71"), salt=decodehexstr("8cdcbd8a494b0eeb"), derivation_method=MasterKey.DERIVMETHOD_EVP_SHA512, derive_iterations=45193, other_derivation_parameters="") #Decrypt the master crypted_key using the passphrase plain_masterkey = decrypt_masterkey(master_key, "hello") #Decrypt a crypted_secret public_key = decodehexstr("046a82d73af2cc093e3df7ae0185f045946970bcd5f0ef26f82d4f9a24e0d50f977c51e311e079e3183cfadd67d9b3f089fe7ba94a196c365fbd9e03b8c423787d") crypted_secret = decodehexstr("ff914ab69f58af92ac56de85051441e729cc51e11608d563e2a266ce3b8c59f573ed6a1828ff98fadb345890b6ed2626") crypter2 = Crypter() crypter2.set_key(plain_masterkey, doublesha256(public_key)) secret = crypter2.decrypt(crypted_secret) print hexstr(secret) #Test the secret k = KEY() k.set_secret(secret) sig = signature1 = k.sign("sign something") k2 = KEY() k2.set_pubkey(public_key) print k2.verify("sign something", sig) #Create a new masterkey m = new_masterkey("hello hello") print m print hexstr(decrypt_masterkey(m, "hello hello" ))