keygen.py 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. #!/usr/bin/env python3
  2. #
  3. # mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet
  4. # Copyright (C)2013-2023 The MMGen Project <mmgen@tuta.io>
  5. # Licensed under the GNU General Public License, Version 3:
  6. # https://www.gnu.org/licenses
  7. # Public project repositories:
  8. # https://github.com/mmgen/mmgen-wallet
  9. # https://gitlab.com/mmgen/mmgen-wallet
  10. """
  11. proto.secp256k1.keygen: secp256k1 public key generation backends for the MMGen suite
  12. """
  13. from ...key import PubKey
  14. from ...keygen import keygen_base
  15. class backend:
  16. class libsecp256k1(keygen_base):
  17. production_safe = True
  18. def __init__(self,cfg):
  19. super().__init__(cfg)
  20. # catch ImportError to satisfy pylint when testing repo with unbuilt secp256k1 extension mod:
  21. try:
  22. from .secp256k1 import pubkey_gen
  23. self.pubkey_gen = pubkey_gen
  24. except ImportError:
  25. from ...util import die
  26. die(3,'libsecp256k1.keygen.backend: you shouldn’t be seeing this')
  27. def to_pubkey(self,privkey):
  28. return PubKey(
  29. s = self.pubkey_gen( privkey, int(privkey.compressed) ),
  30. compressed = privkey.compressed )
  31. @classmethod
  32. def get_clsname(cls,cfg,silent=False):
  33. try:
  34. from .secp256k1 import pubkey_gen
  35. if not pubkey_gen(bytes.fromhex('deadbeef'*8),1):
  36. from ...util import die
  37. die( 'ExtensionModuleError',
  38. 'Unable to execute pubkey_gen() from secp256k1 extension module' )
  39. return cls.__name__
  40. except ImportError as e:
  41. if not silent:
  42. from ...util import ymsg
  43. ymsg(str(e))
  44. cfg._util.qmsg('Using (slow) native Python ECDSA library for public key generation')
  45. return 'python_ecdsa'
  46. class python_ecdsa(keygen_base):
  47. production_safe = False
  48. def __init__(self,cfg):
  49. super().__init__(cfg)
  50. import ecdsa
  51. self.ecdsa = ecdsa
  52. def to_pubkey(self,privkey):
  53. """
  54. devdoc/guide_wallets.md:
  55. Uncompressed public keys start with 0x04; compressed public keys begin with 0x03 or
  56. 0x02 depending on whether they're greater or less than the midpoint of the curve.
  57. """
  58. def privnum2pubkey(numpriv,compressed=False):
  59. pk = self.ecdsa.SigningKey.from_secret_exponent(numpriv,curve=self.ecdsa.SECP256k1)
  60. # vk_bytes = x (32 bytes) + y (32 bytes) (unsigned big-endian)
  61. vk_bytes = pk.verifying_key.to_string()
  62. if compressed: # discard Y coord, replace with appropriate version byte
  63. # even y: <0, odd y: >0 -- https://bitcointalk.org/index.php?topic=129652.0
  64. return (b'\x03' if vk_bytes[-1] & 1 else b'\x02') + vk_bytes[:32]
  65. else:
  66. return b'\x04' + vk_bytes
  67. return PubKey(
  68. s = privnum2pubkey( int.from_bytes(privkey,'big'), compressed=privkey.compressed ),
  69. compressed = privkey.compressed )