Post
Topic
Board Development & Technical Discussion
Re: "Fixing 24-Word Mnemonic Support in bip39-solver-gpu"
by
s.john
on 15/12/2024, 09:01:50 UTC

I read what mcdouglasx said and took it as a challenge for myself. Take a good look at what I did: The ipad_key and opad_key arrays with 128 bytes each to perform HMAC operations. When the mnemonic_length exceeds 128 bytes (24 words), the remainder of the key is not being processed correctly. Therefore, it would be necessary to implement a key normalization process that complies with HMAC. If the key is larger than the block size (128 bytes for SHA-512), it must first be hashed to reduce its size. Then, it should be padded with zeros to reach 128 bytes if needed.

The changes I made to the file int_to_address.cl look like this:
Code:
__kernel void int_to_address(ulong mnemonic_start_hi, ulong mnemonic_start_lo, __global uchar * target_mnemonic, __global uchar * found_mnemonic) {
    ulong idx = get_global_id(0);

    ulong mnemonic_lo = mnemonic_start_lo + idx;
    ulong mnemonic_hi = mnemonic_start_hi;

    // ... [existing code to construct 'bytes' and 'mnemonic']

    // Constructing the mnemonic
    uchar mnemonic[180] = {0};
    uchar mnemonic_length = 11;
    for(int i = 0; i < 12; i++) {
        int word_index = indices[i];
        int word_length = word_lengths[word_index];
        mnemonic_length += word_lengths[word_index];
    }

    int mnemonic_index = 0;
    for (int i = 0; i < 12; i++) {
        int word_index = indices[i];
        int word_length = word_lengths[word_index];
       
        for(int j = 0; j < word_length; j++) {
            mnemonic[mnemonic_index] = words[word_index][j];
            mnemonic_index++;
        }
        mnemonic[mnemonic_index] = 32; // Space
        mnemonic_index++;
    }
    mnemonic[mnemonic_index - 1] = 0; // Null termination

    // Key Normalization
    uchar normalized_key[128] = {0};
    if (mnemonic_length > 128) {
        // If the mnemonic is larger than 128 bytes, hash it with SHA-512
        sha512(&mnemonic, mnemonic_length, &normalized_key);
        // Remaining bytes are already zero-padded (done at initialization)
    } else {
        // If the mnemonic is less than or equal to 128 bytes, copy it directly
        for(int i = 0; i < mnemonic_length; i++) {
            normalized_key[i] = mnemonic[i];
        }
        // Remaining bytes are already zero-padded
    }

    // Initialization of ipad_key and opad_key
    uchar ipad_key[128];
    uchar opad_key[128];
    for(int x = 0; x < 128; x++) {
        ipad_key[x] = 0x36;
        opad_key[x] = 0x5c;
    }

    // Apply XOR with the normalized key
    for(int x = 0; x < 128; x++) {
        ipad_key[x] ^= normalized_key[x];
        opad_key[x] ^= normalized_key[x];
    }

    // Continue seed derivation process
    uchar seed[64] = {0};
    uchar sha512_result[64] = {0};
    uchar key_previous_concat[256] = {0};
    uchar salt[12] = {109, 110, 101, 109, 111, 110, 105, 99, 0, 0, 0, 1};
    for(int x = 0; x < 128; x++) {
        key_previous_concat[x] = ipad_key[x];
    }
    for(int x = 0; x < 12; x++) {
        key_previous_concat[x + 128] = salt[x];
    }

    sha512(&key_previous_concat, 140, &sha512_result);
    copy_pad_previous(&opad_key, &sha512_result, &key_previous_concat);
    sha512(&key_previous_concat, 192, &sha512_result);
    xor_seed_with_round(&seed, &sha512_result);

    for(int x = 1; x < 2048; x++) {
        copy_pad_previous(&ipad_key, &sha512_result, &key_previous_concat);
        sha512(&key_previous_concat, 192, &sha512_result);
        copy_pad_previous(&opad_key, &sha512_result, &key_previous_concat);
        sha512(&key_previous_concat, 192, &sha512_result);
        xor_seed_with_round(&seed, &sha512_result);
    }

    // ... [existing code for key generation and address verification]

    if(found_target == 1) {
        found_mnemonic[0] = 0x01;
        for(int i = 0; i < mnemonic_index; i++) {
            target_mnemonic[i] = mnemonic[i];
        }
    }
}

