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