iris-chat 0.1.32

Iris Chat command line client and shared encrypted chat core
Documentation
use super::*;
use sha2::{Digest, Sha256};
use uuid::Uuid;

pub(super) fn build_nostr_identity_add_app_key_event(
    owner_keys: &Keys,
    owner_pubkey: PublicKey,
    device_pubkey: PublicKey,
    parents: Vec<String>,
    key_added_at_secs: u64,
    event_created_at_secs: u64,
    can_admin: bool,
) -> anyhow::Result<Event> {
    if owner_keys.public_key() != owner_pubkey {
        anyhow::bail!("NostrIdentity roster op signer must be the owner key");
    }

    let profile_id = nostr_identity_profile_id_for_owner(owner_pubkey);
    let key_added_at = i64::try_from(key_added_at_secs)
        .map_err(|_| anyhow::anyhow!("NostrIdentity key added_at overflows i64"))?;
    let event_created_at = i64::try_from(event_created_at_secs)
        .map_err(|_| anyhow::anyhow!("NostrIdentity event created_at overflows i64"))?;
    let capabilities = if can_admin {
        nostr_identity::NostrIdentityCapabilities::app_admin()
    } else {
        nostr_identity::NostrIdentityCapabilities::app_writer()
    };
    let facet = nostr_identity::NostrIdentityFacet::app_key(
        device_pubkey.to_hex(),
        key_added_at,
        None,
        capabilities,
    );

    nostr_identity::build_nostr_identity_roster_op_event(
        owner_keys,
        profile_id,
        parents,
        None,
        nostr_identity::NostrIdentityRosterOp::AddFacet { facet },
        event_created_at,
    )
    .map_err(|error| anyhow::anyhow!(error))
}

pub(super) fn build_nostr_identity_owner_admin_event(
    owner_keys: &Keys,
    owner_pubkey: PublicKey,
    parents: Vec<String>,
    added_at_secs: u64,
    event_created_at_secs: u64,
) -> anyhow::Result<Event> {
    if owner_keys.public_key() != owner_pubkey {
        anyhow::bail!("NostrIdentity roster op signer must be the owner key");
    }

    let profile_id = nostr_identity_profile_id_for_owner(owner_pubkey);
    let added_at = i64::try_from(added_at_secs)
        .map_err(|_| anyhow::anyhow!("NostrIdentity owner added_at overflows i64"))?;
    let event_created_at = i64::try_from(event_created_at_secs)
        .map_err(|_| anyhow::anyhow!("NostrIdentity event created_at overflows i64"))?;
    let facet = nostr_identity::NostrIdentityFacet::app_key(
        owner_pubkey.to_hex(),
        added_at,
        None,
        nostr_identity::NostrIdentityCapabilities::app_admin(),
    );

    nostr_identity::build_nostr_identity_roster_op_event(
        owner_keys,
        profile_id,
        parents,
        None,
        nostr_identity::NostrIdentityRosterOp::AddFacet { facet },
        event_created_at,
    )
    .map_err(|error| anyhow::anyhow!(error))
}

fn nostr_identity_profile_id_for_owner(owner_pubkey: PublicKey) -> nostr_identity::NostrIdentityId {
    let mut hasher = Sha256::new();
    hasher.update(b"iris-chat:nostr-identity-profile:");
    hasher.update(owner_pubkey.to_hex().as_bytes());
    let digest = hasher.finalize();
    let mut bytes = [0u8; 16];
    for (target, source) in bytes.iter_mut().zip(digest.iter()) {
        *target = *source;
    }
    bytes[6] = (bytes[6] & 0x0f) | 0x40;
    bytes[8] = (bytes[8] & 0x3f) | 0x80;
    nostr_identity::NostrIdentityId::from_uuid(Uuid::from_bytes(bytes))
}