feat: add NIP-04 and NIP-44 encrypt/decrypt RPC endpoints for iframe apps
Backend: identity.nostr-encrypt-nip04, identity.nostr-decrypt-nip04, identity.nostr-encrypt-nip44, identity.nostr-decrypt-nip44 endpoints with auto-resolve to default identity. Frontend: appLauncher routes nip04.* and nip44.* postMessage calls to backend RPC. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -340,6 +340,97 @@ impl RpcHandler {
|
||||
}))
|
||||
}
|
||||
|
||||
/// Resolve the identity ID from params, falling back to the default identity.
|
||||
async fn resolve_identity_id(&self, params: &serde_json::Value) -> Result<String> {
|
||||
if let Some(id) = params.get("id").and_then(|v| v.as_str()) {
|
||||
return Ok(id.to_string());
|
||||
}
|
||||
let manager = IdentityManager::new(&self.config.data_dir).await?;
|
||||
let (records, default_id) = manager.list().await?;
|
||||
// Prefer the default identity
|
||||
if let Some(default_id) = default_id {
|
||||
return Ok(default_id);
|
||||
}
|
||||
// Fall back to first identity with a Nostr key, or just the first identity
|
||||
records.iter()
|
||||
.find(|i| i.nostr_pubkey.is_some())
|
||||
.or(records.first())
|
||||
.map(|i| i.id.clone())
|
||||
.ok_or_else(|| anyhow::anyhow!("No identity found"))
|
||||
}
|
||||
|
||||
/// NIP-04 encrypt plaintext for a peer.
|
||||
pub(super) async fn handle_identity_nostr_encrypt_nip04(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<serde_json::Value> {
|
||||
let params = params.unwrap_or_default();
|
||||
let id = self.resolve_identity_id(¶ms).await?;
|
||||
let pubkey = params.get("pubkey").and_then(|v| v.as_str())
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing required parameter: pubkey"))?;
|
||||
let plaintext = params.get("plaintext").and_then(|v| v.as_str())
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing required parameter: plaintext"))?;
|
||||
|
||||
let manager = IdentityManager::new(&self.config.data_dir).await?;
|
||||
let ciphertext = manager.nostr_encrypt_nip04(&id, pubkey, plaintext).await?;
|
||||
|
||||
Ok(serde_json::json!({ "ciphertext": ciphertext }))
|
||||
}
|
||||
|
||||
/// NIP-04 decrypt ciphertext from a peer.
|
||||
pub(super) async fn handle_identity_nostr_decrypt_nip04(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<serde_json::Value> {
|
||||
let params = params.unwrap_or_default();
|
||||
let id = self.resolve_identity_id(¶ms).await?;
|
||||
let pubkey = params.get("pubkey").and_then(|v| v.as_str())
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing required parameter: pubkey"))?;
|
||||
let ciphertext = params.get("ciphertext").and_then(|v| v.as_str())
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing required parameter: ciphertext"))?;
|
||||
|
||||
let manager = IdentityManager::new(&self.config.data_dir).await?;
|
||||
let plaintext = manager.nostr_decrypt_nip04(&id, pubkey, ciphertext).await?;
|
||||
|
||||
Ok(serde_json::json!({ "plaintext": plaintext }))
|
||||
}
|
||||
|
||||
/// NIP-44 encrypt plaintext for a peer.
|
||||
pub(super) async fn handle_identity_nostr_encrypt_nip44(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<serde_json::Value> {
|
||||
let params = params.unwrap_or_default();
|
||||
let id = self.resolve_identity_id(¶ms).await?;
|
||||
let pubkey = params.get("pubkey").and_then(|v| v.as_str())
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing required parameter: pubkey"))?;
|
||||
let plaintext = params.get("plaintext").and_then(|v| v.as_str())
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing required parameter: plaintext"))?;
|
||||
|
||||
let manager = IdentityManager::new(&self.config.data_dir).await?;
|
||||
let ciphertext = manager.nostr_encrypt_nip44(&id, pubkey, plaintext).await?;
|
||||
|
||||
Ok(serde_json::json!({ "ciphertext": ciphertext }))
|
||||
}
|
||||
|
||||
/// NIP-44 decrypt ciphertext from a peer.
|
||||
pub(super) async fn handle_identity_nostr_decrypt_nip44(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<serde_json::Value> {
|
||||
let params = params.unwrap_or_default();
|
||||
let id = self.resolve_identity_id(¶ms).await?;
|
||||
let pubkey = params.get("pubkey").and_then(|v| v.as_str())
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing required parameter: pubkey"))?;
|
||||
let ciphertext = params.get("ciphertext").and_then(|v| v.as_str())
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing required parameter: ciphertext"))?;
|
||||
|
||||
let manager = IdentityManager::new(&self.config.data_dir).await?;
|
||||
let plaintext = manager.nostr_decrypt_nip44(&id, pubkey, ciphertext).await?;
|
||||
|
||||
Ok(serde_json::json!({ "plaintext": plaintext }))
|
||||
}
|
||||
|
||||
/// Resolve a remote peer's DID Document over Tor.
|
||||
/// Queries the peer's /rpc/ endpoint for identity.resolve-did.
|
||||
pub(super) async fn handle_identity_resolve_remote_did(
|
||||
|
||||
@@ -337,6 +337,10 @@ impl RpcHandler {
|
||||
"identity.verify-did-document" => self.handle_identity_verify_did_document(params).await,
|
||||
"identity.create-nostr-key" => self.handle_identity_create_nostr_key(params).await,
|
||||
"identity.nostr-sign" => self.handle_identity_nostr_sign(params).await,
|
||||
"identity.nostr-encrypt-nip04" => self.handle_identity_nostr_encrypt_nip04(params).await,
|
||||
"identity.nostr-decrypt-nip04" => self.handle_identity_nostr_decrypt_nip04(params).await,
|
||||
"identity.nostr-encrypt-nip44" => self.handle_identity_nostr_encrypt_nip44(params).await,
|
||||
"identity.nostr-decrypt-nip44" => self.handle_identity_nostr_decrypt_nip44(params).await,
|
||||
|
||||
// Bitcoin domain names (NIP-05)
|
||||
"identity.register-name" => self.handle_identity_register_name(params).await,
|
||||
|
||||
Reference in New Issue
Block a user