addr.py: move PubKey and PrivKey to key.py

This commit is contained in:
The MMGen Project 2022-01-15 14:00:08 +00:00
commit 545406aea4
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
16 changed files with 134 additions and 97 deletions

View file

@ -26,6 +26,7 @@ from .objmethods import MMGenObject
from .obj import *
from .baseconv import *
from .protocol import hash160
from .key import PrivKey,PubKey
class AddrGenerator(MMGenObject):
def __new__(cls,proto,addr_type):

View file

@ -33,6 +33,7 @@ from .util import (
from .protocol import init_proto
from .obj import *
from .seed import SeedID,is_seed_id
from .key import PrivKey
from .addrlist import KeyList,AddrListData,dmsg_sc
from .passwdlist import PasswordList

View file

@ -23,8 +23,9 @@ addrlist.py: Address list classes for the MMGen suite
from hashlib import sha256,sha512
from .util import qmsg,qmsg_r,suf,make_chksum_N,Msg
from .objmethods import MMGenObject,Hilite,InitErrors
from .obj import MMGenListItem,ListItemAttr,MMGenDict,WalletPassword,PrivKey
from .obj import MMGenListItem,ListItemAttr,MMGenDict,WalletPassword
from .seed import SeedID
from .key import PrivKey
from .obj import MMGenID,MMGenAddrType,CoinAddr,AddrIdx,AddrListID,ViewKey
def dmsg_sc(desc,data):

113
mmgen/key.py Executable file
View file

@ -0,0 +1,113 @@
#!/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/>.
"""
key.py: MMGen public and private key objects
"""
from string import ascii_letters,digits
from .objmethods import Hilite,InitErrors,MMGenObject
from .obj import ImmutableAttr,get_obj,HexStr
class WifKey(str,Hilite,InitErrors):
"""
Initialize a WIF key, checking its well-formedness.
The numeric validity of the private key it encodes is not checked.
"""
width = 53
color = 'blue'
def __new__(cls,proto,wif):
if type(wif) == cls:
return wif
try:
assert set(wif) <= set(ascii_letters+digits),'not an ascii alphanumeric string'
proto.parse_wif(wif) # raises exception on error
return str.__new__(cls,wif)
except Exception as e:
return cls.init_fail(e,wif)
def is_wif(proto,s):
return get_obj( WifKey, proto=proto, wif=s, silent=True, return_bool=True )
class PubKey(HexStr,MMGenObject): # TODO: add some real checks
def __new__(cls,s,privkey):
try:
me = HexStr.__new__(cls,s,case='lower')
me.privkey = privkey
me.compressed = privkey.compressed
return me
except Exception as e:
return cls.init_fail(e,s)
class PrivKey(str,Hilite,InitErrors,MMGenObject):
"""
Input: a) raw, non-preprocessed bytes; or b) WIF key.
Output: preprocessed hexadecimal key, plus WIF key in 'wif' attribute
For coins without a WIF format, 'wif' contains the preprocessed hex.
The numeric validity of the resulting key is always checked.
"""
color = 'red'
width = 64
trunc_ok = False
compressed = ImmutableAttr(bool,typeconv=False)
wif = ImmutableAttr(WifKey,typeconv=False)
# initialize with (priv_bin,compressed), WIF or self
def __new__(cls,proto,s=None,compressed=None,wif=None,pubkey_type=None):
if type(s) == cls:
return s
if wif:
try:
assert s == None,"'wif' and key hex args are mutually exclusive"
assert set(wif) <= set(ascii_letters+digits),'not an ascii alphanumeric string'
k = proto.parse_wif(wif) # raises exception on error
me = str.__new__(cls,k.sec.hex())
me.compressed = k.compressed
me.pubkey_type = k.pubkey_type
me.wif = str.__new__(WifKey,wif) # check has been done
me.orig_hex = None
if k.sec != proto.preprocess_key(k.sec,k.pubkey_type):
from .exception import PrivateKeyError
raise PrivateKeyError(
f'{proto.cls_name} WIF key {me.wif!r} encodes private key with invalid value {me}')
me.proto = proto
return me
except Exception as e:
return cls.init_fail(e,s,objname=f'{proto.coin} WIF key')
else:
try:
assert s,'private key bytes data missing'
assert pubkey_type is not None,"'pubkey_type' arg missing"
assert len(s) == cls.width // 2, f'key length must be {cls.width // 2} bytes'
if pubkey_type == 'password': # skip WIF creation and pre-processing for passwds
me = str.__new__(cls,s.hex())
else:
assert compressed is not None, "'compressed' arg missing"
assert type(compressed) == bool,(
f"'compressed' must be of type bool, not {type(compressed).__name__}" )
me = str.__new__(cls,proto.preprocess_key(s,pubkey_type).hex())
me.wif = WifKey(proto,proto.hex2wif(me,pubkey_type,compressed))
me.compressed = compressed
me.pubkey_type = pubkey_type
me.orig_hex = s.hex() # save the non-preprocessed key
me.proto = proto
return me
except Exception as e:
return cls.init_fail(e,s)

View file

@ -62,8 +62,6 @@ def is_addrlist_id(s): return get_obj(AddrListID, sid=s, silent=True,return_
def is_mmgen_id(proto,s): return get_obj(MMGenID, proto=proto, id_str=s, silent=True,return_bool=True)
def is_coin_addr(proto,s): return get_obj(CoinAddr, proto=proto, addr=s, silent=True,return_bool=True)
def is_wif(proto,s): return get_obj(WifKey, proto=proto, wif=s, silent=True,return_bool=True)
# dict that keeps a list of keys for efficient lookup by index
class IndexedDict(dict):
@ -435,89 +433,6 @@ class WalletPassword(HexStr): color,width,hexcase = 'blue',32,'lower'
class MoneroViewKey(HexStr): color,width,hexcase = 'cyan',64,'lower' # FIXME - no checking performed
class MMGenTxID(HexStr): color,width,hexcase = 'red',6,'upper'
class WifKey(str,Hilite,InitErrors):
"""
Initialize a WIF key, checking its well-formedness.
The numeric validity of the private key it encodes is not checked.
"""
width = 53
color = 'blue'
def __new__(cls,proto,wif):
if type(wif) == cls:
return wif
try:
assert set(wif) <= set(ascii_letters+digits),'not an ascii alphanumeric string'
proto.parse_wif(wif) # raises exception on error
return str.__new__(cls,wif)
except Exception as e:
return cls.init_fail(e,wif)
class PubKey(HexStr,MMGenObject): # TODO: add some real checks
def __new__(cls,s,privkey):
try:
me = HexStr.__new__(cls,s,case='lower')
me.privkey = privkey
me.compressed = privkey.compressed
return me
except Exception as e:
return cls.init_fail(e,s)
class PrivKey(str,Hilite,InitErrors,MMGenObject):
"""
Input: a) raw, non-preprocessed bytes; or b) WIF key.
Output: preprocessed hexadecimal key, plus WIF key in 'wif' attribute
For coins without a WIF format, 'wif' contains the preprocessed hex.
The numeric validity of the resulting key is always checked.
"""
color = 'red'
width = 64
trunc_ok = False
compressed = ImmutableAttr(bool,typeconv=False)
wif = ImmutableAttr(WifKey,typeconv=False)
# initialize with (priv_bin,compressed), WIF or self
def __new__(cls,proto,s=None,compressed=None,wif=None,pubkey_type=None):
if type(s) == cls:
return s
if wif:
try:
assert s == None,"'wif' and key hex args are mutually exclusive"
assert set(wif) <= set(ascii_letters+digits),'not an ascii alphanumeric string'
k = proto.parse_wif(wif) # raises exception on error
me = str.__new__(cls,k.sec.hex())
me.compressed = k.compressed
me.pubkey_type = k.pubkey_type
me.wif = str.__new__(WifKey,wif) # check has been done
me.orig_hex = None
if k.sec != proto.preprocess_key(k.sec,k.pubkey_type):
raise PrivateKeyError(
f'{proto.cls_name} WIF key {me.wif!r} encodes private key with invalid value {me}')
me.proto = proto
return me
except Exception as e:
return cls.init_fail(e,s,objname=f'{proto.coin} WIF key')
else:
try:
assert s,'private key bytes data missing'
assert pubkey_type is not None,"'pubkey_type' arg missing"
assert len(s) == cls.width // 2, f'key length must be {cls.width // 2} bytes'
if pubkey_type == 'password': # skip WIF creation and pre-processing for passwds
me = str.__new__(cls,s.hex())
else:
assert compressed is not None, "'compressed' arg missing"
assert type(compressed) == bool,(
f"'compressed' must be of type bool, not {type(compressed).__name__}" )
me = str.__new__(cls,proto.preprocess_key(s,pubkey_type).hex())
me.wif = WifKey(proto,proto.hex2wif(me,pubkey_type,compressed))
me.compressed = compressed
me.pubkey_type = pubkey_type
me.orig_hex = s.hex() # save the non-preprocessed key
me.proto = proto
return me
except Exception as e:
return cls.init_fail(e,s)
class AddrListID(str,Hilite,InitErrors,MMGenObject):
width = 10
trunc_ok = False

View file

@ -24,8 +24,9 @@ from collections import namedtuple
from .exception import InvalidPasswdFormat
from .util import ymsg,is_hex_str,is_int,keypress_confirm
from .obj import ImmutableAttr,ListItemAttr,MMGenPWIDString,PrivKey
from .obj import ImmutableAttr,ListItemAttr,MMGenPWIDString
from .baseconv import baseconv,is_b32_str,is_b58_str
from .key import PrivKey
from .addr import MMGenPasswordType,AddrIdx,AddrListID,is_xmrseed,is_bip39_str
from .addrlist import (
AddrListChksum,

View file

@ -25,7 +25,7 @@ from collections import namedtuple
from .util import msg,ymsg,Msg,ydie
from .devtools import *
from .obj import CoinAddr,MMGenAddrType,PrivKey
from .obj import CoinAddr,MMGenAddrType
from .globalvars import g
from .amt import BTCAmt,LTCAmt,BCHAmt,B2XAmt,XMRAmt
from .altcoins.eth.obj import ETHAmt
@ -178,9 +178,6 @@ class CoinProtocol(MMGenObject):
def addr_type(self,id_str):
return MMGenAddrType( proto=self, id_str=id_str )
def priv_key(self,s):
return PrivKey( proto=self, s=s )
class Secp256k1(Base):
"""
Bitcoin and Ethereum protocols inherit from this class

View file

@ -23,6 +23,7 @@ tool.py: Routines for the 'mmgen-tool' utility
from .protocol import hash160
from .common import *
from .crypto import *
from .key import PrivKey
from .seedsplit import MasterShareIdx
from .addr import *
from .addrlist import AddrList,KeyAddrList

View file

@ -401,7 +401,8 @@ def parse_arg2():
# begin execution
from mmgen.protocol import init_proto
from mmgen.altcoin import CoinInfo as ci
from mmgen.obj import MMGenAddrType,PrivKey
from mmgen.obj import MMGenAddrType
from mmgen.key import PrivKey
from mmgen.addr import KeyGenerator,AddrGenerator
addr_type = MMGenAddrType(

View file

@ -9,7 +9,8 @@ tool_api_test.py: test the MMGen suite tool API
import sys,os
from mmgen.common import *
from mmgen.obj import PrivKey,CoinAddr
from mmgen.key import PrivKey
from mmgen.addr import CoinAddr
keys = [
'118089d66b4a5853765e94923abdd5de4616c6e5118089d66b4a5853765e9492',

View file

@ -16,6 +16,7 @@ from mmgen.protocol import *
from mmgen.addr import *
from mmgen.tx import *
from mmgen.tw import *
from mmgen.key import *
from ..include.common import getrand
from collections import namedtuple

View file

@ -37,6 +37,7 @@ from mmgen.addr import *
from mmgen.addrlist import *
from mmgen.addrdata import *
from mmgen.amt import *
from mmgen.key import *
opts_data = {
'sets': [('super_silent', True, 'silent', True)],

View file

@ -10,6 +10,7 @@ test.objtest_py_d.ot_btc_mainnet: BTC mainnet test vectors for MMGen data object
from mmgen.obj import *
from mmgen.addrlist import AddrIdxList
from mmgen.seedsplit import *
from mmgen.key import *
from .ot_common import *
from mmgen.protocol import init_proto

View file

@ -374,7 +374,7 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
out.append(self._create_fake_unspent_entry(coinaddr,d['al_id'],idx,lbl,segwit=d['segwit']))
if non_mmgen_input:
from mmgen.obj import PrivKey
from mmgen.key import PrivKey
privkey = PrivKey(
self.proto,
getrand(32),
@ -417,7 +417,7 @@ class TestSuiteMain(TestSuiteBase,TestSuiteShared):
return ad,tx_data
def _make_txcreate_cmdline(self,tx_data):
from mmgen.obj import PrivKey
from mmgen.key import PrivKey
privkey = PrivKey(self.proto,getrand(32),compressed=True,pubkey_type='std')
t = ('compressed','segwit')['S' in self.proto.mmtypes]
from mmgen.addr import AddrGenerator,KeyGenerator

View file

@ -185,7 +185,8 @@ if opt.list_names:
)
die(0,'\n{}\n {}'.format(yellow('Untested commands:'),'\n '.join(uc)))
from mmgen.tx import is_wif,is_coin_addr
from mmgen.key import is_wif
from mmgen.tx import is_coin_addr
def is_wif_loc(s):
return is_wif(proto,s)
def is_coin_addr_loc(s):

View file

@ -44,7 +44,8 @@ NL = ('\n','\r\n')[g.platform=='win']
def is_str(s):
return type(s) == str
from mmgen.obj import is_wif,is_coin_addr
from mmgen.key import is_wif
from mmgen.obj import is_coin_addr
def is_wif_loc(s):
return is_wif(proto,s)
def is_coin_addr_loc(s):