wallet: support explicit trusted daemon for full wallets
What changed, and why it matters
This commit adds a new option for users to explicitly tell a Monero wallet whether its connected daemon should be treated as 'trusted.' A trusted daemon can receive more sensitive data or perform more powerful operations than an untrusted one. The change is a feature addition that exposes an existing underlying wallet capability; it does not by itself create a vulnerability, but it makes it easier for users to accidentally or intentionally mark a remote daemon as trusted. That could increase the impact of other attacks if a user is tricked into trusting a malicious daemon.
Review the documentation and UI/UX around this new flag to ensure users understand the risks of marking a remote or third-party daemon as trusted. Consider adding validation or warnings when `isTrusted=true` is combined with a non-local daemon URI. Verify that the underlying `monero-cpp` change correctly enforces trust semantics and does not default to trusted in unsafe ways.
Security signals we found
Adds explicit trust configuration for daemon connections
Exposes a security-relevant wallet setting through public Java API
Changes wallet construction path to set daemon connection separately after open, using config's trusted flag
No input validation or warnings added for marking remote daemons as trusted
Evidence from the diff
The patch extends the Java/JNI bindings for monero-cpp’s full wallet to support an optional isTrusted flag when setting the daemon connection. It adds setDaemonConnection(connection, isTrusted) to the MoneroWallet interface, implements it in MoneroWalletFull and MoneroWalletRpc, stores the flag in MoneroWalletConfig, and adds JNI plumbing (isTrustedJni) plus a new isDaemonTrusted() query. The C++ side passes boost::optional<bool> to the underlying wallet, with negative values meaning ‘unset’ (default behavior). The change is purely additive and does not alter default trust logic; it surfaces a security-relevant setting that already existed in the native wallet layer.
Changed components
src/main/cpp/monero_jni_bridge.cppsrc/main/cpp/monero_jni_bridge.hsrc/main/java/monero/wallet/MoneroWallet.javasrc/main/java/monero/wallet/MoneroWalletFull.javasrc/main/java/monero/wallet/MoneroWalletRpc.javasrc/main/java/monero/wallet/model/MoneroWalletConfig.javaexternal/monero-cppInspect captured patch +80 / −16
diff --git a/src/main/cpp/monero_jni_bridge.cpp b/src/main/cpp/monero_jni_bridge.cpp
index bb6b204..d0cd7a5 100644
--- a/src/main/cpp/monero_jni_bridge.cpp
+++ b/src/main/cpp/monero_jni_bridge.cpp
@@ -94,7 +94,7 @@ void rethrow_java_exception_as_cpp_exception(JNIEnv* env, jthrowable jexception)
throw runtime_error(msg);
}
-void set_daemon_connection(JNIEnv *env, monero_wallet* wallet, jstring juri, jstring jusername, jstring jpassword, jstring jproxy_uri) {
+void set_daemon_connection(JNIEnv *env, monero_wallet* wallet, jstring juri, jstring jusername, jstring jpassword, jstring jproxy_uri, jint jis_trusted) {
// collect and release string params
const char* _uri = juri ? env->GetStringUTFChars(juri, NULL) : nullptr;
@@ -110,9 +110,12 @@ void set_daemon_connection(JNIEnv *env, monero_wallet* wallet, jstring juri, jst
env->ReleaseStringUTFChars(jpassword, _password);
env->ReleaseStringUTFChars(jproxy_uri, _proxy_uri);
+ // collect trusted param, which is unset if negative
+ boost::optional<bool> is_trusted = jis_trusted < 0 ? boost::none : boost::optional<bool>(jis_trusted != 0);
+
// set daemon connection
try {
- wallet->set_daemon_connection(uri, username, password, proxy_uri);
+ wallet->set_daemon_connection(uri, username, password, proxy_uri, is_trusted);
} catch (...) {
rethrow_cpp_exception_as_java_exception(env);
}
@@ -551,16 +554,27 @@ JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletFull_isViewOnlyJni(JNI
return wallet->is_view_only();
}
-JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_setDaemonConnectionJni(JNIEnv *env, jobject instance, jstring juri, jstring jusername, jstring jpassword, jstring jproxy_uri) {
+JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_setDaemonConnectionJni(JNIEnv *env, jobject instance, jstring juri, jstring jusername, jstring jpassword, jstring jproxy_uri, jint jis_trusted) {
MTRACE("Java_monero_wallet_MoneroWalletFull_setDaemonConnectionJni");
monero_wallet* wallet = get_handle<monero_wallet>(env, instance, JNI_WALLET_HANDLE);
try {
- set_daemon_connection(env, wallet, juri, jusername, jpassword, jproxy_uri);
+ set_daemon_connection(env, wallet, juri, jusername, jpassword, jproxy_uri, jis_trusted);
} catch (...) {
rethrow_cpp_exception_as_java_exception(env);
}
}
+JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletFull_isDaemonTrustedJni(JNIEnv *env, jobject instance) {
+ MTRACE("Java_monero_wallet_MoneroWalletFull_isDaemonTrustedJni");
+ monero_wallet* wallet = get_handle<monero_wallet>(env, instance, JNI_WALLET_HANDLE);
+ try {
+ return static_cast<jboolean>(wallet->is_daemon_trusted());
+ } catch (...) {
+ rethrow_cpp_exception_as_java_exception(env);
+ return false;
+ }
+}
+
JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletFull_getDaemonConnectionJni(JNIEnv *env, jobject instance) {
MTRACE("Java_monero_wallet_MoneroWalletFull_getDaemonConnectionJni()");
diff --git a/src/main/cpp/monero_jni_bridge.h b/src/main/cpp/monero_jni_bridge.h
index 0449131..edc5ae1 100644
--- a/src/main/cpp/monero_jni_bridge.h
+++ b/src/main/cpp/monero_jni_bridge.h
@@ -73,7 +73,7 @@ JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletFull_getSeedLangua
JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletFull_isViewOnlyJni(JNIEnv *, jobject);
-JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_setDaemonConnectionJni(JNIEnv *, jobject, jstring, jstring, jstring, jstring);
+JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_setDaemonConnectionJni(JNIEnv *, jobject, jstring, jstring, jstring, jstring, jint);
JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletFull_getDaemonConnectionJni(JNIEnv *, jobject);
@@ -81,6 +81,8 @@ JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletFull_isConnectedToDaem
JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletFull_isDaemonSyncedJni(JNIEnv *, jobject);
+JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletFull_isDaemonTrustedJni(JNIEnv *, jobject);
+
JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletFull_isSyncedJni(JNIEnv *, jobject);
JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getVersionJni(JNIEnv *, jobject);
diff --git a/src/main/java/monero/wallet/MoneroWallet.java b/src/main/java/monero/wallet/MoneroWallet.java
index 3ddbc21..e7a46c8 100644
--- a/src/main/java/monero/wallet/MoneroWallet.java
+++ b/src/main/java/monero/wallet/MoneroWallet.java
@@ -116,10 +116,18 @@ public interface MoneroWallet {
* @param daemonConnection manages daemon connection information
*/
public void setDaemonConnection(MoneroRpcConnection daemonConnection);
-
+
+ /**
+ * Set the wallet's daemon connection.
+ *
+ * @param daemonConnection manages daemon connection information
+ * @param isTrusted specifies if the daemon is trusted (default = trusted if local address)
+ */
+ public void setDaemonConnection(MoneroRpcConnection daemonConnection, Boolean isTrusted);
+
/**
* Get the wallet's daemon connection.
- *
+ *
* @return the wallet's daemon connection
*/
public MoneroRpcConnection getDaemonConnection();
diff --git a/src/main/java/monero/wallet/MoneroWalletFull.java b/src/main/java/monero/wallet/MoneroWalletFull.java
index 85d7855..3c28de5 100644
--- a/src/main/java/monero/wallet/MoneroWalletFull.java
+++ b/src/main/java/monero/wallet/MoneroWalletFull.java
@@ -209,10 +209,11 @@ public class MoneroWalletFull extends MoneroWalletDefault {
// read wallet data from disk unless provided
MoneroWalletFull wallet;
if (config.getKeysData() == null) {
- wallet = openWallet(config.getPath(), config.getPassword(), config.getNetworkType(), config.getServer());
+ wallet = openWallet(config.getPath(), config.getPassword(), config.getNetworkType(), (MoneroRpcConnection) null);
} else {
- wallet = openWalletData(config.getPassword(), config.getNetworkType(), config.getKeysData(), config.getCacheData(), config.getServer());
+ wallet = openWalletData(config.getPassword(), config.getNetworkType(), config.getKeysData(), config.getCacheData(), null);
}
+ if (config.getServer() != null) wallet.setDaemonConnection(config.getServer(), config.isTrustedDaemon());
// set connection manager
wallet.setConnectionManager(config.getConnectionManager());
@@ -461,17 +462,37 @@ public class MoneroWalletFull extends MoneroWalletDefault {
@Override
public void setDaemonConnection(MoneroRpcConnection daemonConnection) {
+ setDaemonConnection(daemonConnection, null);
+ }
+
+ @Override
+ public void setDaemonConnection(MoneroRpcConnection daemonConnection, Boolean isTrusted) {
assertNotClosed();
- if (daemonConnection == null) setDaemonConnectionJni("", "", "", "");
+ int isTrustedJni = isTrusted == null ? -1 : (isTrusted ? 1 : 0); // negative if unset
+ if (daemonConnection == null) setDaemonConnectionJni("", "", "", "", isTrustedJni);
else {
try {
- setDaemonConnectionJni(daemonConnection.getUri() == null ? "" : daemonConnection.getUri().toString(), daemonConnection.getUsername(), daemonConnection.getPassword(), daemonConnection.getProxyUri());
+ setDaemonConnectionJni(daemonConnection.getUri() == null ? "" : daemonConnection.getUri().toString(), daemonConnection.getUsername(), daemonConnection.getPassword(), daemonConnection.getProxyUri(), isTrustedJni);
} catch (Exception e) {
throw new MoneroError(e.getMessage());
}
}
}
-
+
+ /**
+ * Indicates if the wallet's daemon is trusted.
+ *
+ * @return true if the daemon is trusted, false otherwise
+ */
+ public boolean isDaemonTrusted() {
+ assertNotClosed();
+ try {
+ return isDaemonTrustedJni();
+ } catch (Exception e) {
+ throw new MoneroError(e.getMessage());
+ }
+ }
+
@Override
public MoneroRpcConnection getDaemonConnection() {
assertNotClosed();
@@ -1423,14 +1444,16 @@ public class MoneroWalletFull extends MoneroWalletDefault {
private native boolean isViewOnlyJni();
- private native void setDaemonConnectionJni(String uri, String username, String password, String proxyUri);
+ private native void setDaemonConnectionJni(String uri, String username, String password, String proxyUri, int isTrusted);
private native String[] getDaemonConnectionJni(); // returns [uri, username, password]
private native boolean isConnectedToDaemonJni();
private native boolean isDaemonSyncedJni();
-
+
+ private native boolean isDaemonTrustedJni();
+
private native boolean isSyncedJni();
private native int getNetworkTypeJni();
diff --git a/src/main/java/monero/wallet/MoneroWalletRpc.java b/src/main/java/monero/wallet/MoneroWalletRpc.java
index 3d8974c..607b5c9 100644
--- a/src/main/java/monero/wallet/MoneroWalletRpc.java
+++ b/src/main/java/monero/wallet/MoneroWalletRpc.java
@@ -500,7 +500,12 @@ public class MoneroWalletRpc extends MoneroWalletDefault {
public void setDaemonConnection(MoneroRpcConnection connection) {
setDaemonConnection(connection, null, null);
}
-
+
+ @Override
+ public void setDaemonConnection(MoneroRpcConnection connection, Boolean isTrusted) {
+ setDaemonConnection(connection, isTrusted, null);
+ }
+
public void setDaemonConnection(MoneroRpcConnection connection, Boolean isTrusted, SslOptions sslOptions) {
if (sslOptions == null) sslOptions = new SslOptions();
Map<String, Object> params = new HashMap<String, Object>();
diff --git a/src/main/java/monero/wallet/model/MoneroWalletConfig.java b/src/main/java/monero/wallet/model/MoneroWalletConfig.java
index cdc91e2..82cb68b 100644
--- a/src/main/java/monero/wallet/model/MoneroWalletConfig.java
+++ b/src/main/java/monero/wallet/model/MoneroWalletConfig.java
@@ -17,6 +17,7 @@ public class MoneroWalletConfig {
private String password;
private MoneroNetworkType networkType;
private MoneroRpcConnection server;
+ private Boolean isTrustedDaemon;
private String serverUsername;
private String serverPassword;
private String serverProxyUri;
@@ -45,6 +46,7 @@ public class MoneroWalletConfig {
password = config.getPassword();
networkType = config.getNetworkType();
server = config.getServer();
+ isTrustedDaemon = config.isTrustedDaemon();
connectionManager = config.getConnectionManager();
seed = config.getSeed();
seedOffset = config.getSeedOffset();
@@ -285,6 +287,16 @@ public class MoneroWalletConfig {
return this;
}
+ @JsonProperty("isTrustedDaemon")
+ public Boolean isTrustedDaemon() {
+ return isTrustedDaemon;
+ }
+
+ public MoneroWalletConfig setIsTrustedDaemon(Boolean isTrustedDaemon) {
+ this.isTrustedDaemon = isTrustedDaemon;
+ return this;
+ }
+
@JsonProperty("isMultisig")
public Boolean isMultisig() {
return isMultisig;
Why this scored 21/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.