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
## 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
- `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

View File

@ -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

View File

@ -27,20 +27,25 @@ use crate::errors::*;
#[derive(Clone, Default)]
/// cache datastructure implementing [Save]
pub struct HashCache {
difficulty_map: HashMap<String, u32>,
difficulty_map: HashMap<String, CachedPoWConfig>,
result_map: HashMap<String, String>,
}
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<Option<u32>> {
fn retrive_pow_config(&mut self, string: String) -> CaptchaResult<Option<CachedPoWConfig>> {
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<u32> {
fn remove_pow_config(&mut self, string: &str) -> Option<CachedPoWConfig> {
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;

31
src/cache/mod.rs vendored
View File

@ -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<Option<u32>>")]
#[rtype(result = "CaptchaResult<Option<CachedPoWConfig>>")]
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)]

View File

@ -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),

View File

@ -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

View File

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

View File

@ -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<PoWConfig> {
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<String> = 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));
}
}