Browse Source

protocol.py: check that private key is within allowable range
- if pk == 0 or pk == secp256k1_ge: FAIL
- if pk > secp256k1_ge: pk = pk % secp256k1_ge

MMGen 6 years ago
parent
commit
0d77a78a4c
2 changed files with 24 additions and 7 deletions
  1. 6 4
      mmgen/obj.py
  2. 18 3
      mmgen/protocol.py

+ 6 - 4
mmgen/obj.py

@@ -657,12 +657,14 @@ class PrivKey(str,Hilite,InitErrors,MMGenObject):
 		try:
 			assert s and type(compressed) == bool and pubkey_type,'Incorrect args for PrivKey()'
 			assert len(s) == cls.width / 2,'Key length must be {}'.format(cls.width/2)
-			me = str.__new__(cls,g.proto.preprocess_key(s.encode('hex'),pubkey_type))
-			me.orig_hex = s.encode('hex') # save the non-preprocessed key
+			if pubkey_type == 'password': # skip WIF creation and pre-processing for passwds
+				me = str.__new__(cls,s.encode('hex'))
+			else:
+				me = str.__new__(cls,g.proto.preprocess_key(s.encode('hex'),pubkey_type))
+				me.wif = WifKey(g.proto.hex2wif(me,pubkey_type,compressed),on_fail='raise')
 			me.compressed = compressed
 			me.pubkey_type = pubkey_type
-			if pubkey_type != 'password': # skip WIF creation for passwds
-				me.wif = WifKey(g.proto.hex2wif(me,pubkey_type,compressed),on_fail='raise')
+			me.orig_hex = s.encode('hex') # save the non-preprocessed key
 			return me
 		except Exception as e:
 			fs = "Key={!r}\nCompressed={}\nValue pair cannot be converted to PrivKey\n({})"

+ 18 - 3
mmgen/protocol.py

@@ -22,7 +22,7 @@ protocol.py: Coin protocol functions, classes and methods
 
 import sys,os,hashlib
 from binascii import unhexlify
-from mmgen.util import msg,pmsg,Msg,pdie
+from mmgen.util import msg,pmsg,ymsg,Msg,pdie,ydie
 from mmgen.obj import MMGenObject,BTCAmt,LTCAmt,BCHAmt,B2XAmt,ETHAmt
 from mmgen.globalvars import g
 import mmgen.bech32 as bech32
@@ -93,6 +93,7 @@ class BitcoinProtocol(MMGenObject):
 	witness_vernum     = int(witness_vernum_hex,16)
 	bech32_hrp         = 'bc'
 	sign_mode          = 'daemon'
+	secp256k1_ge       = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
 
 	@classmethod
 	def is_testnet(cls):
@@ -110,7 +111,19 @@ class BitcoinProtocol(MMGenObject):
 	def cap(cls,s): return s in cls.caps
 
 	@classmethod
-	def preprocess_key(cls,hexpriv,pubkey_type): return hexpriv
+	def preprocess_key(cls,hexpriv,pubkey_type):
+		# Key must be non-zero and less than group order of secp256k1 curve
+		if 0 < int(hexpriv,16) < cls.secp256k1_ge:
+			return hexpriv
+		else: # chance of this is less than 1 in 2^127
+			pk = int(hexpriv,16)
+			if pk == 0: # chance of this is 1 in 2^256
+				ydie(3,'Private key is zero!')
+			elif pk == cls.secp256k1_ge: # ditto
+				ydie(3,'Private key == secp256k1_ge!')
+			else:
+				ymsg('Warning: private key is greater than secp256k1 group order!:\n  {}'.format(hexpriv))
+				return '{:064x}'.format(pk % cls.secp256k1_ge)
 
 	@classmethod
 	def hex2wif(cls,hexpriv,pubkey_type,compressed):
@@ -131,6 +144,8 @@ class BitcoinProtocol(MMGenObject):
 		else:
 			assert len(key) == 64,'invalid key length'
 			compressed = False
+		assert 0 < int(key[:64],16) < cls.secp256k1_ge,(
+			"'{}': invalid WIF (produces key outside allowable range)".format(wif))
 		return { 'hex':key[:64], 'pubkey_type':pubkey_type, 'compressed':compressed }
 
 	@classmethod
@@ -292,7 +307,7 @@ class DummyWIF(object):
 	def wif2hex(cls,wif):
 		return { 'hex':str(wif), 'pubkey_type':cls.pubkey_type, 'compressed':False }
 
-class EthereumProtocol(DummyWIF,BitcoinProtocolAddrgen):
+class EthereumProtocol(DummyWIF,BitcoinProtocol):
 
 	addr_width = 40
 	mmtypes    = ('E',)