Browse Source

New die roll (base6d) wallet format

- Permits the creation of wallets by repeated rolls of a die: 50 rolls for
  128-bit, 75 for 192-bit, and 100 for 256-bit seeds.  The base6d format
  uses the digits from one to six, so that user doesn't have to subtract
  one from each roll.

Testing:

  $ test/unit_tests.py baseconv
  $ test/test.py ref3 conv

Example:

  NOTE: when creating a real wallet, the following steps must be performed in a
  secure offline environment, and preferably with the use of a text editor and
  a file written in volatile memory (e.g. /dev/shm), rather than the `echo`
  command.  A more private and user-friendly data input method will be provided
  in a forthcoming patch.

  Sample 128-bit data obtained by rolling a die 50 times and entering each roll
  on the keyboard as a digit:

      15146 56446 53415 45431 55141 32115 41325 16311 32553 43533

  Here spaces have been added for greater readability.  Newlines are also
  permitted.

  Save the data in a file with the extension .b6d:

      $ echo 15146 56446 53415 45431 55141 32115 41325 16311 32553 43533 > myseed.b6d

  Convert to MMGen's default wallet format:

      $ mmgen-walletconv -o wallet myseed.b6d

  Convert the wallet back to base6d:

      $ mmgen-walletconv -o dieroll FE3C6545*.mmdat
      Base6d die roll seed data written to file 'FE3C6545[128].b6d'

      $ cat 'FE3C6545[128].b6d'
      15146 56446 53415 45431 55141
      32115 41325 16311 32553 43533
The MMGen Project 5 years ago
parent
commit
c778636918

+ 21 - 0
mmgen/seed.py

@@ -1014,6 +1014,27 @@ class MMGenSeedFile(SeedSourceUnenc):
 
 		return True
 
+class DieRollSeedFile(SeedSourceUnenc):
+
+	stdin_ok = True
+	fmt_codes = 'b6d','die','dieroll',
+	desc = 'base6d die roll seed data'
+	ext = 'b6d'
+
+	def _format(self):
+		d = baseconv.frombytes(self.seed.data,'b6d',pad='seed',tostr=True) + '\n'
+		self.fmt_data = block_format(d,gw=5,cols=5)
+
+	def _deformat(self):
+		d = self.fmt_data.translate(dict((ord(ws),None) for ws in '\t\n '))
+		seed_bytes = baseconv.tobytes(d,'b6d',pad='seed')
+
+		self.seed = Seed(seed_bytes)
+		self.ssdata.hexseed = seed_bytes.hex()
+
+		check_usr_seed_len(self.seed.bitlen)
+		return True
+
 class PlainHexSeedFile(SeedSourceUnenc):
 
 	stdin_ok = True

+ 4 - 0
mmgen/util.py

@@ -287,6 +287,7 @@ class baseconv(object):
 		'b16':   ('hexadecimal string',   'base16 (hexadecimal) string data'),
 		'b10':   ('base10 string',        'base10 (decimal) string data'),
 		'b8':    ('base8 string',         'base8 (octal) string data'),
+		'b6d':   ('base6d (die roll)',    'base6 data using the digits from one to six'),
 		'mmgen': ('MMGen native mnemonic',
 		'MMGen native mnemonic seed phrase data created using old Electrum wordlist and simple base conversion'),
 	}
@@ -296,6 +297,7 @@ class baseconv(object):
 		'b16': tuple('0123456789abcdef'),
 		'b10': tuple('0123456789'),
 		'b8':  tuple('01234567'),
+		'b6d': tuple('123456'),
 	}
 	mn_base = 1626 # tirosh list is 1633 words long!
 	mn_ids = ('mmgen','tirosh')
@@ -306,9 +308,11 @@ class baseconv(object):
 	}
 	seed_pad_lens = {
 		'b58': { 16:22, 24:33, 32:44 },
+		'b6d': { 16:50, 24:75, 32:100 },
 	}
 	seed_pad_lens_rev = {
 		'b58': { 22:16, 33:24, 44:32 },
+		'b6d': { 50:16, 75:24, 100:32 },
 	}
 
 	@classmethod

+ 1 - 0
test/ref/1378FC64.b6d

@@ -0,0 +1 @@
+135111311335561534526322511624324361264136623152556434641112356463412545532

+ 1 - 0
test/ref/98831F3A.b6d

@@ -0,0 +1 @@
+1253254513316235145146646115665463533224654453113624424263124136533542263452465156633216133232332365

+ 2 - 0
test/ref/FE3C6545.b6d

@@ -0,0 +1,2 @@
+15146 56446 53415 45431 55141
+32115 41325 16311 32553 43533

+ 7 - 0
test/test_py_d/ts_ref_3seed.py

@@ -48,6 +48,7 @@ class TestSuiteRef3Seed(TestSuiteBase,TestSuiteShared):
 		('ref_seed_chk',    ([],'saved seed file')),
 		('ref_hex_chk',     ([],'saved mmhex file')),
 		('ref_plainhex_chk',([],'saved hex file')),
+		('ref_dieroll_chk', ([],'saved dieroll (b6d) file')),
 		('ref_mn_chk',      ([],'saved native MMGen mnemonic file')),
 		('ref_bip39_chk',   ([],'saved BIP39 mnemonic file')),
 		('ref_hincog_chk',  ([],'saved hidden incog reference wallet')),
@@ -60,6 +61,7 @@ class TestSuiteRef3Seed(TestSuiteBase,TestSuiteShared):
 		('ref_walletconv_seed',        ([],'wallet filename (seed)')),
 		('ref_walletconv_hexseed',     ([],'wallet filename (hex seed)')),
 		('ref_walletconv_plainhexseed',([],'wallet filename (plain hex seed)')),
+		('ref_walletconv_dieroll',     ([],'wallet filename (dieroll (b6d) seed)')),
 		('ref_walletconv_incog',       ([],'wallet filename (incog)')),
 		('ref_walletconv_xincog',      ([],'wallet filename (hex incog)')),
 	)
@@ -94,6 +96,10 @@ class TestSuiteRef3Seed(TestSuiteBase,TestSuiteShared):
 		from mmgen.seed import PlainHexSeedFile
 		return self.ref_ss_chk(ss=PlainHexSeedFile)
 
+	def ref_dieroll_chk(self):
+		from mmgen.seed import DieRollSeedFile
+		return self.ref_ss_chk(ss=DieRollSeedFile)
+
 	def ref_mn_chk(self):
 		from mmgen.seed import MMGenMnemonic
 		return self.ref_ss_chk(ss=MMGenMnemonic)
@@ -170,6 +176,7 @@ class TestSuiteRef3Seed(TestSuiteBase,TestSuiteShared):
 	def ref_walletconv_seed(self):         return self.ref_walletconv(ofmt='mmseed')
 	def ref_walletconv_hexseed(self):      return self.ref_walletconv(ofmt='mmhex')
 	def ref_walletconv_plainhexseed(self): return self.ref_walletconv(ofmt='hex')
+	def ref_walletconv_dieroll(self):      return self.ref_walletconv(ofmt='dieroll')
 
 	def ref_walletconv_incog(self,ofmt='incog',ext='mmincog'):
 		args = ['-r0','-p1']

+ 4 - 0
test/test_py_d/ts_wallet.py

@@ -65,6 +65,7 @@ class TestSuiteWalletConv(TestSuiteBase,TestSuiteShared):
 		('ref_seed_conv',      'conversion of saved seed file'),
 		('ref_hex_conv',       'conversion of saved MMGen hexadecimal seed file'),
 		('ref_plainhex_conv',  'conversion of saved plain hexadecimal seed file'),
+		('ref_dieroll_conv',   'conversion of saved dieroll (b6d) seed file'),
 		('ref_brain_conv',     'conversion of ref brainwallet'),
 		('ref_incog_conv',     'conversion of saved incog wallet'),
 		('ref_incox_conv',     'conversion of saved hex incog wallet'),
@@ -76,6 +77,7 @@ class TestSuiteWalletConv(TestSuiteBase,TestSuiteShared):
 		('ref_bip39_conv_out',  'ref seed conversion to BIP39 mnemonic'),
 		('ref_hex_conv_out',    'ref seed conversion to MMGen hex seed'),
 		('ref_plainhex_conv_out','ref seed conversion to plain hex seed'),
+		('ref_dieroll_conv_out','ref seed conversion to dieroll (b6d) seed'),
 		('ref_seed_conv_out',   'ref seed conversion to seed'),
 		('ref_incog_conv_out',  'ref seed conversion to incog data'),
 		('ref_incox_conv_out',  'ref seed conversion to hex incog data'),
@@ -101,6 +103,7 @@ class TestSuiteWalletConv(TestSuiteBase,TestSuiteShared):
 	def ref_seed_conv(self):     return self.ref_mn_conv(ext='mmseed')
 	def ref_hex_conv(self):      return self.ref_mn_conv(ext='mmhex')
 	def ref_plainhex_conv(self): return self.ref_mn_conv(ext='hex')
+	def ref_dieroll_conv(self):  return self.ref_mn_conv(ext='b6d')
 
 	def ref_brain_conv(self):
 		uopts = ['-i','b','-p','1','-l',str(self.seed_len)]
@@ -129,6 +132,7 @@ class TestSuiteWalletConv(TestSuiteBase,TestSuiteShared):
 	def ref_seed_conv_out(self):     return self.walletconv_out('seed')
 	def ref_hex_conv_out(self):      return self.walletconv_out('hexseed')
 	def ref_plainhex_conv_out(self): return self.walletconv_out('hex')
+	def ref_dieroll_conv_out(self):  return self.walletconv_out('dieroll')
 	def ref_incog_conv_out(self):    return self.walletconv_out('i')
 	def ref_incox_conv_out(self):    return self.walletconv_out('xi')
 

+ 26 - 0
test/unit_tests_d/ut_baseconv.py

@@ -101,6 +101,32 @@ class unit_test(object):
 			(('ffffffff',None),'37777777777'),
 			(('ffffffff',20),'00000000037777777777'),
 		),
+		'b6d': (
+			(('00',None),'1'),
+			(('00',1),'1'),
+			(('00',2),'11'),
+			(('01',None),'2'),
+			(('01',1),'2'),
+			(('01',2),'12'),
+			(('0f',None),'34'),
+			(('0f',1),'34'),
+			(('0f',2),'34'),
+			(('010f',None),'2242'),
+			(('010f',20),'11111111111111112242'),
+			(('deadbeef',None),'2525524636426'),
+			(('deadbeef',20),'11111112525524636426'),
+			(('00000000',None),'1'),
+			(('00000000',20),'11111111111111111111'),
+			(('ffffffff',None),'2661215126614'),
+			(('ffffffff',20),'11111112661215126614'),
+
+			(('ff'*16,'seed'),'34164464641266661652465154654653354436664555521414'),
+			(('ff'*24,'seed'),'246111411433323364222465566552324652566121541623426135163525451613554313654'),
+			(('ff'*32,'seed'),'2132521653312613134145131423465414636252114131225324246311141642456513416322412146151432142242565134'),
+			(('00'*16,'seed'),'1'*50),
+			(('00'*24,'seed'),'1'*75),
+			(('00'*32,'seed'),'1'*100),
+		),
 	}
 
 	def run_test(self,name,ut):