Skip to main content

rings_core/message/handlers/
e2e.rs

1use async_trait::async_trait;
2
3use crate::error::Result;
4use crate::message::e2e::E2eHandshakeRequest;
5use crate::message::e2e::E2eHandshakeResponse;
6use crate::message::e2e::E2eStreamFrame;
7use crate::message::effects::CoreEffect;
8use crate::message::HandleMsg;
9use crate::message::Message;
10use crate::message::MessageHandler;
11use crate::message::MessagePayload;
12use crate::message::MessageVerificationExt;
13use crate::message::PayloadSender;
14
15fn e2e_local_or_forward_effects<'payload>(
16    local: crate::dht::Did,
17    ctx: &'payload MessagePayload,
18) -> Option<CoreEffect<'payload>> {
19    if ctx.should_forward_from(local) {
20        Some(CoreEffect::forward_payload(ctx, None))
21    } else {
22        None
23    }
24}
25
26async fn run_e2e_local_or_forward<'payload>(
27    handler: &MessageHandler,
28    ctx: &'payload MessagePayload,
29    local_effects: impl FnOnce() -> Result<Vec<CoreEffect<'payload>>>,
30) -> Result<()> {
31    if let Some(effect) = e2e_local_or_forward_effects(handler.dht.did, ctx) {
32        return handler.run_effects([effect]).await;
33    }
34
35    handler.run_effects(local_effects()?).await
36}
37
38fn e2e_handshake_response_effect<'payload>(
39    ctx: &MessagePayload,
40    msg: &E2eHandshakeRequest,
41    responder_public_key: crate::ecc::PublicKey<33>,
42) -> Result<CoreEffect<'payload>> {
43    msg.verify_requester(ctx.signer())?;
44    Ok(CoreEffect::send_message(
45        Message::E2eHandshakeResponse(E2eHandshakeResponse::new(responder_public_key)),
46        ctx.signer(),
47    ))
48}
49
50#[cfg_attr(all(feature = "wasm", target_family = "wasm"), async_trait(?Send))]
51#[cfg_attr(not(all(feature = "wasm", target_family = "wasm")), async_trait)]
52impl HandleMsg<E2eHandshakeRequest> for MessageHandler {
53    async fn handle(&self, ctx: &MessagePayload, msg: &E2eHandshakeRequest) -> Result<()> {
54        run_e2e_local_or_forward(self, ctx, || {
55            let responder_public_key = self.transport.session_sk().session().account_pubkey()?;
56            Ok(vec![e2e_handshake_response_effect(
57                ctx,
58                msg,
59                responder_public_key,
60            )?])
61        })
62        .await
63    }
64}
65
66#[cfg_attr(all(feature = "wasm", target_family = "wasm"), async_trait(?Send))]
67#[cfg_attr(not(all(feature = "wasm", target_family = "wasm")), async_trait)]
68impl HandleMsg<E2eHandshakeResponse> for MessageHandler {
69    async fn handle(&self, ctx: &MessagePayload, msg: &E2eHandshakeResponse) -> Result<()> {
70        run_e2e_local_or_forward(self, ctx, || {
71            msg.verify_responder(ctx.signer())?;
72            Ok(Vec::new())
73        })
74        .await
75    }
76}
77
78#[cfg_attr(all(feature = "wasm", target_family = "wasm"), async_trait(?Send))]
79#[cfg_attr(not(all(feature = "wasm", target_family = "wasm")), async_trait)]
80impl HandleMsg<E2eStreamFrame> for MessageHandler {
81    async fn handle(&self, ctx: &MessagePayload, msg: &E2eStreamFrame) -> Result<()> {
82        run_e2e_local_or_forward(self, ctx, || {
83            msg.verify_sender(ctx.signer())?;
84            Ok(Vec::new())
85        })
86        .await
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use rand::SeedableRng;
93
94    use super::*;
95    use crate::ecc::SecretKey;
96    use crate::error::Error;
97    use crate::message::e2e::encrypt_stream_with_rng;
98    use crate::message::e2e::E2eHandshakeRequest;
99    use crate::session::SessionSk;
100
101    fn e2e_payload(destination: crate::dht::Did) -> Result<MessagePayload> {
102        let sender = SecretKey::random();
103        let recipient = SecretKey::random();
104        let session_sk = SessionSk::new_with_seckey(&sender)?;
105        let mut rng = rand_hc::Hc128Rng::from_entropy();
106        let mut frames = encrypt_stream_with_rng(
107            b"hello",
108            uuid::Uuid::new_v4(),
109            sender.pubkey(),
110            recipient.pubkey(),
111            16,
112            &mut rng,
113        )?;
114        let encrypted = frames
115            .pop()
116            .ok_or_else(|| Error::InvalidMessage("expected one E2E stream frame".to_string()))?;
117        MessagePayload::new_send(
118            Message::E2eStreamFrame(encrypted),
119            &session_sk,
120            destination,
121            destination,
122        )
123    }
124
125    fn signed_handshake_request(
126        signer: &SecretKey,
127        request: E2eHandshakeRequest,
128        destination: crate::dht::Did,
129    ) -> Result<MessagePayload> {
130        let session_sk = SessionSk::new_with_seckey(signer)?;
131        MessagePayload::new_send(
132            Message::E2eHandshakeRequest(request),
133            &session_sk,
134            destination,
135            destination,
136        )
137    }
138
139    #[test]
140    fn test_local_handshake_request_sends_responder_key_to_signer() -> Result<()> {
141        let requester = SecretKey::random();
142        let responder = SecretKey::random();
143        let request = E2eHandshakeRequest::new(requester.pubkey());
144        let payload = signed_handshake_request(&requester, request, responder.address().into())?;
145        let effect = e2e_handshake_response_effect(&payload, &request, responder.pubkey())?;
146
147        match effect {
148            CoreEffect::SendMessage { msg, destination } => {
149                assert_eq!(destination, requester.address().into());
150                match *msg {
151                    Message::E2eHandshakeResponse(response) => {
152                        assert_eq!(response.responder_public_key, responder.pubkey());
153                        response.verify_responder(responder.address().into())?;
154                    }
155                    msg => {
156                        return Err(Error::InvalidMessage(format!(
157                            "expected E2eHandshakeResponse, got {msg:?}"
158                        )))
159                    }
160                }
161            }
162            effect => {
163                return Err(Error::InvalidMessage(format!(
164                    "expected SendMessage effect, got {effect:?}"
165                )))
166            }
167        }
168        Ok(())
169    }
170
171    #[test]
172    fn test_local_handshake_request_rejects_key_not_owned_by_signer() -> Result<()> {
173        let requester = SecretKey::random();
174        let responder = SecretKey::random();
175        let request = E2eHandshakeRequest::new(responder.pubkey());
176        let payload = signed_handshake_request(&requester, request, responder.address().into())?;
177
178        assert!(matches!(
179            e2e_handshake_response_effect(&payload, &request, responder.pubkey()),
180            Err(Error::E2ePublicKeyDidMismatch { .. })
181        ));
182        Ok(())
183    }
184
185    #[test]
186    fn test_remote_e2e_message_forwards_payload() -> Result<()> {
187        let local = SecretKey::random().address().into();
188        let remote = SecretKey::random().address().into();
189        let payload = e2e_payload(remote)?;
190        let effect = e2e_local_or_forward_effects(local, &payload)
191            .ok_or_else(|| Error::InvalidMessage("expected ForwardPayload effect".to_string()))?;
192
193        match effect {
194            CoreEffect::ForwardPayload {
195                payload: forwarded,
196                next_hop,
197            } => {
198                assert!(std::ptr::eq(forwarded, &payload));
199                assert_eq!(next_hop, None);
200            }
201            effect => {
202                return Err(Error::InvalidMessage(format!(
203                    "expected ForwardPayload, got {effect:?}"
204                )))
205            }
206        }
207        Ok(())
208    }
209}