O ecdsa implementa ECDSA e EdDSA em Python puro, sem OpenSSL por baixo. Isso o torna fácil de instalar em qualquer lugar e prático para vetores de teste e para o ensino, e ele ainda é uma dependência de stacks JOSE mais antigas, como o python-jose. Os mantenedores afirmam que ele não foi projetado para resistir a ataques de canal lateral, então assinaturas em produção devem ficar com o pyca/cryptography ou o coincurve.
Três padrões causam quase todas as falhas de interoperabilidade. O hash padrão é SHA-1: uma SigningKey criada sem hashfunc assina digests SHA-1, o que o teste confirmou ao verificar a assinatura padrão com o pyca/cryptography — ela passou com SHA-1 e falhou com SHA-256. A assinatura é r‖s bruta (64 bytes na P-256), não DER. E VerifyingKey.to_string() retorna o x‖y isolado de 64 bytes sem o prefixo 0x04, que o OpenSSL, o Go e o WebCrypto não leem como um ponto.
A assinatura usa um nonce aleatório, a menos que você chame sign_deterministic(), que reproduziu o vetor de teste P-256 da RFC 6979. O s fica como foi calculado — alto em cerca de metade das vezes —, a menos que você escolha um dos codificadores *_canonize, e a verificação aceita qualquer das metades. Toda escolha de codificação é feita a cada chamada, por meio de sigencode= e sigdecode=, então os lados que assinam e que verificam precisam indicar o mesmo par; uma assinatura DER passada ao decodificador bruto padrão lança BadSignatureError: Malformed formatting of signature.
O suporte a curvas é o mais amplo entre as bibliotecas Python — da P-224 à P-521, secp256k1 e Brainpool —, além de Ed25519 e Ed448. As chaves entram como SPKI PEM ou DER, pontos SEC1 e escalares brutos; to_pem() grava SEC1 EC PRIVATE KEY, a menos que você passe format="pkcs8".
O exemplo corrige os três padrões de uma vez: SHA-256, um nonce determinístico e uma assinatura DER canônica low-S. Para conferir uma assinatura gerada por ele com outra biblioteca, o conversor DER ⇄ bruto e os vetores de teste são o caminho mais rápido.