Serviços em Go que lidam com assinaturas Ethereum costumam importar github.com/ethereum/go-ethereum/crypto e accounts. Com o cgo habilitado, o pacote usa a cópia da libsecp256k1 embutida no go-ethereum; com CGO_ENABLED=0, ele recorre à implementação em Go puro do decred — o teste rodou os dois builds, e todos os fatos saíram idênticos.
crypto.Sign(hash, key) recebe exatamente 32 bytes (caso contrário, hash is required to be exactly 32 bytes) e não calcula hash nem adiciona prefixo. Uma mensagem de login precisa passar antes por accounts.TextHash, que adiciona o prefixo EIP-191 e calcula o hash com keccak-256 — o teste conferiu isso com um keccak independente. Assinar em vez disso o keccak puro da mensagem produz uma assinatura que nunca vai coincidir com a do ethers, do viem ou das carteiras.
A assinatura tem 65 bytes, com o ID de recuperação como v = 0 ou 1, enquanto carteiras, ethers, viem e web3.py colocam 27 ou 28 nessa posição. crypto.Ecrecover rejeitou v = 27/28 com invalid signature recovery id, então subtraia 27 na entrada e some 27 na saída, como faz o exemplo. Os nonces seguem a RFC 6979 — a assinatura coincidiu com uma implementação de referência independente —, e o s é sempre baixo (low-S).
Os dois caminhos de verificação divergem quanto a high-S. crypto.VerifySignature, que recebe 64 bytes sem o v, rejeitou a assinatura espelhada (r, n − s), mas crypto.Ecrecover e crypto.SigToPub recuperaram dela a chave pública correta, como faz o contrato pré-compilado ecrecover da EVM. Por isso, contratos e serviços que recuperam endereços precisam rejeitar high-S por conta própria se tratam uma assinatura como identificador único.
As chaves públicas circulam como pontos SEC1 — na forma não comprimida, de 65 bytes, ou na forma comprimida, de 33 bytes —, e as chaves privadas, como escalares de 32 bytes. Para ver qual convenção uma assinatura segue, cole-a no verificador de assinaturas Ethereum; nonces repetidos entre transações de um mesmo endereço aparecem no verificador de reutilização de nonce no Ethereum.