v0.0.18 - Fix sign-event JSON escaping and variable-path role key caching (per-path re-derivation)

This commit is contained in:
Laan Tungir
2026-08-20 18:34:14 -04:00
parent b5b58ecb87
commit 9c762edbd2
6 changed files with 24 additions and 7 deletions
Generated
+1 -1
View File
@@ -2207,7 +2207,7 @@ dependencies = [
[[package]] [[package]]
name = "signer" name = "signer"
version = "0.0.17" version = "0.0.18"
dependencies = [ dependencies = [
"base64", "base64",
"chacha20poly1305", "chacha20poly1305",
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "signer" name = "signer"
version = "0.0.17" version = "0.0.18"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
description = "Attended Nostr signing daemon — Rust port of n_signer" description = "Attended Nostr signing daemon — Rust port of n_signer"
+16 -4
View File
@@ -359,9 +359,16 @@ fn handle_nostr_verb(
// Ensure key is derived. For variable-path roles, use the concrete // Ensure key is derived. For variable-path roles, use the concrete
// path supplied by the client; for fixed-path roles, use the stored // path supplied by the client; for fixed-path roles, use the stored
// template. // template. Variable-path roles are re-derived whenever the requested
if !role.derived { // path differs from the currently-derived one (the cache is per-path,
let has_variable = role.has_variable_path(); // not per-role).
let has_variable = role.has_variable_path();
let needs_derive = if has_variable && sel.has_role_path {
role.derived_path.as_deref() != Some(sel.role_path.as_str())
} else {
!role.derived
};
if needs_derive {
let result = if has_variable && sel.has_role_path { let result = if has_variable && sel.has_role_path {
ctx.key_store ctx.key_store
.derive_one_with_path(ctx.role_table, ctx.mnemonic, role_index, &sel.role_path) .derive_one_with_path(ctx.role_table, ctx.mnemonic, role_index, &sel.role_path)
@@ -414,7 +421,12 @@ fn handle_nostr_verb(
None => return make_error_response(id, RpcError::INVALID_PARAMS), None => return make_error_response(id, RpcError::INVALID_PARAMS),
}; };
match ctx.key_store.sign_event(role_index, event_json) { match ctx.key_store.sign_event(role_index, event_json) {
Ok(signed) => make_success_response(id, &format!("\"{}\"", signed)), // The signed event is itself JSON; serialize it as a proper
// JSON string value so embedded quotes are escaped.
Ok(signed) => {
let wrapped = serde_json::to_string(&signed).unwrap_or_else(|_| "\"\"".into());
make_success_response(id, &wrapped)
}
Err(_) => make_error_response(id, RpcError::INVALID_PARAMS), Err(_) => make_error_response(id, RpcError::INVALID_PARAMS),
} }
} }
+2
View File
@@ -133,10 +133,12 @@ impl KeyStore {
role.derived = false; role.derived = false;
role.pubkey_hex.clear(); role.pubkey_hex.clear();
role.derived_path = None;
let dk = derive_for_role(concrete_path, role, phrase)?; let dk = derive_for_role(concrete_path, role, phrase)?;
role.pubkey_hex = dk.pubkey_hex.clone(); role.pubkey_hex = dk.pubkey_hex.clone();
role.derived = true; role.derived = true;
role.derived_path = Some(concrete_path.to_string());
if self.keys.len() <= role_index { if self.keys.len() <= role_index {
self.keys.resize_with(role_index + 1, || None); self.keys.resize_with(role_index + 1, || None);
+1 -1
View File
@@ -31,4 +31,4 @@ pub mod error;
pub use error::SignerError; pub use error::SignerError;
/// Version string (matches C NSIGNER_VERSION). /// Version string (matches C NSIGNER_VERSION).
pub const VERSION: &str = "v0.0.17"; pub const VERSION: &str = "v0.0.18";
+3
View File
@@ -137,6 +137,8 @@ pub struct RoleEntry {
pub pubkey_hex: String, pub pubkey_hex: String,
/// 1 if pubkey_hex has been populated. /// 1 if pubkey_hex has been populated.
pub derived: bool, pub derived: bool,
/// The concrete path the key was last derived for (variable-path roles).
pub derived_path: Option<String>,
/// Inclusive lower bound for %d; -1 = fixed path (no variable). /// Inclusive lower bound for %d; -1 = fixed path (no variable).
pub path_range_lo: i32, pub path_range_lo: i32,
/// Inclusive upper bound; == path_range_lo for single. /// Inclusive upper bound; == path_range_lo for single.
@@ -162,6 +164,7 @@ impl Default for RoleEntry {
role_path: String::new(), role_path: String::new(),
pubkey_hex: String::new(), pubkey_hex: String::new(),
derived: false, derived: false,
derived_path: None,
path_range_lo: -1, path_range_lo: -1,
path_range_hi: -1, path_range_hi: -1,
path_default_index: -1, path_default_index: -1,