And in the file just_seed.cl, it turned out like this:
Code:
__kernel void just_seed(ulong mnemonic_start_hi, ulong mnemonic_start_lo, __global uchar * target_mnemonic, __global uchar * found_mnemonic) {
    ulong idx = get_global_id(0);

    ulong seed_start = idx * 64;
    ulong mnemonic_lo = mnemonic_start_lo + idx;
    ulong mnemonic_hi = mnemonic_start_hi;

    // ... [existing code to build 'bytes' and 'mnemonic']

    // Mnemonic construction
    uchar mnemonic[180];
    int mnemonic_index = 0;
    for (int i = 0; i < 12; i++) {
        int word_index = indices[i];
        int word_length = word_lengths[word_index];
       
        for(int j = 0; j < word_length; j++) {
            mnemonic[mnemonic_index] = words[word_index][j];
            mnemonic_index++;
        }
        mnemonic[mnemonic_index] = 32; // Space
        mnemonic_index++;
    }
    mnemonic[mnemonic_index - 1] = 0; // Null termination

    uchar mnemonic_length = 11 + word_lengths[indices[0]] + word_lengths[indices[1]] + word_lengths[indices[2]] + word_lengths[indices[3]] + word_lengths[indices[4]] + word_lengths[indices[5]] + word_lengths[indices[6]] + word_lengths[indices[7]] + word_lengths[indices[8]] + word_lengths[indices[9]] + word_lengths[indices[10]] + word_lengths[indices[11]];

    // Key normalization
    uchar normalized_key[128] = {0};
    if (mnemonic_length > 128) {
        // If the mnemonic is larger than 128 bytes, hash with SHA-512
        sha512(&mnemonic, mnemonic_length, &normalized_key);
        // Fill remaining bytes with zeros (already done during initialization)
    } else {
        // If the mnemonic is 128 bytes or less, copy directly
        for(int i = 0; i < mnemonic_length; i++) {
            normalized_key[i] = mnemonic[i];
        }
        // Remaining bytes are already filled with zeros
    }

    // Initialization of ipad_key and opad_key
    uchar ipad_key[128];
    uchar opad_key[128];
    for(int x = 0; x < 128; x++) {
        ipad_key[x] = 0x36;
        opad_key[x] = 0x5c;
    }

    // Apply XOR with normalized key
    for(int x = 0; x < 128; x++) {
        ipad_key[x] ^= normalized_key[x];
        opad_key[x] ^= normalized_key[x];
    }

    // Continue the seed derivation process
    uchar seed[64] = { 0 };
    uchar sha512_result[64] = { 0 };
    uchar key_previous_concat[256] = { 0 };
    uchar salt[12] = { 109, 110, 101, 109, 111, 110, 105, 99, 0, 0, 0, 1 };
    for(int x = 0; x < 128; x++) {
        key_previous_concat[x] = ipad_key[x];
    }
    for(int x = 0; x < 12; x++) {
        key_previous_concat[x + 128] = salt[x];
    }

    sha512(&key_previous_concat, 140, &sha512_result);
    copy_pad_previous(&opad_key, &sha512_result, &key_previous_concat);
    sha512(&key_previous_concat, 192, &sha512_result);
    xor_seed_with_round(&seed, &sha512_result);

    for(int x = 1; x < 2048; x++) {
        copy_pad_previous(&ipad_key, &sha512_result, &key_previous_concat);
        sha512(&key_previous_concat, 192, &sha512_result);
        copy_pad_previous(&opad_key, &sha512_result, &key_previous_concat);
        sha512(&key_previous_concat, 192, &sha512_result);
        xor_seed_with_round(&seed, &sha512_result);
    }
}

This was my work, test it to see if everything is running fine and let me know




I've made a lot of changes to some of the kernel files and the host file, but the problem I’m facing is the incorrect derivation of addresses from the 24 words mnemonics. As you can see, after running the program, you will get two text files: one for the mnemonics and the other for the addresses. The mnemonics are correct, but the addresses are not. I want to correctly derive the BIP49 addresses from the mnemonics and save them into a text file.

https://github.com/johnnstewart/mt-gpu/tree/main