store sitekeys with pow configs
This commit is contained in:
parent
9f4429aee9
commit
3941284890
8 changed files with 113 additions and 39 deletions
14
CHANGELOG.md
14
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:
|
||||
- <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
|
||||
|
|
|
@ -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
|
||||
|
|
30
src/cache/hashcache.rs
vendored
30
src/cache/hashcache.rs
vendored
|
@ -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
31
src/cache/mod.rs
vendored
|
@ -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)]
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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) {
|
||||
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))
|
||||
} else {
|
||||
Err(CaptchaError::InsuffiencientDifficulty)
|
||||
}
|
||||
}
|
||||
None => Err(CaptchaError::StringNotFound),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue