]> git.puffer.fish Git - matthieu/nova.git/commitdiff
remove dependcy on libsodium (deprecated)
authorMatthieu <matthieu@developershouse.xyz>
Mon, 18 Oct 2021 07:54:43 +0000 (11:54 +0400)
committerMatthieu <matthieu@developershouse.xyz>
Mon, 18 Oct 2021 07:54:43 +0000 (11:54 +0400)
webhook/Cargo.toml
webhook/cargo/BUILD.bazel
webhook/src/handler/error.rs
webhook/src/handler/handler.rs
webhook/src/handler/make_service.rs
webhook/src/handler/signature.rs
webhook/src/handler/tests/handler_integration.rs
webhook/src/handler/tests/signature.rs
webhook/src/handler/tests/utils.rs
webhook/src/main.rs

index 7e73c436a119d55bf93cdb5ef5aee62232949d7f..4ef2674f95deb3150e1f2a3b05e2b0404c5739f6 100644 (file)
@@ -8,11 +8,12 @@ hyper = { version = "0.14", features = ["full"] }
 tokio = { version = "1", features = ["full"] }
 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" }
 libc = "0.2.101"
 lazy_static = "1.4.0"
+ed25519-dalek = "1"
+rand = "0.7"
 
 [[bin]]
 name = "webhook"
index 4569cc1026fd3632e9ffbe14e13f9acc36aa8762..8d3e7f4c9c7767cf4834dbdf12d46830daa87054 100644 (file)
@@ -12,6 +12,15 @@ licenses([
 ])
 
 # Aliased targets
