Electrum 2FA Wallet – Ableitung des dritten XPub

Ich arbeite an einem Projekt, bei dem ich eine Electrum-Mnemonik nehmen, ihren Typ bestimmen und die entsprechenden Adressen für diese Brieftasche ableiten muss.

Ich habe viele Informationen darüber gefunden, wie man den mnemonischen Typ bestimmt und in den Master-Seed versteckt, aber ich habe Schwierigkeiten zu verstehen, wie Adressen für die 2FA-Brieftasche abgeleitet werden. Genauer gesagt konnte ich beide erweiterten Schlüsselpaare ( x1/und x2/in der Wallet-Datei) ableiten, aber ich kann anscheinend nicht in der Lage sein, das dritte xpub ( x3/in der Wallet-Datei) abzuleiten.

Ich habe den Quellcode durchgesehen und es scheint, dass dieser Schlüssel abgeleitet wird, indem beide xpub-Schlüssel kombiniert werden (die Schlüssel sollten ORDERED sein ), die aus dem Master-Seed ( x1/und x2/) abgeleitet werden können, indem sie gehasht SHA-256und beim Ableiten als Index verwendet werden der dritte xpub. Als Root wird ein fest codierter xpub verwendet.

Ich habe Probleme, dies zu verstehen / zu implementieren, da der SHA-256Algorithmus 32 Byte Daten zurückgibt und der untergeordnete Index 4 Byte lang sein muss (gemäß der BIP32Spezifikation).

Hier ist der Code von Electrum, der dies handhabt.

def get_user_id(storage):
    def make_long_id(xpub_hot, xpub_cold):
        return bitcoin.sha256(''.join(sorted([xpub_hot, xpub_cold])))
    xpub1 = storage.get('x1/')['xpub']
    xpub2 = storage.get('x2/')['xpub']
    long_id = make_long_id(xpub1, xpub2)
    short_id = hashlib.sha256(long_id).hexdigest()
    return long_id, short_id

def make_xpub(xpub, s):
    version, _, _, _, c, cK = deserialize_xpub(xpub)
    cK2, c2 = bitcoin._CKD_pub(cK, c, s)
    return bitcoin.serialize_xpub(version, c2, cK2)

Die Funktionen werden dann wie folgt aufgerufen:

xpub1 = wizard.storage.get('x1/')['xpub']
xpub2 = wizard.storage.get('x2/')['xpub']
# Generate third key deterministically.
long_user_id, short_id = get_user_id(wizard.storage)
xpub3 = make_xpub(signing_xpub, long_user_id)

Das long_user_idist 32 Byte lang, da es das Ergebnis der SHA-256Funktion ist und dann verwendet wird, um die make_xpub()Funktion aufzurufen, die 4 Byte Daten als untergeordneten Index aufnehmen soll.

Was fehlt mir hier? Jede Eingabe wird geschätzt.

Antworten (1)

Nachdem ich mich etwas weiter mit dem Quellcode für das Electrum Wallet befasst hatte, konnte ich eine funktionierende Implementierung in JavaScript finden.

Das Problem, das ich in meiner Frage beschrieben habe, war, dass long_user_ides sich um 32 Bytes handelt, während Sie nur 4 Bytes benötigen, um einen untergeordneten Schlüssel abzuleiten. Es stellt sich heraus, dass der dritte xpub eigentlich kein Kind des fest codierten signierenden xpub ist, sondern eher ein Stammschlüssel, der von diesem signierenden Schlüssel abgeleitet ist (dh er hat einen völlig anderen Kettencode und sein Index ist 0).

Hier ist die Funktion, die ich geschrieben habe, die die beiden Wallet-XPUBs (zu finden unter x1/und x2/in der Wallet-/Keystore-Datei) und den fest codierten XPUB von Trustedcoin nimmt und den dritten XPUB ableitet.

import HDKey from 'hdkey';
import crypto from 'crypto';
import secp256k1 from 'secp256k1';

function getCosignerXPub(xpub_x1, xpub_x2){
    const signing_xpub = "xpub661MyMwAqRbcGnMkaTx2594P9EDuiEqMq25PM2aeG6UmwzaohgA6uDmNsvSUV8ubqwA3Wpste1hg69XHgjUuCD5HLcEp2QPzyV1HMrPppsL";

    const xpubs = [xpub_x1, xpub_x2].sort().join("");
    const long_user_id = new Buffer(sha256(xpubs), 'hex');

    const hdkey = HDKey.fromExtendedKey(signing_xpub);

    const data = Buffer.concat([hdkey.publicKey, long_user_id]);

    const I = crypto.createHmac('sha512', hdkey.chainCode).update(data).digest();

    const IL = I.slice(0, 32);
    const IR = I.slice(32);

    const hd = new HDKey(hdkey.versions);

    hd.publicKey = secp256k1.publicKeyTweakAdd(hdkey.publicKey, IL, true);
    hd.chainCode = IR;

    return hd.publicExtendedKey;
}