libchat/crates/client/src/delegate.rs
2026-06-21 21:31:29 -07:00

151 lines
4.3 KiB
Rust

use crypto::{Ed25519SigningKey, Ed25519VerifyingKey};
use libchat::{IdentId, IdentityProvider, trunc};
use crate::ClientError;
type AccountAddr = String;
pub struct DelegateSigner {
signing_key: Ed25519SigningKey,
verifying_key: Ed25519VerifyingKey,
identifier: IdentId,
account_addr: Option<AccountAddr>,
}
impl DelegateSigner {
pub fn random() -> Self {
let signing_key = Ed25519SigningKey::generate();
let verifying_key = signing_key.verifying_key();
let identifier = DelegateCredential::unassociated(&verifying_key).into();
Self {
signing_key,
verifying_key,
identifier,
account_addr: None,
}
}
pub fn associate(&mut self, account_addr: AccountAddr) {
self.identifier =
DelegateCredential::associated(&self.verifying_key, account_addr.as_str()).into();
self.account_addr = Some(account_addr);
}
pub fn account_addr(&self) -> Option<&str> {
self.account_addr.as_deref()
}
}
impl IdentityProvider for DelegateSigner {
fn id(&self) -> libchat::IdentIdRef<'_> {
&self.identifier
}
fn display_name(&self) -> String {
trunc(self.identifier.as_str())
}
fn sign(&self, payload: &[u8]) -> crypto::Ed25519Signature {
self.signing_key.sign(payload)
}
fn public_key(&self) -> &Ed25519VerifyingKey {
&self.verifying_key
}
}
/// Represents the senders information for received frames.
#[derive(Debug)]
pub struct DelegateCredential {
delegate_id: Ed25519VerifyingKey,
account_addr: Option<AccountAddr>,
}
impl DelegateCredential {
const TAG_DELEGATE_ID: u8 = 0x01;
const TAG_ACCOUNT_ADDR: u8 = 0x02;
pub fn unassociated(delegate: &Ed25519VerifyingKey) -> Self {
Self {
delegate_id: delegate.clone(),
account_addr: None,
}
}
pub fn associated(delegate: &Ed25519VerifyingKey, account: &str) -> Self {
Self {
delegate_id: delegate.clone(),
account_addr: Some(account.to_string()),
}
}
pub fn to_vec(self) -> Vec<u8> {
let mut data = Vec::new();
data.extend_from_slice(&[0x23, 0x23]);
let key_bytes = self.delegate_id.as_ref();
data.extend_from_slice(&[Self::TAG_DELEGATE_ID, key_bytes.len() as u8]);
data.extend_from_slice(key_bytes);
if let Some(addr) = self.account_addr {
let addr_bytes = addr.as_bytes();
data.extend_from_slice(&[Self::TAG_ACCOUNT_ADDR, addr_bytes.len() as u8]);
data.extend_from_slice(addr_bytes);
}
data
}
}
impl From<DelegateCredential> for Vec<u8> {
fn from(value: DelegateCredential) -> Self {
value.to_vec()
}
}
impl From<Vec<u8>> for DelegateCredential {
fn from(value: Vec<u8>) -> Self {
assert_eq!(&value[..2], &[0x23, 0x23], "invalid magic bytes");
let mut delegate_id = None;
let mut account_addr = None;
let mut i = 2;
while i + 2 <= value.len() {
let tag = value[i];
let len = value[i + 1] as usize;
i += 2;
let v = &value[i..i + len];
i += len;
match tag {
DelegateCredential::TAG_DELEGATE_ID => {
let bytes: &[u8; 32] = v.try_into().expect("invalid delegate_id length");
delegate_id = Some(
Ed25519VerifyingKey::from_bytes(bytes).expect("invalid verifying key"),
);
}
DelegateCredential::TAG_ACCOUNT_ADDR => {
account_addr =
Some(String::from_utf8(v.to_vec()).expect("invalid account_addr utf8"));
}
_ => {}
}
}
Self {
delegate_id: delegate_id.expect("missing delegate_id TLV field"),
account_addr,
}
}
}
impl From<DelegateCredential> for IdentId {
fn from(value: DelegateCredential) -> Self {
IdentId::new(hex::encode(value.to_vec()))
}
}
impl TryFrom<IdentId> for DelegateCredential {
type Error = ClientError;
fn try_from(value: IdentId) -> Result<Self, Self::Error> {
Ok(hex::decode(value.as_str())
.map_err(|e| ClientError::BadlyFormedCredential)?
.into())
}
}