mirror of
https://github.com/oxen-io/oxen-core.git
synced 2023-12-14 02:22:56 +01:00
Ledger: Add stake unlock signature support
This commit is contained in:
parent
d60548ac0a
commit
07aad36120
8 changed files with 63 additions and 4 deletions
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
||||
/* ======================================================================= */
|
||||
|
|
|
@ -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 */
|
||||
/* ======================================================================= */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue