PIVX Core  5.6.99
P2P Digital Currency
bip38.cpp
Go to the documentation of this file.
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 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  size_t key_size = 39 + 4;
22  std::vector<unsigned char> vchKey;
23  if (!DecodeBase58(base58_str.c_str(), vchKey, key_size) || vchKey.size() != key_size) {
24  return false;
25  }
26  // Hex encoding
27  std::stringstream ss;
28  ss << std::hex;
29  for (unsigned int i = 0; i < vchKey.size(); i++) {
30  const unsigned char* c = vchKey.data() + i;
31  ss << std::setw(2) << std::setfill('0') << (int)*c;
32  }
33  hex_str = ss.str();
34  return true;
35 }
36 
37 
47 void DecryptAES(uint256 encryptedIn, uint256 decryptionKey, uint256& output)
48 {
49  AES256Decrypt(decryptionKey.begin()).Decrypt(output.begin(), encryptedIn.begin());
50 }
51 
52 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  uint64_t s = uint256S(ReverseEndianString(strSalt)).GetCheapHash();
56  scrypt_hash(strPassphrase.c_str(), strPassphrase.size(), BEGIN(s), strSalt.size() / 2, BEGIN(prefactor), 16384, 8, 8, 32);
57 }
58 
59 void ComputePassfactor(std::string ownersalt, uint256 prefactor, uint256& passfactor)
60 {
61  //concat prefactor and ownersalt
62  uint512 temp = uint512S(ReverseEndianString(HexStr(prefactor) + ownersalt));
63  Hash(temp.begin(), temp.end(), passfactor.begin(), passfactor.end());
64 }
65 
66 bool ComputePasspoint(uint256 passfactor, CPubKey& passpoint)
67 {
68  size_t clen = 65;
70  assert(ctx != nullptr);
71  {
72  // Pass in a random blinding seed to the secp256k1 context.
73  std::vector<unsigned char, secure_allocator<unsigned char>> vseed(32);
74  GetRandBytes(vseed.data(), 32);
75  bool ret = secp256k1_context_randomize(ctx, vseed.data());
76  assert(ret);
77  }
78  secp256k1_pubkey pubkey;
79 
80  //passpoint is the ec_mult of passfactor on secp256k1
81  if (!secp256k1_ec_pubkey_create(ctx, &pubkey, passfactor.begin())) {
83  return false;
84  }
85 
86  secp256k1_ec_pubkey_serialize(ctx, (unsigned char*)passpoint.begin(), &clen, &pubkey, SECP256K1_EC_COMPRESSED);
88 
89  if (passpoint.size() != clen)
90  return false;
91 
92  if (!passpoint.IsValid())
93  return false;
94 
95  return true;
96 }
97 
98 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  std::string salt = ReverseEndianString(strAddressHash + strOwnerSalt);
102  uint256 s2(uint256S(salt));
103  scrypt_hash(BEGIN(passpoint), HexStr(passpoint).size() / 2, BEGIN(s2), salt.size() / 2, BEGIN(seedBPass), 1024, 1, 1, 64);
104 }
105 
106 void ComputeFactorB(uint256 seedB, uint256& factorB)
107 {
108  //factorB - a double sha256 hash of seedb
109  Hash(seedB.begin(), seedB.end(), factorB.begin(), factorB.end());
110 }
111 
112 std::string AddressToBip38Hash(const std::string& address)
113 {
114  uint256 addrCheck = Hash(address.begin(), address.end());
115 
116  return HexStr(addrCheck).substr(0, 8);
117 }
118 
119 std::string BIP38_Encrypt(std::string strAddress, std::string strPassphrase, uint256 privKey, bool fCompressed)
120 {
121  std::string strAddressHash = AddressToBip38Hash(strAddress);
122 
123  uint512 hashed;
124  uint64_t salt = uint256S(ReverseEndianString(strAddressHash)).GetCheapHash();
125  scrypt_hash(strPassphrase.c_str(), strPassphrase.size(), BEGIN(salt), strAddressHash.size() / 2, BEGIN(hashed), 16384, 8, 8, 64);
126 
127  arith_uint256 derivedHalf1(hashed.ToString().substr(64, 64));
128  arith_uint256 derivedHalf2(hashed.ToString().substr(0, 64));
129 
130  //block1 = (pointb[1...16] xor derivedhalf1[0...15])
131  arith_uint256 block1 = ((UintToArith256(privKey) << 128) ^ (derivedHalf1 << 128)) >> 128;
132 
133  //encrypt part 1
134  arith_uint512 encrypted1;
135  AES256Encrypt enc(derivedHalf2.begin());
136  enc.Encrypt(encrypted1.begin(), block1.begin());
137 
138  //block2 = (pointb[17...32] xor derivedhalf1[16...31]
139  arith_uint256 p2 = UintToArith256(privKey) >> 128;
140  arith_uint256 dh12 = derivedHalf1 >> 128;
141  arith_uint256 block2 = p2 ^ dh12;
142 
143  //encrypt part 2
144  arith_uint512 encrypted2;
145  enc.Encrypt(encrypted2.begin(), block2.begin());
146 
147  std::string strPrefix = "0142";
148  strPrefix += (fCompressed ? "E0" : "C0");
149 
150  arith_uint512 encryptedKey(ReverseEndianString(strPrefix + strAddressHash));
151 
152  //add encrypted1 to the end of encryptedKey
153  encryptedKey = encryptedKey | (encrypted1 << 56);
154 
155  //add encrypted2 to the end of encryptedKey
156  encryptedKey = encryptedKey | (encrypted2 << (56 + 128));
157 
158  //Base58 checksum is the 4 bytes of dSHA256 hash of the encrypted key
159  uint256 hashChecksum = Hash(encryptedKey.begin(), encryptedKey.begin() + 39);
160  arith_uint512 b58Checksum(hashChecksum.ToString().substr(64 - 8, 8));
161 
162  // append the encrypted key with checksum (currently occupies 312 bits)
163  encryptedKey = encryptedKey | (b58Checksum << 312);
164 
165  //43 bytes is the total size that we are encoding
166  return EncodeBase58(encryptedKey.begin(), encryptedKey.begin() + 43);
167 }
168 
169 bool BIP38_Decrypt(std::string strPassphrase, std::string strEncryptedKey, uint256& privKey, bool& fCompressed)
170 {
171  std::string strKey;
172  if (!Base58ToHex(strEncryptedKey, strKey)) {
173  // incorrect encoding of key
174  return false;
175  }
176 
177  // invalid prefix
178  if (uint256S(ReverseEndianString(strKey.substr(0, 2))) != UINT256_ONE)
179  return false;
180 
181  arith_uint256 type(ReverseEndianString(strKey.substr(2, 2)));
182  arith_uint256 flag(ReverseEndianString(strKey.substr(4, 2)));
183  std::string strAddressHash = strKey.substr(6, 8);
184  std::string ownersalt = strKey.substr(14, 16);
185  uint256 encryptedPart1 = uint256S(ReverseEndianString(strKey.substr(30, 16)));
186  uint256 encryptedPart2 = uint256S(ReverseEndianString(strKey.substr(46, 32)));
187 
188  fCompressed = (flag & 0x20) != ARITH_UINT256_ZERO;
189 
190  //not ec multiplied
191  if (type == arith_uint256(0x42)) {
192  uint512 hashed;
193  encryptedPart1 = uint256S(ReverseEndianString(strKey.substr(14, 32)));
194  uint64_t salt = uint256S(ReverseEndianString(strAddressHash)).GetCheapHash();
195  scrypt_hash(strPassphrase.c_str(), strPassphrase.size(), BEGIN(salt), strAddressHash.size() / 2, BEGIN(hashed), 16384, 8, 8, 64);
196 
197  const uint256& derivedHalf1 = uint256S(hashed.ToString().substr(64, 64));
198  const uint256& derivedHalf2 = uint256S(hashed.ToString().substr(0, 64));
199 
200  uint256 decryptedPart1;
201  DecryptAES(encryptedPart1, derivedHalf2, decryptedPart1);
202 
203  uint256 decryptedPart2;
204  DecryptAES(encryptedPart2, derivedHalf2, decryptedPart2);
205 
206  //combine decrypted parts into 64 bytes
207  arith_uint256 temp1 = UintToArith256(decryptedPart2) << 128;
208  temp1 = temp1 | UintToArith256(decryptedPart1);
209 
210  //xor the decryption with the derived half 1 for the final key
211  privKey = ArithToUint256(temp1 ^ UintToArith256(derivedHalf1));
212 
213  return true;
214  } else if (type != arith_uint256(0x43)) {
215  //invalid type
216  return false;
217  }
218 
219  bool fLotSequence = (flag & 0x04) != 0;
220 
221  std::string prefactorSalt = ownersalt;
222  if (fLotSequence)
223  prefactorSalt = ownersalt.substr(0, 8);
224 
225  uint256 prefactor;
226  ComputePreFactor(strPassphrase, prefactorSalt, prefactor);
227 
228  uint256 passfactor;
229  if (fLotSequence)
230  ComputePassfactor(ownersalt, prefactor, passfactor);
231  else
232  passfactor = prefactor;
233 
234  CPubKey passpoint;
235  if (!ComputePasspoint(passfactor, passpoint))
236  return false;
237 
238  uint512 seedBPass;
239  ComputeSeedBPass(passpoint, strAddressHash, ownersalt, seedBPass);
240 
241  //get derived halfs, being mindful for endian switch
242  const uint256 derivedHalf1 = uint256S(seedBPass.ToString().substr(64, 64));
243  const uint256 derivedHalf2 = uint256S(seedBPass.ToString().substr(0, 64));
244 
246  uint256 decryptedPart2;
247  DecryptAES(encryptedPart2, derivedHalf2, decryptedPart2);
248 
249  //xor decryptedPart2 and 2nd half of derived half 1
250  arith_uint256 x0 = UintToArith256(derivedHalf1) >> 128; //drop off the first half (note: endian)
251  arith_uint256 x1 = UintToArith256(decryptedPart2) ^ x0;
252  arith_uint256 seedbPart2 = x1 >> 64;
253 
255  uint256 decryptedPart1;
256  arith_uint256 x2 = x1 & arith_uint256("0xffffffffffffffff"); // set x2 to seedbPart1 (still encrypted)
257  x2 = x2 << 64; //make room to add encryptedPart1 to the front
258  x2 = UintToArith256(encryptedPart1) | x2; //combine with encryptedPart1
259  DecryptAES(ArithToUint256(x2), derivedHalf2, decryptedPart1);
260 
261  //decrypted part 1: seedb[0..15] xor derivedhalf1[0..15]
262  arith_uint256 x3 = UintToArith256(derivedHalf1) & arith_uint256("0xffffffffffffffffffffffffffffffff");
263  arith_uint256 seedbPart1 = UintToArith256(decryptedPart1) ^ x3;
264  arith_uint256 seedB = seedbPart1 | (seedbPart2 << 128);
265 
266  uint256 factorB;
267  ComputeFactorB(ArithToUint256(seedB), factorB);
268 
269  //multiply passfactor by factorb mod N to yield the priv key
271  assert(ctx != nullptr);
272  {
273  // Pass in a random blinding seed to the secp256k1 context.
274  std::vector<unsigned char, secure_allocator<unsigned char>> vseed(32);
275  GetRandBytes(vseed.data(), 32);
276  bool ret = secp256k1_context_randomize(ctx, vseed.data());
277  assert(ret);
278  }
279  privKey = factorB;
280  if (!secp256k1_ec_privkey_tweak_mul(ctx, privKey.begin(), passfactor.begin())) {
282  return false;
283  }
285 
286  //double check that the address hash matches our final privkey
287  CKey k;
288  k.Set(privKey.begin(), privKey.end(), fCompressed);
289  CPubKey pubkey = k.GetPubKey();
290  std::string address = EncodeDestination(pubkey.GetID());
291 
292  return strAddressHash == AddressToBip38Hash(address);
293 }
arith_uint256 UintToArith256(const uint256 &a)
uint256 ArithToUint256(const arith_uint256 &a)
const arith_uint256 ARITH_UINT256_ZERO
std::string EncodeBase58(const unsigned char *pbegin, const unsigned char *pend)
Why base-58 instead of standard base-64 encoding?
Definition: base58.cpp:73
bool DecodeBase58(const char *psz, std::vector< unsigned char > &vch, int max_ret_len)
Decode a base58-encoded string (psz) into a byte vector (vchRet).
Definition: base58.cpp:23
void ComputePreFactor(std::string strPassphrase, std::string strSalt, uint256 &prefactor)
Definition: bip38.cpp:52
void ComputeSeedBPass(CPubKey passpoint, std::string strAddressHash, std::string strOwnerSalt, uint512 &seedBPass)
Definition: bip38.cpp:98
std::string AddressToBip38Hash(const std::string &address)
Definition: bip38.cpp:112
bool BIP38_Decrypt(std::string strPassphrase, std::string strEncryptedKey, uint256 &privKey, bool &fCompressed)
Definition: bip38.cpp:169
std::string BIP38_Encrypt(std::string strAddress, std::string strPassphrase, uint256 privKey, bool fCompressed)
Definition: bip38.cpp:119
void ComputeFactorB(uint256 seedB, uint256 &factorB)
Definition: bip38.cpp:106
bool ComputePasspoint(uint256 passfactor, CPubKey &passpoint)
Definition: bip38.cpp:66
void ComputePassfactor(std::string ownersalt, uint256 prefactor, uint256 &passfactor)
Definition: bip38.cpp:59
void DecryptAES(uint256 encryptedIn, uint256 decryptionKey, uint256 &output)
39 bytes - 78 characters 1) Prefix - 2 bytes - 4 chars - strKey[0..3] 2) Flagbyte - 1 byte - 2 chars ...
Definition: bip38.cpp:47
A decryption class for AES-256.
Definition: aes.h:56
void Decrypt(unsigned char plaintext[16], const unsigned char ciphertext[16]) const
Definition: aes.cpp:70
An encryption class for AES-256.
Definition: aes.h:44
void Encrypt(unsigned char ciphertext[16], const unsigned char plaintext[16]) const
Definition: aes.cpp:55
An encapsulated private key.
Definition: key.h:30
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:186
void Set(const T pbegin, const T pend, bool fCompressedIn)
Initialize using begin and end iterators to byte data.
Definition: key.h:76
An encapsulated public key.
Definition: pubkey.h:44
CKeyID GetID() const
Get the KeyID of this public key (hash of its serialization)
Definition: pubkey.h:167
bool IsValid() const
Definition: pubkey.h:183
unsigned int size() const
Simple read-only vector-like interface to the pubkey data.
Definition: pubkey.h:121
const unsigned char * begin() const
Definition: pubkey.h:123
256-bit unsigned big integer.
512-bit unsigned big integer.
std::string ToString() const
Definition: uint256.cpp:65
unsigned char * end()
Definition: uint256.h:68
unsigned char * begin()
Definition: uint256.h:63
unsigned char * begin()
256-bit opaque blob.
Definition: uint256.h:138
uint64_t GetCheapHash() const
A cheap hash function that just returns 64 bits from the result, it can be used when the contents are...
Definition: uint256.h:149
512-bit opaque blob.
Definition: uint256.h:184
void scrypt_hash(const char *pass, unsigned int pLen, const char *salt, unsigned int sLen, char *output, unsigned int N, unsigned int r, unsigned int p, unsigned int dkLen)
Definition: hash.cpp:82
uint256 Hash(const T1 pbegin, const T1 pend)
Compute the 256-bit hash of an object.
Definition: hash.h:173
std::string EncodeDestination(const CWDestination &address, const CChainParams::Base58Type addrType)
void GetRandBytes(unsigned char *buf, int num) noexcept
Overall design of the RNG and entropy sources.
Definition: random.cpp:579
#define SECP256K1_CONTEXT_SIGN
Definition: secp256k1.h:168
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize(secp256k1_context *ctx, const unsigned char *seed32) SECP256K1_ARG_NONNULL(1)
Updates the context randomization to protect against side-channel leakage.
Definition: secp256k1.c:560
SECP256K1_API int secp256k1_ec_pubkey_serialize(const secp256k1_context *ctx, unsigned char *output, size_t *outputlen, const secp256k1_pubkey *pubkey, unsigned int flags) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4)
Serialize a pubkey object into a serialized byte sequence.
Definition: secp256k1.c:175
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_create(const secp256k1_context *ctx, secp256k1_pubkey *pubkey, const unsigned char *seckey) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3)
Compute the public key for a secret key.
Definition: secp256k1.c:417
SECP256K1_API secp256k1_context * secp256k1_context_create(unsigned int flags) SECP256K1_WARN_UNUSED_RESULT
Create a secp256k1 context object.
Definition: secp256k1.c:59
#define SECP256K1_EC_COMPRESSED
Flag to pass to secp256k1_ec_pubkey_serialize and secp256k1_ec_privkey_export.
Definition: secp256k1.h:172
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_mul(const secp256k1_context *ctx, unsigned char *seckey, const unsigned char *tweak) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3)
Tweak a private key by multiplying it by a tweak.
Definition: secp256k1.c:514
SECP256K1_API void secp256k1_context_destroy(secp256k1_context *ctx)
Destroy a secp256k1 context object.
Definition: secp256k1.c:93
Opaque data structure that holds a parsed and valid public key.
Definition: secp256k1.h:66
uint256 uint256S(const char *str)
Definition: uint256.h:157
uint512 uint512S(const char *str)
Definition: uint256.h:195
const uint256 UINT256_ONE
Definition: uint256.h:176
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
#define BEGIN(a)
Utilities for converting data from/to strings.
std::string ReverseEndianString(std::string in)
Reverse the endianness of a string.