From a38826b07a2a49059c754466e17381228aa2cd4d Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 23 Apr 2022 02:28:43 +0100 Subject: [PATCH] use ed25519-dalek fork with digest verifier --- actix-web-lab/Cargo.toml | 2 +- actix-web-lab/examples/discord_webhook.rs | 48 +++++++++++------------ 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/actix-web-lab/Cargo.toml b/actix-web-lab/Cargo.toml index 1ab9f98a..2a33a09e 100644 --- a/actix-web-lab/Cargo.toml +++ b/actix-web-lab/Cargo.toml @@ -75,7 +75,7 @@ actix-web = { version = "4", features = ["rustls-0_23"] } async_zip = { version = "0.0.17", features = ["deflate", "tokio"] } base64 = "0.22" digest = "0.10" -ed25519-dalek = "2" +ed25519-dalek = { git = "https://github.com/mkj/curve25519-dalek.git", branch = "parts2" } env_logger = "0.11" futures-util = { version = "0.3.17", default-features = false, features = ["std", "io"] } generic-array = "0.14" diff --git a/actix-web-lab/examples/discord_webhook.rs b/actix-web-lab/examples/discord_webhook.rs index 785f91a9..6d67211d 100644 --- a/actix-web-lab/examples/discord_webhook.rs +++ b/actix-web-lab/examples/discord_webhook.rs @@ -8,8 +8,7 @@ use actix_web::{ App, Error, HttpRequest, HttpServer, }; use actix_web_lab::extract::{Json, RequestSignature, RequestSignatureScheme}; -use bytes::{BufMut as _, BytesMut}; -use ed25519_dalek::{Signature, Verifier as _, VerifyingKey}; +use ed25519_dalek::{Signature, StreamVerifier, VerifyingKey}; use hex_literal::hex; use once_cell::sync::Lazy; use rustls::{pki_types::PrivateKeyDer, ServerConfig}; @@ -24,13 +23,15 @@ static TS_HDR_NAME: HeaderName = HeaderName::from_static("x-signature-timestamp" static APP_PUBLIC_KEY: Lazy = Lazy::new(|| VerifyingKey::from_bytes(APP_PUBLIC_KEY_BYTES).unwrap()); -#[derive(Debug)] +/// Signature scheme for Discord interactions/webhooks. +/// +/// Verification is done in `finalize` so this does not support optional verification. struct DiscordWebhook { /// Signature taken from webhook request header. candidate_signature: Signature, - /// Cloned payload state. - chunks: Vec, + /// Signature verifier. + verifier: StreamVerifier, } impl DiscordWebhook { @@ -58,7 +59,7 @@ impl DiscordWebhook { } impl RequestSignatureScheme for DiscordWebhook { - type Signature = (BytesMut, Signature); + type Signature = Signature; type Error = Error; @@ -66,42 +67,36 @@ impl RequestSignatureScheme for DiscordWebhook { let ts = Self::get_timestamp(req)?.to_owned(); let candidate_signature = Self::get_signature(req)?; + let mut verifier = APP_PUBLIC_KEY + .verify_stream(&candidate_signature) + .map_err(error::ErrorBadRequest)?; + + verifier.update(ts); + Ok(Self { candidate_signature, - chunks: vec![Bytes::from(ts)], + verifier, }) } async fn consume_chunk(&mut self, _req: &HttpRequest, chunk: Bytes) -> Result<(), Self::Error> { - self.chunks.push(chunk); + self.verifier.update(chunk); Ok(()) } async fn finalize(self, _req: &HttpRequest) -> Result { - let buf_len = self.chunks.iter().map(|chunk| chunk.len()).sum(); - let mut buf = BytesMut::with_capacity(buf_len); - - for chunk in self.chunks { - buf.put(chunk); - } + self.verifier.finalize_and_verify().map_err(|_| { + error::ErrorUnauthorized("given signature does not match calculated signature") + })?; - Ok((buf, self.candidate_signature)) + Ok(self.candidate_signature) } fn verify( - (payload, candidate_signature): Self::Signature, + signature: Self::Signature, _req: &HttpRequest, ) -> Result { - if APP_PUBLIC_KEY - .verify(&payload, &candidate_signature) - .is_ok() - { - Ok((payload, candidate_signature)) - } else { - Err(error::ErrorUnauthorized( - "given signature does not match calculated signature", - )) - } + Ok(signature) } } @@ -119,6 +114,7 @@ async fn main() -> io::Result<()> { let (Json(form), _) = body.into_parts(); println!("{}", serde_json::to_string_pretty(&form).unwrap()); + // reply with PONG code web::Json(serde_json::json!({ "type": 1 }))