move coin privkey bytes derivation to derive.py

This commit is contained in:
The MMGen Project 2022-05-26 16:07:20 +00:00
commit 2e1f3b93e4
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
4 changed files with 73 additions and 24 deletions

View file

@ -210,7 +210,6 @@ class AddrList(MMGenObject): # Address info for a single seed ID
(chk,rec)[record] )
def generate(self,seed,addr_idxs):
assert type(addr_idxs) is AddrIdxList
seed = self.scramble_seed(seed.data)
dmsg_sc('seed',seed[:8].hex())
@ -227,31 +226,24 @@ class AddrList(MMGenObject): # Address info for a single seed ID
if self.add_p2pkh:
ag2 = AddrGenerator( self.proto, 'compressed' )
t_addrs,out = ( len(addr_idxs), AddrListData() )
le = self.entry_type
num,pos = (0,0)
from hashlib import sha256,sha512
from .globalvars import g
from .derive import derive_coin_privkey_bytes
while pos != t_addrs:
seed = sha512(seed).digest()
num += 1 # round
t_addrs = len(addr_idxs)
le = self.entry_type
out = AddrListData()
CR = '\n' if g.debug_addrlist else '\r'
if num != addr_idxs[pos]:
continue
pos += 1
for pk_bytes in derive_coin_privkey_bytes(seed,addr_idxs):
if not g.debug:
qmsg_r(f'\rGenerating {self.gen_desc} #{num} ({pos} of {t_addrs})')
qmsg_r(f'{CR}Generating {self.gen_desc} #{pk_bytes.idx} ({pk_bytes.pos} of {t_addrs})')
e = le(proto=self.proto,idx=num)
e = le( proto=self.proto, idx=pk_bytes.idx )
# Secret key is double sha256 of seed hash round /num/
e.sec = PrivKey(
self.proto,
sha256(sha256(seed).digest()).digest(),
pk_bytes.data,
compressed = mmtype.compressed,
pubkey_type = mmtype.pubkey_type )
@ -269,10 +261,8 @@ class AddrList(MMGenObject): # Address info for a single seed ID
out.append(e)
if g.debug_addrlist:
Msg(f'generate():\n{e.pfmt()}')
qmsg('\r{}: {} {}{} generated{}'.format(
qmsg('{}{}: {} {}{} generated{}'.format(
CR,
self.al_id.hl(),
t_addrs,
self.gen_desc,

48
mmgen/derive.py Executable file
View file

@ -0,0 +1,48 @@
#!/usr/bin/env python3
#
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
# Copyright (C)2013-2022 The MMGen Project <mmgen@tuta.io>
#
# 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/>.
"""
derive.py: coin private key secret derivation for the MMGen suite
"""
from collections import namedtuple
from hashlib import sha512,sha256
from .addrlist import AddrIdxList
pk_bytes = namedtuple('coin_privkey_bytes',['idx','pos','data'])
def derive_coin_privkey_bytes(seed,idxs):
assert isinstance(idxs,AddrIdxList)
t_keys = len(idxs)
pos = 0
for idx in range( 1, AddrIdxList.max_len+1 ): # key/addr indexes begin from one
seed = sha512(seed).digest()
if idx == idxs[pos]:
pos += 1
# secret is double sha256 of seed hash round /idx/
yield pk_bytes( idx, pos, sha256(sha256(seed).digest()).digest() )
if pos == t_keys:
break

View file

@ -52,6 +52,7 @@ If no test is specified, all available tests are run
sys.argv.insert(1,'--skip-cfg-file')
opts.UserOpts._reset_ok += ('use_internal_keccak_module',)
g._reset_ok += ('debug_addrlist',)
cmd_args = opts.init(opts_data)

View file

@ -10,12 +10,16 @@ from mmgen.addrlist import AddrIdxList,AddrList,KeyList,KeyAddrList
from mmgen.passwdlist import PasswordList
from mmgen.protocol import init_proto
def do_test(list_type,chksum,pw_id_str=None,add_kwargs=None):
def do_test(list_type,chksum,idx_spec=None,pw_id_str=None,add_kwargs=None):
qmsg(blue(f'Testing {list_type.__name__}'))
proto = init_proto('btc')
seed = Seed(seed_bin=bytes.fromhex('feedbead'*8))
mmtype = MMGenAddrType(proto,'C')
idxs = AddrIdxList('1-3')
idxs = AddrIdxList(idx_spec or '1-3')
if opt.verbose:
debug_addrlist_save = g.debug_addrlist
g.debug_addrlist = True
kwargs = {
'seed': seed,
@ -43,12 +47,18 @@ def do_test(list_type,chksum,pw_id_str=None,add_kwargs=None):
if chksum:
assert al.chksum == chksum, f'{al.chksum} != {chksum}'
if opt.verbose:
g.debug_addrlist = debug_addrlist_save
return True
class unit_tests:
def addr(self,name,ut):
return do_test(AddrList,'BCE8 082C 0973 A525')
return (
do_test(AddrList,'BCE8 082C 0973 A525','1-3') and
do_test(AddrList,'88FA B04B A380 C1CB','199999,99-101,77-78,7,3,2-9')
)
def key(self,name,ut):
return do_test(KeyList,None)