rings_core/message/handlers/
e2e.rs1use 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}