rocket_webhook/webhooks/interface/
public_key.rs1use rocket::{
4 Request,
5 data::Outcome,
6 http::Status,
7 outcome::try_outcome,
8 tokio::io::{AsyncRead, AsyncReadExt},
9};
10use tokio_util::bytes::Bytes;
11
12use crate::{
13 WebhookError,
14 webhooks::{Webhook, utils::body_size},
15};
16
17pub mod algorithms;
19
20pub trait WebhookPublicKeyAlgorithm {
22 fn verify(public_key: &Bytes, message: &[u8], signature: &[u8]) -> Result<(), String>;
23}
24
25pub trait WebhookPublicKey: Webhook {
27 type ALG: WebhookPublicKeyAlgorithm;
29
30 fn public_key(
36 &self,
37 req: &Request<'_>,
38 ) -> impl Future<Output = Outcome<'_, Bytes, WebhookError>> + Send + Sync;
39
40 fn expected_signature(&self, req: &Request<'_>) -> Outcome<'_, Vec<u8>, WebhookError>;
42
43 #[allow(unused_variables)]
48 fn message_to_verify(
49 &self,
50 req: &Request<'_>,
51 body: &Bytes,
52 time_bounds: (u32, u32),
53 ) -> Outcome<'_, Bytes, WebhookError> {
54 Outcome::Success(body.clone())
55 }
56
57 fn validate_with_public_key(
59 &self,
60 req: &Request<'_>,
61 mut body: impl AsyncRead + Unpin + Send + Sync,
62 time_bounds: (u32, u32),
63 ) -> impl Future<Output = Outcome<'_, Vec<u8>, WebhookError>> + Send + Sync
64 where
65 Self: Sync,
66 {
67 async move {
68 let expected_signature = try_outcome!(self.expected_signature(req));
70
71 let public_key = try_outcome!(self.public_key(req).await);
73
74 let mut raw_body = Vec::with_capacity(body_size(req.headers()).unwrap_or(512));
76 if let Err(e) = body.read_to_end(&mut raw_body).await {
77 return Outcome::Error((Status::BadRequest, WebhookError::Read(e)));
78 }
79 let raw_body = Bytes::from(raw_body);
80
81 let message = try_outcome!(self.message_to_verify(req, &raw_body, time_bounds));
83 if let Err(e) = Self::ALG::verify(&public_key, &message, &expected_signature) {
84 return Outcome::Error((Status::Unauthorized, WebhookError::Signature(e)));
85 }
86
87 Outcome::Success(raw_body.into())
88 }
89 }
90}