seed.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. #!/usr/bin/env python3
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C)2013-2024 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: Seed-related classes and methods for the MMGen suite
  20. """
  21. from .util import make_chksum_8,hexdigits_uc,die
  22. from .objmethods import HiliteStr,InitErrors,MMGenObject
  23. from .obj import ImmutableAttr,get_obj
  24. class SeedID(HiliteStr,InitErrors):
  25. color = 'blue'
  26. width = 8
  27. trunc_ok = False
  28. def __new__(cls,seed=None,sid=None):
  29. if isinstance(sid,cls):
  30. return sid
  31. try:
  32. if seed:
  33. assert isinstance(seed,SeedBase),'not a subclass of SeedBase'
  34. return str.__new__(cls,make_chksum_8(seed.data))
  35. elif sid:
  36. assert set(sid) <= set(hexdigits_uc), 'not uppercase hex digits'
  37. assert len(sid) == cls.width, f'not {cls.width} characters wide'
  38. return str.__new__(cls,sid)
  39. raise ValueError('no arguments provided')
  40. except Exception as e:
  41. return cls.init_fail(e,seed or sid)
  42. def is_seed_id(s):
  43. return get_obj( SeedID, sid=s, silent=True, return_bool=True )
  44. class SeedBase(MMGenObject):
  45. lens = ( 128, 192, 256 )
  46. dfl_len = 256
  47. data = ImmutableAttr(bytes,typeconv=False)
  48. sid = ImmutableAttr(SeedID,typeconv=False)
  49. def __init__(self,cfg,seed_bin=None,nSubseeds=None):
  50. if not seed_bin:
  51. from .crypto import Crypto
  52. from hashlib import sha256
  53. # Truncate random data for smaller seed lengths
  54. seed_bin = sha256(Crypto(cfg).get_random(1033)).digest()[:(cfg.seed_len or self.dfl_len)//8]
  55. elif len(seed_bin)*8 not in self.lens:
  56. die(3,f'{len(seed_bin)*8}: invalid seed bit length')
  57. self.cfg = cfg
  58. self.data = seed_bin
  59. self.sid = SeedID(seed=self)
  60. self.nSubseeds = nSubseeds # overrides cfg.subseeds
  61. @property
  62. def bitlen(self):
  63. return len(self.data) * 8
  64. @property
  65. def byte_len(self):
  66. return len(self.data)
  67. @property
  68. def hexdata(self):
  69. return self.data.hex()
  70. @property
  71. def fn_stem(self):
  72. return self.sid
  73. class Seed(SeedBase):
  74. @property
  75. def subseeds(self):
  76. if not hasattr(self,'_subseeds'):
  77. from .subseed import SubSeedList
  78. self._subseeds = SubSeedList(
  79. self,
  80. length = self.nSubseeds or self.cfg.subseeds )
  81. return self._subseeds
  82. def subseed(self,*args,**kwargs):
  83. return self.subseeds.get_subseed_by_ss_idx(*args,**kwargs)
  84. def subseed_by_seed_id(self,*args,**kwargs):
  85. return self.subseeds.get_subseed_by_seed_id(*args,**kwargs)
  86. def split(self,*args,**kwargs):
  87. from .seedsplit import SeedShareList
  88. return SeedShareList(self,*args,**kwargs)
  89. @staticmethod
  90. def join_shares(*args,**kwargs):
  91. from .seedsplit import join_shares
  92. return join_shares(*args,**kwargs)