Add tx secret key via device layer

We add the tx secret key to the tx_extra in staking transactions so that
values can be decoded, but the tx secret key value that we have on hand
is encrypted and so we can't access it.

This moves the call that adds the secret key into the device code so
that devices can provide this.  It also adds the tx version/type earlier
in the process (into `open_tx`) so that the device can know early on
that this is a stake transaction and therefore that leaking the tx
secret key is okay (and can also apply other stake-specific behaviour).
This commit is contained in:
Jason Rhinelander 2020-12-04 11:56:46 -04:00
parent 052d012745
commit ff26b83b45
7 changed files with 42 additions and 9 deletions

View file

@ -637,8 +637,10 @@ namespace cryptonote
tx.extra = extra;
crypto::public_key txkey_pub;
if (tx.type == txtype::stake)
add_tx_secret_key_to_tx_extra(tx.extra, tx_key);
if (tx.type == txtype::stake) {
bool added = hwdev.add_tx_secret_key_to_tx_extra(tx.extra, tx_key);
CHECK_AND_NO_ASSERT_MES(added, false, "Failed to add tx secret key to stake transaction");
}
// if we have a stealth payment id, find it and encrypt it with the tx key now
std::vector<tx_extra_field> tx_extra_fields;
@ -1002,7 +1004,9 @@ namespace cryptonote
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const std::optional<cryptonote::tx_destination_entry>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, const rct::RCTConfig &rct_config, rct::multisig_out *msout, loki_construct_tx_params const &tx_params)
{
hw::device &hwdev = sender_account_keys.get_device();
hwdev.open_tx(tx_key);
auto txversion = static_cast<uint8_t>(transaction::get_max_version_for_hf(tx_params.hf_version));
auto txtype = static_cast<uint8_t>(tx_params.tx_type);
hwdev.open_tx(tx_key, txversion, txtype);
try {
// figure out if we need to make additional tx pubkeys
size_t num_stdaddresses = 0;

View file

@ -204,7 +204,7 @@ namespace hw {
const crypto::public_key &R, const crypto::public_key &A, const std::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
crypto::signature &sig) = 0;
virtual bool open_tx(crypto::secret_key &tx_key) = 0;
virtual bool open_tx(crypto::secret_key &tx_key, uint8_t txversion, uint8_t txtype) = 0;
virtual void get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) = 0;
@ -240,6 +240,10 @@ namespace hw {
virtual bool clsag_hash(const rct::keyV &data, rct::key &hash) = 0;
virtual bool clsag_sign(const rct::key &c, const rct::key &a, const rct::key &p, const rct::key &z, const rct::key &mu_P, const rct::key &mu_C, rct::key &s) = 0;
// Retrieves the tx secret key from the device. `key` will be whatever we got back from the
// device, but for hardware devices that value may be encrypted or null.
virtual bool add_tx_secret_key_to_tx_extra(std::vector<uint8_t>& tx_extra, const crypto::secret_key& key) = 0;
virtual bool close_tx(void) = 0;
virtual bool has_ki_cold_sync(void) const { return false; }

View file

@ -281,7 +281,7 @@ namespace hw {
crypto::generate_tx_proof(prefix_hash, R, A, B, D, r, sig);
}
bool device_default::open_tx(crypto::secret_key &tx_key) {
bool device_default::open_tx(crypto::secret_key &tx_key, uint8_t /*version*/, uint8_t /*type*/) {
cryptonote::keypair txkey = cryptonote::keypair::generate(*this);
tx_key = txkey.sec;
return true;
@ -411,6 +411,12 @@ namespace hw {
return true;
}
bool device_default::add_tx_secret_key_to_tx_extra(std::vector<uint8_t>& tx_extra, const crypto::secret_key& key) {
cryptonote::add_tx_secret_key_to_tx_extra(tx_extra, key);
return true;
}
bool device_default::close_tx() {
return true;
}

View file

@ -112,7 +112,7 @@ namespace hw {
const crypto::public_key &R, const crypto::public_key &A, const std::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
crypto::signature &sig) override;
bool open_tx(crypto::secret_key &tx_key) override;
bool open_tx(crypto::secret_key &tx_key, uint8_t txversion, uint8_t txtype) override;
void get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) override;
bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key) override;
@ -142,6 +142,8 @@ namespace hw {
bool clsag_hash(const rct::keyV &data, rct::key &hash) override;
bool clsag_sign(const rct::key &c, const rct::key &a, const rct::key &p, const rct::key &z, const rct::key &mu_P, const rct::key &mu_C, rct::key &s) override;
bool add_tx_secret_key_to_tx_extra(std::vector<uint8_t>& tx_extra, const crypto::secret_key& key) override;
bool close_tx(void) override;
};

View file

@ -281,6 +281,7 @@ namespace hw {
LEDGER_INS(OPEN_TX, 0x70);
LEDGER_INS(SET_SIGNATURE_MODE, 0x72);
LEDGER_INS(GET_ADDITIONAL_KEY, 0x74);
LEDGER_INS(GET_TX_SECRET_KEY, 0x75);
LEDGER_INS(STEALTH, 0x76);
LEDGER_INS(GEN_COMMITMENT_MASK, 0x77);
LEDGER_INS(BLIND, 0x78);
@ -1324,7 +1325,7 @@ namespace hw {
#endif
}
bool device_ledger::open_tx(crypto::secret_key &tx_key) {
bool device_ledger::open_tx(crypto::secret_key &tx_key, uint8_t txversion, uint8_t txtype) {
auto locks = tools::unique_locks(device_locker, command_locker, *this);
key_map.clear();
@ -1334,6 +1335,8 @@ namespace hw {
//account
send_u32(0, offset);
send_bytes(&txversion, 1, offset);
send_bytes(&txtype, 1, offset);
finish_and_exchange(offset);
@ -1900,6 +1903,18 @@ namespace hw {
return true;
}
bool device_ledger::add_tx_secret_key_to_tx_extra(std::vector<uint8_t>& tx_extra, const crypto::secret_key&) {
auto locks = tools::unique_locks(device_locker, command_locker);
// This will fail if this isn't an open stake tx.
send_simple(INS_GET_TX_SECRET_KEY);
crypto::secret_key key; // Don't need to use the one passed in, the ledger has it stored already in internal state
receive_bytes(key.data, 32);
cryptonote::add_tx_secret_key_to_tx_extra(tx_extra, key);
return true;
}
bool device_ledger::close_tx() {
auto locks = tools::unique_locks(device_locker, command_locker);

View file

@ -290,7 +290,7 @@ namespace hw {
const crypto::public_key &R, const crypto::public_key &A, const std::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
crypto::signature &sig) override;
bool open_tx(crypto::secret_key &tx_key) override;
bool open_tx(crypto::secret_key &tx_key, uint8_t tx_version, uint8_t tx_type) override;
void get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) override;
@ -321,6 +321,7 @@ namespace hw {
bool clsag_hash(const rct::keyV &data, rct::key &hash) override;
bool clsag_sign(const rct::key &c, const rct::key &a, const rct::key &p, const rct::key &z, const rct::key &mu_P, const rct::key &mu_C, rct::key &s) override;
bool add_tx_secret_key_to_tx_extra(std::vector<uint8_t>& tx_extra, const crypto::secret_key& key) override;
bool close_tx() override;

View file

@ -58,7 +58,8 @@ TEST(device, open_close)
{
hw::core::device_default dev;
crypto::secret_key key;
ASSERT_TRUE(dev.open_tx(key));
uint8_t version = 0, type = 0; // These get ignored for the default device so don't worry about them
ASSERT_TRUE(dev.open_tx(key, version, type));
ASSERT_TRUE(dev.close_tx());
}