use crate::error::{EnigmaIdentityError, Result};
use crate::types::{SharedSecret, X3dhBundle, X3dhInitiation, X3dhResponderKeys};
use hkdf::Hkdf;
use rand::rngs::OsRng;
use sha2::Sha256;
use x25519_dalek::{PublicKey as XPublicKey, StaticSecret as XSecret};
pub fn x3dh_initiate(bundle: &X3dhBundle) -> Result<(X3dhInitiation, SharedSecret)> {
crate::identity::LocalIdentity::verify_bundle(bundle)?;
let mut rng = OsRng;
let eph = XSecret::random_from_rng(&mut rng);
let eph_pk = XPublicKey::from(&eph);
let responder_spk = XPublicKey::from(bundle.signed_prekey_public_key);
let dh = eph.diffie_hellman(&responder_spk);
let shared = derive_shared_secret(&dh.to_bytes(), b"enigma-x3dh-v1")?;
Ok((
X3dhInitiation {
initiator_ephemeral_public_key: eph_pk.to_bytes(),
},
shared,
))
}
pub fn x3dh_respond(
responder: &X3dhResponderKeys,
initiation: &X3dhInitiation,
) -> Result<SharedSecret> {
let initiator_eph_pk = XPublicKey::from(initiation.initiator_ephemeral_public_key);
let responder_spk = XSecret::from(responder.signed_prekey_secret_key);
let dh = responder_spk.diffie_hellman(&initiator_eph_pk);
derive_shared_secret(&dh.to_bytes(), b"enigma-x3dh-v1")
}
fn derive_shared_secret(ikm: &[u8], info: &[u8]) -> Result<SharedSecret> {
let hk = Hkdf::<Sha256>::new(None, ikm);
let mut out = [0u8; 32];
hk.expand(info, &mut out)
.map_err(|_| EnigmaIdentityError::CryptoError)?;
Ok(SharedSecret::new(out))
}