O número que nunca deve se repetir
Uma assinatura ECDSA é um par de inteiros, (r, s). Para produzi-la são necessárias três entradas: a chave privada d, o hash da mensagem z e um inteiro secreto novo k — o nonce, abreviação de “number used once” (número usado uma única vez). O signatário escolhe k no intervalo [1, n−1], em que n é a ordem do ponto base G da curva, e calcula:
R = k * G (a point on the curve)
r = x-coordinate of R mod n
s = k^-1 * (z + r * d) mod n
signature = (r, s)
Repare de onde vem cada saída. O s mistura tudo — o nonce, o hash e a chave. Já o r é derivado de k e de mais nada: é a coordenada x de kG, uma função apenas do nonce e da curva. A mensagem não entra nessa conta. A chave privada não entra nessa conta.
Esse é o problema inteiro em uma frase. Duas assinaturas produzidas com o mesmo k terão o mesmo r, e o r é publicado junto com cada assinatura. Um nonce repetido não é uma falha secreta que alguém precisa descobrir — ele se anuncia à vista de todos, num campo que todo verificador lê e todo explorador de blocos guarda para sempre.
Duas assinaturas, duas incógnitas
Suponha que um signatário tenha usado o mesmo k e a mesma chave d para duas mensagens diferentes, com hashes z1 e z2. As duas assinaturas têm o mesmo r, então temos:
s1 = k^-1 * (z1 + r * d) mod n
s2 = k^-1 * (z2 + r * d) mod n
Tudo aqui é público, exceto k e d. Duas equações, duas incógnitas. Multiplique os dois lados de cada uma por k para eliminar o inverso:
k * s1 = z1 + r * d mod n
k * s2 = z2 + r * d mod n
O termo r * d é idêntico nas duas linhas, porque a mesma chave assinou as duas mensagens e o mesmo nonce produziu o mesmo r. Subtraia a segunda equação da primeira e ele se cancela, tirando a chave privada da conta por completo:
k * s1 - k * s2 = z1 - z2 mod n
k * (s1 - s2) = z1 - z2 mod n
k = (z1 - z2) * (s1 - s2)^-1 mod n
Isso recupera o nonce. Substituindo-o em qualquer uma das equações do passo 2, você obtém a chave, já que agora r é o único fator que multiplica d:
k * s1 = z1 + r * d mod n
r * d = k * s1 - z1 mod n
d = (s1 * k - z1) * r^-1 mod n
Quatro passos, sem busca, sem adivinhação. O custo é de duas inversões modulares e um punhado de multiplicações — microssegundos em qualquer máquina, a partir de dados que já são públicos.
Um detalhe prático: as codificações nem sempre preservam o s exato que o signatário calculou. O Bitcoin e várias bibliotecas normalizam para a forma “low-S”, trocando s por n − s quando ele cai na metade superior do intervalo — uma correção de maleabilidade proposta na BIP-62 e especificada como regra de retransmissão na BIP-146, sem relação com nonces, mas que se cruza com esta aritmética. O valor recuperado pode então corresponder a −k em vez de k, e o sinal precisa ser resolvido antes que os números batam. Isso não muda em nada a dificuldade; o tema da codificação é tratado à parte em formatos de assinatura ECDSA.
Não é um experimento mental
Esta é a classe de falha do ECDSA mais produtiva no mundo real, e nenhum dos casos foi causado pela matemática. Em todos eles, uma implementação forneceu um nonce que não deveria ter fornecido.
- PlayStation 3, 2010. A implementação de assinatura de firmware da Sony usava um valor constante no lugar de
k. Todas as assinaturas que ela produzia traziam umridêntico, e a chave de assinatura mestra do console saía de quaisquer duas delas. A falha foi demonstrada publicamente no 27C3, na palestra Console Hacking 2010. É a ilustração mais pura que existe: o algoritmo foi implementado corretamente, exceto por uma variável que nunca variava. - Carteiras Android, 2013. Em 14 de agosto de 2013, o Android Developers Blog publicou Some SecureRandom Thoughts, depois de investigar o que o post descreve como “o comprometimento de uma transação de bitcoin que levou à atualização de vários aplicativos de Bitcoin em 11 de agosto”. A causa raiz era que aplicativos que usam a Java Cryptography Architecture “podem não receber valores criptograficamente fortes em dispositivos Android por causa da inicialização inadequada do PRNG subjacente”. Carteiras que assinavam em dispositivos afetados tiravam nonces de um gerador que podia começar no mesmo estado repetidas vezes. O Google publicou a solução de contorno
PRNGFixespara os níveis de API de 16 a 18 e corrigiu a plataforma. Repare no que quebrou: não o código criptográfico da carteira, nem a curva, mas a primitiva do sistema operacional por baixo dos dois. - A classe mais ampla: nonces enviesados. A repetição completa é a versão barulhenta. A versão silenciosa é um nonce apenas distorcido — alguns bits sem distribuição uniforme, porque o gerador retorna valores num intervalo ligeiramente errado, ou trunca, ou vaza informação pelo tempo de execução. Aí não existe um atalho algébrico limpo, mas o problema pode ser reformulado como a busca de um vetor curto num reticulado, e a redução de reticulados dá conta dele. O trabalho Biased Nonce Sense, de Breitner e Heninger (Financial Cryptography 2019), aplicou isso a blockchains públicas e a varreduras de toda a internet e recuperou centenas de chaves de Bitcoin, além de dezenas de chaves de Ethereum, Ripple, SSH e HTTPS — a partir de nonces que nunca se repetiram, apenas eram imperfeitos. A lição é dura: “quase aleatório” não é defesa.
Nenhum desses casos precisou de uma falha no ECDSA, e nenhum se resolve com uma curva maior. O algoritmo exige um segredo uniforme perfeito por assinatura e não dá nenhum aviso quando não o recebe.
O que a blockchain mostra hoje
Valores r repetidos continuam na blockchain do Bitcoin, para sempre, porque blockchains não esquecem. Nosso verificador de reutilização de nonce busca as transações recentes de um endereço na API do mempool.space, extrai as assinaturas DER das entradas que gastam fundos desse endereço e as agrupa por (chave pública, r). Veja o que ele retornou para 1BFhrfTTZP3Nw4BNy4eX4KFLsn9ZeijcMm em 20 de agosto de 2026:
address 1BFhrfTTZP3Nw4BNy4eX4KFLsn9ZeijcMm (P2PKH)
transactions returned 50 <- API cap, not the full history
spending from it 30
inputs from address 110
signatures parsed 110 (unattributed 0, taproot 0, unparsed 0)
distinct public keys 1
distinct r values 73 <- 110 signatures, only 73 nonces
repeated-r groups 6
identical duplicates 0
Uma chave, 110 assinaturas, 73 nonces distintos. A diferença conta toda a história: 43 das 110 assinaturas estão num grupo que compartilha um r com pelo menos uma outra assinatura, e em cada um dos seis grupos os valores s são diferentes, o que descarta a explicação inofensiva de uma assinatura duplicada. O maior grupo:
r = d47ce4c025c35ec440bc81d99834a624875161a26bf56ef7fdc0f5d52f843ad1
pubkey = 04dbd0c6...49c5ff (uncompressed)
ad698b53bbba9e7d807559ec7d3a466fccb669dd3659a1604a206eeb9965c527 vin 0 2012-12-21
b4922bc8cc3624487cdcf4b0ed5a98a21a4552a48dfe495490a7443dd546821e vin 0 2012-12-21
8ab813e5a4039eb79484c315e9c0e201c9d80385c3f3650a4c4b974db9172148 vin 0 2012-12-21
0b5315f4d472a22a6937a20a8d44b8e77099f2a951b65c53c9ebf912352504fa vin 0 2012-12-19
952d8301780ca74fca5f6541db44ba49e47e8514dfdef36bc7e3d534085dbbf6 vin 0 2012-12-19
... 5 more, the oldest on 2012-08-16
Dois detalhes merecem atenção. Primeiro, o segundo maior grupo reúne outras dez assinaturas que compartilham um r diferente — e, nos dois grupos, todas as assinaturas ficam no mesmo índice de entrada da sua transação (índice 0 em um, índice 1 no outro). Não é assim que se parece uma colisão ocasional do RNG. É assim que se parece um gerador reinicializado no mesmo estado no início de cada operação de assinatura e depois avançado passo a passo: a entrada 0 sempre recebe o primeiro valor, e a entrada 1, o segundo. Segundo, a reutilização vai de agosto a dezembro de 2012 — quase um ano antes da divulgação do problema do SecureRandom no Android. A classe de defeito já circulava na prática antes de ter um nome público, um lembrete de que esses anúncios marcam quando um problema foi percebido, e não quando ele começou.
A solução: pare de jogar dados
A resposta duradoura não é “use um gerador de números aleatórios melhor”. É tirar o gerador de números aleatórios do caminho da assinatura por completo.
RFC 6979, ECDSA determinístico. Em vez de sortear k, derive-o: inicialize um gerador determinístico de bits aleatórios baseado em HMAC (HMAC-DRBG) usando a chave privada e o hash da mensagem como semente e tire k da saída dele, gerando outro valor se ele cair fora de [1, n−1]. O resultado tem aparência uniforme, é imprevisível sem a chave e — o que é crucial — é uma função da mensagem. Uma mensagem diferente produz necessariamente um k diferente, então a reutilização acidental passa a ser estruturalmente impossível, e não apenas improvável. Assinar a mesma mensagem duas vezes reproduz os mesmos bytes, o que é inofensivo e ainda serve como uma propriedade conveniente para testes. Nenhuma fonte de entropia é consultada no momento da assinatura, então uma fonte com defeito não tem como prejudicar você. O FIPS 186-5 permite o ECDSA determinístico ao lado da forma aleatorizada.
RFC 8032, EdDSA. O Ed25519 e o Ed448 tornam a mesma ideia obrigatória ao embuti-la no esquema: o segredo de cada assinatura é derivado do hash de um prefixo derivado da chave junto com a mensagem, e a API não oferece nenhum ponto pelo qual quem implementa possa colocar um valor aleatório no lugar. Se você está escolhendo um esquema, e não herdando um, isso elimina o modo de falha por construção. As assinaturas Taproot do Bitcoin seguem a mesma filosofia — a BIP-340 exige uma derivação determinística do nonce, com aleatoriedade auxiliar opcional.
Assinaturas hedged. O determinismo puro tem uma contrapartida que vale a pena conhecer: uma falha induzida durante a assinatura — uma perturbação no clock ou na tensão de um smartcard — pode fazer um signatário determinístico produzir duas assinaturas diferentes sobre a mesma mensagem com o mesmo k derivado, o que é, de novo, a condição do nonce reutilizado. A RFC 6979 prevê isso na §3.6. A resposta hedged mistura aleatoriedade nova na derivação determinística em vez de substituí-la, o que dá um nonce seguro se ou a derivação ou o RNG estiver íntegro. O pacote crypto/ecdsa do Go assina dessa forma; @noble/curves oferece isso como a opção extraEntropy; a aleatoriedade auxiliar da BIP-340 é a mesma ideia.
Como as bibliotecas mais comuns se comportam por padrão:
| Biblioteca | Nonce determinístico por padrão? | Detalhe |
|---|---|---|
| libsecp256k1 (Bitcoin Core) | Sim — RFC 6979 | É possível passar outra função de nonce, se você precisar. |
@noble/curves | Sim — RFC 6979 | Modo hedged disponível via extraEntropy. |
| Rust p256 / k256 (RustCrypto) | Sim — RFC 6979 | A mesma chave e a mesma mensagem sempre produzem os mesmos bytes; uma variante aleatorizada precisa ser ativada explicitamente. |
| Go crypto/ecdsa | Hedged, não RFC 6979 | Deriva o nonce da chave, da mensagem e de aleatoriedade nova ao mesmo tempo, então os bytes mudam a cada execução e não podem ser fixados em testes. |
| Python ecdsa (Python puro) | Só se ativado | A assinatura determinística é um método separado; a chamada sign() simples usa aleatoriedade. |
| Python cryptography (pyca) | Só se ativado | As versões recentes aceitam uma opção de assinatura determinística, e esse caminho exige um OpenSSL recente por baixo. |
| OpenSSL CLI | Só se ativado (3.2+) | O parâmetro de assinatura nonce-type:1 com um digest explícito: pkeyutl -sign -pkeyopt nonce-type:1 -pkeyopt digest:sha256 ou dgst -sha256 -sign key.pem -sigopt nonce-type:1. |
| Node.js node:crypto | Não — nonce aleatório | A mesma chave e a mesma mensagem produzem bytes de assinatura diferentes a cada chamada. |
| WebCrypto (crypto.subtle) | Não — nonce aleatório | A API também não oferece nenhuma forma de fornecer seu próprio nonce. |
| Java JCA (SunEC) | Não — nonce aleatório | O ECDSA determinístico fica disponível ao adicionar um provedor como o Bouncy Castle. |
| PHP openssl_* / Ruby OpenSSL | Não — nonce aleatório | Nenhuma das duas expõe um modo determinístico. |
Um “Não” nessa tabela não é um relatório de bug. Um nonce aleatorizado tirado de um CSPRNG saudável do sistema operacional é uma forma perfeitamente conforme de assinar, e é o que todas as edições do FIPS 186 especificaram; o FIPS 186-5 (2023) acrescentou a variante determinística ao lado da aleatorizada. A coluna mede dependência, não correção: as linhas “Não” apostam sua chave privada na fonte de entropia no momento da assinatura, e as linhas “Sim”, não. Se isso importa é uma questão do seu ambiente — um servidor reforçado é uma aposta muito diferente de um dispositivo embarcado recém-ligado, de uma imagem de VM clonada ou de um celular de 2013. O guia rápido de interoperabilidade de bibliotecas mostra essa e as outras diferenças de comportamento padrão lado a lado.
O que fazer na prática
- Nunca forneça seu próprio
k. Se uma API de assinatura aceita um parâmetro de nonce, trate-o como um convite a dar um tiro no pé, e não como um recurso — e nunca recorra a ele só porque um teste precisava de bytes reproduzíveis. A assinatura determinística já oferece isso, com segurança. - Nunca escreva o gerador você mesmo. “Usamos o timestamp e o ID da requisição como semente” é exatamente como esses incidentes começam. Use o CSPRNG da plataforma ou uma biblioteca que não precise de um.
- Verifique qual estratégia de nonce você está realmente usando. Um teste de duas linhas diz quase tudo — mas leia o resultado com atenção, porque ele não distingue um nonce aleatório de um nonce hedged:
import { generateKeyPairSync, sign } from "node:crypto";
const { privateKey } = generateKeyPairSync("ec", { namedCurve: "P-256" });
const msg = Buffer.from("same message, twice");
const a = sign("sha256", msg, privateKey);
const b = sign("sha256", msg, privateKey);
console.log(a.equals(b));
// false in node:crypto -> random or hedged nonce
// true -> deterministic derivation (RFC 6979 or EdDSA-style)
Se os bytes forem diferentes, você não descobriu se o nonce é puramente aleatório ou hedged — só a documentação da biblioteca resolve isso. Se forem iguais, confirme que se trata da RFC 6979, e não de algo caseiro, comparando sua saída com os vetores de resposta conhecida do Apêndice A.2 da RFC 6979. Nosso gerador de vetores de teste ECDSA produz assinaturas RFC 6979 para P-256, P-384, P-521 e secp256k1 a partir de uma semente, para que os mesmos vetores possam ser regenerados byte a byte mais tarde.
- Desconfie dos momentos de baixa entropia. O primeiro boot em hardware embarcado, VMs e contêineres restaurados de um snapshot compartilhado e o início da inicialização, antes de o pool de entropia receber sua semente, são os momentos em que um nonce aleatório tem mais chance de decepcionar. A assinatura determinística elimina a questão.
- Se você encontrar reutilização, a chave está perdida. Não enfraquecida — perdida, retroativamente, para qualquer pessoa que já tenha visto aquelas duas assinaturas. Transfira os fundos, troque a chave em todos os lugares em que ela é usada e trate toda assinatura que ela já fez como atribuível a outra pessoa.
Fontes primárias
- RFC 6979 — Uso determinístico do DSA e do ECDSA. A derivação HMAC-DRBG, a justificativa e os vetores de resposta conhecida no Apêndice A; a ressalva sobre ataques por injeção de falhas está na §3.6.
- RFC 8032 — EdDSA (Ed25519, Ed448), em que a derivação determinística do nonce faz parte do esquema, e não é uma opção.
- FIPS 186-5 — o padrão de assinatura digital (DSS) vigente, que especifica o ECDSA e permite a geração determinística de nonces.
- Some SecureRandom Thoughts — Android Developers Blog, 14 de agosto de 2013: o defeito na inicialização do PRNG da plataforma, sua relação com o comprometimento da carteira de Bitcoin e a solução de contorno
PRNGFixes. - Biased Nonce Sense — Breitner e Heninger, IACR ePrint 2019/023 / Financial Cryptography 2019: ataques com reticulados contra nonces apenas enviesados, medidos em dados reais de blockchain e de varreduras de toda a internet.
- Console Hacking 2010 — 27C3, a demonstração pública da falha de nonce constante do PlayStation 3.
- BIP-62 e BIP-146 — canonicalização low-S (uma correção de maleabilidade, e não de nonce, mas o motivo pelo qual os valores recuperados podem precisar de correção de sinal), além da BIP-340 para o nonce determinístico obrigatório do Taproot.
- libsecp256k1 e @noble/curves — duas implementações amplamente auditadas que usam a RFC 6979 por padrão e cujo código-fonte é a documentação mais clara de como a derivação do nonce deve ser.
O artigo ECDSA vs RSA explica por que essa exigência de nonce é uma das poucas vantagens operacionais reais que o RSA ainda mantém e como os dois algoritmos se comparam nos demais aspectos.