Skip to content

Commit

Permalink
Use HMAC derivation for blinding keys
Browse files Browse the repository at this point in the history
  • Loading branch information
sipa committed Feb 15, 2016
1 parent 5daed69 commit c5904a9
Show file tree
Hide file tree
Showing 16 changed files with 373 additions and 103 deletions.
3 changes: 3 additions & 0 deletions src/base58.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,9 @@ bool CBitcoinAddress::Set(const CTxDestination& dest)

CBitcoinAddress& CBitcoinAddress::AddBlindingKey(const CPubKey& pubkey)
{
if (!pubkey.IsValid()) {
return *this;
}
assert(pubkey.size() == 33);
assert(!IsBlinded());
std::vector<unsigned char> data = vchVersion;
Expand Down
9 changes: 5 additions & 4 deletions src/bitcoin-tx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,11 +298,12 @@ static void MutateTxBlind(CMutableTransaction& tx, const string& strInput)

bool fBlindedIns = false;
bool fBlindedOuts = false;
std::vector<std::vector<unsigned char> > input_blinds;
std::vector<std::vector<unsigned char> > output_blinds;
std::vector<uint256> input_blinds;
std::vector<uint256> output_blinds;
std::vector<CPubKey> output_pubkeys;
for (size_t nIn = 0; nIn < tx.vin.size(); nIn++) {
std::vector<unsigned char> blind = ParseHex(input_blinding_factors[nIn]);
uint256 blind;
blind.SetHex(input_blinding_factors[nIn]);
if (blind.size() == 0) {
input_blinds.push_back(blind);
} else if (blind.size() == 32) {
Expand All @@ -323,7 +324,7 @@ static void MutateTxBlind(CMutableTransaction& tx, const string& strInput)
output_pubkeys.push_back(pubkey);
fBlindedOuts = true;
}
output_blinds.push_back(std::vector<unsigned char>(0, 0));
output_blinds.push_back(uint256());
}

if (fBlindedIns && !fBlindedOuts) {
Expand Down
36 changes: 17 additions & 19 deletions src/blind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,33 +34,32 @@ const secp256k1_context* ECC_Blinding_Context() {
return secp256k1_blind_context;
}

int UnblindOutput(const CKey &key, const CTxOut& txout, CAmount& amount_out, std::vector<unsigned char>& blinding_factor_out)
bool UnblindOutput(const CKey &key, const CTxOut& txout, CAmount& amount_out, uint256& blinding_factor_out)
{
if (txout.nValue.IsAmount()) {
amount_out = txout.nValue.GetAmount();
blinding_factor_out.resize(0);
return -1;
if (!key.IsValid()) {
return false;
}
CPubKey ephemeral_key(txout.nValue.vchNonceCommitment);
if (!ephemeral_key.IsValid()) {
return 0;
return false;
}
uint256 nonce = key.ECDH(ephemeral_key);
CSHA256().Write(nonce.begin(), 32).Finalize(nonce.begin());
unsigned char msg[4096];
int msg_size;
uint64_t min_value, max_value, amount;
blinding_factor_out.resize(32);
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());
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());
if (!res || amount > (uint64_t)MAX_MONEY || !MoneyRange((CAmount)amount)) {
amount_out = 0;
blinding_factor_out.resize(0);
} else
blinding_factor_out = uint256();
return false;
} else {
amount_out = (CAmount)amount;
return res ? 1 : 0;
return true;
}
}

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)
void BlindOutputs(const std::vector<uint256 >& input_blinding_factors, const std::vector<uint256 >& output_blinding_factors, const std::vector<CPubKey>& output_pubkeys, CMutableTransaction& tx)
{
assert(tx.vout.size() == output_blinding_factors.size());
assert(tx.vout.size() == output_pubkeys.size());
Expand All @@ -71,20 +70,19 @@ void BlindOutputs(const std::vector<std::vector<unsigned char> >& input_blinding

int nBlindsIn = 0;
for (size_t nIn = 0; nIn < tx.vin.size(); nIn++) {
if (input_blinding_factors[nIn].size() != 0) {
if (input_blinding_factors[nIn] != uint256()) {
assert(input_blinding_factors[nIn].size() == 32);
blindptrs.push_back(&input_blinding_factors[nIn][0]);
blindptrs.push_back(input_blinding_factors[nIn].begin());
nBlindsIn++;
}
}

int nBlindsOut = 0;
int nToBlind = 0;
for (size_t nOut = 0; nOut < tx.vout.size(); nOut++) {
assert((output_blinding_factors[nOut].size() != 0) == !tx.vout[nOut].nValue.IsAmount());
if (output_blinding_factors[nOut].size() != 0) {
assert(output_blinding_factors[nOut].size() == 32);
blindptrs.push_back(&output_blinding_factors[nOut][0]);
assert((output_blinding_factors[nOut] != uint256()) == !tx.vout[nOut].nValue.IsAmount());
if (output_blinding_factors[nOut] != uint256()) {
blindptrs.push_back(output_blinding_factors[nOut].begin());
nBlindsOut++;
} else {
if (output_pubkeys[nOut].IsValid()) {
Expand All @@ -98,7 +96,7 @@ void BlindOutputs(const std::vector<std::vector<unsigned char> >& input_blinding
}

int nBlinded = 0;
unsigned char blind[nToBlind][32];
unsigned char blind[tx.vout.size()][32];

for (size_t nOut = 0; nOut < tx.vout.size(); nOut++) {
if (tx.vout[nOut].nValue.IsAmount() && output_pubkeys[nOut].IsValid()) {
Expand Down
4 changes: 2 additions & 2 deletions src/blind.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
void ECC_Blinding_Start();
void ECC_Blinding_Stop();

int UnblindOutput(const CKey& blinding_key, const CTxOut& txout, CAmount& amount_out, std::vector<unsigned char>& blinding_factor_out);
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);
bool UnblindOutput(const CKey& blinding_key, const CTxOut& txout, CAmount& amount_out, uint256& blinding_factor_out);
void BlindOutputs(const std::vector<uint256>& input_blinding_factors, const std::vector<uint256>& output_blinding_factors, const std::vector<CPubKey>& output_pubkeys, CMutableTransaction& tx);

#endif
2 changes: 1 addition & 1 deletion src/qt/addresstablemodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
return QString();
}
}
strAddress = CBitcoinAddress(newKey.GetID()).AddBlindingKey(wallet->blinding_pubkey).ToString();
strAddress = CBitcoinAddress(newKey.GetID()).AddBlindingKey(wallet->GetBlindingPubKey(GetScriptForDestination(CTxDestination(newKey.GetID())))).ToString();
}
else
{
Expand Down
82 changes: 82 additions & 0 deletions src/rpcdump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -397,3 +397,85 @@ Value dumpwallet(const Array& params, bool fHelp)
file.close();
return Value::null;
}

Value dumpblindingkey(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 1)
throw runtime_error(
"dumpblindingkey \"address\"\n"
"\nDumps the private blinding key for a CT address in hex."
"\nArguments:\n"
"1. \"address\" (string, required) The CT address\n"
);

CBitcoinAddress address(params[0].get_str());
if (!address.IsValid()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
}
if (!address.IsBlinded()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Not a CT address");
}

CTxDestination dest = address.Get();
CScript script = GetScriptForDestination(dest);
CKey key;
key = pwalletMain->GetBlindingKey(&script);
if (key.IsValid()) {
CPubKey pubkey = key.GetPubKey();
if (pubkey == address.GetBlindingKey()) {
return HexStr(key.begin(), key.end());
}
}
// Just for backward compatibility
key = pwalletMain->GetBlindingKey(NULL);
if (key.IsValid()) {
CPubKey pubkey = key.GetPubKey();
if (pubkey == address.GetBlindingKey()) {
return HexStr(key.begin(), key.end());
}
}
throw JSONRPCError(RPC_WALLET_ERROR, "Blinding key for address is unknown");
}

Value importblindingkey(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 2 || params.size() > 2)
throw runtime_error(
"importblindingkey \"address\" \"blindinghex\"\n"
"\nImports a private blinding key in hex for a CT address."
"\nArguments:\n"
"1. \"address\" (string, required) The CT address\n"
"2. \"hexkey\" (string, required) The blinding key in hex\n"
);

CBitcoinAddress address(params[0].get_str());
if (!address.IsValid()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address or script");
}
if (!address.IsBlinded()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Not a CT address");
}

if (!IsHex(params[1].get_str())) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid hexadecimal for key");
}
std::vector<unsigned char> keydata = ParseHex(params[1].get_str());
if (keydata.size() != 32) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid hexadecimal key length");
}

CKey key;
key.Set(keydata.begin(), keydata.end(), true);
if (!key.IsValid() || key.GetPubKey() != address.GetBlindingKey()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Address and key do not match");
}

uint256 keyval;
memcpy(keyval.begin(), &keydata[0], 32);
if (!pwalletMain->AddSpecificBlindingKey(CScriptID(GetScriptForDestination(address.Get())), keyval)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Failed to import blinding key");
}
pwalletMain->MarkDirty();

return Value::null;
}
5 changes: 3 additions & 2 deletions src/rpcmisc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,12 +196,13 @@ Value validateaddress(const Array& params, bool fHelp)
}
#ifdef ENABLE_WALLET
isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : ISMINE_NO;
if (address.IsBlinded() && address.GetBlindingKey() != pwalletMain->blinding_pubkey) {
if (mine != ISMINE_NO && address.IsBlinded() && address.GetBlindingKey() != pwalletMain->GetBlindingPubKey(GetScriptForDestination(dest))) {
// Note: this will fail to return ismine for deprecated static blinded addresses.
mine = ISMINE_NO;
}
ret.push_back(Pair("ismine", (mine & ISMINE_SPENDABLE) ? true : false));
if (!address.IsBlinded() && mine != ISMINE_NO) {
ret.push_back(Pair("confidential", address.AddBlindingKey(pwalletMain->blinding_pubkey).ToString()));
ret.push_back(Pair("confidential", address.AddBlindingKey(pwalletMain->GetBlindingPubKey(GetScriptForDestination(dest))).ToString()));
}
if (mine != ISMINE_NO) {
ret.push_back(Pair("iswatchonly", (mine & ISMINE_WATCH_ONLY) ? true: false));
Expand Down
18 changes: 9 additions & 9 deletions src/rpcrawtransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ Value listunspent(const Array& params, bool fHelp)
if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) {
CBitcoinAddress addr(address);
if (out.tx->GetBlindingFactor(out.i).size() > 0) {
addr.AddBlindingKey(pwalletMain->blinding_pubkey);
addr.AddBlindingKey(out.tx->GetBlindingKey(out.i));
}
entry.push_back(Pair("address", addr.ToString()));
if (pwalletMain->mapAddressBook.count(address))
Expand Down Expand Up @@ -588,8 +588,8 @@ Value rawblindrawtransaction(const Array& params, bool fHelp)

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"));

std::vector<std::vector<unsigned char> > input_blinds;
std::vector<std::vector<unsigned char> > output_blinds;
std::vector<uint256 > input_blinds;
std::vector<uint256 > output_blinds;
std::vector<CPubKey> output_pubkeys;
for (size_t nOut = 0; nOut < tx.vout.size(); nOut++) {
if (!tx.vout[nOut].nValue.IsAmount())
Expand All @@ -603,7 +603,7 @@ Value rawblindrawtransaction(const Array& params, bool fHelp)
}
output_pubkeys.push_back(pubkey);
}
output_blinds.push_back(std::vector<unsigned char>(0, 0));
output_blinds.push_back(uint256());
}

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

