store sitekeys with pow configs

This commit is contained in:
Aravinth Manivannan 2021-04-09 23:20:14 +05:30
parent 9f4429aee9
commit 3941284890
Signed by: realaravinth
GPG key ID: AD9F0F08E855ED88
8 changed files with 113 additions and 39 deletions

View file

@ -1,6 +1,10 @@
## 0.1.3 ## 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:
- <strike>`Retrieve`</strike> `RetrievePoW` now returns `CachedPoWConfig`
## Changed ## Changed
- `Cache` became `CachePoW` (`HashCache` extension) - `Cache` became `CachePoW` (`HashCache` extension)
@ -8,9 +12,11 @@
- `DeleteString` became `DeletePoW` (`HashCache` extension) - `DeleteString` became `DeletePoW` (`HashCache` extension)
- `Save` trait now requires three new message impls (`HashCache` extension_ - `Save` trait now requires three new message impls (`HashCache` extension_
## Added ## Removed
- `HashCache` was extended to store captcha responses - `CachePoW` constructor was removed in favour of `CachwPoWBuilder`
## Fixed
- a bug in `mCaptcha/pow_sha256` was causing errors in PoW computation
## 0.1.2 ## 0.1.2
## Changed ## Changed

View file

@ -104,6 +104,7 @@ async fn main() -> std::io::Result<()> {
string: work_req.string, string: work_req.string,
result: work.result, result: work.result,
nonce: work.nonce, nonce: work.nonce,
key: mcaptcha_name.into(),
}; };
// Server evaluates client's work. Returns true if everything // Server evaluates client's work. Returns true if everything

View file

@ -27,20 +27,25 @@ use crate::errors::*;
#[derive(Clone, Default)] #[derive(Clone, Default)]
/// cache datastructure implementing [Save] /// cache datastructure implementing [Save]
pub struct HashCache { pub struct HashCache {
difficulty_map: HashMap<String, u32>, difficulty_map: HashMap<String, CachedPoWConfig>,
result_map: HashMap<String, String>, result_map: HashMap<String, String>,
} }
impl HashCache { impl HashCache {
// save [PoWConfig] to cache // save [PoWConfig] to cache
fn save_pow_config(&mut self, config: CachePoW) -> CaptchaResult<()> { fn save_pow_config(&mut self, config: CachePoW) -> CaptchaResult<()> {
self.difficulty_map let challenge = config.string;
.insert(config.string, config.difficulty_factor); let config: CachedPoWConfig = CachedPoWConfig {
key: config.key,
difficulty_factor: config.difficulty_factor,
};
self.difficulty_map.insert(challenge, config);
Ok(()) Ok(())
} }
// retrive [PoWConfig] from cache. Deletes config post retrival // retrive [PoWConfig] from cache. Deletes config post retrival
fn retrive_pow_config(&mut self, string: String) -> CaptchaResult<Option<u32>> { fn retrive_pow_config(&mut self, string: String) -> CaptchaResult<Option<CachedPoWConfig>> {
if let Some(difficulty_factor) = self.remove_pow_config(&string) { if let Some(difficulty_factor) = self.remove_pow_config(&string) {
Ok(Some(difficulty_factor.to_owned())) Ok(Some(difficulty_factor.to_owned()))
} else { } else {
@ -49,7 +54,7 @@ impl HashCache {
} }
// delete [PoWConfig] from cache // delete [PoWConfig] from cache
fn remove_pow_config(&mut self, string: &str) -> Option<u32> { fn remove_pow_config(&mut self, string: &str) -> Option<CachedPoWConfig> {
self.difficulty_map.remove(string) self.difficulty_map.remove(string)
} }
@ -192,6 +197,7 @@ mod tests {
const DIFFICULTY_FACTOR: u32 = 54; const DIFFICULTY_FACTOR: u32 = 54;
const DURATION: u64 = 5; const DURATION: u64 = 5;
const KEY: &str = "mcaptchakey";
let addr = HashCache::default().start(); let addr = HashCache::default().start();
let pow: PoWConfig = PoWConfig::new(DIFFICULTY_FACTOR); let pow: PoWConfig = PoWConfig::new(DIFFICULTY_FACTOR);
let visitor_result = AddVisitorResult { let visitor_result = AddVisitorResult {
@ -199,7 +205,14 @@ mod tests {
duration: DURATION, duration: DURATION,
}; };
let string = pow.string.clone(); 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(); addr.send(msg).await.unwrap().unwrap();
@ -208,7 +221,10 @@ mod tests {
.await .await
.unwrap() .unwrap()
.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); let duration: Duration = Duration::new(5, 0);
//sleep(DURATION + DURATION).await; //sleep(DURATION + DURATION).await;

31
src/cache/mod.rs vendored
View file

@ -40,8 +40,6 @@ pub mod messages {
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::errors::*; use crate::errors::*;
use crate::mcaptcha::AddVisitorResult;
use crate::pow::PoWConfig;
/// Message to cache PoW difficulty factor and string /// Message to cache PoW difficulty factor and string
#[derive(Message, Serialize, Deserialize, Builder)] #[derive(Message, Serialize, Deserialize, Builder)]
@ -50,25 +48,32 @@ pub mod messages {
pub string: String, pub string: String,
pub difficulty_factor: u32, pub difficulty_factor: u32,
pub duration: u64, pub duration: u64,
pub key: String,
} }
impl CachePoW { // pub fn new(p: &PoWConfig, k: String, v: &AddVisitorResult) -> Self {
pub fn new(p: &PoWConfig, v: &AddVisitorResult) -> Self { // CachePoWBuilder::default()
CachePoWBuilder::default() // .string(p.string.clone())
.string(p.string.clone()) // .difficulty_factor(v.difficulty_factor)
.difficulty_factor(v.difficulty_factor) // .duration(v.duration)
.duration(v.duration) // .key(k)
.build() // .build()
.unwrap() // .unwrap()
} // }
} //}
/// Message to retrive the the difficulty factor for the specified /// Message to retrive the the difficulty factor for the specified
/// string from the cache /// string from the cache
#[derive(Message)] #[derive(Message)]
#[rtype(result = "CaptchaResult<Option<u32>>")] #[rtype(result = "CaptchaResult<Option<CachedPoWConfig>>")]
pub struct RetrivePoW(pub String); 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 /// Message to delete cached PoW difficulty factor and string
/// when they expire /// when they expire
#[derive(Message)] #[derive(Message)]

View file

@ -68,6 +68,11 @@ pub enum CaptchaError {
#[display(fmt = "String now found")] #[display(fmt = "String now found")]
StringNotFound, 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 /// Used in builder structs when a value is not set
#[display(fmt = "Please set value: {}", _0)] #[display(fmt = "Please set value: {}", _0)]
PleaseSetValue(#[error(not(source))] String), PleaseSetValue(#[error(not(source))] String),

View file

@ -152,6 +152,7 @@
//! string: work_req.string, //! string: work_req.string,
//! result: work.result, //! result: work.result,
//! nonce: work.nonce, //! nonce: work.nonce,
//! key: mcaptcha_name.into(),
//! }; //! };
//! //!
//! // Server evaluates client's work. Returns true if everything //! // Server evaluates client's work. Returns true if everything

View file

@ -46,6 +46,7 @@ pub struct Work {
pub string: String, pub string: String,
pub result: String, pub result: String,
pub nonce: u64, pub nonce: u64,
pub key: String,
} }
impl From<Work> for PoW<String> { impl From<Work> for PoW<String> {

View file

@ -18,7 +18,7 @@
//! module describing mCaptcha system //! module describing mCaptcha system
use actix::dev::*; use actix::dev::*;
use derive_builder::Builder; use derive_builder::Builder;
use pow_sha256::{Config, PoW}; use pow_sha256::Config;
use crate::cache::messages; use crate::cache::messages;
use crate::cache::Save; use crate::cache::Save;
@ -42,18 +42,27 @@ where
{ {
/// utility function to get difficulty factor of site `id` and cache it /// utility function to get difficulty factor of site `id` and cache it
pub async fn get_pow(&self, id: String) -> Option<PoWConfig> { pub async fn get_pow(&self, id: String) -> Option<PoWConfig> {
use crate::cache::messages::CachePoW; use crate::cache::messages::CachePoWBuilder;
use crate::master::GetSite; use crate::master::GetSite;
use crate::mcaptcha::AddVisitor; 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() { if site_addr.is_none() {
return None; return None;
} }
let mcaptcha = site_addr.unwrap().send(AddVisitor).await.unwrap(); let mcaptcha = site_addr.unwrap().send(AddVisitor).await.unwrap();
let pow_config = PoWConfig::new(mcaptcha.difficulty_factor); 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(); self.cache.send(cache_msg).await.unwrap().unwrap();
Some(pow_config) Some(pow_config)
} }
@ -64,19 +73,29 @@ where
let string = work.string.clone(); let string = work.string.clone();
let msg = RetrivePoW(string.clone()); let msg = RetrivePoW(string.clone());
let pow: PoW<String> = work.into();
let difficulty = self.cache.send(msg).await.unwrap()?; let cached_config = self.cache.send(msg).await.unwrap()?;
match difficulty {
Some(difficulty) => { if cached_config.is_none() {
if self.pow.is_sufficient_difficulty(&pow, difficulty) { return Err(CaptchaError::StringNotFound);
Ok(self.pow.is_valid_proof(&pow, &string))
} else {
Err(CaptchaError::InsuffiencientDifficulty)
}
}
None => 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, string: work_req.string,
result: work.result, result: work.result,
nonce: work.nonce, nonce: work.nonce,
key: MCAPTCHA_NAME.into(),
}; };
let res = actors.verify_pow(payload.clone()).await.unwrap(); let res = actors.verify_pow(payload.clone()).await.unwrap();
@ -157,8 +177,27 @@ mod tests {
string: insufficient_work_req.string, string: insufficient_work_req.string,
result: insufficient_work.result, result: insufficient_work.result,
nonce: insufficient_work.nonce, nonce: insufficient_work.nonce,
key: MCAPTCHA_NAME.into(),
}; };
let res = actors.verify_pow(insufficient_work_payload.clone()).await; let res = actors.verify_pow(insufficient_work_payload.clone()).await;
assert_eq!(res, Err(CaptchaError::InsuffiencientDifficulty)); 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));
} }
} }