From 39412848905195ab36e829ac9fb917d6aa45d28b Mon Sep 17 00:00:00 2001 From: realaravinth Date: Fri, 9 Apr 2021 23:20:14 +0530 Subject: [PATCH] store sitekeys with pow configs --- CHANGELOG.md | 14 ++++++--- examples/simple.rs | 1 + src/cache/hashcache.rs | 30 +++++++++++++----- src/cache/mod.rs | 31 +++++++++++-------- src/errors.rs | 5 +++ src/lib.rs | 1 + src/pow.rs | 1 + src/system.rs | 69 +++++++++++++++++++++++++++++++++--------- 8 files changed, 113 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ca9811..77c6766 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ ## 0.1.3 -## Fixed -- a bug in `mCaptcha/pow_sha256` was causing errors in PoW computation + +## Added +- `HashCache` was extended to store captcha responses +- `HashCache` was extended to cache site keys when caching `PoW` configurations + as a result: +- `Retrieve` `RetrievePoW` now returns `CachedPoWConfig` ## Changed - `Cache` became `CachePoW` (`HashCache` extension) @@ -8,9 +12,11 @@ - `DeleteString` became `DeletePoW` (`HashCache` extension) - `Save` trait now requires three new message impls (`HashCache` extension_ -## Added -- `HashCache` was extended to store captcha responses +## Removed +- `CachePoW` constructor was removed in favour of `CachwPoWBuilder` +## Fixed +- a bug in `mCaptcha/pow_sha256` was causing errors in PoW computation ## 0.1.2 ## Changed diff --git a/examples/simple.rs b/examples/simple.rs index 07553a2..9947f4e 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -104,6 +104,7 @@ async fn main() -> std::io::Result<()> { string: work_req.string, result: work.result, nonce: work.nonce, + key: mcaptcha_name.into(), }; // Server evaluates client's work. Returns true if everything diff --git a/src/cache/hashcache.rs b/src/cache/hashcache.rs index 43496a6..df11898 100644 --- a/src/cache/hashcache.rs +++ b/src/cache/hashcache.rs @@ -27,20 +27,25 @@ use crate::errors::*; #[derive(Clone, Default)] /// cache datastructure implementing [Save] pub struct HashCache { - difficulty_map: HashMap, + difficulty_map: HashMap, result_map: HashMap, } impl HashCache { // save [PoWConfig] to cache fn save_pow_config(&mut self, config: CachePoW) -> CaptchaResult<()> { - self.difficulty_map - .insert(config.string, config.difficulty_factor); + let challenge = config.string; + let config: CachedPoWConfig = CachedPoWConfig { + key: config.key, + difficulty_factor: config.difficulty_factor, + }; + + self.difficulty_map.insert(challenge, config); Ok(()) } // retrive [PoWConfig] from cache. Deletes config post retrival - fn retrive_pow_config(&mut self, string: String) -> CaptchaResult> { + fn retrive_pow_config(&mut self, string: String) -> CaptchaResult> { if let Some(difficulty_factor) = self.remove_pow_config(&string) { Ok(Some(difficulty_factor.to_owned())) } else { @@ -49,7 +54,7 @@ impl HashCache { } // delete [PoWConfig] from cache - fn remove_pow_config(&mut self, string: &str) -> Option { + fn remove_pow_config(&mut self, string: &str) -> Option { self.difficulty_map.remove(string) } @@ -192,6 +197,7 @@ mod tests { const DIFFICULTY_FACTOR: u32 = 54; const DURATION: u64 = 5; + const KEY: &str = "mcaptchakey"; let addr = HashCache::default().start(); let pow: PoWConfig = PoWConfig::new(DIFFICULTY_FACTOR); let visitor_result = AddVisitorResult { @@ -199,7 +205,14 @@ mod tests { duration: DURATION, }; let string = pow.string.clone(); - let msg = CachePoW::new(&pow, &visitor_result); + + let msg = CachePoWBuilder::default() + .string(pow.string.clone()) + .difficulty_factor(DIFFICULTY_FACTOR) + .duration(visitor_result.duration) + .key(KEY.into()) + .build() + .unwrap(); addr.send(msg).await.unwrap().unwrap(); @@ -208,7 +221,10 @@ mod tests { .await .unwrap() .unwrap(); - assert_eq!(DIFFICULTY_FACTOR, cache_difficulty_factor.unwrap()); + assert_eq!( + DIFFICULTY_FACTOR, + cache_difficulty_factor.unwrap().difficulty_factor + ); let duration: Duration = Duration::new(5, 0); //sleep(DURATION + DURATION).await; diff --git a/src/cache/mod.rs b/src/cache/mod.rs index 7631ec8..d64a33e 100644 --- a/src/cache/mod.rs +++ b/src/cache/mod.rs @@ -40,8 +40,6 @@ pub mod messages { use serde::{Deserialize, Serialize}; use crate::errors::*; - use crate::mcaptcha::AddVisitorResult; - use crate::pow::PoWConfig; /// Message to cache PoW difficulty factor and string #[derive(Message, Serialize, Deserialize, Builder)] @@ -50,25 +48,32 @@ pub mod messages { pub string: String, pub difficulty_factor: u32, pub duration: u64, + pub key: String, } - impl CachePoW { - pub fn new(p: &PoWConfig, v: &AddVisitorResult) -> Self { - CachePoWBuilder::default() - .string(p.string.clone()) - .difficulty_factor(v.difficulty_factor) - .duration(v.duration) - .build() - .unwrap() - } - } + // pub fn new(p: &PoWConfig, k: String, v: &AddVisitorResult) -> Self { + // CachePoWBuilder::default() + // .string(p.string.clone()) + // .difficulty_factor(v.difficulty_factor) + // .duration(v.duration) + // .key(k) + // .build() + // .unwrap() + // } + //} /// Message to retrive the the difficulty factor for the specified /// string from the cache #[derive(Message)] - #[rtype(result = "CaptchaResult>")] + #[rtype(result = "CaptchaResult>")] pub struct RetrivePoW(pub String); + #[derive(Clone, PartialEq, Debug, Default)] + pub struct CachedPoWConfig { + pub key: String, + pub difficulty_factor: u32, + } + /// Message to delete cached PoW difficulty factor and string /// when they expire #[derive(Message)] diff --git a/src/errors.rs b/src/errors.rs index 3c9555c..dc1e449 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -68,6 +68,11 @@ pub enum CaptchaError { #[display(fmt = "String now found")] StringNotFound, + /// Happens when submitted work is computed over configuration intended for + /// a different mCAptcha sitekey + #[display(fmt = "PoW computed over configuration not intended for target sitekey")] + MCaptchaKeyValidationFail, + /// Used in builder structs when a value is not set #[display(fmt = "Please set value: {}", _0)] PleaseSetValue(#[error(not(source))] String), diff --git a/src/lib.rs b/src/lib.rs index ce7c590..ff14fe8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -152,6 +152,7 @@ //! string: work_req.string, //! result: work.result, //! nonce: work.nonce, +//! key: mcaptcha_name.into(), //! }; //! //! // Server evaluates client's work. Returns true if everything diff --git a/src/pow.rs b/src/pow.rs index abcc428..6acb002 100644 --- a/src/pow.rs +++ b/src/pow.rs @@ -46,6 +46,7 @@ pub struct Work { pub string: String, pub result: String, pub nonce: u64, + pub key: String, } impl From for PoW { diff --git a/src/system.rs b/src/system.rs index de87bf4..0655cc8 100644 --- a/src/system.rs +++ b/src/system.rs @@ -18,7 +18,7 @@ //! module describing mCaptcha system use actix::dev::*; use derive_builder::Builder; -use pow_sha256::{Config, PoW}; +use pow_sha256::Config; use crate::cache::messages; use crate::cache::Save; @@ -42,18 +42,27 @@ where { /// 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::CachePoW; + use crate::cache::messages::CachePoWBuilder; use crate::master::GetSite; use crate::mcaptcha::AddVisitor; - let site_addr = self.master.send(GetSite(id)).await.unwrap(); + let site_addr = self.master.send(GetSite(id.clone())).await.unwrap(); if site_addr.is_none() { return None; } 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 = CachePoW::new(&pow_config, &mcaptcha); + + let cache_msg = CachePoWBuilder::default() + .string(pow_config.string.clone()) + .difficulty_factor(mcaptcha.difficulty_factor) + .duration(mcaptcha.duration) + .key(id) + .build() + .unwrap(); + self.cache.send(cache_msg).await.unwrap().unwrap(); Some(pow_config) } @@ -64,19 +73,29 @@ where let string = work.string.clone(); let msg = RetrivePoW(string.clone()); - let pow: PoW = work.into(); - let difficulty = self.cache.send(msg).await.unwrap()?; - match difficulty { - Some(difficulty) => { - if self.pow.is_sufficient_difficulty(&pow, difficulty) { - Ok(self.pow.is_valid_proof(&pow, &string)) - } else { - Err(CaptchaError::InsuffiencientDifficulty) - } - } - None => Err(CaptchaError::StringNotFound), + let cached_config = self.cache.send(msg).await.unwrap()?; + + if cached_config.is_none() { + return Err(CaptchaError::StringNotFound); } + + let cached_config = cached_config.unwrap(); + + if work.key != cached_config.key { + return Err(CaptchaError::MCaptchaKeyValidationFail); + } + + let pow = work.into(); + + if !self + .pow + .is_sufficient_difficulty(&pow, cached_config.difficulty_factor) + { + return Err(CaptchaError::InsuffiencientDifficulty); + } + + Ok(self.pow.is_valid_proof(&pow, &string)) } } @@ -142,6 +161,7 @@ mod tests { string: work_req.string, result: work.result, nonce: work.nonce, + key: MCAPTCHA_NAME.into(), }; let res = actors.verify_pow(payload.clone()).await.unwrap(); @@ -157,8 +177,27 @@ mod tests { string: insufficient_work_req.string, result: insufficient_work.result, nonce: insufficient_work.nonce, + key: MCAPTCHA_NAME.into(), }; let res = actors.verify_pow(insufficient_work_payload.clone()).await; assert_eq!(res, Err(CaptchaError::InsuffiencientDifficulty)); + + let sitekeyfail_config = actors.get_pow(MCAPTCHA_NAME.into()).await.unwrap(); + let sitekeyfail_work = config + .prove_work( + &sitekeyfail_config.string, + sitekeyfail_config.difficulty_factor, + ) + .unwrap(); + + let sitekeyfail = Work { + string: sitekeyfail_config.string, + result: sitekeyfail_work.result, + nonce: sitekeyfail_work.nonce, + key: "example.com".into(), + }; + + let res = actors.verify_pow(sitekeyfail).await; + assert_eq!(res, Err(CaptchaError::MCaptchaKeyValidationFail)); } }