keygen.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  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. keygen: Public key generation initialization code for the MMGen suite
  20. """
  21. from collections import namedtuple
  22. from .key import PrivKey
  23. keygen_public_data = namedtuple(
  24. 'keygen_public_data', [
  25. 'pubkey',
  26. 'viewkey_bytes',
  27. 'pubkey_type',
  28. 'compressed' ])
  29. class keygen_base:
  30. def __init__(self,cfg):
  31. if not (self.production_safe or cfg.test_suite):
  32. from .util import die
  33. die(2,
  34. f'Public key generator {type(self).__name__!r} is not safe from timing attacks '
  35. 'and may only be used in a testing environment')
  36. def gen_data(self,privkey):
  37. assert isinstance(privkey,PrivKey)
  38. return keygen_public_data(
  39. self.to_pubkey(privkey),
  40. self.to_viewkey(privkey),
  41. privkey.pubkey_type,
  42. privkey.compressed )
  43. def to_viewkey(self,privkey):
  44. return None
  45. @classmethod
  46. def get_clsname(cls,cfg,silent=False):
  47. return cls.__name__
  48. backend_data = {
  49. 'std': {
  50. 'backends': ('libsecp256k1','python-ecdsa'),
  51. 'package': 'secp256k1',
  52. },
  53. 'monero': {
  54. 'backends': ('nacl','ed25519ll-djbec','ed25519'),
  55. 'package': 'xmr',
  56. },
  57. 'zcash_z': {
  58. 'backends': ('nacl',),
  59. 'package': 'zec',
  60. },
  61. }
  62. def get_backends(pubkey_type):
  63. return backend_data[pubkey_type]['backends']
  64. def get_pubkey_type_cls(pubkey_type):
  65. import importlib
  66. return getattr(
  67. importlib.import_module(f'mmgen.proto.{backend_data[pubkey_type]["package"]}.keygen'),
  68. 'backend' )
  69. def _check_backend(cfg,backend,pubkey_type,desc='keygen backend'):
  70. from .util import is_int,die
  71. assert is_int(backend), f'illegal value for {desc} (must be an integer)'
  72. backends = get_backends(pubkey_type)
  73. if not (1 <= int(backend) <= len(backends)):
  74. die(1,
  75. f'{backend}: {desc} out of range\n' +
  76. 'Configured backends: ' +
  77. ' '.join( f'{n}:{k}' for n,k in enumerate(backends,1) )
  78. )
  79. cfg._util.qmsg(f'Using backend {backends[int(backend)-1]!r} for public key generation')
  80. return True
  81. def check_backend(cfg,proto,backend,addr_type):
  82. from .addr import MMGenAddrType
  83. pubkey_type = MMGenAddrType(proto,addr_type or proto.dfl_mmtype).pubkey_type
  84. return _check_backend(
  85. cfg,
  86. backend,
  87. pubkey_type,
  88. desc = '--keygen-backend parameter' )
  89. def KeyGenerator(cfg,proto,pubkey_type,backend=None,silent=False):
  90. """
  91. factory function returning a key generator backend for the specified pubkey type
  92. """
  93. assert pubkey_type in proto.pubkey_types, f'{pubkey_type!r}: invalid pubkey type for coin {proto.coin}'
  94. pubkey_type_cls = get_pubkey_type_cls(pubkey_type)
  95. backend = backend or cfg.keygen_backend
  96. if backend:
  97. _check_backend( cfg, backend, pubkey_type )
  98. backend_id = backend_data[pubkey_type]['backends'][int(backend) - 1 if backend else 0]
  99. backend_clsname = getattr(
  100. pubkey_type_cls,
  101. backend_id.replace('-','_')
  102. ).get_clsname(cfg,silent=silent)
  103. return getattr(pubkey_type_cls,backend_clsname)(cfg)