diff options
Diffstat (limited to 'webhook')
| -rw-r--r-- | webhook/BUILD | 6 | ||||
| -rw-r--r-- | webhook/Cargo.toml | 7 | ||||
| -rw-r--r-- | webhook/cargo/BUILD.bazel | 21 | ||||
| -rw-r--r-- | webhook/src/config.rs | 2 | ||||
| -rw-r--r-- | webhook/src/handler/error.rs | 24 | ||||
| -rw-r--r-- | webhook/src/handler/handler.rs | 166 | ||||
| -rw-r--r-- | webhook/src/handler/make_service.rs | 15 | ||||
| -rw-r--r-- | webhook/src/handler/mod.rs | 6 | ||||
| -rw-r--r-- | webhook/src/handler/signature.rs | 57 | ||||
| -rw-r--r-- | webhook/src/handler/tests/handler.rs (renamed from webhook/README.md) | 0 | ||||
| -rw-r--r-- | webhook/src/handler/tests/handler_integration.rs | 234 | ||||
| -rw-r--r-- | webhook/src/handler/tests/mod.rs | 4 | ||||
| -rw-r--r-- | webhook/src/handler/tests/signature.rs | 30 | ||||
| -rw-r--r-- | webhook/src/handler/tests/utils.rs | 46 | ||||
| -rw-r--r-- | webhook/src/handler/types.rs | 1 | ||||
| -rw-r--r-- | webhook/src/main.rs | 27 |
16 files changed, 507 insertions, 139 deletions
diff --git a/webhook/BUILD b/webhook/BUILD index f29da5f..4340909 100644 --- a/webhook/BUILD +++ b/webhook/BUILD @@ -7,18 +7,18 @@ test_suite(name = "tests") rust_binary( name = "webhook", srcs = glob(["src/**"]), - deps = all_crate_deps() + ["//common/rust:common"], visibility = ["//visibility:public"], + deps = all_crate_deps() + ["//common/rust:common"], ) rust_test( name = "webhook_test", - crate = ":webhook" + crate = ":webhook", ) rust_image( name = "image", + base = "//bazel:base", binary = ":webhook", visibility = ["//visibility:public"], - base = "//bazel:base", ) diff --git a/webhook/Cargo.toml b/webhook/Cargo.toml index 045c710..7e73c43 100644 --- a/webhook/Cargo.toml +++ b/webhook/Cargo.toml @@ -6,14 +6,13 @@ edition = "2018" [dependencies] hyper = { version = "0.14", features = ["full"] } tokio = { version = "1", features = ["full"] } -log = { version = "0.4", features = ["std"] } -config = "0.11" +common = { path = "../common/rust" } serde = { version = "1.0.8", features = ["derive"] } libsodium-sys = "0.2.7" hex = "0.4.3" serde_json = { version = "1.0" } -common = { path = "../common/rust" } -nats = "0.15.2" +libc = "0.2.101" +lazy_static = "1.4.0" [[bin]] name = "webhook" diff --git a/webhook/cargo/BUILD.bazel b/webhook/cargo/BUILD.bazel index e035ab6..4569cc1 100644 --- a/webhook/cargo/BUILD.bazel +++ b/webhook/cargo/BUILD.bazel @@ -13,15 +13,6 @@ licenses([ # Aliased targets alias( - name = "config", - actual = "@raze__config__0_11_0//:config", - tags = [ - "cargo-raze", - "manual", - ], -) - -alias( name = "hex", actual = "@raze__hex__0_4_3//:hex", tags = [ @@ -40,8 +31,8 @@ alias( ) alias( - name = "libsodium_sys", - actual = "@raze__libsodium_sys__0_2_7//:libsodium_sys", + name = "lazy_static", + actual = "@raze__lazy_static__1_4_0//:lazy_static", tags = [ "cargo-raze", "manual", @@ -49,8 +40,8 @@ alias( ) alias( - name = "log", - actual = "@raze__log__0_4_14//:log", + name = "libc", + actual = "@raze__libc__0_2_101//:libc", tags = [ "cargo-raze", "manual", @@ -58,8 +49,8 @@ alias( ) alias( - name = "nats", - actual = "@raze__nats__0_15_2//:nats", + name = "libsodium_sys", + actual = "@raze__libsodium_sys__0_2_7//:libsodium_sys", tags = [ "cargo-raze", "manual", diff --git a/webhook/src/config.rs b/webhook/src/config.rs index eead97b..a054d33 100644 --- a/webhook/src/config.rs +++ b/webhook/src/config.rs @@ -16,4 +16,4 @@ pub struct Discord { pub struct Config { pub server: ServerSettings, pub discord: Discord, -}
\ No newline at end of file +} diff --git a/webhook/src/handler/error.rs b/webhook/src/handler/error.rs new file mode 100644 index 0000000..ccec59f --- /dev/null +++ b/webhook/src/handler/error.rs @@ -0,0 +1,24 @@ +use hyper::{Body, Error, Response, StatusCode}; + +pub struct WebhookError { + pub code: StatusCode, + pub message: String, +} + +impl WebhookError { + pub fn new(code: StatusCode, message: &str) -> WebhookError { + WebhookError { + code, + message: message.to_string(), + } + } +} + +impl Into<Response<Body>> for WebhookError { + fn into(self) -> Response<Body> { + Response::builder() + .status(self.code) + .body(self.message.into()) + .unwrap() + } +} diff --git a/webhook/src/handler/handler.rs b/webhook/src/handler/handler.rs index b993aaa..4af2ba6 100644 --- a/webhook/src/handler/handler.rs +++ b/webhook/src/handler/handler.rs @@ -1,20 +1,32 @@ +use super::error::WebhookError; use super::{signature::validate_signature, types::Interaction}; use crate::config::Config; -use hyper::{Body, Method, Request, Response, StatusCode, body::{to_bytes, Bytes}, service::Service}; -use log::{error, info, trace}; -use nats::Connection; +use common::log::{debug, error, info}; +use common::nats_crate::Connection; +use hyper::{ + body::{to_bytes, Bytes}, + service::Service, + Body, Method, Request, Response, StatusCode, +}; use serde::{Deserialize, Serialize}; -use std::{future::Future, io::{Error, ErrorKind}, pin::Pin, str::from_utf8, sync::Arc, task::{Context, Poll}}; +use std::{ + future::Future, + pin::Pin, + str::from_utf8, + sync::Arc, + task::{Context, Poll}, + time::Duration, +}; /// Hyper service used to handle the discord webhooks #[derive(Clone)] pub struct HandlerService { - pub config: Config, + pub config: Arc<Config>, pub nats: Arc<Connection>, } impl HandlerService { - async fn check_request(&self, req: Request<Body>) -> Result<Bytes, Error> { + async fn check_request(&self, req: Request<Body>) -> Result<Bytes, WebhookError> { if req.method() == Method::POST { let headers = req.headers().clone(); let signature = headers.get("X-Signature-Ed25519"); @@ -30,25 +42,96 @@ impl HandlerService { ) { Ok(data) } else { - Err(Error::new( - ErrorKind::InvalidData, - "invalid signature specified", + Err(WebhookError::new( + StatusCode::UNAUTHORIZED, + "invalid signature", )) } } else { - Err(Error::new( - ErrorKind::BrokenPipe, + Err(WebhookError::new( + StatusCode::BAD_REQUEST, "failed to read signature", )) } } else { - Err(Error::new(ErrorKind::BrokenPipe, "unable to read body")) + Err(WebhookError::new( + StatusCode::BAD_REQUEST, + "unable to read body", + )) } } else { - Err(Error::new(ErrorKind::InvalidData, "missing headers")) + Err(WebhookError::new( + StatusCode::UNAUTHORIZED, + "missing signature headers", + )) } } else { - Err(Error::new(ErrorKind::InvalidData, "invalid method")) + Err(WebhookError::new(StatusCode::NOT_FOUND, "not found")) + } + } + + async fn process_request( + &mut self, + req: Request<Body>, + ) -> Result<Response<Body>, WebhookError> { + match self.check_request(req).await { + Ok(data) => { + let utf8 = from_utf8(&data); + match utf8 { + Ok(data) => match serde_json::from_str::<Interaction>(data) { + Ok(value) => { + if value.t == 1 { + info!("sending pong"); + // a ping must be responded with another ping + return Ok(Response::builder() + .header("Content-Type", "application/json") + .body(serde_json::to_string(&Ping { t: 1 }).unwrap().into()) + .unwrap()); + } else { + debug!("calling nats"); + // this should hopefully not fail ? + let payload = + serde_json::to_string(&common::payloads::CachePayload { + tracing: common::payloads::Tracing { + node_id: "".to_string(), + span: None, + }, + operation: "".to_string(), + data: value, + }) + .unwrap(); + + match self.nats.request_timeout( + "nova.cache.dispatch.interaction", + payload, + Duration::from_secs(2), + ) { + Ok(response) => Ok(Response::builder() + .header("Content-Type", "application/json") + .body(Body::from(response.data)) + .unwrap()), + + Err(error) => { + error!("failed to request nats: {}", error); + Err(WebhookError::new( + StatusCode::INTERNAL_SERVER_ERROR, + "failed to request nats", + )) + } + } + } + } + + Err(_) => Err(WebhookError::new( + StatusCode::BAD_REQUEST, + "invalid json body", + )), + }, + + Err(_) => Err(WebhookError::new(StatusCode::BAD_REQUEST, "not utf-8 body")), + } + } + Err(error) => Err(error), } } } @@ -56,7 +139,7 @@ impl HandlerService { #[derive(Debug, Serialize, Deserialize)] pub struct Ping { #[serde(rename = "type")] - t: i32 + t: i32, } /// Implementation of the service @@ -70,56 +153,13 @@ impl Service<Request<Body>> for HandlerService { } fn call(&mut self, req: Request<Body>) -> Self::Future { - let self_clone = self.clone(); - + let mut clone = self.clone(); Box::pin(async move { - match self_clone.check_request(req).await { - Ok(data) => { - let value: Interaction = serde_json::from_str(from_utf8(&data).unwrap()).unwrap(); - trace!("received value: {:?}", value); - - match value.t { - 1 => { - info!("sending pong"); - // a ping must be responded with another ping - return Ok(Response::builder().header("Content-Type", "application/json").body(serde_json::to_string(&Ping { - t: 1 - }).unwrap().into()).unwrap()); - }, - _ => { - let payload = serde_json::to_string(&common::payloads::CachePayload { - tracing: common::payloads::Tracing { - node_id: "".to_string(), - span: None, - }, - data: value, - }).unwrap(); + let response = clone.process_request(req).await; - match self_clone.nats.request("nova.cache.dispatch.interaction", payload) { - Ok(response) => { - Ok( - Response::builder() - .header("Content-Type", "application/json") - .body(from_utf8(&response.data).unwrap().to_string().into()) - .unwrap() - ) - }, - Err(error) => { - error!("failed to request nats: {}", error); - Ok( - Response::builder() - .status(500) - .body("an internal server error occured".to_string().into()) - .unwrap() - ) - } - } - }, - } - }, - Err(error) => { - Ok(Response::builder().status(StatusCode::UNAUTHORIZED).body(error.to_string().into()).unwrap()) - } + match response { + Ok(r) => Ok(r), + Err(e) => Ok(e.into()), } }) } diff --git a/webhook/src/handler/make_service.rs b/webhook/src/handler/make_service.rs index 96b203d..deeb2fe 100644 --- a/webhook/src/handler/make_service.rs +++ b/webhook/src/handler/make_service.rs @@ -1,12 +1,15 @@ -use std::{future::{Ready, ready}, sync::Arc, task::{Context, Poll}}; -use hyper::service::Service; -use nats::Connection; -use crate::config::Config; use super::handler::HandlerService; - +use crate::config::Config; +use hyper::service::Service; +use common::nats_crate::Connection; +use std::{ + future::{ready, Ready}, + sync::Arc, + task::{Context, Poll}, +}; pub struct MakeSvc { - pub settings: Config, + pub settings: Arc<Config>, pub nats: Arc<Connection>, } diff --git a/webhook/src/handler/mod.rs b/webhook/src/handler/mod.rs index 490c580..a437dd5 100644 --- a/webhook/src/handler/mod.rs +++ b/webhook/src/handler/mod.rs @@ -1,4 +1,8 @@ pub mod make_service; mod signature; mod handler; -mod types;
\ No newline at end of file +mod types; +mod error; + +#[cfg(test)] +pub mod tests;
\ No newline at end of file diff --git a/webhook/src/handler/signature.rs b/webhook/src/handler/signature.rs index 5af6b63..c12f9e8 100644 --- a/webhook/src/handler/signature.rs +++ b/webhook/src/handler/signature.rs @@ -1,11 +1,30 @@ +use common::prometheus::{Counter, HistogramVec, labels, opts, register_counter, register_histogram_vec}; use libsodium_sys::crypto_sign_ed25519_verify_detached; +lazy_static::lazy_static! { + static ref SIGNATURE_TIME_HISTOGRAM: HistogramVec = register_histogram_vec!( + "nova_webhook_signature_time", + "The time taken by the signature verification", + &["signature"] + ).unwrap(); + + static ref SIGNATURE_BODY_COUNTER: Counter = register_counter!(opts!( + "nova_webhook_", + "", + labels! {"handler" => "webhook_main"} + )).unwrap(); +} + /// Checks the signature of a given data using the hex signature and the public key. pub fn validate_signature(hex_public_key: &str, data: &Vec<u8>, hex_signature: &str) -> bool { + SIGNATURE_BODY_COUNTER.inc(); + let timer = SIGNATURE_TIME_HISTOGRAM.with_label_values(&["webhook_main"]).start_timer(); + // First, we need to check if the signature & private key is valid base64. let signature_result = hex::decode(hex_signature); let public_key_result = hex::decode(hex_public_key); + let mut result = false; if signature_result.is_ok() && public_key_result.is_ok() { // Since we now have the signatures in u8 vectors. We will initialize all the // parameters for the ffi call to sodium. @@ -19,7 +38,7 @@ pub fn validate_signature(hex_public_key: &str, data: &Vec<u8>, hex_signature: & // we assume all the parameters are correct for the call unsafe { // If the signature is valid, sodium will return 0 - return crypto_sign_ed25519_verify_detached( + result = crypto_sign_ed25519_verify_detached( signature_pointer.as_ptr(), data_pointer, data_len, @@ -27,39 +46,7 @@ pub fn validate_signature(hex_public_key: &str, data: &Vec<u8>, hex_signature: & ) == 0; } } - false -} - -#[cfg(test)] -mod test { - use crate::handler::signature::validate_signature; - - #[test] - fn validate_signature_test() { - let signature = "543ec3547d57f9ddb1ec4c5c36503ebf288ffda3da3d510764c9a49c2abb57690ef974c63d174771bdd2481de1066966f57abbec12a3ec171b9f6e2373837002"; - let public_key = "eefe0c24473737cb2035232e3b4eb91c206f0a14684168f3503f7d8316058d6f"; - let content = "message de test incroyable".as_bytes().to_vec(); - assert!(validate_signature(public_key, &content, signature)) - } - - #[test] - fn validate_signature_reverse_test() { - let signature = "543ec3547d57f9ddb1ec4c5c36503ebf288ffda3da3d510764c9a49c2abb57690ef974c63d174771bdd2481de1066966f57abbec12a3ec171b9f6e2373837002"; - let public_key = "c029eea18437292c87c62aec34e7d1bd4e38fe6126f3f7c446de6375dc666044"; - let content = "ceci est un test qui ne fonctionnera pas!" - .as_bytes() - .to_vec(); - assert!(!validate_signature(public_key, &content, signature)) - } - - #[test] - fn invalid_hex() { - let signature = "zzz"; - let public_key = "zzz"; - let content = "ceci est un test qui ne fonctionnera pas!" - .as_bytes() - .to_vec(); - assert!(!validate_signature(public_key, &content, signature)) - } + timer.observe_duration(); + result } diff --git a/webhook/README.md b/webhook/src/handler/tests/handler.rs index e69de29..e69de29 100644 --- a/webhook/README.md +++ b/webhook/src/handler/tests/handler.rs diff --git a/webhook/src/handler/tests/handler_integration.rs b/webhook/src/handler/tests/handler_integration.rs new file mode 100644 index 0000000..fd0d67f --- /dev/null +++ b/webhook/src/handler/tests/handler_integration.rs @@ -0,0 +1,234 @@ +use std::time::Duration; + +use crate::{ + config::Config, + handler::tests::utils::{generate_keypair, sign_message}, + start, +}; +use common::{config::test_init, nats_crate::Connection, testcontainers::{Image, images::generic::WaitFor}}; +use common::{ + config::Settings, + log::info, + testcontainers::{clients::Cli, images::generic::GenericImage, Container, Docker}, +}; +use hyper::{Body, Method, Request}; +use lazy_static::{__Deref, lazy_static}; +use serde_json::json; + +#[cfg(all(unix, target_arch = "x86_64"))] +const fn nats_image<'a>() -> &'a str { + return "amd64/nats"; +} + +#[cfg(all(unix, target_arch = "aarch64"))] +const fn nats_image<'a>() -> &'a str { + return "arm64v8/nats"; +} + +#[cfg(all(target_arch = "x86_64", target_os = "windows"))] +const fn nats_image<'a>() -> &'a str { + return "winamd64/nats"; +} + +lazy_static! { + static ref DOCKER: Cli = Cli::default(); + + static ref NATS_CONTAINER: Container<'static, Cli, GenericImage> = { + test_init(); + + let image: GenericImage = GenericImage::new(nats_image()) + .with_wait_for(WaitFor::message_on_stderr("Server is ready")); + + let container = DOCKER.run(image); + container.start(); + container.image().wait_until_ready(&container); + container.get_host_port(4222).unwrap(); + container + }; + + + static ref KEYPAIR: (String, [u8; 64]) = { + generate_keypair() + }; + + static ref SETTINGS: Settings<Config> = { + let port = NATS_CONTAINER.get_host_port(4222).unwrap(); + common::config::Settings { + config: crate::config::Config { + server: crate::config::ServerSettings { + port: 5003, + address: "0.0.0.0".to_string(), + }, + discord: crate::config::Discord { + public_key: KEYPAIR.0.clone(), + client_id: 0, + }, + }, + redis: common::redis::RedisConfiguration { + url: "".to_string(), + }, + monitoring: common::monitoring::MonitoringConfiguration { + enabled: false, + address: None, + port: None, + }, + nats: common::nats::NatsConfiguration { + client_cert: None, + root_cert: None, + jetstream_api_prefix: None, + max_reconnects: None, + reconnect_buffer_size: None, + tls: None, + client_name: None, + tls_required: None, + host: format!("localhost:{}", port), + }, + } + }; + + static ref TASK: () = { + std::thread::spawn(|| { + let r = tokio::runtime::Runtime::new().unwrap(); + r.spawn(async { start(SETTINGS.clone()).await }); + loop {} + }); + std::thread::sleep(Duration::from_secs(1)); + }; +} + +#[tokio::test] +async fn respond_to_pings() { + let _ = NATS_CONTAINER.deref(); + let _ = TASK.deref(); + let ping = json!({ "type": 1 }).to_string(); + let timestamp = "my datetime :)"; + let signature_data = [timestamp.as_bytes().to_vec(), ping.as_bytes().to_vec()].concat(); + let signature = sign_message(signature_data, KEYPAIR.1); + + let req = Request::builder() + .method(Method::POST) + .uri("http://localhost:5003/") + .header("X-Signature-Ed25519", signature) + .header("X-Signature-Timestamp", timestamp) + .body(Body::from(ping.clone())) + .expect("request builder"); + let client = hyper::client::Client::new(); + let result = client.request(req).await.unwrap(); + + assert!(result.status() == 200); +} + +#[tokio::test] +async fn deny_invalid_signatures() { + let _ = NATS_CONTAINER.deref(); + let _ = TASK.deref(); + let ping = json!({ "type": 1 }).to_string(); + let timestamp = "my datetime :)"; + + let req = Request::builder() + .method(Method::POST) + .uri("http://localhost:5003/") + .header("X-Signature-Ed25519", "inva&lid signature :)") + .header("X-Signature-Timestamp", timestamp) + .body(Body::from(ping.clone())) + .expect("request builder"); + let client = hyper::client::Client::new(); + let result = client.request(req).await.unwrap(); + assert!(result.status() == 401); +} + +#[tokio::test] +async fn response_500_when_no_nats_response() { + let _ = NATS_CONTAINER.deref(); + let _ = TASK.deref(); + let ping = json!({ "type": 0 }).to_string(); + let timestamp = "my datetime :)"; + let signature_data = [timestamp.as_bytes().to_vec(), ping.as_bytes().to_vec()].concat(); + let signature = sign_message(signature_data, KEYPAIR.1); + + // we must timeout + let req = Request::builder() + .method(Method::POST) + .uri("http://localhost:5003/") + .header("X-Signature-Ed25519", signature) + .header("X-Signature-Timestamp", timestamp) + .body(Body::from(ping.clone())) + .expect("request builder"); + + let client = hyper::client::Client::new(); + let result = client.request(req).await.unwrap(); + assert!(result.status() == 500); +} + +#[tokio::test] +async fn respond_from_nats_response() { + let _ = NATS_CONTAINER.deref(); + let _ = TASK.deref(); + let nats: Connection = SETTINGS.clone().nats.into(); + let sub = nats.subscribe("nova.cache.dispatch.interaction").unwrap(); + let ping = json!({ "type": 0 }).to_string(); + let timestamp = "my datetime :)"; + let signature_data = [timestamp.as_bytes().to_vec(), ping.as_bytes().to_vec()].concat(); + let signature = sign_message(signature_data, KEYPAIR.1); + + sub.with_handler(move |msg| { + info!("Received {}", &msg); + msg.respond("ok :)").unwrap(); + Ok(()) + }); + + let req = Request::builder() + .method(Method::POST) + .uri("http://localhost:5003/") + .header("X-Signature-Ed25519", signature) + .header("X-Signature-Timestamp", timestamp) + .body(Body::from(ping.clone())) + .expect("request builder"); + let client = hyper::client::Client::new(); + let result = client.request(req).await.unwrap(); + assert!(result.status() == 200); +} + +#[tokio::test] +async fn response_400_when_invalid_json_body() { + let _ = NATS_CONTAINER.deref(); + let _ = TASK.deref(); + let ping = "{".to_string(); + let timestamp = "my datetime :)"; + let signature_data = [timestamp.as_bytes().to_vec(), ping.as_bytes().to_vec()].concat(); + let signature = sign_message(signature_data, KEYPAIR.1); + + let req = Request::builder() + .method(Method::POST) + .uri("http://localhost:5003/") + .header("X-Signature-Ed25519", signature) + .header("X-Signature-Timestamp", timestamp) + .body(Body::from(ping.clone())) + .expect("request builder"); + let client = hyper::client::Client::new(); + let result = client.request(req).await.unwrap(); + assert!(result.status() == 400); +} + +#[tokio::test] +async fn response_400_when_invalid_utf8_body() { + let _ = NATS_CONTAINER.deref(); + let _ = TASK.deref(); + // invalid 2 octet sequence + let ping = vec![0xc3, 0x28]; + + let timestamp = "my datetime :)"; + let signature_data = [timestamp.as_bytes().to_vec(), ping.to_vec()].concat(); + let signature = sign_message(signature_data, KEYPAIR.1); + + let req = Request::builder() + .method(Method::POST) + .uri("http://localhost:5003/") + .header("X-Signature-Ed25519", signature) + .header("X-Signature-Timestamp", timestamp) + .body(Body::from(ping.clone())) + .expect("request builder"); + let client = hyper::client::Client::new(); + let result = client.request(req).await.unwrap(); + assert!(result.status() == 400); +} diff --git a/webhook/src/handler/tests/mod.rs b/webhook/src/handler/tests/mod.rs new file mode 100644 index 0000000..589ad52 --- /dev/null +++ b/webhook/src/handler/tests/mod.rs @@ -0,0 +1,4 @@ +pub mod handler_integration; +pub mod signature; +pub mod utils; +pub mod handler; diff --git a/webhook/src/handler/tests/signature.rs b/webhook/src/handler/tests/signature.rs new file mode 100644 index 0000000..475e446 --- /dev/null +++ b/webhook/src/handler/tests/signature.rs @@ -0,0 +1,30 @@ +use crate::handler::signature::validate_signature; + + +#[test] +fn validate_signature_test() { + let signature = "543ec3547d57f9ddb1ec4c5c36503ebf288ffda3da3d510764c9a49c2abb57690ef974c63d174771bdd2481de1066966f57abbec12a3ec171b9f6e2373837002"; + let public_key = "eefe0c24473737cb2035232e3b4eb91c206f0a14684168f3503f7d8316058d6f"; + let content = "message de test incroyable".as_bytes().to_vec(); + assert!(validate_signature(public_key, &content, signature)) +} + +#[test] +fn validate_signature_reverse_test() { + let signature = "543ec3547d57f9ddb1ec4c5c36503ebf288ffda3da3d510764c9a49c2abb57690ef974c63d174771bdd2481de1066966f57abbec12a3ec171b9f6e2373837002"; + let public_key = "c029eea18437292c87c62aec34e7d1bd4e38fe6126f3f7c446de6375dc666044"; + let content = "ceci est un test qui ne fonctionnera pas!" + .as_bytes() + .to_vec(); + assert!(!validate_signature(public_key, &content, signature)) +} + +#[test] +fn invalid_hex() { + let signature = "zzz"; + let public_key = "zzz"; + let content = "ceci est un test qui ne fonctionnera pas!" + .as_bytes() + .to_vec(); + assert!(!validate_signature(public_key, &content, signature)) +}
\ No newline at end of file diff --git a/webhook/src/handler/tests/utils.rs b/webhook/src/handler/tests/utils.rs new file mode 100644 index 0000000..f8cdac2 --- /dev/null +++ b/webhook/src/handler/tests/utils.rs @@ -0,0 +1,46 @@ +pub fn generate_keypair() -> ( + String, + [u8; libsodium_sys::crypto_sign_ed25519_SECRETKEYBYTES as usize], +) { + use libsodium_sys::crypto_sign_ed25519_keypair; + let pk_s: String; + + let mut pk = [0; libsodium_sys::crypto_sign_ed25519_PUBLICKEYBYTES as usize]; + let mut sk = [0; libsodium_sys::crypto_sign_ed25519_SECRETKEYBYTES as usize]; + + let pk_p = pk.as_mut_ptr(); + let sk_p = sk.as_mut_ptr(); + + // generate keypair + unsafe { + if crypto_sign_ed25519_keypair(pk_p, sk_p) < 0 { + panic!("keypair generation failed!"); + } + }; + + pk_s = hex::encode(pk); + return (pk_s, sk); +} + +pub fn sign_message( + msg: Vec<u8>, + sk: [u8; libsodium_sys::crypto_sign_ed25519_SECRETKEYBYTES as usize], +) -> String { + use libc::c_ulonglong; + use libsodium_sys::crypto_sign_ed25519_detached; + + let len = msg.len(); + let mut signature_len: c_ulonglong = 0; + let mut str = [0; 64]; + unsafe { + crypto_sign_ed25519_detached( + str.as_mut_ptr(), + &mut signature_len, + msg.as_ptr(), + len as u64, + sk.as_ptr(), + ); + }; + + return hex::encode(str); +}
\ No newline at end of file diff --git a/webhook/src/handler/types.rs b/webhook/src/handler/types.rs index 4cba12a..4fc5b68 100644 --- a/webhook/src/handler/types.rs +++ b/webhook/src/handler/types.rs @@ -7,4 +7,3 @@ pub struct Interaction { pub t: i16, pub data: Option<Value>, } - diff --git a/webhook/src/main.rs b/webhook/src/main.rs index c127c2c..98e5f13 100644 --- a/webhook/src/main.rs +++ b/webhook/src/main.rs @@ -1,30 +1,37 @@ use std::{net::ToSocketAddrs, sync::Arc};
-mod handler;
mod config;
+mod handler;
use crate::handler::make_service::MakeSvc;
-use hyper::Server;
-use log::{info, error};
-use common::config::Settings;
use crate::config::Config;
+use common::config::Settings;
+use common::log::{error, info};
+use hyper::Server;
#[tokio::main]
async fn main() {
let settings: Settings<Config> = Settings::new("webhook").unwrap();
+ start(settings).await;
+}
- let addr = format!("{}:{}", settings.config.server.address, settings.config.server.port)
- .to_socket_addrs()
- .unwrap()
- .next()
- .unwrap();
+async fn start(settings: Settings<Config>) {
+ let addr = format!(
+ "{}:{}",
+ settings.config.server.address, settings.config.server.port
+ )
+ .to_socket_addrs()
+ .unwrap()
+ .next()
+ .unwrap();
info!(
"Starting server on {}:{}",
settings.config.server.address, settings.config.server.port
);
+ let config = Arc::new(settings.config);
let server = Server::bind(&addr).serve(MakeSvc {
- settings: settings.config.clone(),
+ settings: config,
nats: Arc::new(settings.nats.into()),
});
|
