1use ed25519_dalek::{PublicKey as Ed25519PublicKey, Signature, Signer, Verifier};
9use hkdf::Hkdf;
10use rand::rngs::OsRng;
11use sha2::Sha256;
12use x25519_dalek::{PublicKey as X25519PublicKey, StaticSecret};
13
14use crate::Identity;
15
16pub struct E2eeKeypair {
17 pub secret: StaticSecret,
19 pub public: X25519PublicKey,
21}
22
23pub fn generate_e2ee_keypair() -> E2eeKeypair {
25 let secret = StaticSecret::new(OsRng);
26 let public = X25519PublicKey::from(&secret);
27 E2eeKeypair { secret, public }
28}
29
30pub fn sign_e2ee_public(identity: &Identity, session_id: &[u8], public: &X25519PublicKey) -> Vec<u8> {
32 let msg = e2ee_message(session_id, public);
33 let signature: Signature = identity.keypair.sign(&msg);
34 signature.to_bytes().to_vec()
35}
36
37pub fn verify_e2ee_public(
39 peer_public: &Ed25519PublicKey,
40 session_id: &[u8],
41 public: &X25519PublicKey,
42 signature: &[u8],
43) -> bool {
44 let Ok(signature) = Signature::from_bytes(signature) else {
45 return false;
46 };
47 let msg = e2ee_message(session_id, public);
48 peer_public.verify(&msg, &signature).is_ok()
49}
50
51pub fn derive_e2ee_shared_key(
53 secret: &StaticSecret,
54 remote_public: &X25519PublicKey,
55 session_id: &[u8],
56) -> [u8; 32] {
57 let shared = secret.diffie_hellman(remote_public);
58 let hk = Hkdf::<Sha256>::new(Some(session_id), shared.as_bytes());
59 let mut out = [0u8; 32];
60 hk.expand(b"rift-e2ee-v1", &mut out)
61 .expect("hkdf expand");
62 out
63}
64
65fn e2ee_message(session_id: &[u8], public: &X25519PublicKey) -> Vec<u8> {
67 let mut msg = Vec::with_capacity(16 + session_id.len() + 32);
68 msg.extend_from_slice(b"rift-e2ee");
69 msg.extend_from_slice(session_id);
70 msg.extend_from_slice(public.as_bytes());
71 msg
72}
73
74pub fn public_key_from_bytes(bytes: [u8; 32]) -> X25519PublicKey {
76 X25519PublicKey::from(bytes)
77}
78
79pub fn ed25519_public_from_bytes(bytes: &[u8]) -> Option<Ed25519PublicKey> {
81 Ed25519PublicKey::from_bytes(bytes).ok()
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn e2ee_sign_verify_roundtrip() {
90 let identity = Identity::generate();
91 let session = [7u8; 32];
92 let kp = generate_e2ee_keypair();
93 let sig = sign_e2ee_public(&identity, &session, &kp.public);
94 let ok = verify_e2ee_public(&identity.keypair.public, &session, &kp.public, &sig);
95 assert!(ok);
96 }
97
98 #[test]
99 fn e2ee_shared_key_matches() {
100 let session = [9u8; 32];
101 let a = generate_e2ee_keypair();
102 let b = generate_e2ee_keypair();
103 let ka = derive_e2ee_shared_key(&a.secret, &b.public, &session);
104 let kb = derive_e2ee_shared_key(&b.secret, &a.public, &session);
105 assert_eq!(ka, kb);
106 }
107
108 #[test]
109 fn e2ee_bad_signature_rejected() {
110 let identity = Identity::generate();
111 let other = Identity::generate();
112 let session = [1u8; 32];
113 let kp = generate_e2ee_keypair();
114 let sig = sign_e2ee_public(&identity, &session, &kp.public);
115 let ok = verify_e2ee_public(&other.keypair.public, &session, &kp.public, &sig);
116 assert!(!ok);
117 }
118}