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
|
## 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
|
||||||
|
|
|
@ -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
|
||||||
|
|
30
src/cache/hashcache.rs
vendored
30
src/cache/hashcache.rs
vendored
|
@ -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
31
src/cache/mod.rs
vendored
|
@ -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)]
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue