key.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  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. key.py: MMGen public and private key objects
  20. """
  21. from .objmethods import Hilite,InitErrors,MMGenObject
  22. from .obj import ImmutableAttr,get_obj
  23. class WifKey(str,Hilite,InitErrors):
  24. """
  25. Initialize a WIF key, checking its well-formedness.
  26. The numeric validity of the private key it encodes is not checked.
  27. """
  28. width = 53
  29. color = 'blue'
  30. def __new__(cls,proto,wif):
  31. if type(wif) == cls:
  32. return wif
  33. try:
  34. assert wif.isascii() and wif.isalnum(), 'not an ASCII alphanumeric string'
  35. proto.decode_wif(wif) # raises exception on error
  36. return str.__new__(cls,wif)
  37. except Exception as e:
  38. return cls.init_fail(e,wif)
  39. def is_wif(proto,s):
  40. return get_obj( WifKey, proto=proto, wif=s, silent=True, return_bool=True )
  41. class PubKey(bytes,InitErrors,MMGenObject): # TODO: add some real checks
  42. def __new__(cls,s,compressed):
  43. try:
  44. assert isinstance(s,bytes)
  45. me = bytes.__new__(cls,s)
  46. me.compressed = compressed
  47. return me
  48. except Exception as e:
  49. return cls.init_fail(e,s)
  50. class PrivKey(bytes,Hilite,InitErrors,MMGenObject):
  51. """
  52. Input: a) raw, non-preprocessed bytes; or b) WIF key.
  53. Output: preprocessed key bytes, plus WIF key in 'wif' attribute
  54. For coins without a WIF format, 'wif' contains the preprocessed hex.
  55. The numeric validity of the resulting key is always checked.
  56. """
  57. color = 'red'
  58. width = 32
  59. trunc_ok = False
  60. compressed = ImmutableAttr(bool,typeconv=False)
  61. wif = ImmutableAttr(WifKey,typeconv=False)
  62. # initialize with (priv_bin,compressed), WIF or self
  63. def __new__(cls,proto,s=None,compressed=None,wif=None,pubkey_type=None):
  64. if type(s) == cls:
  65. return s
  66. if wif:
  67. try:
  68. assert s == None,"'wif' and key hex args are mutually exclusive"
  69. assert wif.isascii() and wif.isalnum(), 'not an ASCII alphanumeric string'
  70. k = proto.decode_wif(wif) # raises exception on error
  71. me = bytes.__new__(cls,k.sec)
  72. me.compressed = k.compressed
  73. me.pubkey_type = k.pubkey_type
  74. me.wif = str.__new__(WifKey,wif) # check has been done
  75. me.orig_bytes = None
  76. if k.sec != proto.preprocess_key(k.sec,k.pubkey_type):
  77. from .util import die
  78. die( 'PrivateKeyError',
  79. f'{proto.cls_name} WIF key {me.wif!r} encodes private key with invalid value {me}' )
  80. me.proto = proto
  81. return me
  82. except Exception as e:
  83. return cls.init_fail(e,s,objname=f'{proto.coin} WIF key')
  84. else:
  85. try:
  86. assert s,'private key bytes data missing'
  87. assert isinstance(s,bytes),'input is not bytes'
  88. assert pubkey_type is not None,"'pubkey_type' arg missing"
  89. assert len(s) == cls.width, f'key length must be {cls.width} bytes'
  90. if pubkey_type == 'password': # skip WIF creation and pre-processing for passwds
  91. me = bytes.__new__(cls,s)
  92. else:
  93. assert compressed is not None, "'compressed' arg missing"
  94. assert type(compressed) == bool,(
  95. f"'compressed' must be of type bool, not {type(compressed).__name__}" )
  96. me = bytes.__new__( cls, proto.preprocess_key(s,pubkey_type) )
  97. me.wif = WifKey( proto, proto.bytes2wif(me,pubkey_type,compressed) )
  98. me.compressed = compressed
  99. me.pubkey_type = pubkey_type
  100. me.orig_bytes = s # save the non-preprocessed key
  101. me.proto = proto
  102. return me
  103. except Exception as e:
  104. return cls.init_fail(e,s)