623 lines
21 KiB
Python
623 lines
21 KiB
Python
import os
|
|
import hashlib
|
|
import binascii
|
|
|
|
from ._libsecp256k1 import ffi, lib
|
|
|
|
|
|
EC_COMPRESSED = lib.SECP256K1_EC_COMPRESSED
|
|
EC_UNCOMPRESSED = lib.SECP256K1_EC_UNCOMPRESSED
|
|
|
|
FLAG_SIGN = lib.SECP256K1_CONTEXT_SIGN
|
|
FLAG_VERIFY = lib.SECP256K1_CONTEXT_VERIFY
|
|
ALL_FLAGS = FLAG_SIGN | FLAG_VERIFY
|
|
NO_FLAGS = lib.SECP256K1_CONTEXT_NONE
|
|
|
|
HAS_RECOVERABLE = hasattr(lib, 'secp256k1_ecdsa_sign_recoverable')
|
|
HAS_SCHNORR = hasattr(lib, 'secp256k1_schnorr_sign')
|
|
HAS_ECDH = hasattr(lib, 'secp256k1_ecdh')
|
|
|
|
|
|
class Base(object):
|
|
|
|
def __init__(self, ctx, flags):
|
|
self._destroy = None
|
|
if ctx is None:
|
|
assert flags in (NO_FLAGS, FLAG_SIGN, FLAG_VERIFY, ALL_FLAGS)
|
|
ctx = lib.secp256k1_context_create(flags)
|
|
self._destroy = lib.secp256k1_context_destroy
|
|
|
|
self.flags = flags
|
|
self.ctx = ctx
|
|
|
|
def __del__(self):
|
|
if not hasattr(self, '_destroy'):
|
|
return
|
|
|
|
if self._destroy and self.ctx:
|
|
self._destroy(self.ctx)
|
|
self.ctx = None
|
|
|
|
|
|
class ECDSA: # Use as a mixin; instance.ctx is assumed to exist.
|
|
|
|
def ecdsa_serialize(self, raw_sig):
|
|
len_sig = 74
|
|
output = ffi.new('unsigned char[%d]' % len_sig)
|
|
outputlen = ffi.new('size_t *', len_sig)
|
|
|
|
res = lib.secp256k1_ecdsa_signature_serialize_der(
|
|
self.ctx, output, outputlen, raw_sig)
|
|
assert res == 1
|
|
|
|
return bytes(ffi.buffer(output, outputlen[0]))
|
|
|
|
def ecdsa_deserialize(self, ser_sig):
|
|
raw_sig = ffi.new('secp256k1_ecdsa_signature *')
|
|
res = lib.secp256k1_ecdsa_signature_parse_der(
|
|
self.ctx, raw_sig, ser_sig, len(ser_sig))
|
|
assert res == 1
|
|
|
|
return raw_sig
|
|
|
|
def ecdsa_serialize_compact(self, raw_sig):
|
|
len_sig = 64
|
|
output = ffi.new('unsigned char[%d]' % len_sig)
|
|
|
|
res = lib.secp256k1_ecdsa_signature_serialize_compact(
|
|
self.ctx, output, raw_sig)
|
|
assert res == 1
|
|
|
|
return bytes(ffi.buffer(output, len_sig))
|
|
|
|
def ecdsa_deserialize_compact(self, ser_sig):
|
|
if len(ser_sig) != 64:
|
|
raise Exception("invalid signature length")
|
|
|
|
raw_sig = ffi.new('secp256k1_ecdsa_signature *')
|
|
res = lib.secp256k1_ecdsa_signature_parse_compact(
|
|
self.ctx, raw_sig, ser_sig)
|
|
assert res == 1
|
|
|
|
return raw_sig
|
|
|
|
def ecdsa_signature_normalize(self, raw_sig, check_only=False):
|
|
"""
|
|
Check and optionally convert a signature to a normalized lower-S form.
|
|
If check_only is True then the normalized signature is not returned.
|
|
|
|
This function always return a tuple containing a boolean (True if
|
|
not previously normalized or False if signature was already
|
|
normalized), and the normalized signature. When check_only is True,
|
|
the normalized signature returned is always None.
|
|
"""
|
|
if check_only:
|
|
sigout = ffi.NULL
|
|
else:
|
|
sigout = ffi.new('secp256k1_ecdsa_signature *')
|
|
|
|
result = lib.secp256k1_ecdsa_signature_normalize(
|
|
self.ctx, sigout, raw_sig)
|
|
|
|
return (bool(result), sigout if sigout != ffi.NULL else None)
|
|
|
|
def ecdsa_recover(self, msg, recover_sig, raw=False, digest=hashlib.sha256):
|
|
if not HAS_RECOVERABLE:
|
|
raise Exception("secp256k1_recovery not enabled")
|
|
if self.flags & ALL_FLAGS != ALL_FLAGS:
|
|
raise Exception("instance not configured for ecdsa recover")
|
|
|
|
msg32 = _hash32(msg, raw, digest)
|
|
pubkey = ffi.new('secp256k1_pubkey *')
|
|
|
|
recovered = lib.secp256k1_ecdsa_recover(
|
|
self.ctx, pubkey, recover_sig, msg32)
|
|
if recovered:
|
|
return pubkey
|
|
raise Exception('failed to recover ECDSA public key')
|
|
|
|
def ecdsa_recoverable_serialize(self, recover_sig):
|
|
if not HAS_RECOVERABLE:
|
|
raise Exception("secp256k1_recovery not enabled")
|
|
|
|
outputlen = 64
|
|
output = ffi.new('unsigned char[%d]' % outputlen)
|
|
recid = ffi.new('int *')
|
|
|
|
lib.secp256k1_ecdsa_recoverable_signature_serialize_compact(
|
|
self.ctx, output, recid, recover_sig)
|
|
|
|
return bytes(ffi.buffer(output, outputlen)), recid[0]
|
|
|
|
def ecdsa_recoverable_deserialize(self, ser_sig, rec_id):
|
|
if not HAS_RECOVERABLE:
|
|
raise Exception("secp256k1_recovery not enabled")
|
|
if rec_id < 0 or rec_id > 3:
|
|
raise Exception("invalid rec_id")
|
|
if len(ser_sig) != 64:
|
|
raise Exception("invalid signature length")
|
|
|
|
recover_sig = ffi.new('secp256k1_ecdsa_recoverable_signature *')
|
|
|
|
parsed = lib.secp256k1_ecdsa_recoverable_signature_parse_compact(
|
|
self.ctx, recover_sig, ser_sig, rec_id)
|
|
if parsed:
|
|
return recover_sig
|
|
else:
|
|
raise Exception('failed to parse ECDSA compact sig')
|
|
|
|
def ecdsa_recoverable_convert(self, recover_sig):
|
|
if not HAS_RECOVERABLE:
|
|
raise Exception("secp256k1_recovery not enabled")
|
|
|
|
normal_sig = ffi.new('secp256k1_ecdsa_signature *')
|
|
|
|
lib.secp256k1_ecdsa_recoverable_signature_convert(
|
|
self.ctx, normal_sig, recover_sig)
|
|
|
|
return normal_sig
|
|
|
|
|
|
class Schnorr: # Use as a mixin; instance.ctx is assumed to exist.
|
|
|
|
def schnorr_recover(self, msg, schnorr_sig, raw=False,
|
|
digest=hashlib.sha256):
|
|
if not HAS_SCHNORR:
|
|
raise Exception("secp256k1_schnorr not enabled")
|
|
if self.flags & FLAG_VERIFY != FLAG_VERIFY:
|
|
raise Exception("instance not configured for sig verification")
|
|
|
|
msg32 = _hash32(msg, raw, digest)
|
|
pubkey = ffi.new('secp256k1_pubkey *')
|
|
|
|
recovered = lib.secp256k1_schnorr_recover(
|
|
self.ctx, pubkey, schnorr_sig, msg32)
|
|
if recovered:
|
|
return pubkey
|
|
raise Exception('failed to recover public key')
|
|
|
|
def schnorr_partial_combine(self, schnorr_sigs):
|
|
"""Combine multiple Schnorr partial signatures."""
|
|
if not HAS_SCHNORR:
|
|
raise Exception("secp256k1_schnorr not enabled")
|
|
assert len(schnorr_sigs) > 0
|
|
|
|
sig64 = ffi.new('char [64]')
|
|
sig64sin = []
|
|
for sig in schnorr_sigs:
|
|
if not isinstance(sig, bytes):
|
|
raise TypeError('expected bytes, got {}'.format(type(sig)))
|
|
if len(sig) != 64:
|
|
raise Exception('invalid signature length')
|
|
sig64sin.append(ffi.new('char []', sig))
|
|
|
|
res = lib.secp256k1_schnorr_partial_combine(
|
|
self.ctx, sig64, sig64sin, len(sig64sin))
|
|
if res <= 0:
|
|
raise Exception('failed to combine signatures ({})'.format(res))
|
|
|
|
return bytes(ffi.buffer(sig64, 64))
|
|
|
|
|
|
class PublicKey(Base, ECDSA, Schnorr):
|
|
|
|
def __init__(self, pubkey=None, raw=False, flags=FLAG_VERIFY, ctx=None):
|
|
Base.__init__(self, ctx, flags)
|
|
if pubkey is not None:
|
|
if raw:
|
|
if not isinstance(pubkey, bytes):
|
|
raise TypeError('raw pubkey must be bytes')
|
|
self.public_key = self.deserialize(pubkey)
|
|
else:
|
|
if not isinstance(pubkey, ffi.CData):
|
|
raise TypeError('pubkey must be an internal object')
|
|
assert ffi.typeof(pubkey) is ffi.typeof('secp256k1_pubkey *')
|
|
self.public_key = pubkey
|
|
else:
|
|
self.public_key = None
|
|
|
|
def serialize(self, compressed=True):
|
|
assert self.public_key, "No public key defined"
|
|
|
|
len_compressed = 33 if compressed else 65
|
|
res_compressed = ffi.new('char [%d]' % len_compressed)
|
|
outlen = ffi.new('size_t *', len_compressed)
|
|
compflag = EC_COMPRESSED if compressed else EC_UNCOMPRESSED
|
|
|
|
serialized = lib.secp256k1_ec_pubkey_serialize(
|
|
self.ctx, res_compressed, outlen, self.public_key, compflag)
|
|
assert serialized == 1
|
|
|
|
return bytes(ffi.buffer(res_compressed, len_compressed))
|
|
|
|
def deserialize(self, pubkey_ser):
|
|
if len(pubkey_ser) not in (33, 65):
|
|
raise Exception("unknown public key size (expected 33 or 65)")
|
|
|
|
pubkey = ffi.new('secp256k1_pubkey *')
|
|
|
|
res = lib.secp256k1_ec_pubkey_parse(
|
|
self.ctx, pubkey, pubkey_ser, len(pubkey_ser))
|
|
if not res:
|
|
raise Exception("invalid public key")
|
|
|
|
self.public_key = pubkey
|
|
return pubkey
|
|
|
|
def combine(self, pubkeys):
|
|
"""Add a number of public keys together."""
|
|
assert len(pubkeys) > 0
|
|
|
|
outpub = ffi.new('secp256k1_pubkey *')
|
|
for item in pubkeys:
|
|
assert ffi.typeof(item) is ffi.typeof('secp256k1_pubkey *')
|
|
|
|
res = lib.secp256k1_ec_pubkey_combine(
|
|
self.ctx, outpub, pubkeys, len(pubkeys))
|
|
if not res:
|
|
raise Exception('failed to combine public keys')
|
|
|
|
self.public_key = outpub
|
|
return outpub
|
|
|
|
def tweak_add(self, scalar):
|
|
"""
|
|
Tweak the current public key by adding a 32 byte scalar times
|
|
the generator to it and return a new PublicKey instance.
|
|
"""
|
|
return _tweak_public(self, lib.secp256k1_ec_pubkey_tweak_add, scalar)
|
|
|
|
def tweak_mul(self, scalar):
|
|
"""
|
|
Tweak the current public key by multiplying it by a 32 byte scalar
|
|
and return a new PublicKey instance.
|
|
"""
|
|
return _tweak_public(self, lib.secp256k1_ec_pubkey_tweak_mul, scalar)
|
|
|
|
def ecdsa_verify(self, msg, raw_sig, raw=False, digest=hashlib.sha256):
|
|
assert self.public_key, "No public key defined"
|
|
if self.flags & FLAG_VERIFY != FLAG_VERIFY:
|
|
raise Exception("instance not configured for sig verification")
|
|
|
|
msg32 = _hash32(msg, raw, digest)
|
|
|
|
verified = lib.secp256k1_ecdsa_verify(
|
|
self.ctx, raw_sig, msg32, self.public_key)
|
|
|
|
return bool(verified)
|
|
|
|
def schnorr_verify(self, msg, schnorr_sig, raw=False,
|
|
digest=hashlib.sha256):
|
|
assert self.public_key, "No public key defined"
|
|
if not HAS_SCHNORR:
|
|
raise Exception("secp256k1_schnorr not enabled")
|
|
if self.flags & FLAG_VERIFY != FLAG_VERIFY:
|
|
raise Exception("instance not configured for sig verification")
|
|
|
|
msg32 = _hash32(msg, raw, digest)
|
|
|
|
verified = lib.secp256k1_schnorr_verify(
|
|
self.ctx, schnorr_sig, msg32, self.public_key)
|
|
|
|
return bool(verified)
|
|
|
|
def ecdh(self, scalar):
|
|
assert self.public_key, "No public key defined"
|
|
if not HAS_ECDH:
|
|
raise Exception("secp256k1_ecdh not enabled")
|
|
if not isinstance(scalar, bytes) or len(scalar) != 32:
|
|
raise TypeError('scalar must be composed of 32 bytes')
|
|
|
|
result = ffi.new('char [32]')
|
|
|
|
res = lib.secp256k1_ecdh(self.ctx, result, self.public_key, scalar)
|
|
if not res:
|
|
raise Exception('invalid scalar ({})'.format(res))
|
|
|
|
return bytes(ffi.buffer(result, 32))
|
|
|
|
|
|
class PrivateKey(Base, ECDSA, Schnorr):
|
|
|
|
def __init__(self, privkey=None, raw=True, flags=ALL_FLAGS, ctx=None):
|
|
assert flags in (ALL_FLAGS, FLAG_SIGN)
|
|
|
|
Base.__init__(self, ctx, flags)
|
|
self.pubkey = None
|
|
self.private_key = None
|
|
if privkey is None:
|
|
self.set_raw_privkey(_gen_private_key())
|
|
else:
|
|
if raw:
|
|
if not isinstance(privkey, bytes) or len(privkey) != 32:
|
|
raise TypeError('privkey must be composed of 32 bytes')
|
|
self.set_raw_privkey(privkey)
|
|
else:
|
|
self.deserialize(privkey)
|
|
|
|
def _update_public_key(self):
|
|
public_key = self._gen_public_key(self.private_key)
|
|
self.pubkey = PublicKey(
|
|
public_key, raw=False, ctx=self.ctx, flags=self.flags)
|
|
|
|
def set_raw_privkey(self, privkey):
|
|
if not lib.secp256k1_ec_seckey_verify(self.ctx, privkey):
|
|
raise Exception("invalid private key")
|
|
self.private_key = privkey
|
|
self._update_public_key()
|
|
|
|
def serialize(self):
|
|
hexkey = binascii.hexlify(self.private_key)
|
|
return hexkey.decode('utf8')
|
|
|
|
def deserialize(self, privkey_ser):
|
|
if len(privkey_ser) != 64:
|
|
raise Exception("invalid private key")
|
|
rawkey = binascii.unhexlify(privkey_ser)
|
|
|
|
self.set_raw_privkey(rawkey)
|
|
return self.private_key
|
|
|
|
def _gen_public_key(self, privkey):
|
|
pubkey_ptr = ffi.new('secp256k1_pubkey *')
|
|
|
|
created = lib.secp256k1_ec_pubkey_create(self.ctx, pubkey_ptr, privkey)
|
|
assert created == 1
|
|
|
|
return pubkey_ptr
|
|
|
|
def tweak_add(self, scalar):
|
|
"""
|
|
Tweak the current private key by adding a 32 byte scalar
|
|
to it and return a new raw private key composed of 32 bytes.
|
|
"""
|
|
return _tweak_private(self, lib.secp256k1_ec_privkey_tweak_add, scalar)
|
|
|
|
def tweak_mul(self, scalar):
|
|
"""
|
|
Tweak the current private key by multiplying it by a 32 byte scalar
|
|
and return a new raw private key composed of 32 bytes.
|
|
"""
|
|
return _tweak_private(self, lib.secp256k1_ec_privkey_tweak_mul, scalar)
|
|
|
|
def ecdsa_sign(self, msg, raw=False, digest=hashlib.sha256, custom_nonce=None):
|
|
msg32 = _hash32(msg, raw, digest)
|
|
raw_sig = ffi.new('secp256k1_ecdsa_signature *')
|
|
nonce_fn = ffi.NULL
|
|
nonce_data = ffi.NULL
|
|
if custom_nonce:
|
|
nonce_fn, nonce_data = custom_nonce
|
|
signed = lib.secp256k1_ecdsa_sign(
|
|
self.ctx, raw_sig, msg32, self.private_key, nonce_fn, nonce_data)
|
|
assert signed == 1
|
|
|
|
return raw_sig
|
|
|
|
def ecdsa_sign_recoverable(self, msg, raw=False, digest=hashlib.sha256):
|
|
if not HAS_RECOVERABLE:
|
|
raise Exception("secp256k1_recovery not enabled")
|
|
|
|
msg32 = _hash32(msg, raw, digest)
|
|
raw_sig = ffi.new('secp256k1_ecdsa_recoverable_signature *')
|
|
|
|
signed = lib.secp256k1_ecdsa_sign_recoverable(
|
|
self.ctx, raw_sig, msg32, self.private_key, ffi.NULL, ffi.NULL)
|
|
assert signed == 1
|
|
|
|
return raw_sig
|
|
|
|
def schnorr_sign(self, msg, raw=False, digest=hashlib.sha256):
|
|
if not HAS_SCHNORR:
|
|
raise Exception("secp256k1_schnorr not enabled")
|
|
|
|
msg32 = _hash32(msg, raw, digest)
|
|
sig64 = ffi.new('char [64]')
|
|
|
|
signed = lib.secp256k1_schnorr_sign(
|
|
self.ctx, sig64, msg32, self.private_key, ffi.NULL, ffi.NULL)
|
|
assert signed == 1
|
|
|
|
return bytes(ffi.buffer(sig64, 64))
|
|
|
|
def schnorr_generate_nonce_pair(self, msg, raw=False,
|
|
digest=hashlib.sha256):
|
|
"""
|
|
Generate a nonce pair deterministically for use with
|
|
schnorr_partial_sign.
|
|
"""
|
|
if not HAS_SCHNORR:
|
|
raise Exception("secp256k1_schnorr not enabled")
|
|
|
|
msg32 = _hash32(msg, raw, digest)
|
|
pubnonce = ffi.new('secp256k1_pubkey *')
|
|
privnonce = ffi.new('char [32]')
|
|
|
|
valid = lib.secp256k1_schnorr_generate_nonce_pair(
|
|
self.ctx, pubnonce, privnonce, msg32, self.private_key,
|
|
ffi.NULL, ffi.NULL)
|
|
assert valid == 1
|
|
|
|
return pubnonce, privnonce
|
|
|
|
def schnorr_partial_sign(self, msg, privnonce, pubnonce_others,
|
|
raw=False, digest=hashlib.sha256):
|
|
"""
|
|
Produce a partial Schnorr signature, which can be combined using
|
|
schnorr_partial_combine to end up with a full signature that is
|
|
verifiable using PublicKey.schnorr_verify.
|
|
|
|
To combine pubnonces, use PublicKey.combine.
|
|
|
|
Do not pass the pubnonce produced for the respective privnonce;
|
|
combine the pubnonces from other signers and pass that instead.
|
|
"""
|
|
if not HAS_SCHNORR:
|
|
raise Exception("secp256k1_schnorr not enabled")
|
|
|
|
msg32 = _hash32(msg, raw, digest)
|
|
sig64 = ffi.new('char [64]')
|
|
|
|
res = lib.secp256k1_schnorr_partial_sign(
|
|
self.ctx, sig64, msg32, self.private_key,
|
|
pubnonce_others, privnonce)
|
|
if res <= 0:
|
|
raise Exception('failed to partially sign ({})'.format(res))
|
|
|
|
return bytes(ffi.buffer(sig64, 64))
|
|
|
|
|
|
def _hash32(msg, raw, digest):
|
|
if not raw:
|
|
msg32 = digest(msg).digest()
|
|
else:
|
|
msg32 = msg
|
|
if len(msg32) * 8 != 256:
|
|
raise Exception("digest function must produce 256 bits")
|
|
return msg32
|
|
|
|
|
|
def _gen_private_key():
|
|
key = os.urandom(32)
|
|
return key
|
|
|
|
|
|
def _tweak_public(inst, func, scalar):
|
|
if not isinstance(scalar, bytes) or len(scalar) != 32:
|
|
raise TypeError('scalar must be composed of 32 bytes')
|
|
assert inst.public_key, "No public key defined."
|
|
|
|
# Create a copy of the current public key.
|
|
newpub = PublicKey(inst.serialize(), raw=True)
|
|
|
|
res = func(inst.ctx, newpub.public_key, scalar)
|
|
if not res:
|
|
raise Exception("Tweak is out of range")
|
|
|
|
return newpub
|
|
|
|
|
|
def _tweak_private(inst, func, scalar):
|
|
if not isinstance(scalar, bytes) or len(scalar) != 32:
|
|
raise TypeError('scalar must be composed of 32 bytes')
|
|
|
|
# Create a copy of the current private key.
|
|
key = ffi.new('char [32]', inst.private_key)
|
|
|
|
res = func(inst.ctx, key, scalar)
|
|
if not res:
|
|
raise Exception("Tweak is out of range")
|
|
|
|
return bytes(ffi.buffer(key, 32))
|
|
|
|
|
|
def _main_cli(args, out, encoding='utf-8'):
|
|
import binascii
|
|
|
|
def show_public(public_key):
|
|
rawp = public_key.serialize()
|
|
out.write(u"Public key: {}\n".format(
|
|
binascii.hexlify(rawp).decode(encoding)))
|
|
|
|
def sign(funcname, params):
|
|
raw = bytes(bytearray.fromhex(params.private_key))
|
|
priv = PrivateKey(raw)
|
|
func = getattr(priv, funcname)
|
|
sig = func(params.message)
|
|
return priv, sig
|
|
|
|
if args.action == 'privkey':
|
|
if args.private_key:
|
|
rawkey = bytes(bytearray.fromhex(args.private_key))
|
|
else:
|
|
rawkey = None
|
|
priv = PrivateKey(rawkey)
|
|
raw = priv.private_key
|
|
out.write(u"{}\n".format(binascii.hexlify(raw).decode(encoding)))
|
|
if args.show_pubkey:
|
|
show_public(priv.pubkey)
|
|
|
|
elif args.action == 'sign':
|
|
priv, sig_raw = sign('ecdsa_sign', args)
|
|
sig = priv.ecdsa_serialize(sig_raw)
|
|
out.write(u"{}\n".format(binascii.hexlify(sig).decode(encoding)))
|
|
if args.show_pubkey:
|
|
show_public(priv.pubkey)
|
|
|
|
elif args.action == 'checksig':
|
|
raw = bytes(bytearray.fromhex(args.public_key))
|
|
sig = bytes(bytearray.fromhex(args.signature))
|
|
pub = PublicKey(raw, raw=True)
|
|
try:
|
|
sig_raw = pub.ecdsa_deserialize(sig)
|
|
good = pub.ecdsa_verify(args.message, sig_raw)
|
|
except:
|
|
good = False
|
|
out.write(u"{}\n".format(good))
|
|
return 0 if good else 1
|
|
|
|
elif args.action == 'signrec':
|
|
priv, sig = sign('ecdsa_sign_recoverable', args)
|
|
sig, recid = priv.ecdsa_recoverable_serialize(sig)
|
|
out.write(u"{} {}\n".format(binascii.hexlify(sig).decode(encoding), recid))
|
|
if args.show_pubkey:
|
|
show_public(priv.pubkey)
|
|
|
|
elif args.action == 'recpub':
|
|
empty = PublicKey(flags=ALL_FLAGS)
|
|
sig_raw = bytes(bytearray.fromhex(args.signature))
|
|
sig = empty.ecdsa_recoverable_deserialize(sig_raw, args.recid)
|
|
pubkey = empty.ecdsa_recover(args.message, sig)
|
|
show_public(PublicKey(pubkey))
|
|
|
|
return 0
|
|
|
|
|
|
def _parse_cli():
|
|
import sys
|
|
from argparse import ArgumentParser
|
|
|
|
py2 = sys.version_info.major == 2
|
|
enc = sys.getfilesystemencoding()
|
|
def bytes_input(s):
|
|
return s if py2 else s.encode(enc)
|
|
|
|
parser = ArgumentParser(prog="secp256k1")
|
|
subparser = parser.add_subparsers(dest='action')
|
|
|
|
genparser = subparser.add_parser('privkey')
|
|
genparser.add_argument('-p', '--show-pubkey', action='store_true')
|
|
genparser.add_argument('-k', '--private_key')
|
|
|
|
sign = subparser.add_parser('sign')
|
|
sign.add_argument('-k', '--private-key', required=True)
|
|
sign.add_argument('-m', '--message', required=True, type=bytes_input)
|
|
sign.add_argument('-p', '--show-pubkey', action='store_true')
|
|
|
|
signrec = subparser.add_parser('signrec')
|
|
signrec.add_argument('-k', '--private-key', required=True)
|
|
signrec.add_argument('-m', '--message', required=True, type=bytes_input)
|
|
signrec.add_argument('-p', '--show-pubkey', action='store_true')
|
|
|
|
check = subparser.add_parser('checksig')
|
|
check.add_argument('-p', '--public-key', required=True)
|
|
check.add_argument('-m', '--message', required=True, type=bytes_input)
|
|
check.add_argument('-s', '--signature', required=True)
|
|
|
|
recpub = subparser.add_parser('recpub')
|
|
recpub.add_argument('-m', '--message', required=True, type=bytes_input)
|
|
recpub.add_argument('-i', '--recid', required=True, type=int)
|
|
recpub.add_argument('-s', '--signature', required=True)
|
|
|
|
return parser, enc
|
|
|
|
|
|
def main():
|
|
import sys
|
|
parser, enc = _parse_cli()
|
|
args = parser.parse_args(sys.argv[1:])
|
|
sys.exit(_main_cli(args, sys.stdout, enc))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|