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-2023 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
  22. from .objmethods import Hilite,InitErrors,MMGenObject
  23. from .obj import ImmutableAttr,get_obj
  24. class SeedID(str,Hilite,InitErrors):
  25. color = 'blue'
  26. width = 8
  27. trunc_ok = False
  28. def __new__(cls,seed=None,sid=None):
  29. if type(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,seed_bin=None,nSubseeds=None):
  50. if not seed_bin:
  51. from .opts import opt
  52. from .crypto import Crypto
  53. from hashlib import sha256
  54. # Truncate random data for smaller seed lengths
  55. seed_bin = sha256(Crypto().get_random(1033)).digest()[:(opt.seed_len or self.dfl_len)//8]
  56. elif len(seed_bin)*8 not in self.lens:
  57. die(3,f'{len(seed_bin)*8}: invalid seed bit length')
  58. self.data = seed_bin
  59. self.sid = SeedID(seed=self)
  60. self.nSubseeds = nSubseeds # will override opt.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. from .opts import opt
  79. self._subseeds = SubSeedList(
  80. self,
  81. length = self.nSubseeds or opt.subseeds )
  82. return self._subseeds
  83. def subseed(self,*args,**kwargs):
  84. return self.subseeds.get_subseed_by_ss_idx(*args,**kwargs)
  85. def subseed_by_seed_id(self,*args,**kwargs):
  86. return self.subseeds.get_subseed_by_seed_id(*args,**kwargs)
  87. def split(self,*args,**kwargs):
  88. from .seedsplit import SeedShareList
  89. return SeedShareList(self,*args,**kwargs)
  90. @staticmethod
  91. def join_shares(*args,**kwargs):
  92. from .seedsplit import join_shares
  93. return join_shares(*args,**kwargs)