enigma-node-types 0.0.1

Canonical node-facing types and strict codecs for Enigma (UserId hashing, identities, presence, relay envelopes).
Documentation
use std::convert::TryFrom;

use serde::{Deserialize, Serialize};

use crate::error::{EnigmaNodeTypesError, Result};
use crate::user_id::{normalize_username, UserId};

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct PublicIdentity {
    pub user_id: UserId,
    pub username_hint: Option<String>,
    pub signing_public_key: Vec<u8>,
    pub encryption_public_key: Vec<u8>,
    pub signature: Vec<u8>,
    pub created_at_ms: u64,
}

impl PublicIdentity {
    pub fn validate(&self) -> Result<()> {
        if self.signing_public_key.is_empty() {
            return Err(EnigmaNodeTypesError::InvalidField("signing_public_key"));
        }
        if self.encryption_public_key.is_empty() {
            return Err(EnigmaNodeTypesError::InvalidField("encryption_public_key"));
        }
        if self.signature.is_empty() {
            return Err(EnigmaNodeTypesError::InvalidField("signature"));
        }
        if self.created_at_ms == 0 {
            return Err(EnigmaNodeTypesError::InvalidField("created_at_ms"));
        }
        if let Some(hint) = &self.username_hint {
            normalize_username(hint)?;
        }
        Ok(())
    }
}

pub fn signed_payload(
    username_hint: &str,
    signing_public_key: &[u8],
    encryption_public_key: &[u8],
) -> Vec<u8> {
    let hint_bytes = username_hint.as_bytes();
    let hint_len = match u32::try_from(hint_bytes.len()) {
        Ok(v) => v,
        Err(_) => u32::MAX,
    };
    let signing_len = match u32::try_from(signing_public_key.len()) {
        Ok(v) => v,
        Err(_) => u32::MAX,
    };
    let encryption_len = match u32::try_from(encryption_public_key.len()) {
        Ok(v) => v,
        Err(_) => u32::MAX,
    };
    let mut payload = Vec::with_capacity(
        hint_bytes.len()
            .saturating_add(signing_public_key.len())
            .saturating_add(encryption_public_key.len())
            .saturating_add(12),
    );
    payload.extend_from_slice(&hint_len.to_be_bytes());
    payload.extend_from_slice(hint_bytes);
    payload.extend_from_slice(&signing_len.to_be_bytes());
    payload.extend_from_slice(signing_public_key);
    payload.extend_from_slice(&encryption_len.to_be_bytes());
    payload.extend_from_slice(encryption_public_key);
    payload
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct RegisterRequest {
    pub identity: PublicIdentity,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct RegisterResponse {
    pub ok: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct ResolveResponse {
    pub identity: Option<PublicIdentity>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct CheckUserResponse {
    pub exists: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct SyncRequest {
    pub identities: Vec<PublicIdentity>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct SyncResponse {
    pub merged: usize,
}