Estoy tratando de ayudar a recuperar una billetera donde el propietario solo escribió 11 de las 12 palabras en la frase inicial. Inicialmente, pensé que la tarea sería rápida y bien definida, pero parece ser un poco más compleja de lo que supuse, y el material de referencia es bastante escaso. En caso de que alguien más tenga un problema similar al mío, quiero dejar esta publicación detallando los pasos que seguí (con ejemplos de código de trabajo).
La billetera con la que estoy tratando es Breadwallet, que aparentemente usa una estrategia de derivación diferente (más antigua) mnemotécnica a HD-master-private-key de la mayoría de las billeteras modernas. Por el momento, solo me enfocaré en recuperar frases parciales de Breadwallet, pero planeo expandir la respuesta eventualmente para cubrir estrategias de derivación más nuevas ( BIP44 ) también.
(El lenguaje utilizado en esta publicación es Python)
Breadwallet usa BIP39 para generar la semilla maestra de 128 bits a partir del mnemotécnico de 12 palabras. Luego, la semilla maestra se usa para generar un conjunto de billeteras/cuentas que contienen cadenas de direcciones, usando BIP32 .
En primer lugar, importe hashlib y binascii, los necesitaremos más adelante.
import hashlib
from binascii import hexlify, unhexlify
Supongamos que tiene 11 de las 12 palabras en su frase semilla. En aras de la simplicidad, usaré las primeras 11 palabras en la lista de palabras BIP39:
partial_seed_phrase = [ 'abandon', 'ability', 'able', 'about', 'above', 'absent', 'absorb', 'abstract', 'absurd', 'abuse', 'access' ]
La lista de palabras contiene 2048 entradas, lo que da a cada palabra 11 bits de entropía (2 11 = 2048). Las 12 palabras tienen 12*11 = 132 bits de entropía en total. La semilla maestra HD tiene una longitud de 128 bits y hay una suma de verificación de 4 bits adjunta al final, lo que eleva el total de bits a 132. Hasta ahora, todo bien.
Si asumimos que wordlist
es una lista de 2048 elementos (omitida por limitaciones de espacio), podemos encontrar el índice (en decimal) de los elementos en partial_seed_phrase
:
mnemonic_in_decimal = map(wordlist.index, partial_seed_phrase)
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Convirtámoslo mnemonic_in_decimal
en una matriz de números binarios de 11 bits de ancho.
mnemonic_in_binary = map('{0:011b}'.format, mnemonic_in_decimal)
# ['00000000000', '00000000001', '00000000010', '00000000011', '00000000100', '00000000101', '00000000110', '00000000111', '00000001000', '00000001001', '00000001010']
Sabemos que falta una sola palabra (11 bits) en algún lugar desconocido de esta matriz. En circunstancias no ideales, tendríamos que comprobar cada una de las 12 ubicaciones en busca de la palabra que falta contra 2048 palabras posibles cada una, para un total de 24576 (12*2048 = 24576) semillas maestras potenciales.
for missing_word_position in range(0,12):
# The missing word belongs at some index from 0-11 in the final 12-word phrase
for wordlist_index in range(0, 2048):
# Iterate over all possibilities for the missing word
missing_word_binary = '{0:011b}'.format(wordlist_index)
front_half = ''.join(mnemonic_in_binary[0:missing_word_position])
back_half = ''.join(mnemonic_in_binary[missing_word_position:12])
seed_and_checksum = front_half + missing_word_binary + back_half
seed = seed_and_checksum[0:128]
checksum = seed_and_checksum[-4:]
Afortunadamente, tenemos una suma de verificación de 4 bits, lo que significa que solo una de cada 16 semillas (2 4 = 16) será válida. Esto significa que terminaremos con un total final de aproximadamente 1536 semillas maestras (24576/16 = 1536) para verificar los fondos. La suma de comprobación se deriva de los primeros bits (en este caso, 4) devueltos al aplicar la función hash SHA-256 a la semilla, por lo que el número final de semillas maestras válidas puede variar, pero promediará alrededor de 1/16 de la total de semillas posibles.
[Más adelante, si alguien quiere ayudar a escribir descripciones o código para cualquiera de los siguientes pasos, ¡se lo agradecería! ]
Que hacer:
extremo este-aa
VidaInformación
rny