PRCYCoin  2.0.0.7rc1
P2P Digital Currency
bip38.cpp
Go to the documentation of this file.
1 // Copyright (c) 2015-2018 The PIVX developers
2 // Copyright (c) 2018-2020 The DAPS Project developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
6 #include "bip38.h"
7 #include "base58.h"
8 #include "hash.h"
9 #include "pubkey.h"
10 #include "util.h"
11 #include "utilstrencodings.h"
12 
13 #include <openssl/aes.h>
14 #include <openssl/sha.h>
15 #include <secp256k1.h>
16 #include <string>
17 
18 
28 void DecryptAES(uint256 encryptedIn, uint256 decryptionKey, uint256& output)
29 {
30  AES_KEY key;
31  AES_set_decrypt_key(decryptionKey.begin(), 256, &key);
32  AES_decrypt(encryptedIn.begin(), output.begin(), &key);
33 }
34 
35 void ComputePreFactor(std::string strPassphrase, std::string strSalt, uint256& prefactor)
36 {
37  //passfactor is the scrypt hash of passphrase and ownersalt (NOTE this needs to handle alt cases too in the future)
38  uint64_t s = uint256S(ReverseEndianString(strSalt)).GetCheapHash();
39  scrypt_hash(strPassphrase.c_str(), strPassphrase.size(), BEGIN(s), strSalt.size() / 2, BEGIN(prefactor), 16384, 8, 8, 32);
40 }
41 
42 void ComputePassfactor(std::string ownersalt, uint256 prefactor, uint256& passfactor)
43 {
44  //concat prefactor and ownersalt
45  uint512 temp(ReverseEndianString(HexStr(prefactor) + ownersalt));
46  Hash(temp.begin(), 40, passfactor.begin()); //40 bytes is the length of prefactor + salt
47  Hash(passfactor.begin(), 32, passfactor.begin());
48 }
49 
50 bool ComputePasspoint(uint256 passfactor, CPubKey& passpoint)
51 {
52  //passpoint is the ec_mult of passfactor on secp256k1
53  int clen = 65;
54  return secp256k1_ec_pubkey_create(UBEGIN(passpoint), &clen, passfactor.begin(), true) != 0;
55 }
56 
57 void ComputeSeedBPass(CPubKey passpoint, std::string strAddressHash, std::string strOwnerSalt, uint512& seedBPass)
58 {
59  // Derive decryption key for seedb using scrypt with passpoint, addresshash, and ownerentropy
60  std::string salt = ReverseEndianString(strAddressHash + strOwnerSalt);
61  uint256 s2(uint256S(salt));
62  scrypt_hash(BEGIN(passpoint), HexStr(passpoint).size() / 2, BEGIN(s2), salt.size() / 2, BEGIN(seedBPass), 1024, 1, 1, 64);
63 }
64 
65 void ComputeFactorB(uint256 seedB, uint256& factorB)
66 {
67  //factorB - a double sha256 hash of seedb
68  Hash(seedB.begin(), 24, factorB.begin()); //seedB is only 24 bytes
69  Hash(factorB.begin(), 32, factorB.begin());
70 }
71 
72 std::string AddressToBip38Hash(std::string address)
73 {
74  uint256 addrCheck;
75  Hash((void*)address.c_str(), address.size(), addrCheck.begin());
76  Hash(addrCheck.begin(), 32, addrCheck.begin());
77 
78  return HexStr(addrCheck).substr(0, 8);
79 }
80 
81 std::string BIP38_Encrypt(std::string strAddress, std::string strPassphrase, uint256 privKey, bool fCompressed)
82 {
83  std::string strAddressHash = AddressToBip38Hash(strAddress);
84 
85  uint512 hashed;
86  uint64_t salt = uint256S(ReverseEndianString(strAddressHash)).GetCheapHash();
87  scrypt_hash(strPassphrase.c_str(), strPassphrase.size(), BEGIN(salt), strAddressHash.size() / 2, BEGIN(hashed), 16384, 8, 8, 64);
88 
89  uint256 derivedHalf1(hashed.ToString().substr(64, 64));
90  uint256 derivedHalf2(hashed.ToString().substr(0, 64));
91 
92  //block1 = (pointb[1...16] xor derivedhalf1[0...15])
93  uint256 block1 = uint256((privKey << 128) ^ (derivedHalf1 << 128)) >> 128;
94 
95  //encrypt part 1
96  uint512 encrypted1;
97  AES_KEY key;
98  AES_set_encrypt_key(derivedHalf2.begin(), 256, &key);
99  AES_encrypt(block1.begin(), encrypted1.begin(), &key);
100 
101  //block2 = (pointb[17...32] xor derivedhalf1[16...31]
102  uint256 p2 = privKey >> 128;
103  uint256 dh12 = derivedHalf1 >> 128;
104  uint256 block2 = uint256(p2 ^ dh12);
105 
106  //encrypt part 2
107  uint512 encrypted2;
108  AES_encrypt(block2.begin(), encrypted2.begin(), &key);
109 
110  std::string strPrefix = "0142";
111  strPrefix += (fCompressed ? "E0" : "C0");
112 
113  uint512 encryptedKey(ReverseEndianString(strPrefix + strAddressHash));
114 
115  //add encrypted1 to the end of encryptedKey
116  encryptedKey = encryptedKey | (encrypted1 << 56);
117 
118  //add encrypted2 to the end of encryptedKey
119  encryptedKey = encryptedKey | (encrypted2 << (56 + 128));
120 
121  //Base58 checksum is the 4 bytes of dSHA256 hash of the encrypted key
122  uint256 hashChecksum = Hash(encryptedKey.begin(), encryptedKey.begin() + 39);
123  uint512 b58Checksum(hashChecksum.ToString().substr(64 - 8, 8));
124 
125  // append the encrypted key with checksum (currently occupies 312 bits)
126  encryptedKey = encryptedKey | (b58Checksum << 312);
127 
128  //43 bytes is the total size that we are encoding
129  return EncodeBase58(encryptedKey.begin(), encryptedKey.begin() + 43);
130 }
131 
132 bool BIP38_Decrypt(std::string strPassphrase, std::string strEncryptedKey, uint256& privKey, bool& fCompressed)
133 {
134  std::string strKey = DecodeBase58(strEncryptedKey.c_str());
135 
136  //incorrect encoding of key, it must be 39 bytes - and another 4 bytes for base58 checksum
137  if (strKey.size() != (78 + 8))
138  return false;
139 
140  //invalid prefix
141  if (uint256(ReverseEndianString(strKey.substr(0, 2))) != uint256(0x01))
142  return false;
143 
144  uint256 type(ReverseEndianString(strKey.substr(2, 2)));
145  uint256 flag(ReverseEndianString(strKey.substr(4, 2)));
146  std::string strAddressHash = strKey.substr(6, 8);
147  std::string ownersalt = strKey.substr(14, 16);
148  uint256 encryptedPart1(ReverseEndianString(strKey.substr(30, 16)));
149  uint256 encryptedPart2(ReverseEndianString(strKey.substr(46, 32)));
150 
151  fCompressed = (flag & uint256(0x20)) != 0;
152 
153  //not ec multiplied
154  if (type == uint256(0x42)) {
155  uint512 hashed;
156  encryptedPart1 = uint256(ReverseEndianString(strKey.substr(14, 32)));
157  uint64_t salt = uint256S(ReverseEndianString(strAddressHash)).GetCheapHash();
158  scrypt_hash(strPassphrase.c_str(), strPassphrase.size(), BEGIN(salt), strAddressHash.size() / 2, BEGIN(hashed), 16384, 8, 8, 64);
159 
160  uint256 derivedHalf1(uint256S(hashed.ToString().substr(64, 64)));
161  uint256 derivedHalf2(uint256S(hashed.ToString().substr(0, 64)));
162 
163  uint256 decryptedPart1;
164  DecryptAES(encryptedPart1, derivedHalf2, decryptedPart1);
165 
166  uint256 decryptedPart2;
167  DecryptAES(encryptedPart2, derivedHalf2, decryptedPart2);
168 
169  //combine decrypted parts into 64 bytes
170  uint256 temp1 = decryptedPart2 << 128;
171  temp1 = temp1 | decryptedPart1;
172 
173  //xor the decryption with the derived half 1 for the final key
174  privKey = temp1 ^ derivedHalf1;
175 
176  return true;
177  } else if (type != uint256(0x43)) //invalid type
178  return false;
179 
180  bool fLotSequence = (flag & 0x04) != 0;
181 
182  std::string prefactorSalt = ownersalt;
183  if (fLotSequence)
184  prefactorSalt = ownersalt.substr(0, 8);
185 
186  uint256 prefactor;
187  ComputePreFactor(strPassphrase, prefactorSalt, prefactor);
188 
189  uint256 passfactor;
190  if (fLotSequence)
191  ComputePassfactor(ownersalt, prefactor, passfactor);
192  else
193  passfactor = prefactor;
194 
195  CPubKey passpoint;
196  if (!ComputePasspoint(passfactor, passpoint))
197  return false;
198 
199  uint512 seedBPass;
200  ComputeSeedBPass(passpoint, strAddressHash, ownersalt, seedBPass);
201 
202  //get derived halfs, being mindful for endian switch
203  uint256 derivedHalf1(uint256S(seedBPass.ToString().substr(64, 64)));
204  uint256 derivedHalf2(uint256S(seedBPass.ToString().substr(0, 64)));
205 
207  uint256 decryptedPart2;
208  DecryptAES(encryptedPart2, derivedHalf2, decryptedPart2);
209 
210  //xor decryptedPart2 and 2nd half of derived half 1
211  uint256 x0 = derivedHalf1 >> 128; //drop off the first half (note: endian)
212  uint256 x1 = decryptedPart2 ^ x0;
213  uint256 seedbPart2 = x1 >> 64;
214 
216  uint256 decryptedPart1;
217  uint256 x2 = x1 & uint256("0xffffffffffffffff"); // set x2 to seedbPart1 (still encrypted)
218  x2 = x2 << 64; //make room to add encryptedPart1 to the front
219  x2 = encryptedPart1 | x2; //combine with encryptedPart1
220  DecryptAES(x2, derivedHalf2, decryptedPart1);
221 
222  //decrypted part 1: seedb[0..15] xor derivedhalf1[0..15]
223  uint256 x3 = derivedHalf1 & uint256("0xffffffffffffffffffffffffffffffff");
224  uint256 seedbPart1 = decryptedPart1 ^ x3;
225  uint256 seedB = seedbPart1 | (seedbPart2 << 128);
226 
227  uint256 factorB;
228  ComputeFactorB(seedB, factorB);
229 
230  //multiply passfactor by factorb mod N to yield the priv key
231  privKey = factorB;
232  if (!secp256k1_ec_privkey_tweak_mul(privKey.begin(), passfactor.begin()))
233  return false;
234 
235  //double check that the address hash matches our final privkey
236  CKey k;
237  k.Set(privKey.begin(), privKey.end(), fCompressed);
238  CPubKey pubkey = k.GetPubKey();
239  std::string address = CBitcoinAddress(pubkey.GetID()).ToString();
240 
241  return strAddressHash == AddressToBip38Hash(address);
242 }
ReverseEndianString
std::string ReverseEndianString(std::string in)
Reverse the endianess of a string.
Definition: utilstrencodings.h:109
base_uint::end
unsigned char * end()
Definition: arith_uint256.h:245
BEGIN
#define BEGIN(a)
Utilities for converting data from/to strings.
Definition: utilstrencodings.h:17
secp256k1_ec_privkey_tweak_mul
SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_mul(unsigned char *seckey, const unsigned char *tweak) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2)
Tweak a private key by multiplying it with tweak.
Definition: secp256k1.c:262
base_uint::begin
unsigned char * begin()
Definition: arith_uint256.h:240
CBitcoinAddress
base58-encoded PRCY addresses.
Definition: base58.h:109
CKey::Set
void Set(const T pbegin, const T pend, bool fCompressedIn)
Initialize using begin and end iterators to byte data.
Definition: key.h:83
scrypt_hash
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:83
ComputeFactorB
void ComputeFactorB(uint256 seedB, uint256 &factorB)
Definition: bip38.cpp:65
EncodeBase58
std::string EncodeBase58(const unsigned char *pbegin, const unsigned char *pend)
Why base-58 instead of standard base-64 encoding?
Definition: base58.cpp:83
pubkey.h
DecodeBase58
bool DecodeBase58(const char *psz, std::vector< unsigned char > &vch)
Decode a base58-encoded string (psz) into a byte vector (vchRet).
Definition: base58.cpp:22
CBase58Data::ToString
std::string ToString() const
Definition: base58.cpp:200
AddressToBip38Hash
std::string AddressToBip38Hash(std::string address)
Definition: bip38.cpp:72
UBEGIN
#define UBEGIN(a)
Definition: utilstrencodings.h:19
base_uint::GetCheapHash
uint64_t GetCheapHash() const
Definition: arith_uint256.h:304
secp256k1.h
DecryptAES
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:28
ComputePasspoint
bool ComputePasspoint(uint256 passfactor, CPubKey &passpoint)
Definition: bip38.cpp:50
secp256k1_ec_pubkey_create
SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_create(unsigned char *pubkey, int *pubkeylen, const unsigned char *seckey, int compressed) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3)
Compute the public key for a secret key.
Definition: secp256k1.c:191
HexStr
std::string HexStr(const T itbegin, const T itend, bool fSpaces=false)
Definition: utilstrencodings.h:85
ComputeSeedBPass
void ComputeSeedBPass(CPubKey passpoint, std::string strAddressHash, std::string strOwnerSalt, uint512 &seedBPass)
Definition: bip38.cpp:57
uint256
256-bit unsigned big integer.
Definition: uint256.h:38
uint256S
uint256 uint256S(const char *str)
Definition: uint256.h:99
CKey::GetPubKey
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:79
CPubKey
An encapsulated public key.
Definition: pubkey.h:37
CKey
An encapsulated private key.
Definition: key.h:39
key
CKey key
Definition: bip38tooldialog.cpp:173
BIP38_Encrypt
std::string BIP38_Encrypt(std::string strAddress, std::string strPassphrase, uint256 privKey, bool fCompressed)
Definition: bip38.cpp:81
hash.h
base58.h
utilstrencodings.h
ComputePreFactor
void ComputePreFactor(std::string strPassphrase, std::string strSalt, uint256 &prefactor)
Definition: bip38.cpp:35
uint512
512-bit unsigned big integer.
Definition: uint256.h:73
Hash
std::string Hash(std::string input)
Compute the 256-bit hash of a std::string.
Definition: hash.h:122
BIP38_Decrypt
bool BIP38_Decrypt(std::string strPassphrase, std::string strEncryptedKey, uint256 &privKey, bool &fCompressed)
Definition: bip38.cpp:132
util.h
ComputePassfactor
void ComputePassfactor(std::string ownersalt, uint256 prefactor, uint256 &passfactor)
Definition: bip38.cpp:42
CPubKey::GetID
CKeyID GetID() const
Get the KeyID of this public key (hash of its serialization)
Definition: pubkey.h:143
bip38.h
base_uint::ToString
std::string ToString() const
Definition: arith_uint256.cpp:199