LOCK(pwalletMain->cs_wallet);

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

for (size_t nOut = 0; nOut < tx.vout.size(); nOut++) {
if (!tx.vout[nOut].nValue.IsAmount()) {
std::vector<unsigned char> blinding_factor;
uint256 blinding_factor;
CAmount amount;
if (UnblindOutput(pwalletMain->blinding_key, tx.vout[nOut], amount, blinding_factor) != 0) {
output_blinds.push_back(blinding_factor);
Expand All @@ -675,14 +675,14 @@ Value blindrawtransaction(const Array& params, bool fHelp)
}
} else if (tx.vout[nOut].nValue.vchNonceCommitment.size() == 0) {
output_pubkeys.push_back(CPubKey());
output_blinds.push_back(std::vector<unsigned char>(0, 0));
output_blinds.push_back(uint256());
} else {
CPubKey pubkey(tx.vout[nOut].nValue.vchNonceCommitment);
if (!pubkey.IsValid()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter: invalid confidentiality public key given"));
}
output_pubkeys.push_back(pubkey);
output_blinds.push_back(std::vector<unsigned char>(0, 0));
output_blinds.push_back(uint256());
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/rpcserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ static const CRPCCommand vRPCCommands[] =
{ "wallet", "backupwallet", &backupwallet, true, false, true },
{ "wallet", "dumpprivkey", &dumpprivkey, true, false, true },
{ "wallet", "dumpwallet", &dumpwallet, true, false, true },
{ "wallet", "dumpblindingkey", &dumpblindingkey, true, false, true },
{ "wallet", "encryptwallet", &encryptwallet, true, false, true },
{ "wallet", "getaccountaddress", &getaccountaddress, true, false, true },
{ "wallet", "getaccount", &getaccount, true, false, true },
Expand All @@ -336,6 +337,7 @@ static const CRPCCommand vRPCCommands[] =
{ "wallet", "importprivkey", &importprivkey, true, false, true },
{ "wallet", "importwallet", &importwallet, true, false, true },
{ "wallet", "importaddress", &importaddress, true, false, true },
{ "wallet", "importblindingkey", &importblindingkey, true, false, true },
{ "wallet", "keypoolrefill", &keypoolrefill, true, false, true },
{ "wallet", "listaccounts", &listaccounts, false, false, true },
{ "wallet", "listaddressgroupings", &listaddressgroupings, false, false, true },
Expand Down
2 changes: 2 additions & 0 deletions src/rpcserver.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ extern json_spirit::Value importprivkey(const json_spirit::Array& params, bool f
extern json_spirit::Value importaddress(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value dumpwallet(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value importwallet(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value dumpblindingkey(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value importblindingkey(const json_spirit::Array& params, bool fHelp);

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

0 comments on commit c5904a9

Please sign in to comment.