keygen.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. #!/usr/bin/env python3
  2. #
  3. # MMGen Wallet, a terminal-based cryptocurrency wallet
  4. # Copyright (C)2013-2025 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. 'monero': {
  53. 'backends': ('nacl', 'ed25519ll-djbec', 'ed25519'),
  54. 'package': 'xmr'},
  55. 'zcash_z': {
  56. 'backends': ('nacl',),
  57. 'package': 'zec'}}
  58. def get_backends(pubkey_type):
  59. return backend_data[pubkey_type]['backends']
  60. def get_pubkey_type_cls(pubkey_type):
  61. import importlib
  62. return getattr(
  63. importlib.import_module(f'mmgen.proto.{backend_data[pubkey_type]["package"]}.keygen'),
  64. 'backend')
  65. def _check_backend(cfg, backend, pubkey_type, *, desc='keygen backend'):
  66. from .util import is_int, die
  67. assert is_int(backend), f'illegal value for {desc} (must be an integer)'
  68. backends = get_backends(pubkey_type)
  69. if not (1 <= int(backend) <= len(backends)):
  70. die(1,
  71. f'{backend}: {desc} out of range\n' +
  72. 'Configured backends: ' +
  73. ' '.join(f'{n}:{k}' for n, k in enumerate(backends, 1))
  74. )
  75. cfg._util.qmsg(f'Using backend {backends[int(backend)-1]!r} for public key generation')
  76. return True
  77. def check_backend(cfg, proto, backend, addr_type):
  78. from .addr import MMGenAddrType
  79. pubkey_type = MMGenAddrType(proto, addr_type or proto.dfl_mmtype).pubkey_type
  80. return _check_backend(cfg, backend, pubkey_type, desc='--keygen-backend parameter')
  81. def KeyGenerator(cfg, proto, pubkey_type, *, backend=None, silent=False):
  82. """
  83. factory function returning a key generator backend for the specified pubkey type
  84. """
  85. assert pubkey_type in proto.pubkey_types, f'{pubkey_type!r}: invalid pubkey type for coin {proto.coin}'
  86. pubkey_type_cls = get_pubkey_type_cls(pubkey_type)
  87. backend = backend or cfg.keygen_backend
  88. if backend:
  89. _check_backend(cfg, backend, pubkey_type)
  90. backend_id = backend_data[pubkey_type]['backends'][int(backend) - 1 if backend else 0]
  91. backend_clsname = getattr(
  92. pubkey_type_cls,
  93. backend_id.replace('-', '_')
  94. ).get_clsname(cfg, silent=silent)
  95. return getattr(pubkey_type_cls, backend_clsname)(cfg)