def serialize(self, compressed=True): privser = ffi.new('char [279]') keylen = ffi.new('size_t *') res = lib.secp256k1_ec_privkey_export( self.ctx, privser, keylen, self.private_key, int(compressed)) assert res == 1 return bytes(ffi.buffer(privser, keylen[0]))
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_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 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) serialized = lib.secp256k1_ec_pubkey_serialize( self.ctx, res_compressed, outlen, self.public_key, int(compressed)) assert serialized == 1 return bytes(ffi.buffer(res_compressed, len_compressed))
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 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 _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 ecdsa_sign(self, msg, raw=False, digest=hashlib.sha256): msg32 = _hash32(msg, raw, digest) raw_sig = ffi.new('secp256k1_ecdsa_signature *') signed = lib.secp256k1_ecdsa_sign( self.ctx, raw_sig, msg32, self.private_key, ffi.NULL, ffi.NULL) assert signed == 1 return raw_sig
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))) 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))
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
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 deserialize(self, privkey_ser): privkey = ffi.new('char [32]') res = lib.secp256k1_ec_privkey_import( self.ctx, privkey, privkey_ser, len(privkey_ser)) if not res: raise Exception("invalid private key") rawkey = bytes(ffi.buffer(privkey, 32)) self.set_raw_privkey(rawkey) return self.private_key
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 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 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 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 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))
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 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 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')