+alias(
+    name = "ed25519_dalek",
+    actual = "@raze__ed25519_dalek__1_0_1//:ed25519_dalek",
+    tags = [
+        "cargo-raze",
+        "manual",
+    ],
+)
+
 alias(
     name = "hex",
     actual = "@raze__hex__0_4_3//:hex",
@@ -49,8 +58,8 @@ alias(
 )
 
 alias(
-    name = "libsodium_sys",
-    actual = "@raze__libsodium_sys__0_2_7//:libsodium_sys",
+    name = "rand",
+    actual = "@raze__rand__0_7_3//:rand",
     tags = [
         "cargo-raze",
         "manual",
index ccec59fedc402fc1aaae8580dcb6386c9904201a..f998b84aa9011a85e193047e31a5a738b11f21bc 100644 (file)
@@ -1,4 +1,4 @@
-use hyper::{Body, Error, Response, StatusCode};
+use hyper::{Body, Response, StatusCode};
 
 pub struct WebhookError {
     pub code: StatusCode,
index 4af2ba67897e24c7b6bb92617d15fa4c6607244a..d482e0188053c8dd03236670e15633db57605b2a 100644 (file)
@@ -17,12 +17,14 @@ use std::{
     task::{Context, Poll},
     time::Duration,
 };
+use ed25519_dalek::PublicKey;
 
 /// Hyper service used to handle the discord webhooks
 #[derive(Clone)]
 pub struct HandlerService {
     pub config: Arc<Config>,
     pub nats: Arc<Connection>,
+    pub public_key: Arc<PublicKey>
 }
 
 impl HandlerService {
@@ -36,7 +38,7 @@ impl HandlerService {
                     let contatenated_data = [timestamp.as_bytes().to_vec(), data.to_vec()].concat();
                     if let Ok(signature_str) = &signature.to_str() {
                         if validate_signature(
-                            &self.config.discord.public_key,
+                            &self.public_key,
                             &contatenated_data,
                             signature_str,
                         ) {
index deeb2fe6a0451bffd85fad321b965400087327df..9e904369d65f92845aa7a5c764242aef058fff9c 100644 (file)
@@ -7,10 +7,12 @@ use std::{
     sync::Arc,
     task::{Context, Poll},
 };
+use ed25519_dalek::PublicKey;
 
 pub struct MakeSvc {
     pub settings: Arc<Config>,
     pub nats: Arc<Connection>,
+    pub public_key: Arc<PublicKey>
 }
 
 impl<T> Service<T> for MakeSvc {
@@ -26,6 +28,7 @@ impl<T> Service<T> for MakeSvc {
         ready(Ok(HandlerService {
             config: self.settings.clone(),
             nats: self.nats.clone(),
+            public_key: self.public_key.clone()
         }))
     }
 }
index c12f9e84f94ef2ed5aa8e8b9a28d442765018427..748fa6a1bd2c0cbc8f9e4f4b35362f88c2b1c15c 100644 (file)
@@ -1,5 +1,8 @@
 use common::prometheus::{Counter, HistogramVec, labels, opts, register_counter, register_histogram_vec};
-use libsodium_sys::crypto_sign_ed25519_verify_detached;
+use ed25519_dalek::PublicKey;
+use ed25519_dalek::Verifier;
+use ed25519_dalek::Signature;
+use std::convert::TryInto;
 
 lazy_static::lazy_static! {
     static ref SIGNATURE_TIME_HISTOGRAM: HistogramVec = register_histogram_vec!(
@@ -8,43 +11,29 @@ lazy_static::lazy_static! {
         &["signature"]
     ).unwrap();
 
-    static ref SIGNATURE_BODY_COUNTER: Counter = register_counter!(opts!(
-        "nova_webhook_",
-        "",
+    static ref SIGNATURE_COUNTER: Counter = register_counter!(opts!(
+        "nova_webhook_signatures_verify",
+        "number of signatures verification issued by the service",
         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();
+fn demo<T, const N: usize>(v: Vec<T>) -> [T; N] {
+    v.try_into()
+        .unwrap_or_else(|v: Vec<T>| panic!("Expected a Vec of length {} but it was {}", N, v.len()))
+}
+
+pub fn validate_signature(public_key: &PublicKey, data: &Vec<u8>, hex_signature: &str) -> bool {
+    SIGNATURE_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.
-        let signature_pointer = signature_result.unwrap();
-        let private_key_pointer = public_key_result.unwrap();
-
-        let data_pointer = data.as_ptr();
-        let data_len = data.len() as u64;
-
-        // A ffi call is considered unsafe by the Rust compiler
-        // we assume all the parameters are correct for the call
-        unsafe {
-            // If the signature is valid, sodium will return 0
-            result = crypto_sign_ed25519_verify_detached(
-                signature_pointer.as_ptr(),
-                data_pointer,
-                data_len,
-                private_key_pointer.as_ptr(),
-            ) == 0;
-        }
+    if let Ok(signature) = signature_result {
+        let sig = Signature::from(demo(signature));
+
+        result = public_key.verify(data, &sig).is_ok();
     }
 
     timer.observe_duration();
index fd0d67f959757cb71e543aee79202d44d7dc7e38..2475b9462a541898e38893c267459c85ff60e901 100644 (file)
@@ -47,7 +47,7 @@ lazy_static! {
     };
 
     
-    static ref KEYPAIR: (String, [u8; 64]) = {
+    static ref TEST_KEYPAIR: ed25519_dalek::Keypair = {
         generate_keypair()
     };
 
@@ -60,7 +60,7 @@ lazy_static! {
                     address: "0.0.0.0".to_string(),
                 },
                 discord: crate::config::Discord {
-                    public_key: KEYPAIR.0.clone(),
+                    public_key: hex::encode(TEST_KEYPAIR.to_bytes()),
                     client_id: 0,
                 },
             },
@@ -103,7 +103,7 @@ async fn respond_to_pings() {
     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 signature = sign_message(signature_data, &TEST_KEYPAIR);
 
     let req = Request::builder()
         .method(Method::POST)
@@ -144,7 +144,7 @@ async fn response_500_when_no_nats_response() {
     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);
+    let signature = sign_message(signature_data, &TEST_KEYPAIR);
 
     // we must timeout
     let req = Request::builder()
@@ -169,7 +169,7 @@ async fn respond_from_nats_response() {
     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);
+    let signature = sign_message(signature_data, &TEST_KEYPAIR);
 
     sub.with_handler(move |msg| {
         info!("Received {}", &msg);
@@ -196,7 +196,7 @@ async fn response_400_when_invalid_json_body() {
     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 signature = sign_message(signature_data, &TEST_KEYPAIR);
 
     let req = Request::builder()
         .method(Method::POST)
@@ -219,7 +219,7 @@ async fn response_400_when_invalid_utf8_body() {
 
     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 signature = sign_message(signature_data, &TEST_KEYPAIR);
 
     let req = Request::builder()
         .method(Method::POST)
index 475e446f7ce611aed8c2cf11fdd09f79bb9d4ddc..490143b6f733d0cf9eb3f2131be33c6090a103ac 100644 (file)
@@ -1,30 +1,33 @@
 use crate::handler::signature::validate_signature;
-
+use ed25519_dalek::PublicKey;
 
 #[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))
+    let public_key = PublicKey::from_bytes(&hex::decode("eefe0c24473737cb2035232e3b4eb91c206f0a14684168f3503f7d8316058d6f").unwrap()).unwrap();
+
+    assert!(validate_signature(&public_key, &content, signature))
 }
 
 #[test]
 fn validate_signature_reverse_test() {
     let signature = "543ec3547d57f9ddb1ec4c5c36503ebf288ffda3da3d510764c9a49c2abb57690ef974c63d174771bdd2481de1066966f57abbec12a3ec171b9f6e2373837002";
-    let public_key = "c029eea18437292c87c62aec34e7d1bd4e38fe6126f3f7c446de6375dc666044";
+    let public_key = PublicKey::from_bytes(&hex::decode("c029eea18437292c87c62aec34e7d1bd4e38fe6126f3f7c446de6375dc666044").unwrap()).unwrap();
+
     let content = "ceci est un test qui ne fonctionnera pas!"
         .as_bytes()
         .to_vec();
-    assert!(!validate_signature(public_key, &content, signature))
+    assert!(!validate_signature(&public_key, &content, signature))
 }
 
 #[test]
 fn invalid_hex() {
     let signature = "zzz";
-    let public_key = "zzz";
+    let public_key = PublicKey::from_bytes(&hex::decode("c029eea18437292c87c62aec34e7d1bd4e38fe6126f3f7c446de6375dc666044").unwrap()).unwrap();
+
     let content = "ceci est un test qui ne fonctionnera pas!"
         .as_bytes()
         .to_vec();
-    assert!(!validate_signature(public_key, &content, signature))
+    assert!(!validate_signature(&public_key, &content, signature))
 }
\ No newline at end of file
index f8cdac2e5dedb224d28c48bbc4a18c7e8be9b57f..5e59f090fcaf8f094315e59968e76903f60cc06a 100644 (file)
@@ -1,46 +1,15 @@
-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;
+use rand::rngs::OsRng;
+use ed25519_dalek::{Signer, Keypair, Signature};
 
-    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 generate_keypair() -> Keypair {
+    let mut csprng = OsRng{};
+     Keypair::generate(&mut csprng)
 }
 
 pub fn sign_message(
-    msg: Vec<u8>,
-    sk: [u8; libsodium_sys::crypto_sign_ed25519_SECRETKEYBYTES as usize],
+    message: Vec<u8>,
+    keypair: &Keypair,
 ) -> 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);
+    let signature: Signature = keypair.sign(&message);
+    return hex::encode(signature.to_bytes());
 }
\ No newline at end of file
index 98e5f133a123551efa70709392e1f055e82bdf82..00d42ee3c766b5aaaffd07fe042cf0ad7781b17d 100644 (file)
@@ -6,6 +6,7 @@ use crate::handler::make_service::MakeSvc;
 use crate::config::Config;\r
 use common::config::Settings;\r
 use common::log::{error, info};\r
+use ed25519_dalek::PublicKey;\r
 use hyper::Server;\r
 \r
 #[tokio::main]\r
@@ -30,9 +31,12 @@ async fn start(settings: Settings<Config>) {
     );\r
 \r
     let config = Arc::new(settings.config);\r
+    let public_key =\r
+        Arc::new(PublicKey::from_bytes(&hex::decode(&config.discord.public_key).unwrap()).unwrap());\r
     let server = Server::bind(&addr).serve(MakeSvc {\r
         settings: config,\r
         nats: Arc::new(settings.nats.into()),\r
+        public_key: public_key,\r
     });\r
 \r
     if let Err(e) = server.await {\r