3rdIteration / btcrecover

BTCRecover is an open source wallet password and seed recovery tool. For seed based recovery, this is primarily useful in situations where you have lost/forgotten parts of your mnemonic, or have made an error transcribing it. (So you are either seeing an empty wallet or gettign an error that your seed is invalid) For wallet password or passphrase recovery, it is primarily useful if you have a reasonable idea about what your password might be.
https://btcrecover.readthedocs.io/
GNU General Public License v2.0
584 stars 181 forks source link

Dogechain.info wallet format has recently changed, support is now broken. #317

Open 3rdIteration opened 2 years ago

3rdIteration commented 2 years ago

These wallets are also incorrectly detected as a blockchain.com wallet, either way it's broken.

Sample of updated wallet files is here: https://github.com/3rdIteration/btcrecover/blob/6ba24508d4a752bc06a1dd29d52c55c23f1f0795/btcrecover/test/test-wallets/dogechain.wallet.aes.json.2022-01

The correct password is the standard test password, "btcr-test-password".

jimtje commented 2 years ago

FWIW here is the decryptor code in js:

function getSHA256(t) {
    var md = forge.md.sha256.create();
    md.update(t);
    return md.digest().toHex();
}

function decryptWallet(payload, password, salt, iterations, aes_cipher) {
    // decrypts the wallet
    // payload, salt are in Base64
    // payload contains the IV (first 16 or 12 bytes)

    var encryption_key = forge.util.bytesToHex(forge.pkcs5.pbkdf2(forge.util.encode64(forge.util.hexToBytes(getSHA256(password))), forge.util.decode64(salt), iterations, 32, "sha256"));

    var iv_hex = null;
    var payload_hex = null;
    var aes_auth_tag = null;

    var payload_bytes = forge.util.decode64(payload);

    if (aes_cipher === "AES-CBC") {
    iv_hex = forge.util.bytesToHex(payload_bytes.slice(0,16));
    payload_hex = forge.util.bytesToHex(payload_bytes.slice(16));
    } else if (aes_cipher === "AES-GCM") {
    iv_hex = forge.util.bytesToHex(payload_bytes.slice(0,12));
    aes_auth_tag = forge.util.bytesToHex(payload_bytes.slice(12, 12+16));
    payload_hex = forge.util.bytesToHex(payload_bytes.slice(12+16));
    } else {
    throw("Unrecognized cipher");
    }

    // decrypt the wallet
    var decipher = forge.cipher.createDecipher(aes_cipher, forge.util.hexToBytes(encryption_key));

    if (aes_cipher === "AES-CBC") {
    decipher.start({iv: forge.util.hexToBytes(iv_hex)});
    } else if (aes_cipher === "AES-GCM") {
    decipher.start({iv: forge.util.hexToBytes(iv_hex), tag: forge.util.hexToBytes(aes_auth_tag), additionalData: forge.util.hexToBytes(""), tagLength: 128});
    } else {
    throw("Unrecognized cipher");
    }

    decipher.update(forge.util.createBuffer(forge.util.hexToBytes(payload_hex)));

    var output_data = null;

    if (decipher.finish()) {
    // decryption succeeded, but need to check output
    try {
        output_data = JSON.parse(decipher.output.data);
    } catch (err) {
        output_data = null;
    }
    }

    return output_data; // the decrypted wallet, or null if failed

}

function encryptWallet(payload, password, iterations, aes_cipher) {
    // encrypts the payload
    // payload is in JSON
    // prepends IV (16 bytes) to payload
    // returns Base64 iv+payload, and salt

    var salt_bytes = forge.random.getBytesSync(16);
    var key_bytes = forge.pkcs5.pbkdf2(forge.util.encode64(forge.util.hexToBytes(getSHA256(password))), salt_bytes, iterations, 32, "sha256");

    var iv_bytes = null;

    if (aes_cipher === "AES-CBC") {
    iv_bytes = forge.random.getBytesSync(16);
    } else if (aes_cipher === "AES-GCM") {
    iv_bytes = forge.random.getBytesSync(12);
    } else {
    throw("Unrecognized cipher");
    }

    var cipher = forge.cipher.createCipher(aes_cipher, key_bytes);

    if (aes_cipher === "AES-CBC") {
    cipher.start({iv: iv_bytes});
    } else if (aes_cipher === "AES-GCM") {
    cipher.start({iv: iv_bytes, additionalData: forge.util.hexToBytes(""), tagLength: 128});
    } else {
    throw("Unrecognized cipher");
    }

    cipher.update(forge.util.createBuffer(JSON.stringify(payload)));
    cipher.finish();

    var encrypted_payload = null;

    if (aes_cipher === "AES-CBC") {
    encrypted_payload = iv_bytes + cipher.output.data;
    } else if (aes_cipher === "AES-GCM") {
    encrypted_payload = iv_bytes + cipher.mode.tag.data + cipher.output.data;
    } else {
    throw("Unrecognized cipher");
    }

    var response = {
    salt: forge.util.encode64(salt_bytes),
    payload: forge.util.encode64(encrypted_payload),
    cipher: aes_cipher
    };

    var decrypted_payload = decryptWallet(response.payload, password, response.salt, iterations, aes_cipher);

    if (decrypted_payload === null || JSON.stringify(decrypted_payload) !== JSON.stringify(payload)) {
    throw("Unable to decrypt encrypted payload.");
    }

    return response;

}

function testBrowserEncryption(){
    var result = false;
    try {
    encryptWallet({a:1}, forge.util.bytesToHex(forge.random.getBytesSync(12)), 1000, "AES-CBC");
    encryptWallet({a:1}, forge.util.bytesToHex(forge.random.getBytesSync(12)), 1000, "AES-GCM");
    result = true; // if we got here, we're fine
    } catch (err) {
    result = false;
    }

    try {
    var s_special = "`1234567890-=~!@#$%^&*()_+abcdпароль";
    var s_normal = "`1234567890-=~!@#$%^&*()_+abcdefghABCDEFG";

    result = (result &&
          (s_special !== forge.util.encodeUtf8(s_special)) &&
          (s_normal === forge.util.encodeUtf8(s_normal)) &&
          (getSHA256(s_special) === "33bc2af5524c1b8512b4d2b06d8f7b5efa30aca50f4f9801a3f0ba01240964e6") &&
          (getSHA256(forge.util.encodeUtf8(s_special)) === "4b21f9780a8ce6fec928c0754b0e55ddb7298308a72f21a8f0120019819c6644") &&
          (getSHA256(s_normal) === "5962ca3e3114351e42762683bd861eaa735e8c8ca07378c63330fcaccc504a56") &&
          (getSHA256(forge.util.encodeUtf8(s_normal)) === "5962ca3e3114351e42762683bd861eaa735e8c8ca07378c63330fcaccc504a56"));
    } catch (err) {
    result = false;
    }

    return result;
}

However with mandatory 2FA it seems, offline recovery is probably not an option anymore.

3rdIteration commented 2 years ago

Once you have the wallet file it would be fine, so if you want to fix it up and bring a PR then please do.

Basically dogechain support has come through two abandoned sponsored features (so the person sponsoring didn't end up paying on delivery of working code) so I'm not inclined to expand any time on this wallet type beyond approving a PR.

jimtje commented 2 years ago

I can certainly do that, but I'm not sure how much utility the feature would have. My old dogechain wallet, which did not have 2FA enabled, had it forced upon it at some point in the last few months and since it's linked to my old school email that I no longer have access to, I'm effectively locked out. I have it backed up, but if I remember correctly the most obvious way to back up one's account is the plaintext pdf. Any new account I open have the same problem - 2FA enforced logins. Is this specific feature one that is frequently asked about beyond the two abandoned sponsors? Because it feels particularly niche, all things considered.

I maintain a library for opening legacy format wallets and while a different use case, I'd be happy to integrate/solve problems that actual long time crypto users have ran into, like figuring out what coin is in a particular wallet.dat file as in #80 - and the feature would also do the same for passworded but not encrypted wallet files - and bitcoinj-based wallets and keyfiles as well. If you think that's within the scope of this repo I'd be happy to add that set of features instead, as almost inevitably I find myself referring those who ask questions about my library to this repo.

BeatTheBullets commented 1 year ago

@jimtje I am currently trying to regain acces to an old wallet of mine from 2017. I had given up hope on this wallet but recently discovering this btcrecover program I would like to give it another try. So it is very unfortunate that the one wallet type I am trying to recover has broken due to this recent update.

I have acces to my 2FA so I have been able to download the wallet file(looks the same as the example above from 3rdItteration), but now I need to decrypt it so the btcrecover program can use it again. I have taken a look at your wallet decryptor javascript code but I am unsure how to run it with the wallet. I would very much appreciate it if you could help with this matter.

3rdIteration commented 1 year ago

@jimtje I am currently trying to regain acces to an old wallet of mine from 2017. I had given up hope on this wallet but recently discovering this btcrecover program I would like to give it another try. So it is very unfortunate that the one wallet type I am trying to recover has broken due to this recent update.

I have acces to my 2FA so I have been able to download the wallet file(looks the same as the example above from 3rdItteration), but now I need to decrypt it so the btcrecover program can use it again. I have taken a look at your wallet decryptor javascript code but I am unsure how to run it with the wallet. I would very much appreciate it if you could help with this matter.

Support could be fixed if you want to sponsor it.

BeatTheBullets commented 1 year ago

@3rdIteration What would this sponsorship entail (specifics)?

3rdIteration commented 1 year ago

It's already half done, so would basically just be $250 USD. Once the sponsorship payment was recieved then the feature would be completed and merged within two weeks.

BeatTheBullets commented 1 year ago

@3rdIteration It is good to hear that it is almost finished. Unfortunately I do not have that kind of money right now.

sa976171 commented 5 months ago

Can you please fix the issue. I have 30k dogecoin in dogechain wallet. I will donate to the creator if i am able to recover it

3rdIteration commented 5 months ago

I'm happy to look at it as a trusted recovery if you like.

tdfd2024 commented 4 months ago

I'm happy to look at it as a trusted recovery if you like. Hello, image dogechain.info will stop functioning on June 1st, could you help us decipher the code? What good will the code be if it is of no use to anyone? I think everyone who can restore access to their dogecoins will thank you financially

3rdIteration commented 4 months ago

If you want a trusted recovery then you can request one here: https://cryptoguide.tips/recovery-services-consultations/