Skip to content

Commit

Permalink
[Refactoring] BIP38: init uint from string and use arith where needed
Browse files Browse the repository at this point in the history
  • Loading branch information
random-zebra committed May 5, 2021
1 parent 948bb36 commit b4a459b
Showing 1 changed file with 39 additions and 37 deletions.
76 changes: 39 additions & 37 deletions src/bip38.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ void ComputePreFactor(std::string strPassphrase, std::string strSalt, uint256& p
void ComputePassfactor(std::string ownersalt, uint256 prefactor, uint256& passfactor)
{
//concat prefactor and ownersalt
uint512 temp(ReverseEndianString(HexStr(prefactor) + ownersalt));
uint512 temp = uint512S(ReverseEndianString(HexStr(prefactor) + ownersalt));
Hash(temp.begin(), 40, passfactor.begin()); //40 bytes is the length of prefactor + salt
Hash(passfactor.begin(), 32, passfactor.begin());
}
Expand Down Expand Up @@ -110,31 +110,31 @@ std::string BIP38_Encrypt(std::string strAddress, std::string strPassphrase, uin
uint64_t salt = uint256S(ReverseEndianString(strAddressHash)).GetCheapHash();
scrypt_hash(strPassphrase.c_str(), strPassphrase.size(), BEGIN(salt), strAddressHash.size() / 2, BEGIN(hashed), 16384, 8, 8, 64);

uint256 derivedHalf1(hashed.ToString().substr(64, 64));
uint256 derivedHalf2(hashed.ToString().substr(0, 64));
arith_uint256 derivedHalf1(hashed.ToString().substr(64, 64));
arith_uint256 derivedHalf2(hashed.ToString().substr(0, 64));

//block1 = (pointb[1...16] xor derivedhalf1[0...15])
uint256 block1 = uint256((privKey << 128) ^ (derivedHalf1 << 128)) >> 128;
arith_uint256 block1 = ((UintToArith256(privKey) << 128) ^ (derivedHalf1 << 128)) >> 128;

//encrypt part 1
uint512 encrypted1;
arith_uint512 encrypted1;
AES_KEY key;
AES_set_encrypt_key(derivedHalf2.begin(), 256, &key);
AES_encrypt(block1.begin(), encrypted1.begin(), &key);

//block2 = (pointb[17...32] xor derivedhalf1[16...31]
uint256 p2 = privKey >> 128;
uint256 dh12 = derivedHalf1 >> 128;
uint256 block2 = uint256(p2 ^ dh12);
arith_uint256 p2 = UintToArith256(privKey) >> 128;
arith_uint256 dh12 = derivedHalf1 >> 128;
arith_uint256 block2 = p2 ^ dh12;

//encrypt part 2
uint512 encrypted2;
arith_uint512 encrypted2;
AES_encrypt(block2.begin(), encrypted2.begin(), &key);

std::string strPrefix = "0142";
strPrefix += (fCompressed ? "E0" : "C0");

uint512 encryptedKey(ReverseEndianString(strPrefix + strAddressHash));
arith_uint512 encryptedKey(ReverseEndianString(strPrefix + strAddressHash));

//add encrypted1 to the end of encryptedKey
encryptedKey = encryptedKey | (encrypted1 << 56);
Expand All @@ -144,7 +144,7 @@ std::string BIP38_Encrypt(std::string strAddress, std::string strPassphrase, uin

//Base58 checksum is the 4 bytes of dSHA256 hash of the encrypted key
uint256 hashChecksum = Hash(encryptedKey.begin(), encryptedKey.begin() + 39);
uint512 b58Checksum(hashChecksum.ToString().substr(64 - 8, 8));
arith_uint512 b58Checksum(hashChecksum.ToString().substr(64 - 8, 8));

// append the encrypted key with checksum (currently occupies 312 bits)
encryptedKey = encryptedKey | (b58Checksum << 312);
Expand All @@ -162,27 +162,27 @@ bool BIP38_Decrypt(std::string strPassphrase, std::string strEncryptedKey, uint2
return false;

//invalid prefix
if (uint256(ReverseEndianString(strKey.substr(0, 2))) != uint256(0x01))
if (uint256S(ReverseEndianString(strKey.substr(0, 2))) != UINT256_ONE)
return false;

uint256 type(ReverseEndianString(strKey.substr(2, 2)));
uint256 flag(ReverseEndianString(strKey.substr(4, 2)));
arith_uint256 type(ReverseEndianString(strKey.substr(2, 2)));
arith_uint256 flag(ReverseEndianString(strKey.substr(4, 2)));
std::string strAddressHash = strKey.substr(6, 8);
std::string ownersalt = strKey.substr(14, 16);
uint256 encryptedPart1(ReverseEndianString(strKey.substr(30, 16)));
uint256 encryptedPart2(ReverseEndianString(strKey.substr(46, 32)));
uint256 encryptedPart1 = uint256S(ReverseEndianString(strKey.substr(30, 16)));
uint256 encryptedPart2 = uint256S(ReverseEndianString(strKey.substr(46, 32)));

fCompressed = (flag & uint256(0x20)) != 0;
fCompressed = (flag & 0x20) != ARITH_UINT256_ZERO;

//not ec multiplied
if (type == uint256(0x42)) {
if (type == arith_uint256(0x42)) {
uint512 hashed;
encryptedPart1 = uint256(ReverseEndianString(strKey.substr(14, 32)));
encryptedPart1 = uint256S(ReverseEndianString(strKey.substr(14, 32)));
uint64_t salt = uint256S(ReverseEndianString(strAddressHash)).GetCheapHash();
scrypt_hash(strPassphrase.c_str(), strPassphrase.size(), BEGIN(salt), strAddressHash.size() / 2, BEGIN(hashed), 16384, 8, 8, 64);

uint256 derivedHalf1(uint256S(hashed.ToString().substr(64, 64)));
uint256 derivedHalf2(uint256S(hashed.ToString().substr(0, 64)));
const uint256& derivedHalf1 = uint256S(hashed.ToString().substr(64, 64));
const uint256& derivedHalf2 = uint256S(hashed.ToString().substr(0, 64));

uint256 decryptedPart1;
DecryptAES(encryptedPart1, derivedHalf2, decryptedPart1);
Expand All @@ -191,15 +191,17 @@ bool BIP38_Decrypt(std::string strPassphrase, std::string strEncryptedKey, uint2
DecryptAES(encryptedPart2, derivedHalf2, decryptedPart2);

//combine decrypted parts into 64 bytes
uint256 temp1 = decryptedPart2 << 128;
temp1 = temp1 | decryptedPart1;
arith_uint256 temp1 = UintToArith256(decryptedPart2) << 128;
temp1 = temp1 | UintToArith256(decryptedPart1);

//xor the decryption with the derived half 1 for the final key
privKey = temp1 ^ derivedHalf1;
privKey = ArithToUint256(temp1 ^ UintToArith256(derivedHalf1));

return true;
} else if (type != uint256(0x43)) //invalid type
} else if (type != arith_uint256(0x43)) {
//invalid type
return false;
}

bool fLotSequence = (flag & 0x04) != 0;

Expand All @@ -224,32 +226,32 @@ bool BIP38_Decrypt(std::string strPassphrase, std::string strEncryptedKey, uint2
ComputeSeedBPass(passpoint, strAddressHash, ownersalt, seedBPass);

//get derived halfs, being mindful for endian switch
uint256 derivedHalf1(uint256S(seedBPass.ToString().substr(64, 64)));
uint256 derivedHalf2(uint256S(seedBPass.ToString().substr(0, 64)));
const uint256 derivedHalf1 = uint256S(seedBPass.ToString().substr(64, 64));
const uint256 derivedHalf2 = uint256S(seedBPass.ToString().substr(0, 64));

/** Decrypt encryptedpart2 using AES256Decrypt to yield the last 8 bytes of seedb and the last 8 bytes of encryptedpart1. **/
uint256 decryptedPart2;
DecryptAES(encryptedPart2, derivedHalf2, decryptedPart2);

//xor decryptedPart2 and 2nd half of derived half 1
uint256 x0 = derivedHalf1 >> 128; //drop off the first half (note: endian)
uint256 x1 = decryptedPart2 ^ x0;
uint256 seedbPart2 = x1 >> 64;
arith_uint256 x0 = UintToArith256(derivedHalf1) >> 128; //drop off the first half (note: endian)
arith_uint256 x1 = UintToArith256(decryptedPart2) ^ x0;
arith_uint256 seedbPart2 = x1 >> 64;

/** Decrypt encryptedpart1 to yield the remainder of seedb. **/
uint256 decryptedPart1;
uint256 x2 = x1 & uint256("0xffffffffffffffff"); // set x2 to seedbPart1 (still encrypted)
arith_uint256 x2 = x1 & arith_uint256("0xffffffffffffffff"); // set x2 to seedbPart1 (still encrypted)
x2 = x2 << 64; //make room to add encryptedPart1 to the front
x2 = encryptedPart1 | x2; //combine with encryptedPart1
DecryptAES(x2, derivedHalf2, decryptedPart1);
x2 = UintToArith256(encryptedPart1) | x2; //combine with encryptedPart1
DecryptAES(ArithToUint256(x2), derivedHalf2, decryptedPart1);

//decrypted part 1: seedb[0..15] xor derivedhalf1[0..15]
uint256 x3 = derivedHalf1 & uint256("0xffffffffffffffffffffffffffffffff");
uint256 seedbPart1 = decryptedPart1 ^ x3;
uint256 seedB = seedbPart1 | (seedbPart2 << 128);
arith_uint256 x3 = UintToArith256(derivedHalf1) & arith_uint256("0xffffffffffffffffffffffffffffffff");
arith_uint256 seedbPart1 = UintToArith256(decryptedPart1) ^ x3;
arith_uint256 seedB = seedbPart1 | (seedbPart2 << 128);

uint256 factorB;
ComputeFactorB(seedB, factorB);
ComputeFactorB(ArithToUint256(seedB), factorB);

//multiply passfactor by factorb mod N to yield the priv key
secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
Expand Down

0 comments on commit b4a459b

Please sign in to comment.