
Étude pédagogique de l'entropie des clés Bitcoin : énumération de portefeuilles à RNG faible (classe Milk Sad / CVE-2023-39910) vs l'impossibilité de forcer une bonne clé par force brute.
| Expérience | Espace de recherche | Faisable ? | Ce qu'elle enseigne |
|---|
weak_entropy — énumère un espace de portefeuilles à RNG faible | 2³² (un mauvais RNG) | Oui, en quelques jours | Un portefeuille est aussi solide que l'entropie qui le sous-tend. |
blind_search — force par force brute un bon espace de clés | 2¹⁶⁰ (un bon RNG) | Non, jamais | Avec une entropie réelle, l'espace de clés est physiquement impossible à explorer. |
Tout l'intérêt réside dans le contraste : les clés sont volées en attaquant un mauvais aléa, jamais en surpassant par le calcul un bon aléa. Les deux expériences dérivent le même type d'adresses réelles sur le mainnet, la seule variable étant donc l'entropie.
Ce dépôt est destiné à la formation défensive et à la recherche en sécurité : comprendre pourquoi les bugs de portefeuille à RNG faible (Milk Sad / CVE-2023-39910, le bug de 2018 de l'extension Trust Wallet, et leurs semblables) ont été catastrophiques, et pourquoi forcer par force brute une clé correctement générée ne l'est pas.
weak_entropy énumère un espace synthétique autonome. Sa
weak_entropy() est un modèle délibéré de la classe de vulnérabilité — un PRNG 32 bits
alimentant BIP39 — pas un clone octet pour octet du RNG d'un portefeuille réel. Les
adresses qu'elle produit ne correspondent à aucun portefeuille réel.blind_search est une consultation de solde en lecture seule sur votre propre nœud. Elle
ne dérive aucune clé et ne peut rien dépenser ; son mode principal mesure la futilité
de la recherche aveugle.Utilisez-le sur des portefeuilles que vous contrôlez, sur testnet, ou comme bac à sable. Ne le dirigez pas vers l'argent des autres.
Uniquement pour l'éducation et la recherche en sécurité autorisée. Utilisez-le uniquement sur des clés, portefeuilles et systèmes que vous possédez ou pour lesquels vous avez l'autorisation écrite explicite de tester. L'accès non autorisé aux portefeuilles ou systèmes d'autrui, ainsi que le vol d'actifs numériques, sont illégaux en vertu de lois telles que le CFAA des États-Unis (18 U.S.C. § 1030), le Computer Misuse Act 1990 du Royaume-Uni et la directive UE 2013/40/EU — quels que soient les moyens techniques. Fourni « en l'état », sans garantie ; les auteurs n'assument aucune responsabilité en cas d'usage abusif. Ceci n'est pas un conseil juridique. Conditions complètes et recommandations de divulgation responsable : DISCLAIMER.md.
La sécurité d'un portefeuille est bornée par l'entropie qui a alimenté sa graine, et non par la robustesse de SHA-256 / PBKDF2 / secp256k1 en aval.
BIP39 transforme l'entropie en phrase mnémonique, PBKDF2 l'étire en une graine, et BIP32 dérive une arborescence de clés. Tout cela est robuste. Mais aucune de ces étapes n'ajoute d'entropie — si la graine a été tirée d'un petit ensemble, chaque adresse que le portefeuille peut produire est énumérable, et nul hachage ne le masque.
bitcoin-security/
├── bitcoin_security/ # the package
│ ├── derivation.py # shared core: BIP39 + BIP32 + P2PKH/WIF
│ ├── data/english.txt # BIP39 English wordlist (2048 words)
│ ├── weak_entropy/ # Experiment 1 — enumerable weak space
│ │ ├── enumerate.py # recovery demo + full-space dump
│ │ └── README.md
│ └── blind_search/ # Experiment 2 — futile brute-force search
│ ├── scan.py # UTXO-set balance scanner + odds
│ └── README.md
├── tests/ # offline test suites (no node/network)
│ ├── test_blind_search_addresses.py
│ └── test_blind_search_pipeline.py
├── docs/FINDINGS.md # benchmarks + exact full-space size derivation
├── pyproject.toml
├── requirements.txt
└── LICENSE
L'arborescence reflète l'idée : un noyau derivation partagé, deux expériences qui
l'utilisent pour aboutir à des conclusions opposées sur le même espace d'adresses.
pip install -r requirements.txt # runtime deps
# optional — installs the package + `bsec-enumerate` / `bsec-scan` commands:
pip install -e .
coincurve (libsecp256k1 native) rend la dérivation ~5× plus rapide mais reste optionnel —
derivation.py retombe sur ecdsa en pur Python avec une sortie identique. Rien
ici ne touche au réseau, à l'exception du drapeau --check-balance activable explicitement et de
blind_search (qui nécessite un nœud Bitcoin local).
Tout s'exécute comme un module depuis la racine du dépôt ; aucune installation requise :
python3 -m bitcoin_security.weak_entropy --help
python3 -m bitcoin_security.blind_search --help
weak_entropy : l'entropie faible est énumérableLes 16 octets « aléatoires » d'entropie BIP39 du portefeuille proviennent en réalité d'une graine de 32 bits. La dérivation complète est réelle et vérifiée par rapport aux vecteurs de spécification.
seed32 ──MT19937──▶ 16-byte entropy ──BIP39──▶ mnemonic ──PBKDF2(2048)──▶ 64-byte seed
│
address ◀──hash160──── pubkey ◀──BIP32 m/44'/0'/0'/0/0
# Recovery: derive a victim wallet from a weak seed, then recover its full
# mnemonic + private key knowing only the public address.
python3 -m bitcoin_security.weak_entropy
# Expand one seed into every form.
python3 -m bitcoin_security.weak_entropy --show 3571
# Materialize the whole 2**32 space to sharded, resumable files.
python3 -m bitcoin_security.weak_entropy --dump-all --out-dir space_dump --max-file-mb 1000
Les lignes sont minimales (address <TAB> seed32) car toute autre forme est une
fonction déterministe de seed32, régénérée à la demande avec --show. Les dumps
sont reprenables à la granularité du fragment (vérifié pour reproduire une couverture
contiguë, sans trou ni doublon). Référence complète des drapeaux dans
weak_entropy/README.md.
Avertissement sur l'échelle. Le dump complet 2³² fait ~196 Go (minimal) ou ~1,05 To (
--full) et prend des jours même avec le backend natif — voirdocs/FINDINGS.md. Vous n'avez presque jamais besoin de tout ; dump une tranche bornée pour inspection.
blind_search : impossible de forcer par force brute une bonne cléUn véritable scanner de solde fonctionnel sur l'ensemble UTXO d'un nœud local (élagage OK) via
scantxoutset. Son but est le résultat nul : chercher à l'aveugle dans l'espace
d'adresses, constater que vous ne trouvez rien, et afficher le calcul montrant que vous n'en
trouverez jamais.
python3 -m bitcoin_security.blind_search --odds-only # just the math, no node
python3 -m bitcoin_security.blind_search --random 200000 --odds # search 200k random addrs, then the math
python3 -m bitcoin_security.blind_search --control # verify the pipeline finds known-funded addrs
Le résultat : l'espace est de 2¹⁶⁰ ≈ 1,46×10⁴⁸ ; ~55 M d'adresses ont un solde ; une
adresse aléatoire est donc financée avec une probabilité d'environ 3,8×10⁻⁴¹. Même à un milliard
d'adresses/seconde, il faudrait attendre ~8×10²³ années — environ 61 000 milliards de fois l'âge
de l'univers. Ce n'est pas un problème de matériel : d'après la limite de Landauer, le simple
comptage jusqu'à 2¹⁶⁰ dépasse l'énergie restante du soleil. Configuration du nœud dans
blind_search/README.md.
Les deux expériences dérivent le même type d'adresse. La seule différence est la taille de l'ensemble dans lequel la graine a été tirée :
Cet écart — 128 bits — est tout l'enjeu. C'est dans cet écart que se sont produits tous les vols réels de cette classe.
python3 tests/test_blind_search_addresses.py # BIP-173/350/380 address vectors
python3 tests/test_blind_search_pipeline.py # scan pipeline, RPC stubbed
python3 -m bitcoin_security.derivation # derivation self-test (spec vectors)
Détails complets, méthodes et spécifications machine dans docs/FINDINGS.md.
ecdsa en pur Python par coincurve (libsecp256k1)
a réduit la dérivation par portefeuille de 3,02 ms → 0,591 ms (~5,1×) sur un Apple M3
Max ; le plancher restant est PBKDF2-HMAC-SHA512 ×2048 à 0,463 ms/portefeuille.--full, à partir
d'une forme fermée exacte pour les chiffres seed32 plus une longueur moyenne d'adresse mesurée.bx seed (MT19937 32 bits
initialisé à partir de l'heure). De vrais fonds ont été volés. https://milksad.info/MIT — voir LICENSE. Conditions d'utilisation et recommandations de divulgation responsable : DISCLAIMER.md. Signalement : SECURITY.md.