Skip to content

Commit c5904a9

Browse files
committed
Use HMAC derivation for blinding keys
1 parent 5daed69 commit c5904a9

16 files changed

+373
-103
lines changed

src/base58.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,9 @@ bool CBitcoinAddress::Set(const CTxDestination& dest)
237237

238238
CBitcoinAddress& CBitcoinAddress::AddBlindingKey(const CPubKey& pubkey)
239239
{
240+
if (!pubkey.IsValid()) {
241+
return *this;
242+
}
240243
assert(pubkey.size() == 33);
241244
assert(!IsBlinded());
242245
std::vector<unsigned char> data = vchVersion;

src/bitcoin-tx.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -298,11 +298,12 @@ static void MutateTxBlind(CMutableTransaction& tx, const string& strInput)
298298

299299
bool fBlindedIns = false;
300300
bool fBlindedOuts = false;
301-
std::vector<std::vector<unsigned char> > input_blinds;
302-
std::vector<std::vector<unsigned char> > output_blinds;
301+
std::vector<uint256> input_blinds;
302+
std::vector<uint256> output_blinds;
303303
std::vector<CPubKey> output_pubkeys;
304304
for (size_t nIn = 0; nIn < tx.vin.size(); nIn++) {
305-
std::vector<unsigned char> blind = ParseHex(input_blinding_factors[nIn]);
305+
uint256 blind;
306+
blind.SetHex(input_blinding_factors[nIn]);
306307
if (blind.size() == 0) {
307308
input_blinds.push_back(blind);
308309
} else if (blind.size() == 32) {
@@ -323,7 +324,7 @@ static void MutateTxBlind(CMutableTransaction& tx, const string& strInput)
323324
output_pubkeys.push_back(pubkey);
324325
fBlindedOuts = true;
325326
}
326-
output_blinds.push_back(std::vector<unsigned char>(0, 0));
327+
output_blinds.push_back(uint256());
327328
}
328329

329330
if (fBlindedIns && !fBlindedOuts) {

src/blind.cpp

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -34,33 +34,32 @@ const secp256k1_context* ECC_Blinding_Context() {
3434
return secp256k1_blind_context;
3535
}
3636

37-
int UnblindOutput(const CKey &key, const CTxOut& txout, CAmount& amount_out, std::vector<unsigned char>& blinding_factor_out)
37+
bool UnblindOutput(const CKey &key, const CTxOut& txout, CAmount& amount_out, uint256& blinding_factor_out)
3838
{
39-
if (txout.nValue.IsAmount()) {
40-
amount_out = txout.nValue.GetAmount();
41-
blinding_factor_out.resize(0);
42-
return -1;
39+
if (!key.IsValid()) {
40+
return false;
4341
}
4442
CPubKey ephemeral_key(txout.nValue.vchNonceCommitment);
4543
if (!ephemeral_key.IsValid()) {
46-
return 0;
44+
return false;
4745
}
4846
uint256 nonce = key.ECDH(ephemeral_key);
4947
CSHA256().Write(nonce.begin(), 32).Finalize(nonce.begin());
5048
unsigned char msg[4096];
5149
int msg_size;
5250
uint64_t min_value, max_value, amount;
53-
blinding_factor_out.resize(32);
54-
int res = secp256k1_rangeproof_rewind(secp256k1_blind_context, &blinding_factor_out[0], &amount, msg, &msg_size, nonce.begin(), &min_value, &max_value, &txout.nValue.vchCommitment[0], &txout.nValue.vchRangeproof[0], txout.nValue.vchRangeproof.size());
51+
int res = secp256k1_rangeproof_rewind(secp256k1_blind_context, blinding_factor_out.begin(), &amount, msg, &msg_size, nonce.begin(), &min_value, &max_value, &txout.nValue.vchCommitment[0], &txout.nValue.vchRangeproof[0], txout.nValue.vchRangeproof.size());
5552
if (!res || amount > (uint64_t)MAX_MONEY || !MoneyRange((CAmount)amount)) {
5653
amount_out = 0;
57-
blinding_factor_out.resize(0);
58-
} else
54+
blinding_factor_out = uint256();
55+
return false;
56+
} else {
5957
amount_out = (CAmount)amount;
60-
return res ? 1 : 0;
58+
return true;
59+
}
6160
}
6261

63-
void BlindOutputs(const std::vector<std::vector<unsigned char> >& input_blinding_factors, const std::vector<std::vector<unsigned char> >& output_blinding_factors, const std::vector<CPubKey>& output_pubkeys, CMutableTransaction& tx)
62+
void BlindOutputs(const std::vector<uint256 >& input_blinding_factors, const std::vector<uint256 >& output_blinding_factors, const std::vector<CPubKey>& output_pubkeys, CMutableTransaction& tx)
6463
{
6564
assert(tx.vout.size() == output_blinding_factors.size());
6665
assert(tx.vout.size() == output_pubkeys.size());
@@ -71,20 +70,19 @@ void BlindOutputs(const std::vector<std::vector<unsigned char> >& input_blinding
7170

7271
int nBlindsIn = 0;
7372
for (size_t nIn = 0; nIn < tx.vin.size(); nIn++) {
74-
if (input_blinding_factors[nIn].size() != 0) {
73+
if (input_blinding_factors[nIn] != uint256()) {
7574
assert(input_blinding_factors[nIn].size() == 32);
76-
blindptrs.push_back(&input_blinding_factors[nIn][0]);
75+
blindptrs.push_back(input_blinding_factors[nIn].begin());
7776
nBlindsIn++;
7877
}
7978
}
8079

8180
int nBlindsOut = 0;
8281
int nToBlind = 0;
8382
for (size_t nOut = 0; nOut < tx.vout.size(); nOut++) {
84-
assert((output_blinding_factors[nOut].size() != 0) == !tx.vout[nOut].nValue.IsAmount());
85-
if (output_blinding_factors[nOut].size() != 0) {
86-
assert(output_blinding_factors[nOut].size() == 32);
87-
blindptrs.push_back(&output_blinding_factors[nOut][0]);
83+
assert((output_blinding_factors[nOut] != uint256()) == !tx.vout[nOut].nValue.IsAmount());
84+
if (output_blinding_factors[nOut] != uint256()) {
85+
blindptrs.push_back(output_blinding_factors[nOut].begin());
8886
nBlindsOut++;
8987
} else {
9088
if (output_pubkeys[nOut].IsValid()) {
@@ -98,7 +96,7 @@ void BlindOutputs(const std::vector<std::vector<unsigned char> >& input_blinding
9896
}
9997

10098
int nBlinded = 0;
101-
unsigned char blind[nToBlind][32];
99+
unsigned char blind[tx.vout.size()][32];
102100

103101
for (size_t nOut = 0; nOut < tx.vout.size(); nOut++) {
104102
if (tx.vout[nOut].nValue.IsAmount() && output_pubkeys[nOut].IsValid()) {

src/blind.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
void ECC_Blinding_Start();
99
void ECC_Blinding_Stop();
1010

11-
int UnblindOutput(const CKey& blinding_key, const CTxOut& txout, CAmount& amount_out, std::vector<unsigned char>& blinding_factor_out);
12-
void BlindOutputs(const std::vector<std::vector<unsigned char> >& input_blinding_factors, const std::vector<std::vector<unsigned char> >& output_blinding_factors, const std::vector<CPubKey>& output_pubkeys, CMutableTransaction& tx);
11+
bool UnblindOutput(const CKey& blinding_key, const CTxOut& txout, CAmount& amount_out, uint256& blinding_factor_out);
12+
void BlindOutputs(const std::vector<uint256>& input_blinding_factors, const std::vector<uint256>& output_blinding_factors, const std::vector<CPubKey>& output_pubkeys, CMutableTransaction& tx);
1313

1414
#endif

src/qt/addresstablemodel.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
383383
return QString();
384384
}
385385
}
386-
strAddress = CBitcoinAddress(newKey.GetID()).AddBlindingKey(wallet->blinding_pubkey).ToString();
386+
strAddress = CBitcoinAddress(newKey.GetID()).AddBlindingKey(wallet->GetBlindingPubKey(GetScriptForDestination(CTxDestination(newKey.GetID())))).ToString();
387387
}
388388
else
389389
{

src/rpcdump.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,3 +397,85 @@ Value dumpwallet(const Array& params, bool fHelp)
397397
file.close();
398398
return Value::null;
399399
}
400+
401+
Value dumpblindingkey(const Array& params, bool fHelp)
402+
{
403+
if (fHelp || params.size() < 1 || params.size() > 1)
404+
throw runtime_error(
405+
"dumpblindingkey \"address\"\n"
406+
"\nDumps the private blinding key for a CT address in hex."
407+
"\nArguments:\n"
408+
"1. \"address\" (string, required) The CT address\n"
409+
);
410+
411+
CBitcoinAddress address(params[0].get_str());
412+
if (!address.IsValid()) {
413+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
414+
}
415+
if (!address.IsBlinded()) {
416+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Not a CT address");
417+
}
418+
419+
CTxDestination dest = address.Get();
420+
CScript script = GetScriptForDestination(dest);
421+
CKey key;
422+
key = pwalletMain->GetBlindingKey(&script);
423+
if (key.IsValid()) {
424+
CPubKey pubkey = key.GetPubKey();
425+
if (pubkey == address.GetBlindingKey()) {
426+
return HexStr(key.begin(), key.end());
427+
}
428+
}
429+
// Just for backward compatibility
430+
key = pwalletMain->GetBlindingKey(NULL);
431+
if (key.IsValid()) {
432+
CPubKey pubkey = key.GetPubKey();
433+
if (pubkey == address.GetBlindingKey()) {
434+
return HexStr(key.begin(), key.end());
435+
}
436+
}
437+
throw JSONRPCError(RPC_WALLET_ERROR, "Blinding key for address is unknown");
438+
}
439+
440+
Value importblindingkey(const Array& params, bool fHelp)
441+
{
442+
if (fHelp || params.size() < 2 || params.size() > 2)
443+
throw runtime_error(
444+
"importblindingkey \"address\" \"blindinghex\"\n"
445+
"\nImports a private blinding key in hex for a CT address."
446+
"\nArguments:\n"
447+
"1. \"address\" (string, required) The CT address\n"
448+
"2. \"hexkey\" (string, required) The blinding key in hex\n"
449+
);
450+
451+
CBitcoinAddress address(params[0].get_str());
452+
if (!address.IsValid()) {
453+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address or script");
454+
}
455+
if (!address.IsBlinded()) {
456+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Not a CT address");
457+
}
458+
459+
if (!IsHex(params[1].get_str())) {
460+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid hexadecimal for key");
461+
}
462+
std::vector<unsigned char> keydata = ParseHex(params[1].get_str());
463+
if (keydata.size() != 32) {
464+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid hexadecimal key length");
465+
}
466+
467+
CKey key;
468+
key.Set(keydata.begin(), keydata.end(), true);
469+
if (!key.IsValid() || key.GetPubKey() != address.GetBlindingKey()) {
470+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Address and key do not match");
471+
}
472+
473+
uint256 keyval;
474+
memcpy(keyval.begin(), &keydata[0], 32);
475+
if (!pwalletMain->AddSpecificBlindingKey(CScriptID(GetScriptForDestination(address.Get())), keyval)) {
476+
throw JSONRPCError(RPC_WALLET_ERROR, "Failed to import blinding key");
477+
}
478+
pwalletMain->MarkDirty();
479+
480+
return Value::null;
481+
}

src/rpcmisc.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,12 +196,13 @@ Value validateaddress(const Array& params, bool fHelp)
196196
}
197197
#ifdef ENABLE_WALLET
198198
isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : ISMINE_NO;
199-
if (address.IsBlinded() && address.GetBlindingKey() != pwalletMain->blinding_pubkey) {
199+
if (mine != ISMINE_NO && address.IsBlinded() && address.GetBlindingKey() != pwalletMain->GetBlindingPubKey(GetScriptForDestination(dest))) {
200+
// Note: this will fail to return ismine for deprecated static blinded addresses.
200201
mine = ISMINE_NO;
201202
}
202203
ret.push_back(Pair("ismine", (mine & ISMINE_SPENDABLE) ? true : false));
203204
if (!address.IsBlinded() && mine != ISMINE_NO) {
204-
ret.push_back(Pair("confidential", address.AddBlindingKey(pwalletMain->blinding_pubkey).ToString()));
205+
ret.push_back(Pair("confidential", address.AddBlindingKey(pwalletMain->GetBlindingPubKey(GetScriptForDestination(dest))).ToString()));
205206
}
206207
if (mine != ISMINE_NO) {
207208
ret.push_back(Pair("iswatchonly", (mine & ISMINE_WATCH_ONLY) ? true: false));

src/rpcrawtransaction.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ Value listunspent(const Array& params, bool fHelp)
421421
if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) {
422422
CBitcoinAddress addr(address);
423423
if (out.tx->GetBlindingFactor(out.i).size() > 0) {
424-
addr.AddBlindingKey(pwalletMain->blinding_pubkey);
424+
addr.AddBlindingKey(out.tx->GetBlindingKey(out.i));
425425
}
426426
entry.push_back(Pair("address", addr.ToString()));
427427
if (pwalletMain->mapAddressBook.count(address))
@@ -588,8 +588,8 @@ Value rawblindrawtransaction(const Array& params, bool fHelp)
588588

589589
if (inputBlinds.size() != tx.vin.size()) throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter: one (potentially empty) input blind for each input must be provided"));
590590

591-
std::vector<std::vector<unsigned char> > input_blinds;
592-
std::vector<std::vector<unsigned char> > output_blinds;
591+
std::vector<uint256 > input_blinds;
592+
std::vector<uint256 > output_blinds;
593593
std::vector<CPubKey> output_pubkeys;
594594
for (size_t nOut = 0; nOut < tx.vout.size(); nOut++) {
595595
if (!tx.vout[nOut].nValue.IsAmount())
@@ -603,7 +603,7 @@ Value rawblindrawtransaction(const Array& params, bool fHelp)
603603
}
604604
output_pubkeys.push_back(pubkey);
605605
}
606-
output_blinds.push_back(std::vector<unsigned char>(0, 0));
606+
output_blinds.push_back(uint256());
607607
}
608608

609609
BlindOutputs(input_blinds, output_blinds, output_pubkeys, tx);
@@ -649,8 +649,8 @@ Value blindrawtransaction(const Array& params, bool fHelp)
649649

650650
LOCK(pwalletMain->cs_wallet);
651651

652-
std::vector<std::vector<unsigned char> > input_blinds;
653-
std::vector<std::vector<unsigned char> > output_blinds;
652+
std::vector<uint256> input_blinds;
653+
std::vector<uint256> output_blinds;
654654
std::vector<CPubKey> output_pubkeys;
655655
for (size_t nIn = 0; nIn < tx.vin.size(); nIn++) {
656656
std::map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.find(tx.vin[nIn].prevout.hash);
@@ -665,7 +665,7 @@ Value blindrawtransaction(const Array& params, bool fHelp)
665665

666666
for (size_t nOut = 0; nOut < tx.vout.size(); nOut++) {
667667
if (!tx.vout[nOut].nValue.IsAmount()) {
668-
std::vector<unsigned char> blinding_factor;
668+
uint256 blinding_factor;
669669
CAmount amount;
670670
if (UnblindOutput(pwalletMain->blinding_key, tx.vout[nOut], amount, blinding_factor) != 0) {
671671
output_blinds.push_back(blinding_factor);
@@ -675,14 +675,14 @@ Value blindrawtransaction(const Array& params, bool fHelp)
675675
}
676676
} else if (tx.vout[nOut].nValue.vchNonceCommitment.size() == 0) {
677677
output_pubkeys.push_back(CPubKey());
678-
output_blinds.push_back(std::vector<unsigned char>(0, 0));
678+
output_blinds.push_back(uint256());
679679
} else {
680680
CPubKey pubkey(tx.vout[nOut].nValue.vchNonceCommitment);
681681
if (!pubkey.IsValid()) {
682682
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter: invalid confidentiality public key given"));
683683
}
684684
output_pubkeys.push_back(pubkey);
685-
output_blinds.push_back(std::vector<unsigned char>(0, 0));
685+
output_blinds.push_back(uint256());
686686
}
687687
}
688688

src/rpcserver.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ static const CRPCCommand vRPCCommands[] =
321321
{ "wallet", "backupwallet", &backupwallet, true, false, true },
322322
{ "wallet", "dumpprivkey", &dumpprivkey, true, false, true },
323323
{ "wallet", "dumpwallet", &dumpwallet, true, false, true },
324+
{ "wallet", "dumpblindingkey", &dumpblindingkey, true, false, true },
324325
{ "wallet", "encryptwallet", &encryptwallet, true, false, true },
325326
{ "wallet", "getaccountaddress", &getaccountaddress, true, false, true },
326327
{ "wallet", "getaccount", &getaccount, true, false, true },
@@ -336,6 +337,7 @@ static const CRPCCommand vRPCCommands[] =
336337
{ "wallet", "importprivkey", &importprivkey, true, false, true },
337338
{ "wallet", "importwallet", &importwallet, true, false, true },
338339
{ "wallet", "importaddress", &importaddress, true, false, true },
340+
{ "wallet", "importblindingkey", &importblindingkey, true, false, true },
339341
{ "wallet", "keypoolrefill", &keypoolrefill, true, false, true },
340342
{ "wallet", "listaccounts", &listaccounts, false, false, true },
341343
{ "wallet", "listaddressgroupings", &listaddressgroupings, false, false, true },

src/rpcserver.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ extern json_spirit::Value importprivkey(const json_spirit::Array& params, bool f
150150
extern json_spirit::Value importaddress(const json_spirit::Array& params, bool fHelp);
151151
extern json_spirit::Value dumpwallet(const json_spirit::Array& params, bool fHelp);
152152
extern json_spirit::Value importwallet(const json_spirit::Array& params, bool fHelp);
153+
extern json_spirit::Value dumpblindingkey(const json_spirit::Array& params, bool fHelp);
154+
extern json_spirit::Value importblindingkey(const json_spirit::Array& params, bool fHelp);
153155

154156
extern json_spirit::Value getgenerate(const json_spirit::Array& params, bool fHelp); // in rpcmining.cpp
155157
extern json_spirit::Value setgenerate(const json_spirit::Array& params, bool fHelp);

0 commit comments

Comments
 (0)