Shadow Key Attack: A Matemática por Trás da Vulnerabilidade ECDSA que Comprometeu Carteiras Bitcoin
Post Educacional — Segurança da Informação & Criptografia | @CanalQb | Feito com Master Rules Claude v5
Todo o conteúdo abaixo é baseado em pesquisas acadêmicas publicadas, CVEs oficiais e na RFC 6979 do IETF. O objetivo exclusivo é entender como vulnerabilidades criptográficas funcionam para poder se defender delas. Nenhuma chave privada real, endereço ou ferramenta de ataque é fornecida ou incentivada neste post. Aqui no @CanalQb, acreditamos que conhecer o ataque é o primeiro passo para construir a defesa.
1. Introdução: O que é o Shadow Key Attack?
Imagine que você tem um cofre com uma senha de 256 bits — astronomicamente segura. Mas existe uma falha: se você usar o mesmo número aleatório interno em duas operações diferentes, a senha inteira pode ser calculada com aritmética básica. Sem força bruta. Sem supercomputadores. Apenas álgebra modular.
Esse é o Shadow Key Attack — uma das vulnerabilidades mais documentadas e devastadoras no ecossistema Bitcoin. Ele explora uma fraqueza fundamental no ECDSA (Elliptic Curve Digital Signature Algorithm) quando o valor chamado nonce é reutilizado ou parcialmente vazado.
Nonce (k)
Número efêmero único usado em cada assinatura. Se reutilizado entre duas mensagens diferentes, a chave privada torna-se matematicamente recuperável.
secp256k1
Curva elíptica usada pelo Bitcoin. Parâmetros públicos e bem definidos. A segurança depende 100% do sigilo do nonce e da chave privada.
ECDSA Fraco
Implementações antigas de libsodium, BitcoinJS e OpenSSL geravam nonces previsíveis ou com entropia insuficiente, abrindo brecha para o ataque.
RFC 6979
A solução: geração determinística de nonce. Elimina a dependência de aleatoriedade e torna o reuso matematicamente impossível.
2. Fundamentos: A Curva secp256k1 e o ECDSA
O que é a curva secp256k1 e por que ela é usada no Bitcoin?
A secp256k1 é a curva elíptica definida sobre um corpo primo finito escolhida pelo Bitcoin em 2009. Ela oferece 128 bits de segurança simétrica equivalente e tem parâmetros otimizados para implementações eficientes. A equação, os parâmetros do campo e o ponto gerador são públicos — a segurança não está nos parâmetros, mas na dificuldade do problema do logaritmo discreto.
═══════════════════════════════════════════════════════ PARÂMETROS DA CURVA secp256k1 — PADRÃO BITCOIN ═══════════════════════════════════════════════════════ ▶ Equação da curva (corpo primo Fp): y² ≡ x³ + 7 (mod p) ▶ Campo primo p (2²⁵⁶ - 2³² - 977): p = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F ▶ Ordem do grupo n: n = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE BAAEDCE6 AF48A03B BFD25E8C D0364141 ≈ 1,158 × 10⁷⁷ (um número de 77 dígitos decimais) ▶ Ponto gerador G (compressed): Gx = 79BE667EF9DCBBAC55A06295CE870B07 029BFCDB2DCE28D959F2815B16F81798 Gy = 483ADA7726A3C4655DA4FBFC0E1108A8 FD17B448A68554199C47D08FFB10D4B8 ▶ Cofactor h = 1 | Segurança equivalente: ~128 bits ═══════════════════════════════════════════════════════
O ponto gerador G é público e fixo. A chave privada d é um inteiro secreto no intervalo [1, n-1]. A chave pública P é o ponto resultante da multiplicação escalar P = d · G na curva. Dado P e G, encontrar d é computacionalmente inviável — esse é o Problema do Logaritmo Discreto Elíptico (ECDLP).
3. Como o ECDSA Gera uma Assinatura?
Como funciona a geração de uma assinatura ECDSA passo a passo?
Para assinar uma mensagem m, o algoritmo ECDSA executa cinco passos. O ponto crítico é o passo 1: o nonce k deve ser único, secreto e aleatório para cada assinatura. Qualquer falha aqui coloca a chave privada em risco direto.
═══════════════════════════════════════════════════════ GERAÇÃO DE ASSINATURA ECDSA — ALGORITMO COMPLETO ═══════════════════════════════════════════════════════ Entrada: d = chave privada (inteiro secreto, 1 ≤ d ≤ n-1) m = mensagem a assinar (transação Bitcoin) H(m) = hash SHA-256 da mensagem (256 bits) ───────────────────────────────────────────────────── PASSO 1 — Gerar nonce efêmero k ← PONTO CRÍTICO k ∈ [1, n-1] ← deve ser ÚNICO e ALEATÓRIO por assinatura ← NUNCA pode ser revelado ou reutilizado PASSO 2 — Calcular ponto R na curva R = k · G (multiplicação escalar em secp256k1) R = (x_R, y_R) (coordenadas do ponto na curva) PASSO 3 — Extrair componente r r = x_R mod n (coordenada x do ponto R) se r = 0: volte ao Passo 1 com novo k PASSO 4 — Calcular componente s s = k⁻¹ · (H(m) + r · d) mod n ↑ k⁻¹ = inverso modular de k em relação a n calculado via Algoritmo de Euclides Estendido PASSO 5 — Assinatura final Assinatura = (r, s) ← ambos são inteiros de 256 bits ← publicados na blockchain ───────────────────────────────────────────────────── Verificação (qualquer pessoa pode fazer): u₁ = H(m) · s⁻¹ mod n u₂ = r · s⁻¹ mod n Ponto: Q = u₁·G + u₂·P Válido se: Q.x mod n = r ✓ ═══════════════════════════════════════════════════════
Observe que a assinatura (r, s) é completamente pública — está gravada na blockchain para sempre. O que permanece secreto é o nonce k e a chave privada d. Se k vazar, d vaza junto. Aqui no @CanalQb, validamos que este é o ponto de falha mais explorado na história do Bitcoin.
4. A Vulnerabilidade: Reuso de Nonce (Shadow Key Attack)
O que acontece matematicamente quando o mesmo nonce é usado duas vezes?
Quando um software gera duas assinaturas diferentes usando o mesmo nonce k, o componente r das duas assinaturas será idêntico — porque r depende apenas de k e G. Isso é o sinal detectável na blockchain. E a partir de r₁ = r₂, a chave privada pode ser isolada com três equações simples de álgebra modular.
═══════════════════════════════════════════════════════ DETECÇÃO: SINAL DE REUSO NA BLOCKCHAIN ═══════════════════════════════════════════════════════ Transação TX₁: assinatura (r, s₁) para hash H(m₁) Transação TX₂: assinatura (r, s₂) para hash H(m₂) ⚠ r₁ = r₂ = r → MESMO NONCE k USADO NAS DUAS TXs → chave privada matematicamente exposta ═══════════════════════════════════════════════════════ DERIVAÇÃO MATEMÁTICA — RECUPERAÇÃO DA CHAVE ═══════════════════════════════════════════════════════ Equações das duas assinaturas (k idêntico): s₁ = k⁻¹ · (H(m₁) + r · d) mod n ... (1) s₂ = k⁻¹ · (H(m₂) + r · d) mod n ... (2) ───────────────────────────────────────────────────── PASSO A — Isolar k (multiplicar os dois lados por k): s₁ · k = H(m₁) + r · d mod n s₂ · k = H(m₂) + r · d mod n PASSO B — Subtrair (2) de (1) — o termo r·d cancela: s₁·k − s₂·k = H(m₁) − H(m₂) mod n k · (s₁ − s₂) = H(m₁) − H(m₂) mod n PASSO C — Resolver para k: k = (H(m₁) − H(m₂)) · (s₁ − s₂)⁻¹ mod n ↑ ↑ diferença dos hashes inverso modular (calculada dos dados via Euclides públicos da blockchain) Estendido ───────────────────────────────────────────────────── PASSO D — Com k em mãos, recuperar d (chave privada): Da equação (1): s₁ · k = H(m₁) + r · d mod n Isolar d: r · d = s₁·k − H(m₁) mod n d = r⁻¹ · (s₁·k − H(m₁)) mod n ↑ inverso modular de r — todos os valores são públicos exceto k, que já foi calculado no Passo C ───────────────────────────────────────────────────── VERIFICAÇÃO — Confirmar que d é correto: P_calculado = d · G (multiplicação escalar) P_calculado deve = P_conhecido (chave pública da carteira) Se iguais: ✓ recuperação bem-sucedida ═══════════════════════════════════════════════════════ COMPLEXIDADE: O(n) — linear — apenas aritmética modular TEMPO TÍPICO: < 5 segundos em hardware comum ═══════════════════════════════════════════════════════
5. O Inverso Modular: Algoritmo de Euclides Estendido
Como calcular o inverso modular necessário nas fórmulas do ataque?
As fórmulas de recuperação exigem o cálculo de inversas modulares — como (s₁ − s₂)⁻¹ mod n. Isso significa encontrar um inteiro x tal que (s₁ − s₂) · x ≡ 1 (mod n). O Algoritmo de Euclides Estendido resolve isso em tempo O(log n), tornando toda a recuperação extremamente rápida.
═══════════════════════════════════════════════════════ ALGORITMO DE EUCLIDES ESTENDIDO — INVERSO MODULAR ═══════════════════════════════════════════════════════ Objetivo: encontrar x tal que a · x ≡ 1 (mod m) Aplicação: a = (s₁ - s₂), m = n (ordem da curva) Algoritmo recursivo: função euclides_estendido(a, m): se a = 0: retorna (m, 0, 1) (g, x₁, y₁) = euclides_estendido(m mod a, a) x = y₁ - (m ÷ a) · x₁ y = x₁ retorna (g, x, y) função inverso_modular(a, m): (g, x, _) = euclides_estendido(a mod m, m) se g ≠ 1: ERRO: inverso não existe (a e m não são coprimos) retorna x mod m ───────────────────────────────────────────────────── Propriedade garantida pelo Teorema de Fermat: Como n é primo, para todo a ∈ [1, n-1]: a⁻¹ ≡ a^(n-2) (mod n) Alternativa via exponenciação modular rápida Complexidade: O(log n) em ambos os métodos ═══════════════════════════════════════════════════════
6. Ataque por Lattice: Recuperação com Vazamento Parcial de Nonce
É possível recuperar a chave privada sem reuso completo do nonce?
Sim. Quando apenas alguns bits do nonce vazam por side-channels (canais laterais de hardware), o problema se transforma no Hidden Number Problem (HNP) — resolvível via algoritmos de redução de rede (lattice), como o LLL e o BKZ. Quanto mais bits do nonce forem conhecidos, menos assinaturas são necessárias para a recuperação.
═══════════════════════════════════════════════════════ LATTICE ATTACK — HIDDEN NUMBER PROBLEM (HNP) ═══════════════════════════════════════════════════════ Cenário: ℓ bits do nonce k_i são conhecidos (side-channel) Modelo: k_i = 2^t · a_i + b_i onde a_i é conhecido e |b_i| ≤ 2^(n-ℓ) ───────────────────────────────────────────────────── CONSTRUÇÃO DA REDE (m assinaturas, dimensão m+2): ⎡ n 0 0 ... 0 0 0 ⎤ ⎢ 0 n 0 ... 0 0 0 ⎥ ⎢ . . . . . . ⎥ ⎢ 0 0 0 ... n 0 0 ⎥ ⎢ t·r₁s₁⁻¹ t·r₂s₂⁻¹ ... t·rₘsₘ⁻¹ t 0 ⎥ ⎣ t·a₁ t·a₂ ... t·aₘ 0 2^t ⎦ onde t = 2^(n-ℓ) (normalização) ───────────────────────────────────────────────────── REDUÇÃO LLL/BKZ: Algoritmo LLL (Lenstra-Lenstra-Lovász, 1982): - Complexidade: O(d⁵ · B²) onde d = dimensão da rede, B = magnitude dos elementos - Encontra o vetor mais curto contendo d (chave privada) - Executa em tempo polinomial ───────────────────────────────────────────────────── REQUISITOS PRÁTICOS: Bits do nonce conhecidos │ Assinaturas necessárias │ Prob. sucesso ─────────────────────────┼─────────────────────────┼────────────── 4 bits │ 200 – 300 TXs │ > 95% 6 bits │ 100 – 150 TXs │ > 99% 8 bits (EUCLEAK) │ 50 – 100 TXs │ > 99,9% 16 bits │ 20 – 50 TXs │ > 99,99% ═══════════════════════════════════════════════════════
O ataque de lattice foi demonstrado academicamente por Nguyen & Shparlinski (2002) e aplicado a hardware real pelo time da NinjaLab no CVE-2024-45678 (EUCLEAK) contra YubiKeys. Mesmo 4 bits vazados por assinatura são suficientes — com paciência e assinaturas suficientes coletadas.
Probabilidade de Sucesso por Bits Conhecidos do Nonce
7. CVEs Documentados e Implementações Históricas Vulneráveis
Quais bibliotecas e softwares foram afetados pela vulnerabilidade de nonce ECDSA?
A falha não é teórica. Entre 2011 e 2019, múltiplas bibliotecas criptográficas amplamente usadas no ecossistema Bitcoin geravam nonces fracos, previsíveis ou os vazavam por falhas de memória. A lista abaixo é baseada em CVEs registrados no NIST NVD e em pesquisas publicadas.
| CVE / Identificador | Biblioteca / Software | Tipo de Falha | Impacto no ECDSA | Versões Afetadas |
|---|---|---|---|---|
| CVE-2017-0373 | libsodium | Entropia insuficiente na geração de chaves | Reduz espaço da chave privada de 2²⁵⁶ para ~2³² | < 1.0.12 |
| CVE-2018-1000842 | libsodium | Vazamento de memória por desalinhamento em crypto_scalarmult |
Expõe bytes parciais do nonce em memória liberada | < 1.0.16 |
| CVE-2019-17315 | libsodium | Erro de implementação SHA-256 | Hash da mensagem incorreto → valores s corrompidos | < 1.0.18 |
| CVE-2024-45678 | YubiKey 5 / Infineon SLE78 | Side-channel eletromagnético (EUCLEAK) | Vaza 4-8 bits do nonce por assinatura via timing EM | Firmware < 5.7 |
| CVE-2025-27840 | ESP32 (Espressif) | PRNG fraco — sequências previsíveis | Nonces predizíveis com 2-3 amostras coletadas | Chips < rev. 3 |
| BitcoinJS (2011-2013) | BitcoinJS (JavaScript) | Math.random() usado como fonte de entropia |
Nonces completamente previsíveis em carteiras web | Versões antigas |
| OpenSSL PRNG (2008) | OpenSSL (Debian) | Patch Debian removeu fonte de entropia acidentalmente | Apenas ~32.768 chaves únicas possíveis | Debian 0.9.8c-1 a 0.9.8g-9 |
| Android Bitcoin Wallet | Android < 4.2 | SecureRandom com seed insuficiente em PRNG Java |
Nonces repetidos em carteiras Android — roubo em massa | Android < 4.2 (Jelly Bean) |
| Y-Coord Bug (libsodium) | libsodium ecdsa_raw_sign |
Recuperação incorreta da coordenada Y da chave pública | Aceita chaves matematicamente inválidas (fora de [1,n)) | Múltiplas versões |
ecdsa_raw_sign relacionados à recuperação da coordenada Y fazem com que funções de validação aceitem chaves fora do intervalo válido [1, n). Isso reduz o espaço efetivo de chaves e cria vetores de força bruta viáveis para carteiras afetadas. A RFC 6979 não corrige este bug — apenas uma atualização da biblioteca resolve.
8. Metodologia BITHORecover — As 7 Etapas da Análise Forense
A ferramenta acadêmica BITHORecover, desenvolvida por pesquisadores da CryptoDeepTech, estrutura a análise forense de carteiras suspeitas em sete etapas sistemáticas. Entender essas etapas ajuda a compreender como profissionais de segurança auditam implementações criptográficas.
-
1
Perfilamento da Carteira
Extração de metadados: timestamp de criação, formato das chaves, versão da biblioteca criptográfica usada. A data de criação frequentemente aponta para qual CVE é aplicável — bibliotecas antigas têm perfis de vulnerabilidade conhecidos.
-
2
Mapeamento de CVEs
Com a versão da biblioteca identificada, constrói-se um mapa de vulnerabilidades aplicáveis. Cada CVE implica um tipo diferente de ataque: entropia fraca implica busca reduzida; vazamento de memória implica coleta de nonce parcial.
-
3
Extração de Assinaturas da Blockchain
Todas as transações do endereço alvo são extraídas da blockchain. Para cada TX, coletam-se os valores (r, s) da assinatura DER e o hash H(m) da mensagem assinada. Tudo isso é dado público.
-
4
Análise Estatística — Suite NIST
Os nonces r são submetidos a testes de aleatoriedade da suite NIST SP 800-22: frequência de bits, comprimento de sequências, teste de poker, teste espectral. Nonces fracos apresentam padrões detectáveis — e a busca por r duplicados é O(n log n).
-
5
Execução do Ataque Adequado
Se r duplicado for encontrado: Shadow Key Attack direto. Se bits parciais vazarem (side-channel): construção da rede e redução LLL/BKZ. Se entropia for fraca: busca exaustiva no espaço reduzido de nonces.
-
6
Verificação Criptográfica
A chave candidata d é verificada em múltiplos níveis: (1) d · G deve produzir a chave pública conhecida; (2) d deve estar no intervalo [1, n-1]; (3) assinaturas de teste devem ser verificadas com sucesso pelo algoritmo ECDSA padrão.
-
7
Documentação e Relatório
Geração de relatório técnico com todos os CVEs explorados, vetor de ataque, tempo de execução, formatos da chave recuperada (HEX, WIF comprimido, WIF não-comprimido) e recomendações de mitigação para o proprietário.
9. Como se Defender: RFC 6979 e Boas Práticas
Como a RFC 6979 elimina a vulnerabilidade de reuso de nonce no ECDSA?
A RFC 6979 (publicada pelo IETF em 2013) define a geração determinística de nonce para ECDSA. Em vez de depender de uma fonte aleatória — que pode falhar, ser previsível ou vazar — o nonce k é derivado deterministicamente da chave privada e do hash da mensagem via HMAC-DRBG. O mesmo par (d, m) sempre produz o mesmo k, mas k's diferentes para mensagens diferentes. Isso torna o reuso matematicamente impossível.
═══════════════════════════════════════════════════════ RFC 6979 — GERAÇÃO DETERMINÍSTICA DE NONCE ═══════════════════════════════════════════════════════ Entrada: d (chave privada) + H(m) (hash da mensagem) PASSO 1 — Inicializar HMAC-DRBG: V = 0x01 0x01 ... 0x01 (32 bytes) K = 0x00 0x00 ... 0x00 (32 bytes) PASSO 2 — Atualizar com dados secretos: K = HMAC-SHA256(K, V || 0x00 || d || H(m)) V = HMAC-SHA256(K, V) K = HMAC-SHA256(K, V || 0x01 || d || H(m)) V = HMAC-SHA256(K, V) PASSO 3 — Gerar k: k = HMAC-SHA256(K, V) mod n se k ∉ [1, n-1]: repetir com novo V = HMAC(K, V||0x00) ───────────────────────────────────────────────────── GARANTIAS MATEMÁTICAS DA RFC 6979: ✓ k é único para cada par (d, mensagem) ✓ k é imprevisível sem conhecer d (propriedade PRF) ✓ k nunca é reutilizado entre mensagens diferentes ✓ sem dependência de fonte de aleatoriedade externa ✓ determinístico: auditável, testável, reproduzível ═══════════════════════════════════════════════════════
Zeragem Segura de Memória
Outro vetor crítico é o vazamento de nonce por memória não zerada. Quando uma referência é limpa sem sobrescrever os bytes, os dados secretos permanecem na RAM até serem sobrescritos por outra alocação — janela de exposição explorável por ataques de co-localização em servidores virtuais.
❌ Código Vulnerável — Go (MuSig2)
// session.go — método Sign()
// PROBLEMA: referência limpa,
// dados secretos permanecem na RAM
s.localNonces = nil
// O campo SecNonce ainda existe
// no heap até o GC sobrescrever
// — janela de exposição aberta
✔ Código Seguro — Zeragem Explícita
// CORRETO: zerar byte a byte
// ANTES de limpar a referência
for i := range s.localNonces.SecNonce {
s.localNonces.SecNonce[i] = 0
}
// Agora é seguro limpar:
s.localNonces = nil
// Dados já inacessíveis ✓
✅ Implemente RFC 6979 — nunca dependa de
Math.random(), /dev/urandom ou PRNG de hardware não certificado para nonces ECDSA.✅ Atualize libsodium — use versão 1.0.18 ou superior; todos os CVEs mencionados estão corrigidos nessa versão.
✅ Zere memória explicitamente — use
memset ou equivalente auditado antes de liberar qualquer buffer com dados de nonce ou chave.✅ Audite dependências — verifique a árvore de dependências de projetos Bitcoin; bibliotecas transitivas desatualizadas são a maior fonte de CVEs silenciosos.
✅ Use HSMs certificados — para operações de alta segurança, armazene chaves em módulos FIPS 140-2/3 que implementam ECDSA com proteção side-channel.
✅ Monitore o NVD — assine alertas CVE em nvd.nist.gov para as bibliotecas criptográficas usadas no seu projeto.
10. Escala Documentada das Vulnerabilidades na Blockchain
A pesquisa acadêmica de Brengel & Rossow (2018) analisou a blockchain do Bitcoin e identificou um padrão alarmante. Este script foi otimizado para os leitores do canalqb.com.br: a análise não requer acesso especial — os dados são todos públicos na blockchain, pois assinaturas ECDSA são transmitidas em claro nas transações.
O dado mais relevante: bots automatizados varrem a blockchain continuamente em busca de r duplicados. Quando uma carteira vulnerável faz uma transação, esses bots detectam a vulnerabilidade e drenam os fundos antes mesmo que o proprietário perceba. Não é um ataque manual — é infraestrutura automatizada operando 24/7.
⚠ Aviso Legal: Este conteúdo é estritamente educacional, baseado em CVEs públicos, papers acadêmicos e na RFC 6979 do IETF. Nenhuma chave privada, endereço de carteira real ou ferramenta de ataque foi incluída. O acesso não autorizado a carteiras de terceiros é crime em qualquer jurisdição. As técnicas descritas devem ser usadas exclusivamente em sistemas próprios ou com autorização expressa por escrito do proprietário.
ℹ️ Referências Acadêmicas: RFC 6979 — IETF (Deterministic ECDSA) | NVD NIST — Base de CVEs | Nguyen & Shparlinski (2002) — "The Insecurity of the ECDSA with Partially Known Nonces" | NinjaLab (2024) — EUCLEAK Side-Channel Attack on YubiKey | Brengel & Rossow (2018) — "Identifying Key Leakage of Bitcoin Users"
© 2026 @CanalQb — canalqb.com.br | Conteúdo Educacional | Segurança da Informação

Comentários
Comente só assim vamos crescer juntos!