From 2d120d6791d8a32e7b3e31a7c5f1b54e3b35f9ef Mon Sep 17 00:00:00 2001 From: realaravinth Date: Sat, 10 Apr 2021 13:14:48 +0530 Subject: [PATCH] token validation --- CHANGELOG.md | 1 + examples/simple.rs | 24 ++++++++++++++++++--- src/cache/hashcache.rs | 16 +++++++------- src/cache/mod.rs | 14 ++++++------ src/lib.rs | 23 ++++++++++++++++++-- src/system.rs | 48 ++++++++++++++++++++++++++++++++---------- 6 files changed, 95 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4195385..74ddbb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ as a result: - `Retrieve` `RetrievePoW` now returns `CachedPoWConfig` - random token generation post `PoW` verification +- token validation ## Changed - `Cache` became `CachePoW` (`HashCache` extension) diff --git a/examples/simple.rs b/examples/simple.rs index 7735121..0fdfb31 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -1,5 +1,5 @@ use m_captcha::{ - cache::HashCache, + cache::{messages::VerifyCaptchaResult, HashCache}, master::{AddSiteBuilder, Master}, pow::{ConfigBuilder, Work}, system::SystemBuilder, @@ -107,11 +107,29 @@ async fn main() -> std::io::Result<()> { key: mcaptcha_name.into(), }; - // Server evaluates client's work. Returns true if everything + // mCAptcha evaluates client's work. Returns a token if everything // checksout and Err() if something fishy is happening let res = system.verify_pow(payload.clone()).await; assert!(res.is_ok()); - // TODO add server-sideverification + + // The client should submit the token to the mCaptcha protected service + // The service should validate the token received from the client + // with the mCaptcha server before processing client's + // request + + // mcaptcha protected service sends the following paylaod to mCaptcha + // server: + let verify_msg = VerifyCaptchaResult { + token: res.unwrap(), + key: mcaptcha_name.into(), + }; + + // on mCaptcha server: + let res = system.validate_verification_tokens(verify_msg).await; + // mCaptcha will return true if token is valid and false if + // token is invalid + assert!(res.is_ok()); + assert!(res.unwrap()); Ok(()) } diff --git a/src/cache/hashcache.rs b/src/cache/hashcache.rs index 0d1ce16..f13e257 100644 --- a/src/cache/hashcache.rs +++ b/src/cache/hashcache.rs @@ -61,13 +61,13 @@ impl HashCache { // save captcha result fn save_captcha_result(&mut self, res: CacheResult) -> CaptchaResult<()> { - self.result_map.insert(res.result, res.key); + self.result_map.insert(res.token, res.key); Ok(()) } // verify captcha result fn verify_captcha_result(&mut self, challenge: VerifyCaptchaResult) -> CaptchaResult { - if let Some(captcha_id) = self.remove_cache_result(&challenge.result) { + if let Some(captcha_id) = self.remove_cache_result(&challenge.token) { if captcha_id == challenge.key { return Ok(true); } else { @@ -141,7 +141,7 @@ impl Handler for HashCache { let addr = ctx.address(); let del_msg = DeleteCaptchaResult { - result: msg.result.clone(), + token: msg.token.clone(), }; let duration: Duration = Duration::new(msg.duration.clone(), 0); @@ -161,7 +161,7 @@ impl Handler for HashCache { impl Handler for HashCache { type Result = MessageResult; fn handle(&mut self, msg: DeleteCaptchaResult, _ctx: &mut Self::Context) -> Self::Result { - self.remove_cache_result(&msg.result); + self.remove_cache_result(&msg.token); MessageResult(Ok(())) } } @@ -252,7 +252,7 @@ mod tests { let add_cache = CacheResult { key: KEY.into(), - result: RES.into(), + token: RES.into(), duration: DURATION, }; @@ -260,14 +260,14 @@ mod tests { let verify_msg = VerifyCaptchaResult { key: KEY.into(), - result: RES.into(), + token: RES.into(), }; assert!(addr.send(verify_msg).await.unwrap().unwrap()); let verify_msg = VerifyCaptchaResult { key: "cz".into(), - result: RES.into(), + token: RES.into(), }; assert!(!addr.send(verify_msg).await.unwrap().unwrap()); @@ -276,7 +276,7 @@ mod tests { let verify_msg = VerifyCaptchaResult { key: KEY.into(), - result: RES.into(), + token: RES.into(), }; assert!(!addr.send(verify_msg).await.unwrap().unwrap()); } diff --git a/src/cache/mod.rs b/src/cache/mod.rs index 582c7fc..fd20d0c 100644 --- a/src/cache/mod.rs +++ b/src/cache/mod.rs @@ -42,7 +42,7 @@ pub mod messages { use crate::errors::*; /// Message to cache PoW difficulty factor and string - #[derive(Message, Serialize, Deserialize, Builder)] + #[derive(Message, Serialize, Deserialize, Builder, Clone)] #[rtype(result = "CaptchaResult<()>")] pub struct CachePoW { pub string: String, @@ -68,7 +68,7 @@ pub mod messages { #[rtype(result = "CaptchaResult>")] pub struct RetrivePoW(pub String); - #[derive(Clone, PartialEq, Debug, Default)] + #[derive(Clone, PartialEq, Debug, Default, Deserialize, Serialize)] pub struct CachedPoWConfig { pub key: String, pub difficulty_factor: u32, @@ -86,7 +86,7 @@ pub mod messages { #[derive(Message, Serialize, Deserialize, Builder)] #[rtype(result = "CaptchaResult<()>")] pub struct CacheResult { - pub result: String, + pub token: String, // key is Captcha identifier pub key: String, pub duration: u64, @@ -99,7 +99,7 @@ pub mod messages { CacheResultBuilder::default() .key(c.key) .duration(c.duration) - .result(get_random(32)) + .token(get_random(32)) .build() .unwrap() } @@ -107,10 +107,10 @@ pub mod messages { /// Message to verify captcha result against /// the stored captcha key - #[derive(Message)] + #[derive(Message, Clone, Deserialize, Serialize)] #[rtype(result = "CaptchaResult")] pub struct VerifyCaptchaResult { - pub result: String, + pub token: String, pub key: String, } @@ -118,6 +118,6 @@ pub mod messages { #[derive(Message)] #[rtype(result = "CaptchaResult<()>")] pub struct DeleteCaptchaResult { - pub result: String, + pub token: String, } } diff --git a/src/lib.rs b/src/lib.rs index b1b686d..ba99090 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,7 +47,7 @@ //! //! ```rust //! use m_captcha::{ -//! cache::HashCache, +//! cache::{messages::VerifyCaptchaResult, HashCache}, //! master::{AddSiteBuilder, Master}, //! pow::{ConfigBuilder, Work}, //! system::SystemBuilder, @@ -160,7 +160,26 @@ //! let res = system.verify_pow(payload.clone()).await; //! assert!(res.is_ok()); //! -//! Ok(()) +//! // The client should submit the token to the mCaptcha protected service +//! // The service should validate the token received from the client +//! // with the mCaptcha server before processing client's +//! // request +//! +//! // mcaptcha protected service sends the following paylaod to mCaptcha +//! // server: +//! let verify_msg = VerifyCaptchaResult { +//! token: res.unwrap(), +//! key: mcaptcha_name.into(), +//! }; +//! +//! // on mCaptcha server: +//! let res = system.validate_verification_tokens(verify_msg).await; +//! // mCaptcha will return true if token is valid and false if +//! // token is invalid +//! assert!(res.is_ok()); +//! assert!(res.unwrap()); +//! +//! Ok(()) //! } //! ``` #![forbid(unsafe_code)] diff --git a/src/system.rs b/src/system.rs index 880d595..71fce90 100644 --- a/src/system.rs +++ b/src/system.rs @@ -20,7 +20,7 @@ use actix::dev::*; use derive_builder::Builder; use pow_sha256::Config; -use crate::cache::messages; +use crate::cache::messages::*; use crate::cache::Save; use crate::errors::*; use crate::master::Master; @@ -37,14 +37,13 @@ pub struct System { impl System where T: Save, - ::Context: ToEnvelope - + ToEnvelope - + ToEnvelope - + ToEnvelope, + ::Context: ToEnvelope + + ToEnvelope + + ToEnvelope + + ToEnvelope, { /// utility function to get difficulty factor of site `id` and cache it pub async fn get_pow(&self, id: String) -> Option { - use crate::cache::messages::CachePoWBuilder; use crate::master::GetSite; use crate::mcaptcha::AddVisitor; @@ -55,8 +54,6 @@ where let mcaptcha = site_addr.unwrap().send(AddVisitor).await.unwrap(); let pow_config = PoWConfig::new(mcaptcha.difficulty_factor); - // let cache_msg = CachePoW::new(&pow_config, &mcaptcha); - let cache_msg = CachePoWBuilder::default() .string(pow_config.string.clone()) .difficulty_factor(mcaptcha.difficulty_factor) @@ -71,8 +68,6 @@ where /// utility function to verify [Work] pub async fn verify_pow(&self, work: Work) -> CaptchaResult { - use crate::cache::messages::*; - let string = work.string.clone(); let msg = RetrivePoW(string.clone()); @@ -102,10 +97,18 @@ where } let msg: CacheResult = cached_config.into(); - let res = msg.result.clone(); + let res = msg.token.clone(); self.cache.send(msg).await.unwrap()?; Ok(res) } + + /// utility function to validate verification tokens + pub async fn validate_verification_tokens( + &self, + msg: VerifyCaptchaResult, + ) -> CaptchaResult { + self.cache.send(msg).await.unwrap() + } } #[cfg(test)] @@ -159,13 +162,18 @@ mod tests { #[actix_rt::test] async fn verify_pow_works() { + // start system let actors = boostrap_system(10).await; + // get work let work_req = actors.get_pow(MCAPTCHA_NAME.into()).await.unwrap(); + // get config let config = get_config(); + // generate proof let work = config .prove_work(&work_req.string, work_req.difficulty_factor) .unwrap(); + // generate proof payload let mut payload = Work { string: work_req.string, result: work.result, @@ -173,9 +181,27 @@ mod tests { key: MCAPTCHA_NAME.into(), }; + // verifiy proof let res = actors.verify_pow(payload.clone()).await; assert!(res.is_ok()); + // verify validation token + let mut verifi_msg = VerifyCaptchaResult { + token: res.unwrap(), + key: MCAPTCHA_NAME.into(), + }; + assert!(actors + .validate_verification_tokens(verifi_msg.clone()) + .await + .unwrap()); + + // verify wrong validation token + verifi_msg.token = MCAPTCHA_NAME.into(); + assert!(!actors + .validate_verification_tokens(verifi_msg) + .await + .unwrap()); + payload.string = "wrongstring".into(); let res = actors.verify_pow(payload.clone()).await; assert_eq!(res, Err(CaptchaError::StringNotFound));