use crate::error::{EnigmaIdentityError, Result};
use crate::types::{LocalUser, X3dhBundle, X3dhResponderKeys};
use ed25519_dalek::{Signer, SigningKey, VerifyingKey};
use rand::rngs::OsRng;
use uuid::Uuid;
use x25519_dalek::{PublicKey as XPublicKey, StaticSecret as XSecret};
const MAX_USERNAME_LEN: usize = 64;
pub struct LocalIdentity {
user: LocalUser,
identity_signing: SigningKey,
signed_prekey: XSecret,
signed_prekey_signature: [u8; 64],
}
impl LocalIdentity {
pub fn new(username: &str) -> Result<Self> {
let username = Self::validate_username(username)?;
let mut rng = OsRng;
let identity_signing = SigningKey::generate(&mut rng);
let signed_prekey = XSecret::random_from_rng(&mut rng);
let signed_prekey_public = XPublicKey::from(&signed_prekey);
let payload =
Self::signed_prekey_payload(&identity_signing.verifying_key(), &signed_prekey_public);
let signature = identity_signing.sign(&payload);
let signed_prekey_signature = signature.to_bytes();
Ok(Self {
user: LocalUser {
uuid: Uuid::new_v4(),
username: username.to_string(),
},
identity_signing,
signed_prekey,
signed_prekey_signature,
})
}
pub fn user(&self) -> &LocalUser {
&self.user
}
pub fn bundle(&self) -> X3dhBundle {
let id_pk = self.identity_signing.verifying_key().to_bytes();
let spk_pk = XPublicKey::from(&self.signed_prekey).to_bytes();
X3dhBundle {
username: self.user.username.clone(),
identity_public_key: id_pk,
signed_prekey_public_key: spk_pk,
signed_prekey_signature: self.signed_prekey_signature,
}
}
pub fn responder_keys(&self) -> X3dhResponderKeys {
let id_pk = self.identity_signing.verifying_key().to_bytes();
let id_sk = self.identity_signing.to_bytes();
let spk_pk = XPublicKey::from(&self.signed_prekey).to_bytes();
let spk_sk = self.signed_prekey.to_bytes();
X3dhResponderKeys {
identity_public_key: id_pk,
identity_secret_key: id_sk,
signed_prekey_public_key: spk_pk,
signed_prekey_secret_key: spk_sk,
signed_prekey_signature: self.signed_prekey_signature,
}
}
pub fn verify_bundle(bundle: &X3dhBundle) -> Result<()> {
Self::validate_username(&bundle.username)?;
let vk = VerifyingKey::from_bytes(&bundle.identity_public_key)
.map_err(|_| EnigmaIdentityError::InvalidData)?;
let spk_pk = XPublicKey::from(bundle.signed_prekey_public_key);
let payload = Self::signed_prekey_payload(&vk, &spk_pk);
let sig = ed25519_dalek::Signature::from_bytes(&bundle.signed_prekey_signature);
vk.verify_strict(&payload, &sig)
.map_err(|_| EnigmaIdentityError::InvalidBundleSignature)
}
fn signed_prekey_payload(vk: &VerifyingKey, signed_prekey_public: &XPublicKey) -> Vec<u8> {
let mut out = Vec::with_capacity(64);
out.extend_from_slice(&vk.to_bytes());
out.extend_from_slice(&signed_prekey_public.to_bytes());
out
}
fn validate_username(input: &str) -> Result<&str> {
let trimmed = input.trim();
if trimmed.is_empty() || trimmed.len() > MAX_USERNAME_LEN {
return Err(EnigmaIdentityError::InvalidUsername);
}
Ok(trimmed)
}
}