seed.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. #!/usr/bin/env python3
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C)2013-2022 The MMGen Project <mmgen@tuta.io>
  5. #
  6. # This program is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. """
  19. seed.py: Seed-related classes and methods for the MMGen suite
  20. """
  21. from .common import *
  22. from .objmethods import Hilite,InitErrors
  23. from .obj import ImmutableAttr,get_obj
  24. from .crypto import get_random,scramble_seed
  25. class SeedID(str,Hilite,InitErrors):
  26. color = 'blue'
  27. width = 8
  28. trunc_ok = False
  29. def __new__(cls,seed=None,sid=None):
  30. if type(sid) == cls:
  31. return sid
  32. try:
  33. if seed:
  34. assert isinstance(seed,SeedBase),'not a subclass of SeedBase'
  35. return str.__new__(cls,make_chksum_8(seed.data))
  36. elif sid:
  37. assert set(sid) <= set(hexdigits.upper()),'not uppercase hex digits'
  38. assert len(sid) == cls.width, f'not {cls.width} characters wide'
  39. return str.__new__(cls,sid)
  40. raise ValueError('no arguments provided')
  41. except Exception as e:
  42. return cls.init_fail(e,seed or sid)
  43. def is_seed_id(s):
  44. return get_obj( SeedID, sid=s, silent=True, return_bool=True )
  45. class SeedBase(MMGenObject):
  46. data = ImmutableAttr(bytes,typeconv=False)
  47. sid = ImmutableAttr(SeedID,typeconv=False)
  48. def __init__(self,seed_bin=None):
  49. if not seed_bin:
  50. # Truncate random data for smaller seed lengths
  51. seed_bin = sha256(get_random(1033)).digest()[:(opt.seed_len or g.dfl_seed_len)//8]
  52. elif len(seed_bin)*8 not in g.seed_lens:
  53. die(3,f'{len(seed_bin)}: invalid seed length')
  54. self.data = seed_bin
  55. self.sid = SeedID(seed=self)
  56. @property
  57. def bitlen(self):
  58. return len(self.data) * 8
  59. @property
  60. def byte_len(self):
  61. return len(self.data)
  62. @property
  63. def hexdata(self):
  64. return self.data.hex()
  65. @property
  66. def fn_stem(self):
  67. return self.sid
  68. class Seed(SeedBase):
  69. @property
  70. def subseeds(self):
  71. if not hasattr(self,'_subseeds'):
  72. from .subseed import SubSeedList
  73. self._subseeds = SubSeedList(self)
  74. return self._subseeds
  75. def subseed(self,*args,**kwargs):
  76. return self.subseeds.get_subseed_by_ss_idx(*args,**kwargs)
  77. def subseed_by_seed_id(self,*args,**kwargs):
  78. return self.subseeds.get_subseed_by_seed_id(*args,**kwargs)
  79. def split(self,*args,**kwargs):
  80. from .seedsplit import SeedShareList
  81. return SeedShareList(self,*args,**kwargs)
  82. @staticmethod
  83. def join_shares(*args,**kwargs):
  84. from .seedsplit import join_shares
  85. return join_shares(*args,**kwargs)