wallet: display private view keys for hardware wallets
What changed, and why it matters
This change lets users of Ledger hardware wallets see their private view key in the Monero command-line wallet, something previously hidden with the message 'On device. Not available.' The key is already cached inside the wallet software for normal operation; the patch simply exposes it through the existing `viewkey` command when the user asks. It does not appear to leak the key to anyone else or bypass hardware protections for the spend key.
No urgent action required. If maintainers want to reduce risk, they could add a warning when printing a hardware-wallet cached view key, require explicit user confirmation, or document that the view key is exportable to the host for scanning purposes. Review whether `soft_request_view_key()` enforces any user confirmation on the Ledger device.
Security signals we found
Private view key displayed to user via existing CLI command
View key already cached in host wallet process before this commit
Hardware wallet spend key remains on device
No new network, RPC, or logging exposure observed
No authentication or authorization gate added around the viewkey command
Evidence from the diff
The commit adds a get_cached_view_key() method to the device abstraction and Ledger implementation, returning the already-cached this->viewkey after a soft_request_view_key() check. In simplewallet::viewkey(), the logic is changed from unconditionally printing ‘On device. Not available’ for hardware wallets to attempting to retrieve and print the cached view key. The spend key remains on the Ledger device. The view key was already present in host memory, so this is a UI/UX change rather than a cryptographic exposure.
Changed components
src/device/device.hppsrc/device/device_ledger.cppsrc/device/device_ledger.hppsrc/simplewallet/simplewallet.cppInspect captured patch +19 / −5
diff --git a/src/device/device.hpp b/src/device/device.hpp
index 81caec9..cafd897 100644
--- a/src/device/device.hpp
+++ b/src/device/device.hpp
@@ -151,6 +151,7 @@ namespace hw {
/* ======================================================================= */
virtual bool get_public_address(cryptonote::account_public_address &pubkey) = 0;
virtual bool get_secret_keys(crypto::secret_key &viewkey , crypto::secret_key &spendkey) = 0;
+ virtual bool get_cached_view_key(crypto::secret_key &viewkey_out) { return false; }
virtual bool generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key, uint64_t kdf_rounds) = 0;
/* ======================================================================= */
diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp
index e0a45a6..d4477fb 100644
--- a/src/device/device_ledger.cpp
+++ b/src/device/device_ledger.cpp
@@ -657,6 +657,15 @@ namespace hw {
return true;
}
+ bool device_ledger::get_cached_view_key(crypto::secret_key &viewkey_out) {
+ AUTO_LOCK_CMD();
+
+ if (!this->soft_request_view_key()) return false;
+ viewkey_out = this->viewkey;
+
+ return true;
+ }
+
bool device_ledger::generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key, uint64_t kdf_rounds) {
AUTO_LOCK_CMD();
diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp
index 1db6fbf..26ce594 100644
--- a/src/device/device_ledger.hpp
+++ b/src/device/device_ledger.hpp
@@ -224,6 +224,7 @@ namespace hw {
/* ======================================================================= */
bool get_public_address(cryptonote::account_public_address &pubkey) override;
bool get_secret_keys(crypto::secret_key &viewkey , crypto::secret_key &spendkey) override;
+ bool get_cached_view_key(crypto::secret_key &viewkey_out) override;
bool generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key, uint64_t kdf_rounds) override;
void display_address(const cryptonote::subaddress_index& index, const boost::optional<crypto::hash8> &payment_id) override;
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index 69c66d0..ccd7e74 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -829,13 +829,16 @@ bool simple_wallet::viewkey(const std::vector<std::string> &args/* = std::vector
{
// don't log
PAUSE_READLINE();
- if (m_wallet->key_on_device()) {
- std::cout << "secret: On device. Not available" << std::endl;
- } else {
- SCOPED_WALLET_UNLOCK();
+ SCOPED_WALLET_UNLOCK();
+ crypto::secret_key viewkey = m_wallet->get_account().get_keys().m_view_secret_key;
+ bool available = viewkey != crypto::null_skey;
+ if (!available && m_wallet->key_on_device()) available = m_wallet->get_account().get_device().get_cached_view_key(viewkey);
+ if (available) {
printf("secret: ");
- print_secret_key(m_wallet->get_account().get_keys().m_view_secret_key);
+ print_secret_key(viewkey);
putchar('\n');
+ } else {
+ std::cout << "secret: On device. Not available" << std::endl;
}
std::cout << "public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_view_public_key) << std::endl;
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.