Ledger: Add stake unlock signature support

This commit is contained in:
Jason Rhinelander 2020-12-06 23:17:07 -04:00
parent d60548ac0a
commit 07aad36120
8 changed files with 63 additions and 4 deletions

View file

@ -438,6 +438,19 @@ namespace cryptonote
crypto::signature signature;
uint32_t nonce; // TODO: remove this nonce value if we ever have to make other changes to this structure
// The value we sign when signing an unlock request. For backwards compatibility we send this as a
// "nonce" (although it isn't and never was a nonce), which is required to be an unsigned 32-bit
// value. We could just as easily sign with crypto::null_hash, but using a distinct value makes it
// slightly less likely that we could end up using the same message as some other signing process.
static constexpr crypto::hash HASH{
'U','N','L','K','U','N','L','K','U','N','L','K','U','N','L','K',
'U','N','L','K','U','N','L','K','U','N','L','K','U','N','L','K'};
// For now, we still have to send that (not a) "nonce" value in the unlock tx on the wire, but
// future HF versions could remove it from the wire (though at 4 bytes it isn't worth doing
// until we also need to make some other change to unlocks here). So for now, we always send
// this in `nonce`.
static constexpr uint32_t FAKE_NONCE = 0x4B4C4E55;
// Compares equal if this represents the same key image unlock (but does *not* require equality of signature/nonce)
bool operator==(const tx_extra_tx_key_image_unlock &other) const { return key_image == other.key_image; }

View file

@ -180,6 +180,7 @@ namespace hw {
virtual bool secret_key_to_public_key(const crypto::secret_key &sec, crypto::public_key &pub) = 0;
virtual bool generate_key_image(const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_image &image) = 0;
virtual bool generate_key_image_signature(const crypto::key_image& image, const crypto::public_key& pub, const crypto::secret_key& sec, crypto::signature& sig) = 0;
virtual bool generate_unlock_signature(const crypto::public_key& pub, const crypto::secret_key& sec, crypto::signature& sig) = 0;
// alternative prototypes available in libringct
rct::key scalarmultKey(const rct::key &P, const rct::key &a)

View file

@ -268,6 +268,11 @@ namespace hw {
return true;
}
bool device_default::generate_unlock_signature(const crypto::public_key& pub, const crypto::secret_key& sec, crypto::signature& sig) {
crypto::generate_signature(cryptonote::tx_extra_tx_key_image_unlock::HASH, pub, sec, sig);
return true;
}
bool device_default::conceal_derivation(crypto::key_derivation &derivation, const crypto::public_key &tx_pub_key, const std::vector<crypto::public_key> &additional_tx_pub_keys, const crypto::key_derivation &main_derivation, const std::vector<crypto::key_derivation> &additional_derivations){
return true;
}

View file

@ -102,6 +102,7 @@ namespace hw {
bool secret_key_to_public_key(const crypto::secret_key &sec, crypto::public_key &pub) override;
bool generate_key_image(const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_image &image) override;
bool generate_key_image_signature(const crypto::key_image& image, const crypto::public_key& pub, const crypto::secret_key& sec, crypto::signature& sig) override;
bool generate_unlock_signature(const crypto::public_key& pub, const crypto::secret_key& sec, crypto::signature& sig) override;
/* ======================================================================= */

View file

@ -293,6 +293,7 @@ namespace hw {
LEDGER_INS(CLOSE_TX, 0x80);
LEDGER_INS(GET_TX_PROOF, 0xA0);
LEDGER_INS(GEN_UNLOCK_SIGNATURE, 0xA2);
LEDGER_INS(GET_RESPONSE, 0xc0);
@ -1270,6 +1271,39 @@ namespace hw {
return true;
}
bool device_ledger::generate_unlock_signature(const crypto::public_key& pub, const crypto::secret_key& sec, crypto::signature& sig) {
auto locks = tools::unique_locks(device_locker, command_locker);
#ifdef DEBUG_HWDEVICE
log_hexbuffer("generate_unlock_signature: [[IN]] pub ", pub.data, 32);
const crypto::secret_key sec_x = hw::ledger::decrypt(sec);
log_hexbuffer("generate_unlock_signature: [[IN]] sec ", sec_x.data, 32);
#endif
// Ask for confirmation:
int offset = set_command_header_noopt(INS_GEN_UNLOCK_SIGNATURE);
CHECK_AND_ASSERT_THROW_MES(finish_and_exchange(offset, true) == SW_OK, "Unlock denied on device.");
// If we got permission then we can ask for the actual signature:
offset = set_command_header_noopt(INS_GEN_UNLOCK_SIGNATURE, 1);
send_bytes(pub.data, 32, offset);
send_secret(sec.data, offset);
finish_and_exchange(offset);
receive_bytes(reinterpret_cast<char*>(&sig), 64);
#ifdef DEBUG_HWDEVICE
// We can't check the actual returned signature byte values because a random component is
// involved, but we *can* attempt to verify the signature
bool good = crypto::check_signature(cryptonote::tx_extra_tx_key_image_unlock::HASH, pub, sig);
log_hexbuffer("generate_unlock_signature: signature.c", sig.c.data, 32);
log_hexbuffer("generate_unlock_signature: signature.r", sig.r.data, 32);
log_message("generate_unlock_signature: signature returned from device", good ? "passed" : "FAILED");
#endif
return true;
}
/* ======================================================================= */
/* TRANSACTION */
/* ======================================================================= */

View file

@ -282,6 +282,7 @@ namespace hw {
bool secret_key_to_public_key(const crypto::secret_key &sec, crypto::public_key &pub) override;
bool generate_key_image(const crypto::public_key &pub, const crypto::secret_key &sec, crypto::key_image &image) override;
bool generate_key_image_signature(const crypto::key_image& image, const crypto::public_key& pub, const crypto::secret_key& sec, crypto::signature& sig) override;
bool generate_unlock_signature(const crypto::public_key& pub, const crypto::secret_key& sec, crypto::signature& sig) override;
/* ======================================================================= */
/* TRANSACTION */

View file

@ -42,6 +42,7 @@
#include <lokimq/base64.h>
#include "common/password.h"
#include "common/string_util.h"
#include "cryptonote_basic/tx_extra.h"
#include "cryptonote_core/loki_name_system.h"
#include "common/rules.h"
#include "cryptonote_config.h"
@ -8623,7 +8624,7 @@ wallet2::request_stake_unlock_result wallet2::can_request_stake_unlock(const cry
// out a nonce value of 0. The nonce value, unfortunately, can't be easily removed from the
// key image unlock tx_extra data without versioning/replacing it, so we still send this 0,
// but this will hopefully make it easier in the future to eliminate from the tx extra.
unlock.nonce = 0;
unlock.nonce = tx_extra_tx_key_image_unlock::FAKE_NONCE;
if (!generate_signature_for_request_stake_unlock(unlock.key_image, unlock.signature))
{
result.msg = tr("Failed to generate signature to sign request. The key image: ") + contribution.key_image + (" doesn't belong to this wallet");
@ -14585,6 +14586,7 @@ bool wallet2::generate_signature_for_request_stake_unlock(const crypto::key_imag
}
// generate ephemeral secret key
auto& hwdev = m_account.get_device();
cryptonote::keypair in_ephemeral;
crypto::key_image ki;
bool r = cryptonote::generate_key_image_helper(
@ -14596,13 +14598,14 @@ bool wallet2::generate_signature_for_request_stake_unlock(const crypto::key_imag
td.m_internal_output_index,
in_ephemeral,
ki,
m_account.get_device());
hwdev);
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
THROW_WALLET_EXCEPTION_IF(td.m_key_image_known && !td.m_key_image_partial && ki != td.m_key_image, error::wallet_internal_error, "key_image generated not matched with cached key image");
THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != pkey, error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
// null_hash because we hard-code a nonce of 0 (see comment in can_request_stake_unlock)
crypto::generate_signature(crypto::null_hash, in_ephemeral.pub, in_ephemeral.sec, signature);
THROW_WALLET_EXCEPTION_IF(
!hwdev.generate_unlock_signature(in_ephemeral.pub, in_ephemeral.sec, signature),
error::wallet_internal_error, "Hardware device failed to sign the unlock request");
return true;
}
//----------------------------------------------------------------------------------------------------

View file

@ -31,6 +31,7 @@
#include "loki_tests.h"
#include "common/string_util.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_basic/tx_extra.h"
#include "cryptonote_config.h"
#include "cryptonote_core/loki_name_system.h"
#include "cryptonote_core/service_node_list.h"