I am trying to reproduce the signing algorithm used for tz2 address which uses the secp256pk1 elliptic curve.

My problem is that when I try to verify if the signature is valid with the tezos cli, I get results that I can't explain. I notice about half of the signature are valid and the other half is invalid.

Here is my python implementation of it:

import ecdsa
import hashlib
import base58

# Public Key listed below
x = b'\xe72q\x9d\xc5&?\xbf\x7f\x07@\t&\xa2\xe6H\xcd\x10\xa74\x81B=ZX~\xab\xdb4\xe4\x15\x86'
y = b"\x85\xa6\xdc\x82\xed>\xee\xa6\n\x7f\xcbI\xf5\xb6 '\xc0\x11\x8f\xc9\xa3\xf6\x82.u\xe48\xee\xa9\x90\xbe\xc8"
pub = x + y

# Secret Key listed below
secret = bytes.fromhex("d163c550ee8703c161b8663f250a4abff1afceb0a47d1ad1b8d645d33fe7db9a")
message = b"test"

def tb(l):
    return b''.join(map(lambda x: x.to_bytes(1, 'big'), l))

def base58_encode(v: bytes) -> bytes:
    return base58.b58encode_check(tb([13, 115, 101, 19, 63]) + v)

def blake2b_32(by):
    return hashlib.blake2b(by, digest_size=32)

digest = hashlib.blake2b(message, digest_size=32).digest()
sk = ecdsa.SigningKey.from_string(secret, curve=ecdsa.SECP256k1)
sig = sk.sign_digest(digest)

vk = ecdsa.VerifyingKey.from_string(pub, curve=ecdsa.SECP256k1)
vk.default_hashfunc = blake2b_32
print("Verify sig digest: ", vk.verify_digest(sig, digest))
print("Verify sig: ", vk.verify(sig, b"test"))
print("Tezos sig: ", base58_encode(sig))

Example of failing output:

Verify sig digest:  True
Verify sig:  True
Tezos sig:  b'spsig15ztbzB8VVaFdPkzxLDz4GYNxTpoYniRaXDdj2pKZD6xoLkGE7ofKZH4VxBbWMYFd6TSo5kQ8YgNkVCDSXxqvnEQEzJ3PJ'

Example of successful output:

Verify sig digest:  True
Verify sig:  True
Tezos sig:  b'spsig1bHeCpE4XMECu65i4RoTdC9RFLfbVB2zW4fAadaV2zgfcTuLXtoxP7MMNWDWF1JUSfN5u8qPg3Ci9SD6Hf1YeGN7F1vFjx'

Here is the detail of the debugging key I am using in the script:

Secret Key: spsk31nG6K6tHTiLPbT91YWSwwSPn4Qejv4w3Tn67hfKPNWNztRDTg

Public Key: sppk7b4TURq2T9rhPLFaSz6mkBCzKzfiBjctQSMorvLD5GSgCduvKuf

Public Key Hash: tz2BFTyPeYRzxd5aiBchbXN3WCZhx7BqbMBq

Command that I run to check the validity of my signatures

tezos-cli check that 0x74657374 was signed by test_account to produce spsig15ztbzB8VVaFdPkzxLDz4GYNxTpoYniRaXDdj2pKZD6xoLkGE7ofKZH4VxBbWMYFd6TSo5kQ8YgNkVCDSXxqvnEQEzJ3PJ

Can someone explain to me what is wrong with the implementation above?

2 Answers 2


The immediate problem seems to be that you are not getting signatures in "lower S" form. This seems better:

sk.sign_digest(digest, sigencode=ecdsa.util.sigencode_string_canonize)

I cannot vouch for the correctness of the code generally.


Not a direct answer, but workaround

python-ecdsa lib generates non-deterministic signatures by default (see more at https://www.rfc-editor.org/rfc/rfc6979#section-3.2). Each time you run your code you get a new result. I've experienced this issue and eventually switched to secp256k1 package (there were several extra reasons for that): https://github.com/baking-bad/pytezos/blob/41f983c40cdb2a4445a88eccf86a2eddaa24e555/pytezos/crypto.py#L213

  • 1
    Thanks for your answer Michael, from my experiments Tezos do not require deterministic signature (It seems to generate deterministic signatures though). What are the other reasons you switched to deterministic signatures? Commented May 6, 2019 at 18:55
  • That's right, I tried to emulate node cli and that was the only way to make sure everything works as expected via unittests) The other reason I switched from python-ecdsa to secp256k1 is that VerifyingKey can only be constructed from the public key in its full form X+Y, while a compact form is used in Tezos. Just avoiding custom implementation of Y derivation, although it's not that complicated :) Commented May 7, 2019 at 7:17
  • Not a direct answer to your question though, my bad. Commented May 7, 2019 at 7:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.