commit 16f972d0b3e5443ae74775dbdf9fe899074b3646
parent ef737fbb510a5ec20476f202462fcda15932a6e3
Author: triesap <tyson@radroots.org>
Date: Thu, 26 Mar 2026 23:59:07 +0000
custody: add active identity operations
Diffstat:
11 files changed, 315 insertions(+), 142 deletions(-)
diff --git a/src/app/runtime.rs b/src/app/runtime.rs
@@ -13,7 +13,7 @@ use crate::config::{
MycAuditConfig, MycConfig, MycIdentitySourceSpec, MycPersistenceConfig, MycRuntimeAuditBackend,
MycSignerStateBackend, MycTransportDeliveryPolicy,
};
-use crate::custody::MycIdentityProvider;
+use crate::custody::{MycActiveIdentity, MycIdentityProvider};
use crate::discovery::MycDiscoveryContext;
use crate::error::MycError;
use crate::operability::server::run_observability_server;
@@ -25,7 +25,7 @@ use crate::policy::MycPolicyContext;
use crate::transport::{
MycNip46Service, MycNostrTransport, MycPublishOutcome, MycTransportSnapshot,
};
-use radroots_identity::{RadrootsIdentity, RadrootsIdentityPublic};
+use radroots_identity::RadrootsIdentityPublic;
use radroots_nostr_signer::prelude::{
RadrootsNostrFileSignerStore, RadrootsNostrSignerApprovalRequirement,
RadrootsNostrSignerAuthState, RadrootsNostrSignerConnectionRecord, RadrootsNostrSignerManager,
@@ -73,8 +73,8 @@ pub struct MycStartupSnapshot {
pub struct MycSignerContext {
signer_identity_provider: MycIdentityProvider,
user_identity_provider: MycIdentityProvider,
- signer_identity: RadrootsIdentity,
- user_identity: RadrootsIdentity,
+ signer_identity: MycActiveIdentity,
+ user_identity: MycActiveIdentity,
signer_store: Arc<dyn RadrootsNostrSignerStore>,
operation_audit_store: Arc<dyn MycOperationAuditStore>,
policy: MycPolicyContext,
@@ -124,7 +124,7 @@ impl MycRuntime {
&self.config
}
- pub fn signer_identity(&self) -> &RadrootsIdentity {
+ pub fn signer_identity(&self) -> &MycActiveIdentity {
self.signer.signer_identity()
}
@@ -132,7 +132,7 @@ impl MycRuntime {
self.signer.signer_public_identity()
}
- pub fn user_identity(&self) -> &RadrootsIdentity {
+ pub fn user_identity(&self) -> &MycActiveIdentity {
self.signer.user_identity()
}
@@ -698,7 +698,7 @@ impl MycRuntime {
fn recovery_publisher_identity(
&self,
record: &MycDeliveryOutboxRecord,
- ) -> Result<RadrootsIdentity, MycError> {
+ ) -> Result<MycActiveIdentity, MycError> {
if record.kind != MycDeliveryOutboxKind::DiscoveryHandlerPublish {
return Ok(self.signer_identity().clone());
}
@@ -966,7 +966,7 @@ impl MycRuntimePaths {
}
impl MycSignerContext {
- pub fn signer_identity(&self) -> &RadrootsIdentity {
+ pub fn signer_identity(&self) -> &MycActiveIdentity {
&self.signer_identity
}
@@ -982,7 +982,7 @@ impl MycSignerContext {
self.signer_identity.to_public()
}
- pub fn user_identity(&self) -> &RadrootsIdentity {
+ pub fn user_identity(&self) -> &MycActiveIdentity {
&self.user_identity
}
@@ -1048,8 +1048,8 @@ impl MycSignerContext {
MycIdentityProvider::from_source("signer", signer_identity_source)?;
let user_identity_provider =
MycIdentityProvider::from_source("user", user_identity_source)?;
- let signer_identity = signer_identity_provider.load_identity()?;
- let user_identity = user_identity_provider.load_identity()?;
+ let signer_identity = signer_identity_provider.load_active_identity()?;
+ let user_identity = user_identity_provider.load_active_identity()?;
let signer_store = Self::build_signer_store(persistence, &paths.signer_state_path)?;
let operation_audit_store =
Self::build_operation_audit_store(persistence, &paths.audit_dir, audit_config)?;
@@ -1558,8 +1558,12 @@ mod tests {
.mark_publish_workflow_published(&workflow.workflow_id)
.expect("mark workflow published");
- let event = RadrootsNostrEventBuilder::new(RadrootsNostrKind::Custom(24133), "recovery")
- .sign_with_keys(runtime.signer_identity().keys())
+ let event = runtime
+ .signer_identity()
+ .sign_event_builder(
+ RadrootsNostrEventBuilder::new(RadrootsNostrKind::Custom(24133), "recovery"),
+ "recovery test",
+ )
.expect("sign event");
let outbox_record = MycDeliveryOutboxRecord::new(
MycDeliveryOutboxKind::ListenerResponsePublish,
@@ -1666,10 +1670,13 @@ mod tests {
let workflow = manager
.begin_connect_secret_publish_finalization(&connection.connection_id)
.expect("begin workflow");
- let event =
- RadrootsNostrEventBuilder::new(RadrootsNostrKind::Custom(24133), "queued-recovery")
- .sign_with_keys(runtime.signer_identity().keys())
- .expect("sign event");
+ let event = runtime
+ .signer_identity()
+ .sign_event_builder(
+ RadrootsNostrEventBuilder::new(RadrootsNostrKind::Custom(24133), "queued-recovery"),
+ "queued recovery test",
+ )
+ .expect("sign event");
let outbox_record = MycDeliveryOutboxRecord::new(
MycDeliveryOutboxKind::ListenerResponsePublish,
event,
diff --git a/src/control.rs b/src/control.rs
@@ -158,7 +158,10 @@ pub async fn accept_client_uri(
)?;
let response_relays = merge_relays(&client_uri.relays, &preferred_relays);
let workflow = manager.begin_connect_secret_publish_finalization(&connection.connection_id)?;
- let event = match event.sign_with_keys(runtime.signer_identity().keys()) {
+ let event = match runtime
+ .signer_identity()
+ .sign_event_builder(event, "connect accept response")
+ {
Ok(event) => event,
Err(error) => {
return Err(cancel_connect_accept_workflow_on_error(
@@ -413,7 +416,10 @@ async fn replay_authorized_request(
let connection = manager.get_connection(connection_id)?.ok_or_else(|| {
MycError::InvalidOperation(format!("connection `{connection_id}` was not found"))
})?;
- let event = match event.sign_with_keys(runtime.signer_identity().keys()) {
+ let event = match runtime
+ .signer_identity()
+ .sign_event_builder(event, "authorized auth replay response")
+ {
Ok(event) => event,
Err(error) => {
return Err(cancel_auth_replay_workflow_on_error(
diff --git a/src/custody.rs b/src/custody.rs
@@ -1,7 +1,12 @@
use std::path::PathBuf;
use std::sync::Arc;
+use nostr::nips::nip44::Version;
+use nostr::nips::{nip04, nip44};
use radroots_identity::{RadrootsIdentity, RadrootsIdentityId};
+use radroots_nostr::prelude::{
+ RadrootsNostrClient, RadrootsNostrEvent, RadrootsNostrEventBuilder, RadrootsNostrPublicKey,
+};
use radroots_nostr_accounts::prelude::{
RadrootsNostrSecretVault, RadrootsNostrSecretVaultOsKeyring,
};
@@ -10,6 +15,11 @@ use serde::Serialize;
use crate::config::{MycIdentityBackend, MycIdentitySourceSpec};
use crate::error::MycError;
+#[derive(Clone)]
+pub struct MycActiveIdentity {
+ identity: Arc<RadrootsIdentity>,
+}
+
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct MycIdentityStatusOutput {
pub backend: MycIdentityBackend,
@@ -146,8 +156,12 @@ impl MycIdentityProvider {
}
}
- pub fn resolved_status(&self, identity: &RadrootsIdentity) -> MycIdentityStatusOutput {
- self.status_with_result(Ok(identity))
+ pub fn load_active_identity(&self) -> Result<MycActiveIdentity, MycError> {
+ self.load_identity().map(MycActiveIdentity::new)
+ }
+
+ pub fn resolved_status(&self, identity: &MycActiveIdentity) -> MycIdentityStatusOutput {
+ self.status_with_result(Ok(identity.as_identity()))
}
pub fn probe_status(&self) -> MycIdentityStatusOutput {
@@ -210,6 +224,123 @@ impl MycIdentityProvider {
}
}
+impl MycActiveIdentity {
+ pub fn new(identity: RadrootsIdentity) -> Self {
+ Self {
+ identity: Arc::new(identity),
+ }
+ }
+
+ pub fn id(&self) -> RadrootsIdentityId {
+ self.identity.id()
+ }
+
+ pub fn public_key(&self) -> RadrootsNostrPublicKey {
+ self.identity.public_key()
+ }
+
+ pub fn public_key_hex(&self) -> String {
+ self.identity.public_key_hex()
+ }
+
+ pub fn secret_key_hex(&self) -> String {
+ self.identity.secret_key_hex()
+ }
+
+ pub fn to_public(&self) -> radroots_identity::RadrootsIdentityPublic {
+ self.identity.to_public()
+ }
+
+ pub fn nostr_client(&self) -> RadrootsNostrClient {
+ RadrootsNostrClient::from_identity(self.as_identity())
+ }
+
+ pub fn nostr_client_owned(&self) -> RadrootsNostrClient {
+ RadrootsNostrClient::from_identity_owned((*self.identity).clone())
+ }
+
+ pub fn sign_event_builder(
+ &self,
+ builder: RadrootsNostrEventBuilder,
+ operation: &str,
+ ) -> Result<RadrootsNostrEvent, MycError> {
+ builder
+ .sign_with_keys(self.identity.keys())
+ .map_err(|error| {
+ MycError::InvalidOperation(format!("failed to sign {operation} event: {error}"))
+ })
+ }
+
+ pub fn sign_unsigned_event(
+ &self,
+ unsigned_event: nostr::UnsignedEvent,
+ operation: &str,
+ ) -> Result<nostr::Event, MycError> {
+ unsigned_event
+ .sign_with_keys(self.identity.keys())
+ .map_err(|error| {
+ MycError::InvalidOperation(format!("failed to sign {operation}: {error}"))
+ })
+ }
+
+ pub fn nip04_encrypt(
+ &self,
+ public_key: &RadrootsNostrPublicKey,
+ plaintext: impl Into<String>,
+ ) -> Result<String, MycError> {
+ nip04::encrypt(
+ self.identity.keys().secret_key(),
+ public_key,
+ plaintext.into(),
+ )
+ .map_err(|error| MycError::Nip46Encrypt(error.to_string()))
+ }
+
+ pub fn nip04_decrypt(
+ &self,
+ public_key: &RadrootsNostrPublicKey,
+ ciphertext: impl AsRef<str>,
+ ) -> Result<String, MycError> {
+ nip04::decrypt(
+ self.identity.keys().secret_key(),
+ public_key,
+ ciphertext.as_ref(),
+ )
+ .map_err(|error| MycError::Nip46Decrypt(error.to_string()))
+ }
+
+ pub fn nip44_encrypt(
+ &self,
+ public_key: &RadrootsNostrPublicKey,
+ plaintext: impl Into<String>,
+ ) -> Result<String, MycError> {
+ nip44::encrypt(
+ self.identity.keys().secret_key(),
+ public_key,
+ plaintext.into(),
+ Version::V2,
+ )
+ .map_err(|error| MycError::Nip46Encrypt(error.to_string()))
+ }
+
+ pub fn nip44_decrypt(
+ &self,
+ public_key: &RadrootsNostrPublicKey,
+ ciphertext: impl AsRef<str>,
+ ) -> Result<String, MycError> {
+ nip44::decrypt(
+ self.identity.keys().secret_key(),
+ public_key,
+ ciphertext.as_ref(),
+ )
+ .map_err(|error| MycError::Nip46Decrypt(error.to_string()))
+ }
+
+ pub(crate) fn as_identity(&self) -> &RadrootsIdentity {
+ self.identity.as_ref()
+ }
+}
+
impl MycIdentityStatusOutput {
pub fn with_inherited_from(mut self, inherited_from: impl Into<String>) -> Self {
self.inherited_from = Some(inherited_from.into());
diff --git a/src/discovery.rs b/src/discovery.rs
@@ -3,12 +3,11 @@ use std::fs;
use std::path::{Path, PathBuf};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
-use radroots_identity::RadrootsIdentity;
use radroots_nostr::prelude::{
- RadrootsNostrApplicationHandlerSpec, RadrootsNostrClient, RadrootsNostrError,
- RadrootsNostrEvent, RadrootsNostrFilter, RadrootsNostrKind, RadrootsNostrMetadata,
- RadrootsNostrRelayUrl, radroots_nostr_build_application_handler_event,
- radroots_nostr_filter_tag, radroots_nostr_metadata_has_fields, radroots_nostr_tag_first_value,
+ RadrootsNostrApplicationHandlerSpec, RadrootsNostrError, RadrootsNostrEvent,
+ RadrootsNostrFilter, RadrootsNostrKind, RadrootsNostrMetadata, RadrootsNostrRelayUrl,
+ radroots_nostr_build_application_handler_event, radroots_nostr_filter_tag,
+ radroots_nostr_metadata_has_fields, radroots_nostr_tag_first_value,
};
use radroots_nostr_connect::prelude::{RadrootsNostrConnectBunkerUri, RadrootsNostrConnectUri};
use radroots_nostr_signer::prelude::RadrootsNostrSignerRequestId;
@@ -18,7 +17,7 @@ use tokio::task::JoinSet;
use crate::app::MycRuntime;
use crate::audit::{MycOperationAuditKind, MycOperationAuditOutcome, MycOperationAuditRecord};
use crate::config::MycDiscoveryMetadataConfig;
-use crate::custody::MycIdentityProvider;
+use crate::custody::{MycActiveIdentity, MycIdentityProvider};
use crate::error::MycError;
use crate::outbox::{MycDeliveryOutboxKind, MycDeliveryOutboxRecord};
use crate::transport::{MycNostrTransport, MycPublishOutcome, MycRelayPublishResult};
@@ -32,8 +31,8 @@ const DISCOVERY_RELAY_FETCH_CONCURRENCY_LIMIT: usize = 8;
#[derive(Clone)]
pub struct MycDiscoveryContext {
- app_identity: RadrootsIdentity,
- signer_identity: RadrootsIdentity,
+ app_identity: MycActiveIdentity,
+ signer_identity: MycActiveIdentity,
domain: String,
handler_identifier: String,
public_relays: Vec<RadrootsNostrRelayUrl>,
@@ -290,7 +289,7 @@ impl MycDiscoveryContext {
let app_identity = match discovery.app_identity_source() {
Some(source) => {
- MycIdentityProvider::from_source("discovery app", source)?.load_identity()?
+ MycIdentityProvider::from_source("discovery app", source)?.load_active_identity()?
}
None => runtime.signer_identity().clone(),
};
@@ -322,11 +321,11 @@ impl MycDiscoveryContext {
})
}
- pub fn app_identity(&self) -> &RadrootsIdentity {
+ pub fn app_identity(&self) -> &MycActiveIdentity {
&self.app_identity
}
- pub fn signer_identity(&self) -> &RadrootsIdentity {
+ pub fn signer_identity(&self) -> &MycActiveIdentity {
&self.signer_identity
}
@@ -452,13 +451,8 @@ impl MycDiscoveryContext {
pub fn build_signed_handler_event(&self) -> Result<RadrootsNostrEvent, MycError> {
let builder = radroots_nostr_build_application_handler_event(&self.build_handler_spec())?;
- builder
- .sign_with_keys(self.app_identity.keys())
- .map_err(|error| {
- MycError::InvalidOperation(format!(
- "failed to sign NIP-89 application handler event: {error}"
- ))
- })
+ self.app_identity
+ .sign_event_builder(builder, "NIP-89 application handler")
}
pub fn write_bundle(
@@ -1436,7 +1430,7 @@ async fn fetch_live_nip89_events_for_relay(
context: &MycDiscoveryContext,
relay: &RadrootsNostrRelayUrl,
) -> Result<Vec<MycSourcedLiveNip89Event>, MycError> {
- let client = RadrootsNostrClient::from_identity(context.app_identity());
+ let client = context.app_identity().nostr_client();
let _ = client.add_relay(relay.as_str()).await?;
client
.try_connect_relay(
@@ -2088,7 +2082,7 @@ impl MycDiscoveryBundleOutput {
fn render_nostrconnect_url(
template: &str,
- signer_identity: &RadrootsIdentity,
+ signer_identity: &MycActiveIdentity,
public_relays: &[RadrootsNostrRelayUrl],
) -> Result<String, MycError> {
let bunker_uri = RadrootsNostrConnectUri::Bunker(RadrootsNostrConnectBunkerUri {
diff --git a/src/lib.rs b/src/lib.rs
@@ -31,7 +31,7 @@ pub use config::{
MycTransportDeliveryPolicy,
};
pub use control::{MycAcceptedConnectionOutput, MycAuthorizedReplayOutput};
-pub use custody::{MycIdentityProvider, MycIdentityStatusOutput};
+pub use custody::{MycActiveIdentity, MycIdentityProvider, MycIdentityStatusOutput};
pub use discovery::{
MycDiscoveryBundleManifest, MycDiscoveryBundleOutput, MycDiscoveryContext,
MycDiscoveryDiffOutput, MycDiscoveryLiveStatus, MycDiscoveryRelayFetchStatus,
diff --git a/src/operability/mod.rs b/src/operability/mod.rs
@@ -4,10 +4,7 @@ use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use std::time::Duration;
-use radroots_identity::RadrootsIdentity;
-use radroots_nostr::prelude::{
- RadrootsNostrClient, RadrootsNostrRelayStatus, RadrootsNostrRelayUrl,
-};
+use radroots_nostr::prelude::{RadrootsNostrRelayStatus, RadrootsNostrRelayUrl};
use radroots_nostr_signer::prelude::{
RadrootsNostrSignerPublishWorkflowRecord, RadrootsNostrSignerPublishWorkflowState,
RadrootsNostrSignerRequestDecision,
@@ -19,7 +16,7 @@ use tokio::task::JoinSet;
use crate::app::MycRuntime;
use crate::audit::{MycOperationAuditKind, MycOperationAuditOutcome};
use crate::config::{MycRuntimeAuditBackend, MycSignerStateBackend, MycTransportDeliveryPolicy};
-use crate::custody::MycIdentityStatusOutput;
+use crate::custody::{MycActiveIdentity, MycIdentityStatusOutput};
use crate::discovery::MycDiscoveryContext;
use crate::error::MycError;
use crate::outbox::{MycDeliveryOutboxRecord, MycDeliveryOutboxStatus, now_unix_secs};
@@ -1176,7 +1173,7 @@ fn required_available_relays(
}
async fn probe_relays(
- identity: &RadrootsIdentity,
+ identity: &MycActiveIdentity,
relays: &[RadrootsNostrRelayUrl],
connect_timeout_secs: u64,
) -> Result<Vec<MycRelayProbe>, MycError> {
@@ -1198,7 +1195,7 @@ async fn probe_relays(
let Some((relay_index, relay)) = pending.next() else {
break;
};
- let identity = (*identity).clone();
+ let identity = identity.clone();
join_set.spawn(async move {
let probe = probe_relay(identity, relay.clone(), connect_timeout_secs).await;
(relay_index, probe)
@@ -1218,7 +1215,7 @@ async fn probe_relays(
let Some((relay_index, relay)) = pending.next() else {
break;
};
- let identity = (*identity).clone();
+ let identity = identity.clone();
join_set.spawn(async move {
let probe = probe_relay(identity, relay.clone(), connect_timeout_secs).await;
(relay_index, probe)
@@ -1235,12 +1232,12 @@ async fn probe_relays(
}
async fn probe_relay(
- identity: RadrootsIdentity,
+ identity: MycActiveIdentity,
relay: RadrootsNostrRelayUrl,
connect_timeout_secs: u64,
) -> Result<MycRelayProbe, MycError> {
let relay_url = relay.to_string();
- let client = RadrootsNostrClient::from_identity_owned(identity);
+ let client = identity.nostr_client_owned();
client
.add_relay(relay.as_str())
.await
diff --git a/src/transport.rs b/src/transport.rs
@@ -3,7 +3,6 @@ pub mod nip46;
use std::collections::{BTreeMap, BTreeSet};
use std::time::Duration;
-use radroots_identity::RadrootsIdentity;
use radroots_nostr::prelude::{
RadrootsNostrClient, RadrootsNostrEvent, RadrootsNostrEventBuilder, RadrootsNostrOutput,
RadrootsNostrRelayUrl,
@@ -12,6 +11,7 @@ use serde::Serialize;
use tokio::time::sleep;
use crate::config::{MycTransportConfig, MycTransportDeliveryPolicy};
+use crate::custody::MycActiveIdentity;
use crate::error::MycError;
pub use nip46::{MycNip46Handler, MycNip46Service};
@@ -72,14 +72,14 @@ struct MycPublishSettings {
impl MycNostrTransport {
pub fn bootstrap(
config: &MycTransportConfig,
- signer_identity: &RadrootsIdentity,
+ signer_identity: &MycActiveIdentity,
) -> Result<Option<Self>, MycError> {
if !config.enabled {
return Ok(None);
}
Ok(Some(Self {
- client: RadrootsNostrClient::from_identity(signer_identity),
+ client: signer_identity.nostr_client(),
relays: config.parse_relays()?,
connect_timeout_secs: config.connect_timeout_secs,
delivery_policy: config.delivery_policy,
@@ -118,7 +118,7 @@ impl MycNostrTransport {
}
pub async fn publish_once(
- signer_identity: &RadrootsIdentity,
+ signer_identity: &MycActiveIdentity,
relays: &[RadrootsNostrRelayUrl],
config: &MycTransportConfig,
operation: &str,
@@ -130,16 +130,12 @@ impl MycNostrTransport {
));
}
- let event = event
- .sign_with_keys(signer_identity.keys())
- .map_err(|error| {
- MycError::InvalidOperation(format!("failed to sign publish event: {error}"))
- })?;
+ let event = signer_identity.sign_event_builder(event, "publish")?;
Self::publish_event_once(signer_identity, relays, config, operation, &event).await
}
pub async fn publish_event_once(
- signer_identity: &RadrootsIdentity,
+ signer_identity: &MycActiveIdentity,
relays: &[RadrootsNostrRelayUrl],
config: &MycTransportConfig,
operation: &str,
@@ -153,7 +149,7 @@ impl MycNostrTransport {
let settings = MycPublishSettings::from_config(config);
publish_with_policy(relays, &settings, operation, || async {
- let client = RadrootsNostrClient::from_identity(signer_identity);
+ let client = signer_identity.nostr_client();
for relay in relays {
client
.add_relay(relay.as_str())
@@ -517,21 +513,23 @@ mod tests {
use std::collections::{HashMap, HashSet};
use std::sync::{Arc, Mutex};
- use radroots_identity::RadrootsIdentity;
use radroots_nostr::prelude::{
RadrootsNostrEventId, RadrootsNostrOutput, RadrootsNostrRelayUrl,
};
use tokio::time::Instant;
use crate::config::{MycTransportConfig, MycTransportDeliveryPolicy};
+ use crate::custody::MycActiveIdentity;
use super::{MycNostrTransport, MycPublishSettings, MycTransportSnapshot, publish_with_policy};
- fn signer_identity() -> RadrootsIdentity {
- RadrootsIdentity::from_secret_key_str(
- "1111111111111111111111111111111111111111111111111111111111111111",
+ fn signer_identity() -> MycActiveIdentity {
+ MycActiveIdentity::new(
+ radroots_identity::RadrootsIdentity::from_secret_key_str(
+ "1111111111111111111111111111111111111111111111111111111111111111",
+ )
+ .expect("identity"),
)
- .expect("identity")
}
#[test]
diff --git a/src/transport/nip46.rs b/src/transport/nip46.rs
@@ -1,8 +1,5 @@
use std::future::Future;
-use nostr::nips::nip04;
-use nostr::nips::nip44;
-use nostr::nips::nip44::Version;
use radroots_nostr::prelude::{
RadrootsNostrEvent, RadrootsNostrEventBuilder, RadrootsNostrFilter, RadrootsNostrKind,
RadrootsNostrPublicKey, RadrootsNostrRelayPoolNotification, RadrootsNostrRelayUrl,
@@ -75,12 +72,10 @@ impl MycNip46Handler {
&self,
event: &RadrootsNostrEvent,
) -> Result<RadrootsNostrConnectRequestMessage, MycError> {
- let decrypted = nip44::decrypt(
- self.signer.signer_identity().keys().secret_key(),
- &event.pubkey,
- &event.content,
- )
- .map_err(|err| MycError::Nip46Decrypt(err.to_string()))?;
+ let decrypted = self
+ .signer
+ .signer_identity()
+ .nip44_decrypt(&event.pubkey, &event.content)?;
serde_json::from_str(&decrypted)
.map_err(radroots_nostr_connect::prelude::RadrootsNostrConnectError::from)
.map_err(Into::into)
@@ -95,13 +90,10 @@ impl MycNip46Handler {
let envelope = response.into_envelope(request_id.into())?;
let payload = serde_json::to_string(&envelope)
.map_err(|err| MycError::Nip46Encrypt(err.to_string()))?;
- let ciphertext = nip44::encrypt(
- self.signer.signer_identity().keys().secret_key(),
- &client_public_key,
- payload,
- Version::V2,
- )
- .map_err(|err| MycError::Nip46Encrypt(err.to_string()))?;
+ let ciphertext = self
+ .signer
+ .signer_identity()
+ .nip44_encrypt(&client_public_key, payload)?;
Ok(RadrootsNostrEventBuilder::new(
radroots_nostr_kind(RADROOTS_NOSTR_CONNECT_RPC_KIND),
@@ -541,7 +533,11 @@ impl MycNip46Handler {
});
}
- match unsigned_event.sign_with_keys(self.signer.user_identity().keys()) {
+ match self
+ .signer
+ .user_identity()
+ .sign_unsigned_event(unsigned_event, "managed user sign_event")
+ {
Ok(event) => Ok(RadrootsNostrConnectResponse::SignedEvent(event)),
Err(error) => Ok(RadrootsNostrConnectResponse::Error {
result: None,
@@ -554,12 +550,15 @@ impl MycNip46Handler {
&self,
request: RadrootsNostrConnectRequest,
) -> Result<RadrootsNostrConnectResponse, MycError> {
- let user_secret_key = self.signer.user_identity().keys().secret_key();
Ok(match request {
RadrootsNostrConnectRequest::Nip04Encrypt {
public_key,
plaintext,
- } => match nip04::encrypt(user_secret_key, &public_key, plaintext) {
+ } => match self
+ .signer
+ .user_identity()
+ .nip04_encrypt(&public_key, plaintext)
+ {
Ok(ciphertext) => RadrootsNostrConnectResponse::Nip04Encrypt(ciphertext),
Err(error) => RadrootsNostrConnectResponse::Error {
result: None,
@@ -569,7 +568,11 @@ impl MycNip46Handler {
RadrootsNostrConnectRequest::Nip04Decrypt {
public_key,
ciphertext,
- } => match nip04::decrypt(user_secret_key, &public_key, ciphertext) {
+ } => match self
+ .signer
+ .user_identity()
+ .nip04_decrypt(&public_key, ciphertext)
+ {
Ok(plaintext) => RadrootsNostrConnectResponse::Nip04Decrypt(plaintext),
Err(error) => RadrootsNostrConnectResponse::Error {
result: None,
@@ -579,7 +582,11 @@ impl MycNip46Handler {
RadrootsNostrConnectRequest::Nip44Encrypt {
public_key,
plaintext,
- } => match nip44::encrypt(user_secret_key, &public_key, plaintext, Version::V2) {
+ } => match self
+ .signer
+ .user_identity()
+ .nip44_encrypt(&public_key, plaintext)
+ {
Ok(ciphertext) => RadrootsNostrConnectResponse::Nip44Encrypt(ciphertext),
Err(error) => RadrootsNostrConnectResponse::Error {
result: None,
@@ -589,7 +596,11 @@ impl MycNip46Handler {
RadrootsNostrConnectRequest::Nip44Decrypt {
public_key,
ciphertext,
- } => match nip44::decrypt(user_secret_key, &public_key, ciphertext) {
+ } => match self
+ .signer
+ .user_identity()
+ .nip44_decrypt(&public_key, ciphertext)
+ {
Ok(plaintext) => RadrootsNostrConnectResponse::Nip44Decrypt(plaintext),
Err(error) => RadrootsNostrConnectResponse::Error {
result: None,
@@ -692,18 +703,22 @@ impl MycNip46Service {
let response_event =
self.handler
.build_response_event(event.pubkey, request_id.as_str(), response)?;
- let response_event =
- match response_event.sign_with_keys(self.handler.signer.signer_identity().keys()) {
- Ok(event) => event,
- Err(error) => {
- self.record_listener_publish_local_rejection(
- connection_id.as_ref(),
- request_id.as_str(),
- format!("failed to sign NIP-46 response event: {error}"),
- );
- continue;
- }
- };
+ let response_event = match self
+ .handler
+ .signer
+ .signer_identity()
+ .sign_event_builder(response_event, "NIP-46 response")
+ {
+ Ok(event) => event,
+ Err(error) => {
+ self.record_listener_publish_local_rejection(
+ connection_id.as_ref(),
+ request_id.as_str(),
+ format!("failed to sign NIP-46 response event: {error}"),
+ );
+ continue;
+ }
+ };
let mut workflow_id = None;
if let Some(connect_connection_id) = consume_connect_secret_for.as_ref() {
@@ -1296,8 +1311,9 @@ mod tests {
let response_builder = handler
.build_response_event(event.pubkey, "req-1", RadrootsNostrConnectResponse::Pong)
.expect("response builder");
- let response_event = response_builder
- .sign_with_keys(runtime.signer_identity().keys())
+ let response_event = runtime
+ .signer_identity()
+ .sign_event_builder(response_builder, "test response")
.expect("sign response");
let decrypted = nip44::decrypt(
client_keys().secret_key(),
diff --git a/tests/nip46_e2e.rs b/tests/nip46_e2e.rs
@@ -6,8 +6,8 @@ use std::time::Duration;
use futures_util::{SinkExt, StreamExt};
use myc::control;
use myc::{
- MycConfig, MycConnectionApproval, MycDeliveryOutboxKind, MycDeliveryOutboxRecord,
- MycDeliveryOutboxStatus, MycDiscoveryContext, MycDiscoveryLiveStatus,
+ MycActiveIdentity, MycConfig, MycConnectionApproval, MycDeliveryOutboxKind,
+ MycDeliveryOutboxRecord, MycDeliveryOutboxStatus, MycDiscoveryContext, MycDiscoveryLiveStatus,
MycDiscoveryRelayFetchStatus, MycDiscoveryRepairOutcome, MycOperationAuditKind,
MycOperationAuditOutcome, MycOperationAuditRecord, MycRuntime, MycRuntimeAuditBackend,
MycSignerStateBackend, MycTransportDeliveryPolicy, diff_live_nip89, fetch_live_nip89,
@@ -617,14 +617,17 @@ fn build_external_request_event(
.expect("sign external request event")
}
-fn build_signer_noise_event(signer_identity: &RadrootsIdentity, created_at_unix: u64) -> Event {
- EventBuilder::new(
- Kind::Custom(RADROOTS_NOSTR_CONNECT_RPC_KIND),
- "non-nip44-signer-noise",
- )
- .custom_created_at(Timestamp::from(created_at_unix))
- .sign_with_keys(signer_identity.keys())
- .expect("sign noise event")
+fn build_signer_noise_event(signer_identity: &MycActiveIdentity, created_at_unix: u64) -> Event {
+ signer_identity
+ .sign_event_builder(
+ RadrootsNostrEventBuilder::new(
+ RadrootsNostrKind::Custom(RADROOTS_NOSTR_CONNECT_RPC_KIND),
+ "non-nip44-signer-noise",
+ )
+ .custom_created_at(Timestamp::from(created_at_unix)),
+ "signer noise event",
+ )
+ .expect("sign noise event")
}
fn decrypt_response(
@@ -1660,12 +1663,16 @@ async fn startup_recovery_republishes_queued_listener_connect_secret_job() -> Te
.with_approval_requirement(RadrootsNostrSignerApprovalRequirement::NotRequired),
)?;
let workflow = manager.begin_connect_secret_publish_finalization(&connection.connection_id)?;
- let event = RadrootsNostrEventBuilder::new(
- RadrootsNostrKind::Custom(RADROOTS_NOSTR_CONNECT_RPC_KIND),
- "startup-recovery",
- )
- .sign_with_keys(runtime.signer_identity().keys())
- .map_err(|error| format!("failed to sign startup recovery event: {error}"))?;
+ let event = runtime
+ .signer_identity()
+ .sign_event_builder(
+ RadrootsNostrEventBuilder::new(
+ RadrootsNostrKind::Custom(RADROOTS_NOSTR_CONNECT_RPC_KIND),
+ "startup-recovery",
+ ),
+ "startup recovery",
+ )
+ .map_err(|error| format!("failed to sign startup recovery event: {error}"))?;
let outbox_record = MycDeliveryOutboxRecord::new(
MycDeliveryOutboxKind::ListenerResponsePublish,
event,
@@ -1755,12 +1762,18 @@ async fn startup_recovery_republishes_queued_connect_accept_job() -> TestResult<
.with_approval_requirement(RadrootsNostrSignerApprovalRequirement::NotRequired),
)?;
let workflow = manager.begin_connect_secret_publish_finalization(&connection.connection_id)?;
- let event = RadrootsNostrEventBuilder::new(
- RadrootsNostrKind::Custom(RADROOTS_NOSTR_CONNECT_RPC_KIND),
- "startup-recovery-connect-accept",
- )
- .sign_with_keys(runtime.signer_identity().keys())
- .map_err(|error| format!("failed to sign startup recovery connect-accept event: {error}"))?;
+ let event = runtime
+ .signer_identity()
+ .sign_event_builder(
+ RadrootsNostrEventBuilder::new(
+ RadrootsNostrKind::Custom(RADROOTS_NOSTR_CONNECT_RPC_KIND),
+ "startup-recovery-connect-accept",
+ ),
+ "startup recovery connect accept",
+ )
+ .map_err(|error| {
+ format!("failed to sign startup recovery connect-accept event: {error}")
+ })?;
let outbox_record = MycDeliveryOutboxRecord::new(
MycDeliveryOutboxKind::ConnectAcceptPublish,
event,
@@ -1854,12 +1867,16 @@ async fn startup_recovery_republishes_queued_auth_replay_job() -> TestResult<()>
ping_request_message("startup-recovery-auth"),
)?;
let workflow = manager.begin_auth_replay_publish_finalization(&connection.connection_id)?;
- let event = RadrootsNostrEventBuilder::new(
- RadrootsNostrKind::Custom(RADROOTS_NOSTR_CONNECT_RPC_KIND),
- "startup-recovery-auth-replay",
- )
- .sign_with_keys(runtime.signer_identity().keys())
- .map_err(|error| format!("failed to sign startup recovery auth-replay event: {error}"))?;
+ let event = runtime
+ .signer_identity()
+ .sign_event_builder(
+ RadrootsNostrEventBuilder::new(
+ RadrootsNostrKind::Custom(RADROOTS_NOSTR_CONNECT_RPC_KIND),
+ "startup-recovery-auth-replay",
+ ),
+ "startup recovery auth replay",
+ )
+ .map_err(|error| format!("failed to sign startup recovery auth-replay event: {error}"))?;
let outbox_record = MycDeliveryOutboxRecord::new(
MycDeliveryOutboxKind::AuthReplayPublish,
event,
diff --git a/tests/operability_cli.rs b/tests/operability_cli.rs
@@ -2,7 +2,7 @@ use std::path::Path;
use std::process::Command;
use myc::{
- MycDeliveryOutboxKind, MycDeliveryOutboxRecord, MycOperationAuditKind,
+ MycActiveIdentity, MycDeliveryOutboxKind, MycDeliveryOutboxRecord, MycOperationAuditKind,
MycOperationAuditOutcome, MycOperationAuditRecord, MycRuntime,
};
use radroots_identity::RadrootsIdentity;
@@ -53,9 +53,12 @@ MYC_TRANSPORT_CONNECT_TIMEOUT_SECS=1\n",
env_path
}
-fn signed_event(identity: &RadrootsIdentity) -> nostr::Event {
- RadrootsNostrEventBuilder::new(RadrootsNostrKind::Custom(24133), "operability")
- .sign_with_keys(identity.keys())
+fn signed_event(identity: &MycActiveIdentity) -> nostr::Event {
+ identity
+ .sign_event_builder(
+ RadrootsNostrEventBuilder::new(RadrootsNostrKind::Custom(24133), "operability"),
+ "operability test event",
+ )
.expect("sign event")
}
diff --git a/tests/operability_e2e.rs b/tests/operability_e2e.rs
@@ -3,9 +3,10 @@ use std::time::Duration;
use std::time::{SystemTime, UNIX_EPOCH};
use myc::{
- MycConfig, MycDeliveryOutboxKind, MycDeliveryOutboxRecord, MycOperationAuditKind,
- MycOperationAuditOutcome, MycOperationAuditRecord, MycRuntime, MycRuntimeAuditBackend,
- MycRuntimeStatus, MycSignerStateBackend, MycTransportDeliveryPolicy, collect_status_full,
+ MycActiveIdentity, MycConfig, MycDeliveryOutboxKind, MycDeliveryOutboxRecord,
+ MycOperationAuditKind, MycOperationAuditOutcome, MycOperationAuditRecord, MycRuntime,
+ MycRuntimeAuditBackend, MycRuntimeStatus, MycSignerStateBackend, MycTransportDeliveryPolicy,
+ collect_status_full,
};
use radroots_identity::RadrootsIdentity;
use radroots_nostr::prelude::{
@@ -122,9 +123,12 @@ fn write_test_identity(path: &Path, secret_key: &str) {
.expect("write identity");
}
-fn signed_delivery_event(identity: &RadrootsIdentity, content: &str) -> nostr::Event {
- RadrootsNostrEventBuilder::new(RadrootsNostrKind::Custom(24133), content)
- .sign_with_keys(identity.keys())
+fn signed_delivery_event(identity: &MycActiveIdentity, content: &str) -> nostr::Event {
+ identity
+ .sign_event_builder(
+ RadrootsNostrEventBuilder::new(RadrootsNostrKind::Custom(24133), content),
+ "operability delivery test event",
+ )
.expect("sign event")
}