URI: 
       tencryption of bip32 master private keys - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 58538ba825c199b4f3286cc3d7f3b3d2a33473e3
   DIR parent a3de537d9b0f8a936b0d90ab1a7cb0acc1b18e39
  HTML Author: ThomasV <thomasv@gitorious>
       Date:   Mon,  5 Aug 2013 13:53:50 +0200
       
       encryption of bip32 master private keys
       
       Diffstat:
         M lib/account.py                      |      33 +++++++++++++++++--------------
         M lib/wallet.py                       |      73 +++++++++++++++++++------------
       
       2 files changed, 63 insertions(+), 43 deletions(-)
       ---
   DIR diff --git a/lib/account.py b/lib/account.py
       t@@ -1,18 +1,21 @@
       -"""
       -todolist:
       - * passwords, private keys storage
       - * multisig service
       - * compatibility with old addresses for restore
       - * gui
       - 
       -        an account may use one or several MPKs.
       -        due to the type 1 derivations, we need to pass the mpk to this function
       -        None : all accounts
       -        -1 : imported
       -        0,1... : seeded sequences
       -
       -        each account has a public and private master key
       -"""
       +#!/usr/bin/env python
       +#
       +# Electrum - lightweight Bitcoin client
       +# Copyright (C) 2013 thomasv@gitorious
       +#
       +# This program is free software: you can redistribute it and/or modify
       +# it under the terms of the GNU General Public License as published by
       +# the Free Software Foundation, either version 3 of the License, or
       +# (at your option) any later version.
       +#
       +# This program is distributed in the hope that it will be useful,
       +# but WITHOUT ANY WARRANTY; without even the implied warranty of
       +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
       +# GNU General Public License for more details.
       +#
       +# You should have received a copy of the GNU General Public License
       +# along with this program. If not, see <http://www.gnu.org/licenses/>.
       +
        
        from bitcoin import *
        
   DIR diff --git a/lib/wallet.py b/lib/wallet.py
       t@@ -195,7 +195,7 @@ class Wallet:
                self.create_new_account('Main account')
        
        
       -    def create_new_account(self, name):
       +    def create_new_account(self, name, password):
                keys = self.accounts.keys()
                i = 0
        
       t@@ -205,9 +205,9 @@ class Wallet:
                    i += 1
        
                start = "m/0'/"
       +        master_k = self.get_master_private_key(start, password )
                master_c, master_K, master_cK = self.master_public_keys[start]
       -        master_k = self.master_private_keys[start] # needs decryption
       -        k, c, K, cK = bip32_private_derivation(master_k, master_c, start, derivation) # this is a type 1 derivation
       +        k, c, K, cK = bip32_private_derivation(master_k, master_c, start, derivation)
                
                self.accounts[derivation] = BIP32_Account({ 'name':name, 'c':c, 'K':K, 'cK':cK })
                self.save_accounts()
       t@@ -269,6 +269,17 @@ class Wallet:
                raise
                return self.config.get("master_public_key")
        
       +    def get_master_private_key(self, account, password):
       +        master_k = pw_decode( self.master_private_keys[account], password)
       +        master_c, master_K, master_Kc = self.master_public_keys[account]
       +        try:
       +            K, Kc = get_pubkeys_from_secret(master_k.decode('hex'))
       +            assert K.encode('hex') == master_K
       +        except:
       +            raise BaseException("Invalid password")
       +        return master_k
       +
       +
            def get_address_index(self, address):
                if address in self.imported_keys.keys():
                    return -1, None
       t@@ -291,8 +302,29 @@ class Wallet:
                #todo:  #self.sequences[0].check_seed(seed)
                return seed
                
       +
            def get_private_key(self, address, password):
       -        return self.get_private_keys([address], password).get(address)
       +        if address in self.imported_keys.keys():
       +            return pw_decode( self.imported_keys[address], password )
       +        else:
       +            account, sequence = self.get_address_index(address)
       +            m = re.match("m/0'/(\d+)'", account)
       +            if m:
       +                num = int(m.group(1))
       +                master_k = self.get_master_private_key("m/0'/", password)
       +                master_c, _, _ = self.master_public_keys["m/0'/"]
       +                master_k, master_c = CKD(master_k, master_c, num + BIP32_PRIME)
       +                return self.accounts[account].get_private_key(sequence, master_k)
       +                
       +            m2 = re.match("m/1'/(\d+) & m/2'/(\d+)", account)
       +            if m2:
       +                num = int(m2.group(1))
       +                master_k = self.get_master_private_key("m/1'/", password)
       +                master_c, master_K, _ = self.master_public_keys["m/1'/"]
       +                master_k, master_c = CKD(master_k.decode('hex'), master_c.decode('hex'), num)
       +                return self.accounts[account].get_private_key(sequence, master_k)
       +        return
       +
        
            def get_private_keys(self, addresses, password):
                if not self.seed: return {}
       t@@ -300,29 +332,8 @@ class Wallet:
                seed = self.decode_seed(password)
                out = {}
                for address in addresses:
       -            if address in self.imported_keys.keys():
       -                out[address] = pw_decode( self.imported_keys[address], password )
       -            else:
       -                account, sequence = self.get_address_index(address)
       -                print_error( "found index", address, account, sequence)
       -
       -                m = re.match("m/0'/(\d+)'", account)
       -                if m:
       -                    num = int(m.group(1))
       -                    master_k = self.master_private_keys["m/0'/"]
       -                    master_c, _, _ = self.master_public_keys["m/0'/"]
       -                    master_k, master_c = CKD(master_k, master_c, num + BIP32_PRIME)
       -                    pk = self.accounts[account].get_private_key(sequence, master_k)
       -                    out[address] = pk
       -
       -                m2 = re.match("m/1'/(\d+) & m/2'/(\d+)", account)
       -                if m2:
       -                    num = int(m2.group(1))
       -                    master_k = self.master_private_keys["m/1'/"]
       -                    master_c, master_K, _ = self.master_public_keys["m/1'/"]
       -                    master_k, master_c = CKD(master_k.decode('hex'), master_c.decode('hex'), num)
       -                    pk = self.accounts[account].get_private_key(sequence, master_k)
       -                    out[address] = pk
       +            pk = self.get_private_key(address, password)
       +            if pk: out[address] = pk
        
                return out
        
       t@@ -724,7 +735,7 @@ class Wallet:
                        if not self.use_change or account == -1:
                            change_addr = inputs[-1]['address']
                        else:
       -                    change_addr = self.accounts[account][1][-self.gap_limit_for_change]
       +                    change_addr = self.accounts[account].get_addresses(1)[-self.gap_limit_for_change]
        
                    # Insert the change output at a random position in the outputs
                    posn = random.randint(0, len(outputs))
       t@@ -949,6 +960,12 @@ class Wallet:
                    self.imported_keys[k] = c
                self.config.set_key('imported_keys', self.imported_keys, True)
        
       +        for k, v in self.master_private_keys.items():
       +            b = pw_decode(v, old_password)
       +            c = pw_encode(b, new_password)
       +            self.master_private_keys[k] = c
       +        self.config.set_key('master_private_keys', self.master_private_keys, True)
       +
        
            def freeze(self,addr):
                if self.is_mine(addr) and addr not in self.frozen_addresses: