From 2e1f3b93e42c60c0b6863974db3bb20827eb7809 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Thu, 26 May 2022 16:07:20 +0000 Subject: [PATCH] move coin privkey bytes derivation to derive.py --- mmgen/addrlist.py | 32 ++++++++------------- mmgen/derive.py | 48 ++++++++++++++++++++++++++++++++ test/unit_tests.py | 1 + test/unit_tests_d/ut_addrlist.py | 16 +++++++++-- 4 files changed, 73 insertions(+), 24 deletions(-) create mode 100755 mmgen/derive.py diff --git a/mmgen/addrlist.py b/mmgen/addrlist.py index f9b92bca..8532fde6 100755 --- a/mmgen/addrlist.py +++ b/mmgen/addrlist.py @@ -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, diff --git a/mmgen/derive.py b/mmgen/derive.py new file mode 100755 index 00000000..f5ef135c --- /dev/null +++ b/mmgen/derive.py @@ -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 +# +# 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 . + +""" +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 diff --git a/test/unit_tests.py b/test/unit_tests.py index 0d92deb7..ffdcc46a 100755 --- a/test/unit_tests.py +++ b/test/unit_tests.py @@ -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) diff --git a/test/unit_tests_d/ut_addrlist.py b/test/unit_tests_d/ut_addrlist.py index 2c244150..48f65199 100755 --- a/test/unit_tests_d/ut_addrlist.py +++ b/test/unit_tests_d/ut_addrlist.py @@ -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)