alterion_encrypt/
interceptor.rs1use actix_web::{
3 dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},
4 web, Error, HttpMessage,
5 body::{BoxBody, EitherBody, MessageBody},
6};
7use futures_util::future::{ready, LocalBoxFuture, Ready};
8use futures_util::TryStreamExt;
9use std::{rc::Rc, sync::Arc};
10use tokio::sync::RwLock;
11use alterion_ecdh::{KeyStore, HandshakeStore, ecdh, ecdh_ephemeral};
12use redis::aio::ConnectionManager;
13use crate::tools::crypt::aes_decrypt;
14use crate::tools::serializer::{deserialize_packet, build_signed_response_raw, derive_wrap_key};
15use zeroize::ZeroizeOnDrop;
16
17#[derive(Clone)]
19pub struct DecryptedBody(pub Vec<u8>);
20
21#[derive(Clone, ZeroizeOnDrop)]
24pub struct RequestSessionKeys {
25 pub enc_key: [u8; 32],
26}
27
28pub struct Interceptor {
57 pub key_store: Arc<RwLock<KeyStore>>,
58 pub handshake_store: HandshakeStore,
59 pub replay_store: Option<ConnectionManager>,
60}
61
62impl<S, B> Transform<S, ServiceRequest> for Interceptor
63where
64 S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
65 B: MessageBody + 'static,
66{
67 type Response = ServiceResponse<EitherBody<B>>;
68 type Error = Error;
69 type Transform = InterceptorService<S>;
70 type InitError = ();
71 type Future = Ready<Result<Self::Transform, Self::InitError>>;
72
73 fn new_transform(&self, service: S) -> Self::Future {
74 ready(Ok(InterceptorService {
75 service: Rc::new(service),
76 key_store: self.key_store.clone(),
77 handshake_store: self.handshake_store.clone(),
78 replay_store: self.replay_store.clone(),
79 }))
80 }
81}
82
83pub struct InterceptorService<S> {
84 service: Rc<S>,
85 key_store: Arc<RwLock<KeyStore>>,
86 handshake_store: HandshakeStore,
87 replay_store: Option<ConnectionManager>,
88}
89
90impl<S, B> Service<ServiceRequest> for InterceptorService<S>
91where
92 S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
93 B: MessageBody + 'static,
94{
95 type Response = ServiceResponse<EitherBody<B>>;
96 type Error = Error;
97 type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
98
99 forward_ready!(service);
100
101 fn call(&self, mut req: ServiceRequest) -> Self::Future {
102 let service = self.service.clone();
103 let key_store = self.key_store.clone();
104 let handshake_store = self.handshake_store.clone();
105 let replay_store = self.replay_store.clone();
106
107 Box::pin(async move {
108 let has_body = !matches!(req.method().as_str(), "GET" | "HEAD" | "OPTIONS");
109
110 if has_body {
111 let mut payload = req.take_payload();
112 let mut raw = web::BytesMut::new();
113 while let Some(chunk) = payload
114 .try_next().await
115 .map_err(actix_web::error::ErrorBadRequest)?
116 {
117 raw.extend_from_slice(&chunk);
118 }
119
120 if !raw.is_empty() {
121 match deserialize_packet(&raw) {
122 Ok(packet) => {
123 let client_pk_bytes: [u8; 32] = packet.client_pk.as_ref()
124 .try_into()
125 .map_err(|_| actix_web::error::ErrorBadRequest("client_pk must be 32 bytes"))?;
126
127 let (shared_secret, server_pk) =
128 if packet.key_id.starts_with("hs_") {
129 ecdh_ephemeral(&handshake_store, &packet.key_id, &client_pk_bytes)
130 .await
131 .map_err(|e| actix_web::error::ErrorUnauthorized(e.to_string()))?
132 } else {
133 ecdh(&key_store, &packet.key_id, &client_pk_bytes)
134 .await
135 .map_err(|e| actix_web::error::ErrorUnauthorized(e.to_string()))?
136 };
137
138 let shared_bytes: &[u8; 32] = shared_secret.as_ref()
139 .try_into()
140 .map_err(|_| actix_web::error::ErrorInternalServerError("shared secret length invalid"))?;
141 let wrap_key = derive_wrap_key(shared_bytes, &client_pk_bytes, &server_pk);
142
143 let enc_key_bytes = aes_decrypt(packet.kx.as_ref(), &wrap_key)
144 .map_err(|e| actix_web::error::ErrorUnauthorized(e.to_string()))?;
145 let enc_key: [u8; 32] = enc_key_bytes.as_slice()
146 .try_into()
147 .map_err(|_| actix_web::error::ErrorBadRequest("enc_key must be 32 bytes"))?;
148
149 if let Some(mut redis) = replay_store {
150 let seen_key = format!("replay:seen:{}", hex::encode(packet.kx.as_ref()));
151 let is_new: bool = redis::cmd("SET")
152 .arg(&seen_key).arg(1u8)
153 .arg("NX").arg("EX").arg(60u64)
154 .query_async(&mut redis).await
155 .map(|v: Option<String>| v.is_some())
156 .unwrap_or(true);
157 if !is_new {
158 return Err(actix_web::error::ErrorUnauthorized("replay detected"));
159 }
160 }
161
162 let decrypted = aes_decrypt(packet.data.as_ref(), &enc_key)
163 .map_err(|e| actix_web::error::ErrorBadRequest(e.to_string()))?;
164
165 req.extensions_mut().insert(DecryptedBody(decrypted));
166 req.extensions_mut().insert(RequestSessionKeys { enc_key });
167 }
168 Err(_) => {
169 let frozen: actix_web::web::Bytes = raw.freeze();
170 let (_, mut pl) = actix_http::h1::Payload::create(true);
171 pl.unread_data(frozen);
172 req.set_payload(actix_web::dev::Payload::from(pl));
173 }
174 }
175 }
176 }
177
178 let session_keys = req.extensions().get::<RequestSessionKeys>().cloned();
179 let res = service.call(req).await?;
180
181 let session_keys = match session_keys {
182 Some(k) => k,
183 None => return Ok(res.map_into_left_body()),
184 };
185
186 let (req, res) = res.into_parts();
187 let (head, body) = res.into_parts();
188
189 let body_bytes = actix_web::body::to_bytes(body)
190 .await
191 .map_err(|_| actix_web::error::ErrorInternalServerError("body collect failed"))?;
192
193 let encrypted = match build_signed_response_raw(&body_bytes, &session_keys.enc_key) {
194 Ok(b) => b,
195 Err(_) => return Ok(ServiceResponse::new(
196 req,
197 head.set_body(BoxBody::new(body_bytes)).map_into_right_body(),
198 )),
199 };
200
201 Ok(ServiceResponse::new(
202 req,
203 head.set_body(BoxBody::new(encrypted)).map_into_right_body(),
204 ))
205 })
206 }
207}