Line data Source code
1 : // Copyright (c) 2017-2021 The PIVX Core developers
2 : // Distributed under the MIT software license, see the accompanying
3 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 :
5 : #include "bip38.h"
6 :
7 : #include "base58.h"
8 : #include "crypto/aes.h"
9 : #include "key_io.h"
10 : #include "hash.h"
11 : #include "utilstrencodings.h"
12 : #include "random.h"
13 :
14 : #include <secp256k1.h>
15 : #include <string>
16 :
17 1 : static bool Base58ToHex(const std::string& base58_str, std::string& hex_str)
18 : {
19 : // Base58 decoding
20 : // it must be 39 bytes - and another 4 bytes for base58 checksum
21 1 : size_t key_size = 39 + 4;
22 2 : std::vector<unsigned char> vchKey;
23 1 : if (!DecodeBase58(base58_str.c_str(), vchKey, key_size) || vchKey.size() != key_size) {
24 : return false;
25 : }
26 : // Hex encoding
27 1 : std::stringstream ss;
28 1 : ss << std::hex;
29 44 : for (unsigned int i = 0; i < vchKey.size(); i++) {
30 43 : const unsigned char* c = vchKey.data() + i;
31 43 : ss << std::setw(2) << std::setfill('0') << (int)*c;
32 : }
33 1 : hex_str = ss.str();
34 1 : return true;
35 : }
36 :
37 :
38 : /** 39 bytes - 78 characters
39 : * 1) Prefix - 2 bytes - 4 chars - strKey[0..3]
40 : * 2) Flagbyte - 1 byte - 2 chars - strKey[4..5]
41 : * 3) addresshash - 4 bytes - 8 chars - strKey[6..13]
42 : * 4) Owner Entropy - 8 bytes - 16 chars - strKey[14..29]
43 : * 5) Encrypted Part 1 - 8 bytes - 16 chars - strKey[30..45]
44 : * 6) Encrypted Part 2 - 16 bytes - 32 chars - strKey[46..77]
45 : */
46 :
47 2 : void DecryptAES(uint256 encryptedIn, uint256 decryptionKey, uint256& output)
48 : {
49 2 : AES256Decrypt(decryptionKey.begin()).Decrypt(output.begin(), encryptedIn.begin());
50 2 : }
51 :
52 0 : void ComputePreFactor(std::string strPassphrase, std::string strSalt, uint256& prefactor)
53 : {
54 : //passfactor is the scrypt hash of passphrase and ownersalt (NOTE this needs to handle alt cases too in the future)
55 0 : uint64_t s = uint256S(ReverseEndianString(strSalt)).GetCheapHash();
56 0 : scrypt_hash(strPassphrase.c_str(), strPassphrase.size(), BEGIN(s), strSalt.size() / 2, BEGIN(prefactor), 16384, 8, 8, 32);
57 0 : }
58 :
59 0 : void ComputePassfactor(std::string ownersalt, uint256 prefactor, uint256& passfactor)
60 : {
61 : //concat prefactor and ownersalt
62 0 : uint512 temp = uint512S(ReverseEndianString(HexStr(prefactor) + ownersalt));
63 0 : Hash(temp.begin(), temp.end(), passfactor.begin(), passfactor.end());
64 0 : }
65 :
66 0 : bool ComputePasspoint(uint256 passfactor, CPubKey& passpoint)
67 : {
68 0 : size_t clen = 65;
69 0 : secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
70 0 : assert(ctx != nullptr);
71 0 : {
72 : // Pass in a random blinding seed to the secp256k1 context.
73 0 : std::vector<unsigned char, secure_allocator<unsigned char>> vseed(32);
74 0 : GetRandBytes(vseed.data(), 32);
75 0 : bool ret = secp256k1_context_randomize(ctx, vseed.data());
76 0 : assert(ret);
77 : }
78 0 : secp256k1_pubkey pubkey;
79 :
80 : //passpoint is the ec_mult of passfactor on secp256k1
81 0 : if (!secp256k1_ec_pubkey_create(ctx, &pubkey, passfactor.begin())) {
82 0 : secp256k1_context_destroy(ctx);
83 0 : return false;
84 : }
85 :
86 0 : secp256k1_ec_pubkey_serialize(ctx, (unsigned char*)passpoint.begin(), &clen, &pubkey, SECP256K1_EC_COMPRESSED);
87 0 : secp256k1_context_destroy(ctx);
88 :
89 0 : if (passpoint.size() != clen)
90 : return false;
91 :
92 0 : if (!passpoint.IsValid())
93 0 : return false;
94 :
95 : return true;
96 : }
97 :
98 0 : void ComputeSeedBPass(CPubKey passpoint, std::string strAddressHash, std::string strOwnerSalt, uint512& seedBPass)
99 : {
100 : // Derive decryption key for seedb using scrypt with passpoint, addresshash, and ownerentropy
101 0 : std::string salt = ReverseEndianString(strAddressHash + strOwnerSalt);
102 0 : uint256 s2(uint256S(salt));
103 0 : scrypt_hash(BEGIN(passpoint), HexStr(passpoint).size() / 2, BEGIN(s2), salt.size() / 2, BEGIN(seedBPass), 1024, 1, 1, 64);
104 0 : }
105 :
106 0 : void ComputeFactorB(uint256 seedB, uint256& factorB)
107 : {
108 : //factorB - a double sha256 hash of seedb
109 0 : Hash(seedB.begin(), seedB.end(), factorB.begin(), factorB.end());
110 0 : }
111 :
112 1 : std::string AddressToBip38Hash(const std::string& address)
113 : {
114 1 : uint256 addrCheck = Hash(address.begin(), address.end());
115 :
116 2 : return HexStr(addrCheck).substr(0, 8);
117 : }
118 :
119 1 : std::string BIP38_Encrypt(std::string strAddress, std::string strPassphrase, uint256 privKey, bool fCompressed)
120 : {
121 1 : std::string strAddressHash = AddressToBip38Hash(strAddress);
122 :
123 1 : uint512 hashed;
124 3 : uint64_t salt = uint256S(ReverseEndianString(strAddressHash)).GetCheapHash();
125 1 : scrypt_hash(strPassphrase.c_str(), strPassphrase.size(), BEGIN(salt), strAddressHash.size() / 2, BEGIN(hashed), 16384, 8, 8, 64);
126 :
127 3 : arith_uint256 derivedHalf1(hashed.ToString().substr(64, 64));
128 3 : arith_uint256 derivedHalf2(hashed.ToString().substr(0, 64));
129 :
130 : //block1 = (pointb[1...16] xor derivedhalf1[0...15])
131 2 : arith_uint256 block1 = ((UintToArith256(privKey) << 128) ^ (derivedHalf1 << 128)) >> 128;
132 :
133 : //encrypt part 1
134 1 : arith_uint512 encrypted1;
135 2 : AES256Encrypt enc(derivedHalf2.begin());
136 1 : enc.Encrypt(encrypted1.begin(), block1.begin());
137 :
138 : //block2 = (pointb[17...32] xor derivedhalf1[16...31]
139 1 : arith_uint256 p2 = UintToArith256(privKey) >> 128;
140 1 : arith_uint256 dh12 = derivedHalf1 >> 128;
141 2 : arith_uint256 block2 = p2 ^ dh12;
142 :
143 : //encrypt part 2
144 1 : arith_uint512 encrypted2;
145 1 : enc.Encrypt(encrypted2.begin(), block2.begin());
146 :
147 2 : std::string strPrefix = "0142";
148 1 : strPrefix += (fCompressed ? "E0" : "C0");
149 :
150 2 : arith_uint512 encryptedKey(ReverseEndianString(strPrefix + strAddressHash));
151 :
152 : //add encrypted1 to the end of encryptedKey
153 20 : encryptedKey = encryptedKey | (encrypted1 << 56);
154 :
155 : //add encrypted2 to the end of encryptedKey
156 20 : encryptedKey = encryptedKey | (encrypted2 << (56 + 128));
157 :
158 : //Base58 checksum is the 4 bytes of dSHA256 hash of the encrypted key
159 1 : uint256 hashChecksum = Hash(encryptedKey.begin(), encryptedKey.begin() + 39);
160 2 : arith_uint512 b58Checksum(hashChecksum.ToString().substr(64 - 8, 8));
161 :
162 : // append the encrypted key with checksum (currently occupies 312 bits)
163 20 : encryptedKey = encryptedKey | (b58Checksum << 312);
164 :
165 : //43 bytes is the total size that we are encoding
166 2 : return EncodeBase58(encryptedKey.begin(), encryptedKey.begin() + 43);
167 : }
168 :
169 1 : bool BIP38_Decrypt(std::string strPassphrase, std::string strEncryptedKey, uint256& privKey, bool& fCompressed)
170 : {
171 2 : std::string strKey;
172 1 : if (!Base58ToHex(strEncryptedKey, strKey)) {
173 : // incorrect encoding of key
174 : return false;
175 : }
176 :
177 : // invalid prefix
178 2 : if (uint256S(ReverseEndianString(strKey.substr(0, 2))) != UINT256_ONE)
179 : return false;
180 :
181 2 : arith_uint256 type(ReverseEndianString(strKey.substr(2, 2)));
182 2 : arith_uint256 flag(ReverseEndianString(strKey.substr(4, 2)));
183 2 : std::string strAddressHash = strKey.substr(6, 8);
184 2 : std::string ownersalt = strKey.substr(14, 16);
185 3 : uint256 encryptedPart1 = uint256S(ReverseEndianString(strKey.substr(30, 16)));
186 3 : uint256 encryptedPart2 = uint256S(ReverseEndianString(strKey.substr(46, 32)));
187 :
188 3 : fCompressed = (flag & 0x20) != ARITH_UINT256_ZERO;
189 :
190 : //not ec multiplied
191 2 : if (type == arith_uint256(0x42)) {
192 1 : uint512 hashed;
193 3 : encryptedPart1 = uint256S(ReverseEndianString(strKey.substr(14, 32)));
194 3 : uint64_t salt = uint256S(ReverseEndianString(strAddressHash)).GetCheapHash();
195 1 : scrypt_hash(strPassphrase.c_str(), strPassphrase.size(), BEGIN(salt), strAddressHash.size() / 2, BEGIN(hashed), 16384, 8, 8, 64);
196 :
197 3 : const uint256& derivedHalf1 = uint256S(hashed.ToString().substr(64, 64));
198 3 : const uint256& derivedHalf2 = uint256S(hashed.ToString().substr(0, 64));
199 :
200 1 : uint256 decryptedPart1;
201 1 : DecryptAES(encryptedPart1, derivedHalf2, decryptedPart1);
202 :
203 1 : uint256 decryptedPart2;
204 1 : DecryptAES(encryptedPart2, derivedHalf2, decryptedPart2);
205 :
206 : //combine decrypted parts into 64 bytes
207 1 : arith_uint256 temp1 = UintToArith256(decryptedPart2) << 128;
208 11 : temp1 = temp1 | UintToArith256(decryptedPart1);
209 :
210 : //xor the decryption with the derived half 1 for the final key
211 3 : privKey = ArithToUint256(temp1 ^ UintToArith256(derivedHalf1));
212 :
213 1 : return true;
214 0 : } else if (type != arith_uint256(0x43)) {
215 : //invalid type
216 : return false;
217 : }
218 :
219 0 : bool fLotSequence = (flag & 0x04) != 0;
220 :
221 1 : std::string prefactorSalt = ownersalt;
222 0 : if (fLotSequence)
223 0 : prefactorSalt = ownersalt.substr(0, 8);
224 :
225 0 : uint256 prefactor;
226 0 : ComputePreFactor(strPassphrase, prefactorSalt, prefactor);
227 :
228 0 : uint256 passfactor;
229 0 : if (fLotSequence)
230 0 : ComputePassfactor(ownersalt, prefactor, passfactor);
231 : else
232 0 : passfactor = prefactor;
233 :
234 0 : CPubKey passpoint;
235 0 : if (!ComputePasspoint(passfactor, passpoint))
236 : return false;
237 :
238 0 : uint512 seedBPass;
239 0 : ComputeSeedBPass(passpoint, strAddressHash, ownersalt, seedBPass);
240 :
241 : //get derived halfs, being mindful for endian switch
242 0 : const uint256 derivedHalf1 = uint256S(seedBPass.ToString().substr(64, 64));
243 0 : const uint256 derivedHalf2 = uint256S(seedBPass.ToString().substr(0, 64));
244 :
245 : /** Decrypt encryptedpart2 using AES256Decrypt to yield the last 8 bytes of seedb and the last 8 bytes of encryptedpart1. **/
246 0 : uint256 decryptedPart2;
247 0 : DecryptAES(encryptedPart2, derivedHalf2, decryptedPart2);
248 :
249 : //xor decryptedPart2 and 2nd half of derived half 1
250 0 : arith_uint256 x0 = UintToArith256(derivedHalf1) >> 128; //drop off the first half (note: endian)
251 0 : arith_uint256 x1 = UintToArith256(decryptedPart2) ^ x0;
252 0 : arith_uint256 seedbPart2 = x1 >> 64;
253 :
254 : /** Decrypt encryptedpart1 to yield the remainder of seedb. **/
255 0 : uint256 decryptedPart1;
256 0 : arith_uint256 x2 = x1 & arith_uint256("0xffffffffffffffff"); // set x2 to seedbPart1 (still encrypted)
257 0 : x2 = x2 << 64; //make room to add encryptedPart1 to the front
258 0 : x2 = UintToArith256(encryptedPart1) | x2; //combine with encryptedPart1
259 0 : DecryptAES(ArithToUint256(x2), derivedHalf2, decryptedPart1);
260 :
261 : //decrypted part 1: seedb[0..15] xor derivedhalf1[0..15]
262 0 : arith_uint256 x3 = UintToArith256(derivedHalf1) & arith_uint256("0xffffffffffffffffffffffffffffffff");
263 0 : arith_uint256 seedbPart1 = UintToArith256(decryptedPart1) ^ x3;
264 0 : arith_uint256 seedB = seedbPart1 | (seedbPart2 << 128);
265 :
266 0 : uint256 factorB;
267 0 : ComputeFactorB(ArithToUint256(seedB), factorB);
268 :
269 : //multiply passfactor by factorb mod N to yield the priv key
270 0 : secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
271 0 : assert(ctx != nullptr);
272 0 : {
273 : // Pass in a random blinding seed to the secp256k1 context.
274 0 : std::vector<unsigned char, secure_allocator<unsigned char>> vseed(32);
275 0 : GetRandBytes(vseed.data(), 32);
276 0 : bool ret = secp256k1_context_randomize(ctx, vseed.data());
277 0 : assert(ret);
278 : }
279 0 : privKey = factorB;
280 0 : if (!secp256k1_ec_privkey_tweak_mul(ctx, privKey.begin(), passfactor.begin())) {
281 0 : secp256k1_context_destroy(ctx);
282 : return false;
283 : }
284 0 : secp256k1_context_destroy(ctx);
285 :
286 : //double check that the address hash matches our final privkey
287 0 : CKey k;
288 0 : k.Set(privKey.begin(), privKey.end(), fCompressed);
289 0 : CPubKey pubkey = k.GetPubKey();
290 0 : std::string address = EncodeDestination(pubkey.GetID());
291 :
292 0 : return strAddressHash == AddressToBip38Hash(address);
293 : }
|